1. gcc的__attribute__编译属性

要了解Linux Kernel代码的分段信息,需要了解一下gcc的__attribute__的编绎属性,__attribute__主要用于改变所声明或定义的函数或 数据的特性,它有很多子项,用于改变作用对象的特性。比如对函数,noline将禁止进行内联扩展、noreturn表示没有返回值、pure表明函数除 返回值外,不会通过其它(如全局变量、指针)对函数外部产生任何影响。但这里我们比较感兴趣的是对代码段起作用子项section。

__attribute__的section子项的使用格式为:

__attribute__((section("section_name")))

其作用是将作用的函数或数据放入指定名为"section_name"输入段。

这里还要注意一下两个概念:输入段和输出段

输入段和输出段是相对于要生成最终的elf或binary时的Link过程说的,Link过程的输入大都是由源代码编绎生成的目标文件.o,那么这些.o 文件中包含的段相对link过程来说就是输入段,而Link的输出一般是可执行文件elf或库等,这些输出文件中也包含有段,这些输出文件中的段就叫做输 出段。输入段和输出段本来没有什么必然的联系,是互相独立,只是在Link过程中,Link程序会根据一定的规则(这些规则其实来源于Link Script),将不同的输入段重新组合到不同的输出段中,即使是段的名字,输入段和输出段可以完全不同。

其用法举例如下:

int var __attribute__((section(".xdata"))) = 0;

这样定义的变量var将被放入名为.xdata的输入段,(注意:__attribute__这种用法中的括号好像很严格,这里的几个括号好象一个也不能少。)

static int __attribute__((section(".xinit"))) functionA(void)

{

.....
}

这个例子将使函数functionA被放入名叫.xinit的输入段。

需要着重注意的是,__attribute__的section属性只指定对象的输入段,它并不能影响所指定对象最终会放在可执行文件的什么段。

2. Linux Kernel源代码中与段有关的重要宏定义

A. 关于__init、__initdata、__exit、__exitdata及类似的宏

打开Linux Kernel源代码树中的文件:include/init.h,可以看到有下面的宏定议:

#define __init __attribute__ ((__section__ (".init.text"))) __cold

#define __initdata __attribute__ (( __section__ (".init.data")))

#define __exitdata __attribute__ (( __section__ (".exit.data")))

#define __exit_call __attribute_used__ __attribute__ (( __section__ (".exitcall.exit")))

#define __init_refok oninline __attribute__ ((__section__ (".text.init.refok")))

#define __initdata_refok __attribute__ ((__section__ (".data.init.refok")))

#define __exit_refok noinline __attribute__ ((__section__ (".exit.text.refok")))

.........

#ifdef MODULE

#define __exit __attribute__ (( __section__ (".exit.text"))) __cold

#else

#define __exit __attribute_used__ __attribute__ ((__section__ (".exit.text"))) __cold

#endif

对于经常写驱动模块或翻阅Kernel源代码的人,看到熟悉的宏了吧:__init, __initdata, __exit, __exitdata。

__init 宏最常用的地方是驱动模块初始化函数的定义处,其目的是将驱动模块的初始化函数放入名叫.init.text的输入段。对于__initdata来说,用 于数据定义,目的是将数据放入名叫.init.data的输入段。其它几个宏也类似。另外需要注意的是,在以上定意中,用__section__代替了 section。还有其它一些类似的宏定义,这里不一一列出,其作用都是类似的。

B. 关于initcall的一些宏定义

在该文件中,下面这条宏定议更为重要,它是一条可扩展的宏:

#define __define_initcall(level,fn,id) \

static initcall_t __initcall_##fn##id __attribute_used__ \

__attribute__ ((__section__(".initcall" level ".init"))) = fn

