attribute用法ction部分

更新时间:2023-08-03 11:41:57 阅读: 评论:0

attribute⽤法ction部分
1. gcc的__attribute__编译属性
要了解Linux Kernel代码的分段信息,需要了解⼀下gcc的__attribute__的编绎属性,__attribute__主要⽤于改变所声明或定义的函数或数据的特性,它有很多⼦项,⽤于改变作⽤对象的特性。⽐如对函数,noline将禁⽌进⾏内联扩展、noreturn表⽰没有返回值、pure表明函数除返回值外,不会通过其它(如全局变量、指针)对函数外部产⽣任何影响。但这⾥我们⽐较感兴趣的是对代码段起作⽤⼦项ction。
__attribute__的ction⼦项的使⽤格式为:
__attribute__((ction("ction_name")))
其作⽤是将作⽤的函数或数据放⼊指定名为"ction_name"输⼊段。
这⾥还要注意⼀下两个概念:输⼊段和输出段
输⼊段和输出段是相对于要⽣成最终的elf或binary时的Link过程说的,Link过程的输⼊⼤都是由源代码编绎⽣成的⽬标⽂件.o,那么这些.o ⽂件中包含的段相对link过程来说就是输⼊段,⽽Link的输出⼀般是可执⾏⽂件elf或库等,这些输出⽂件中也包含有段,这些输出⽂件中的段就叫做输出段。输⼊段和输出段
百草园和三味书屋本来没有什么必然的联系,是互相独⽴,只是在Link过程中,Link程序会根据⼀定的规则(这些规则其实来源于Link Script),将不同的输⼊段重新组合到不同的输出段中,即使是段的名字,输⼊段和输出段可以完全不同。
其⽤法举例如下:
布氏菌病int  var __attribute__((ction(".xdata"))) = 0;
这样定义的变量var将被放⼊名为.xdata的输⼊段,(注意:__attribute__这种⽤法中的括号好像很严格,这⾥的⼏个括号好象⼀个也不能少。)
static int __attribute__((ction(".xinit"))) functionA(void)
{
.....
}
这个例⼦将使函数functionA被放⼊名叫.xinit的输⼊段。
需要着重注意的是,__attribute__的ction属性只指定对象的输⼊段,它并不能影响所指定对象最终会放在可执⾏⽂件的什么段。
2. Linux Kernel源代码中与段有关的重要宏定义
A. 关于__init、__initdata、__exit、__exitdata及类似的宏
打开Linux Kernel源代码树中的⽂件:include/init.h,可以看到有下⾯的宏定议:
#define __init  __attribute__ ((__ction__ (".")))  __cold
#define __initdata  __attribute__ (( __ction__ (".init.data")))
#define __exitdata  __attribute__ (( __ction__ (".exit.data")))
#define __exit_call  __attribute_ud__ __attribute__ (( __ction__ (".it")))
#define __init_refok  oninline __attribute__ ((__ction__ (".fok")))
#define __initdata_refok __attribute__ ((__ction__ (".fok")))
#define __exit_refok noinline __attribute__ ((__ction__ (".fok")))
肖申克的救赎台词
.........
#ifdef MODULE
#define __exit  __attribute__ (( __ction__ ("."))) __cold
#el
#define __exit __attribute_ud__ __attribute__ ((__ction__ ("."))) __cold
#endif
对于经常写驱动模块或翻阅Kernel源代码的⼈,看到熟悉的宏了吧:__init, __initdata, __exit, __exitdata。
__init 宏最常⽤的地⽅是驱动模块初始化函数的定义处,其⽬的是将驱动模块的初始化函数放⼊名叫.的输⼊段。对于__initdata来说,⽤于数据定义,⽬的是将数据放⼊名叫.init.data的输⼊段。其它⼏个宏也类似。另外需要注意的是,在以上定意中,⽤__ction__代替了ction。还有其它⼀些类似的宏定义,这⾥不⼀⼀列出,其作⽤都是类似的。
B. 关于initcall的⼀些宏定义
在该⽂件中,下⾯这条宏定议更为重要,它是⼀条可扩展的宏:
#define  __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __attribute_ud__ \
__attribute__ ((__ction__(".initcall" level ".init"))) = fn
这条宏带有3个参数:level,fn, id,分析该宏可以看出:
 1.其⽤来定义类型为initcall_t的static函数指针,函数指针的名称由参数fn和id决定:__initcall_##fn##id,这就是函数指针的名称,它其实是⼀个变量名称。从该名称的定义⽅法我们其学到了宏定义的⼀种⾼级⽤法,即利⽤宏的参数产⽣名称,这要借助于"##"这⼀符号组合的作⽤。
 2.  这⼀函数指针变量放⼊什么输⼊段呢,请看__attribute__ ((__ction__ (".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. __tup宏的来源及使⽤
__tup这条宏在Linux Kernel中使⽤最多的地⽅就是定义处理Kernel启动参数的函数及数据结构,请看下⾯的宏定义:
#define __tup_param(str, unique_id, fn, early)  \
static char __tup_str_##unique_id[] __initdata __aligned(1) = str; \
网购英语static struct obs_kernel_param __tup_##unique_id \
__ud __ction(.init.tup)  \
__attribute__((aligned((sizeof(long))))) \
= { __tup_str_##unique_id, fn, early }
#define __tup(str, fn)    \
__tup_param(str, fn, fn, 0)
使⽤Kernel中的例⼦分析⼀下这两条定义:
__tup("root=",root_dev_tup);
这条语句出现在init/do_mounts.c中,其作⽤是处理Kernel启动时的像root=/dev/mtdblock3之类的参数的。
分解⼀下这条语句,⾸先变为:
__tup_param("root=",root_dev_tup,root_dev_tup,0);
继续分解,将得到下⾯这段代吗:
static char __tup_str_root_dev_tup_id[] __initdata __aligned(1) = "root=";
static struct obs_kernel_param __tup_root_dev_tup_id
赞美母爱的诗歌__ud __ction(.init.tup)
__attribute__((aligned((sizeof(long)))))
= { __tup_str_root_dev_tup_id, root_dev_tup, 0 };
这段代码定义了两个变量:字符数组变量__tup_str_root_dev_tup_id,其初始化内容为"root=",由于该变量⽤__initdata修饰,它将被放⼊.init.data输⼊段;另⼀变量是结构变量__tup_root_dev_tup_id,其类型为struct obs_kernel_param, 该变理被放⼊输⼊段.init.tup中。结构struct struct obs_kernel_param也在该⽂件中定义如下:
struct obs_kernel_param {
const char *str;
int (*tup_func)(char *);
int early;
};
变量__tup_root_dev_tup_id的三个成员分别被初始化为:有必要的英文
__tup_str_root_dev_tup_id --> 前⾯定义的字符数组变量,初始内容为"root="。凉拌菜有哪些
root_dev_tup --> 通过宏传过来的处理函数。
0 -->常量0,该成员的作⽤以后分析。
现在不难想像内核启动时怎么处理启动参数的了:通过__tup宏定义obs_kernel_param结构变量都被放⼊.init.tup段中,这样⼀来实际是使.init.tup段变成⼀张表,Kernel在处理每⼀个启动参数时,都会来查找这张表,与每⼀个数据项中的成员str进⾏⽐较,如果完全相同,就会调⽤该数据项的函数指针成员tup_func所指向的函数(该函数是在使⽤__tup宏定义该变量时传⼊的函数参数),并将启动参数如root=后⾯的内容传给该处理函数。

本文发布于:2023-08-03 11:41:57,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1106515.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:定义   变量   函数   参数   启动   指定   函数指针
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图