searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

利用qemu搭建内核开发环境

2024-03-06 06:13:33
86
0

在Qemu中开发内核的优势

在进行内核驱动开发或者内核功能开发的时候,如果在主机上进行调试,每次安装需要数分钟的时间,如果出错需要重启主机,要花费更多的时间。

甚至在某些极端情况下会损坏主机文件系统上重要内容,或者启动出错则需要更长的时间先恢复错误然后继续调试。

在大部分情况下,内核驱动的开发可以在虚拟机上进行,每次重启虚机的时间在数十秒之内,可以节省大量的时间。并且不用担心影响到开发主机,下面介绍如何利用qemu来做内核开发。

 

第一步安装qemu虚拟机

有图形界面的安装

qemu-img create -f qcow2 new_image.qcow2 40G qemu-system-x86_64 \

-machine q35 -cpu host -enable-kvm \

-smp 2,sockets=1,cores=2,threads=1 -m 4G \

-boot d -cdrom ./ctyunos-2.0.1-220329-everything-x86_64-dvd.iso \

-hda ./new_image.qcow2 -vnc :2

进入安装界面安装 vncviewer :2 

无图形界面,通过virt-install安装

virt-install -n vm1 -r 1024 --vcpus=1 --os-variant=rhel5.4 --accelerate \

--nographics -v --disk path=./new_image.qcow2  --extra-args "console=ttyS0" \

--location ./ctyunos-2.0.1-220329-everything-x86_64-dvd.iso --check path_in_use=off

对qemu镜像进行必要的设置

NBD方式对镜像修改

modprobe nbd max_part=8

qemu-nbd -c /dev/nbd0  vm1.qcow2

fdisk -l /dev/nbd0

如果disk是使用磁盘分区的方式

mount /dev/nbd0p3 /mnt/disk

如果disk是使用lvm的方式

vgscan --cache vgchange -ay

如果vg重名,可以先修改镜像中vg name,完成内部修改后再改回去

mount /dev/mapper/centos-root /mnt/

更改完成之后断开连接

umount /mnt/disk qemu-nbd --disconnect /dev/nbd0

第二步在主机上编译镜像使用的内核

内核编译 解压代码

使用默认平台编译配置

make x86_64_defconfig

将内核日志文件系统和访问镜像磁盘需要的模块编入内核

CONFIG_XFS_FS = y CONFIG_EXT4_FS = y

内核调试信息和脚本打开

CONFIG_DEBUG_INFO=y CONFIG_GDB_SCRIPTS=y

使用软时钟中断,避免调试是被中断

CONFIG_HYPERVISOR_GUEST=y CONFIG_PARAVIRT=y CONFIG_PARAVIRT_CLOCK=y

如果需要调试函数的输入输出参数或者临时变量,可以修改函数的编译优化选项

__attribute__((optimize("O0"))) 

第三步qemu配置使用主机上准备好的内核

主机上编译kernel并安装模块

make modules_install

生成启动需要的initrd

dracut  -f ./initrd_4.19.90.img 4.19.90

在qemu启动配置上加入内核和ramdisk路径,kernel的启动参数

-kernel ./kernel/arch/x86/boot/bzImage \

-append 'root=/dev/sda3 crashkernel=512M console=ttyS0 nokaslr' \

-initrd ./initrd_4.19.90.img

在qemu启动配置上加入调试指令 -S -s 

第四步 gdb启动内核

启动虚拟机,这时虚拟机cpu是freeze状态

使用gdb连接到qemu调试端口,并且在内核启动位置等待

gdb ./kernel/vmlinux

(gdb) target remote: 1234

(gdb) hbreak start_kernel

(gdb) continue

为需要调试的函数加上断点

(gdb) break ima_load_digest_lists

继续运行到断点,并开始调试

(gdb) continue

第四步 gdb调试内核模块

 

将需要调试的内核模块加入到内核中进行编译, 以紫金设备驱动为例

drivers/infiniband/Kconfig 加入 source drivers/infiniband/hw/zijin_roce/Kconfig

drivers/infiniband/hw/Makefile 加入 obj-$(CONFIG_INFINIBAND_ZIJIN_ROCE) += zijin_roce/

加载到kernel之后, gdb中load新的symboles

(gdb) lx-symbols

(gdb) break zijin_ib_create_cq

如果必须调试第一次驱动加载, 在init流程必走的路径上加入断点,再reload symbols, 然后继续调试

(gdb) break local_pci_probe

(gdb) lx-symbols

 

至此内核在虚机机镜像中进行内核或者模块的调试步骤介绍完毕。

GDB调试技巧 

多进程调试

切换fork之后跟踪父进程还是子进程

(gdb) set follow-fork-mode child

多线程调试

只在特定线程上打断点

(gdb) break function_name thread n

选择当前跟踪的线程

(gdb) thread n

当一个线程中断时,其他线程继续运行

(gdb) set non-stop on

其他

gdb打印有默认长度限制,如果要显示结构体中某些很长的元素

(gdb) set print elements 0

qemu启动过程会收到多次信号,可以忽略这些信号

(gdb) handle SIGUSR2 nostop noprint

0条评论
0 / 1000
l****n
3文章数
0粉丝数
l****n
3 文章 | 0 粉丝
l****n
3文章数
0粉丝数
l****n
3 文章 | 0 粉丝
原创

