天天看点

【Linux API 分析】| module_init与module_exit

作者:嵌入式艺术

【Linux API 分析】module_init与module_exit

Linux版本:4.19

1、前言

module_init与module_exit用于我们驱动的加载,卸载,是我们驱动初始化/退出的入口函数。

  • module_init:内核启动时或者动态插入模块时调用
  • module_exit:驱动移除时调用

下面主要分析一下这两个接口的底层实现。

2、调用层次分析

2.1 module_init

#ifndef MODULE
 /**
  * module_init() - driver initialization entry point
  * @x: function to be run at kernel boot time or module insertion
  *
  * module_init() will either be called during do_initcalls() (if
  * builtin) or at module insertion time (if a module).  There can only
  * be one per module.
  */
 #define module_init(x)  __initcall(x);
 
 /**
  * module_exit() - driver exit entry point
  * @x: function to be run when driver is removed
  *
  * module_exit() will wrap the driver clean-up code
  * with cleanup_module() when used with rmmod when
  * the driver is a module.  If the driver is statically
  * compiled into the kernel, module_exit() has no effect.
  * There can only be one per module.
  */
 #define module_exit(x)  __exitcall(x);
 
 #else /* MODULE */
 
 /*
  * In most cases loadable modules do not need custom
  * initcall levels. There are still some valid cases where
  * a driver may be needed early if built in, and does not
  * matter when built as a loadable module. Like bus
  * snooping debug drivers.
  */
 #define early_initcall(fn)      module_init(fn)
 #define core_initcall(fn)       module_init(fn)
 #define core_initcall_sync(fn)      module_init(fn)
 #define postcore_initcall(fn)       module_init(fn)
 #define postcore_initcall_sync(fn)  module_init(fn)
 #define arch_initcall(fn)       module_init(fn)
 #define subsys_initcall(fn)     module_init(fn)
 #define subsys_initcall_sync(fn)    module_init(fn)
 #define fs_initcall(fn)         module_init(fn)
 #define fs_initcall_sync(fn)        module_init(fn)
 #define rootfs_initcall(fn)     module_init(fn)
 #define device_initcall(fn)     module_init(fn)
 #define device_initcall_sync(fn)    module_init(fn)
 #define late_initcall(fn)       module_init(fn)
 #define late_initcall_sync(fn)      module_init(fn)
 
 #define console_initcall(fn)        module_init(fn)
 #define security_initcall(fn)       module_init(fn)
 
 /* 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)));
 
 /* 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)));
 
 #endif           

2.2 __initcall

#define __initcall(fn) device_initcall(fn)
 
 #define __exitcall(fn)                      \
     static exitcall_t __exitcall_##fn __exit_call = fn           

2.3 device_initcall

#define pure_initcall(fn)       __define_initcall(fn, 0)  
   
 #define core_initcall(fn)       __define_initcall(fn, 1)  
 #define core_initcall_sync(fn)      __define_initcall(fn, 1s)  
 #define postcore_initcall(fn)       __define_initcall(fn, 2)  
 #define postcore_initcall_sync(fn)  __define_initcall(fn, 2s)  
 #define arch_initcall(fn)       __define_initcall(fn, 3)  
 #define arch_initcall_sync(fn)      __define_initcall(fn, 3s)  
 #define subsys_initcall(fn)     __define_initcall(fn, 4)  
 #define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)  
 #define fs_initcall(fn)         __define_initcall(fn, 5)  
 #define fs_initcall_sync(fn)        __define_initcall(fn, 5s)  
 #define rootfs_initcall(fn)     __define_initcall(fn, rootfs)  
 #define device_initcall(fn)     __define_initcall(fn, 6)  
 #define device_initcall_sync(fn)    __define_initcall(fn, 6s)  
 #define late_initcall(fn)       __define_initcall(fn, 7)  
 #define late_initcall_sync(fn)      __define_initcall(fn, 7s)  
   
 #define __initcall(fn) device_initcall(fn)             

2.4 ___define_initcall

#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
 #define ___define_initcall(fn, id, __sec)           \
     __ADDRESSABLE(fn)                   \
     asm(".section   \"" #__sec ".init\", \"a\"  \n" \
     "__initcall_" #fn #id ":            \n" \
         ".long  " #fn " - .         \n" \
         ".previous                  \n");
 #else
 #define ___define_initcall(fn, id, __sec) \
     static initcall_t __initcall_##fn##id __used \
         __attribute__((__section__(#__sec ".init"))) = fn;
 #endif           

2.5、module_init调用顺序汇总

module_init
     ---> __initcall
         ---> device_initcall
             ---> __define_initcall(include/linux/init.h)
                 ---> ___define_initcall(/include/linux/init.h)           

综上,我们调用顺序:module_init(fn)---> initcall(fn) ---> device_initcall(fn) ---> define_initcall(fn, 6)

3、源码分析

通过上面了解,我们最后调用的是___define_initcall的函数,下面我们主要分析该函数的意义。

了解之前呢,我们先来学习一下#与##的作用!

3.1 # 与 ## 的作用

符号 作用 举例
## “##”符号 可以是连接的意思 例如 initcall_##fn##id 为initcall_fnid那么,fn = test_init,id = 6时, initcall_##fn##id为 initcall_test_init6
# “#”符号 可以是字符串化的意思 例如 #id 为 “id”,id=6 时,#id 为“6”

3.2 __define_initcall

#define __define_initcall(fn, id) \  
     static initcall_t __initcall_##fn##id __used \  
     __attribute__((__section__(".initcall" #id ".init"))) = fn             

这里我们以module_init(test_init)为例,转换后的结果为:

static initcall_t __initcall_test_init6 __used __attribute__((__section__(".initcall6.init"))) = test_init           

通过__attribute__(__section__)设置函数属性,将test_init放在字段.initcall6.init中。

该字段通过链接器链接起来,形成一个列表进行统一管理。

......
 __initcall6_start = .; KEEP(*(.initcall6.init)) KEEP(*(.initcall6s.init)) 
 ......           
还记得__define_initcall的定义吗?
#define pure_initcall(fn)       __define_initcall(fn, 0)  
  
#define core_initcall(fn)       __define_initcall(fn, 1)  
#define core_initcall_sync(fn)      __define_initcall(fn, 1s)  
#define postcore_initcall(fn)       __define_initcall(fn, 2)  
#define postcore_initcall_sync(fn)  __define_initcall(fn, 2s)  
#define arch_initcall(fn)       __define_initcall(fn, 3)  
#define arch_initcall_sync(fn)      __define_initcall(fn, 3s)  
#define subsys_initcall(fn)     __define_initcall(fn, 4)  
#define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)  
#define fs_initcall(fn)         __define_initcall(fn, 5)  
#define fs_initcall_sync(fn)        __define_initcall(fn, 5s)  
#define rootfs_initcall(fn)     __define_initcall(fn, rootfs)  
#define device_initcall(fn)     __define_initcall(fn, 6)  
#define device_initcall_sync(fn)    __define_initcall(fn, 6s)  
#define late_initcall(fn)       __define_initcall(fn, 7)  
#define late_initcall_sync(fn)      __define_initcall(fn, 7s)  
  
#define __initcall(fn) device_initcall(fn)            

不同的宏定义,被赋予了不同的调用等级,最后将不同的驱动初始化函数统一汇总到__initcallx_start字段统一管理,形成一个有序的列表。

这样,我们在内核中,按照顺序遍历这个列表,最后执行对应的模块初始化函数fn即可实现驱动的初始化。

这篇内容主要分析module_init的调用以及作用,后续再详细分析内核是如何调用初始化函数的。
【Linux API 分析】| module_init与module_exit

继续阅读