kernel

本文最后更新于:2025年6月7日 晚上

kernel

kernel docs

linux内核源码

  • git获取指定版本的linux内核源码

  • https://www.kernel.org/

    1
    2
    3
    4
    Clone
    git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
    https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
    https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux.git

  • Linux kernel source tree

    1
    2
    3
    Clone
    https://github.com/torvalds/linux.git
    git@github.com:torvalds/linux.git

  • Linux kernel stable tree mirror

    1
    2
    3
    Clone
    https://github.com/gregkh/linux.git
    git@github.com:gregkh/linux.git

  • https://mirrors.tuna.tsinghua.edu.cn/help/linux-stable.git/

    如需克隆 Linux Stable 代码,使用:

    1
    git clone https://mirrors.tuna.tsinghua.edu.cn/git/linux-stable.git

    若要将 mirror 加入已有代码库,可在已有仓库中运行:

    1
    git remote add mirror https://mirrors.tuna.tsinghua.edu.cn/git/linux-stable.git

    或运行:

    1
    git remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/linux-stable.git

    将默认上游设置为镜像站。

DebianKernel

Debian Linux Kernel Handbook:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 克隆 Debian 内核团队的 Linux 内核源代码仓库到本地
git clone https://salsa.debian.org/kernel-team/linux.git

# 切换到 debian/6.1.99-1 分支,并基于该分支创建一个本地分支 debian/6.1.99-1
git checkout -b debian/6.1.99-1 debian/6.1.99-1

# 显示当前分支的版本描述,确认是否正确切换到 6.1.99-1 分支
git describe

# 使用远程上游 Linux 内核仓库生成 orig tarball(已注释,选择其一)
#debian/bin/genorig.py https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

# 使用本地上游内核仓库(~/code/linux)生成 orig tarball,生成 ../linux_6.1.99.orig.tar.xz
debian/bin/genorig.py ~/code/linux

# 运行 debian/rules 的 orig 目标,将 orig tarball 与 Debian 打包文件整合
debian/rules orig

# 运行 debian/rules 的 debian/control 目标,生成 debian/control 文件,定义要构建的包
debian/rules debian/control

# 安装当前目录(.)的构建依赖,确保所有编译所需的工具和库都已安装
apt build-dep .

# 构建二进制包(.deb),不生成源码包(-b),不签名(-uc -us),使用 32 线程并行编译(-j32)
#dpkg-buildpackage -b -uc -us -j32

# 生成所有二进制包,包括内核、文档、源码等
debian/rules binary

# 使用32线程编译,仅生成当前架构(如 amd64)的二进制包
FEATURESET=none DEB_BUILD_OPTIONS=parallel=32 debian/rules binary-arch

# debian/build,setup none、clond、rt
make -f debian/rules.gen setup_amd64

# debian/build,setup none
make -f debian/rules.gen setup_amd64_none_amd64

# 仅编译none架构的内核
make -f debian/rules.gen build-arch_amd64_none_amd64

# 仅将none内核打成deb包
make -f debian/rules.gen binary-arch_amd64_none_amd64

# 仅编译perf deb包
make -f debian/rules.gen binary-arch_amd64_real_perf

1
2
3
4
# cd ~/code/linux,合并多个config配置文件,前面的setup也会调用scripts/kconfig/merge_config.sh
scripts/kconfig/merge_config.sh debian/config/config debian/config/kernelarch-x86/config debian/config/amd64/config

make bindeb-pkg -j32

openeuler

wsl

  • https://github.com/microsoft/WSL2-Linux-Kernel

  • https://learn.microsoft.com/en-us/community/content/wsl-user-msft-kernel-v6

  • https://learn.microsoft.com/en-us/windows/wsl/wsl-config#configure-global-options-with-wslconfig

  • SHA and HMAC algs using patches

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    + sha512hmac ./vmlinuz-5.10.0-136.12.0.90.ctl3.x86_64
    libkcapi - Error: Netlink error: cannot open netlink socket
    libkcapi - Error: Netlink error: cannot open netlink socket
    libkcapi - Error: NETLINK_CRYPTO: cannot obtain cipher information for hmac(sha512) (is required crypto_user.c patch missing? see documentation)
    Allocation of hmac(sha512) cipher failed (ret=-93)
    error: Bad exit status from /var/tmp/rpm-tmp.cDI4EA (%install)


    RPM build errors:
    line 162: It's not recommended to have unversioned Obsoletes: Obsoletes: kernel-tools-libs
    line 177: It's not recommended to have unversioned Obsoletes: Obsoletes: kernel-tools-libs-devel
    Bad exit status from /var/tmp/rpm-tmp.cDI4EA (%install)

    wsl2内核开启CONFIG_CRYPTO_USER可修复此bug:

    1
    2
    3
    4
    5
    make menuconfig KCONFIG_CONFIG=Microsoft/config-wsl
    CONFIG_CRYPTO_USER=y # 启用CONFIG_CRYPTO_USER
    make -j$(nproc) KCONFIG_CONFIG=Microsoft/config-wsl
    make modules_install headers_install
    cp arch/x86/boot/bzImage /mnt/c/Users/17895/BzImage

    1
    2
    3
    # 编辑C:\Users\17895\.wslconfig
    [wsl2]
    kernel=C:\\Users\\17895\\bzImage

  • 使用 Sparse VHD 优化 WSL2 磁盘空间占用的技术分享

  • WSL 中的高级设置配置

    1
    2
    3
    4
    5
    [wsl2]

    # Enable experimental features
    [experimental]
    sparseVhd=true
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    PS C:\Users\17895> wsl -l
    适用于 Linux 的 Windows 子系统分发:
    Ubuntu-24.04 (默认)
    docker-desktop
    PS C:\Users\17895> wsl -l -v
    NAME STATE VERSION
    * Ubuntu-24.04 Stopped 2
    docker-desktop Stopped 2
    PS C:\Users\17895> wsl --shutdown
    PS C:\Users\17895> wsl --manage Ubuntu-24.04 --set-sparse true
    正在进行转换,这可能需要几分钟时间。
    操作成功完成。
    PS C:\Users\17895> wsl --manage docker-desktop --set-sparse true
    正在进行转换,这可能需要几分钟时间。
    操作成功完成。

others

linux内核邮件列表

Linux 内核相关网站的出现顺序:

  1. LKML.org
    • 简介:LKML(Linux Kernel Mailing List)是 Linux 内核开发者的主要讨论平台。邮件列表自 1991 年 Linux 内核项目开始时就存在,但 LKML.org 作为便于访问和搜索的网页形式出现时间稍晚,具体时间不详。
    • 网址https://lkml.org/
  2. LWN.net
    • 成立时间:1998 年
    • 简介:LWN.net 是一个提供 Linux 和开源软件相关新闻和文章的网站。它涵盖了广泛的技术主题,并提供深入的分析。
    • 网址https://lwn.net/
  3. Patchwork
    • 成立时间:2000 年代初
    • 简介:Patchwork 由 OzLabs 创建,用于管理邮件列表中的补丁。它帮助开发者跟踪和处理提交的补丁。
    • 网址https://patchwork.ozlabs.org/
  4. lore.kernel.org
    • 成立时间:2018 年
    • 简介:lore.kernel.org 是一个托管和搜索 Linux 内核相关邮件列表的平台,提供强大的搜索和索引功能,使开发者更容易找到相关讨论和补丁。
    • 网址https://lore.kernel.org/

