Linux Kernel Module Autoloading with modprobe and systemd

Linux Kernel Module Autoloading, modprobe and systemd – Free Linux Kernel Development Course | EmbeddedPathashala

FREE LINUX KERNEL DEVELOPMENT COURSE — EmbeddedPathashala

Linux Kernel Module Autoloading, modprobe, and systemd Explained

Understand how the Linux kernel automatically loads modules at boot, resolves dependencies, and lets you control which modules load — updated for Linux 6.x

Level
Intermediate
Kernel
Linux 6.x
Topic
Module Loading

Linux Kernel Module Autoloading, modprobe, and systemd — Free Linux Kernel Development Course

When you plug in a USB device or boot your Linux system, dozens of kernel modules load silently in the background without you typing a single command. How does that happen? The answer lies in a powerful trio: modprobe, modules.dep, and systemd. In this free Linux kernel development course lecture from EmbeddedPathashala, you will learn exactly how Linux autoloads kernel modules, resolves module dependencies, and lets you control or block specific modules — all updated for the modern Linux 6.x kernel.

What You Will Learn

  • What kernel module autoloading means and why it matters
  • How modprobe resolves and loads module dependencies automatically
  • What the modules.dep file is and how depmod generates it
  • How module stacking works and how dependencies are tracked
  • How systemd autoloads modules at boot using modules-load.d
  • How to blacklist a kernel module to prevent it from loading
  • Useful kernel command-line parameters for debugging module loading
  • What DKMS is and when you need it

Prerequisites

  • Basic understanding of what a Linux kernel module is
  • Familiarity with writing and compiling a simple kernel module (init/exit functions)
  • Comfort with Linux terminal commands
  • Basic awareness of module stacking and EXPORT_SYMBOL (covered in earlier lectures)

What Is Kernel Module Autoloading?

In Linux, a kernel module is a piece of code that you can load into the running kernel without rebooting. You have probably used insmod or modprobe to manually load a module. But in real-world embedded products and desktop Linux systems alike, modules need to load automatically — either when hardware is detected or when the system boots.

Kernel module autoloading is the mechanism by which the Linux kernel (and userspace tools like udev and systemd) automatically load the right kernel modules at the right time, without any manual intervention from the user.

📌 Note: insmod is a low-level tool that loads a single module with no dependency resolution. modprobe is smarter — it reads the dependency database and loads all required modules in the correct order. For production use, always prefer modprobe.
insmod vs modprobe — What is the Difference?
insmod
  • Loads one module only
  • No dependency resolution
  • Fails if dependencies are missing
  • Requires full path to .ko file
  • Low-level tool
insmod /path/to/mymodule.ko
vs
modprobe
  • Loads module + all dependencies
  • Reads modules.dep database
  • Loads in correct dependency order
  • Works with just the module name
  • Preferred for production use
modprobe mymodule

Understanding modules.dep and the depmod Command

The secret behind modprobe‘s ability to resolve dependencies is a file called modules.dep. This file lives at /lib/modules/$(uname -r)/modules.dep and contains a complete dependency map for every kernel module installed on your system.

Who Creates modules.dep?

The modules.dep file is generated by a tool called depmod. When you run make install after building your kernel module, the install target typically calls depmod automatically. You can also run it manually at any time:

# Rebuild the module dependency database for the running kernel
sudo depmod -a

# Rebuild for a specific kernel version
sudo depmod -a 6.8.0-45-generic

depmod scans all installed .ko files, reads the symbols each module exports and imports, and writes the resulting dependency graph into modules.dep.

What modules.dep Looks Like

The format is simple: each line shows a module followed by a colon and then a list of modules it depends on. If module A depends on module B, the entry looks like:

extra/module_a.ko: extra/module_b.ko

This tells modprobe that before loading module_a.ko, it must first load module_b.ko. When you run modprobe module_a, it automatically loads module_b first, then loads module_a — all in the correct order.

💡 Tip: There is also a binary version of the dependency file called modules.dep.bin at the same location. This is the same data in a faster binary format that modprobe reads by default. Both files are generated together by depmod.

Module Stacking and Dependency Resolution in Practice

Module stacking means building your kernel code as multiple modules where one module depends on another. A common pattern in embedded Linux kernel development is to split functionality into a “core” module that exports symbols, and one or more “user” modules that import those symbols.

When you use EXPORT_SYMBOL() in a module to export a function or variable, that symbol becomes available to other modules. Any module that calls an exported symbol from another module creates a dependency on that module.

Module Stacking — How modprobe Resolves the Load Order
User runs: modprobe user_lkm
modprobe checks modules.dep
extra/user_lkm.ko: extra/core_lkm.ko
Step 1: Load dependency first
🔵 core_lkm.ko loaded → exports symbols
Step 2: Load the requested module
🟢 user_lkm.ko loaded → uses core symbols
✅ Both modules active, stack is live

