4. 虚拟化平台基础配置:启动脚本编写、内存与CPU分配、虚拟网络配置

好,咱们接着往下走。上一章我们把QHV(QNX Hypervisor)的架构和基本概念捋了一遍。这一章,咱们要动真格的了——动手配置一个能跑起来的虚拟化平台。

说白了,就是三件事:启动脚本怎么写、内存和CPU怎么分、虚拟网络怎么搭。这三件事搞定了,你的虚拟机就能“活”过来。

4.1 启动脚本编写:让虚拟机“自启动”

我个人习惯,先把启动脚本的骨架搭好。QNX的虚拟化启动,核心文件就是 .build 文件。这个文件告诉系统:你要启动哪些虚拟机,它们各自用什么资源。

来看一个最简的示例:

# 这是 host 的 build 文件片段
# 定义第一个虚拟机:Linux Guest
vm-guest1 {
    # 虚拟机类型
    type = linux;
    # 启动镜像路径
    image = /path/to/guest1/vmlinux;
    # 内核参数
    cmdline = "console=ttyS0 root=/dev/vda rw";
    # 分配的内存大小(MB)
    memory = 512;
    # 分配的CPU核心(从0开始)
    cpus = 0-1;
    # 虚拟网卡
    vnet {
        # 网卡类型:virtio
        type = virtio;
        # 桥接到 host 的哪个网络接口
        bridge = "br0";
        # MAC 地址
        mac = "00:16:3e:00:00:01";
    }
}

嗯,这里要注意:type 字段决定了虚拟机启动时加载的引导方式。Linux 和 QNX 的引导流程不一样,别搞混了。我在项目中遇到过有人把 QNX 的虚拟机配成了 type = linux,结果启动到一半就卡死了。

小技巧: 如果你不确定虚拟机类型,可以先配成 type = generic,然后手动指定引导参数。不过我个人建议,能用原生类型就用原生类型,省心。

4.2 内存与CPU分配:别让虚拟机“饿着”

内存和CPU的分配,是虚拟化配置里最容易出问题的地方。你想想看,如果给一个实时性要求很高的QNX虚拟机只分了一个CPU核心,它跑着跑着就被Linux虚拟机抢了时间片,那后果...嗯,你懂的。

我一般遵循这个原则:

  • 实时虚拟机(如QNX):分配独占的CPU核心,不要跟其他虚拟机共享。内存尽量给够,但别超过物理内存的70%。
  • 非实时虚拟机(如Linux):可以共享CPU核心,但要做好cgroup限制。内存可以按需分配,但别让host swap。

来看一个更完整的配置示例:

# host 的 build 文件
vm-guest1 {
    type = qnx;
    image = /path/to/guest1/ifs;
    cmdline = "root=/dev/hd0";
    memory = 1024;          # 1GB 内存
    cpus = 0;               # 独占 CPU 0
    # 内存大页支持(提升性能)
    hugepages = 2M;
}

vm-guest2 {
    type = linux;
    image = /path/to/guest2/vmlinux;
    cmdline = "console=ttyS0 root=/dev/vda rw";
    memory = 2048;          # 2GB 内存
    cpus = 1-3;             # 共享 CPU 1-3
    # CPU 亲和性设置
    cpumask = 0xE;          # 二进制 1110,对应 CPU 1,2,3
}
警告: 千万不要把 host 的所有 CPU 都分配给虚拟机!我曾经见过一个项目,工程师把 4 个 CPU 核心全部分给了两个虚拟机,结果 host 自己没核心可用了,整个系统响应慢得像蜗牛。至少留一个核心给 host。

关于内存分配,我还有一个经验:别把内存分得太碎。比如你有 8GB 内存,与其分成 4 个 2GB 的虚拟机,不如分成 2 个 3GB 的虚拟机,剩下的 2GB 留给 host。碎片化的内存分配会导致 TLB 缺失率上升,性能下降明显。