利用qemu搭建内核开发环境

2024-03-06 06:13:33
86
0

在Qemu中开发内核的优势

在进行内核驱动开发或者内核功能开发的时候,如果在主机上进行调试,每次安装需要数分钟的时间,如果出错需要重启主机,要花费更多的时间。

甚至在某些极端情况下会损坏主机文件系统上重要内容,或者启动出错则需要更长的时间先恢复错误然后继续调试。

在大部分情况下,内核驱动的开发可以在虚拟机上进行,每次重启虚机的时间在数十秒之内,可以节省大量的时间。并且不用担心影响到开发主机,下面介绍如何利用qemu来做内核开发。

 

第一步安装qemu虚拟机

有图形界面的安装

qemu-img create -f qcow2 new_image.qcow2 40G qemu-system-x86_64 \

-machine q35 -cpu host -enable-kvm \

-smp 2,sockets=1,cores=2,threads=1 -m 4G \

-boot d -cdrom ./ctyunos-2.0.1-220329-everything-x86_64-dvd.iso \

-hda ./new_image.qcow2 -vnc :2

进入安装界面安装 vncviewer :2 

无图形界面,通过virt-install安装

virt-install -n vm1 -r 1024 --vcpus=1 --os-variant=rhel5.4 --accelerate \

--nographics -v --disk path=./new_image.qcow2  --extra-args "console=ttyS0" \

--location ./ctyunos-2.0.1-220329-everything-x86_64-dvd.iso --check path_in_use=off

对qemu镜像进行必要的设置

NBD方式对镜像修改

modprobe nbd max_part=8

qemu-nbd -c /dev/nbd0  vm1.qcow2

fdisk -l /dev/nbd0

如果disk是使用磁盘分区的方式

mount /dev/nbd0p3 /mnt/disk

如果disk是使用lvm的方式

vgscan --cache vgchange -ay

如果vg重名,可以先修改镜像中vg name,完成内部修改后再改回去

mount /dev/mapper/centos-root /mnt/

更改完成之后断开连接

umount /mnt/disk qemu-nbd --disconnect /dev/nbd0

第二步在主机上编译镜像使用的内核

内核编译 解压代码

使用默认平台编译配置

make x86_64_defconfig

将内核日志文件系统和访问镜像磁盘需要的模块编入内核

CONFIG_XFS_FS = y CONFIG_EXT4_FS = y

内核调试信息和脚本打开

CONFIG_DEBUG_INFO=y CONFIG_GDB_SCRIPTS=y

使用软时钟中断,避免调试是被中断

CONFIG_HYPERVISOR_GUEST=y CONFIG_PARAVIRT=y CONFIG_PARAVIRT_CLOCK=y

如果需要调试函数的输入输出参数或者临时变量,可以修改函数的编译优化选项

__attribute__((optimize("O0"))) 

第三步qemu配置使用主机上准备好的内核

主机上编译kernel并安装模块

make modules_install

生成启动需要的initrd

dracut  -f ./initrd_4.19.90.img 4.19.90

在qemu启动配置上加入内核和ramdisk路径,kernel的启动参数

-kernel ./kernel/arch/x86/boot/bzImage \

-append 'root=/dev/sda3 crashkernel=512M console=ttyS0 nokaslr' \

-initrd ./initrd_4.19.90.img

在qemu启动配置上加入调试指令 -S -s 

第四步 gdb启动内核

启动虚拟机,这时虚拟机cpu是freeze状态

使用gdb连接到qemu调试端口,并且在内核启动位置等待

gdb ./kernel/vmlinux

(gdb) target remote: 1234

(gdb) hbreak start_kernel

(gdb) continue

为需要调试的函数加上断点

(gdb) break ima_load_digest_lists

继续运行到断点,并开始调试

(gdb) continue

第四步 gdb调试内核模块

 

将需要调试的内核模块加入到内核中进行编译, 以紫金设备驱动为例

drivers/infiniband/Kconfig 加入 source drivers/infiniband/hw/zijin_roce/Kconfig

drivers/infiniband/hw/Makefile 加入 obj-$(CONFIG_INFINIBAND_ZIJIN_ROCE) += zijin_roce/

加载到kernel之后, gdb中load新的symboles

(gdb) lx-symbols

(gdb) break zijin_ib_create_cq

如果必须调试第一次驱动加载, 在init流程必走的路径上加入断点,再reload symbols, 然后继续调试

(gdb) break local_pci_probe

(gdb) lx-symbols

 

至此内核在虚机机镜像中进行内核或者模块的调试步骤介绍完毕。

GDB调试技巧 

多进程调试

切换fork之后跟踪父进程还是子进程

(gdb) set follow-fork-mode child

多线程调试

只在特定线程上打断点

(gdb) break function_name thread n

选择当前跟踪的线程

(gdb) thread n

当一个线程中断时,其他线程继续运行

(gdb) set non-stop on

其他

gdb打印有默认长度限制,如果要显示结构体中某些很长的元素

(gdb) set print elements 0

qemu启动过程会收到多次信号,可以忽略这些信号

(gdb) handle SIGUSR2 nostop noprint

文章来自个人专栏
内核开发
1 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0