> 技术文档 > Windows 11下纯软件模拟虚拟机的设备模拟与虚拟化(仅终端和网络)

Windows 11下纯软件模拟虚拟机的设备模拟与虚拟化(仅终端和网络)

Windows 11下用GCC的C++代码实现的虚拟机需要终端输入/输出(如串口或虚拟控制台)和网络连接,但不需要完整的硬件设备(如磁盘、显卡、USB 等)。在终端输入/输出方面,参考qemu的源代码,但不调用qemu程序,可以使用 Linux 虚拟控制台(TTY)串口(Serial Console) 进行交互。模拟QEMU提供 的virtio-consoleisa-serial 设备允许宿主机通过 stdio 或管道与虚拟机交互,这样,虚拟机的 ttyS0(串口)会直接连接到宿主机的 stdio(终端)。网络连接使用 virtio-net用户态网络(SLIRP) 提供网络访问,这样,虚拟机可以通过 NAT 方式访问外部网络(类似宿主机的网络代理)。

以下是一个在Windows 11下使用GCC的C++实现轻量级虚拟机的方案,重点实现终端I/O和网络功能,无需完整硬件模拟:

核心设计思路

  1. 使用Windows Hypervisor Platform (WHP) - 微软官方虚拟化API
  2. 简化设备模型 - 仅实现必要设备:
    • virtio-console 用于终端I/O
    • virtio-net + SLIRP 用于网络
  3. 主机交互
    • 终端I/O通过标准输入输出重定向
    • 网络通过用户态NAT实现

代码框架

#include #include #include #include // 虚拟设备基类class VirtDevice {public: virtual void handle_io(uint16_t port, uint8_t* data, uint32_t size, bool is_write) = 0; virtual ~VirtDevice() {}};// VirtIO 控制台设备 (简化版)class VirtioConsole : public VirtDevice {public: void handle_io(uint16_t port, uint8_t* data, uint32_t size, bool is_write) override { if (is_write) { // 虚拟机输出 -> 宿主终端 fwrite(data, 1, size, stdout); fflush(stdout); } else { // 宿主输入 -> 虚拟机 (需异步处理) // 实际实现需用环形缓冲区和中断机制 } }};// SLIRP网络后端 (伪代码)class SlirpBackend {public: void start_nat() { // 实现NAT网络转换 // 使用WinSock API处理数据包转发 }};// VirtIO 网卡设备class VirtioNet : public VirtDevice { SlirpBackend slirp;public: VirtioNet() { slirp.start_nat(); } void handle_io(uint16_t port, uint8_t* data, uint32_t size, bool is_write) override { // 网络包处理逻辑 if (is_write) { slirp.send_packet(data, size); // 发送到外部网络 } else { slirp.recv_packet(data, size); // 接收外部数据 } }};// 虚拟机管理器class MiniVM { WHV_PARTITION_HANDLE partition; std::vector<VirtDevice*> devices; public: bool initialize() { // 1. 初始化Hyper-V分区 WHV_PARTITION_HANDLE partition; CHECK(WHvCreatePartition(&partition)); // 2. 设置基础CPU特性 WHV_PARTITION_PROPERTY prop{}; prop.ProcessorCount = 1; WHvSetPartitionProperty(partition, WHvPartitionPropertyCodeProcessorCount, &prop); // 3. 分配内存 (示例: 512MB) const SIZE_T ramSize = 512 * 1024 * 1024; WHvMapGpaRange(partition, physical_mem, 0, ramSize, WHvMapGpaRangeFlagReadWrite); // 4. 注册设备 devices.push_back(new VirtioConsole()); devices.push_back(new VirtioNet()); return true; } void run() { // CPU执行循环 while (true) { auto exit_context = WHvRunVirtualProcessor(partition, 0); // 处理VM退出事件 switch (exit_context->ExitReason) { case WHvRunVpExitReasonX64IoPortAccess: { auto io = &exit_context->IoPortAccess; for (auto dev : devices) {  dev->handle_io(io->PortNumber, io->Data, io->AccessSize, io->Direction); } break; } // 其他退出处理... } } } void load_kernel(const char* kernel_path) { // 加载Linux内核到内存 // 设置启动参数 (指定console=ttyS0) }};int main() { MiniVM vm; if (vm.initialize()) { vm.load_kernel(\"vmlinux.bin\"); vm.run(); } return 0;}