这条宏带有3个参数:level,fn, id,分析该宏可以看出:

 1.其用来定义类型为initcall_t的static函数指针,函数指针的名称由参数fn和id决定:__initcall_##fn##id,这 就是函数指针的名称,它其实是一个变量名称。从该名称的定义方法我们其学到了宏定义的一种高级用法,即利用宏的参数产生名称,这要借助于"##"这一符号 组合的作用。

 2. 这一函数指针变量放入什么输入段呢,请看__attribute__ ((__section__ (".initcall" levle ".init"))),输入段的名称由level决定,如果level="1",则输入段是.initcall1.init,如果level="3s",则输入段是.initcall3s.init。这一函数指针变量就是放在用这种方法决定的输入段中的。

 3. 这一定义的函数指针变量的初始值是什么叫,其实就是宏参数fn,实际使用中,fn其实就是真实定义好的函数。

该宏定义并不直接使用,请看接下来的这些宏定义:

#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)

这些宏定义出来是为了方便的使用__define_initcall宏定义的,上面每条宏第一次使用时都会产生一个新的输入段。

接下来还有一条

#define __initcall(fn) device_initcall(fn)
这一条其实只是定义了另一个别名,即平常使用的__initcall其实就是这儿的device_initcall,用它定义的函数指定位于段.initcall6.init中。

C. __setup宏的来源及使用

__setup这条宏在Linux Kernel中使用最多的地方就是定义处理Kernel启动参数的函数及数据结构,请看下面的宏定义:

#define __setup_param(str, unique_id, fn, early) \
static char __setup_str_##unique_id[] __initdata __aligned(1) = str; \
static struct obs_kernel_param __setup_##unique_id \
__used __section(.init.setup) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }

#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)

使用Kernel中的例子分析一下这两条定义:

__setup("root=",root_dev_setup);

这条语句出现在init/do_mounts.c中,其作用是处理Kernel启动时的像root=/dev/mtdblock3之类的参数的。

分解一下这条语句,首先变为:

__setup_param("root=",root_dev_setup,root_dev_setup,0);

继续分解,将得到下面这段代吗:

static char __setup_str_root_dev_setup_id[] __initdata __aligned(1) = "root=";
static struct obs_kernel_param __setup_root_dev_setup_id
__used __section(.init.setup)
__attribute__((aligned((sizeof(long)))))
= { __setup_str_root_dev_setup_id, root_dev_setup, 0 };

这段代码定义了两个变量:字符数组变量__setup_str_root_dev_setup_id,其初始化内容为"root=",由于该变量用 __initdata修饰,它将被放入.init.data输入段;另一变量是结构变量__setup_root_dev_setup_id,其类型为 struct obs_kernel_param, 该变理被放入输入段.init.setup中。结构struct struct obs_kernel_param也在该文件中定义如下:

struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
int early;
};

变量__setup_root_dev_setup_id的三个成员分别被初始化为:

__setup_str_root_dev_setup_id --> 前面定义的字符数组变量,初始内容为"root="。

root_dev_setup --> 通过宏传过来的处理函数。

0 -->常量0,该成员的作用以后分析。

现在不难想像内核启动时怎么处理启动参数的了:通过__setup宏定义obs_kernel_param结构变量都被放入.init.setup段中, 这样一来实际是使.init.setup段变成一张表,Kernel在处理每一个启动参数时,都会来查找这张表,与每一个数据项中的成员str进行比较, 如果完全相同,就会调用该数据项的函数指针成员setup_func所指向的函数(该函数是在使用__setup宏定义该变量时传入的函数参数),并将启 动参数如root=后面的内容传给该处理函数。

