内核初始化宏

常用的宏:
__init,标记内核启动时所用的初始化代码,内核启动完成后就不再使用。其所修饰的内容被放到.init.text section中。
__exit,标记模块退出代码,对非模块无效
__initdata,标记内核启动时所用的初始化数据结构,内核启动完成后不再使用。其所修饰的内容被放到.init.data section中。
__devinit,标记设备初始化所用的代码
__devinitdata,标记设备初始化所用的数据结构
__devexit,标记设备移除时所用的代码

xxx_initcall,7个级别的初始化函数

kernel将初始化要执行的init函数,分为7个级别,core_initcall, postcore_initcall, arch_initcall, subsys_initcall, fs_iitcall, device_initcall, late_initcall。这7个级别优先级递减,即先执行core_initcall, 最后执行late_initcall

所有标识为__init的函数在链接的时候都放在.init.text这个区段内,在这个区段中,函数的摆放顺序是和链接的顺序有关的,是不确定的

所有的__init函数在区段.initcall.init中还保存了一份函数指针,在初始化时内核会通过这些函数指针调用这些__init函数指针,并在整个初始化完成后,释放整个init区段(包括.init.text,.initcall.init等)

在2.4内核中,这些函数指针的顺序也是和链接的顺序有关的,是不确定的。
在2.6内核中,initcall.init区段又分成7个子区段,分别是
.initcall1.init
.initcall2.init
.initcall3.init
.initcall4.init
.initcall5.init
.initcall6.init
.initcall7.init
当需要把函数fn放到.initcall1.init区段时,只要声明
core_initcall(fn);
其他的各个区段的定义方法分别是:
core_initcall(fn) --->.initcall1.init
postcore_initcall(fn) --->.initcall2.init
arch_initcall(fn) --->.initcall3.init
subsys_initcall(fn) --->.initcall4.init
fs_initcall(fn) --->.initcall5.init
device_initcall(fn) --->.initcall6.init
late_initcall(fn) --->.initcall7.init
而与2.4兼容的initcall(fn)则等价于device_initcall(fn)。
各个子区段之间的顺序是确定的,即先调用.initcall1.init中的函数指针再调用.initcall2.init中的函数指针,而在每个子区段中的函数指针的顺序是和链接顺序相关的,是不确定的

driver中的使用:
module_init, module_exit函数所调用的函数,需要分别用__init和__exit来标记
pci_driver数据结构不需要标记
probe和remove函数用__devinit和__devexit来标记
如果remove使用__devexit标记,则在pci_drvier结构中要用__devexit_p(remove)来引用remove函数
如果不确定需不需要添加宏,则不要添加

我们写驱动中所用到的module_init对应的是
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
所以,驱动对应的加载的优先级为6

同一等级的优先级的驱动,加载顺序是链接过程决定的,结果是不确定的,我们无法去手动设置谁先谁后。

最后这些驱动加载的顺序,可以查看在根目录下生成的system.map

猜你喜欢

转载自blog.csdn.net/shixha/article/details/23916029