宏标记
linux内核中绝大多数初始化函数和变量都使用了各种各样的宏标记,形如;
static int __init pci_porc_init(void){
...
}
static char version[] __devinitdata=drv_name".."
module_init(...)
其中__init,module_init,__devinitdata就是宏标记
宏标记有什么作用?
1)合理利用内存,很多初始化函数只在启动阶段使用内存,启动完成后就不再使用内存了,因此可以通过一种宏来标记这种特性,在启动完成后释放这部分内存
2)区分动态模块加载和静态内核编译,在宏标记出现之前,要想使某段代码支持动态模块加载需要在代码中添加众多繁琐的ifndef model ...而自2.6引入宏标记以后这些繁琐的代码可统统使用简单的宏加以标记,内核会根据相应的宏动态判断。
绝大多数宏都在include/linux/init.h中定义,而这些宏很多都是用于告送连接器把这些具有特殊属性的代码或数据结构放到特殊的、专用的内存区。这样做,内核能够以一种简单的方式很容易访问这一类具有特殊属性的对象。
__init 启动时初始化函数:用于启动阶段后期不再需要的函数,启动完释放这部分代码所占的空间
__exit 相关内核组件卸载时调用,卸载后释放
core_initcall 启动时需要执行的初始化函数
postcore_initcall .....
.....
device_initcall
...
late_initcall
__initcall device_initcall的别名
__exitcall 与__exit类似
__initdata 仅在启动时用于已初始化的数据结构
__exitdata
设备初始化函数宏
_devinit 用于标记初始化设备的函数 如pci_driver->probe函数 (当内核在编译时不支持热插拔,则由__devinit修饰的函数在启动阶段结束时不再需要了,因此,当不支持热插拔时__devinit变成了__init的别名)
_devexit 用于标记设备卸载时调用的函数(当pci驱动被编译进内核且不支持热插拔时,由__devexit标记的函数pci_driver->remove因为不需要而被丢弃。当模块被加载到不支持模块卸载时也被丢弃)
_devexit_p 用于初始化由__devexit标记的函数的指针。如果内核既支持模块也支持热插拔,__devexit_p(fn)返回fn,否则返回null
_devinitdata 当不支持热插拔时,数据也只在启动时需要,通常,在设备初始化时,设备驱动程序也用这个宏标记pci_driver->probe函数搜索到的字符串。如pci设备驱动用__devinitdata标记pci_device_id表,一旦启动结束且不支持热插拔,内核将不再需要这个表。
_devexitdata
以e100为例补充pci_driver 结构(对照上一章) 注意其中的各种宏
.....{....
static int _ _devinit e100_probe(struct pci_dev *pdev,const struct pci_device_id *ent){
...
}
static void _ _devexit e100_remove(struct pci_dev *pdev){
...
}
#ifdef CONFIG_PM
static int e100_suspend(struct pci_dev *pdev, u32 state){
...
}
static int e100_resume(struct pci_dev *pdev){
...
}
#endif
static struct pci_driver e100_driver = {
.name = DRV_NAME,
.id_table = e100_id_table,
.probe = e100_probe,
.remove = _ _devexit_p(e100_remove),
#ifdef CONFIG_PM
.suspend = e100_suspend,
.resume = e100_resume,
#endif
};
static int _ _init e100_init_module(void){
...
return pci_module_init(&e100_driver);
}
static void _ _exit e100_cleanup_module(void){
pci_unregister_driver(&e100_driver);
}
module_init(e100_init_module); //#define module_init(x) __initcall(x)
module_exit(e100_cleanup_module); //module_exit ......__exitcall
}
启动时传入内核配置选项
linux允许用户在启动阶段传递内核配置选项给内核,这些配置都是在parse_args()中处理的,parse_args()是解析具有形如“变量=值”的输入字符串的函数,它查找关键字并调用相应的处理函数。在加载模块、解析模块命令行参数时,parse_args也会调用。
内核用_setup宏来注册关键字及相关的处理函数 如 __setup(string,func)
内核将_setup宏的两个输入参数放入obs_kernel_param类型的数据结构中,
struct obs_kernel_param{
const char *str; //关键字
int (*setup_func)(char *) //处理函数
int early;
}
_setup实际上是__setup_param的别名,__setup_param宏会把所有的obs_kernel_params实例放到专门的内存区(看最顶上的图)
阅读(1145) | 评论(0) | 转发(0) |