module_init和module_exit

-- 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 --