Chinaunix首页 | 论坛 | 博客
  • 博客访问: 385311
  • 博文数量: 96
  • 博客积分: 647
  • 博客等级: 上士
  • 技术积分: 490
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-29 22:15
文章分类
文章存档

2015年(1)

2014年(10)

2013年(26)

2012年(59)

我的朋友

分类: LINUX

2013-03-02 10:52:33

原文地址:linux内核驱动宏定义 作者:bough22

宏符号
linux内核中绝大多数初始化函数和变量都利用了各式各样的宏符号,形如;
static int __init pci_porc_init(void){
...

static char version[] __devinitdata=drv_name".."
module_init(...)
其中__init,module_init,__devinitdata即便宏符号
宏符号有什么作用?
1)科学利用内存,许多初始化函数只在启用阶段利用内存,启用告终后就不再利用内存了,因而能够穿越一种宏来符号这种个性,在启用告终后释放这局部内存
2)判别动态模块加载和静态内核编译,在宏符号揭示之前,要想使某段代码扶持动态模块加载必需在代码中增加许多繁琐的ifndef model h.himuno.com...而自2.6引入宏符号尔后这些繁琐的代码可统统利用容易的宏加以符号,内核会依据相应的宏动态推断。
绝大多数宏都在include/linux/init.h中定义,而这些宏许多都是用于告送连接器把这些具有特异属性lo.rialogo.com的代码或数据构造放到特异的、专用的内存区。这么做,内核能够以一种容易的措施很轻率拜会这一类具有特异属性的对象。__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实例放到专程的内存区(看最顶上的图)这么在穿越代码审查后,你能够确信你的代码中不再存在这类讹谬了。 

在内核里经常可以看到__init, __devinit这样的语句,这都是在init.h中定义的宏,gcc在编译时会将被修饰的内容放到这些宏所代表的section。

其典型的定义如下:
  1. #define __init        __section(.init.text) __cold notrace
  2. #define __initdata    __section(.init.data)
  3. #define __initconst    __section(.init.rodata)
  4. #define __exitdata    __section(.exit.data)
  5. #define __exit_call    __used __section(.exitcall.exit)
其典型用法如下:
  1. static int __init xxx_drv_init(void)
  2. {
  3.      return pci_register_driver(&xxx_driver);
  4. }
根据上面的定义与用法,xxx_drv_init()函数将会被link到.init.text段。
之所以加入这样的宏,原因有2:
1,一部分内核初始化机制依赖与它。如kernel将初始化要执行的init函数,分为7个级别,core_initcall, postcore_initcall, arch_initcall, subsys_initcall, fs_iitcall, device_initcall, late_initcall。这7个级别优先级递减,即先执行core_initcall, 最后执行late_initcall。通过使用文中提到的宏,gcc会将初始化代码按下面的结构安排:
在内核初始化时,从__initcall_start到__initcall_end之间的initcall被一次执行。
2,提高系统效率
初始化代码的特点是,在系统启动时运行,且一旦运行后马上推出内存,不再占用内存。

================================================================================
常用的宏:
  • __init,标记内核启动时所用的初始化代码,内核启动完成后就不再使用。其所修饰的内容被放到.init.text section中。
  • __exit,标记模块退出代码,对非模块无效
  • __initdata,标记内核启动时所用的初始化数据结构,内核启动完成后不再使用。其所修饰的内容被放到.init.data section中。
  • __devinit,标记设备初始化所用的代码
  • __devinitdata,标记设备初始化所用的数据结构
  • __devexit,标记设备移除时所用的代码
  • xxx_initcall,7个级别的初始化函数
==================================================================================
driver中的使用:
  • module_init, module_exit函数所调用的函数,需要分别用__init和__exit来标记
  • pci_driver数据结构不需要标记
  • probe和remove函数用__devinit和__devexit来标记
  • 如果remove使用__devexit标记,则在pci_drvier结构中要用__devexit_p(remove)来引用remove函数
  • 如果不确定需不需要添加宏,则不要添加
阅读(1983) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~