在接触过的linux kernel外设驱动中(camera sensor驱动, TP驱动),模型都是一样
#include <linux/init.h>
//......
//.......
static int __init XXX_init(void)
{
//...
}
static void __exit XXX_exit(void)
{
//...
}
module_init(XXX_init);
module_exit(XXX_exit);
MODULE_DESCRIPTION(“XXX”);
MODULE_AUTHOR("XXX");
MODULE_LICENSE("XXX");
在这里主要研究module_init,module_init()是一个宏,在 include/linux/init.h中定义.
(有两个实现,通过MODULE宏来区分;宏MODULE被定义说明这个驱动是编译成模块的,宏MODULE没有被定义说明这个驱动是编译到内核即build-in)
1. MODULE未定义 的情况 - 即该驱动是build-in到linux kernel中!
首先先把module_init(func)宏展开
static initcall_t __initcall_func6 __used __attribute__((__section__(".initcall" level ".init"))) = func;
其中initcall_t为函数指针类型,即typedef int (*initcall_t)(void);
这句语句就是定义了一个函数指针变量,变量名为__inicall_func6, 该变量赋值为func.
__attribute__((__section__(".initcall" level ".init"))) 表明要将变量__initcall_func6放到.initcall6.init输入段中
疑问:这个函数什么时候被调用的?
从以上的解释看到,这个函数指针变量是被放到.initcall6.init段中,inux kernel是怎样是怎样分段的?
在include/asm-generic/vmlinux.lds.h头文件中,可以看到时怎样分段的!
其中有一个initcall段,分为0,1,2,....7,共8个initcall段,
#define INIT_DATA_SECTION(initsetup_align) //...定义省略...
#define INIT_CALLS //...定义省略...
在init/main.c文件中的do_initcalls()函数会分别去执行放在initcall段的函数.
所以只要看do_initcalls()是什么时候被调用的,就知道在驱动文件中定义的函数指针是什么时候被调用的了!
通过看linux kernel的代码,具体的调用流程是start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls()
2.定义了MODULE - 即该驱动是被编译成模块,需要使用insmod 或 modprob 来加载模块到kernel
首先先把module_init(func)宏展开
static inline initcall_t __inittest(void)
{
return func;
}
int init_module(void) __attribute__((alias(func)));
__inittest函数是对func做类型检查;
__attribute__((alias(func))) 是为func取别名,别名为init_module.
这种情况下,什么时候会调用这个函数呢?
是在insmode时会去调用
insmod时候,在系统内部会调用sys_init_module() 去找到init_module函数的入口地址