关键实现细节

1. 终端输入输出
  • 设备模拟
    • 实现virtio-console设备的简化版
    • 使用I/O端口或MMIO与虚拟机通信
  • 主机集成
    • 输出:直接写入stdout
    • 输入:使用独立线程读取stdin并注入输入缓冲区
    • 启用行缓冲:setvbuf(stdout, NULL, _IOLBF, 0);
2. 网络实现
  • SLIRP架构:#mermaid-svg-360sqtIxnPTwbANs {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-360sqtIxnPTwbANs .error-icon{fill:#552222;}#mermaid-svg-360sqtIxnPTwbANs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-360sqtIxnPTwbANs .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-360sqtIxnPTwbANs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-360sqtIxnPTwbANs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-360sqtIxnPTwbANs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-360sqtIxnPTwbANs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-360sqtIxnPTwbANs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-360sqtIxnPTwbANs .marker.cross{stroke:#333333;}#mermaid-svg-360sqtIxnPTwbANs svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-360sqtIxnPTwbANs .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-360sqtIxnPTwbANs .cluster-label text{fill:#333;}#mermaid-svg-360sqtIxnPTwbANs .cluster-label span{color:#333;}#mermaid-svg-360sqtIxnPTwbANs .label text,#mermaid-svg-360sqtIxnPTwbANs span{fill:#333;color:#333;}#mermaid-svg-360sqtIxnPTwbANs .node rect,#mermaid-svg-360sqtIxnPTwbANs .node circle,#mermaid-svg-360sqtIxnPTwbANs .node ellipse,#mermaid-svg-360sqtIxnPTwbANs .node polygon,#mermaid-svg-360sqtIxnPTwbANs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-360sqtIxnPTwbANs .node .label{text-align:center;}#mermaid-svg-360sqtIxnPTwbANs .node.clickable{cursor:pointer;}#mermaid-svg-360sqtIxnPTwbANs .arrowheadPath{fill:#333333;}#mermaid-svg-360sqtIxnPTwbANs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-360sqtIxnPTwbANs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-360sqtIxnPTwbANs .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-360sqtIxnPTwbANs .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-360sqtIxnPTwbANs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-360sqtIxnPTwbANs .cluster text{fill:#333;}#mermaid-svg-360sqtIxnPTwbANs .cluster span{color:#333;}#mermaid-svg-360sqtIxnPTwbANs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-360sqtIxnPTwbANs :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}TAP设备NAT转换WinSock虚拟机VirtioNetSlirpBackend宿主网络
  • 关键功能
    • DHCP服务:为虚拟机分配内网IP(如10.0.2.15)
    • DNS转发:解析宿主机DNS配置
    • 端口转发:实现-netdev user,hostfwd=tcp::2222-:22
3. 启动配置

虚拟机启动时需传递内核参数:

console=ttyS0 earlycon=uart8250,mmio,0x10000000 root=/dev/ram0

构建与运行

  1. 依赖项

    • Windows SDK (含Hypervisor Platform API)
    • GCC for Windows (MinGW-w64)
    • 虚拟化支持:启用Hyper-V/WHPX
  2. 编译命令

g++ -o minivm.exe main.cpp -lwinhvplatform -lws2_32
  1. 运行示例
# 启动虚拟机minivm.exe -kernel vmlinux -initrd initrd.img# 网络测试 (从宿主机访问)telnet localhost 2222

性能优化建议

  1. 批处理I/O

    • 使用DMA代替端口I/O
    • 实现VIRTIO_F_ANY_LAYOUT特性
  2. 异步网络

    • 使用IOCP完成端口
    • 零拷贝数据传递
  3. 中断合并

    // 延迟中断提交void VirtioConsole::schedule_irq() { if (!irq_pending) { irq_timer = setTimeout(1ms, [this]{ inject_irq(); irq_pending = false; }); }}

此方案实现了核心功能,代码约500行可完成基础版本。实际开发中需处理更多边界情况,建议参考QEMU的hw/char/virtio-console.cnet/slirp实现细节。