attribute section的用法的更多相关文章

  1. PHP模板引擎Smarty内建函数section,sectionelse用法详解

    本文实例讲述了PHP模板引擎Smarty内建函数section,sectionelse用法.分享给大家供大家参考,具体如下: section 是 Smarty 模板中除了 foreach 以外的另一种 ...

  2. critical section的用法

    critical section Critical Section: 不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它进行访问.每个进程中访问临界资源的那段代码称为临界区(Critical ...

  3. C#中的Attribute定义及用法

    1.Attribute定义 公共语言运行时允许添加类似关键字的描述声明,叫做attributes, 它对程序中的元素进行标注,如类型.字段.方法和属性等.Attributes和Microsoft .N ...

  4. 有关C#标签Attribute的熟悉

    Attribute 简单用法: 最近用到了,所以静下心来找些资料看了一下,终于把这东西搞清楚了. 一.什么是Attribute 先看下面的三段代码: 1.自定义Attribute类:VersionAt ...

  5. C# 特性(Attribute)详细介绍

    1.什么是Atrribute 首先,我们肯定Attribute是一个类,下面是msdn文档对它的描述:公共语言运行时允许你添加类似关键字的描述声明,叫做attributes, 它对程序中的元素进行标注 ...

  6. (转)Attribute在.net编程中的应用

    Attribute在.net编程中的应用(一)Attribute的基本概念 经常有朋友问,Attribute是什么?它有什么用?好像没有这个东东程序也能运行.实际上在.Net中,Attribute是一 ...

  7. Attribute在.net编程中的应用(一)

    Attribute的基本概念 经常有朋友问,Attribute是什么?它有什么用?好像没有这个东东程序也能运行.实际上在.Net中,Attribute是一个非常重要的组成部分,为了帮助大家理解和掌握A ...

  8. [转]Attribute在.net编程中的应用

    Attribute在.net编程中的应用(一) Attribute的基本概念 经常有朋友问,Attribute是什么?它有什么用?好像没有这个东东程序也能运行.实际上在.Net中,Attribute是 ...

  9. (转)__attribute__之section 分析详解

    原文地址:__attribute__之section详解 前言 第一次接触 "section" 是在公司的一个STM32的项目代码中,前工程师将所有的初始化函数都使用的" ...

随机推荐

  1. 解决Mysql的主从数据库没有同步的两种方法

    今天发现Mysql的主从数据库没有同步 先上Master库: mysql>show processlist;   查看下进程是否Sleep太多.发现很正常.show master status; ...

  2. python 自动化之路 day 07 面向对象基础

    本节内容:   面向对象编程介绍 为什么要用面向对象进行开发? 面向对象的特性:封装.继承.多态 类.方法.   面向对象编程 OOP编程是利用"类"和"对象" ...

  3. Spring-Boot-XML-Restful-Service

    http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-write-an-xml-rest-service ...

  4. YII2数据库操作出现类似Database Exception – yii\db\Exception SQLSTATE

    yii2安装后,连接数据库,必须要安装pdo_mysql扩展

  5. html网页音乐播放器自带播放列表

    基于网页的音乐播放器demo  http://pan.baidu.com/s/1dDgm7HR 自己diy了一个手机端在线音乐播放器演示地址http://shanxi2014.com/zhuandiz ...

  6. Boost1.62+win7+VC2015编译

    下载 通过boost官方网站, 或直接在source forge下载boost_1_62_0. 可选包 Zlib library, 环境变量: ZLIB_SOURCE bzip2, 环境变量: BZI ...

  7. 从IT的角度思考BIM(二):模式与框架

    我们满怀着美好期许,鼓起勇气敲响了 BIM 世界的大门.忽然人群中有人高呼:BIM 已死,大家都散了吧! 这时人群开始骚动起来.“我早就说这玩意是忽悠人的吧,你们不信还偏要来”,“我花了好多钱准备这次 ...

  8. Windows Server以服务方式部署Tomcat

    部署免安装版Tomcat7,步骤如下: 解压Tomcat7到指定目录之后,通过命令行定位到 apache-tomcat-7.0.53\bin 目录下面,然后输入 service.bat install ...

  9. 2015年9月29日 sql 触发器

    触发器(trigger):当有关联操作的时候使用(级联操作),属于ddl关键字. eg:下订单时,创建中的商品数量要减少:退票时,总的票数要增加.         在订单上建立触发器         ...

  10. 转:浅谈命令查询职责分离(CQRS)模式

    原文来自于:http://www.cnblogs.com/yangecnu/p/Introduction-CQRS.html 在常用的三层架构中,通常都是通过数据访问层来修改或者查询数据,一般修改和查 ...