How Linux Handles Module Dependencies with modules.dep
The Linux operating system maintains a modular architecture that
allows device drivers and kernel extensions to load and unload on demand
without rebooting the system. When modules rely on symbols and functions
exported by other modules, the system prevents runtime errors by
resolving these interdependencies automatically. This article examines
how Linux maps, organizes, and resolves these relationships using the
modules.dep file and related user-space tools like
depmod and modprobe.
The Purpose of modules.dep
The Linux kernel does not contain native, high-level graph-traversal
logic to hunt down missing dependencies on disk; that responsibility
belongs to user space. To make dependency resolution fast and efficient,
the operating system precomputes a static mapping of all module
relationships and stores it in a file called
modules.dep.
Located in /lib/modules/$(uname -r)/modules.dep, this
plain-text file lists every compiled kernel module for the current
kernel release along with the exact paths of the modules it requires.
Modern Linux distributions also generate an optimized binary equivalent,
modules.dep.bin, which allows tools to perform fast lookups
without parsing large text files.
How modules.dep Is Generated with depmod
The generation of modules.dep is handled by the
depmod utility, typically executed during system
installation, kernel updates, or manual driver compilation.
Symbol Inspection: Kernel modules are ELF (Executable and Linkable Format) binaries. When
depmodruns, it scans all.ko(kernel object) files under/lib/modules/$(uname -r)/. It inspects the ELF symbol tables of each module, identifying:- Provided symbols: Functions and variables exposed
via macros like
EXPORT_SYMBOL()orEXPORT_SYMBOL_GPL(). - Required symbols: External functions and variables referenced by the module but not defined internally.
- Provided symbols: Functions and variables exposed
via macros like
Graph Construction:
depmodcross-references the required symbols of each module with the provided symbols of all other available modules.Output Generation: Once the dependency graph is established,
depmodwrites the output tomodules.dep.
The format of modules.dep follows a straightforward
layout:
kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko: kernel/drivers/net/mdio.ko kernel/net/core/ptp.ko
In this example, the e1000e.ko driver requires both
mdio.ko and ptp.ko. The target module appears
first, followed by a colon and a space-separated list of its direct and
indirect dependencies.
Dependency Resolution at Runtime: modprobe vs. insmod
Linux provides two primary tools for inserting modules into the
kernel: insmod and modprobe. Understanding the
difference highlights why modules.dep is essential.
insmod
The insmod command is a low-level utility that instructs
the kernel directly to load a specific module file into memory using the
init_module or finit_module system calls. It
does not read configuration files or dependency lists. If you attempt to
load a module with unmet dependencies using insmod, the
kernel will reject the request with an unresolved symbol error (such as
Unknown symbol in module).
modprobe
The standard, intelligent utility for module management is
modprobe. When requested to load a module (for example,
modprobe e1000e), modprobe performs the
following steps:
- Consults
modules.dep.bin(ormodules.dep): It searches for the requested module name to locate its file path and its declared dependencies. - Builds an Execution Order: It constructs a topological sort of the required modules, ensuring that base dependencies load before the modules that rely on them.
- Loads Modules in Sequence:
modprobeinvokes the kernel module loading system calls for each dependency in order, ending with the requested module. - Applies Configurations: It accounts for module
aliases, blacklists, and custom parameters defined in
/etc/modprobe.d/.
Unloading Dependent Modules
modules.dep also assists when removing modules. When you
execute modprobe -r <module_name>, the utility
unloads the specified module and evaluates the dependency chain in
reverse. If any supporting modules were loaded exclusively to satisfy
the removed module and are no longer in use by any other running
component, modprobe safely unloads them as well, reclaiming
system memory.