咱们在内核中常常遇到初始化函数是这样定义的:static int __init init_func(); ,与普通函数相比,定义中多了__init。那么,__init是什么意思呢?还有与其匹配的__exit呢?html
__init* macrolinux
__init定义在:include/linux/init.h编程
#define __init __attribute__ ((__section__ (".init.text")))
#define __initdata __attribute__ ((__section__ (".init.data")))数组
It tells the compiler to put the variable or the function in a special section, which is declared in vmlinux.lds. init puts the function in the ".init.text" section and initdata puts the data in the ".init.data" section.
译文:__init宏告知编译器,将变量或函数放在一个特殊的区域,这个区域定义在vmlinux.lds中。__init将函数放在".init.text"这个代码区中,__initdata将数据放在".init.data"这个数据区中。数据结构
标记为初始化的函数,代表该函数供在初始化期间使用。在模块装载以后,模块装载就会将初始化函数扔掉。这样能够将该函数占用的内存释放出来。函数
__exit* macro
__exit定义在:include/linux/init.hpost
#ifdef MODULE #define __exit __attribute__ ((__section__(".exit.text"))) #else #define __exit __attribute_used__ __attribute__((__section__(".exit.text"))) #endif
The exit macro tells the compiler to put the function in the ".exit.text" section. The exit_data macro tells the compiler to put the data in the ".exit.data" section.
exit.* sections make sense only for the modules : exit functions will never be called if compiled statically. That's why there is a ifdef : exit.* sections will be discarded only if modules support is disabled.
译文:__exit宏告知编译器,将函数放在".exit.text"这个区域中。__exitdata宏则告知编译器将数据放在".exit.data"这个区域中。
exit.*区域仅仅对于模块是有用的:若是编译稳定的话,exit函数将永远不会被调用。只有当模块支持无效的时候,exit.*区域将被丢弃。这就是为何定义中会出现ifdef。ui
Prototype of a module
A module must use the init and exit macros. Here is a prototype of a module :spa
#include <linux/module.h> #include <linux/kernel.h> #define MODULE_AUTHOR "tyler@agat.net" #define MODULE_DESC "Description of the module" int __init init_function(void) { /* Do something */ if (err) return -ERR; return 0; } void __exit exit_function() { /* Do something */ } module_init(init_function); module_exit(exit_function); MODULE_LICENSE("GPL"); MODULE_AUTHOR(MODULE_AUTHOR); MODULE_DESCRIPTION(MODULE_DESC);
1)全部标识为__init的函数,在连接的时候,都放在.init.text这个区域中。在这个区域中,函数的摆放顺序是和连接顺序有关的,是不肯定的。
2)全部的__init函数在区域.initcall.init中还保存了一份函数指针。在初始化时,内核会经过这些函数指针调用这些__init函数,并在整个初始化完成后,释放整个init区域 (包括.init.text, .initcall.init...)
注:这些函数在内核初始化过程当中的调用顺序只和这里的函数指针顺序有关,和1)中所述的这些函数代码自己在.init.text区域中的顺序无关。.net
在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中的函数指针,等等。而在每一个子区域中的函数指针的顺序是和连接顺序相关的,是不肯定的。
在内核中,不一样的init函数被放在不一样的子区域中,所以也就决定了他们的调用顺序。这样也就解决了一些init函数之间必须保证必定的调用顺序问题。
linux下 container_of()宏的简要解析
ARRAY_SIZE 宏仍是比较有意思的,实际上是个c 的编程技巧,这个技巧颇有用哦!能够在include/linux/kernel.h 中找到它的定义:
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
该宏能够方便的求出一个数组中有多少数据成员,这在不少状况下是颇有用的,好比对于 int a[]={1,5,65,23,12,20,3} 数组,可使用该宏求出a[] 有7 个元素。
Linux中__init、__devinit等初始化宏
在内核里常常能够看到__init, __devinit这样的语句,这都是在init.h中定义的宏,gcc在编译时会将被修饰的内容放到这些宏所表明的section。
其典型的定义以下:
#define __init __section(.init.text) __cold notrace #define __initdata __section(.init.data) #define __initconst __section(.init.rodata) #define __exitdata __section(.exit.data) #define __exit_call __used __section(.exitcall.exit)
其典型用法以下:
static int __init xxx_drv_init(void) { return pci_register_driver(&xxx_driver); }
根据上面的定义与用法,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函数。
若是不肯定需不须要添加宏,则不要添加。
转自:http://blog.chinaunix.net/uid-24807808-id-3127876.html