本文使用的Linux源码内核版本:Linux 5.10.0
__init宏定义
在Linux的内核源码里,尤其是一些关键的内核函数,会包含__init宏,它是用来做什么的呢?
asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
那么,它的作用是什么?先去找下宏定义:
【include/linux/init.h】
#define __init __section(".init.text") __cold __latent_entropy __noinitretpoline
#define __initdata __section(".init.data")
#define __initconst __section(".init.rodata")
#define __exitdata __section(".exit.data")
#define __exit_call __used __section(".exitcall.exit")
可以看到__init宏由多个关键字组成:__section、__cold 、__latent_entropy 、__noinitretpoline。
下面单独先对这几个关键字进行分析。
__section(".init.text")的含义
__section就是GCC的一个编译特性,在编译时该带该关键字的函数,放到“.init.text”的数据段中。
这样做的目的就是将相关的函数全部放在一起,比如这里将相关的初始化时需要运行的函数全部放到".init.text"数据段,在系统运行时直接让PC指针按顺序执行这些函数。
__section关键字:
__section实际也是一个宏,它的定义如下:
#define __section(section) __attribute__((__section__(section)))
又看到了熟悉的__attribute__关键字,
__attribute__的作用:
GNU C 的一大特色就是__attribute__ 机制。
__attribute 其实是个编译器指令,告诉编译器声明的特性,或者让编译器进行更多的错误检查和高级优化。
attribute 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
attribute 书写特征是:attribute 前后都有两个下划线,并切后面会紧跟一对原括弧,括弧里面是相应的__attribute__ 参数。
attribute 语法格式为:attribute ((attribute-list)) 在使用__attribute__ 参数时,你也可以在参数的前后都加上“__” (两个下划线),例如,使用__aligned__而不是aligned
,这样,你就可以在相应的头文件里使用它而不用关心头文件里是否有重名的宏定义。
在GCC中有定义
section (“section-name”)
Normally, the compiler places the code it generates in the text section. Sometimes, however, you need additional sections, or you need certain particular functions to appear in special sections. The section attribute specifies that a function lives in a particular section. For example, the declaration:
extern void foobar (void) attribute ((section (“bar”)));
puts the function foobar in the bar section.
Some file formats do not support arbitrary sections so the section attribute is not available on all platforms. If you need to map the entire contents of a module to a particular section, consider using the facilities of the linker instead.
通常,编译器会将其生成的代码放在该text部分中。但是,有时您需要其他部分,或者需要某些特殊功能才能出现在特殊部分中。该section 属性指定函数位于特定节中。
针对该定义,我们继续分析。
__attribute__((__section__(section)))定义:
attribute((section(”name“)))是gcc编译器支持的一个编译特性,实现在编译时把某个函数/数据放到name的数据段中。
这样做的优点:
- 系统在初始化时,不需要知道有什么模块需要初始化,只需要把name数据段中的所有初始化接口执行一遍即可。
- 当系统启动完毕后,这个段中的内存会被释放掉供其他使用。
__cold宏定义:
__cold也是一个宏,它的定义如下:
/*
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-cold-function-attribute
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Label-Attributes.html#index-cold-label-attribute
*/
#define __cold __attribute__((__cold__))
熟悉的__attribute__关键字,参考上文。
__cold__的属性用于通知编译器该函数不太可能执行。
该函数针对大小而不是速度进行了优化,并且在许多目标上将其放置在文本部分的特殊子节中,因此所有冷函数都显示在一起,从而提高了程序非冷部分的代码局部性。
分支预测机制将导致代码中调用冷函数的路径标记为不太可能。因此,标记用于处理不太可能的条件(例如perror寒冷)的函数很有用,以改善在极少数情况下确实会调用标记函数的热函数的优化。
__latent_entropy宏定义:
__latent_entropy也是一个宏,它的定义如下:
【include/linux/complier-gcc.h】
#if defined(LATENT_ENTROPY_PLUGIN) && !defined(__CHECKER__)
#define __latent_entropy __attribute__((latent_entropy))
#endif
latent_entropy是gcc编译器支持的一个编译特性,它只能作用于函数和变量。
如果它是在一个函数上,那么plugin将对其进行检测。
如果属性在一个变量上,那么plugin将用一个随机值初始化它。变量必须是整型、整型数组类型或具有整型字段的结构。
__noinitretpoline宏定义:
__noinitretpoline也是一个宏,它的定义如下:
【include/linux/complier-gcc.h】
#ifdef CONFIG_RETPOLINE
#define __noretpoline __attribute__((__indirect_branch__("keep")))
#endif
/* Built-in __init functions needn't be compiled with retpoline */
#if defined(__noretpoline) && !defined(MODULE)
#define __noinitretpoline __noretpoline
#else
#define __noinitretpoline /一般情况下,定义了MODULE,就会走这个分支**/
#endif
间接分支。用于预测间接分支指令的目标地址的方法和间接分支预测器逻辑单元。
在表中存储间接分支指令的预测的目标地址,所述预测的目标地址是由之前的间接分支指令的间接路径历史与之前的条件分支指令的被采用/不被采用历史的组合进行索引的。
不过一般情况下,定义了MODULE,就会走下面的分支。(这个宏一般用不到)
参考:
https://mp.weixin.qq.com/s?__biz=MzAwMDUwNDgxOA==&mid=2652663356&idx=1&sn=779762953029c0e0946c22ef2bb0b754&chksm=810f28a1b678a1b747520ba3ee47c9ed2e8ccb89ac27075e2d069237c13974aa43537bff4fba&mpshare=1&scene=1&srcid=0111Ys4k5rkBto22dLokVT5A&pass_ticket=bGNWMdGEbb0307Tm%2Ba%2FzAKZjWKsImCYqUlDUYPZYkLgU061qPsHFESXlJj%2Fyx3VM#rd