Git邮件向Linux社区提交内核补丁

内核源码结构

内核源码在线阅读

Linux 0.11

Linux 启动过程

内核启动参数

日志级别

debug

1
dyndbg='file fs/xfs/* +p' debug

在 Linux 内核启动参数中,debug 是一个 全局调试开关,它会显著增加内核运行时输出的日志量。以下是启用 debug 参数后,内核会输出的 主要日志类型 及其作用:


1. 内核通用日志(所有子系统)

日志级别:强制设置为 KERN_DEBUG(级别 7),输出所有优先级 ≤7 的日志。 • 包含内容: • 内核初始化流程(如 CPU 检测、内存映射)。 • 设备驱动加载(如存储控制器、USB、GPU)。 • 中断和调度事件。 • 文件系统挂载(如 ext4XFSbtrfs)。 • 网络协议栈初始化。


2. 动态调试日志(Dynamic Debug)

触发条件debug 会隐式激活部分内核模块的 动态调试输出(需模块支持)。 • 典型日志: • 驱动探测(probe)和初始化细节。 • 硬件寄存器读写(如 PCIe 设备配置)。 • 文件系统元数据操作(如 XFS 的 inode 读写)。 • 块设备 I/O 请求(如磁盘读写队列)。


3. 关键子系统日志
(1) 存储和文件系统

• SCSI/SATA/NVMe 磁盘检测。 • LVM/RAID 设备组装。 • 文件系统挂载错误(如 superblock 损坏)。 • 加密卷解锁(如 LUKS)。

(2) 网络

• 网卡驱动加载(如 igbe1000e)。 • IP 地址分配(DHCP 或静态)。 • 防火墙规则初始化(如 iptables/nftables)。

(3) 电源管理

• CPU 频率调整(cpufreq)。 • ACPI 事件(如休眠/唤醒)。


