linux驱动 重点:module_init module_exit

来源:互联网 发布:sqlserver培训 编辑:程序博客网 时间:2024/05/20 23:07
一直以来写linux驱动,都是按照固定格式,定义一个初始化和推出函数,书上告诉我这两个函数会被调用,至于为什么会被调用,在哪调用,一直不清楚。偶然的一个机会,看到blob里面的代码,里面有一个初始化函数列表。按照一般的编程想法,各部分的初始化函数会在一个固定的函数里调用比如: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是结束,通过这两个变量我们就可以访问到所有的初始化函数了。 这两个变量在那定义的呢?在一个连接器脚本文件里 . = ALIGN(4); .initlist : { __initlist_start = .; *(.initlist) __initlist_end = .; }这两个变量的值正好定义在.initlist区段的开始和结束地址,所以我们能通过这两个变量访问到所有的初始化函数。与此类似,内核中也是用到这种方法,所以我们写驱动的时候比较独立,不用我们自己添加代码在一个固定的地方来调用我们自己的初始化函数和退出函数,连接器已经为我们做好了。当然module_init还有其他的特性,比如:我们的初始化函数在完成初始化后,代码占用的空间会被释放,这又是为什么呢?今天晚了,下次再写。本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/citytramper/archive/2006/02/16/600708.aspx