内核源码结构:Linux内核源码目录树解析、Kconfig与Makefile工作机制、车规级芯片的BSP结构
各位同学,今天我们来啃一块硬骨头——Linux内核源码结构。说实话,我当年刚接触内核源码时,第一反应是「这玩意儿是人写的吗?」几十万个文件,上千万行代码,光目录就看得人眼花。但别怕,我带你一层层剥开它。
一、Linux内核源码目录树解析
先说说内核源码的顶层目录。你下载一份Linux内核源码,解压后看到的那些文件夹,每个都有它的使命。我习惯把它们分成三类:核心代码、架构相关代码、以及工具和文档。
| 目录 | 作用 | 我的经验 |
|---|---|---|
| arch/ | 存放不同CPU架构的代码 | 车规级芯片主要看arch/arm64/ |
| drivers/ | 设备驱动的大本营 | 我们写驱动主要在这里折腾 |
| include/ | 内核头文件 | 找数据结构定义就来这 |
| kernel/ | 核心调度、进程管理 | 一般不动,但得知道它在哪 |
| Documentation/ | 内核文档 | 说实话,有些文档比代码还难懂 |
嗯,这里要注意一个细节。arch/目录下每个子目录代表一种CPU架构。比如我们做车规级芯片,NXP S32G用的是Cortex-A53,Renesas R-Car用的是Cortex-A57,都属于arm64架构。所以你的驱动代码如果涉及底层寄存器操作,大概率要放在arch/arm64/下面。
核心观点:内核源码不是让你全部读完的。我做了十年驱动,也没读完所有代码。关键是知道「什么东西在哪个目录」,遇到问题知道去哪找。
二、Kconfig与Makefile工作机制
这两个文件,说白了就是内核的「菜单」和「菜谱」。Kconfig定义了你有哪些菜可以选,Makefile告诉你每个菜怎么做。
2.1 Kconfig——内核配置的菜单
Kconfig的语法其实不复杂。我刚开始学的时候,觉得它跟C语言宏定义差不多。你看这个例子:
config S32G_CAN
tristate "S32G CAN controller support"
depends on ARM64
help
Say Y here to support CAN controller on NXP S32G.
This driver can also be built as a module.
这里tristate表示三种状态:Y(编译进内核)、M(编译成模块)、N(不编译)。depends on表示依赖关系——你选了CAN驱动,前提是你选了ARM64架构。我在项目中遇到过有人把depends写成了select,结果编译出一堆莫名其妙的错误。
小技巧:写Kconfig时,help信息别偷懒。我见过太多驱动只有一行help,过两个月自己都忘了这个配置是干嘛的。
2.2 Makefile——内核的构建脚本
Makefile的写法更直接。你只需要告诉内核:这个文件要编译成什么。
obj-$(CONFIG_S32G_CAN) += s32g_can.o
s32g_can-objs := s32g_can_main.o s32g_can_regs.o
第一行:如果CONFIG_S32G_CAN被配置为Y或M,就编译s32g_can.o。第二行:s32g_can.o由两个源文件链接而成。说白了,Makefile就是告诉编译器「把这些文件拼起来」。
为什么会这样设计?我个人的理解是,内核要支持成千上万的硬件,不可能把所有代码都编译进去。通过Kconfig和Makefile的组合,你可以像点菜一样,只编译你需要的部分。这对车规级芯片尤其重要——嵌入式设备的存储空间就那么点,能省则省。
避坑指南:我曾经在Makefile里漏写了一个反斜杠,结果编译时提示找不到文件。查了整整一下午才发现是换行符的问题。记住:Makefile对空格和换行极其敏感,别手滑。
三、车规级芯片的BSP结构
BSP,全称Board Support Package。说白了,就是芯片厂商给你的一套「样板间」。你拿到一块开发板,厂商会给你一个BSP包,里面包含了让这块板子跑起来的最小系统。
以NXP S32G为例,它的BSP结构大致是这样的:
s32g_bsp/
├── board/
│ └── s32g274a-evb/ # 开发板配置
├── drivers/
│ ├── can/ # CAN控制器驱动
│ ├── pcie/ # PCIe控制器驱动
│ └── net/ # 以太网驱动
├── dts/ # 设备树文件
│ └── s32g274a.dtsi
├── include/
│ └── s32g_regs.h # 寄存器地址定义
└── Makefile
你想想看,如果没有BSP,你要自己从头写时钟配置、中断控制器、DMA……那得写到猴年马月去。BSP的价值就在于,它帮你把芯片厂商已经验证过的底层代码封装好了,你只需要关注你的驱动逻辑。
我记得有一次做Renesas R-Car H3的驱动,厂商给的BSP里有一个bug——GPIO中断号配错了。我花了三天定位到问题,然后给厂商提了patch。嗯,这就是做车规级驱动的日常——你不仅要会用BSP,还得能修BSP。
3.1 设备树在BSP中的角色
设备树(Device Tree)是车规级芯片BSP里最重要的部分。它用文本描述硬件资源,比如:
can0: can@401d0000 {
compatible = "nxp,s32g-can";
reg = <0x0 0x401d0000 0x0 0x1000>;
interrupts = <0 33 4>;
clocks = <&clks S32G_CLK_CAN>;
};
这段描述告诉内核:有一个CAN控制器,地址在0x401d0000,中断号是33,时钟源是S32G_CLK_CAN。驱动加载时,内核会解析这个节点,然后调用对应的probe函数。
关键点:设备树是硬件和驱动之间的「翻译官」。你写驱动时,大部分时间都在跟设备树打交道。我建议你养成一个习惯:拿到一块新板子,先看它的dts文件,比看芯片手册快得多。
四、总结一下
今天的内容,说白了就是三件事:
- 内核源码目录——知道去哪找代码
- Kconfig和Makefile——知道怎么配、怎么编
- BSP结构——知道厂商给了你什么
这些东西看起来琐碎,但都是基本功。我见过太多人一上来就写驱动代码,结果连Kconfig的依赖关系都搞不清楚,编译报错就傻眼。你先把这些基础打牢,后面写驱动才能事半功倍。
下一章,我们开始真正动手——搭建一个车规级驱动的开发环境。到时候我会拿NXP S32G的开发板做演示,手把手带你跑通第一个驱动。