网站首页 > 开源技术 正文
版本信息
宿主机:ubuntu 20.04.6 LTS (Focal Fossa)
虚拟机:ubuntu 20.04.6 LTS (Focal Fossa)
安装宿主机的步骤省略,和一般的在vmware中安装虚拟机没有任何区别。
需要注意的是需要打开Intel VT-x
如果启动虚拟机报告此平台不支持虚拟化的Intel VT-x/EPT. 不使用虚拟化的Intel VT-x/EPT,是否继续,参考下面的文章解决.
https://blog.csdn.net/2301_77695535/article/details/146309899
宿主机安装QEMU/KVM和Virsh
Virsh是Virtual Shell的缩写,是一个用于管理虚拟机的命令行工具。你可以使用Virsh创建、编辑、启动、停止、关闭和删除VM。Virsh目前支持KVM,LXC,Xen,QEMU,OpenVZ,VirtualBox和VMware ESX。这里我们使用Virsh管理QEMU/KVM虚拟机。
在安装之前,首先要确认你的CPU是否支持虚拟化技术。使用grep查看cpuinfo是否有"vmx"(Intel-VT 技术)或"svm"(AMD-V 支持)输出:
egrep "(svm|vmx)" /proc/cpuinfo
某些CPU型号在默认情况下,BIOS中可能禁用了VT支持。我们需要再检查BIOS设置是否启用了VT的支持。使用kvm-ok命令进行检查:
sudo apt install cpu-checker
kvm-ok
如果输出为:
INFO: /dev/kvm exists
KVM acceleration can be used
证明CPU的虚拟化支持已经在BIOS中启用。
运行下面的命令安装QEMU/KVM和Virsh:
sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager
检查libvirt守护程序是否已经启动:
sudo systemctl is-active libvirtd
active
如果没有输出active,运行下面的命令启动libvertd服务:
sudo systemctl enable libvirtd
sudo systemctl start libvirtd
在宿主机中安装qemu虚拟机
创建一个虚拟机镜像,大小为40G,qow2格式动态分配磁盘占用空间。
qemu-img create -f qcow2 ubuntutest.img 40G
创建虚拟机系统,安装操作系统:
qemu-system-x86_64 \
-name ubuntutest \
-smp 2 \
-m 4096 \
-hda ubuntutest.img \
-cdrom ubuntu-20.04.6-live-server-amd64.iso \
-boot d
按照步骤,配置安装即可,这一步和正常的虚拟机安装没有什么区别。
注意这里我没有添加-enable-kvm,这可能会影响gdb的软件断点。
配置上网
方案1:自建tap网卡NAT上网
宿主机创建TAP设备
sudo ip tuntap add dev tap0 mode tap
sudo ip addr add 192.168.100.1/24 dev tap0
sudo ip link set tap0 up
宿主机配置IP转发及NAT规则
sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -o ens33 -j MASQUERADE
sudo iptables -A FORWARD -i tap0 -j ACCEPT
启动带TAP的QEMU
qemu-system-x86_64 \
-enable-kvm \
-m 4096 \
-drive file=./ubuntutest.img \
-boot d \
-net nic -net tap,ifname=tap0,script=no,downscript=no
配置虚拟机的网络为192.168.100.0/24网段中的任意一个地址(例如192.168.100.10)
打开netplan的配置文件,修改地址后,使用sudo netplan apply重启网络。
# This is the network config written by 'subiquity'
network:
ethernets:
ens3:
dhcp4: no
addresses:
- 192.168.100.10/24
routes:
- to: default
via: 192.168.100.1
nameservers:
addresses: [8.8.8.8]
version: 2
利用virbr0实现nat上网
创建并配置bridge.conf
- 文件路径:通常位于/etc/qemu/或/usr/local/etc/qemu/(取决于安装方式)。使用以下命令创建:
sudo mkdir -p /etc/qemu
sudo vim /etc/qemu/bridge.conf
内容示例:
allow virbr0 # 若使用libvirt默认桥接接口
接下来启动时指定使用virbr0 bridge上网。
qemu-system-x86_64 \
-enable-kvm \
-m 4096 \
-drive file=./ubuntutest.img -boot d \
-netdev bridge,id=net0,br=virbr0 \
-device virtio-net-pci,netdev=net0
配置虚拟机网络时,配置和virbr0一个网段的,即192.168.122.0/24(例如192.168.122.10)
下载linux内核并且编译
安装编译依赖
首先我们需要安装编译内核用到的依赖包,我是在宿主机上编译linux内核代码的,因此下面的语句直接在宿主机上执行。
sudo apt install libncurses5-dev libssl-dev bison flex libelf-dev gcc make openssl libc6-dev dwarves
下载linux内核代码并构建
下载Linux内核代码,这里我使用的是5.10.209版本
https://www.kernel.org/pub/linux/kernel/v5.x/linux-5.10.209.tar.gz
使用下面的语句make kernel编译参数:
sudo make menuconfig
为了构建能够调试的内核,我们需要配置以下几个参数。
- CONFIG_DEBUG_INFO 在内核和内核模块中包含调试信息,这个选项在幕后为gcc使用的编译器参数增加了-g选项。
这个选项的菜单路径为:
Kernel hacking --->
Compile-time checks and compiler options --->
[*] Compile the kernel with debug info
也可以直接在.config中设置CONFIG_DEBUG_INFO。
CONFIG_DEBUG_INFO=y
- CONFIG_FRAME_POINTER 这个选项会将调用帧信息保存在寄存器或堆栈上的不同位置,使gdb在调试内核时可以更准确地构造堆栈回溯跟踪(stack back traces)。
也可以在.config中设置:
CONFIG_FRAME_POINTER=y
- 启用CONFIG_GDB_SCRIPTS,但要关闭CONFIG_DEBUG_INFO_REDUCED。
CONFIG_GDB_SCRIPTS=y
CONFIG_DEBUG_INFO_REDUCED=n
- CONFIG_KGDB 启用内置的内核调试器,该调试器允许进行远程调试。
Kernel hacking --->
Generic Kernel Debugging Instruments --->
[*] KGDB: kernel debugger
CONFIG_KGDB=y
- 关闭CONFIG_RANDOMIZE_BASE设置
CONFIG_RANDOMIZE_BASE的位置在如下位置可以找到:
Processor type and features --->
Randomize the address of the kernel image (KASLR)
或者直接在.config中添加下面的语句
CONFIG_RANDOMIZE_BASE=n
KASLR会更改引导时放置内核代码的基地址。如果你在内核配置中启用了KASLR(CONFIG_RANDOMIZE_BASE=y),则无法从gdb设置断点。设置完必要的内核参数后,我们开始编译内核:
sudo make -j8
sudo make INSTALL_MOD_STRIP=1 modules_install
sudo make install
make modules_install会将module文件安装到/lib/modules/5.10.209,并且最好添加上INSTALL_MOD_STRIP=1,否则initrd.img体积会很大。
编译大概需要30G空间,因此需要事先准备好>=30G的磁盘。
这些步骤执行完毕之后,我们就得到了需要的linux内核镜像bzImage(vmlinuz)和initrd.img
xu@xu-dev:~/work/linux-5.10.209$ ls /boot/ -alh |grep 5.10.209
-rw-r--r-- 1 root root 243K 4月 20 19:34 config-5.10.209
lrwxrwxrwx 1 root root 19 4月 20 19:34 initrd.img -> initrd.img-5.10.209
-rw-r--r-- 1 root root 60M 4月 20 19:34 initrd.img-5.10.209
-rw-r--r-- 1 root root 5.5M 4月 20 19:34 System.map-5.10.209
lrwxrwxrwx 1 root root 16 4月 20 19:34 vmlinuz -> vmlinuz-5.10.209
-rw-r--r-- 1 root root 14M 4月 20 19:34 vmlinuz-5.10.209
编译的产物介绍
在Linux内核编译过程中,生成的文件根据功能可分为以下几类,以下是详细介绍及对应的文件作用与来源:
核心可执行文件
- vmlinux 描述:原始的、未经压缩的内核可执行文件,包含完整的符号表和调试信息,体积较大。 生成路径:位于内核源码根目录或 arch/<架构>/boot/compressed/ 目录下。 用途:用于调试和分析内核崩溃问题,不直接用于启动系统。 来源:编译过程通过链接 head-y、init-y、core-y 等目标文件生成 。
- zImage** 与 **bzImage 描述:压缩后的内核镜像文件,bzImage(Big zImage)支持更大体积的内核(超过512KB时使用)。 生成路径:位于 arch/<架构>/boot/ 目录下(如 arch/x86/boot/bzImage)。 结构:由 setup.bin(引导程序)和压缩后的 vmlinux 拼接而成,附加解压头信息。 用途:直接用于系统引导,是大多数Linux发行版的默认内核文件 。
- uImage 描述:专为U-Boot引导程序设计的镜像文件,在 zImage 基础上添加U-Boot头部信息。 生成路径:需通过 mkimage 工具处理 zImage 生成。 用途:嵌入式系统中配合U-Boot使用 。
引导相关文件
- System.map 描述:内核符号映射文件,记录所有函数和变量的内存地址。 生成路径:内核源码根目录下。 用途:调试时关联地址与符号,例如分析内核崩溃日志 。
- initrd.img** 或 **initramfs 描述:初始内存文件系统,包含启动早期所需的临时驱动和工具(如磁盘驱动、文件系统模块)。 生成路径:通过 mkinitramfs 或 dracut 生成,存放于 /boot/ 目录。 用途:解决根文件系统挂载前的依赖问题 。
内核模块文件
- .ko** 文件(Kernel Object)** 描述:动态可加载内核模块,按需插入内核运行。 生成路径:各驱动或功能模块目录下(如 drivers/net/ethernet/)。 安装路径:通过 make modules_install 安装到 /lib/modules/<内核版本>/ 目录。 用途:灵活扩展内核功能(如添加新硬件驱动) 。
配置文件与日志
- .config 描述:内核编译的配置文件,记录所有选中的功能选项(如 CONFIG_DEBUG_INFO=y)。 生成路径:内核源码根目录下。 来源:通过 make menuconfig 或复制默认配置(如 arch/arm/configs/s5pv210_defconfig)生成 。
- vmlinux.lds 描述:链接脚本文件,定义内核各段(代码、数据、堆栈)的内存布局。 生成路径:arch/<架构>/kernel/ 目录下。 用途:指导链接器生成 vmlinux 。
中间文件与工具生成文件
- .o** 与 **built-in.o 描述:编译过程中生成的中间目标文件,built-in.o 是同一目录下所有 .o 文件的合并。 生成路径:各子模块目录下(如 init/built-in.o)。 用途:逐步构建最终内核镜像 。
- 设备树文件(.dtb) 描述:描述硬件拓扑结构的二进制文件,用于嵌入式系统(如ARM架构)。 生成路径:通过设备树编译器(DTC)从 .dts 文件生成,位于 arch/<架构>/boot/dts/。 用途:适配不同硬件平台 。
启动管理与版本标识
- vmlinuz-<版本号> 描述:安装到 /boot/ 目录的压缩内核镜像,通常为 bzImage 的符号链接。 用途:Grub等引导程序通过该文件加载内核 。
- config-<版本号> 描述:.config 文件的备份,保存编译时的完整配置。 生成路径:/boot/ 目录下 。
总结对比表
文件类型 | 关键文件 | 作用 | 生成命令 |
核心镜像 | vmlinux, bzImage | 内核执行与引导 | make, make bzImage |
模块 | .ko | 动态扩展内核功能 | make modules |
配置 | .config | 记录编译选项 | make menuconfig |
引导支持 | initrd.img | 早期启动依赖加载 | mkinitramfs |
符号映射 | System.map | 调试符号地址映射 | 自动生成 |
设备树 | .dtb | 嵌入式硬件描述 | make dtbs |
内核debug
在获取内核镜像bzImage和initrd.img之后,就可以使用其启动qemu虚拟机。
注意我这里的调试是使用qemu的-kernel和-initrd去直接加载内核,而没有使用grub去加载内核。
我的qemu的启动脚本如下所示:
qemu-system-x86_64 \
-smp 2 \
-m 4096 \
-S -s \
-drive file=/home/xu/work/ubuntutest.img \
-netdev bridge,id=net0,br=virbr0 \
-device virtio-net-pci,netdev=net0 \
-kernel /home/xu/work/kernel-with-rwx/bzImage \
-initrd /home/xu/work/kernel-with-rwx/initrd.img-5.10.209 \
-append "root=/dev/mapper/ubuntu--vg-ubuntu--lv ro maybe-ubiquity console=ttyS0\
-nographic
- -smp 2 分配2个虚拟CPU核心(vCPU),默认情况下每个核心为单线程、单插槽。若需更细粒度控制(如多插槽、多线程),可扩展为-smp 2,sockets=1,cores=2,threads=1
- -m 4096 为虚拟机分配4096MB(4GB)内存。QEMU默认分配128MB,此参数需根据宿主机资源和虚拟机需求调整
- -S 启动时暂停CPU执行,等待外部调试器(如GDB)连接后继续运行,常用于内核调试
- -s 启用GDB调试服务器,默认监听本地端口1234。结合-S可实现从启动阶段调试内核
- -drive file=/home/xu/work/ubuntutest.img 加载名为ubuntutest.img的磁盘镜像作为主硬盘。默认接口类型为if=virtio(高性能虚拟化驱动),若未指定格式,QEMU自动检测(如qcow2或raw)
- -netdev bridge,id=net0,br=virbr0 创建桥接网络后端,连接到宿主机的virbr0网桥,允许虚拟机通过宿主机网络接口访问外部 。
- -device virtio-net-pci,netdev=net0 为虚拟机添加虚拟网卡,使用virtio-net-pci驱动(高性能半虚拟化网卡),绑定到上述网络后端。
- -kernel /home/xu/work/kernel-with-rwx/bzImage 指定Linux内核镜像文件bzImage,绕过虚拟机的BIOS引导,直接加载内核
- -initrd /home/xu/work/kernel-with-rwx/initrd.img-5.10.209 使用initrd.img-5.10.209作为初始内存文件系统,包含启动初期所需的模块和工具
- -append "root=/dev/mapper/ubuntu--vg-ubuntu--lv ro maybe-ubiquity console=ttyS0"``root=/dev/mapper/ubuntu--vg-ubuntu--lv:指定根文件系统位置(LVM逻辑卷)。ro:以只读模式挂载根文件系统,通常需后续切换为读写模式。console=ttyS0:将控制台输出重定向到串口ttyS0,配合-nographic使用。(ubuntu系统通过df -h查看根文件系统的路径)。
上面提到,还有一种是使用grub启动内核,相对要麻烦一点,需要把宿主机上的内核拷贝到虚拟机上,并执行make modules_install和make install,然后启动时使用-boot d从硬盘加载内核。
qemu-system-x86_64 \
-enable-kvm \
-smp 2 \
-m 4096 \
-drive file=./ubuntutest.img -boot d \
-netdev bridge,id=net0,br=virbr0 \
-device virtio-net-pci,netdev=net0
不过我觉得使用-kernel和-initrd更为方便,也更加推荐。
接下来启动gdb调试,使用另一个终端打开gdb:
xu@xu-dev:~/work/linux-5.10.209$ gdb vmlinux -q
Reading symbols from vmlinux...
warning: File "/home/xu/work/linux-5.10.209/scripts/gdb/vmlinux-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
add-auto-load-safe-path /home/xu/work/linux-5.10.209/scripts/gdb/vmlinux-gdb.py
line to your configuration file "/home/xu/.gdbinit".
To completely disable this security protection add
set auto-load safe-path /
line to your configuration file "/home/xu/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual. E.g., run from the shell:
info "(gdb)Auto-loading safe path"
使用gdb远程连接:
(gdb) target remote :1234
Remote debugging using :1234
0x000000000000fff0 in exception_stacks ()
下断点
(gdb) b start_kernel
Breakpoint 1 at 0xffffffff82daad61: file init/main.c, line 847.
继续执行,触发断点:
(gdb) c
Continuing.
Thread 1 hit Breakpoint 1, start_kernel () at init/main.c:847
单步调试:
847 {
(gdb) n
851 set_task_stack_end_magic(&init_task);
(gdb) n
852 smp_setup_processor_id();
(gdb) n
855 cgroup_init_early();
(gdb)
问题
证书找不到
CC [M] kernel/kheaders.o
CC certs/system_keyring.o
make[1]: *** No rule to make target 'debian/canonical-certs.pem', needed by 'certs/x509_certificate_list'. Stop.
make: *** [Makefile:1832: certs] Error 2
解决办法:
1.修改
CONFIG_SYSTEM_TRUSTED_KEYS
修改前:原变量有值
CONFIG_SYSTEM_TRUSTED_KEYS="
debian/canonical-certs.pem"
修改后:将该变量赋空值
CONFIG_SYSTEM_TRUSTED_KEYS=""
2.修改
CONFIG_SYSTEM_REVOCATION_KEYS(可选)
如果
CONFIG_SYSTEM_REVOCATION_KEYS的值不为空的话,也将其赋空值。
修改前:原变量有值
CONFIG_SYSTEM_REVOCATION_KEYS="
debian/canonical-revoked-certs.pem"
修改后:将该变量赋空值
CONFIG_SYSTEM_REVOCATION_KEYS=""
内核停留在“loading initial ramdisk”很长时间并且启动之后kernel crash报告引导内核时报错Kernel panic not syncing : System is deadlocked on memory
编译内核的过程中,当安装内核模块未使用INSTALL_MOD_STRIP=1时,会导致initrd文件过大,导致启动时卡在解压initrd上花费过度的时间。
例如这里,我的initrd.img是1.2G。
为了解决该问题,需要在安装module的时候添加INSTALL_MOD_STRIP=1
sudo make INSTALL_MOD_STRIP=1 modules_install
sudo make install
kernel crash报告引导内核时报错Kernel panic not syncing : System is deadlocked on memory
这个问题和initrd.img相关。一种方式是通过减小initrd.img解决,还有一种方式就是通过调大虚拟机内存解决。
gdb软件中断无法触发,硬件中断可以触发
qemu如果启动时指定了-enable-kvm,会导致该问题。
如果启动的时候指定了-enable-kvm,则设置断点时需要使用硬件断点,例如
hb start_kernel
PS
如何清理内核编译产物
若在编译并安装 Linux 内核后希望卸载,以下是具体操作步骤及注意事项:
步骤 1:确认内核版本及安装路径
- 查看已安装的内核版本:
ls /boot | grep vmlinuz # 列出所有内核文件
uname -r # 查看当前正在运行的内核版本(避免误删)
- 确认手动编译安装的内核版本:
假设目标卸载的内核版本为 5.18.8,需确保其不在 uname -r 的输出中,否则删除后可能导致系统无法启动。
步骤 2:手动删除内核相关文件
手动编译安装的内核文件分散在多个目录中,需按路径逐一删除:
- 删除 /boot 目录下的内核文件:
sudo rm -rf /boot/vmlinuz-5.18.8
sudo rm -rf /boot/initrd.img-5.18.8
sudo rm -rf /boot/config-5.18.8
sudo rm -rf /boot/System.map-5.18.8
- 删除内核模块目录:
sudo rm -rf /lib/modules/5.18.8
- 删除内核源码目录(可选):
sudo rm -rf /usr/src/linux-5.18.8
- 清理 initramfs 残留:
sudo rm -rf /var/lib/initramfs-tools/5.18.8
步骤 3:更新 GRUB 引导配置
删除内核后需更新 GRUB,避免残留无效启动项:
sudo update-grub
步骤 4:重启系统
sudo reboot
重启后通过 uname -r 确认当前内核版本是否已切换至其他可用版本。
参考文章
https://github.com/mz1999/blog/blob/master/docs/gdb-kernel-debugging.md
猜你喜欢
- 2025-05-02 Linux基础运维篇:Linux系统监控工具(第015课)
- 2025-05-02 CentOS 7安装TCP BBR拥塞算法(centos7开启bbr加速)
- 2025-05-02 Emacs 折腾日记(十八)——改变Emacs的样貌
- 2025-05-02 Linux man 命令使用教程(linux man -k)
- 2025-05-02 Linux 源代码makefile文件功能解析
- 2025-05-02 Emacs 折腾日记(十五)——窗口(emacs切换窗口)
- 2025-05-02 空间电推进技术概览及评述(连载之四)
- 2025-05-02 每天LINUX学习:Linux开启VLAN的支持及配置方法
- 2025-05-02 用云存储30分钟快速搭建APP,你信吗?
- 2025-05-02 图文详解SAMA5D2处理器在加载Linux内核前到底做了什么?
你 发表评论:
欢迎- 最近发表
-
- 10款鲜为人知的PHP框架(10款鲜为人知的php框架代码)
- 3分钟搞懂反弹shell(反弹shell的常用命令)
- 计算机专业必须掌握的脚本开发语言—shell
- shell 基本语法(shell基本语法set)
- 学习Shell 教程(shell编程学习)
- 一个有意思的PHP Webshell,利用伪协议执行代码
- Linux入门-shell编程-适合小白(linux shell编程是什么)
- GrayLog开源日志管理平台技术文章合集【共58篇】
- AI大模型 MiniMax 基于 Apache Doris 的日志系统,PB 级秒级查询响应
- 互联网大厂后端必看!手把手教你替换 Spring Boot 中的日志框架
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)