百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

qemu linux内核(5.10.209)开发环境搭建

nanshan 2025-05-09 20:37 21 浏览 0 评论

版本信息

宿主机: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内核编译过程中,生成的文件根据功能可分为以下几类,以下是详细介绍及对应的文件作用与来源:

核心可执行文件

  1. vmlinux 描述:原始的、未经压缩的内核可执行文件,包含完整的符号表和调试信息,体积较大。 生成路径:位于内核源码根目录或 arch/<架构>/boot/compressed/ 目录下。 用途:用于调试和分析内核崩溃问题,不直接用于启动系统。 来源:编译过程通过链接 head-y、init-y、core-y 等目标文件生成 。
  2. zImage** 与 **bzImage 描述:压缩后的内核镜像文件,bzImage(Big zImage)支持更大体积的内核(超过512KB时使用)。 生成路径:位于 arch/<架构>/boot/ 目录下(如 arch/x86/boot/bzImage)。 结构:由 setup.bin(引导程序)和压缩后的 vmlinux 拼接而成,附加解压头信息。 用途:直接用于系统引导,是大多数Linux发行版的默认内核文件 。
  3. uImage 描述:专为U-Boot引导程序设计的镜像文件,在 zImage 基础上添加U-Boot头部信息。 生成路径:需通过 mkimage 工具处理 zImage 生成。 用途:嵌入式系统中配合U-Boot使用 。

引导相关文件

  1. System.map 描述:内核符号映射文件,记录所有函数和变量的内存地址。 生成路径:内核源码根目录下。 用途:调试时关联地址与符号,例如分析内核崩溃日志 。
  2. initrd.img** 或 **initramfs 描述:初始内存文件系统,包含启动早期所需的临时驱动和工具(如磁盘驱动、文件系统模块)。 生成路径:通过 mkinitramfs 或 dracut 生成,存放于 /boot/ 目录。 用途:解决根文件系统挂载前的依赖问题 。

内核模块文件

  1. .ko** 文件(Kernel Object)** 描述:动态可加载内核模块,按需插入内核运行。 生成路径:各驱动或功能模块目录下(如 drivers/net/ethernet/)。 安装路径:通过 make modules_install 安装到 /lib/modules/<内核版本>/ 目录。 用途:灵活扩展内核功能(如添加新硬件驱动) 。

配置文件与日志

  1. .config 描述:内核编译的配置文件,记录所有选中的功能选项(如 CONFIG_DEBUG_INFO=y)。 生成路径:内核源码根目录下。 来源:通过 make menuconfig 或复制默认配置(如 arch/arm/configs/s5pv210_defconfig)生成 。
  2. vmlinux.lds 描述:链接脚本文件,定义内核各段(代码、数据、堆栈)的内存布局。 生成路径:arch/<架构>/kernel/ 目录下。 用途:指导链接器生成 vmlinux 。