Installing Stacked Modules and Verifying Dependencies

After building your stacked modules, installing them with make install copies the .ko files to the correct location under /lib/modules/$(uname -r)/extra/ and runs depmod to update the dependency database.

You can verify the recorded dependency like this:

# Check what modules.dep says about your module's dependencies
grep user_lkm /lib/modules/$(uname -r)/modules.dep

# Expected output:
# extra/user_lkm.ko: extra/core_lkm.ko

The colon-separated format tells you: the module on the left depends on every module listed on the right.

📌 Note — Module.symvers: When you build a kernel module, the build system generates a file called Module.symvers. This file records every symbol exported by your module (via EXPORT_SYMBOL) along with the symbol’s CRC checksum. When another module depends on your module, the kernel uses these CRCs to verify that the interface has not changed between builds. Always keep Module.symvers available when building dependent modules.

Checking What Is Currently Loaded

You can inspect the currently loaded modules and their dependencies at runtime using these commands:

# List all currently loaded modules
lsmod

# Show detailed info about a specific module including its dependencies
modinfo core_lkm

# Check which modules are using a given module (who depends on it)
lsmod | grep core_lkm

The lsmod output has three columns: the module name, its size in memory, and how many other modules are currently using it. If the “used by” count is non-zero, you cannot remove that module until its dependents are removed first.

Quick Reference: Module Management Commands in Linux 6.x

CommandWhat It DoesWhen to Use
modprobe <name>Load module + all its dependenciesAlways prefer this for loading
modprobe -r <name>Remove module + unneeded dependenciesCleaner than rmmod for stacked modules
insmod <path.ko>Load single module, no dependency resolutionQuick testing only
rmmod <name>Remove single moduleSimple cases with no dependents
lsmodList currently loaded modulesChecking what is active
modinfo <name>Show module metadata, parameters, dependenciesInspecting any module
depmod -aRebuild modules.dep dependency databaseAfter installing new modules

How systemd Autoloads Kernel Modules at Boot

On all modern Linux distributions — Ubuntu, Fedora, Debian, and their embedded variants — it is systemd that is responsible for loading kernel modules at system boot, not a legacy init script.

systemd does this through a service called systemd-modules-load.service. At boot, this service reads a set of configuration files to know which modules to load. These files live under /etc/modules-load.d/.

