你是否好奇过,当你的程序调用 printf 或执行 syscall 时,底层究竟发生了什么?为了探求答案,我开始深入学习 Linux 内核的源代码。在持续学习和撰写笔记九个多月后,我意外地发现,关于内核内部机制的内容非常受欢迎,并且收到了大量关于“如何开始为 Linux 内核做贡献”的询问。因此,我认为有必要写一篇指引,帮助你迈出参与这个庞大开源项目的第一步。本文将专注于“如何做”,而非“为什么做”,是一份纯实战指南。
第一步:获取与构建 Linux 内核
想要开始内核开发,首先你得有一份源代码并能运行它。通常有两种方式:在虚拟机上运行,或在真实硬件上运行。我们先从获取代码开始。
如果你只想升级现有系统内核,使用发行版的包管理器是最简单的。例如,在 Ubuntu 上更新到 4.1 内核:
$ sudo add-apt-repository ppa:kernel-ppa/ppa
$ sudo apt-get update
然后查看可用版本并安装:
$ apt-cache showpkg linux-headers
$ sudo apt-get install linux-headers-${version} linux-headers-${version}-generic linux-image-${version}-generic --fix-missing
重启后,在 GRUB 菜单中就能看到新内核。
但对于开发而言,你需要克隆源代码仓库。Linux 内核开发完全基于 git,你可以从官方仓库克隆:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
或者,从 GitHub 上的镜像仓库克隆(对许多人来说更便利):
$ git clone git@github.com:torvalds/linux.git
为了方便同步上游代码,建议将主线仓库添加为远程 upstream:
git remote add upstream git@github.com:torvalds/linux.git
之后,你通常会有两个远程仓库:origin(你的 fork)和 upstream(主线)。
配置与编译内核
获取代码后,下一步是配置。最简单的方法是复制当前系统的配置:
$ sudo cp /boot/config-$(uname -r) ~/dev/linux/.config
或者,如果内核支持,也可以从 /proc/config.gz 获取:
$ cat /proc/config.gz | gunzip > ~/dev/linux/.config
你也可以手动配置。内核根目录的 Makefile 提供了多种配置目标,例如 make menuconfig 会启动一个基于菜单的配置界面。

其他有用的配置命令包括 make x86_64_defconfig(生成 x86_64 默认配置),或 make ARCH=arm64 defconfig(为指定架构生成默认配置)。make nconfig 则提供了一个基于 ncurses 的配置界面。

配置完成后,开始编译。基础编译命令是:
$ make
为了加快编译速度,可以使用 -jN 参数指定并行任务数:
$ make -j8
如果为其他架构交叉编译,则需要指定 ARCH 和 CROSS_COMPILER 参数:
$ make -j4 ARCH=arm64 CROSS_COMPILER=aarch64-linux-gnu- defconfig
$ make -j4 ARCH=arm64 CROSS_COMPILER=aarch64-linux-gnu-
编译成功后,你会得到压缩后的内核映像文件,例如 arch/x86/boot/bzImage。
安装或运行新内核
在真实硬件上安装
编译完成后,可以安装到本地系统:
$ sudo make headers_install
$ sudo make modules_install
$ sudo make install
之后需要更新引导加载程序配置。可以使用一个简单的脚本来适配不同发行版(如 Fedora 的 grub2-mkconfig 或 Ubuntu 的 update-grub)。重启后,即可在启动菜单中选择新内核。
在虚拟机中运行(QEMU)
对于开发测试,在虚拟机中运行更为安全便捷。我们需要先构建一个初始内存磁盘镜像(initrd)。
首先,下载并编译 BusyBox 作为简易根文件系统:
$ mkdir initrd
$ cd initrd
$ curl http://busybox.net/downloads/busybox-1.23.2.tar.bz2 | tar xjf -
$ cd busybox-1.23.2
$ make menuconfig
在配置中,需要进入 Busybox Settings -> Build Options,选中 Build static binary (no shared libs)。

然后编译安装:
$ make -j4
$ sudo make install
返回 initrd 目录,创建 initramfs 结构并复制 BusyBox:
$ cd ..
$ mkdir -pv initramfs
$ cd initramfs
$ mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}
$ cp -av ../busybox-1.23.2/_install/* .
创建一个简单的 /init 脚本用于启动:
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
exec /bin/sh
为其添加执行权限,然后打包成 initrd 镜像:
$ find . -print0 | cpio --null -ov --format=newc | gzip -9 > ~/dev/initrd_x86_64.gz
现在,可以使用 QEMU 运行我们编译的内核了:
$ qemu-system-x86_64 -snapshot -m 8GB -serial stdio -kernel ~/dev/linux/arch/x86_64/boot/bzImage -initrd ~/dev/initrd_x86_64.gz -append "root=/dev/sda1 ignore_loglevel"

至此,你有了一个可以自由修改和测试的内核环境。
开始你的第一个内核贡献
现在进入正题:如何做出并提交你的第一个修改。一个对新手友好的起点是 drivers/staging/ 目录下的代码,这里包含许多需要清理和优化的“暂存”驱动。
让我们看一个实际例子:替换一个自定义函数。
在 drivers/staging/dgap/dgap.c 中,有一个 dgap_sindex 函数,用于查找两个字符串中首个共同字符的位置。然而,内核的 lib/string.c 中已经有一个功能完全相同的标准函数 strpbrk()。重复造轮子并非好事,我们的任务就是移除自定义函数,改用标准函数。
-
创建开发分支:首先,从同步好的主分支创建一个新分支。
$ git checkout -b “dgap-remove-dgap_sindex”
-
修改代码:将 dgap_sindex 的调用替换为 strpbrk,并删除 dgap_sindex 函数定义。
-
确保驱动配置已启用:需要在内核配置中启用该驱动,路径为:
Device Drivers
-> Staging drivers
-> Digi EPCA PCI products

-
提交更改:使用 git commit -s -v 提交。-s 会自动添加 Signed-off-by 行,-v 会显示变更详情。提交信息至关重要:
- 第一行(摘要):格式为
[PATCH] 子系统/目录: 简要说明。例如:[PATCH] staging/dgap: Use strpbrk() instead of dgap_sindex()。
- 正文:空一行后,详细描述修改的内容和原因。例如,说明
strpbrk() 是标准函数,可以替代自定义实现。
- 签名:最后是
Signed-off-by: Your Name <your.email@example.com>。
-
生成补丁:使用 git format-patch 生成补丁文件。
$ git format-patch master
这会生成类似 0001-staging-dgap-Use-strpbrk-instead-of-dgap_sindex.patch 的文件。
-
找到正确的维护者:使用脚本找到应该接收此补丁的维护者和邮件列表。
$ ./scripts/get_maintainer.pl -f drivers/staging/dgap/dgap.c
-
发送补丁:使用 git send-email 发送补丁到脚本输出的邮件列表和维护者邮箱。请务必使用纯文本格式。
发送后,就是等待反馈。如果补丁被接受,维护者会将其合并到自己的树中,最终进入主线。如果收到修改意见,则需要迭代改进,并以 [PATCH v2]、[PATCH v3] 为前缀重新发送。
内核开发中的重要准则
在结束前,分享一些至关重要的“行”与“禁”:
希望这份指南能为你打开 Linux 内核开发的大门。实践出真知,从一个小补丁开始,你就能逐渐融入这个全球最大的开源协作项目。如果你在云栈社区或其它技术论坛遇到了志同道合的伙伴,不妨一起交流学习,共同成长。Happy Hacking!