4. DRIVE OS与Hypervisor调试

NVIDIA DRIVE OS 运行在 Hypervisor 之上,典型形态是 QNX Hypervisor 承载安全域(QNX Guest)与多个 Linux Guest(如用于座舱、感知、AI 推理的虚拟机)。调试这类系统时,最容易犯的错误是把它当成单机 Linux 来查:直接进 Guest 看 dmesg、看 systemd,却忽略了 Guest 的“生死”其实由 Hypervisor 决定。因此本节的核心思路是:先判断故障发生在哪一层,再沿该层的证据链向下追。四类典型故障——虚拟机启动失败、Guest OS 异常、资源分配冲突、跨虚拟化域通信故障——分别对应不同的观察入口和判断依据。

一、先建立分层定位模型

DRIVE OS 的虚拟化栈自下而上大致分为:硬件与固件(SoC、BootROM、ATF/UEFI)→ Hypervisor(QNX Hypervisor,负责 vCPU 调度、内存分区、设备直通与虚拟设备)→ Guest OS(QNX 或 Linux 内核)→ Guest 用户态服务。任何一层出问题,上层都可能表现为“起不来”或“卡住”。调试的第一步不是敲命令,而是确定当前证据来自哪一层。

DRIVE OS 虚拟化分层与四类故障的定位入口 Guest 用户态服务 systemd / init 脚本、感知与推理进程、日志服务 Guest OS 内核 Linux / QNX 内核、驱动、virtio 前端、panic 与 oops Hypervisor(QNX Hypervisor) vCPU 调度 · 内存分区 · 设备直通/虚拟设备 · vdev 配置 Guest 生命周期管理、启动镜像加载、域间通信后端 硬件与固件 SoC、BootROM、ATF/UEFI、内存与中断控制器 ① 虚拟机启动失败 入口:Hypervisor 控制台 / vdev 配置 / 镜像加载日志 特征:Guest 从未进入内核,无 Guest 侧日志 ② Guest OS 异常 入口:Guest 串口 / dmesg / panic 回溯 特征:内核已启动,运行中崩溃或挂起 ③ 资源分配冲突 入口:内存分区表 / 中断号 / 设备直通归属 特征:多域争用同一物理资源,随机失败 ④ 跨域通信故障 入口:virtio 队列 / 共享内存 / 中断注入 特征:各域自身正常,但数据不通或超时 快速分层判断路径 1. Hypervisor 控制台能否看到该 Guest 的创建记录? 2. Guest 串口是否输出内核启动早期日志? 3. 内存/中断/设备是否与其他域重叠? 4. 各域独立正常时,检查 vdev 与共享内存通道 原则:下层无证据时,不要在上层反复重启 Guest

二、虚拟机启动失败:从 Hypervisor 侧找证据

虚拟机启动失败最典型的特征是:Guest 串口完全没有输出,或者只输出一行就停住。此时问题几乎一定在 Hypervisor 或更下层。调试入口是 Hypervisor 控制台与 vdev 配置文件。

首先确认 Hypervisor 是否成功加载了 Guest 镜像。QNX Hypervisor 通常通过 qvm 进程配置启动 Guest,配置文件中会指定内核镜像路径、设备树、内存大小、vCPU 数量。若镜像路径错误或镜像损坏,Hypervisor 会报加载失败,而 Guest 侧毫无感知。可以在 Hypervisor 控制台执行:

# 查看 Hypervisor 管理的虚拟机列表与状态
qvm -l

# 查看某个 Guest 的详细配置与运行状态
qvm -i guest_linux_0

# 查看 Hypervisor 日志中的 Guest 创建记录
cat /var/log/qvm.log | grep -i "guest_linux_0"

如果 qvm -l 中该 Guest 状态为 not started 或 failed,说明问题在创建阶段。常见原因包括:内存分区超出 Hypervisor 可用范围、vCPU 数量超过物理核数、设备直通引用了已被其他域占用的设备。若状态为 running 但 Guest 无输出,则要检查串口是否被正确映射到该 Guest——很多情况下 Guest 其实在运行,只是串口被分配给了另一个域,导致“看起来没启动”。

判断技巧:在 Hypervisor 控制台用 qvm -i 查看 Guest 的 vCPU 是否在消耗时间。若 vCPU 时间持续增长但串口无输出,问题多半在串口映射或 Guest 内核早期初始化,而非虚拟机未启动。

三、Guest OS 异常:区分 panic、hang 与 OOM