systemd Module Autoloading Flow at Boot
🚀 System Boot Starts
systemd starts → systemd-modules-load.service activates
Reads module list from:
/etc/modules-load.d/*.conf /usr/lib/modules-load.d/*.conf /run/modules-load.d/*.conf
🔧 Calls modprobe for each listed module → modules load with full dependency resolution

How to Make Your Kernel Module Load Automatically at Boot

If you want your custom kernel module to load every time the system boots, create a configuration file inside /etc/modules-load.d/:

# Create a config file to autoload your module at boot
# The filename can be anything ending in .conf
sudo nano /etc/modules-load.d/mydriver.conf

# Inside the file, just write the module name (one per line):
# mydriver

The file format is extremely simple: one module name per line. Lines starting with # are comments. After creating this file, systemd will call modprobe mydriver on every subsequent boot.

# Verify the service runs correctly
systemctl status systemd-modules-load.service

# Manually trigger it (useful for testing without rebooting)
sudo systemctl restart systemd-modules-load.service
💡 Best Practice: In embedded Linux products, you often need specific peripheral drivers to load before your application starts. Always use /etc/modules-load.d/ with a product-specific .conf file rather than loading modules from a startup script. This gives you proper dependency ordering and boot-time error reporting through journald.

How to Blacklist a Kernel Module in Linux 6.x

Sometimes a module causes problems — it might make your system freeze, conflict with another driver, or simply not work correctly. In these situations, you want to prevent the module from loading automatically. This is called blacklisting.

There are two ways to blacklist a kernel module:

Method 1: Blacklist via /etc/modprobe.d/ (Permanent)

Create a file in /etc/modprobe.d/ with a .conf extension and add a blacklist line:

# Create a blacklist file
sudo nano /etc/modprobe.d/blacklist-problematic.conf

# Add this line inside the file:
# blacklist problematic_module

# After saving, update initramfs so the change takes effect on next boot
sudo update-initramfs -u      # Debian/Ubuntu
# or
sudo dracut --force            # Fedora/RHEL

Method 2: Blacklist via Kernel Command Line (Temporary/Recovery)

When a module causes boot problems and you cannot even reach the shell to edit files, you can blacklist it directly on the kernel command line. This is the fastest way to recover a system that hangs at boot:

# At the GRUB boot menu, press 'e' to edit the boot entry
# Add this to the end of the linux/linuxefi line:
module_blacklist=problematic_module

# To blacklist multiple modules:
module_blacklist=module1,module2,module3
⚠️ Warning: The kernel command-line blacklist only applies to that single boot. It does not persist across reboots. For a permanent blacklist, always use the /etc/modprobe.d/ method and update initramfs.

You can always check the current kernel command line to see what parameters were passed at boot:

cat /proc/cmdline
Choosing the Right Blacklist Method
🔧 /etc/modprobe.d/ Blacklist
  • Permanent — survives reboots
  • Requires filesystem access
  • Standard production method
  • Needs initramfs update
⚡ Kernel Command Line
  • Temporary — single boot only
  • Works even if system won’t boot
  • Best for recovery/debugging
  • No filesystem changes needed

Useful Kernel Command-Line Parameters for Debugging Module Loading

The kernel command line is a powerful place to control module loading behaviour during debugging. Beyond blacklisting, the Linux kernel provides several parameters that help you diagnose what is happening during initialization.

ParameterWhat It DoesWhen to Use It
module_blacklist=mod1,mod2Prevents listed modules from loadingModule causes hangs or conflicts
debugEnables verbose kernel debugging output (all log levels)General boot debugging
initcall_debugPrints every initcall as it runs at boot with timingDiagnosing where boot hangs
ignore_loglevelPrints ALL kernel messages to console regardless of log levelWhen printk messages aren’t showing
rd.breakDrops into a shell during initramfs stageDebugging early boot
nomoduleDisables all module loading entirelyTesting kernel without any external modules

The initcall_debug parameter is especially valuable for embedded Linux developers. Every driver’s module_init() function is called through an initcall chain at boot. When the system hangs during boot, initcall_debug lets you pinpoint exactly which initcall is responsible.

# Add to GRUB kernel line for boot debugging:
initcall_debug ignore_loglevel

# After booting, search the journal for initcall timings:
journalctl -b | grep "initcall"

What Is DKMS — Dynamic Kernel Module Support?

One practical challenge with out-of-tree kernel modules (modules that are not part of the mainline kernel source) is that they need to be recompiled every time the kernel is updated. If you install a new kernel version without recompiling your module, the module simply will not load because the kernel ABI has changed.

DKMS (Dynamic Kernel Module Support) solves this problem automatically. It registers your module’s source code with the system, and whenever a new kernel is installed, DKMS automatically recompiles and installs your module for the new kernel version.

# Install DKMS on Ubuntu/Debian
sudo apt install dkms

# Register a module with DKMS (requires a dkms.conf file in your module source)
sudo dkms add -m mymodule -v 1.0

# Build for the current kernel
sudo dkms build -m mymodule -v 1.0

# Install it
sudo dkms install -m mymodule -v 1.0

# Verify installation
sudo dkms status

DKMS is widely used for Wi-Fi drivers, GPU drivers, VirtualBox guest additions, and other drivers that ship outside the mainline kernel. For embedded product development, DKMS is most relevant on development boards where you want module updates to survive kernel upgrades automatically.

📌 Note: DKMS requires the kernel headers package to be installed. On Ubuntu, install them with sudo apt install linux-headers-$(uname -r). On embedded targets, you typically cross-compile instead and deploy pre-built modules.

Best Practices for Kernel Module Autoloading

  • Always use modprobe, not insmod, for production module loading. modprobe handles dependencies correctly and integrates with the module database.
  • Run depmod -a after installing any new module. Without this, modprobe will not know about your new module or its dependencies.
  • Use /etc/modules-load.d/ for persistent autoloading. Never put modprobe calls in startup scripts when a proper configuration file location exists.
  • Keep your Module.symvers file safe. Losing it between builds can cause symbol mismatch errors when loading dependent modules.
  • Test blacklisting on the kernel command line before making it permanent. Verify the system behaves as expected before editing /etc/modprobe.d/.
  • Use DKMS for out-of-tree drivers in long-lived deployments. Manual rebuilds after every kernel update are error-prone.
  • On embedded systems, prefer statically compiled drivers for truly critical hardware — modules can fail to load, static drivers cannot.

Common Mistakes and Troubleshooting

❌ Module loads fine with insmod but fails with modprobe

The module may not be installed under /lib/modules/$(uname -r)/. Run sudo make install then sudo depmod -a. modprobe only works with properly installed modules.

❌ “Unknown symbol” error when loading a module

The dependency module is not loaded. Check modules.dep to verify the dependency is recorded. If you see an empty dependency list but the module imports symbols, run depmod -a again after reinstalling both modules.

❌ Module loads but does not persist after reboot

You likely loaded it manually with modprobe but did not add it to /etc/modules-load.d/. Create a .conf file there with your module name to make it persistent.

❌ Module keeps loading even after blacklisting

Another module may be pulling it in as a dependency. Check with modprobe --show-depends <problematic_module>. Also check if udev has a rule that triggers loading. You may need to blacklist the parent module too, or use install problematic_module /bin/true in modprobe.d to completely disable loading via any path.

Key Takeaways

  • modprobe is the correct tool for module loading — it resolves and loads all dependencies automatically using the modules.dep database.
  • depmod builds the dependency database by scanning installed .ko files — always run it after installing new modules.
  • systemd handles module autoloading at boot via systemd-modules-load.service, reading module names from /etc/modules-load.d/*.conf.
  • Blacklisting prevents a module from loading — use /etc/modprobe.d/ for permanent blacklisting or the kernel command line for temporary recovery.
  • DKMS automatically rebuilds out-of-tree modules when a new kernel is installed, solving the kernel update problem.
  • Kernel command-line parameters like initcall_debug and ignore_loglevel are essential tools for debugging module loading failures at boot.

Frequently Asked Questions

Q1: What is the difference between modprobe and insmod for loading kernel modules?

insmod loads a single module from a full file path with no dependency resolution — if the module needs symbols from another module, you must manually load that other module first. modprobe reads the dependency database (modules.dep) and automatically loads all required dependency modules in the correct order. Always use modprobe in production and in scripts.

Q2: How do I make a kernel module load automatically every time my Linux system boots?

Create a file ending in .conf inside /etc/modules-load.d/ containing your module name. For example: echo "mydriver" | sudo tee /etc/modules-load.d/mydriver.conf. systemd’s systemd-modules-load.service will load it at every boot via modprobe.

Q3: What is modules.dep and how is it generated?

modules.dep is a text file at /lib/modules/$(uname -r)/modules.dep that records the dependency relationships between all installed kernel modules. It is generated by running depmod -a, which scans all installed .ko files and determines which modules depend on which other modules by examining their exported and imported symbols.

Q4: How do I permanently prevent a kernel module from loading?

Create a file in /etc/modprobe.d/ (for example blacklist.conf) and add the line blacklist modulename. Then run sudo update-initramfs -u (on Debian/Ubuntu) or sudo dracut --force (on Fedora/RHEL) to apply the change to the initramfs. The module will be blocked from loading on all subsequent boots.

Q5: What is DKMS and when should I use it?

DKMS (Dynamic Kernel Module Support) is a framework that keeps out-of-tree kernel modules automatically compiled and installed across kernel upgrades. Use it when you have a kernel module that is not part of the mainline Linux kernel and your system’s kernel gets updated regularly. Without DKMS, every kernel update would break your module until you manually recompile it.

Q6: How do I find out which kernel modules a module depends on before loading it?

Use modprobe --show-depends modulename to display the full list of modules that would be loaded, in order, when you load that module. You can also run modinfo modulename to see the “depends” field in the module’s metadata. Additionally, grep modulename /lib/modules/$(uname -r)/modules.dep shows the dependency line directly.

Q7: What does the initcall_debug kernel parameter do?

Adding initcall_debug to the kernel command line causes the kernel to print each initcall function name and how long it took to execute during the boot sequence. This is invaluable when your system hangs at boot — you can see exactly which driver’s initialization function is the last thing to run before the hang, narrowing down the problematic module quickly.

Q8: Can I load a kernel module at boot without systemd?

Yes. On systems without systemd (some embedded systems use BusyBox init or OpenRC), you can add modprobe modulename calls to your init scripts (such as /etc/rc.local on older systems or a custom init script). On OpenRC, add module names to /etc/conf.d/modules. The mechanism differs but the result is the same — modprobe is called during boot to load the required modules.

Conclusion

Understanding kernel module autoloading is a fundamental skill for anyone pursuing embedded Linux kernel development or Linux device driver engineering. The three key tools — depmod, modprobe, and systemd — work together to create a reliable, ordered module loading system that handles complex dependency chains automatically.

As you build more complex kernel modules for real embedded products, you will rely on these mechanisms daily: ensuring your custom drivers load in the right order, persisting through reboots, and surviving kernel updates through DKMS. Mastering module autoloading now will save you hours of debugging time later in your career.

In the next lecture, we cover kernel module security — a critical topic for anyone shipping Linux kernel modules in production products. You will learn how kernel address leaks work, what dmesg_restrict and kptr_restrict do, and how to write modules that do not inadvertently expose sensitive kernel information.

Continue Your Free Linux Kernel Development Journey

EmbeddedPathashala offers completely free, in-depth courses on Linux kernel programming, Linux device drivers, and embedded systems — for engineering students and professionals.

Visit EmbeddedPathashala

Leave a Reply

Your email address will not be published. Required fields are marked *