4. initramfs 阶段日志(需配合 rd.debug

如果同时使用 rd.debug,还会输出 initramfs 内部的详细操作: • 设备节点创建(udev 规则执行)。 • 存储解密(如 cryptsetup)。 • 根文件系统挂载尝试。 • dracut 脚本的执行流程。


5. 日志示例(实际输出片段)
1
2
3
4
5
[    0.123456] DEBUG: CPU0: Intel(R) Core(TM) i7-9750H, 2600 MHz
[ 0.234567] DEBUG: pci 0000:00:1f.2: [8086:15d7] type 00 class 0x010601
[ 1.345678] DEBUG: XFS (sda1): Mounting V5 Filesystem
[ 1.456789] DEBUG: scsi 2:0:0:0: Direct-Access ATA SSD 2TB 1.23 PQ: 0 ANSI: 5
[ 2.567890] DEBUG: systemd[1]: Reading config file /etc/systemd/system.conf

6. 注意事项
  1. 日志量极大
    debug 会导致日志刷屏,建议通过串口控制台或 journalctl -k 捕获。
  2. 性能影响
    生产环境慎用,可能降低启动速度。
  3. 需结合过滤工具
    使用 grepdmesg --level=debug 聚焦关键问题:
    1
    dmesg | grep -i "error\|fail\|warn\|xfs\|scsi"

7. 与其他参数对比
参数 作用 日志范围
debug 强制输出所有可用调试日志 全局(内核 + 支持动态调试的模块)
loglevel=7 仅设置日志级别(不强制模块输出) 依赖模块是否主动生成调试日志
dyndbg="file xxx +p" 显式激活特定模块的调试日志 仅指定模块(如 fs/xfs/*

总结

debug 输出的日志:覆盖内核全局事件、驱动初始化、存储/网络操作等,优先级 ≤7 的所有信息。 • 适用场景
• 系统启动失败、硬件兼容性问题、驱动加载异常。
• 需快速获取最大信息量时(无需逐一手动启用模块调试)。
优化建议
若日志过多,可结合 dyndbg 限制范围(如 debug dyndbg="file fs/xfs/* +p")。

dynamic debug

dyndbg="QUERY", module.dyndbg="QUERY", or ddebug_query="QUERY"三者的区别?

If foo module is not built-in, foo.dyndbg will still be processed at boot time, without effect, but will be reprocessed when module is loaded later. dyndbg_query= and bare dyndbg= are only processed at boot.

  • foo.dyndbg:

    如果 foo 模块不是内核的一部分,而是在运行时可加载的模块,foo.dyndbg 的设置会在系统启动时被处理,但在此时不会产生实际效果。这是因为 foo 模块尚未加载,所以动态调试设置在这个时候并不影响任何东西。

    当稍后加载 foo 模块时,foo.dyndbg 的设置会被重新处理并生效。因此,动态调试设置在模块加载后才真正生效。

  • dyndbg_query= 和 dyndbg=:

    这两种设置(全局动态调试设置)只在系统启动时被处理,而不会在系统运行时重新处理。一旦系统进入运行状态,这些设置就不再生效。

    这意味着如果你想要在系统运行时动态调整调试设置,你应该使用模块特定的设置,比如 foo.dyndbg,而不是全局设置。

    综合来说,foo.dyndbg 在系统启动时会被处理,但不会在 foo 模块加载前生效。而 dyndbg_query= 和dyndbg= 这两种设置只在系统启动时生效,进入系统后不再生效。

在grub中新增 dyndbg 参数:

1
dyndbg='module phytium_dc_drm +p; module snd_soc_phytium_i2s +p; module snd_soc_pmdk_es8388 +p; module snd_soc_pmdk_es8336 +p; module snd_soc_pmdk_dp +p; file *usb* +p'
1
update-grub

进入系统后:

1
echo 'module phytium_dc_drm +p; module snd_soc_phytium_i2s +p; module snd_soc_pmdk_es8388 +p; module snd_soc_pmdk_es8336 +p; module snd_soc_pmdk_dp +p; file *usb* +p' > /sys/kernel/debug/dynamic_debug/control

printk

grub

grub doc

grub命令

/etc/defualt/grub

/etc/default/grub示例配置:

1
2
3
4
5
6
GRUB_DEFAULT=saved
GRUB_SAVEDEFAULT=true
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX=""

开机进入GRUB修复
  • 使用ls命令列出分区:(hd0),(hd1),(hd1,gpt3),(hd1,gpt2),(hd1,gpt1)
  • 检查发现vmlinuzinitramfs文件位于(hd1,gpt2)分区。
  • 查看(hd0,gpt3)/etc/fstab,确认根分区(/)挂载在/dev/sda3
  • 最终目标:手动加载内核并启动系统。

  1. 列出所有分区,确认文件位置

    1
    2
    grub> ls
    (hd0),(hd1),(hd1,gpt3),(hd1,gpt2),(hd1,gpt1)
    检查(hd1,gpt2)分区,确认包含vmlinuzinitramfs文件:
    1
    grub> ls (hd1,gpt2)/
    假设输出显示vmlinuzinitramfs存在。

  2. 设置根分区为包含内核的分区
    将GRUB的root变量设置为(hd1,gpt2),因为内核文件在此分区:

    1
    grub> set root=(hd1,gpt2)

  3. 查找真正的根分区挂载点
    查看(hd0,gpt3)上的/etc/fstab文件,确认系统的根分区(/):

    1
    grub> cat (hd0,gpt3)/etc/fstab
    输出显示/dev/sda3挂载到/,例如:
    1
    2
    # / was on /dev/sda3 during installation
    UUID=xxxx-xxxx-xxxx-xxxx / ext4 defaults 0 1

  4. 加载Linux内核
    指定内核文件路径和根分区:

    1
    grub> linux /vmlinuz root=/dev/sda3 ro

    • /vmlinuz 是内核文件(假设位于(hd1,gpt2)/vmlinuz)。
    • root=/dev/sda3 指定根分区。
    • ro 表示以只读模式启动。
  5. 加载initramfs文件
    指定初始内存盘文件:

    1
    grub> initrd /initramfs.img

    • /initramfs.img 是initramfs文件(假设位于(hd1,gpt2)/initramfs.img)。
  6. 启动系统
    执行启动命令:

    1
    grub> boot


重新生成initramfs

使用 dracut 生成系统中所有内核版本的 initramfs:

1
2
3
for kernel_version in $(ls /lib/modules/); do
dracut --force /boot/initramfs-${kernel_version}.img ${kernel_version}
done

grub串口

将GRUB输出重定向到串口步骤如下:

  1. 同时启用图形终端和串行终端,并设置了串行终端的参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    1 # Generated by deepin-installer  vim /etc/default/grub
    2 GRUB_BACKGROUND="/boot/grub/themes/deepin-fallback/background.jpg"
    3 GRUB_CMDLINE_LINUX_DEFAULT="video=efifb:nobgrt splash ignore_loglevel initcall_debug acpi.debug_layer=0x2 acpi.debug_level=0xffffffff no_console_suspend console=tty console=ttyAMA0,115200 dyndbg='module snd_soc_pmdk_dp +p; file *usb* +p' plymouth.ignore-serial-consoles"
    4 GRUB_DEFAULT=0
    5 GRUB_DISTRIBUTOR="`/usr/bin/lsb_release -d -s 2>/dev/null || echo Deepin`"
    6 GRUB_THEME="/boot/grub/themes/deepin-fallback/theme.txt"
    7 GRUB_TIMEOUT=5
    8 GRUB_GFXMODE=
    9 DEEPIN_GFXMODE_DETECT=1
    10 GRUB_TERMINAL="console serial"
    11 GRUB_SERIAL_COMMAND="serial --speed=115200 efi0 --word=8 --parity=no --stop=1"

    在这个 GRUB 配置中,你配置了同时启用图形终端和串行终端,并设置了串行终端的参数。具体解释如下:

    第 10 行 (GRUB_TERMINAL="console serial"): 这一行指示 GRUB 同时启用图形终端和串行终端。这意味着 GRUB 将能够在本地控制台(图形终端)和串行控制台(通过串行端口)上进行交互。

    第 11 行 (GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"): 这一行配置了串行终端的参数。具体的参数设置如下:

    • --speed=115200:串行通信的波特率设置为 115200 比特每秒。
    • --unit=0:串行端口的编号或单元号设置为 0,通常对应于 /dev/ttyS0 或 /dev/ttyAMA0,这是第一个串口。但比如loongarch、ft2000机器, 串口可能注册为了别的名字, 比如efi0。
    • --word=8:数据位设置为 8 位。
    • --parity=no:不使用奇偶校验。
    • --stop=1:停止位设置为 1 位。

    这个配置表明 GRUB 将同时在图形终端和串行终端上操作,并且串行终端的参数已设置为常见的串口通信配置。如果你的系统具有串行控制台(例如通过串口连接到终端服务器),这个配置允许你在本地和远程两个终端上查看 GRUB 菜单和启动过程。

  2. 最大程度开启grub调试信息

    1
    grub-editenv /boot/grub/grubenv set debug=all

  3. 更新 GRUB 以应用更改

    1
    update-grub

grub源码

BIOS, UEFI, MBR, GPT, GRUB

grub中更改默认启动内核

centos中更改默认启动内核

在grub.cfg中查找内核启动选项:

1
2
3
4
5
grep -E "^menuentry" /boot/efi/EFI/ctyunos/grub.cfg

menuentry 'CTyunOS (4.19.90-2102.2.0.0070.1.all.rc5.ctl2.aarch64) 2 24.07' --class ctyunos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-4.19.90-2102.2.0.0070.1.all.rc5.ctl2.aarch64-advanced-02de5198-45ec-4794-8a93-c4d2864f0e3b' {
menuentry 'Kylin V10 (V10) rescue (on /dev/mapper/klas-root)' --class gnu-linux --class gnu --class os $menuentry_id_option 'osprober-gnulinux-/vmlinuz-0-rescue-d9d667fdb84247168957a932c8c6a733--c16672cc-6d1d-4253-84b0-06e288652f4a' {
menuentry 'Kylin V10 (V10) (on /dev/mapper/klas-root)' --class gnu-linux --class gnu --class os $menuentry_id_option 'osprober-gnulinux-/vmlinuz-4.19.90-52.22.v2207.ky10.aarch64--c16672cc-6d1d-4253-84b0-06e288652f4a' {

可以进一步使用awk/boot/grub2/grub.cfg 文件中提取了 menuentry 行并显示相应的启动项名称:

1
awk -F\' '$1=="menuentry " {print i++ " : " $2}' /boot/efi/EFI/ctyunos/grub.cfg
1
2
3
4
awk -F\' '$1=="menuentry " {print i++ " : " $2}' /boot/grub2/grub.cfg

0 : CTyunOS (4.19.90-2102.2.0.0076.ctl2.x86_64) 2 24.07
1 : CTyunOS (0-rescue-c59d31ab74dd4c4e95314c300589c91d) 2 24.07
  • -F\':将单引号(')作为字段分隔符。这样,menuentry 的名称(即系统引导项的名称)会出现在 $2 中。
  • $1=="menuentry ":这个条件表示只处理以 "menuentry " 开头的行。$1 是使用 -F\' 分隔符之后的第一部分,所以 $1 应该是 "menuentry "
  • {print i++ " : " $2}:当匹配到 "menuentry " 行时,输出一个计数器 i$2(也就是 menuentry 的名称部分)。i++ 会在每次输出后递增,给每个菜单项分配一个编号。
debian12中更改默认启动内核
1
2
3
4
5
6
7
8
9
10
11
12
13
root@debian:/boot/grub# awk -F"'" '/(menuentry|submenu) / { print i++ ": " $2 }' /boot/grub/grub.cfg

0: Debian GNU/Linux
1: Advanced options for Debian GNU/Linux
2: Debian GNU/Linux, with Linux 6.6.92
3: Debian GNU/Linux, with Linux 6.6.92 (recovery mode)
4: Debian GNU/Linux, with Linux 6.1.0-35-amd64
5: Debian GNU/Linux, with Linux 6.1.0-35-amd64 (recovery mode)
6: Debian GNU/Linux, with Linux 5.10.235
7: Debian GNU/Linux, with Linux 5.10.235 (recovery mode)
8: Debian GNU/Linux, with Linux 4.19.325
9: Debian GNU/Linux, with Linux 4.19.325 (recovery mode)
10: UEFI Firmware Settings
1
2
3
4
5
6
7
8
9
10
11
awk -F"'" '
/submenu / { submenu_id = i++; print submenu_id ": " $2; in_submenu=1; sub_i=0; next }
/menuentry / {
if (in_submenu) {
print submenu_id "." sub_i++ ": " $2
} else {
print i++ ": " $2
}
}
/^}/ { in_submenu=0 }
' /boot/grub/grub.cfg
1
2
3
4
5
6
7
8
9
10
11
0: Debian GNU/Linux
1: Advanced options for Debian GNU/Linux
1.0: Debian GNU/Linux, with Linux 6.6.92
1.1: Debian GNU/Linux, with Linux 6.6.92 (recovery mode)
1.2: Debian GNU/Linux, with Linux 6.1.0-35-amd64
1.3: Debian GNU/Linux, with Linux 6.1.0-35-amd64 (recovery mode)
1.4: Debian GNU/Linux, with Linux 5.10.235
1.5: Debian GNU/Linux, with Linux 5.10.235 (recovery mode)
1.6: Debian GNU/Linux, with Linux 4.19.325
1.7: Debian GNU/Linux, with Linux 4.19.325 (recovery mode)
2: UEFI Firmware Settings
更改默认启动内核

GRUB菜单项的索引是从0开始计算的,这意味着第一个菜单项的索引为0,第二个菜单项的索引为1,以此类推。

将第三个GRUB菜单项设置为默认启动项:

1
grub2-set-default 2
验证当前的默认启动项
1
2
3
4
grub2-editenv list

saved_entry=2
boot_success=0

grub中禁止某个驱动加载

1
initcall_debug no_console_suspend ignore_loglevel initcall_blacklist=phytium_spi_driver_init module_blacklist=spi_phytium_plat dyndbg='module phytium_dc_drm +p; module snd_soc_phytium_i2s +p; module snd_soc_pmdk_es8388 +p; module snd_soc_pmdk_es8336 +p; module snd_soc_pmdk_dp +p; file *usb* +p'
1
initcall_blacklist=phytium_spi_driver_init
1
2
3
initcall_blacklist=  [KNL] Do not execute a comma-separated list of
initcall functions. Useful for debugging built-in
modules and initcalls.
1
2
module_blacklist=  [KNL] Do not load a comma-separated list of
modules. Useful for debugging problem modules.

grubby

1. grubby 命令简介

grubby 是一个用于管理和配置 Linux 系统的引导加载程序(主要针对 GRUB 和 GRUB2)的工具,通常用于修改内核启动参数、默认内核、引导条目等。它在许多基于 Red Hat 的发行版(如 RHEL、CentOS、Fedora)以及其他使用 GRUB 的系统中广泛使用。

  • 功能
    • 查看、添加、删除或修改 GRUB 配置文件中的内核引导条目。
    • 配置内核命令行参数(例如 rocrashkernelkfence.sample_interval)。
    • 设置默认启动内核或调整引导顺序。
    • 支持多内核管理,方便在系统上维护多个内核版本或配置。
  • 常用场景
    • 更新 GRUB 配置以启用调试参数(如 KFENCE 或 KASAN)。
    • 切换默认内核版本(例如从 5.10.0-136.12.0.90.kfence5.10.0-136.12.0.90.kasan)。
    • 添加硬件相关参数(如 iommu=pthugepages)。
    • 管理救援模式(rescue mode)条目。
  • 工作原理
    • grubby 解析 GRUB 配置文件(通常是 /boot/grub/grub.cfg/boot/grub2/grub.cfg)或相关的配置模板(/etc/grub.d//etc/default/grub)。
    • 修改配置后,grubby 可以自动更新 GRUB 的最终配置文件(通过调用 grub2-mkconfig 或直接编辑)。
  • 常见命令
    • grubby --info=ALL:列出所有 GRUB 引导条目的详细信息。
    • grubby --default-kernel:显示当前默认启动的内核。
    • grubby --set-default=<kernel>:设置默认启动内核。
    • grubby --add-kernel=<kernel> --args=<args>:添加新内核条目并指定参数。
    • grubby --update-kernel=<kernel> --args=<args>:更新指定内核的启动参数。

2. 使用 grubby 的简单示例

以下是一些与你的环境相关的 grubby 操作示例:

  • 列出所有内核信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    grubby --info=ALL

    index=0
    kernel=/boot/vmlinuz-5.10.0-136.12.0.90.kfence.ctl3.x86_64
    args="ro console=ttyS0,115200n8 crashkernel=512M amd_iommu=on iommu=pt hugepagesz=1GB hugepages=869 default_hugepagesz=1GB pci=realloc modprobe.blacklist=virtio_blk"
    root=UUID=81a77f8a-5e51-41a8-b6d4-419281e24974
    initrd=/boot/initramfs-5.10.0-136.12.0.90.kfence.ctl3.x86_64.img
    title=ctyunos (5.10.0-136.12.0.90.kfence.ctl3.x86_64) 23.01 2.1
    index=1
    kernel=/boot/vmlinuz-5.10.0-136.12.0.90.kasan.ctl3.x86_64
    args="ro console=ttyS0,115200n8 crashkernel=512M amd_iommu=on iommu=pt hugepagesz=1GB hugepages=869 default_hugepagesz=1GB pci=realloc modprobe.blacklist=virtio_blk"
    root=UUID=81a77f8a-5e51-41a8-b6d4-419281e24974
    initrd=/boot/initramfs-5.10.0-136.12.0.90.kasan.ctl3.x86_64.img
    title=ctyunos (5.10.0-136.12.0.90.kasan.ctl3.x86_64) 23.01 2.1
    index=2
    kernel=/boot/vmlinuz-5.10.0-136.12.0.90.ctl3.x86_64
    args="ro console=ttyS0,115200n8 crashkernel=512M amd_iommu=on iommu=pt hugepagesz=1GB hugepages=869 default_hugepagesz=1GB pci=realloc modprobe.blacklist=virtio_blk"
    root=UUID=81a77f8a-5e51-41a8-b6d4-419281e24974
    initrd=/boot/initramfs-5.10.0-136.12.0.90.ctl3.x86_64.img
    title=ctyunos (5.10.0-136.12.0.90.ctl3.x86_64) 23.01 2.1
    index=3
    kernel=/boot/vmlinuz-0-rescue-c6f9b8a106494102adaeca11a92de74f
    args="ro console=ttyS0,115200n8 crashkernel=512M amd_iommu=on iommu=pt hugepagesz=1GB hugepages=869 default_hugepagesz=1GB pci=realloc modprobe.blacklist=virtio_blk"
    root=UUID=81a77f8a-5e51-41a8-b6d4-419281e24974
    initrd=/boot/initramfs-0-rescue-c6f9b8a106494102adaeca11a92de74f.img
    title=ctyunos (0-rescue-c6f9b8a106494102adaeca11a92de74f) 23.01 2.1
    index=4
    non linux entry
    index=5
    non linux entry
    index=6
    non linux entry
    输出所有内核的详细信息,包括版本、参数等。

  • 查看默认内核

    1
    grubby --default-kernel
    输出当前默认启动的内核(可能是 /boot/vmlinuz-5.10.0-136.12.0.90.kfence.ctl3.x86_64)。

  • 添加 KFENCE 参数

    1
    grubby --update-kernel=/boot/vmlinuz-5.10.0-136.12.0.90.kfence.ctl3.x86_64 --args="kfence.sample_interval=200"
    将采样间隔设置为 200ms。

  • 切换到 KASAN 内核

    1
    grubby --set-default=/boot/vmlinuz-5.10.0-136.12.0.90.kasan.ctl3.x86_64

  • 恢复默认参数(移除特定参数)

    1
    grubby --update-kernel=/boot/vmlinuz-5.10.0-136.12.0.90.kfence.ctl3.x86_64 --remove-args="kfence.sample_interval"


内核编译

Makefile kbuild

defconfig

clean

  • linux内核配置清理make clean

    1
    2
    3
    make clean      # 删除大多数的编译生成文件, 但是会保留内核的配置文件.config, 还有足够的编译支持来建立扩展模块
    make mrproper # 删除所有的编译生成文件, 还有内核配置文件, 再加上各种备份文件
    make distclean # mrproper删除的文件, 加上编辑备份文件和一些补丁文件

编译内核deb包

在 Linux 内核构建过程中,make deb-pkg 和 make bindeb-pkg 是用于生成 Debian 包的两个目标。这两个目标的主要区别在于生成的 Debian 包的内容和形式:

make deb-pkg

  • 这个目标生成的是一个包含完整内核源代码、配置文件和构建所需的所有内容的 Debian 源码包(source package)。
  • Debian 源码包一般包括 linux-<version>.tar.xz 源码压缩包、debian/ 目录中的维护文件以及其他构建所需的文件。这个包允许其他人在其系统上重新构建内核。
  • 也包含make bindeb-pkg产物。

make bindeb-pkg

x86上交叉编译arm内核deb

要在 x86 架构 的机器上使用 make bindeb-pkgARM 架构交叉编译内核 .deb 包,可以按照以下步骤进行设置:


1. 安装必要的工具

在主机上安装交叉编译工具链和必要的软件包:

1
2
sudo apt update
sudo apt install gcc-arm-linux-gnueabi make dpkg-dev

如果目标是 64 位 ARM:

1
sudo apt install gcc-aarch64-linux-gnu

2. 准备内核源码

确保你已经下载并解压了正确版本的 Linux 内核源码,例如:

1
2
3
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.9.tar.xz
tar -xf linux-6.9.tar.xz
cd linux-6.9

3. 配置交叉编译环境

为交叉编译设置工具链: - 对于 32 位 ARM:

1
2
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabi-
- 对于 64 位 ARM:
1
2
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-


4. 配置内核

加载适合目标设备的内核配置:

1
make ARCH=arm64 defconfig
如果有特定的设备配置(比如从设备厂家获取的 .config 文件),可以直接复制到源码目录:
1
cp /path/to/your/.config .


5. 编译并生成 .deb

使用 bindeb-pkg 目标生成 .deb 包:

1
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bindeb-pkg -j$(nproc)

编译完成后,生成的 .deb 包会出现在源码目录的上一级目录,例如:

1
2
../linux-image-6.9.0-arm64.deb
../linux-headers-6.9.0-arm64.deb


6. 注意事项
  1. 内核配置的适配性
    确保使用了正确的内核配置(.config),以支持目标 ARM 硬件。
  2. 模块支持
    如果内核需要额外模块,确保在 .config 中启用对应选项。
  3. 检查工具链兼容性
    工具链版本需与目标架构和内核源码兼容。

完成后,你可以将生成的 .deb 包传输到目标 ARM 设备上安装。

linux-perf deb

在deepin 23上构建 linux-perf deb包

在debian12中下载内核源码:

1
apt source linux=6.1.55-1

将内核原源码拷贝到deepin 23上:

1
rsync -avzP linux-6.1.55 wujing@10.20.53.48:~/code/linux-6.1.55-1

在deepin 23 上构建deb包(包含 linux-perf):

1
cd code/linux-6.1.55-1/linux-6.1.55
1
dpkg-buildpackage -b -nc -uc -us -j16

内核调试包

编译内核rpm包

  • 编译内核rpm包

    本教程基于 openEuler 20.03

    使用 yum-builddep 安装 Linux 内核的构建依赖项:

    1
    sudo yum-builddep kernel

    也可以通过下方命令安装 Linux 内核的构建依赖项:

    1
    sudo yum install bison flex ncurses-devel elfutils-libelf-devel openssl-devel make rpm-build

    编译内核rpm包:

    1
    make binrpm-pkg -j8 2> make_error.log

rpmbuild编译510-amd-genoa

内核源码目录下执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash

set -aex

# 进入内核源码所在目录
cd ~/code/linux

git restore .
git checkout ctkernel-lts-5.10/yuanql9/develop-amd-genoa
make distclean
git clean -fdx
git restore .

rm -rf /root/rpmbuild/SOURCES/kernel.tar.gz
tar --xform="s/^./kernel/" --exclude=".git" -chzf /root/rpmbuild/SOURCES/kernel.tar.gz .
cp build/* /root/rpmbuild/SOURCES

# 不要检查kabi变化
sed -i -E '0,/%define with_kabichk 1/! {/%define with_kabichk 1/s/1/0/}' build/kernel.spec

# 在版本号后面追加.amd.genoa.$(git rev-parse HEAD | head -c 12)
sed -i "/%global pkg_release/s/$/\.amd.genoa.$(git rev-parse HEAD | head -c 12)/" build/kernel.spec

# rpmbuild -ba build/kernel.spec

x86上交叉编译arm内核rpm

gcc-linaro-7.3.1-2018.05-x86_64_arm-linux-gnueabihf

https://releases.linaro.org/components/toolchain/binaries/7.3-2018.05/arm-linux-gnueabihf/

gcc-linaro-7.3.1-2018.05-x86_64_arm-linux-gnueabihf.tar.xz

下载gcc-linaro-7.3.1-2018.05-x86_64_arm-linux-gnueabihf.tar.xz并解压:

1
2
3
4
5
tar -xJf gcc-linaro-7.3.1-2018.05-x86_64_arm-linux-gnueabihf.tar.xz
export CROSS_COMPILE=/root/Downloads/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
export ARCH=arm64
make openeuler_defconfig
make binrpm-pkg -j32

建议使用下方gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu进行交叉编译。

gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu

https://blog.csdn.net/qq_37200742/article/details/128331909

https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads

https://developer.arm.com/downloads/-/gnu-a#panel4a

gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz

在浏览器中下载上述gcc-arm-10.3交叉编译工具链后进行如下操作:

1
2
3
4
5
6
7
cp /root/Downloads/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz /root
tar -xJf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz
export CROSS_COMPILE=/root/Downloads/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
export ARCH=arm64
make openeuler_defconfig
make binrpm-pkg -j32
rpmbuild -ba --target=aarch64 build/kernel.spec --define "_host_cpu aarch64"

也可使用rpmbuild交叉编译的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13

# 搭建交叉编译环境
mkdir -p /root/Downloads/cross_compile
cd /root/Downloads/cross_compile
wget -O gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz "https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz?rev=1cb9c51b94f54940bdcccd791451cec3&hash=B380A59EA3DC5FDC0448CA6472BF6B512706F8EC"
tar -xJf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz

# 编译内核(由于zlib缺失,无法编译bpftool等,需要改动kernel.spec)
cd /root/code/linux
git cherry-pick 69478eae21ab # 69478eae21ab 在分支 ctkernel-lts-5.10/yuanql9/develop-cross-compile中
export CROSS_COMPILE=/root/Downloads/cross_compile/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
export ARCH=arm64
time rpmbuild -ba --target=aarch64 --define "_host_cpu aarch64" --without=bpftool --without=perf --without=kvm_stat build/kernel.spec

zlib not found
  • https://www.zlib.net/
  • https://blog.csdn.net/qq_38232169/article/details/135398623
1
2
3
4
5
6
7
CROSS_PREFIX=/root/Downloads/cross_compile/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu- ./configure --prefix=/root/Downloads/cross_compile/usr/local
make
make install
export PKG_CONFIG_PATH=/root/Downloads/cross_compile/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
export LD_LIBRARY_PATH=/root/Downloads/cross_compile/usr/local/lib:$LD_LIBRARY_PATH
export CFLAGS="-I/root/Downloads/cross_compile/usr/local/include $CFLAGS"
export LDFLAGS="-L/root/Downloads/cross_compile/usr/local/lib $LDFLAGS"
1
2
3
export CROSS_COMPILE=/root/Downloads/cross_compile/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
export ARCH=arm64
time rpmbuild -ba --target=aarch64 --define "_host_cpu aarch64" build/kernel.spec

交叉编译bpftool、perf、kvm_stat还有问题,故暂时采用上一小节改动kernel.spec使用rpmbuild -ba --target=aarch64 --define "_host_cpu aarch64" --without=bpftool --without=perf --without=kvm_stat build/kernel.spec方式进行交叉编译。

内核模块

KBUILD_EXTRA_SYMBOLS

dkms

地址空间随机化

内核 C 语言

likely和unlikely

内核section

EXPORT_SYMBOL

VA_ARGS

__VA_ARGS__ 是一个在宏定义中使用的预处理器标识符,用于表示可变数量的参数。它允许在宏中接受并处理任意数量的参数。

  • 用途: 处理可变参数列表。

  • 语法: __VA_ARGS__ 在宏定义中占位,表示传递给宏的所有额外参数。

  • 示例:

    1
    #define PRINT(fmt, ...) printf(fmt, __VA_ARGS__)

    使用宏 PRINT 可以传递任意数量的参数:

    1
    2
    PRINT("Value: %d\n", 42);
    PRINT("Values: %d, %d\n", 1, 2);

在宏 PRINT 中,fmt 是格式化字符串,而 __VA_ARGS__ 被替换为 printf 的其他参数。

内核链表

sysfs

内核启动流程

vmlinux.lds.S

0号进程,1号进程,2号进程

根文件系统rootfs

initramfs

initrd 和 initramfs
制作initramfs
initramfs解压
lsinitramfs

在Linux的GRUB启动配置中,可以通过 initramfs调试参数 来排查系统启动问题(如文件系统挂载失败、驱动加载问题等)。以下是常用的调试参数及其作用:


grub中initramfs调试参数

1. 常用 initramfs 调试参数
(1) rd.debug

作用:启用 dracut(或 initramfs)的详细调试日志。 • 示例

1
rd.debug
输出:显示 initramfs 阶段的所有脚本执行过程,适合排查启动流程问题。

(2) rd.break

作用:在 initramfs 启动过程中暂停,进入交互式 Shell。 • 常用断点

1
2
3
rd.break=pre-mount   # 在挂载根文件系统前暂停
rd.break=pre-pivot # 在切换到真实根文件系统前暂停
rd.break=pre-trigger # 在 systemd 启动前暂停
使用方式: 1. 在 GRUB 启动菜单按 e 编辑内核参数,添加 rd.break=pre-mount。 2. 启动后会进入 initramfs 的 Shell,可手动执行命令(如 mountlsblk)。 3. 输入 exit 继续启动。

(3) rd.shell

作用:如果 initramfs 启动失败,自动进入紧急 Shell。 • 示例

1
rd.shell
适用场景:系统无法挂载根文件系统时,手动修复。

(4) rd.initcall.debug

作用:打印 initramfs 阶段的所有内核初始化调用(initcall)。 • 示例

1
rd.initcall.debug
适用场景:排查驱动加载顺序问题。

(5) rd.driver.blacklist / rd.driver.pre

作用:黑名单或优先加载特定驱动。 • 示例

1
2
rd.driver.blacklist=nouveau  # 禁用 nouveau 驱动
rd.driver.pre=ahci # 优先加载 ahci 驱动
适用场景:驱动冲突或加载失败时调试。

(6) rootdelay

作用:延长 initramfs 等待根设备的时间(秒)。 • 示例

1
rootdelay=30
适用场景:慢速存储设备(如 USB/NAS)需要更长时间初始化。

(7) rd.lvm.vg / rd.lvm.lv

作用:手动指定 LVM 卷组或逻辑卷。 • 示例

1
rd.lvm.vg=vg00 rd.lvm.lv=vg00/root
适用场景:LVM 设备未被自动激活时。

(8) rd.live.ram

作用:将 Live CD 的 initramfs 完全加载到内存。 • 示例

1
rd.live.ram
适用场景:调试 Live 系统启动问题。

(9) rd.luks.uuid

作用:手动指定 LUKS 加密设备的 UUID。 • 示例

1
rd.luks.uuid=1234-5678-90ab-cdef
适用场景:加密设备未被自动解锁时。

(10) rd.md.uuid

作用:手动指定软 RAID(mdadm)设备的 UUID。 • 示例

1
rd.md.uuid=abcd1234:5678ef90
适用场景:RAID 设备未被自动组装时。


2. 组合使用示例
示例 1:调试根文件系统挂载失败
1
rd.debug rd.break=pre-mount rootdelay=30

作用: • 打印详细日志(rd.debug)。 • 在挂载根文件系统前暂停(rd.break=pre-mount)。 • 延长设备等待时间(rootdelay=30)。

示例 2:排查 LVM + LUKS 启动问题
1
rd.debug rd.lvm.vg=vg00 rd.luks.uuid=1234-5678

作用: • 打印详细日志(rd.debug)。 • 手动指定 LVM 卷组(rd.lvm.vg)。 • 手动指定 LUKS 设备 UUID(rd.luks.uuid)。


3. 如何应用这些参数?
  1. 在 GRUB 启动菜单编辑内核参数: • 启动时按 e 进入编辑模式。 • 在 linuxlinux16 行末尾添加调试参数(如 rd.debug rd.break=pre-mount)。 • 按 Ctrl+XF10 启动。

  2. 永久修改 GRUB 配置(谨慎操作):

    1
    sudo nano /etc/default/grub
    • 修改 GRUB_CMDLINE_LINUX,例如:
    1
    GRUB_CMDLINE_LINUX="rd.debug rd.shell"
    • 更新 GRUB:
    1
    2
    sudo update-grub   # Debian/Ubuntu
    sudo grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL/CentOS


4. 调试技巧

查看日志: • dmesg:内核日志。 • journalctl -xb:systemd 日志。 • /run/initramfs/init.logdracut 日志(需在 initramfs Shell 中查看)。

手动挂载根文件系统(在 rd.break Shell 中):

1
2
3
mkdir /mnt/root
mount /dev/mapper/vg00-root /mnt/root # 替换为实际设备
chroot /mnt/root # 切换到真实根文件系统


总结
参数 作用 适用场景
rd.debug 打印详细日志 排查 initramfs 流程问题
rd.break 进入调试 Shell 手动修复挂载/驱动问题
rd.shell 启动失败时进入 Shell 紧急修复
rootdelay 延长设备等待时间 慢速存储设备
rd.lvm.vg 手动指定 LVM LVM 未自动激活
rd.luks.uuid 手动指定 LUKS 加密设备未解锁

通过合理组合这些参数,可以高效定位和解决 initramfs 阶段的启动问题。

do_initcalls

系统调用

ERESTARTSYS

open openat

uid和euid

内核单元测试

Linux 内核单元测试主要通过两大框架:

  1. Kselftest
    • 适用于集成测试和功能测试,支持用户空间和内核空间交互。
    • 测试代码在 tools/testing/selftests/ 目录,运行命令:make -C tools/testing/selftests run_tests
  2. KUnit
    • 轻量级单元测试框架,专注内核态的单元测试。
    • 测试代码在 lib/kunit/,运行命令:./tools/testing/kunit/kunit.py run

Kselftest 适合功能测试,KUnit 适合内核模块的单元测试。

内核gcov代码覆盖率

内核gcov编译选项

在 Linux 4.19.90 内核源码中开启 gcov (代码覆盖率分析工具)需要设置以下编译选项:

1
2
CONFIG_GCOV_KERNEL=y
CONFIG_GCOV_PROFILE_ALL=y
  • CONFIG_GCOV_KERNEL: 启用内核模块的代码覆盖率分析。
  • CONFIG_GCOV_PROFILE_ALL: 对所有内核子系统启用代码覆盖率分析。

编译内核后安装重启。

查看内核代码覆盖率

进入到内核源码目录:

1
2
cd /home/wujing/code/linux-y
sudo -s
1
2
3
4
gcov kernel/gcov/base.c -o /sys/kernel/debug/gcov/home/wujing/code/linux-y/kernel/gcov
File 'kernel/gcov/base.c'Lines executed:65.22% of 46
Creating 'base.c.gcov'
File 'include/linux/module.h'Lines executed:100.00% of 5Creating 'module.h.gcov'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
gcov net/ipv4/xfrm4_state.c -o /sys/kernel/debug/gcov/home/wujing/code/linux-y/net/ipv4/
File 'include/net/xfrm.h'
Lines executed:0.00% of 20
Creating 'xfrm.h.gcov'

File 'net/ipv4/xfrm4_state.c'
Lines executed:7.50% of 40
Creating 'xfrm4_state.c.gcov'

File 'include/linux/skbuff.h'
Lines executed:0.00% of 1
Creating 'skbuff.h.gcov'

File 'include/linux/string.h'
Lines executed:0.00% of 6
Creating 'string.h.gcov'

File 'include/net/net_namespace.h'
Lines executed:0.00% of 1
Creating 'net_namespace.h.gcov'
1
2
3
4
gcov init/main.c -o /sys/kernel/debug/gcov/home/wujing/code/linux-y/init
File 'init/main.c'
Lines executed:70.57% of 367
Creating 'main.c.gcov'

kpatch

编译安装测试内核

本教程以openeuler-4-19为例,编译安装测试内核。

1
2
cd ~/code/openeuler-4-19
git checkout -b 95170c1fb40c 95170c1fb40c

自定义内核版本号

1
2
3
4
5
6
7
8
9
10
11
12
13
cd build/spec
diff -u kernel.spec.bak kernel.spec
--- kernel.spec.bak 2024-07-29 16:43:55.850551849 +0800
+++ kernel.spec 2024-07-29 14:47:21.439102425 +0800
@@ -24,7 +24,7 @@

Name: kernel
Version: 4.19.90
-Release: %{hulkrelease}.0068
+Release: %{hulkrelease}.0068.ksmd
Summary: Linux Kernel
License: GPLv2
URL: http://www.kernel.org/

安装编译依赖

1
yum-builddep -y kernel.spec

编译

1
cd ../../
1
2
pwd
/home/wujing/code/openeuler-4-19/build/spec
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
./build/build.sh

Wrote: /home/wujing/rpmbuild/RPMS/x86_64/bpftool-debuginfo-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/python3-perf-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/python2-perf-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/kernel-tools-devel-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/kernel-tools-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/kernel-tools-debuginfo-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/python3-perf-debuginfo-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/python2-perf-debuginfo-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/bpftool-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/perf-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/perf-debuginfo-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/kernel-devel-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/kernel-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/kernel-debugsource-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/kernel-source-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm
Wrote: /home/wujing/rpmbuild/RPMS/x86_64/kernel-debuginfo-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64.rpm

制作补丁文件

1
2
3
4
cp kernel/sched/fair.c kernel/sched/fair.c.orig
git checkout master
# patch文件路径(patch文件必须支持在kernel-source路径下通过patch -p1的方式修改源码)
diff -uNap openeuler-4-19/kernel/sched/fair.c.orig openeuler-4-19/kernel/sched/fair.c > openeuler-4-19/kernel/sched/fair.c.patch

编译更新补丁工具

1
2
3
4
5
6
7
8
yum install -y git rpm-build elfutils-libelf-devel gdb-headless
git clone https://gitee.com/src-openeuler/kpatch.git
git checkout -b origin/openEuler-22.03-LTS-SP3 remotes/origin/openEuler-22.03-LTS-SP3
mkdir -p ~/rpmbuild/SOURCES/
/bin/cp kpatch/* ~/rpmbuild/SOURCES/
rpmbuild -ba kpatch/kpatch.spec
rpm -Uvh ~/rpmbuild/RPMS/`arch`/kpatch*.rpm
rpm -Uvh ~/rpmbuild/RPMS/noarch/kpatch*.rpm

热补丁制作

环境准备

安装依赖软件包:

1
yum install -y make gcc patch bison flex openssl-devel kpatch kpatch-runtime elfutils-libelf-devel

安装当前内核源码和开发包(这里是上面带ksmd的rpmb包):

1
yum install -y kernel-source-`uname -r` kernel-debuginfo-`uname -r` kernel-devel-`uname -r`

进入热补丁制作目录并准备环境

1
2
3
4
5
cd /opt/patch_workspace
rm -rf kernel-source .config
ln -s /usr/src/linux-`uname -r`/ kernel-source
ln -s /usr/src/linux-`uname -r`/.config .config
ln -s /usr/lib/debug/lib/modules/`uname -r`/vmlinux vmlinux

开始制作热补丁

给make_hotpatch执行权限:

1
sudo chmod +x make_hotpatch
1
sudo cp ~/code/openeuler-4-19/kernel/sched/fair.c.1.patch .
1
2
3
4
5
6
7
8
9
10
11
12
13
sudo ./make_hotpatch -i fairisolcpus -p fair.c.patch

kernel version:4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64
Using source directory at /usr/src/linux-4.19.90-2102.2.0.0068.ksmd.ctl2.x86_64
Testing patch file(s)
Reading special section data
Building original source
Building patched source
Extracting new and modified ELF sections
fair.o: changed function: select_idle_sibling
Patched objects: vmlinux vmlinux
Building patch module: klp_fairisolcpus.ko
SUCCESS

补丁制作完成,补丁文件以压缩包的格式存放于/opt/patch_workspace/hotpatch目录下。

管理热补丁

1
cd hotpatch
加载热补丁
1
2
3
4
sudo livepatch -l klp_fairisolcpus.tar.gz

insmod /lib/modules/hotpatch.227734/klp_fairisolcpus/klp_fairisolcpus.ko
install patch klp_fairisolcpus.tar.gz success
激活热补丁
1
2
3
sudo livepatch -a fairisolcpus

active patch klp_fairisolcpus success
回退热补丁
1
2
3
sudo livepatch -d fairisolcpus

deactive patch klp_fairisolcpus success
卸载热补丁
1
2
3
sudo livepatch -r fairisolcpus

remove patch klp_fairisolcpus success

异常分类

fault_trap_abort

根据图片中关于 异常分类(Fault/Trap/Abort) 的详细定义,我将用更具体的 代码示例执行流程 来演示这三类异常的区别,并结合底层机制进行解析。


1. Fault(故障)示例:缺页异常(Page Fault)

场景

程序访问一个未加载到物理内存的虚拟地址,触发缺页异常。 #### 详细流程

1
2
3
// 示例代码(C语言)
int *ptr = (int *)0x1000; // 假设 0x1000 是未映射的虚拟地址
int value = *ptr; // 触发 Page Fault
1. 异常触发
CPU 执行 mov eax, [0x1000] 时,发现该地址未映射到物理内存,触发 Fault 类异常。 2. 状态恢复
• CPU 保存当前状态(寄存器、指令指针等),并将返回地址指向 导致 Fault 的指令(即 mov eax, [0x1000])。
• 控制权转交给操作系统的缺页处理程序。 3. 纠正处理
• 操作系统从磁盘加载缺失的页到内存,并更新页表。
4. 重新执行
• 返回后,CPU 重新执行原指令,此时内存访问成功,程序继续运行。

关键点

• Fault 的返回地址指向 原指令,确保纠正后能重试。
• 可恢复,是 被动触发的异常(如缺页、除零)。


2. Trap(陷阱)示例:系统调用(Syscall)

场景

用户程序通过 int 0x80syscall 指令主动触发系统调用。 #### 详细流程(以 x86 的 int 0x80 为例)

1
2
3
4
5
6
; 示例代码(汇编)
mov eax, 4 ; 系统调用号(write)
mov ebx, 1 ; 文件描述符(stdout)
mov ecx, msg ; 字符串地址
mov edx, len ; 字符串长度
int 0x80 ; 触发 Trap,进入内核态
1. 异常触发
CPU 执行 int 0x80 后,检测到 Trap 类异常,切换到内核态。 2. 内核处理
• CPU 保存当前状态,并将返回地址指向 下一条指令(即 int 0x80 之后的指令)。
• 内核根据 eax 中的系统调用号执行 write() 函数。 3. 恢复执行
• 系统调用完成后,CPU 返回到用户态,继续执行 int 0x80 的下一条指令。

关键点

• Trap 的返回地址指向 下一条指令,用于 主动触发的连贯操作(如系统调用、调试断点)。
• 必须 完整执行原指令 后才触发。


3. Abort(中止)示例:硬件错误(Double Fault)

场景

CPU 在处理一个异常时又遇到另一个异常(如页表损坏或栈溢出),触发 Double Fault(双重故障)。 #### 详细流程

1
2
3
4
// 示例场景:内核栈溢出导致无法处理缺页异常
void recursive_function() {
recursive_function(); // 无限递归,栈溢出
}
1. 首次异常
• 程序触发缺页异常(Fault),CPU 尝试调用缺页处理程序。
2. 二次异常
• 处理缺页时发现内核栈已耗尽,无法保存状态,触发 Abort(Double Fault)。
3. 系统终止
• CPU 无法恢复现场,直接跳转到 中止处理程序(如重启或蓝屏)。

关键点

• Abort 不提供返回地址,因为无法定位错误指令或恢复状态。
• 通常由 硬件或内核级严重错误 引起(如内存校验失败、CPU 非法状态)。


三类异常的对比总结

异常类型 触发时机 返回地址 是否可恢复 典型场景
Fault 指令执行 原指令 缺页异常、除零错误
Trap 指令执行 下一条指令(JMP等指令例外) 系统调用、调试断点
Abort 无法定位指令 无(程序终止) 硬件故障、内核栈溢出

扩展问题

Q: 为什么系统调用要用 Trap 而不是 Fault?

A: 系统调用是 程序主动发起的请求,需要在完整执行 int 0x80 后进入内核态,并确保返回时继续执行后续代码。Fault 的“重试”机制不适用于此场景。

Q: 分段错误(Segmentation Fault)属于哪一类?

A: 多数情况下是 Fault(如访问非法地址时可被 SIGSEGV 捕获),但若内核无法处理(如页表损坏),可能升级为 Abort

其它


kernel
https://realwujing.github.io/linux/kernel/kernel/
作者
Wu Jing
发布于
2023年4月19日
许可协议