C 很好地解释了uuu读取uu大部分、uuuu初始化、uuuu退出宏
C 很好地解释了uuu读取uu大部分、uuuu初始化、uuuu退出宏,c,gcc,linux-kernel,C,Gcc,Linux Kernel,\u的宏扩展主要为: #define __read_mostly __attribute__((__section__(".data..read_mostly")) 这一个来自cache.h \uuu init: #define __init __section(.init.text) __cold notrace #define __exit __section(.exit.text) __exitused __cold notrace 从init.h
\u的宏扩展主要为
:
#define __read_mostly __attribute__((__section__(".data..read_mostly"))
这一个来自cache.h
\uuu init
:
#define __init __section(.init.text) __cold notrace
#define __exit __section(.exit.text) __exitused __cold notrace
从init.h
\u退出
:
#define __init __section(.init.text) __cold notrace
#define __exit __section(.exit.text) __exitused __cold notrace
在网上搜索之后,我没有找到任何关于这个问题的好解释
那里发生了什么
附加问题:我听说过各种“链接器魔术”
用于内核开发。任何信息
关于这一点,我将非常高兴
我对这些宏的功能有一些想法。与\uuu init
类似,应该指示初始化后可以删除功能代码\uu read\u大多数情况下
用于指示数据很少被写入,从而最大限度地减少缓存未命中。但是我不知道他们是如何做到的。我的意思是它们是gcc
扩展。所以在理论上,它们可以用小用户的c代码来演示
更新1:
我试着用任意的节名测试\uuuuuuuuuuuuuu
。测试代码:
#include <stdio.h>
#define __read_mostly __attribute__((__section__("MY_DATA")))
struct ro {
char a;
int b;
char * c;
};
struct ro my_ro __read_mostly = {
.a = 'a',
.b = 3,
.c = NULL,
};
int main(int argc, char **argv) {
printf("hello");
printf("my ro %c %d %p \n", my_ro.a, my_ro.b, my_ro.c);
return 0;
}
现在,没有\u read\u宏,汇编代码基本保持不变
这就是区别
--- rm.S 2012-07-17 16:17:05.795771270 +0600
+++ rw.S 2012-07-17 16:19:08.633895693 +0600
@@ -1,6 +1,6 @@
.file "ro.c"
.globl my_ro
- .section MY_DATA,"aw",@progbits
+ .data
.align 16
.type my_ro, @object
.size my_ro, 16
所以本质上只创建了一个小节,没有什么特别之处
甚至objdump disassmbly也没有显示任何差异
因此,我对它们的最后结论是,链接器的工作是为标有特殊名称的数据部分做些什么。我认为linux内核使用某种定制的链接器脚本来实现这些功能
关于\u read\u的一点主要是
,放在那里的数据可以以某种方式进行分组和管理,以减少缓存未命中
lkml的某个人提交了一个补丁来删除\uuu read\u大部分。这引发了一场关于\uu read\u主要是的优点和缺点的精彩讨论
以下是链接:
我将在\uu init
和\uu exit
上发布进一步的更新
更新2
这些宏\uuuuu init
、\uuu exit
和\uuu read
将数据的内容(在\uu read\u大多数情况下)和文本(在\uuuu init
和\uu exit
的情况下)放入自定义命名部分。这些部分由链接器使用。现在,由于各种原因,链接器没有用作其默认行为,因此使用链接器脚本来实现这些宏的目的
可以了解如何使用自定义链接器脚本来消除死代码(链接器链接到但从未执行的代码)。这个问题在嵌入式场景中非常重要。本文档讨论如何微调链接器脚本以删除死代码:elinux.org/images/2/2d/ELC2010-gc-sections\u Denys\u Vlasenko.pdf
在内核中,可以找到初始链接器脚本include/asm generic/vmlinux.lds.h
。这不是最后的脚本。这是一种起点,链接器脚本针对不同平台进行了进一步修改
快速查看此文件,即可立即找到感兴趣的部分:
#define READ_MOSTLY_DATA(align) \
. = ALIGN(align); \
*(.data..read_mostly) \
. = ALIGN(align);
似乎此部分使用的是“.data..readmostry”部分
您还可以找到\u init
和\u exit
与节相关的链接器命令:
#define INIT_TEXT \
*(.init.text) \
DEV_DISCARD(init.text) \
CPU_DISCARD(init.text) \
MEM_DISCARD(init.text)
#define EXIT_TEXT \
*(.exit.text) \
DEV_DISCARD(exit.text) \
CPU_DISCARD(exit.text) \
MEM_DISCARD(exit.text)
链接似乎是一件非常复杂的事情:)是一种向编译器提供超出语言本身规范的指令的通用机制
列出的宏的常用功能是使用,其描述如下:
节
属性指定函数位于特定节中。例如,宣言:
extern void foobar (void) __attribute__ ((section ("bar")));
将函数foobar放入bar部分
那么,把东西放在一个部分意味着什么呢?对象文件分为以下几个部分:.text
用于可执行机器代码,data
用于读写数据,rodata
用于只读数据,用于初始化为零的数据,
等。这些部分的名称和用途取决于平台惯例,一些特殊部分只能使用\uuuu属性(section))
语法从C访问
在您的示例中,您可以猜到,.data..read\u mostary
是.data
的一个子部分,用于主要读取的数据.init.text
是一个文本(机器代码)部分,将在程序初始化时运行,等等
在Linux上,决定如何处理各个部分是内核的工作;当用户空间请求执行程序时,它将逐节读取程序映像并进行适当处理:。数据
节映射为读写页,。rodata
为只读,。text
为仅执行,等。大概在程序启动之前执行.init.text
;这可以由内核或放在程序入口点的用户空间代码来完成(我猜是后者)
如果您想查看这些属性的效果,一个好的测试是使用-S
选项运行gcc以输出汇编代码,其中将包含节指令。然后,您可以使用和不使用section指令来运行汇编程序,并使用objdump
甚至十六进制转储生成的对象文件来查看其区别。据我所知,这些宏仅由内核使用。理论上,它们可以应用于用户空间,但我不认为是这样。他们都将相似的变量分组,并为不同的效果编码在一起
初始化/退出
设置内核需要很多代码;这发生在任何用户空间运行之前。即,在init任务运行之前。