4.3 虚拟网络配置:让虚拟机“联网”

虚拟网络这块,说白了就是让虚拟机之间、虚拟机与外部网络能通信。QNX 的虚拟化方案支持多种网络模型,我重点讲两种最常用的:

4.3.1 桥接模式(Bridge)

这种模式下,虚拟机就像一台独立的物理机,直接接入 host 的网络。配置起来也简单:

# host 端配置
# 创建桥接接口
io-pkt-v6-hc -d e1000 -p tcpip -o iface=br0,mac=00:16:3e:00:00:ff

# 将物理网卡加入桥接
ifconfig en0 down
brconfig br0 add en0
ifconfig en0 up
ifconfig br0 up

# 虚拟机配置(在 build 文件中)
vm-guest1 {
    vnet {
        type = virtio;
        bridge = "br0";
        mac = "00:16:3e:00:00:01";
    }
}

这种模式的好处是配置简单,虚拟机可以直接获取 DHCP 地址。但缺点也很明显——所有流量都经过 host 的协议栈,性能有损耗。

4.3.2 直通模式(Passthrough)

如果你对网络性能要求极高(比如跑工业以太网协议),可以考虑直通模式。直接把物理网卡分配给某个虚拟机:

# 在 build 文件中
vm-guest1 {
    # 直通物理网卡
    passthrough {
        device = "pci:0:0:1.0";  # PCI 地址
    }
}
重要提醒: 直通模式虽然性能好,但有个坑——直通后的网卡只能被一个虚拟机使用,host 和其他虚拟机都用不了。我在一个项目中就吃过这个亏,把唯一的网卡直通给了 Linux 虚拟机,结果 QNX 虚拟机没法联网了。后来加了一块物理网卡才解决。

4.4 实战:一个完整的启动脚本示例

好了,理论讲完了,咱们来写一个完整的启动脚本。这个脚本会启动两个虚拟机:一个 QNX 做实时控制,一个 Linux 做人机界面。

# host.build
# 定义 host 网络
io-pkt-v6-hc -d e1000 -p tcpip -o iface=br0,mac=00:16:3e:00:00:ff

# 第一个虚拟机:QNX 实时控制
vm-qnx-rt {
    type = qnx;
    image = /images/qnx-rt.ifs;
    cmdline = "root=/dev/hd0";
    memory = 512;
    cpus = 0;               # 独占 CPU 0
    vnet {
        type = virtio;
        bridge = "br0";
        mac = "00:16:3e:00:00:01";
    }
}

# 第二个虚拟机:Linux HMI
vm-linux-hmi {
    type = linux;
    image = /images/linux-hmi/vmlinux;
    cmdline = "console=ttyS0 root=/dev/vda rw";
    memory = 2048;
    cpus = 1-2;             # 共享 CPU 1-2
    vnet {
        type = virtio;
        bridge = "br0";
        mac = "00:16:3e:00:00:02";
    }
}

# 启动所有虚拟机
vm-start -all

这个脚本看起来简单,但我在实际项目中踩过不少坑。比如:

  • 镜像路径写错:检查三遍!我曾经因为一个拼写错误,浪费了半天时间。
  • MAC 地址冲突:每个虚拟机的 MAC 地址必须唯一,否则网络会乱套。
  • CPU 核心重叠:如果两个虚拟机都分配了同一个 CPU 核心,性能会互相干扰。
避坑指南: 启动脚本写完后,先用 vm-status 命令检查一下虚拟机的状态。如果某个虚拟机没起来,用 vm-log 查看日志。我曾经靠这个命令救回过一个项目——日志里明确写着“内存不足”,我一看,原来是给 Linux 虚拟机分了 4GB,但物理机总共才 4GB。

嗯,这一章的内容就到这里。启动脚本、内存CPU分配、虚拟网络配置,这三块是虚拟化平台的基础。下一章咱们会深入讲虚拟机的启动流程和调试技巧。到时候见!