Guest OS 异常指内核已经启动,但在运行过程中崩溃或挂起。这类问题的证据在 Guest 侧,但根因可能在资源分配或跨域通信。需要区分三种表现:

  • Kernel panic / oops:Guest 串口会打印回溯栈。重点看崩溃函数与调用链,判断是驱动问题、内存越界还是 virtio 后端异常。
  • 系统 hang 住:无 panic 输出,但服务无响应。此时在 Hypervisor 侧观察该 Guest 的 vCPU 是否仍在调度。若 vCPU 停止运行,可能是 Guest 内部死锁或等待某个永远不会到达的中断。
  • OOM 或服务被杀:Guest 内存分区被耗尽。注意 Guest 看到的内存是 Hypervisor 分配的上限,不是物理内存总量,因此 free 显示的总量本身就是分区大小。

对 hang 住的情况,一个有效手段是在 Hypervisor 侧触发 Guest 的 vCPU 回溯(若 Hypervisor 支持),或在 Guest 内核中预先启用 hung_task 检测与 sysrq。例如在 Guest 内核命令行加入:

hung_task_panic=1 hung_task_timeout_secs=30

这样当有任务阻塞超过 30 秒时,Guest 会主动 panic 并输出阻塞任务的调用栈,把“静默 hang”转化为“可分析的 panic”。

四、资源分配冲突:内存、中断与设备直通

资源分配冲突的典型症状是“随机失败”:同一个 Guest 有时能启动,有时不能;或者两个 Guest 交替出问题。根因通常是多个域声明了同一份物理资源。需要重点核对三类资源:

资源类型冲突表现核对位置
内存分区Guest 启动即失败或运行中随机崩溃Hypervisor 内存分区表、各 Guest 配置的 memory 段
中断号设备无响应、中断风暴、Guest hang设备树中断映射、直通设备的中断归属
设备直通某域设备可用,另一域同设备不可用vdev 配置中 passthrough 设备列表

核对内存分区时,要把所有 Guest 的分区大小、起始地址与 Hypervisor 保留区画在一张地址图上,确认没有重叠。中断冲突则要确认每个直通设备的中断只被一个域接管;若两个域都试图接管同一中断控制器线路,会出现中断被错误域处理的现象。设备直通冲突最隐蔽:两个 Guest 配置了同一个 PCIe 设备,先启动的域拿到设备,后启动的域静默失败。

资源冲突往往不会在配置阶段报错,而是在运行阶段以随机故障出现。建议在系统集成阶段就建立一张“资源归属表”,记录每个内存段、中断号、直通设备的唯一归属域,作为后续排障的基线。

五、跨虚拟化域通信故障:沿 virtio 与共享内存链路排查

跨域通信故障的特点是:每个 Guest 单独看都正常,但域间数据不通、超时或丢包。DRIVE OS 中常见的跨域通道包括 virtio 前端/后端、共享内存环形缓冲区、以及基于中断的域间通知。排查应沿“前端 → 后端 → 共享内存 → 中断注入”这条链路逐段确认。

第一步确认 virtio 设备在 Guest 内是否被识别。在 Linux Guest 中:

# 查看 virtio 设备与队列状态
ls /sys/bus/virtio/devices/
dmesg | grep -i virtio

# 查看网络类 virtio 接口统计
ip -s link show

若 Guest 内看不到 virtio 设备,问题在 Hypervisor 的 vdev 配置或后端未启动。若设备存在但队列无数据流动,则检查共享内存区域是否被两个域正确映射到同一物理地址,以及中断注入是否生效。一个常见故障是:后端已经写入数据并触发中断,但前端所在域的中断被屏蔽或路由错误,导致数据“已到达但无人处理”。此时在 Hypervisor 侧观察中断计数,与 Guest 侧 /proc/interrupts 对比,就能定位中断是否丢失。

对于共享内存通道,还要确认内存属性一致:若一端按 cacheable 映射、另一端按 non-cacheable 映射,会出现数据可见性不一致,表现为“偶尔读到旧数据”。这类问题不会报错,只能通过统一映射属性解决。

六、把四类故障串成一条调试路径

实际排障时,四类故障往往交织。建议按以下顺序推进:先在 Hypervisor 控制台确认 Guest 是否被创建、vCPU 是否在运行;再通过 Guest 串口确认内核是否启动、是否 panic;然后核对内存、中断、设备归属,排除资源冲突;最后在“各域独立正常但通信失败”时,沿 virtio 与共享内存链路逐段验证。每一步都以“当前层是否有证据”为判断依据,避免在无证据的层反复操作。这样既能快速缩小范围,也能避免把 Hypervisor 层的问题误判为 Guest 内部问题。