注:记录网络编辑,摘编,编辑。
module_init(XX_dev_init); //..模块初始化
module_exit(XX_dev_exit); //..模块初退出
做 linux驱动,都是按照固定格式,定义一个初始化和推出函数,这两个函数会被调用,至于为什么会被调用,在哪调用,一直不清楚。分析一吧各部分的初始化函数会在一个固定的函数里调用比如:void init(void){ init_a(); init_b();} 如果再加入一个初始化函数呢,那么再init_b()后面再加一行:init_c();这样确实能完成我们的功能,但这样有一定的问题,就是不能独立的添加初始化函数,每次添加一个新的函数都要修改init函数,blob中的初始化函数就是完全独立的,只要用一个宏来修饰一下:void init_a(void){}__initlist(init_a, 1);它是通过这个宏来实现初始化函数列表的呢?先来看__initlist的定义:#define __init __attribute__((unused, __section__(".initlist")))#define __initlist(fn, lvl) /
static initlist_t __init_##fn __init = { /
magic: INIT_MAGIC, /
callback: fn, /
level: lvl }
看来就是定义了一个结构体,存了初始化函数的指针,没什么特别的。请注意:__section__(".initlist")这个属性起什么作用呢?它告诉连接器这个变量存放在.initlist区段,如果所有的初始化函数都是用这个宏,那么每个函数会有对应的一个initlist_t结构体变量存放在.initlist区段,也就是说我们可以在.initlist区段找到所有初始化函数的指针。怎么找到.initlist区段的地址呢?extern u32 __initlist_start;
extern u32 __initlist_end;
这两个变量起作用了,__initlist_start是.initlist区段的开始,__initlist_end是结束,通过这两个变量我们就可以访问到所有的初始化函数了。 这两个变量在那定义的呢?在一个连接器脚本文件里( GCC 中那段启动SECTION) . = ALIGN(4);
.initlist : {
__initlist_start = .;
*(.initlist)
__initlist_end = .;
}
这两个变量的值正好定义在.initlist区段的开始和结束地址,所以我们能通过这两个变量访问到所有的初始化函数。与此类似,内核中也是用到这种方法,所以我们写驱动的时候比较独立,不用我们自己添加代码在一个固定的地方来调用我们自己的初始化函数和退出函数,连接器已经为我们做好了。当然module_init还有其他的特性, |