This is a simple example of hello world. It is also the second chapter of "Linux Device Drivers.
Development Environment required
To enable kernel module compilation, We need to install the rpm package of kernel-devel. For example, in Fedora, we need yum install kernel-devel. If the linux version used does not provide automatic installation of this package, we need to compile the linux kernel to obtain it. Related Files of this package are placed under/usr/src/kernel/<uname-r>, which is the kernel tree.
Applet
Create the hello. c file. The following is the content. The simhei part is the main part of the applet, which is enough as a complete module applet. Other colors are some of our experiments. The dark red part is the part for processing the loading module with parameters.
# Include <Linux/init. h>
# Include
<Linux/module. h>
# Include
<Linux/sched. h>
Module_license ("dual BSD/GPL ");
Static char * Whom = "world ";
Statci int howmany = 1;
Module_param (howmany, Int, s_irugo );
Module_param (whom, CHARP, s_irugo );
/* Export_symbol (XXXX );
*/
Static int _ init hello_init (void)
{
Int I = 0;
Printk ("Hello
World enter/N ");
For (I = 0; I
Printk ("Hello, % s/n", whom ");
/** The following is the KERN_ALERT test. This is a macro, which is equivalent to the string "<1>". Do not add a comma. */
Printk (KERN_ALERT "Hello world module loaded! /N ");
/** Obtain the name and number of the current process through current (struct task_struct). The header file linux/sched. h * is required *
** KERN_INFO is equivalent to the string "", that is, no content is displayed. However, we recommend that you specify it in the program to indicate information
*
**
Level to make the program more standardized and readable */
Printk (KERN_INFO "The Process is [% s], PID [% I]/n". current-> comm, current-> pid );
Return 0;
}
Static void _ exit hello_exit (void)
{
Printk ("Hello
World exit! /N ");
}
Module_init (hello_init );
Module_exit (hello_exit );
The header file module. h defines many symbol and parameters for Loading modules. init. h is used for initialization and cleanup functions. These two header files are required in module programming. In addition, moduleparam. h is used to pass parameters during module loading and is also a common header file.
Module_init and module_exit are used for kernel macros to indicate the trigger when the module is loaded and detached. Both functions are static and only used once, because they cannot be called by other files.
The module_init type is _ init. This indicates that this function is only used for initialization. After initialization, its space will be recycled, you can use _ initdata to indicate that data is only used during initialization and will be recycled. Generally, at the end of an application, you do not need to carefully check the resource. Because the process ends, the system automatically performs the operation. However, in the kernel module, exit needs to delete all the resources created or allocated in init carefully. Otherwise, these resources will be released until the system ends (such as reboot, we also noticed that this type is _ exit and is only used for unload.
Another special macro defines MODULE_LICENSE to indicate the license. If it is not set, the system will prompt: hello: module license 'Unspecified 'taints kernel during loading. It can be "GPL v2", "GPL and additional rigths", etc,
If you do not intend to make public, you can use "Proprietary ". If you need other macros, such as MODULE_AUTHOR (), MODULE_VERSION (), and MODULE_DESCRIPTION (), you can view them in linux/module. h.
The kernel module does not allow function linking. It only allows the functions defined by the kernel. include is placed in/usr/src/kernel/$ {shell uname-r. For OS, the kernel module runs in the kernel space, while the application module runs in the user space. If an application sends a system call or is suspended due to a hardware interruption, it can be transferred from user space to kernel space. The error of the kernel module may cause the system to crash and affect the current process, which is different from that of a common program.
In the preceding example, there is an EXPORT_SYMBOL (name) that has been commented out, or EXPORT_SYMBOL_GPL (name ). There is no link or library in the kernel module. This does not mean that there is a plane structure between kernel modules. One module can use the Symbol of another module, this is common in device drivers. For example, many devices are USB-based, so they can use USB core and the symbol in the input module. If our module also wants to be referenced by other modules, the preceding two methods are required, while _ GPL indicates that it can only be used for a module with GPL copyright. These symbrs are global.
The kernel module can carry parameter calls. For example, a device driver can carry information related to system hardware. The parameter description is module_param (name, type, perm) and the array module_param_array (name, type, num, perm ). Type is a type, such as bool, invbool, charp, int, long, short, uint, ulong, and ushort. Num is actually the length of array. Perm is permission, and the related values can be found in linux/stat. h. S_IRUGO indicates read-only, and can be read or written as S_IRGO | S_IWUSR. 0 indicates no sysfs. When calling, we can use insmod hello. ko whom = "Mom" howloud = 5 is used to provide the parameter. We can choose not to give the parameter, use the default value, or just give one of the parameters.
Compile
Then create the Makefile file:
# Makefile 2.6
# Indicates that the module is created from Hello. O and is named hello. Ko.
OBJ-M: = Hello. o
# Module-objs: If the module consists of N files, other files should be described as follows: module-objs: = file1.o file2.o, because our module is called hello, in this example, it should be written as hello-objs.
Hello-objs: =
PWD: = $ (shell PWD)
Kdir: =/lib/modules/$ (shell uname-R)/build
ALL:
#-C indicates the kernel source directory. In/lib/modules/<uname-R'>/build, you can find the makefile of the highest lenvel of the kernel, M = indicates that when the module target is created, makefile returns to the directory of our Module Program.
Make-C $ (kdir) M = $ (PWD) Modules
Clean:
Make-C $ (kdir) M = $ (PWD)
Clean
If we have multiple c files, we can add their obj files to the test-objs parameters. The next step is make. After compilation, the hello. o file and hello. ko are generated, as well as the hello. mod. c and its obj files, Module. markers.
Module. sysvers Modules. order
File.
We have multiple *. in the c file, we want to create a module named hello. We have three modules *. c file, which is hello. c, file1.c, and file2.c. This is a problem because obj-m: = hello. o, this is the name of the specified module, hello-objs: = file1.o file2.o hello. o: The obj file included in the hello module. If we do not enter hello in the file. o, then the hello. c. in CC [M] file1.o and file2.o, use LD [M] To get the module hello. o. If hello is entered here. o, so both obj-m and hello-objs contain hello. o, it will generate loops and obfuscation for make, so it cannot be written like this.If we construct a module by multiple C files, the name of the C file cannot be the same as that of the module,
In this example, we can change hello. c to hello_main.c, and in Makefile, obj-m: = hello. o, hello-objs = file1.o file2.o hello_main.o.
Load and uninstall
To load this module, you can use insmod or modprobe to run lsmod. You can see that the module has been loaded and uninstalled using the rmmod command. These commands require the root permission.
Compared with imsnod, modprobe will check whether the symbols in the downloaded module is not defined in the kernel. If any of them is not defined, in the path of this module, he will find whether other modules contain these definitions, and load them together if they contain them. In this case, the module unresolved symbols is reported if no matching module is found in insmod or modprobe.
The module currently loaded using lsmod is actually equivalent to cat/proc/modules.
If you want to view the result of printk and the system output is on the console, if you use init 3 to enter our system, that is, using the command line method, we can directly see the output result, if we use terminal or console in the graphic interface or remotely access the terminal, this output is invisible,
You can use the dmesg command to view details. You can also view it in/var/log/messages. Generally, I like dmesg.
Matching with the Kernel version
The kernel module is closely related to the kernel. If we try to load a module but cannot be compatible with the kernel, the following Error is reported: Error inserting '. /hello. ko ':-1 Invalid module format. This is determined by vermagic. o in the kernel.
Since changes to the kernel of each version will affect us, if we need a module to work in multiple versions at the same time to maintain source code consistency, we need to add definitions such as # ifdef, you can use Linux/utsrelease. uts_relese, liux/version under H. linux_version_code or kernel_version under H. The latter two convert their values to the hexadecimal format of 0x.
. We only talk about source code consistency. We still need to specify different kdir for compilation. In this case, according to our programming philosophy, we use the macro to specify the low-level code, which is independent from the OS version, you can also use the underlying code to shield the differences from each other, so that the program becomes readable and easy to maintain. If you want to release a commercial version, you can access the official Linux kernel of upstream in the best way if you want to release a different OS version or upgrade it later. Otherwise, publish the source, which allows users to compile by themselves in different versions. We can also make these steps into a small tool for release. If the binary mode is provided, the kernel version should be specified, and the problem of future OS upgrade should be taken into account.
Handle errors during initialization
For some reason, some errors may occur during initialization. If it appears in the Register process, we can choose to continue initialization to reduce the functions provided by the module. This is the common practice. If we think that the module cannot be loaded due to a serious error, in order to release the kernel space, we should clear all registration before the error.
In a common program, Goto is undesirable, but it is not deleted in C. In module programming, using goto to handle errors reduces complexity and increases readability, therefore, in the kernel, Goto is often used to handle errors. The following is an example:
Struct ST1 * Item1;
Struct st2 * item2;
Int stuff_ OK;
/** We noticed that _ exit is not used here, because this function will not be called only during unload. */
Void my_cleanup (void)
{
If (item)
Release_func (Item1 );
If (item2)
Release_func2 (item2 );
If (stuff_ OK)
Unregister_stuff ();
Return;
}
Int _ init my_init (void)
{
Int err =-enomen;
Item1 = allocate_func (ARG );
Item2 = allocate_func2 (arg2 );
If (! Item1 |! Item2)
Goto fail;
Err = register_stuff (item1, item2 );
If (! Err)
Stuff_ OK = 1;
Else
Goto fail;
/** 0 indicates success. Other error codes can be found in linux/errno. h. Of course, we can also define it */
Return 0;
Fail:
My_cleanup ();
Return err;
}
If we have multiple register in the order of A, B, and C, the order is C, B, And a in the unregister process. During the initialization process, once registered, it may be called slowly. Even if the initialization function is not completed, the necessary initialization must be completed before registration. Initialization fails, but registration may also be called, which must be considered. For simple processing, you can set the tag. After the initialization is successful, the tag is set to trigger the function to check the tag to determine whether the initialization is complete.
Related links:
My articles related to the kernel module