Last Updated: 2023-03-12 12:38:52 Sunday
-- TOC --
前一篇详解Linux驱动基础使用了一段最简单的内核模块测试代码:
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static int __init hello_init(void)
{
printk(KERN_ALERT "Hello, world\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_ALERT "ByeBye, world\n");
}
module_init(hello_init);
module_exit(hello_exit);
本文分析这段代码中的__init
和__exit
!
The
__init
token in the definition may look a little strange; it is a hint to the kernel that the given function is used only at initialization time. The module loader drops the initialization function after the module is loaded, making its memory available for other uses. There is a similar tag__initdata
for data used only during initialization. Use of__init
and__initdata
is optional, but it is worth the trouble. Just be sure not to use them for any function (or data structure) you will be using after initialization completes. You may also encounter__devinit
and__devinitdata
in the kernel source; these translate to__init
and__initdata
only if the kernel has not been configured for hotpluggable devices.
当初始化结束后,被__init
和__initdata
修饰的符号,将会从内核空间的内存中清理掉,这些符号将不能再被访问。
The
__exit
modifier marks the code as being for module unload only (by causing the compiler to place it in a special ELF section). If your module is built directly into the kernel, or if your kernel is configured to disallow the unloading of modules, functions marked__exit
are simply discarded. If your module does not define a cleanup function, the kernel does not allow it to be unloaded.
被__exit
修饰的接口,只会再unload的时候被调用,如果kernel没有配置支持模块动态加载,这个接口在模块被直接编译进内核时,直接丢弃。如果module没有定义清理函数(用module_exit指定),kernel将不允许其卸载。
使用__init
和__exit
后,.ko文件会存在两个特别的代码段,.text.init
和.text.exit
,注册的init和exit函数,分别存放在这两个section中。如果不使用这两个token,注册的函数都在.text.unlikely
段中,但insmod还是能够成功。(由module_init定义)
$ readelf -S hello_mod.ko
There are 49 section headers, starting at offset 0x1a560:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040
0000000000000000 0000000000000000 AX 0 0 1
[ 2] .init.text PROGBITS 0000000000000000 00000040
0000000000000018 0000000000000000 AX 0 0 1
[ 3] .rela.init.text RELA 0000000000000000 0000e590
0000000000000060 0000000000000018 I 46 2 8
[ 4] .exit.text PROGBITS 0000000000000000 00000058
000000000000000c 0000000000000000 AX 0 0 1
[ 5] .rela.exit.text RELA 0000000000000000 0000e5f0
0000000000000030 0000000000000018 I 46 4 8
...
[22] .exit.data PROGBITS 0000000000000000 00000288
0000000000000008 0000000000000000 WA 0 0 8
[23] .rela.exit.data RELA 0000000000000000 0000e740
0000000000000018 0000000000000018 I 46 22 8
[24] .init.data PROGBITS 0000000000000000 00000290
0000000000000008 0000000000000000 WA 0 0 8
[25] .rela.init.data RELA 0000000000000000 0000e758
0000000000000018 0000000000000018 I 46 24 8
...
在<linux/init.h>
中,有如下定义:
/* These are for everybody (although not all archs will actually
discard it in modules) */
#define __init __section(".init.text") __cold __latent_entropy __noinitretpoline
#define __initdata __section(".init.data")
#define __initconst __section(".init.rodata")
#define __exitdata __section(".exit.data")
#define __exit_call __used __section(".exitcall.exit")
这些定义,都使用了编译器的指定section的功能,参考:gcc的__attribute__机制。
如果没有module_exit定义清理函数,模块将不允许unload:
$ sudo rmmod hello_mod
rmmod: ERROR: libkmod/libkmod-module.c:799 kmod_module_remove_module() could not remove 'hello_mod': Device or resource busy
rmmod: ERROR: could not remove module hello_mod: Device or resource busy
一次测试时,在init函数中调用了exit,出现如下warning:
WARNING: modpost: /home/xinlin/test/mychar/mychar.o(.text.unlikely+0x82): Section mismatch in reference from the function mychar_init.cold() to the function .exit.text:mychar_exit()
The function mychar_init.cold() references a function in an exit section.
Often the function mychar_exit() has valid usage outside the exit section
and the fix is to remove the __exit annotation of mychar_exit.
以上warning,只使用__exit,没有使用__init。
如果同时使用这两个token,warning如下:
WARNING: modpost: /home/xinlin/test/mychar/mychar.o(.init.text+0xa7): Section mismatch in reference from the function init_module() to the function .exit.text:mychar_exit()
The function __init init_module() references
a function __exit mychar_exit().
This is often seen when error handling in the init function
uses functionality in the exit path.
The fix is often to remove the __exit annotation of
mychar_exit() so it may be used outside an exit section.
warning直接建议remove the __exit annotation,好吧!
本文链接:https://cs.pynote.net/sf/linux/dd/202112021/
-- EOF --
-- MORE --