我们在内核中经常遇到初始化函数是这样定义的: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的更多相关文章

  1. linux内核中经常用到的设备初始化宏

    内核使用了大量不同的宏来标记具有不同作用的函数和数据结构.如宏__init.__devinit等.这些宏在include/linux/init.h头文件中定义.编译器通过这些宏可以把代码优化放到合适的 ...

  2. 浅析 Linux 初始化 init 系统

    近年来,Linux 系统的 init 进程经历了两次重大的演进,传统的 sysvinit 已经逐渐淡出历史舞台,新的 UpStart 和 systemd 各有特点,越来越多的 Linux 发行版采纳了 ...

  3. 【转】Linux 初始化 init 系统 [sysvinit systemd upstart]

    http://www.ibm.com/developerworks/cn/views/linux/libraryview.jsp?sort_by=&show_abstract=true& ...

  4. 浅析 Linux 初始化 init 系统,第 1 部分: sysvinit 第 2 部分: UpStart 第 3 部分: Systemd

    浅析 Linux 初始化 init 系统,第 1 部分: sysvinit  第 2 部分: UpStart 第 3 部分: Systemd http://www.ibm.com/developerw ...

  5. powerpc e500系列,linux初始化的tlb汇编,添加人肉代码注释

    powerpc e500的内核启动,关于tlb的初始化可以说是重头戏.看懂这段代码后,powerpc的虚实映射基本不在话下. 这段初始化tlb要考虑的,主要是将boot可能初始化过的tlb全清零,然后 ...

  6. 温故之--Linux 初始化 init 系统

    参选URL: http://www.ibm.com/developerworks/cn/linux/1407_liuming_init1/index.html 本系列一共三篇,看完记住,那水平就不一样 ...

  7. Linux 初始化系统(init)- systemd

    wikipedia 浅析 Linux 初始化 init 系统 systemd 中文手册 fedoraproject - systemd 1. Systemd 简介 Systemd 是 Linux 系统 ...

  8. 内核初始化优化宏(__init, __devinit)

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

  9. Linux内核宏DEVICE_ATTR使用

    1.前言 在Linux驱动程序编写中,使用DEVICE_ATTR宏,可以定义一个struct device_attribute设备属性,并使用sysfs的API函数,便可以在设备目录下创建出属性文件, ...

随机推荐

  1. input 输入值的监听 禁止输入特殊字符

    1.input  输入值的监听 //用于监听input的值变化(input的值产生变化才会触发事件) (function ($) { $.fn.watch = function (callback) ...

  2. 学习笔记39—笑谈FireFox标签不同步(IOS和Wiindows)

    为了解决国内用户连接 全球同步服务器 困难的问题,火狐中国版推出了 全球服务 和 本地服务 两套服务. 这两套服务的账号和数据并不通用,并且只有中国版提供了切换功能,因此当你在同步过程中遇到“未知账号 ...

  3. @Value注解分类解析

    1.1.1            @Value注解 @Value的作用是通过注解将常量.配置文件中的值.其他bean的属性值注入到变量中,作为变量的初始值. (1)常量注入 @Value(" ...

  4. Python Scrapy 爬虫框架实例(一)

    之前有介绍 scrapy 的相关知识,但是没有介绍相关实例,在这里做个小例,供大家参考学习. 注:后续不强调python 版本,默认即为python3.x. 爬取目标 这里简单找一个图片网站,获取图片 ...

  5. 主元素问题 Majority Element

    2018-09-23 13:25:40 主元素问题是一个非常经典的问题,一般来说,主元素问题指的是数组中元素个数大于一半的数字,显然这个问题可以通过遍历计数解决,时间复杂度为O(n),空间复杂度为O( ...

  6. Determine destination location of apt-get install <package>?

    https://askubuntu.com/questions/129022/determine-destination-location-of-apt-get-install-package dpk ...

  7. 并发之ThreadLocal

    ThreadLocal ThreadLocal 用一种存储变量与线程绑定的方式,在每个线程中用自己的 ThreadLocalMap 安全隔离变量,为解决多线程程序的并发问题提供了一种新的思路.   简 ...

  8. 57 ORM多表查询

    多表查询from django.db import models# Create your models here. class Author(models.Model): nid = models. ...

  9. 378. Kth Smallest Element in a Sorted Matrix(java,优先队列)

    题目: Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the ...

  10. 【转】XP系统远程桌面连接2012R2提示:远程计算机需要网络级别身份验证,而您的计算机不支持该验证

    一.背景 因对方客户的服务器是内网的,需要操作更新服务器的数据库表信息,因此远程对方客户办公司的电脑远程服务器:但是在远程桌面连接出现问题. 二.错误问题 错误问题:“远程计算机需要网络级别身份验证, ...