kernel
本文最后更新于:2025年6月7日 晚上
kernel
kernel docs
linux内核源码
-
1
2
3
4Clone
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 -
1
2
3Clone
https://github.com/torvalds/linux.git
git@github.com:torvalds/linux.git Linux kernel stable tree mirror
1
2
3Clone
https://github.com/gregkh/linux.git
git@github.com:gregkh/linux.githttps://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 |
|
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
5make 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/BzImage1
2
3# 编辑C:\Users\17895\.wslconfig
[wsl2]
kernel=C:\\Users\\17895\\bzImage-
1
2
3
4
5[wsl2]
# Enable experimental features
[experimental]
sparseVhd=true1
2
3
4
5
6
7
8
9
10
11
12
13
14
15PS 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 内核相关网站的出现顺序:
- LKML.org
- 简介:LKML(Linux Kernel Mailing List)是 Linux 内核开发者的主要讨论平台。邮件列表自 1991 年 Linux 内核项目开始时就存在,但 LKML.org 作为便于访问和搜索的网页形式出现时间稍晚,具体时间不详。
- 网址:https://lkml.org/
- LWN.net
- 成立时间:1998 年
- 简介:LWN.net 是一个提供 Linux 和开源软件相关新闻和文章的网站。它涵盖了广泛的技术主题,并提供深入的分析。
- 网址:https://lwn.net/
- Patchwork
- 成立时间:2000 年代初
- 简介:Patchwork 由 OzLabs 创建,用于管理邮件列表中的补丁。它帮助开发者跟踪和处理提交的补丁。
- 网址:https://patchwork.ozlabs.org/
- lore.kernel.org
- 成立时间:2018 年
- 简介:lore.kernel.org 是一个托管和搜索 Linux 内核相关邮件列表的平台,提供强大的搜索和索引功能,使开发者更容易找到相关讨论和补丁。
- 网址:https://lore.kernel.org/
Git邮件向Linux社区提交内核补丁
-
本次示例在debian12上操作:
1
sudo apt install b4
1
b4 am -o - 20241008094325.896208-1-leitao@debian.org | git am -3
内核源码结构
内核源码在线阅读
Linux 0.11
- 品读 Linux 0.11 核心代码
- Linux内核完全注释(修正版v3.0).pdf
- 调试 Linux 最早期的代码
- Linux-0.11操作系统源码调试-在 Ubuntu22 上
- https://github.com/yuan-xy/Linux-0.11
Linux 启动过程
内核启动参数
日志级别
debug
1 |
|
在 Linux 内核启动参数中,debug
是一个
全局调试开关,它会显著增加内核运行时输出的日志量。以下是启用
debug
参数后,内核会输出的 主要日志类型
及其作用:
1. 内核通用日志(所有子系统)
• 日志级别:强制设置为 KERN_DEBUG
(级别
7
),输出所有优先级 ≤7 的日志。 •
包含内容: • 内核初始化流程(如 CPU 检测、内存映射)。
• 设备驱动加载(如存储控制器、USB、GPU)。 • 中断和调度事件。 •
文件系统挂载(如
ext4
、XFS
、btrfs
)。 •
网络协议栈初始化。
2. 动态调试日志(Dynamic Debug)
• 触发条件:debug
会隐式激活部分内核模块的 动态调试输出(需模块支持)。 •
典型日志: •
驱动探测(probe
)和初始化细节。 • 硬件寄存器读写(如 PCIe
设备配置)。 • 文件系统元数据操作(如 XFS 的 inode 读写)。 • 块设备 I/O
请求(如磁盘读写队列)。
3. 关键子系统日志
(1) 存储和文件系统
• SCSI/SATA/NVMe 磁盘检测。 • LVM/RAID 设备组装。 •
文件系统挂载错误(如 superblock
损坏)。 • 加密卷解锁(如
LUKS)。
(2) 网络
• 网卡驱动加载(如 igb
、e1000e
)。 • IP
地址分配(DHCP 或静态)。 • 防火墙规则初始化(如
iptables
/nftables
)。
(3) 电源管理
• CPU 频率调整(cpufreq
)。 • ACPI
事件(如休眠/唤醒)。
4.
initramfs
阶段日志(需配合
rd.debug
)
如果同时使用 rd.debug
,还会输出 initramfs
内部的详细操作: • 设备节点创建(udev
规则执行)。 •
存储解密(如 cryptsetup
)。 • 根文件系统挂载尝试。 •
dracut
脚本的执行流程。
5. 日志示例(实际输出片段)
1 |
|
6. 注意事项
- 日志量极大:
debug
会导致日志刷屏,建议通过串口控制台或journalctl -k
捕获。 - 性能影响:
生产环境慎用,可能降低启动速度。 - 需结合过滤工具:
使用grep
或dmesg --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 |
|
1 |
|
进入系统后:
1 |
|
printk
grub
grub doc
- GNU GRUB Manual 2.12
- 6.1 Simple configuration handling /etc/default/grub
- 17.3.1 serial
- 15 GRUB environment variables
grub命令
/etc/defualt/grub
/etc/default/grub示例配置: 1
2
3
4
5
6GRUB_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)
。 - 检查发现
vmlinuz
和initramfs
文件位于(hd1,gpt2)
分区。 - 查看
(hd0,gpt3)/etc/fstab
,确认根分区(/
)挂载在/dev/sda3
。 - 最终目标:手动加载内核并启动系统。
列出所有分区,确认文件位置
检查1
2grub> ls
(hd0),(hd1),(hd1,gpt3),(hd1,gpt2),(hd1,gpt1)(hd1,gpt2)
分区,确认包含vmlinuz
和initramfs
文件:
假设输出显示1
grub> ls (hd1,gpt2)/
vmlinuz
和initramfs
存在。设置根分区为包含内核的分区
将GRUB的root
变量设置为(hd1,gpt2)
,因为内核文件在此分区:
1
grub> set root=(hd1,gpt2)
查找真正的根分区挂载点
查看(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加载Linux内核
指定内核文件路径和根分区:
1
grub> linux /vmlinuz root=/dev/sda3 ro
/vmlinuz
是内核文件(假设位于(hd1,gpt2)/vmlinuz
)。root=/dev/sda3
指定根分区。ro
表示以只读模式启动。
加载initramfs文件
指定初始内存盘文件:
1
grub> initrd /initramfs.img
/initramfs.img
是initramfs文件(假设位于(hd1,gpt2)/initramfs.img
)。
启动系统
执行启动命令:
1
grub> boot
重新生成initramfs
使用 dracut 生成系统中所有内核版本的 initramfs:
1 |
|
grub串口
- 配置Grub2实现串口终端控制Linux系统
- Linux串口调试配置_GRUB串口通信_串口登陆
- 针对于grub的让开机信息重定向输出到串口上(内含精简操作指南)
- FT2000+模块在麒麟系统下串口输出功能调试
将GRUB输出重定向到串口步骤如下:
同时启用图形终端和串行终端,并设置了串行终端的参数
1
2
3
4
5
6
7
8
9
10
111 # 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 菜单和启动过程。
最大程度开启grub调试信息
1
grub-editenv /boot/grub/grubenv set debug=all
更新 GRUB 以应用更改
1
update-grub
grub源码
BIOS, UEFI, MBR, GPT, GRUB
grub中更改默认启动内核
centos中更改默认启动内核
在grub.cfg中查找内核启动选项:
1 |
|
可以进一步使用awk
从 /boot/grub2/grub.cfg
文件中提取了 menuentry
行并显示相应的启动项名称:
1 |
|
1 |
|
-F\'
:将单引号('
)作为字段分隔符。这样,menuentry
的名称(即系统引导项的名称)会出现在$2
中。$1=="menuentry "
:这个条件表示只处理以"menuentry "
开头的行。$1
是使用-F\'
分隔符之后的第一部分,所以$1
应该是"menuentry "
。{print i++ " : " $2}
:当匹配到"menuentry "
行时,输出一个计数器i
和$2
(也就是menuentry
的名称部分)。i++
会在每次输出后递增,给每个菜单项分配一个编号。
debian12中更改默认启动内核
1 |
|
1 |
|
1 |
|
更改默认启动内核
GRUB菜单项的索引是从0开始计算的,这意味着第一个菜单项的索引为0,第二个菜单项的索引为1,以此类推。
将第三个GRUB菜单项设置为默认启动项:
1 |
|
验证当前的默认启动项
1 |
|
grub中禁止某个驱动加载
1 |
|
1 |
|
1 |
|
1 |
|
grubby
1. grubby
命令简介
grubby
是一个用于管理和配置 Linux
系统的引导加载程序(主要针对 GRUB 和
GRUB2)的工具,通常用于修改内核启动参数、默认内核、引导条目等。它在许多基于
Red Hat 的发行版(如 RHEL、CentOS、Fedora)以及其他使用 GRUB
的系统中广泛使用。
- 功能:
- 查看、添加、删除或修改 GRUB 配置文件中的内核引导条目。
- 配置内核命令行参数(例如
ro
、crashkernel
或kfence.sample_interval
)。 - 设置默认启动内核或调整引导顺序。
- 支持多内核管理,方便在系统上维护多个内核版本或配置。
- 常用场景:
- 更新 GRUB 配置以启用调试参数(如 KFENCE 或 KASAN)。
- 切换默认内核版本(例如从
5.10.0-136.12.0.90.kfence
到5.10.0-136.12.0.90.kasan
)。 - 添加硬件相关参数(如
iommu=pt
或hugepages
)。 - 管理救援模式(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
32grubby --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 参数:
将采样间隔设置为 200ms。1
grubby --update-kernel=/boot/vmlinuz-5.10.0-136.12.0.90.kfence.ctl3.x86_64 --args="kfence.sample_interval=200"
切换到 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
linux kernel: defconfig和.config
1
make savedefconfig
clean
-
1
2
3make 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
:
这个目标生成的是一个包含已编译内核二进制文件、头文件、模块、配置文件等内容的 Debian 二进制包(binary package)。
Debian 二进制包的形式为
linux-image-<version>_<architecture>.deb
。这个包通常用于直接在 Debian 或基于 Debian 的系统上安装内核二进制文件,而不需要重新构建整个内核。
x86上交叉编译arm内核deb
要在 x86 架构 的机器上使用
make bindeb-pkg
为 ARM 架构交叉编译内核
.deb
包,可以按照以下步骤进行设置:
1. 安装必要的工具
在主机上安装交叉编译工具链和必要的软件包:
1 |
|
如果目标是 64 位 ARM:
1 |
|
2. 准备内核源码
确保你已经下载并解压了正确版本的 Linux 内核源码,例如:
1 |
|
3. 配置交叉编译环境
为交叉编译设置工具链: - 对于 32 位 ARM: 1
2export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabi-1
2export 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. 注意事项
- 内核配置的适配性
确保使用了正确的内核配置(.config
),以支持目标 ARM 硬件。 - 模块支持
如果内核需要额外模块,确保在.config
中启用对应选项。 - 检查工具链兼容性
工具链版本需与目标架构和内核源码兼容。
完成后,你可以将生成的 .deb
包传输到目标 ARM
设备上安装。
linux-perf deb
在deepin 23上构建 linux-perf deb包
在debian12中下载内核源码:
1 |
|
将内核原源码拷贝到deepin 23上:
1 |
|
在deepin 23 上构建deb包(包含 linux-perf):
1 |
|
1 |
|
内核调试包
- Ubuntu Debug Symbol Packages
- Linux内核调试的方式以及工具集锦
- 如何下载或安装 RHEL 系统的内核调试信息包?
- Linux 之十八 Ubuntu 22.04 配置内核版本、GRUB 引导、远程桌面、包后缀(-dev、-dbg等)
- (转载)ubuntu 安装 dbgsym (debug-info)
- dpkg-buildpackage error
编译内核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 |
|
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并解压:
1 |
|
建议使用下方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交叉编译工具链后进行如下操作:
1 |
|
也可使用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 |
|
1 |
|
交叉编译bpftool、perf、kvm_stat还有问题,故暂时采用上一小节改动kernel.spec使用rpmbuild -ba --target=aarch64 --define "_host_cpu aarch64" --without=bpftool --without=perf --without=kvm_stat build/kernel.spec
方式进行交叉编译。
内核模块
Linux内核驱动学习-编写最简单Linux内核模块HelloWorld
1
2
3
4
5
6
7
8
9
10KVERS = $(shell uname -r)
# Kernel modules
obj-m += hello.o
# Specify flags for the module compilation.
#EXTRA_CFLAGS=-g -O0
build: kernel_modules
kernel_modules:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
clean:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean-
1
make CONFIG_SPI_PHYTIUM_PLAT=m -C /data3/home/yuanqiliang/code/arm-kernel-5.10/kernel M=/data3/home/yuanqiliang/code/arm-kernel-5.10/kernel/drivers/spi modules
KBUILD_EXTRA_SYMBOLS
dkms
地址空间随机化
内核 C 语言
- GUN C编译器拓展语法学习笔记(一)GNU C特殊语法部分详解
- GUN C编译器拓展语法学习笔记(二)属性声明
- GUN C编译器拓展语法学习笔记(三)内联函数、内建函数与可变参数宏
- 两种 C 语言之间的差异
- linux内核中6个经典C语言函数代码赏析
- Linux内核中常用的C语言技巧
- 弱符号__attribute__((weak))
- linux kernel中__setup()函数介绍
likely和unlikely
内核section
EXPORT_SYMBOL
VA_ARGS
__VA_ARGS__
是一个在宏定义中使用的预处理器标识符,用于表示可变数量的参数。它允许在宏中接受并处理任意数量的参数。
用途: 处理可变参数列表。
语法:
__VA_ARGS__
在宏定义中占位,表示传递给宏的所有额外参数。示例:
1
#define PRINT(fmt, ...) printf(fmt, __VA_ARGS__)
使用宏
PRINT
可以传递任意数量的参数:1
2PRINT("Value: %d\n", 42);
PRINT("Values: %d, %d\n", 1, 2);
在宏 PRINT
中,fmt
是格式化字符串,而
__VA_ARGS__
被替换为 printf
的其他参数。
内核链表
- 玩转内核链表list_head,教你管理不同类型节点的实现,建议收藏
- Linux内核中经典链表 list_head 常见使用方法解析
- Linux内核中的常用宏container_of其实很简单
- Linux内核中container_of的原理及其使用详解
- Linux 内核 hlist 详解
- hlist用法以及hlist_for_each_entry 使用举例
sysfs
内核启动流程
vmlinux.lds.S
0号进程,1号进程,2号进程
根文件系统rootfs
initramfs
initrd 和 initramfs
制作initramfs
initramfs解压
lsinitramfs
在Linux的GRUB启动配置中,可以通过 initramfs调试参数 来排查系统启动问题(如文件系统挂载失败、驱动加载问题等)。以下是常用的调试参数及其作用:
grub中initramfs调试参数
- dracut.cmdline - dracut kernel command line options
- initramfs-tools - an introduction to writing scripts for mkinitramfs
1. 常用 initramfs
调试参数
(1) rd.debug
• 作用:启用 dracut
(或
initramfs
)的详细调试日志。 • 示例:
1
rd.debug
initramfs
阶段的所有脚本执行过程,适合排查启动流程问题。
(2) rd.break
• 作用:在 initramfs
启动过程中暂停,进入交互式 Shell。 • 常用断点:
1
2
3rd.break=pre-mount # 在挂载根文件系统前暂停
rd.break=pre-pivot # 在切换到真实根文件系统前暂停
rd.break=pre-trigger # 在 systemd 启动前暂停e
编辑内核参数,添加 rd.break=pre-mount
。 2.
启动后会进入 initramfs
的 Shell,可手动执行命令(如
mount
、lsblk
)。 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
2rd.driver.blacklist=nouveau # 禁用 nouveau 驱动
rd.driver.pre=ahci # 优先加载 ahci 驱动
(6) rootdelay
• 作用:延长 initramfs
等待根设备的时间(秒)。 • 示例: 1
rootdelay=30
(7) rd.lvm.vg
/
rd.lvm.lv
• 作用:手动指定 LVM 卷组或逻辑卷。 •
示例: 1
rd.lvm.vg=vg00 rd.lvm.lv=vg00/root
(8) rd.live.ram
• 作用:将 Live CD 的 initramfs
完全加载到内存。 • 示例: 1
rd.live.ram
(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
2. 组合使用示例
示例 1:调试根文件系统挂载失败
1 |
|
• 作用: • 打印详细日志(rd.debug
)。 •
在挂载根文件系统前暂停(rd.break=pre-mount
)。 •
延长设备等待时间(rootdelay=30
)。
示例 2:排查 LVM + LUKS 启动问题
1 |
|
• 作用: • 打印详细日志(rd.debug
)。 •
手动指定 LVM 卷组(rd.lvm.vg
)。 • 手动指定 LUKS 设备
UUID(rd.luks.uuid
)。
3. 如何应用这些参数?
在 GRUB 启动菜单编辑内核参数: • 启动时按
e
进入编辑模式。 • 在linux
或linux16
行末尾添加调试参数(如rd.debug rd.break=pre-mount
)。 • 按Ctrl+X
或F10
启动。永久修改 GRUB 配置(谨慎操作):
• 修改1
sudo nano /etc/default/grub
GRUB_CMDLINE_LINUX
,例如:• 更新 GRUB:1
GRUB_CMDLINE_LINUX="rd.debug rd.shell"
1
2sudo update-grub # Debian/Ubuntu
sudo grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL/CentOS
4. 调试技巧
• 查看日志: • dmesg
:内核日志。 •
journalctl -xb
:systemd 日志。 •
/run/initramfs/init.log
:dracut
日志(需在
initramfs
Shell 中查看)。
• 手动挂载根文件系统(在 rd.break
Shell
中): 1
2
3mkdir /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
- Linux 驱动开发 二:module_init机制
- initcall 机制
- linux内核中do_initcalls函数的执行逻辑分析
- initcall_debug来查看开机慢问题
- Linux 各种 initcall 的调用原理
- 【Linux内核源码分析】initcall机制与module_init
系统调用
-
安装 POSIX 系统帮助手册:
1
sudo apt-get install manpages-posix-dev
查看所有系统调用:
1
man syscalls
查看open系统调用:
1
2man open
man 2 open
ERESTARTSYS
open openat
uid和euid
- 理解Effective UID(EUID)和Real UID(RUID)
- Linux进程的uid和euid
- geteuid()和getuid()的区别
- getuid() 与 geteuid() 获得 UID 以及 有效 UID 值
- linux c setuid函数解析
内核单元测试
- 如何测试Linux内核?
- KUnit和kselftest的区别
- 关于kunit的一点够用就行知识概念
- KUnit - Linux Kernel Unit Testing
- 深入了解KUnit:Linux内核新一代单元测试工具(上)
- LWN:Linux kernel要有几种test framework?
Linux 内核单元测试主要通过两大框架:
- Kselftest:
- 适用于集成测试和功能测试,支持用户空间和内核空间交互。
- 测试代码在
tools/testing/selftests/
目录,运行命令:make -C tools/testing/selftests run_tests
。
- KUnit:
- 轻量级单元测试框架,专注内核态的单元测试。
- 测试代码在
lib/kunit/
,运行命令:./tools/testing/kunit/kunit.py run
。
Kselftest 适合功能测试,KUnit 适合内核模块的单元测试。
内核gcov代码覆盖率
内核gcov编译选项
在 Linux 4.19.90 内核源码中开启 gcov (代码覆盖率分析工具)需要设置以下编译选项:
1 |
|
- CONFIG_GCOV_KERNEL: 启用内核模块的代码覆盖率分析。
- CONFIG_GCOV_PROFILE_ALL: 对所有内核子系统启用代码覆盖率分析。
编译内核后安装重启。
查看内核代码覆盖率
进入到内核源码目录:
1 |
|
1 |
|
1 |
|
1 |
|
kpatch
编译安装测试内核
本教程以openeuler-4-19为例,编译安装测试内核。
1 |
|
自定义内核版本号
1 |
|
安装编译依赖
1 |
|
编译
1 |
|
1 |
|
1 |
|
制作补丁文件
1 |
|
编译更新补丁工具
1 |
|
热补丁制作
环境准备
安装依赖软件包:
1 |
|
安装当前内核源码和开发包(这里是上面带ksmd的rpmb包):
1 |
|
进入热补丁制作目录并准备环境
1 |
|
开始制作热补丁
给make_hotpatch执行权限:
1 |
|
1 |
|
1 |
|
补丁制作完成,补丁文件以压缩包的格式存放于/opt/patch_workspace/hotpatch目录下。
管理热补丁
1 |
|
加载热补丁
1 |
|
激活热补丁
1 |
|
回退热补丁
1 |
|
卸载热补丁
1 |
|
异常分类

根据图片中关于 异常分类(Fault/Trap/Abort) 的详细定义,我将用更具体的 代码示例 和 执行流程 来演示这三类异常的区别,并结合底层机制进行解析。
1. Fault(故障)示例:缺页异常(Page Fault)
场景
程序访问一个未加载到物理内存的虚拟地址,触发缺页异常。 ####
详细流程
1
2
3// 示例代码(C语言)
int *ptr = (int *)0x1000; // 假设 0x1000 是未映射的虚拟地址
int value = *ptr; // 触发 Page Fault
CPU 执行 mov eax, [0x1000]
时,发现该地址未映射到物理内存,触发 Fault 类异常。 2.
状态恢复:
• CPU 保存当前状态(寄存器、指令指针等),并将返回地址指向 导致
Fault 的指令(即 mov eax, [0x1000]
)。
• 控制权转交给操作系统的缺页处理程序。 3.
纠正处理:
• 操作系统从磁盘加载缺失的页到内存,并更新页表。
4. 重新执行:
• 返回后,CPU
重新执行原指令,此时内存访问成功,程序继续运行。
关键点
• Fault 的返回地址指向
原指令,确保纠正后能重试。
• 可恢复,是 被动触发的异常(如缺页、除零)。
2. Trap(陷阱)示例:系统调用(Syscall)
场景
用户程序通过 int 0x80
或 syscall
指令主动触发系统调用。 #### 详细流程(以 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,进入内核态
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(); // 无限递归,栈溢出
}
• 程序触发缺页异常(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。