阅读Linux源码:在一堆同名的头文件中找到正确的头文件
1. 提出问题
大家在阅读Linux源码时,是否会遇到类似的问题:#include后面是相对路径,无法确定具体包含的是哪个头文件。
举个例子:arch/arm64/kernel/sys.c包含的#include <asm/unistd.h>头文件,具体是哪个呢?
arch/arm64/include/asm/unistd.h
arch/arm64/include/uapi/asm/unistd.h
2. 个人经验
分享一下个人经验哈,如果大家有更好的办法可以留言。
- 先把内核编译通过,例如
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j16
- 修改一下arch/arm64/kernel/sys.c时间戳
touch arch/arm64/kernel/sys.c
- 重新编译一次,加上 V=1
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j16 V=1
此次编译会重新编译arch/arm64/kernel/sys.c文件,在编译输出中,搜索关键字sys.c可以找到完整的编译命令,如下图。我是使用mobaxterm进行搜索。
- 从上述输出中整理出-I选项,得到gcc搜索头文件的先后顺序,如下:
-I./arch/arm64/include
-I./arch/arm64/include/generated
-I./include
-I./arch/arm64/include/uapi
-I./arch/arm64/include/generated/uapi
-I./include/uapi
-I./include/generated/uapi
在编译Linux内核或用户空间程序时,使用-I
选项来指定头文件的搜索路径。这些路径的顺序代表头文件被优先使用的顺序。
3. 优先级特征分析
-
本地架构特定头文件:首先搜索的是架构特定(这里是ARM64)的头文件目录,包括生成的和非生成的:
-I./arch/arm64/include
-I./arch/arm64/include/generated
-
通用头文件:接着是通用的头文件路径,适用于所有架构:
-I./include
-
架构特定用户API头文件:然后是架构特定的用户API头文件,同样分为生成的和非生成的:
-I./arch/arm64/include/uapi
-I./arch/arm64/include/generated/uapi
-
通用用户API头文件:最后是通用的用户API头文件路径:
-I./include/uapi
-I./include/generated/uapi
优先级特征
- 先架构后通用:优先考虑架构特定的头文件,之后才是通用的。这保证了针对特定架构的优化和特性可以覆盖掉通用实现。
- 先非生成后生成:在同一级别的目录中,先查找非生成的头文件,再查找生成的头文件。这是因为生成的文件通常依赖于当前配置,并且可能包含对编译过程至关重要的信息。
- 先uapi外后uapi内:对于每个级别的目录,先查找标准头文件,然后再查找用户API(uapi)相关的头文件。这是因为uapi文件专为用户空间程序设计,而标准头文件则可能包含了更底层或更广泛的定义。
综上所述,编译器会按照上述顺序依次查找所需的头文件。如果同一个头文件出现在多个路径中,那么最先匹配到的那个会被使用。
4. 解决问题
到这里,就可以回到最初的问题了
arch/arm64/kernel/sys.c包含的#include <asm/unistd.h>头文件,具体是哪个呢?
arch/arm64/include/asm/unistd.h
arch/arm64/include/uapi/asm/unistd.h
一定是先搜索到的arch/arm64/include/asm/unistd.h !
依次类推,层层的包含关系,都会非常清楚:
arch/arm64/kernel/sys.c
->#include <asm/unistd.h>即arch/arm64/include/asm/unistd.h
->#include <uapi/asm/unistd.h>即arch/arm64/include/uapi/asm/unistd.h
->#include <asm-generic/unistd.h>即include/asm-generic/unistd.h
->#include <uapi/asm-generic/unistd.h>即include/uapi/asm-generic/unistd.h
如果你有更好的方法,可以留言讨论!感谢点赞关注!