中间文件与工具生成文件

  1. .o** 与 **built-in.o 描述:编译过程中生成的中间目标文件,built-in.o 是同一目录下所有 .o 文件的合并。 生成路径:各子模块目录下(如 init/built-in.o)。 用途:逐步构建最终内核镜像 。
  2. 设备树文件(.dtb 描述:描述硬件拓扑结构的二进制文件,用于嵌入式系统(如ARM架构)。 生成路径:通过设备树编译器(DTC)从 .dts 文件生成,位于 arch/<架构>/boot/dts/。 用途:适配不同硬件平台 。

启动管理与版本标识

  1. vmlinuz-<版本号> 描述:安装到 /boot/ 目录的压缩内核镜像,通常为 bzImage 的符号链接。 用途:Grub等引导程序通过该文件加载内核 。
  2. 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:确认内核版本及安装路径

  1. 查看已安装的内核版本
ls /boot | grep vmlinuz    # 列出所有内核文件
uname -r                  # 查看当前正在运行的内核版本(避免误删)
  1. 确认手动编译安装的内核版本
    假设目标卸载的内核版本为 5.18.8,需确保其不在 uname -r 的输出中,否则删除后可能导致系统无法启动。

步骤 2:手动删除内核相关文件

手动编译安装的内核文件分散在多个目录中,需按路径逐一删除:

  1. 删除 /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
  1. 删除内核模块目录
sudo rm -rf /lib/modules/5.18.8
  1. 删除内核源码目录(可选)
sudo rm -rf /usr/src/linux-5.18.8
  1. 清理 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

相关推荐

ssh终端xshell日志查看命令(xshell怎么看日志)

现在我们云服务器运维较多用的是SSH工具,其中常用的包括PUTTY、XSHELL等,其实大同小异界面UI稍微不同,但是都可以进入远程连接。这里有朋友提到如何查看服务器的日志文件,这个其实和是否使用XS...

使用 Fail Ban 日志分析 SSH 攻击行为

通过分析`fail2ban`日志可以识别和应对SSH暴力破解等攻击行为。以下是详细的操作流程和关键分析方法:---###**一、Fail2ban日志位置**Fail2ban的日志路径因系统配置...

如何高效读取Linux日志文件?这些命令要熟记于心!

在Linux系统中,日志文件通常存储在/var/log目录下。比如,/var/log/syslog(或/var/log/messages,视发行版而定)记录系统整体事件,/var/log/a...

Windows服务器远程登录日志查询方法,linux查看登录日志方法

概述本文介绍Windows、Linux服务器查询系统的远程登录日志方法。根据服务器所使用的操作系统不同,有以下两种查询方法。Linux操作系统的登录日志查询通过远程连接登录Linux服务器,使用roo...

iptables防火墙如何记录日志(防火墙日志查看)

例如:记录所有ssh服务的登录的日志首先,我们需要了解如何将所有的iptables的INPUT链数据包记录到/var/log/messages中。如果你已经有一些iptables规则了,那么将记录日志...

如何安全管理SSH密钥以防止服务器被入侵

SSH密钥安全管理实施指南(2025年更新版)一、密钥生成与存储规范高强度密钥生成bashCopyCodessh-keygen-ted25519-a100#生成ED25519算法密钥(比...

在CentOS上安装nginx服务器(centos搭建代理服务器)

一、环境描述1.虚拟机配置CPU:单核内存:2GB硬盘:120GBIP:10.24.17.1082.操作系统版本:CentOS6.6x86_64安装方式:Minimal3.虚拟化环境VM...

CentOS7安全加固的一份整理规划建议

◆更新系统:及时更新CentOS7操作系统版本和安全补丁,确保系统以最新状态运行。◆关闭不必要的服务:在运行系统时,应关闭不需要的服务和端口,以减少系统暴露的攻击面。◆安装防火墙:使用iptables...

第四十七天-二叉树,centOS安装tomcat,Maven,vsftpd

学习笔记:1.Maven是Apache下的一个纯Java开发的开源项目。基于项目对象模型(缩写:POM)概念,Maven利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。Maven...

Linux远程桌面连接使用教程 Widows终端远程连接Linux服务器

一、前言为什么不是远程连接Linux服务器?因为我不会,远程连接window我就用电脑自带的“远程桌面连接”。以下所述都是在CentOS操作系统下的。服务器刚换成Linux的时候很迷茫,感觉无从下手...

CentOS 安全加固操作,保护你的操作系统

系统加固是保障系统安全的重要手段,对于维护企业数据安全、用户隐私以及系统稳定运行具有重要意义。加固后的系统更加健壮和稳定,能够有效减少因安全问题导致的系统故障和停机时间,提高系统的可用性和可靠性。通过...

Dockerfile部署Java项目(docker如何部署java项目)

1、概述本文主要会简单介绍什么是Docker,什么是Dockerfile,如何安装Docker,Dockerfile如何编写,如何通过Dockerfile安装jar包并外置yaml文件以及如何通过do...

CentOS7云主机部署Fail2ban阻断SSH暴力破解

关于Fail2banFail2ban可以监视你的系统日志,然后匹配日志的错误信息(正则式匹配)执行相应的屏蔽动作(一般情况下是调用防火墙屏蔽)例如:当有人在试探你的HTTP、SSH、SMTP、FTP密...

在CentOS7上用源码编译安装PostgreSQL

1、新建postgres用户#useraddpostgres&&passwdpostgres2、安装依赖包#yum-yinstallmakegccgcc-c++readline...

pure-ftpd 使用(ftp prompt命令)

pure-ftpd是一个免费的ftp软件,其他介绍就不多说了。我们直接开始主题安装centosyuminstallepel-releaseyuminstallpure-ftpd配置备份原配置...

取消回复欢迎 发表评论: