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任务运行之前。