- Kernel module (Русский)
- Contents
- Обзор
- Получение информации
- Автоматическое управление модулями
- Управление модулями вручную
- Настройка параметров модуля
- С помощью файлов в /etc/modprobe.d/
- С помощью командной строки ядра
- Создание псевдонимов
- Запрет загрузки
- С помощью файлов в /etc/modprobe.d/
- С помощью командной строки ядра
- Linux Kernel Module Programming: Hello World Program
- Kernel modules¶
- Lab objectives¶
- Kernel Modules Overview¶
- An example of a kernel module¶
- Compiling kernel modules¶
- Loading/unloading a kernel module¶
- Kernel Module Debugging¶
- objdump¶
- addr2line¶
- minicom¶
- netconsole¶
- Printk debugging¶
- Dynamic debugging¶
- Dyndbg Options¶
- KDB: Kernel debugger¶
- Exercises¶
- 0. Intro¶
Kernel module (Русский)
Модули ядра — это отдельные кусочки кода, которые могут быть загружены и выгружены из ядра по мере необходимости. Они расширяют функциональность ядра без необходимости перезагрузки системы.
Contents
Обзор
Чтобы создать модуль ядра, вы можете прочитать The Linux Kernel Module Programming Guide. Модуль можно сконфигурировать как вкомпилированный, а можно как загружаемый. Чтобы иметь возможность динамически загружать или выгружать модуль, его необходимо сконфигурировать как загружаемый модуль в настройке ядра (в этом случае строка, относящаяся к модулю должна быть отмечена буквой M ).
Модули хранятся в /usr/lib/modules/kernel_release . Чтобы узнать текущую версию вашего ядра, используйте команду uname -r .
Получение информации
Чтобы узнать, какие модули ядра загружены в настоящий момент:
Чтобы показать информацию о модуле:
Чтобы вывести список опций, с которыми загружен модуль:
Чтобы отобразить настройки для всех модулей:
Чтобы отобразить настройки для отдельного модуля:
Чтобы узнать зависимости модуля (или его псевдонима), включая сам модуль:
Автоматическое управление модулями
Сегодня все необходимые загрузки модулей делаются автоматически с помощью udev, поэтому если вам не нужно загружать какие-либо модули, не входящие в стандартное ядро, вам не придётся прописывать модули, требующиеся для загрузки в каком-либо конфигурационном файле. Однако, бывают случаи, когда вам необходимо загружать свой модуль в процессе загрузки или наоборот не загружать какой-то стандартный модуль, чтобы ваш компьютер правильно функционировал.
Чтобы дополнительные модули ядра загружались автоматически в процессе загрузки, создаются статические списки в конфигурационных файлах в директории /etc/modules-load.d/ . Каждый конфигурационный файл называется по схеме /etc/modules-load.d/
.conf . Эти конфигурационные файлы содержат список названий модулей ядра, которые необходимо грузить, разделённых переносом строки. Пустые строки и строки, в которых первым непробельным символом является # или ; , игнорируются.
Смотрите modules-load.d(5) для дополнительной информации.
Управление модулями вручную
Управление модулями ядра производится с помощью утилит, предоставляемых пакетом kmod . Вы можете использовать эти утилиты вручную.
Загрузка модуля из другого места (для тех модулей, которых нет в /lib/modules/$(uname -r)/ ):
Альтернативный вариант выгрузки модуля:
Настройка параметров модуля
Чтобы передать параметр модулю ядра, вы можете воспользоваться конфигурационным файлом в modprobe или использовать командную строку ядра.
С помощью файлов в /etc/modprobe.d/
Файлы в директории /etc/modprobe.d/ можно использовать для передачи настроек модуля в udev, который через modprobe управляет загрузкой модулей во время загрузки системы. Конфигурационные файлы в этой директории могут иметь любое имя, оканчивающееся расширением .conf . Синтаксис следующий:
С помощью командной строки ядра
Если модуль вкомпилирован в ядро, вы также можете передать параметры модулю с помощью командной строки ядра. Для всех стандартных загрузчиков, подойдёт следующий синтаксис:
Просто добавьте это в загрузчике в строку с ядром, как описано в параметрах ядра.
Создание псевдонимов
Псевдонимы (алиасы) — это альтернативные названия для модуля. Например: alias my-mod really_long_modulename означает, что вы можете использовать modprobe my-mod вместо modprobe really_long_modulename . Вы также можете использовать звёздочки в стиле shell, то есть alias my-mod* really_long_modulename будет иметь тот же эффект, что и modprobe my-mod-something . Создайте алиас:
У некоторых модулей есть алиасы, которые используются для их автоматической загрузки, когда они потребуются определённой программе. Отключение этих алиасов может предотвратить их автоматическую загрузку, при этом остаётся возможность из загрузки вручную.
Запрет загрузки
В терминах модулей ядра blacklisting означает механизм, предотвращающий загрузку какого-то модуля. Это может понадобиться, например если вам не нужна работа какого-то оборудования или если загрузка данного модуля вызывает проблемы: например, могут быть два модуля ядра, которые пытаются управлять одним и тем же оборудованием, и их совместная загрузка приводит к конфликту.
Некоторые модули загружаются как часть initramfs. Команда mkinitcpio -M напечатает все автоматически обнаруженные модули: для предотвращения initramfs от загрузки каких-то из этих модулей, занесите их в чёрный список в /etc/modprobe.d/modprobe.conf . Команда mkinitcpio -v отобразит все модули, которые необходимы некоторым хукам (например, filesystems хук, block хук и т.д.). Не забудьте добавить этот .conf файл в секцию FILES в /etc/mkinitcpio.conf , если вы этого ещё не сделали, пересоберите initramfs после того, как вы запретили загрузку модулей, а затем перезагрузитесь.
С помощью файлов в /etc/modprobe.d/
Создайте .conf файл в /etc/modprobe.d/ и добавьте строку для каждого модуля, который вы хотите запретить, используя ключевое слово blacklist . Например, если вы хотите запретить загружать модуль pcspkr :
Можно изменить такое поведение. Команда install заставляет modprobe запускать вашу собственную команду вместо вставки модуля в ядро как обычно. Поэтому вы можете насильно сделать так, чтобы модуль никогда не загружался:
Это запретит данный модуль и все модули, зависящие от него.
С помощью командной строки ядра
Вы также можете запрещать модули из загрузчика.
Источник
Linux Kernel Module Programming: Hello World Program
Kernel modules are pieces of code that can be loaded and unloaded into the kernel upon demand. They extend the functionality of the kernel without the need to reboot the system.
Custom codes can be added to Linux kernels via two methods.
- The basic way is to add the code to the kernel source tree and recompile the kernel.
- A more efficient way is to do this is by adding code to the kernel while it is running. This process is called loading the module, where module refers to the code that we want to add to the kernel.
Since we are loading these codes at runtime and they are not part of the official Linux kernel, these are called loadable kernel module(LKM), which is different from the “base kernel”. Base kernel is located in /boot directory and is always loaded when we boot our machine whereas LKMs are loaded after the base kernel is already loaded. Nonetheless, these LKM are very much part of our kernel and they communicate with base kernel to complete their functions.
LKMs can perform a variety of task, but basically they come under three main categories,
- device driver,
- filesystem driver and
- System calls.
So what advantage do LKMs offer?
One major advantage they have is that we don’t need to keep rebuilding the kernel every time we add a new device or if we upgrade an old device. This saves time and also helps in keeping our base kernel error free. A useful rule of thumb is that we should not change our base kernel once we have a working base kernel.
Also, it helps in diagnosing system problems. For example, assume we have added a module to the base kernel(i.e., we have modified our base kernel by recompiling it) and the module has a bug in it. This will cause error in system boot and we will never know which part of the kernel is causing problems. Whereas if we load the module at runtime and it causes problems, we will immediately know the issue and we can unload the module until we fix it.
LKMs are very flexible, in the sense that they can be loaded and unloaded with a single line of command. This helps in saving memory as we load the LKM only when we need them. Moreover, they are not slower than base kernel because calling either one of them is simply loading code from a different part of memory.
**Warning: LKMs are not user space programs. They are part of the kernel. They have free run of the system and can easily crash it.
So now that we have established the use loadable kernel modules, we are going to write a hello world kernel module. That will print a message when we load the module and an exit message when we unload the module.
Explanation for the above code:
Kernel modules must have at least two functions: a “start” (initialization) function called init_module() which is called when the module is insmoded into the kernel, and an “end” (cleanup) function called cleanup_module() which is called just before it is rmmoded. Actually, things have changed starting with kernel 2.3.13. You can now use whatever name you like for the start and end functions of a module. In fact, the new method is the preferred method. However, many people still use init_module() and cleanup_module() for their start and end functions. In this code we have used hello_start() as init function and hello_end() as cleanup function.
Another thing that you might have noticed is that instead of printf() function we have used printk(). This is because module will not print anything on the console but it will log the message in /var/log/kern.log. Hence it is used to debug kernel modules. Moreover, there are eight possible loglevel strings, defined in the header , that are required while using printk(). We have list them in order of decreasing severity:
- KERN_EMERG: Used for emergency messages, usually those that precede a crash.
- KERN_ALERT: A situation requiring immediate action.
- KERN_CRIT: Critical conditions, often related to serious hardware or software failures.
- KERN_ERR: Used to report error conditions; device drivers often use KERN_ERR to report hardware difficulties.
- KERN_WARNING: Warnings about problematic situations that do not, in themselves, create serious problems with the system.
- KERN_NOTICE: Situations that are normal, but still worthy of note. A number of security-related conditions are reported at this level.
- KERN_INFO: Informational messages. Many drivers print information about the hardware they find at startup time at this level.
- KERN_DEBUG: Used for debugging messages.
We have used KERN_INFO to print the message.
Preparing the system to run the code:
The system must be prepared to build kernel code, and to do this you must have the Linux headers installed on your device. On a typical Linux desktop machine you can use your package manager to locate the correct package to install. For example, under 64-bit Debian you can use:
Makefile to compile the source code:
**Note: Don’t forget the tab spaces in Makefile
Compiling and loading the module:
Run the make command to compile the source code. Then use insmod to load the module.
Now we will use insmod to load the hello.ko object.
Testing the module:
You can get information about the module using the modinfo command, which will identify the description, author and any module parameters that are defined:
To see the message, we need to read the kern.log in /var/log directory.
This article is contributed by Akshat Sinha. If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.
Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.
Источник
Kernel modules¶
Lab objectives¶
- creating simple modules
- describing the process of kernel module compilation
- presenting how a module can be used with a kernel
- simple kernel debugging methods
Kernel Modules Overview¶
A monolithic kernel, though faster than a microkernel, has the disadvantage of lack of modularity and extensibility. On modern monolithic kernels, this has been solved by using kernel modules. A kernel module (or loadable kernel mode) is an object file that contains code that can extend the kernel functionality at runtime (it is loaded as needed); When a kernel module is no longer needed, it can be unloaded. Most of the device drivers are used in the form of kernel modules.
For the development of Linux device drivers, it is recommended to download the kernel sources, configure and compile them and then install the compiled version on the test /development tool machine.
An example of a kernel module¶
Below is a very simple example of a kernel module. When loading into the kernel, it will generate the message «Hi» . When unloading the kernel module, the «Bye» message will be generated.
The generated messages will not be displayed on the console but will be saved in a specially reserved memory area for this, from where they will be extracted by the logging daemon (syslog). To display kernel messages, you can use the dmesg command or inspect the logs:
Compiling kernel modules¶
Compiling a kernel module differs from compiling an user program. First, other headers should be used. Also, the module should not be linked to libraries. And, last but not least, the module must be compiled with the same options as the kernel in which we load the module. For these reasons, there is a standard compilation method ( kbuild ). This method requires the use of two files: a Makefile and a Kbuild file.
Below is an example of a Makefile :
And the example of a Kbuild file used to compile a module:
As you can see, calling make on the Makefile file in the example shown will result in the make invocation in the kernel source directory ( /lib/modules/`uname -r`/build ) and referring to the current directory ( M = `pwd` ). This process ultimately leads to reading the Kbuild file from the current directory and compiling the module as instructed in this file.
For labs we will configure different KDIR, according to the virtual machine specifications:
A Kbuild file contains one or more directives for compiling a kernel module. The easiest example of such a directive is obj-m = module.o . Following this directive, a kernel module ( ko — kernel object) will be created, starting from the module.o file. module.o will be created starting from module.c or module.S . All of these files can be found in the Kbuild ‘s directory.
An example of a Kbuild file that uses several sub-modules is shown below:
For the example above, the steps to compile are:
- compile the module-a.c and module-b.c sources, resulting in module-a.o and module-b.o objects
- module-a.o and module-b.o will then be linked in supermodule.o
- from supermodule.o will be created supermodule.ko module
The suffix of targets in Kbuild determines how they are used, as follows:
- M (modules) is a target for loadable kernel modules
- Y (yes) represents a target for object files to be compiled and then linked to a module ( $(mode_name)-y ) or within the kernel ( obj-y )
- any other target suffix will be ignored by Kbuild and will not be compiled
These suffixes are used to easily configure the kernel by running the make menuconfig command or directly editing the .config file. This file sets a series of variables that are used to determine which features are added to the kernel at build time. For example, when adding BTRFS support with make menuconfig, add the line CONFIG_BTRFS_FS = y to the .config file. The BTRFS kbuild contains the line obj-$(CONFIG_BTRFS_FS):= btrfs.o , which becomes obj-y:= btrfs.o . This will compile the btrfs.o object and will be linked to the kernel. Before the variable was set, the line became obj:=btrfs.o and so it was ignored, and the kernel was build without BTRFS support.
For more details, see the Documentation/kbuild/makefiles.txt and Documentation/kbuild/modules.txt files within the kernel sources.
Loading/unloading a kernel module¶
To load a kernel module, use the insmod utility. This utility receives as a parameter the path to the *.ko file in which the module was compiled and linked. Unloading the module from the kernel is done using the rmmod command, which receives the module name as a parameter.
When loading the kernel module, the routine specified as a parameter of the module_init macro will be executed. Similarly, when the module is unloaded the routine specified as a parameter of the module_exit will be executed.
A complete example of compiling and loading/unloading a kernel module is presented below:
Information about modules loaded into the kernel can be found using the lsmod command or by inspecting the /proc/modules , /sys/module directories.
Kernel Module Debugging¶
Troubleshooting a kernel module is much more complicated than debugging a regular program. First, a mistake in a kernel module can lead to blocking the entire system. Troubleshooting is therefore much slowed down. To avoid reboot, it is recommended to use a virtual machine (qemu, virtualbox, vmware).
When a module containing bugs is inserted into the kernel, it will eventually generate a kernel oops. A kernel oops is an invalid operation detected by the kernel and can only be generated by the kernel. For a stable kernel version, it almost certainly means that the module contains a bug. After the oops appears, the kernel will continue to work.
Very important to the appearance of a kernel oops is saving the generated message. As noted above, messages generated by the kernel are saved in logs and can be displayed with the dmesg command. To make sure that no kernel message is lost, it is recommended to insert/test the kernel directly from the console, or periodically check the kernel messages. Noteworthy is that an oops can occur because of a programming error, but also a because of hardware error.
If a fatal error occurs, after which the system can not return to a stable state, a kernel panic is generated.
Look at the kernel module below that contains a bug that generates an oops:
Inserting this module into the kernel will generate an oops:
Although relatively cryptic, the message provided by the kernel to the appearance of an oops provides valuable information about the error. First line:
Tells us the cause and the address of the instruction that generated the error. In our case this is an invalid access to memory.
Tells us that it’s the first oops (#1). This is important in the context that an oops can lead to other oopses. Usually only the first oops is relevant. Furthermore, the oops code ( 0002 ) provides information about the error type (see arch/x86/include/asm/traps.h ):
- Bit 0 == 0 means no page found, 1 means protection fault
- Bit 1 == 0 means read, 1 means write
- Bit 2 == 0 means kernel, 1 means user mode
In this case, we have a write access that generated the oops (bit 1 is 1).
Below is a dump of the registers. It decodes the instruction pointer ( EIP ) value and notes that the bug appeared in the my_oops_init function with a 5-byte offset ( EIP: [ ] my_oops_init+0x5 ). The message also shows the stack content and a backtrace of calls until then.
If an invalid read call is generated ( #define OP_OOPS OP_READ ), the message will be the same, but the oops code will differ, which would now be 0000 :
objdump¶
Detailed information about the instruction that generated the oops can be found using the objdump utility. Useful options to use are -d to disassemble the code and -S for interleaving C code in assembly language code. For efficient decoding, however, we need the address where the kernel module was loaded. This can be found in /proc/modules .
Here’s an example of using objdump on the above module to identify the instruction that generated the oops:
Note that the instruction that generated the oops ( c89d4005 identified earlier) is:
That is exactly what was expected — storing value 3 at 0x0001234.
The /proc/modules is used to find the address where a kernel module is loaded. The —adjust-vma option allows you to display instructions relative to 0xc89d4000 . The -l option displays the number of each line in the source code interleaved with the assembly language code.
addr2line¶
A more simplistic way to find the code that generated an oops is to use the addr2line utility:
Where 0x5 is the value of the program counter ( EIP = c89d4005 ) that generated the oops, minus the base address of the module ( 0xc89c4000 ) according to /proc/modules
minicom¶
Minicom (or other equivalent utilities, eg picocom, screen) is a utility that can be used to connect and interact with a serial port. The serial port is the basic method for analyzing kernel messages or interacting with an embedded system in the development phase. There are two more common ways to connect:
- a serial port where the device we are going to use is /dev/ttyS0
- a serial USB port (FTDI) in which case the device we are going to use is /dev/ttyUSB .
For the virtual machine used in the lab, the device that we need to use is displayed after the virtual machine starts:
netconsole¶
Netconsole is a utility that allows logging of kernel debugging messages over the network. This is useful when the disk logging system does not work or when serial ports are not available or when the terminal does not respond to commands. Netconsole comes in the form of a kernel module.
To work, it needs the following parameters:
- port, IP address, and the source interface name of the debug station
- port, MAC address, and IP address of the machine to which the debug messages will be sent
These parameters can be configured when the module is inserted into the kernel, or even while the module is inserted if it has been compiled with the CONFIG_NETCONSOLE_DYNAMIC option.
An example configuration when inserting netconsole kernel module is as follows:
Thus, the debug messages on the station that has the address 192.168.191.130 will be sent to the eth0 interface, having source port 6666 . The messages will be sent to 192.168.191.1 with the MAC address 00:50:56:c0:00:08 , on port 6000 .
Messages can be played on the destination station using netcat:
Alternatively, the destination station can configure syslogd to intercept these messages. More information can be found in Documentation/networking/netconsole.txt .
Printk debugging¶
The two oldest and most useful debugging aids are Your Brain and Printf .
For debugging, a primitive way is often used, but it is quite effective: printk debugging. Although a debugger can also be used, it is generally not very useful: simple bugs (uninitialized variables, memory management problems, etc.) can be easily localized by control messages and the kernel-decoded oop message.
For more complex bugs, even a debugger can not help us too much unless the operating system structure is very well understood. When debugging a kernel module, there are a lot of unknowns in the equation: multiple contexts (we have multiple processes and threads running at a time), interruptions, virtual memory, etc.
You can use printk to display kernel messages to user space. It is similar to printf ‘s functionality; the only difference is that the transmitted message can be prefixed with a string of » » , where n indicates the error level (loglevel) and has values between 0 and 7 . Instead of » » , the levels can also be coded by symbolic constants:
The definitions of all log levels are found in linux/kern_levels.h . Basically, these log levels are used by the system to route messages sent to various outputs: console, log files in /var/log etc.
To display printk messages in user space, the printk log level must be of higher priority than console_loglevel variable. The default console log level can be configured from /proc/sys/kernel/printk .
For instance, the command:
will enable all the kernel log messages to be displayed in the console. That is, the logging level has to be strictly less than the console_loglevel variable. For example, if the console_loglevel has a value of 5 (specific to KERN_NOTICE ), only messages with loglevel stricter than 5 (i.e KERN_EMERG , KERN_ALERT , KERN_CRIT , KERN_ERR , KERN_WARNING ) will be shown.
Console-redirected messages can be useful for quickly viewing the effect of executing the kernel code, but they are no longer so useful if the kernel encounters an irreparable error and the system freezes. In this case, the logs of the system must be consulted, as they keep the information between system restarts. These are found in /var/log and are text files, populated by syslogd and klogd during the kernel run. syslogd and klogd take the information from the virtual file system mounted in /proc . In principle, with syslogd and klogd turned on, all messages coming from the kernel will go to /var/log/kern.log .
A simpler version for debugging is using the /var/log/debug file. It is populated only with the printk messages from the kernel with the KERN_DEBUG log level.
Given that a production kernel (similar to the one we’re probably running with) contains only release code, our module is among the few that send messages prefixed with KERN_DEBUG . In this way, we can easily navigate through the /var/log/debug information by finding the messages corresponding to a debugging session for our module.
Such an example would be the following:
The format of the messages must obviously contain all the information of interest in order to detect the error, but inserting in the code printk to provide detailed information can be as time-consuming as writing the code to solve the problem. This is usually a trade-off between the completeness of the debugging messages displayed using printk and the time it takes to insert these messages into the text.
A very simple way, less time-consuming for inserting printk and providing the possibility to analyze the flow of instructions for tests is the use of the predefined constants __FILE__ , __LINE__ and __func__ :
- __FILE__ is replaced by the compiler with the name of the source file it is currently being compiled.
- __LINE__ is replaced by the compiler with the line number on which the current instruction is found in the current source file.
- __func__ / __FUNCTION__ is replaced by the compiler with the name of the function in which the current instruction is found.
__FILE__ and __LINE__ are part of the ANSI C specifications: __func__ is part of specification C99; __FUNCTION__ is a GNU C extension and is not portable; However, since we write code for the Linux kernel, we can use it without any problems.
The following macro definition can be used in this case:
Then, at each point where we want to see if it is «reached» in execution, insert PRINT_DEBUG; This is a simple and quick way, and can yield by carefully analyzing the output.
The dmesg command is used to view the messages printed with printk but not appearing on the console.
To delete all previous messages from a log file, run:
To delete messages displayed by the dmesg command, run:
Dynamic debugging¶
Dynamic dyndbg debugging enables dynamic debugging activation/deactivation. Unlike printk , it offers more advanced printk options for the messages we want to display; it is very useful for complex modules or troubleshooting subsystems. This significantly reduces the amount of messages displayed, leaving only those relevant for the debug context. To enable dyndbg , the kernel must be compiled with the CONFIG_DYNAMIC_DEBUG option. Once configured, pr_debug() , dev_dbg() and print_hex_dump_debug() , print_hex_dump_bytes() can be dynamically enabled per call.
The /sys/kernel/debug/dynamic_debug/control file from the debugfs (where /sys/kernel/debug is the path to which debugfs was mounted) is used to filter messages or to view existing filters.
Debugfs is a simple file system, used as a kernel-space interface and user-space interface to configure different debug options. Any debug utility can create and use its own files /folders in debugfs.
For example, to display existing filters in dyndbg , you will use:
And to enable the debug message from line 1603 in the svcsock.c file:
The /debug/dynamic_debug/control file is not a regular file. It shows the dyndbg settings on the filters. Writing in it with an echo will change these settings (it will not actually make a write). Be aware that the file contains settings for dyndbg debugging messages. Do not log in this file.
Dyndbg Options¶
func — just the debug messages from the functions that have the same name as the one defined in the filter.
file — the name of the file(s) for which we want to display the debug messages. It can be just the source name, but also the absolute path or kernel-tree path.
module — module name.
format — only messages whose display format contains the specified string.
line — the line or lines for which we want to enable debug calls.
In addition to the above options, a series of flags can be added, removed, or set with operators + , — or = :
- p activates the pr_debug() .
- f includes the name of the function in the printed message.
- l includes the line number in the printed message.
- m includes the module name in the printed message.
- t includes the thread id if it is not called from interrupt context
- _ no flag is set.
KDB: Kernel debugger¶
The kernel debugger has proven to be very useful to facilitate the development and debugging process. One of its main advantages is the possibility to perform live debugging. This allows us to monitor, in real time, the accesses to memory or even modify the memory while debugging. The debugger has been integrated in the mainline kernel starting with version 2.6.26-rci. KDB is not a source debugger, but for a complete analysis it can be used in parallel with gdb and symbol files — see the GDB debugging section
To use KDB, you have the following options:
- non-usb keyboard + VGA text console
- serial port console
- USB EHCI debug port
For the lab, we will use a serial interface connected to the host. The following command will activate GDB over the serial port:
KDB is a stop mode debugger, which means that, while it is active, all the other processes are stopped. The kernel can be forced to enter KDB during execution using the following SysRq command
or by using the key combination Ctrl+O g in a terminal connected to the serial port (for example using minicom).
KDB has various commands to control and define the context of the debugged system:
- lsmod, ps, kill, dmesg, env, bt (backtrace)
- dump trace logs
- hardware breakpoints
- modifying memory
For a better description of the available commands you can use the help command in the KDB shell. In the next example, you can notice a simple KDB usage example which sets a hardware breakpoint to monitor the changes of the mVar variable.
Exercises¶
To solve exercises, you need to perform these steps:
- prepare skeletons from templates
- build modules
- copy modules to the VM
- start the VM and test the module in the VM.
The current lab name is kernel_modules. See the exercises for the task name.
The skeleton code is generated from full source examples located in tools/labs/templates . To solve the tasks, start by generating the skeleton code for a complete lab:
You can also generate the skeleton for a single task, using
Once the skeleton drivers are generated, build the source:
Then, copy the modules and start the VM:
The modules are placed in /home/root/skels/kernel_modules/ .
Alternatively, we can copy files via scp, in order to avoid restarting the VM. For additional details about connecting to the VM via the network, please check Connecting to the Virtual Machine .
Review the Exercises section for more detailed information.
Before starting the exercises or generating the skeletons, please run git pull inside the Linux repo, to make sure you have the latest version of the exercises.
If you have local changes, the pull command will fail. Check for local changes using git status . If you want to keep them, run git stash before pull and git stash pop after. To discard the changes, run git reset —hard master .
If you already generated the skeleton before git pull you will need to generate it again.
0. Intro¶
Using cscope or LXR find the definitions of the following symbols in the Linux kernel source code:
- module_init() and module_exit()
- what do the two macros do? What is init_module and cleanup_module ?
- ignore_loglevel
- What is this variable used for?
If you have problems using cscope, it is possible that the database is not generated. To generate it, use the following command in the kernel directory:
When searching for a structure using cscope, use only the structure name (without struct ). So, to search for the structure struct module , you will use the command
Источник