📜 ⬆️ ⬇️

We work with kernel modules in Linux


The kernel is the part of the operating system whose work is completely hidden from the user, because the user does not work with it directly: the user works with the programs. But, nevertheless, without a kernel, a single program cannot work, i.e. they are useless without a core. This mechanism is somewhat similar to the relationship between the waiter and the client: the work of a good waiter should be almost imperceptible for the client, but without the waiter the client will not be able to transfer the order to the chef, and this order will not be delivered.
In Linux, the kernel is monolithic , i.e. all its drivers and subsystems operate in their own address space, separated from the user's one. The term "monolith" itself says that everything is concentrated in the core, and, logically, nothing can be added or removed to it. In the case of the Linux kernel, this is only partly true: the Linux kernel can work in this mode, however, in the vast majority of builds, it is possible to modify parts of the kernel code without recompiling it, and even without unloading it. This is achieved by loading and unloading certain parts of the kernel, which are called modules. Most often, in the course of work, it is necessary to connect modules of device drivers, support for cryptographic algorithms, network tools, and in order to be able to do this correctly, you need to understand the structure of the kernel and be able to work correctly with its modules. This will be discussed in this article.

In modern kernels, when devices are connected, modules are connected automatically, and this event is handled by the udev daemon, which creates the corresponding device file in the "/ dev" directory. All this is done if the corresponding module is correctly installed in the tree of modules. In the case of file systems, the situation is the same: when you try to mount a file system, the kernel loads the necessary module automatically, and performs the mount.
If the need for a module is not so obvious, the kernel does not load it on its own. For example, to support the encryption function on the loop device, you need to manually load the cryptoloop module, and for direct encryption, the encryption algorithm module, for example, blowfish.

Search for the required module

The modules are stored in the directory "/ lib / modules / <kernel version>" as files with the extension "ko". To get a list of all modules from the tree, you can execute the command to search all files with the extension “ko” in the directory with modules of the current kernel:

find / lib / modules / `uname -r` -name '* .ko'

The resulting list will give some insight into the available modules, their purpose and names. For example, the path “kernel / drivers / net / wireless / rt2x00 / rt73usb.ko” clearly indicates that this module is a driver for a wireless device based on the rt73 chip. More detailed information about the module can be obtained using the modinfo command:
')
# modinfo rt73usb

filename: /lib/modules/2.6.38-gentoo-r1/kernel/drivers/net/wireless/rt2x00/rt73usb.ko
license: GPL
firmware: rt73.bin
description: Ralink RT73 USB Wireless LAN driver.
version: 2.3.0
author: rt2x00.serialmonkey.com
depends: rt2x00lib, rt2x00usb, crc-itu-t
vermagic: 2.6.38-gentoo-r1 SMP preempt mod_unload modversions CORE2
parm: nohwcrypt: Disable hardware encryption. (bool)

The “firmware” field indicates that this module does not work by itself, it needs the binary firmware of the device in the special file “rt73.bin”. The need for a firmware file appeared due to the fact that the interaction interface with the device is closed, and these functions are assigned to the firmware file (firmware). You can get the firmware from the developer's site, the installation disc that came with the device, or somewhere in the distribution repositories, then you need to copy it to the "/ lib / firmware" directory, and the file name must match the one specified in the module.
The next field to pay attention to is the "depends" field. Listed here are the modules that this data depends on. It is logical to assume that the modules depend on each other, for example, the USB drive support module depends on the USB controller support module. These dependencies are calculated automatically, and will be described below.
The last important field is “param”. It describes all the parameters that the module can take when it is loaded, and their descriptions. In this case, only one is possible: “nohwcrypt”, which, judging by the description, disables hardware encryption. The type of parameter value is indicated in brackets.
More information about the module can be found in the documentation for the kernel source codes (Documentation directory) in the source tree. For example, you can find the code for the desired video mode driver “vesafb” in the documentation file “Documentation / fb / vesafb.txt” relative to the root of the source tree.

Loading and unloading modules

You can load the module into the kernel with the help of two commands: “insmod” and “modprobe”, which differ from each other in the possibility of rendering and satisfying dependencies. The “insmod” command loads a specific file with the “ko” extension, and if the module depends on other modules that are not yet loaded into the kernel, the command will generate an error and will not load the module. The “modprobe” command works only with the module tree, and it is possible to load only from there by the module name, and not by the file name. This implies the area of ​​application of these commands: using the “insmod” module file is loaded from an arbitrary location of the file system (for example, the user compiled the modules and decided to test its operation before transferring to the kernel tree), and “modprobe” - to load already ready modules included into the module tree of the current kernel version. For example, to load the kernel module "rt73usb" from the kernel tree, including all dependencies, and disabling hardware encryption, run the command:

# modprobe rt73usb nohwcrypt = 0

This module will be loaded with the “insmod” command as follows:

# insmod /lib/modules/2.6.38-gentoo-r1/kernel/drivers/net/wireless/rt2x00/rt73usb.ko nohwcrypt = 0

