linux版本:4.19
module_init()与module_exit()用于驱动的加载,分别是驱动的入口与退出函数
module_init()是驱动的入口函数,该函数定义在include\linux\module.h文件
/**
* 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);
__initcall(x)函数定义在include\linux\init.h文件里面
#define __initcall(fn) device_initcall(fn)
device_initcall(fn)定义也在include\linux\init.h文件里面
#define device_initcall(fn) __define_initcall(fn, 6)
__define_initcall(fn, 6)定义也在include\linux\init.h文件里面
#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
___define_initcall(fn, id, .initcall##id)定义也在include\linux\init.h文件里面
/*
* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
* by link order.
* For backwards compatibility, initcall() puts the call in
* the device init subsection.
*
* The `id' arg to __define_initcall() is needed so that multiple initcalls
* can point at the same handler without causing duplicate-symbol build errors.
*
* Initcalls are run by placing pointers in initcall sections that the
* kernel iterates at runtime. The linker can do dead code / data elimination
* and remove that completely, so the initcall sections have to be marked
* as KEEP() in the linker script.
*/
#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
综上module_init调用顺序如下:
module_init()
--->__initcall()
--->device_initcall()
--->__define_initcall()
--->___define_initcall()
对___define_initcall()函数进行分析
首先了解一下#与##的作用
符号 | 作用 | 举例 |
---|---|---|
## | “##”符号可以是连接的意思 | 例如initcall_##fn##id为initcall_fnid,假设fn为fbmem_init,id=6时,initcall_##fn##id为initcall_fbmem_init6 |
# | "#"符号可以是字符串的意思 | 例如#id为“id”,id=6时,#id为“6” |
所以module_init(fbmem_init)函数,相当于
static initcall_t __initcall_fbmem_init6 __used __attribute__((__section__(".initcall6.init"))) =fbmem_init
这行代码定义了一个静态变量__initcall_fbmem_init6,变量类型为initcall_t(是一个函数指针类型,用于表示内核的初始化函数),__used 用于告诉编译器不要将“__initcall_fbmem_Init6”变量视为未使用的变量,避免编译器优化移除它。
attribute((section(“.initcall6.init”))),编译器指令,告诉编译器将“__initcall_fbmem_init6”变量放置在‘.initcall6.init’的代码段中(这个代码段通常用于存放内核的初始化函数,在启动的时候按顺序执行,.initcall1.init、.initcall2.init 。。。)
‘= fbmem_init’ 这将’__initcall_fbmem_init6’初始化为指向‘fbmem_init’函数的指针。
总之,这段代码的作用是将‘fbmem_init’函数注册为一个初始化函数,使其在内核启动的时候执行。
对于编译成模块,执行sudo insmod的时候,是如何加载的,等我知道后再来写