linux初始化宏__init, __exit
我们在内核中经常遇到初始化函数是这样定义的:static int __init init_func(); ,与普通函数相比,定义中多了__init。那么,__init是什么意思呢?还有与其匹配的__exit呢?
__init* macro
__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.h
#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。
Prototype of a module
A module must use the init and exit macros. Here is a prototype of a module :
#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 ;
}
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区域中的顺序无关。
在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
linux初始化宏__init, __exit的更多相关文章
- linux内核中经常用到的设备初始化宏
内核使用了大量不同的宏来标记具有不同作用的函数和数据结构.如宏__init.__devinit等.这些宏在include/linux/init.h头文件中定义.编译器通过这些宏可以把代码优化放到合适的 ...
- 浅析 Linux 初始化 init 系统
近年来,Linux 系统的 init 进程经历了两次重大的演进,传统的 sysvinit 已经逐渐淡出历史舞台,新的 UpStart 和 systemd 各有特点,越来越多的 Linux 发行版采纳了 ...
- 【转】Linux 初始化 init 系统 [sysvinit systemd upstart]
http://www.ibm.com/developerworks/cn/views/linux/libraryview.jsp?sort_by=&show_abstract=true& ...
- 浅析 Linux 初始化 init 系统,第 1 部分: sysvinit 第 2 部分: UpStart 第 3 部分: Systemd
浅析 Linux 初始化 init 系统,第 1 部分: sysvinit 第 2 部分: UpStart 第 3 部分: Systemd http://www.ibm.com/developerw ...
- powerpc e500系列,linux初始化的tlb汇编,添加人肉代码注释
powerpc e500的内核启动,关于tlb的初始化可以说是重头戏.看懂这段代码后,powerpc的虚实映射基本不在话下. 这段初始化tlb要考虑的,主要是将boot可能初始化过的tlb全清零,然后 ...
- 温故之--Linux 初始化 init 系统
参选URL: http://www.ibm.com/developerworks/cn/linux/1407_liuming_init1/index.html 本系列一共三篇,看完记住,那水平就不一样 ...
- Linux 初始化系统(init)- systemd
wikipedia 浅析 Linux 初始化 init 系统 systemd 中文手册 fedoraproject - systemd 1. Systemd 简介 Systemd 是 Linux 系统 ...
- 内核初始化优化宏(__init, __devinit)
在内核里经常可以看到__init, __devinit这样的语句,这都是在init.h中定义的宏,gcc在编译时会将被修饰的内容放到这些宏所代表的section. 原文地址:http://blog.c ...
- Linux内核宏DEVICE_ATTR使用
1.前言 在Linux驱动程序编写中,使用DEVICE_ATTR宏,可以定义一个struct device_attribute设备属性,并使用sysfs的API函数,便可以在设备目录下创建出属性文件, ...
随机推荐
- [原][osgEarth]添加自由飞行漫游器
//头文件里 #define MANIPULATOR_W 0x01#define MANIPULATOR_A 0x02#define MANIPULATOR_S 0x04#define MANIPUL ...
- vuex深入理解 modules
一.什么是module? 背景:在Vue中State使用是单一状态树结构,应该的所有的状态都放在state里面,如果项目比较复杂,那state是一个很大的对象,store对象也将对变得非常大,难于管理 ...
- PCB板的三种敷铜方法解析
1 do not pour over all same net objects:仅仅对相同网络的焊盘进行连接,其他如覆铜.导线不连接. 2 pour over all same net objects ...
- openvswitch vlan下的虚拟机与物理机通信
1,安装openvswitch ,图形界面显示等用到的安装包. yum install libvirt openvswitch python-virtinst xauth tigervnc -y 2, ...
- AtCoder Beginner Contest 113 D Number of Amidakuji
Number of Amidakuji 思路:dp dp[i][j]表示经过(i, j) 这个点的方案数 然后一层一层地转移, 对于某一层, 用二进制枚举这一层的连接情况, 判断连接是否符合题意, 然 ...
- IOException parsing XML document from class path resource [WebRoot/WEB-INF/applicationContext.xml];
parsing XML document from class path resource [applicationContext.xml]; nested exception is java.io. ...
- python - argparse 模块学习
python - argparse 模块学习 设置一个解析器 使用argparse的第一步就是创建一个解析器对象,并告诉它将会有些什么参数.那么当你的程序运行时,该解析器就可以用于处理命令行参数. 解 ...
- 关于怎么在CSDN中修改代码行中字体的颜色
先吐槽一下自己的心路历程吧,自己现在也是在CSDN中发表了自己好几篇的原创博文,但每一篇博文自己总感觉怪怪的,就是说不出自己哪里有毛病呢,知道今天恍然大悟,原来自己的代码行真心丑的要死,没有呈现出在编 ...
- TStringList 常用方法与属性
/TStringList 常用方法与属性 :varList: TStringList;i: Integer;begin List := TStringList.Create;List.Add('Str ...
- python基础之函数参数,名称空间,以及函数嵌套
函数进阶内容梗概: 1. 函数参数--动态传参 2. 名称空间, 局部名称空间, 全局名称空间, 作⽤用域, 加载顺序. 3. 函数的嵌套 4. gloabal , nonlocal 关键字 1. 函 ...