Figure: The List of Kernel Modules
There are two ways that a kernel module can be loaded. The first way is to use the insmod command to manually insert the module into the kernel.
The second, and much more clever way, is to load the module as it is needed; this is known as demand loading.
When the kernel discovers the need for a module, for example when the user mounts a file system that is not in the kernel, the kernel will request that the kernel daemon (kerneld) attempts to load the appropriate module.
The kernel daemon is a normal user process albeit with super user privileges. When it is started up, usually at system boot time, it opens up an Inter-Process Communication (IPC) channel to the kernel. This link is used by the kernel to send messages to the kerneld asking for various tasks to be performed.
Kerneld‘s major function is to load and unload kernel modules but it is also capable of other tasks such as starting up the PPP link over serial line when it is needed and closing it down when it is not. Kerneld does not perform these tasks itself, it runs the neccessary programs such as insmod to do the work. Kerneld is just an agent of the kernel, scheduling work on its behalf.
The insmod utility must find the requested kernel module that it is to load. Demand loaded kernel modules are normally kept in /lib/modules/kernel-version. The kernel modules are linked object files just like other programs in the system except that they are linked as a relocatable images. That is, images that are not linked to run from a particular address. They can be either a.out or elf format object files. insmod makes a privileged system call to find the kernel’s exported symbols.
These are kept in pairs containing the symbol’s name and its value, for example its address. The kernel‘s exported symbol table is held in the first module data structure in the list of modules maintained by the kernel and pointed at by the module_list pointer.
Only specifically entered symbols are added into the table, which is built when the kernel is compiled and linked, not every symbol in the kernel is exported to its modules. An example symbol is ``request_irq'' which is the kernel routine that must be called when a driver wishes to take control of a particular system interrupt. In my current kernel, this has a value of 0x0010cd30. You can easily see the exported kernel symbols and their values by looking at /proc/ksyms or by using the ksyms utility. The ksyms utility can either show you all of the exported kernel symbols or only those symbols exported by loaded modules. insmod reads the module into its virtual memory and fixes up its unresolved references to kernel routines and resources using the exported symbols from the kernel. This fixing up takes the form of patching the module image in memory. insmod physically writes the address of the symbol into the appropriate place in the module.
When insmod has fixed up the module’s references to exported kernel symbols,it asks the kernel for enough space to hold the new kernel, again using a privileged system call. The kernel allocates a new module data structure and enough kernel memory to hold the new module and puts it at the end of the kernel modules list. The new module is marked as UNINITIALIZED.
Figure 12.1 shows the list of kernel modules after two modules,VFAT and VFAT have been loaded into the kernel. Not shown in the diagram is the first module on the list, which is a pseudo-module that is only there to hold the kernel’s exported symbol table. You can use the command insmod to list all of the loaded kernel modules and their interdependencies. lsmod simply reformats /proc/modules which is built from the list of kernel module data structures. The memory that the kernel allocates for it is mapped into the insmod process’s address space so that it can access it. insmod copies the module into the allocated space and relocates it so that it will run from the kernel address that it has been allocated. This must happen as the module cannot expect to be loaded at the same address twice let alone into the same address in two different Linux systems. Again, this relocation involves patching the module image with the appropriate addresses.
The new module also exports symbols to the kernel and insmod builds a table of these exported images. Every kernel module must contain module initialization and module cleanup routines and these symbols are deliberately not exported but insmod must know the addresses of them so that it can pass them to the kernel. All being well, insmod is now ready to initialize the module and it makes a privileged system call passing the kernel the addresses of the module’s initialization and cleanup routines.
When a new module is added into the kernel, it must update the kernel’s set of symbols and modify the modules that are being used by the new module. Modules that have other modules dependent on them must maintain a list of references at the end of their symbol table and pointed at by their module data structure. Figure 12.1 shows that the VFAT file system module is dependent on the FAT file system module. So, the FAT module contains a reference to the VFAT module; the reference was added when the VFAT module was loaded. The kernel calls the modules initialization routine and, if it is successful it carries on installing the module. The module’s cleanup routine address is stored in it’s module data structure and it will be called by the kernel when that module is unloaded. Finally, the module’s state is set to RUNNING.