Linux 内核问题调试手段——QEMU 专题

VM 启动后没有输出?莫慌~

一般而言,在运行 qemu-system-xxx 启动虚拟机后,我们希望在主机的当前终端上看到内核的启动日志,并在虚拟机启动后用该终端直接操作虚拟机。然而有时,当前终端在运行了 qemu-system-xxx 后却毫无动静。此时,在确认内核可以正常启动的前提下,要去确认几件事情:

  • 当前终端对 QEMU 虚拟机而言是什么设备;

  • 当前内核使用的 /dev/console 指向了哪个设备。

先找到一个可用的虚拟机终端(比如通过 VNC 连接的方式),然后向 /dev/{console,tty{,S}{0,1,...}} 等物理终端设备文件逐个尝试输入一些内容,看看哪个文件能让主机的当前终端有输出1。确定了是哪个设备之后,用 console= 内核启动参数将其设定为内核的输出设备即可。

关于问题的原因,内核文档 中提到:

If no console device is specified, the first device found capable of acting as a system console will be used. At this time, the system first looks for a VGA card and then for a serial port. So if you don’t have a VGA card in your system the first serial port will automatically become the console.

由此可推测,出现本例所述的情况很可能是因为内核在各种条件的允许下自动将显卡的虚拟控制台用作了 /dev/console,此时若没有指定 console=ttyS0 的话,串口设备就不会有任何内核打印输出。

其他参考材料:

将内核串口打印记录为日志

  1. 设置内核启动参数,将内核打印输出至串口。完整的内核启动参数列表见此处 。CentOS 上使用 grubby 修改内核启动参数:

    1grubby --args="earlyprintk=serial console=tty0 console=ttyS0" --update-kernel /boot/vmlinuz-4.19.90+
    
  2. QEMU 中设置 VM 串口输出至文件或 stdio(链接 ):

     1-serial dev
     2 Redirect the virtual serial port to host character device dev. (...)
     3
     4 Available character devices are:
     5
     6 ...
     7
     8 file:filename
     9	    Write output to filename. No character can be read.
    10
    11 stdio
    12	    [Unix only] standard input/output
    13
    14 ...
    
    1sudo qemu-system-x86_64 \
    2        # . . .
    3        -serial file:serial.log \
    

QEMU Monitor

QEMU 作为一个虚拟机平台,理应可以对运行于其上的虚拟机实例实施控制,比如强制关机、冷重启、发送指令等。事实上 QEMU 的确有这么一个控制台,被称为 QEMU Monitor。在 QEMU Monitor 中,我们可以使用一系列的命令来操控正在运行的虚拟机,其中最常见的有:

1system_powerdown # 强制关机
2system_wakeup    # (从暂停中)恢复运行
3system_reset     # 强制重启

因此,当受测试的内核出现严重问题导致虚拟机无法接收用户输入的时候,我们可以用 QEMU Monitor 来重掌虚拟机的控制权。

-daemonize 的虚拟机而言,提供以下两种使用 Monitor 的方法供参考:

  1. -vnc 使能 VNC 连接,在 VNC 窗口中按 Ctrl-Alt-2 切换至 Monitor,随后按 Ctrl-Alt-1 返回之前的虚拟机终端;

  2. -monitor 将 Monitor 输出重定向至主机的某个设备上,随后与 Monitor 建立连接。如:

    1sudo qemu-system-x86_64 \
    2    -monitor telnet:127.0.0.1:55555,server,nowait \
    3    # ...
    4
    5# 待 vm 启动后建立连接
    6telnet 127.0.0.1 55555
    

    注意:若同时配置了 -vnc,则此时 VNC 窗口无法进入 Monitor。

完整的 QEMU Monitor 命令列表可以查阅 QEMU 文档 ,而关于更多可以用 QEMU Monitor 完成的操作则可以参考 openSUSE 的文档 所展示的丰富用例。

QEMU + GDB 调试内核

可以在启动虚拟机时添加以下参数:

1-S # stops qemu waiting for gdb
2
3-s # makes qemu able to be attached by gdb through localhost:1234
4   # same as `-gdb tcp::1234`

在虚拟机启动后,准备启动 GDB。可以为 GDB 准备一份调试内核用的符号信息:

1objcopy --only-keep-debug vmlinux kernel.sym
2gdb
3(gdb) file ./kernel.sym

也可以直接 gdb vmlinux。随后在 GDB 里进行一系列后续操作:

1(gdb) target remote :1234  # 远程连接
2(gdb) hbreak start_kernel  # 设置断点
3(gdb) c

参考资料:


  1. 凭我的经验,一般而言,主机的当前终端对 QEMU 虚拟机而言是串口设备,即 ttyS0。 ↩︎


基于完整发行版搭建内核验证环境
Linux 内核问题调试手段