But you need to remember that when using “insmod” all dependencies will have to be loaded manually. Therefore, this command is gradually being supplanted by the “modprobe” command.

After loading a module, you can check its presence in the list of modules loaded into the kernel using the “lsmod” command:

# lsmod | grep rt73usb

ModuleSizeUsed by
rt73usb173050
crc_itu_t999onert73usb
rt2x00usb5749onert73usb
rt2x00lib194842rt73usb, rt2x00usb

From the output of the command, it is clear that the module is loaded, and also uses other modules in its work.
To unload it, you can use the “rmmod” command or the same “modprobe” command with the “-r” key. As a parameter, both commands need to pass only the name of the module. If the module is not used, then it will be unloaded, and if it is used, an error will be generated, and you will have to unload all modules that depend on it:

# rmmod rt2x00usb
ERROR: Module rt2x00usb is in use by rt73usb

# rmmod rt73usb
# rmmod rt2x00usb

After the module is unloaded, all the features it provides will be removed from the kernel table.

Different mechanisms are provided for automatic loading of modules in different distributions. I will not go into details here, they are different for each distribution, but one method of loading is always efficient and convenient: using start scripts. In the same RedHat systems, you can write the module load commands directly to "/etc/rc.d/rc.local" with all the options.
The module configuration files are located in the /etc/modprobe.d/ directory and have the “conf” extension. These files mainly list the alternative names of the modules, their parameters used when loading them, as well as blacklists prohibited for download. For example, in order for the above-mentioned module to be immediately loaded with the “nohwcrypt = 1” option, you need to create a file in which to write the line:

options rt73usb nohwcrypt = 1

The blacklist of modules is stored primarily in the file "/etc/modules.d/blacklist.conf" in the format "blacklist <module name>". This function is used to prevent loading of buggy or conflicting modules.

Building the module and adding it to the tree

Sometimes there is no driver in the kernel, so you have to manually compile it. This is also the case if the additional software requires the addition of its module to the kernel, such as vmware, virtualbox, or the Nvidia card support package. The compilation process itself is no different from the program building process, but there are still certain requirements.
First, you need a compiler. Usually the “gcc” installation installs everything you need to build the module. If something is missing, the build program will tell about it, and you will need to install the missing packages.
Second, the kernel header files are needed. The fact is that kernel modules always come together with the kernel, using its header files, since any deviation and inconsistency between the module versions and the loaded kernel leads to the inability to load this module into the kernel.
If the system is based on the distribution kernel, then you need to install packages with kernel header files. In most distributions, these are “kernel-headers” and / or “kernel-devel” packages. For the assembly of modules this will be enough. If the kernel was built manually, then these packages are not needed: it is enough to make a symbolic link "/ usr / src / linux" referring to the tree of the configured source codes of the current kernel.
After the module is compiled, one or more files with the extension “ko” will be output. You can try to download them using the "insmod" command and test their work.
If the modules are loaded and working (or too lazy to manually load the dependencies), you need to copy them into the module tree of the current kernel, after which you must update the dependencies of the modules with the “depmod” command. It will walk recursively through the module tree and write down all the dependencies in the “modules.dep” file, which, in consequence, will be analyzed by the “modprobe” command. Now the modules are ready to be loaded using the modprobe command and can be loaded by name with all dependencies.
It should be noted that when updating the kernel this module will not work. You will need new header files and will need to rebuild the module again.

“Listening” to what the kernel says

When the slightest problems with the module, you need to look at the kernel messages. They are output by the “dmesg” command and, depending on the syslog settings, to the file "/ var / log / messages". Kernel messages can be informative or debugging, which will help to determine the problem during the module operation, and can report an error while working with the module, such as insufficient symbols and dependencies, incorrectly passed parameters. For example, the above-considered module “rt73usb” requires a parameter of type bool, which means that the parameter can accept either “0” or “1”. If you try to transfer "2", the system will give an error:

# modprobe rt73usb nohwcrypt = 2
FATAL: Error inserting rt73usb (/lib/modules/2.6.38-gentoo-r1/kernel/drivers/net/wireless/rt2x00/rt73usb.ko): Invalid argument

The “Invalid argument” error can talk about anything, the kernel itself cannot write an error to the console, only using the “printk” function to write to the system log. Looking at the logs you can already find out what the error is:

# dmesg | tail -n1
rt73usb: `2 'invalid for parameter` nohwcrypt'

In this example, only the last line is output with an error so as not to block the article. A module can write several lines, so it is better to display the full log, or at least the last ten lines.
The error is already easy to find: the value “2” is unacceptable for the parameter “nohwcrypt”. After the fix, the module will correctly boot into the kernel.

From all of the above, we can draw one conclusion: the Linux kernel plays by its rules and deals with serious things. Nevertheless, this is just a program, it is, in fact, not much different from other ordinary programs. Understanding that the kernel is not so terrible, as it seems, can be the first step towards understanding the internal structure of the system and, as a result, will help you quickly and efficiently solve the tasks faced by any Linux administrator in their daily work.

Source: https://habr.com/ru/post/117654/


All Articles