-- TOC --
继续来深入详解Linux驱动基础中的测试代码,module_init和module_exit。
这是两个宏函数,定义在<linux/module.h>
中:
/* Each module must use one module_init(). */
#define module_init(initfn) \
static inline initcall_t __maybe_unused __inittest(void) \
{ return initfn; } \
int init_module(void) __copy(initfn) \
__attribute__((alias(#initfn))); \
___ADDRESSABLE(init_module, __initdata);
/* This is only required if you want to be unloadable. */
#define module_exit(exitfn) \
static inline exitcall_t __maybe_unused __exittest(void) \
{ return exitfn; } \
void cleanup_module(void) __copy(exitfn) \
__attribute__((alias(#exitfn))); \
___ADDRESSABLE(cleanup_module, __exitdata);
分析完module_init,基本上module_exit也分析完了!
宏展开后,首先,定义了一个返回函数指针类型的static function,
/*
* Used for initialization calls..
*/
typedef int (*initcall_t)(void);
typedef void (*exitcall_t)(void);
__maybe_unused
是一个编译器的attribute,防止此static函数在没有被使用的时候warning。所以,在module_init后面的代码,可以使用__inittest
来直接得到模块初始化接口。但如果init接口有__init
修饰,模块加载后初始化接口会从内存中清除掉,此时这个函数的返回值什么呢?
接着,定义一个函数,固定名为init_module
,此接口是初始化函数的别名,copy所有属性。
最后,又是一个很复杂的___ADDRESSABLE
宏定义,实际上是定了一个名字唯一的符号,存放init_module
的地址,放置位置由__initdata
指定。(参考COUNTER)
所以,整个module_init
所做的事情,就是:
想必是有了这些信息,kernel就可以轻松加载模块了。
module_exit
基本一样,略。
编译好的模块的字符串表中,可以看到复杂且唯一的符号:
$ readelf -p47 hello_mod.ko
String dump of section '.strtab':
[ 1] hello_mod.c
[ d] hello_init
[ 18] hello_exit
...
[ 4b] __UNIQUE_ID___addressable_cleanup_module189
[ 77] __UNIQUE_ID___addressable_init_module188
...
本文链接:https://cs.pynote.net/sf/linux/dd/202112022/
-- EOF --
-- MORE --