找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2385

积分

0

好友

323

主题
发表于 2 小时前 | 查看: 3| 回复: 0

你是否好奇过,当你的程序调用 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 会启动一个基于菜单的配置界面。

Linux 内核 menuconfig 配置界面

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

Linux 内核 nconfig 配置界面

配置完成后,开始编译。基础编译命令是:

$ make

为了加快编译速度,可以使用 -jN 参数指定并行任务数:

$ make -j8

如果为其他架构交叉编译,则需要指定 ARCHCROSS_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)

BusyBox 静态编译选项配置界面

然后编译安装:

$ 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"

QEMU 中运行自定义内核的终端界面

至此,你有了一个可以自由修改和测试的内核环境。

开始你的第一个内核贡献

现在进入正题:如何做出并提交你的第一个修改。一个对新手友好的起点是 drivers/staging/ 目录下的代码,这里包含许多需要清理和优化的“暂存”驱动。

让我们看一个实际例子:替换一个自定义函数。
drivers/staging/dgap/dgap.c 中,有一个 dgap_sindex 函数,用于查找两个字符串中首个共同字符的位置。然而,内核的 lib/string.c 中已经有一个功能完全相同的标准函数 strpbrk()。重复造轮子并非好事,我们的任务就是移除自定义函数,改用标准函数。

  1. 创建开发分支:首先,从同步好的主分支创建一个新分支。

    $ git checkout -b “dgap-remove-dgap_sindex”
  2. 修改代码:将 dgap_sindex 的调用替换为 strpbrk,并删除 dgap_sindex 函数定义。

  3. 确保驱动配置已启用:需要在内核配置中启用该驱动,路径为:

    Device Drivers
    -> Staging drivers
       -> Digi EPCA PCI products

    内核配置中启用 Staging 驱动

  4. 提交更改:使用 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>
  5. 生成补丁:使用 git format-patch 生成补丁文件。

    $ git format-patch master

    这会生成类似 0001-staging-dgap-Use-strpbrk-instead-of-dgap_sindex.patch 的文件。

  6. 找到正确的维护者:使用脚本找到应该接收此补丁的维护者和邮件列表。

    $ ./scripts/get_maintainer.pl -f drivers/staging/dgap/dgap.c
  7. 发送补丁:使用 git send-email 发送补丁到脚本输出的邮件列表和维护者邮箱。请务必使用纯文本格式。

发送后,就是等待反馈。如果补丁被接受,维护者会将其合并到自己的树中,最终进入主线。如果收到修改意见,则需要迭代改进,并以 [PATCH v2][PATCH v3] 为前缀重新发送。

内核开发中的重要准则

在结束前,分享一些至关重要的“行”与“禁”:

  • 三思而后行:提交前反复检查你的修改。
  • 编译!编译!再编译!:任何微小修改后,都要确保内核能编译通过。无法编译的补丁没人会看。
  • 遵守代码风格:Linux 内核有严格的编码规范。务必使用 scripts/checkpatch.pl 检查你的改动。
    $ ./scripts/checkpatch.pl -f drivers/staging/dgap/dgap.c
  • 提交信息要清晰:详细描述“改了啥”和“为什么改”,这是审核和日后查阅的依据。
  • 不要发送 GitHub Pull Request:Linus Torvalds 不接受 GitHub PR,必须通过邮件列表发送补丁。
  • 拆分独立的修改:如果一次提交包含多个不相关的改动,请用 git format-patch 拆分成多个补丁。
  • 耐心等待回复:维护者非常忙碌,如果没有立即收到回复,请耐心等待。
  • 善用工具脚本scripts/ 目录下有很多宝藏脚本,例如 get_maintainer.pl, checkpatch.pl 等。
  • 订阅邮件列表:订阅 linux-kernel@vger.kernel.org 和相关的子系统列表,感受社区脉搏,了解开发动态。

希望这份指南能为你打开 Linux 内核开发的大门。实践出真知,从一个小补丁开始,你就能逐渐融入这个全球最大的开源协作项目。如果你在云栈社区或其它技术论坛遇到了志同道合的伙伴,不妨一起交流学习,共同成长。Happy Hacking!




上一篇:探索Linux C程序启动:从`_start`到`main`的幕后旅程
下一篇:深入解析链接器:虚拟内存如何让链接时地址“预言”成为可能?
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-17 08:12 , Processed in 0.648457 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表