深入探究Linux内核的配置与构建
1. 内核Makefile设置
在
kernel/kernel
目录下存在一个标准的
Makefile
文件,其中包含大量的
make
目标。默认情况下,内核会针对运行该
Makefile
的架构进行构建,大多数情况是某种x86变体。但我们要为ARM目标板进行交叉编译。
可以通过设置环境变量
ARCH
来选择构建的架构,有两种方式实现:
-
命令行设置
:在调用
make
时设置,例如
make ARCH=arm
。
-
编辑Makefile文件
:使用喜欢的编辑器打开
kernel/kernel
中的
Makefile
文件,找到第195行,原内容为
ARCH ?= $(SUBARCH)
,将其修改为
ARCH ?= arm
。
下一行定义了
CROSS_COMPILE
变量,它通过为工具名添加前缀来标识交叉工具链。我们的ARM交叉工具链前缀为
arm-linux-
,可以在这里输入,也可以在配置过程中指定。完成修改后保存文件并退出编辑器。
执行
make help
命令,会看到大量按类别划分的
make
目标。我们当前关注的是
Architecture specific targets (arm)
类别,其中有许多以
_defconfig
结尾的目标名,这些目标用于为各种基于ARM的开发板生成默认配置文件。不过,BeagleBone不在默认配置列表中,需要执行以下命令:
cp ../configs/beaglebone arch/arm/configs/beaglebone_defconfig
接着执行
make beaglebone_defconfig
,可能会看到一些关于“recursive dependency detected!”的“错误”,根据
beagleboard.org
论坛的说法,这些错误并无大碍,执行完成后会生成默认配置文件
.config
。
2. 内核配置方式
构建内核的过程始于调用执行配置过程的
make
目标,常见的有以下三种方式:
-
make config
:启动一个基于文本的脚本,会依次引导你完成每个配置选项。每个选项通常有三到四个选择,三个选择分别是“y”(是)、“n”(否)和“?”(请求帮助),默认选择以大写显示。部分选项还有第四个选择“m”,表示将该功能构建为可加载的内核模块,而非直接构建到内核映像中。内核模块是动态扩展内核的一种方式,对设备驱动等非常有用。不过,
make config
的问题在于非常繁琐,现代内核有超过2000个选项,而通常我们只需要更改少数选项,其余保持默认状态,但该方式会强制你逐个处理每个选项。
-
make menuconfig
:基于
ncurses
库,会弹出一个伪图形界面,如图所示。配置选项按类别分组,你只需访问需要更改的选项类别即可。界面说明清晰且较为直观,但它不是真正的图形程序,鼠标无法使用。与
make config
一样,也能获取帮助文本。退出主菜单时,会提示你是否保存新配置。
-
make xconfig
:这是作者认为使用起来最方便的方式,它会弹出一个基于X Windows的菜单。使用此选项需要运行X Windows,并且安装
g11
、Qt图形库和Qt开发工具包。在默认显示模式下,
xconfig
会在左侧显示配置类别树,选择其中一个类别会在右上角窗口显示该类别的选项,选择某个选项则会在右下角窗口显示该选项的帮助信息。大多数选项以复选框形式呈现,点击复选框可在选中(已选择)和未选中(未选择)状态之间切换,这称为二进制选项。可构建为内核模块的选项有第三种状态,用圆点表示该功能将作为模块构建,这是三态选项。部分选项有除了“是”或“否”之外的一组允许值,用单选按钮表示;还有一些选项需要输入数值,例如“General setup”下的“kernel log buffer size”,双击该选项会在两个右侧窗口之间弹出一个对话框用于输入数值。如果某些选项因其他选项未被选中而不可用,则不会显示。
以下是配置方式的对比表格:
| 配置方式 | 界面类型 | 鼠标支持 | 操作便捷性 |
| ---- | ---- | ---- | ---- |
| make config | 文本界面 | 不支持 | 繁琐 |
| make menuconfig | 伪图形界面 | 不支持 | 较便捷 |
| make xconfig | 图形界面 | 支持 | 最便捷 |
3. 尝试xconfig配置
熟悉内核配置选项的最佳方法是启动
xconfig
查看。操作步骤如下:
cd kernel/kernel
make xconfig
经过一段时间的程序和文件构建后,会出现菜单。浏览子菜单及其各种子子菜单,感受Linux内核功能的灵活性和丰富性,并阅读帮助说明。
大多数关键配置选项在默认的
.config
文件中应该已正确设置,但有一个选项尚未设置,即在“General setup”下的“Cross-compiler tool prefix”,需要将其设置为“arm-linux-”。同时,确保在“Network File Systems”下选择“NFS client support”和“Root file system on NFS”。默认配置启用了许多暂时不需要的选项,例如BeagleBone Black没有内置无线支持,因此可以安全地移除蓝牙和所有与“Wireless”相关的选项。
在阅读后续内容时,暂时保持配置菜单打开。
make xconfig
在保存配置方面提供了额外的灵活性,你可以使用“File -> Save As”选项将配置保存到自定义命名的文件中,之后可以使用“File -> Load”加载该文件进行进一步修改或将其设为新的默认配置。
4. xconfig选项介绍
xconfig
菜单的“Option”菜单中有几个有用的选项:
-
Show Name
:在右上角面板添加一列,显示每个配置变量的名称。
-
Show Range
:显示二进制和三态选项的范围和当前值,作者认为该选项作用不大。
-
Show Data
:对于二进制和三态选项,其功能与“Show Range”基本重复,但对数值和文本选项可能有帮助。
-
Show All Options
:该选项很容易理解,它会以灰色显示所有因某些条件不满足而无法选择的选项。如果你确定某个选项存在但找不到,可以启用此选项,然后使用“Edit -> Find”搜索其名称。
-
Show Prompt Options
:只显示在满足依赖关系的情况下你可以更改的选项。
完成配置后,关闭
xconfig
菜单,系统会询问你是否保存或丢弃更改。
5..config文件说明
真正的内核开发者往往更喜欢使用
make menuconfig
而不是
make xconfig
。无论使用哪种配置方式,最终都会生成一个名为
.config
的文件,其中包含所有配置变量。文件顶部有注释提示“# Automatically generated file; DO NOT EDIT”,手动编辑由工具生成的文件被认为是不好的做法,因为很可能会导致问题。
未选择的选项会保留在文件中,但会被“注释掉”,已选择的选项会被赋予相应的值,如“y”表示将功能构建到内核映像中,“m”表示构建为模块,数值选项则为具体数字,或从列表中选择的元素。
.config
文件会被包含在
Makefile
中,用于控制构建内容和方式。在构建过程早期,
.config
文件会被转换为C头文件
include/linux/autoconf.h
,以便配置变量可用于控制条件编译。
6. 构建过程中遇到的问题及解决方法
在构建刚刚打补丁并配置好的内核时,可能会遇到问题。
arch/arm/kernel/return_address.c
文件无法编译,原因是该文件中包含的一个函数在
arch/arm/include/asm/ftrace.h
中也被定义为内联函数。通过网络搜索发现,问题是内核在
gcc
版本6下无法构建,建议回退到
gcc
版本4.9。
但即使构建了
gcc 4.9
工具链,仍然会出现相同的问题。查看
ftrace.h
文件可以发现,存在一个基于几个配置变量的条件判断,最终将
return_address()
函数声明为内联函数而非简单的原型。真正的问题在于这些配置变量由
depends
和
selects
控制,使用
make xconfig
很难更改它们。
解决方法是编辑
ftrace.h
文件,通过注释掉条件判断部分,只保留函数原型。以下是修改后的代码:
// #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
/*
* return_address uses walk_stackframe to do it's work. If both
* CONFIG_FRAME_POINTER=y and CONFIG_ARM_UNWIND=y walk_stackframe uses un wind
* information. For this to work in the function tracer many functions would
* have to be marked with __notrace. So for now just depend on
* !CONFIG_ARM_UNWIND.
*/
void *return_address(unsigned int);
//#else
//static inline void *return_address(unsigned int level)
//{
//
return NULL;
//}
//#endif
需要注意的是,修改内核源代码需要谨慎,因为更改内核代码可能会在未来引发其他问题,但在这种情况下,该方法似乎可行。
7. 内核构建流程
内核的实际构建过程会根据构建目标的不同而略有差异,下面分别介绍为目标板和工作站构建内核的过程。
7.1 为目标板构建内核
为目标板构建内核的步骤如下:
1.
清理中间文件(可选)
:执行
make clean
命令,该命令会删除之前构建过程中生成的所有中间文件,确保所有内容都根据当前配置选项进行构建。实际上,如果是首次构建,由于没有中间文件需要清理,所以可以不执行此命令。
make clean
-
构建内核和模块
:执行
make命令,这是构建过程的核心步骤,会构建可执行的内核映像和所有内核模块,此过程可能需要一些时间。构建完成后,压缩后的内核映像位于arch/$(ARCH)/boot/zImage。
make
-
安装内核模块(需root权限)
:执行
make modules_install INSTALL_MOD_PATH=/export/rootfs命令,将内核模块复制到$(INSTALL_MOD_PATH)/lib/modules/<kernel_version>目录,其中<kernel_version>是标识所构建特定内核的字符串。需要注意的是,为目标板构建时,必须明确指定根文件系统,否则模块会被安装到工作站文件系统的/lib/modules目录。严格来说,在这种情况下,此步骤并非必需,因为启动内核不需要任何模块。
sudo make modules_install INSTALL_MOD_PATH=/export/rootfs
构建过程是递归的,内核源代码树中的每个子目录都有自己的
Makefile
,用于处理该目录中的源文件,顶层
Makefile
会递归调用所有子
Makefile
。
7.2 为工作站构建内核
为工作站构建内核的过程与为目标板构建略有不同,具体步骤如下:
1.
安装内核模块(需root权限)
:执行
make modules_install
命令,将内核模块安装到工作站的文件系统中。
sudo make modules_install
-
安装内核(需root权限)
:执行
make install命令,该命令会完成以下几件事:-
将内核可执行文件(对于x86架构构建为
vmlinuz)和链接器映射文件System.map复制到/boot目录。 -
在
/boot/grub/grub.conf文件中添加一个新条目,以便GRand统一引导加载程序(GRUB)可以将新内核作为启动选项提供。 -
在
/boot目录中创建一个初始RAM磁盘initrd。
-
将内核可执行文件(对于x86架构构建为
sudo make install
8. 工作站相关说明
在继续加载和测试新内核之前,有必要对工作站上的两个重要组件进行简要说明。
8.1 GRUB引导加载程序
如今,大多数工作站安装都包含一个名为GRUB(GRand Unified Bootloader)的引导加载程序,用于选择要启动的特定内核或替代操作系统。拥有启动多个内核映像的能力非常重要,例如,如果你构建了一个新内核但无法正常启动,可以随时返回并启动已知能正常工作的映像,然后尝试找出新内核的问题所在。如果你对此感兴趣,可以以root用户身份查看工作站上的
/boot/grub2/grub.cfg
文件。
8.2 初始RAM磁盘(initrd)
大多数Linux内核都设置为使用
initrd
(initial ramdisk的缩写)。初始RAM磁盘是一个非常小的Linux文件系统,由引导加载程序加载到RAM中,并在内核启动时挂载,在挂载主根文件系统之前使用。使用
initrd
的常见原因是在挂载根分区之前需要加载一些内核模块,这些模块通常用于支持根分区使用的文件系统(如
ext3
),或者用于支持硬盘连接的控制器(如SCSI或RAID)。
9. 启动新内核
构建好新内核后,有两种方式可以测试它:
-
将新映像加载到闪存中
:可以将新内核映像添加到现有的内核映像中,或者替换当前的内核映像。
-
通过TFTP网络启动新映像
:这在开发环境中非常有利,因为可以快速测试新内核映像,而无需进行耗时的闪存烧录过程。
通过TFTP网络启动新内核映像的具体操作步骤如下:
1.
移动内核映像
:将
arch/arm/boot/zImage
文件移动到TFTP目录(如
/var/lib/tftpboot
),可以通过对
uEnv.txt
文件进行小修改来实现。
2.
修改uEnv.txt文件
:复制
uEnv.txt
为
uEnvnet.txt
,并使用编辑器打开。注意文件底部以
netboot =
开头的行,该命令通过TFTP将内核映像加载到内存中,它相当于当前用于从eMMC闪存加载内核映像的
loadkernel
命令。在
uenvcmd
中,将
loadkernel
替换为
netboot
。
cp uEnv.txt uEnvnet.txt
# 编辑uEnvnet.txt,将loadkernel替换为netboot
-
保存并启动
:将
uEnvnet.txt保存到eMMC的引导分区,将开发板启动到u-boot提示符,将bootenv更改为uEnvnet.txt,然后执行boot命令。内核启动后,执行uname -a命令,会发现内核映像的时间戳与构建时间一致。
# 假设已经将uEnvnet.txt保存到eMMC的引导分区
# 进入u-boot提示符
setenv bootenv uEnvnet.txt
boot
uname -a
完成上述操作后,你就成功成为一名Linux开发者了!
10. 内核配置背后的原理
虽然这部分信息对于构建内核的过程并非必需,但当你进行设备驱动开发或其他内核增强工作(甚至是对内核进行修改)时,就需要修改控制配置过程的文件。
所有配置菜单中的信息都由一组名为
Kconfig
的文本文件提供,这些文件分散在整个源代码树中。
Kconfig
文件是用配置语言编写的脚本文件,看起来很像shell脚本语言,但并不完全相同。主
Kconfig
文件位于
linux/arch/$(ARCH)
目录下,其中
ARCH
是
Makefile
中标识基础架构的变量。
以
kernel/arch/arm
目录下的
Kconfig
文件为例,找到大约第249行的
menu “System Type”
行,将该文件的结构与从“System Type”开始的配置菜单进行比较,会发现模式非常清晰。每个配置选项由关键字
config
后跟选项的符号名称标识,例如
config MMU
,这些选项会被转换为
Makefile
变量或宏,如
CONFIG_MMU
。
Makefile
使用这些变量来确定要在内核中包含哪些组件,并将
#define
符号传递给源代码。
以下是内核配置与构建的流程图:
graph TD
A[设置ARCH和CROSS_COMPILE] --> B[生成默认配置文件]
B --> C{选择配置方式}
C -->|make config| D[文本配置]
C -->|make menuconfig| E[伪图形配置]
C -->|make xconfig| F[图形配置]
D --> G[构建内核]
E --> G
F --> G
G --> H[清理中间文件]
H --> I[构建内核和模块]
I --> J{构建目标}
J -->|目标板| K[安装模块到目标板根文件系统]
J -->|工作站| L[安装模块到工作站]
L --> M[安装内核到工作站]
K --> N[启动新内核]
M --> N
通过以上步骤和说明,你可以全面了解Linux内核的配置、构建和启动过程,希望这些内容对你有所帮助。
深入探究Linux内核的配置与构建
11. 总结与注意事项
在整个Linux内核的配置、构建和启动过程中,有许多关键要点需要我们牢记,以下为大家总结梳理:
| 阶段 | 关键操作 | 注意事项 |
|---|---|---|
| 配置 |
选择合适的配置方式(
make config
、
make menuconfig
、
make xconfig
),设置关键选项如“Cross - compiler tool prefix”为“arm - linux - ”,确保“NFS client support”和“Root file system on NFS”被选中
|
避免使用
make config
处理大量选项时的繁琐;使用
make xconfig
需安装相关依赖;手动编辑
.config
文件要谨慎
|
| 构建 |
为目标板构建时执行
make clean
、
make
、
make modules_install INSTALL_MOD_PATH=/export/rootfs
;为工作站构建时执行
make modules_install
和
make install
|
首次构建可跳过
make clean
;为目标板构建时需指定根文件系统;修改内核源代码要谨慎,可能引发后续问题
|
| 启动 |
通过TFTP网络启动新内核,移动内核映像,修改
uEnv.txt
文件
|
确保TFTP目录设置正确,
uEnvnet.txt
文件修改无误
|
同时,我们还需要注意以下几点:
-
内核源代码修改
:修改内核源代码是一个需要极为谨慎的操作,因为这可能会在未来引发难以预料的问题。在修改之前,一定要充分了解相关代码的功能和影响。
-
依赖关系
:内核配置选项之间存在复杂的依赖关系,某些选项的选择会影响其他选项的可用性。在进行配置时,要仔细阅读帮助信息,确保满足依赖条件。
-
权限问题
:在安装内核模块和内核时,通常需要root权限。在执行相关命令时,要确保自己具有足够的权限,避免因权限不足导致操作失败。
12. 常见问题及解决办法
在Linux内核的配置、构建和启动过程中,可能会遇到各种问题,下面为大家列举一些常见问题及相应的解决办法:
12.1 配置阶段问题
-
找不到特定选项
:如果确定某个选项存在但找不到,可以在
make xconfig中启用“Show All Options”选项,然后使用“Edit -> Find”搜索其名称。 - 选项不可用 :某些选项可能因为其他选项未被选中而不可用。检查相关依赖选项,确保满足其条件。
12.2 构建阶段问题
-
编译错误
:如
arch/arm/kernel/return_address.c文件无法编译,可能是由于配置变量或编译器版本问题。可以尝试回退到合适的编译器版本,或者像前面提到的那样编辑相关头文件。 - 依赖错误 :如果出现依赖错误,检查配置选项是否正确,确保所有依赖的模块和库都已正确安装。
12.3 启动阶段问题
-
内核无法启动
:可能是内核配置不正确、内核映像损坏或引导参数错误。检查配置选项,确保内核映像完整,检查
uEnv.txt或其他引导配置文件是否正确。 -
网络启动失败
:检查TFTP服务是否正常运行,内核映像是否正确移动到TFTP目录,
uEnvnet.txt文件中的网络启动命令是否正确。
以下是问题与解决办法的对应表格:
| 问题类型 | 具体问题 | 解决办法 |
| ---- | ---- | ---- |
| 配置阶段 | 找不到特定选项 | 启用“Show All Options”,使用“Edit -> Find”搜索 |
| 配置阶段 | 选项不可用 | 检查相关依赖选项,确保满足条件 |
| 构建阶段 | 编译错误 | 回退编译器版本,编辑相关头文件 |
| 构建阶段 | 依赖错误 | 检查配置选项,确保依赖模块和库已安装 |
| 启动阶段 | 内核无法启动 | 检查配置、内核映像和引导参数 |
| 启动阶段 | 网络启动失败 | 检查TFTP服务、内核映像位置和启动命令 |
13. 进阶操作建议
对于已经掌握了基本的内核配置与构建的开发者来说,还可以尝试一些进阶操作,进一步提升对Linux内核的理解和应用能力。
13.1 定制内核功能
根据实际需求,有针对性地定制内核功能。例如,如果设备不需要某些功能,可以在配置阶段将其禁用,以减小内核体积,提高系统性能。可以通过
make xconfig
等配置方式,仔细筛选每个配置选项,去除不必要的功能。
13.2 开发内核模块
学习开发内核模块,这是动态扩展内核功能的重要方式。可以从简单的设备驱动模块开始,逐步深入了解内核模块的开发流程和技术。开发内核模块需要掌握一定的内核编程知识和工具,如
insmod
、
rmmod
等命令用于加载和卸载模块。
13.3 性能优化
对内核进行性能优化,提高系统的响应速度和稳定性。可以通过调整内核参数、优化内存管理、调整调度算法等方式实现。例如,合理设置内核日志缓冲区大小、调整内存分配策略等。
以下是进阶操作的步骤列表:
1.
定制内核功能
:
- 进入内核源代码目录。
- 执行
make xconfig
打开配置界面。
- 仔细筛选配置选项,禁用不需要的功能。
- 保存配置并重新构建内核。
2.
开发内核模块
:
- 学习内核编程基础知识。
- 编写内核模块代码。
- 使用
make
命令编译模块。
- 使用
insmod
命令加载模块,
rmmod
命令卸载模块。
3.
性能优化
:
- 了解内核参数和调度算法。
- 调整相关参数,如内核日志缓冲区大小、内存分配策略等。
- 重新构建并启动内核,测试性能提升效果。
14. 未来发展趋势
随着技术的不断发展,Linux内核也在不断演进,未来可能会呈现以下发展趋势:
14.1 支持更多硬件平台
随着硬件技术的不断创新,新的硬件平台不断涌现。Linux内核将继续加强对各种硬件平台的支持,包括新兴的物联网设备、人工智能芯片等,以满足不同领域的需求。
14.2 强化安全性
安全问题一直是操作系统领域的重要关注点。未来,Linux内核将进一步强化安全性,采用更先进的安全机制和技术,如加密技术、访问控制等,保护系统免受各种安全威胁。
14.3 提高性能和效率
为了适应日益增长的计算需求,Linux内核将不断优化性能和效率。通过改进调度算法、内存管理等方面,提高系统的响应速度和资源利用率。
14.4 融合新技术
随着人工智能、大数据等新技术的发展,Linux内核可能会融合这些新技术,为用户提供更强大的功能和服务。例如,利用人工智能技术优化内核调度,提高系统性能。
以下是未来发展趋势的表格总结:
| 发展趋势 | 具体内容 |
| ---- | ---- |
| 支持更多硬件平台 | 加强对物联网设备、人工智能芯片等新硬件平台的支持 |
| 强化安全性 | 采用加密技术、访问控制等先进安全机制 |
| 提高性能和效率 | 改进调度算法、内存管理等方面 |
| 融合新技术 | 融合人工智能、大数据等新技术,优化内核功能 |
15. 总结
通过本文的介绍,我们详细了解了Linux内核的配置、构建、启动过程,以及背后的原理和常见问题的解决办法。同时,我们还探讨了进阶操作建议和未来发展趋势。希望这些内容能够帮助大家更好地掌握Linux内核技术,在实际开发和应用中发挥更大的作用。
在学习和实践过程中,要不断积累经验,勇于尝试新的技术和方法,逐步提升自己的能力。相信随着不断的探索和实践,大家能够在Linux内核领域取得更好的成果。
最后,再次提醒大家,在进行内核开发和修改时,一定要谨慎操作,确保系统的稳定性和安全性。祝愿大家在Linux内核的学习和应用中取得成功!
超级会员免费看
2

被折叠的 条评论
为什么被折叠?



