ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

xvisor时间虚拟化实战:从虚拟定时器设计到Guest时钟校正

xvisor时间虚拟化实战:从虚拟定时器设计到Guest时钟校正 1. 为什么专门聊xvisor的时间虚拟化1.1 从一次“Guest卡死”说起先讲个我实际踩过的坑。前两年在一款ARM嵌入式板卡上做多系统隔离方案选了xvisor作为Type-1 hypervisor跑一个轻量级Linux Guest。功能验证基本通过但跑了一天后发现Guest的gettimeofday返回值跟墙钟差了将近40秒再跑几天直接差到几分钟。起初我以为只是NTP没同步后来用串口抓日志发现vCPU在定时器中断里反复进出Guest的时钟中断频率明显不对甚至出现连续drop tick的情况。这个问题带我走进了xvisor的时间虚拟化实现。说实话虚拟化领域大家聊得最多的是CPU虚拟化、内存虚拟化、中断虚拟化时间虚拟化经常被一笔带过。但真正做过hypervisor的人都知道时间虚拟化是“看着不起眼、做起来头疼”的模块——它跟调度器、中断控制器、物理定时器、Guest内部时钟源全都有耦合。Guest卡死、时钟漂移、性能抖动十有八九是时间虚拟化没设计好。这篇文章就以xvisor为例掰开揉碎讲清楚时间虚拟化是怎么设计的。适合三类人看一是做嵌入式虚拟化方案选型的技术负责人想评估xvisor能不能用二是自己写或维护hypervisor的开发者想参考一套可落地的timer虚拟化设计三是正在被“Guest时钟不准”“定时器中断风暴”之类问题折磨的运维和驱动开发同学。1.2 xvisor是谁为什么拿它当例子xvisor是一个开源Type-1 hypervisor主打轻量、可移植官方支持ARM、x86、RISC-V等架构在嵌入式领域常被拿来跟KVM、Xen、Xenomai这类方案做对比。它的代码量比Linux内核小好几个数量级但虚拟化核心要素齐全CPU虚拟化、设备模型、中断虚拟化、时间虚拟化都有完整实现。也正因为代码规模可控非常适合用来“解剖”虚拟化内部原理。选xvisor讲时间虚拟化还有一个重要原因它的设计思路很“直给”。不像大型hypervisor那样为了兼容各种硬件而堆满条件分支xvisor在时间子系统的抽象上做得很精简——核心对象少、依赖关系清晰、中断流明确。读懂它的时间虚拟化再回头看KVM的kvm-clock、Xen的xen_timer会发现底层逻辑是相通的只是工程复杂度不同。这篇文章我不会只贴源码也不会只讲理论。我会从“Guest感知的时间到底是什么”这个根问题出发拆解xvisor的设计取舍然后给出我在实际编译、配置、验证过程中用到的操作步骤和调试命令最后把我踩过的坑整理成排查清单。保证你看完能对时间虚拟化有一个完整的、可复用的认知框架。2. 时间虚拟化的核心难点Guest眼中的“时间”从哪来2.1 三条时间线Guest同时在用三套时钟要理解时间虚拟化先得搞清楚虚拟机里的“时间”不是一个东西。实际运行中一个Guest至少依赖三条时间线。第一是墙钟时间wall clock也就是人类可读的日期时间比如2025年几月几日几点几分。Guest里的用户程序调用clock_gettime(CLOCK_REALTIME)拿到的基本是这条线。墙钟时间通常是软件维护的根节点是某个RTC芯片或网络时间协议。第二是单调递增时间monotonic time用来计算时间差、超时判断比如clock_gettime(CLOCK_MONOTONIC)。这条线不能被用户跳变只能单调往前走。内核里的jiffies、高精度定时器hrtimer都依赖它。第三是硬件定时器时间线。CPU或者SoC内部有周期性的硬件定时器——ARM的Generic Timer、x86的TSC/APIC timer、RISC-V的mtime等。操作系统用它来产生周期性tick中断驱动调度器、时间轮、延迟执行等机制。三条时间线各有各的虚拟化难点。墙钟时间主要是“怎么把宿主机的当前时间安全地给Guest”单调时间主要涉及“offset和速率怎么折算”硬件定时器则是真正的硬骨头——因为Guest要直接操作硬件定时器寄存器而hypervisor必须截获这些访问并进行模拟。2.2 直接透传的致命问题Guest能碰到物理中断有人可能会问既然ARM有Generic Timer这种虚拟化友好的硬件直接把定时器硬件给Guest用不就行了问题没那么简单。ARM Generic Timer确实提供了CNTVCT虚拟计数器、CNTVOFF虚拟偏移、CNTV_TVAL虚拟定时器这些机制硬件层面就已经支持时间虚拟化。但xvisor的目标是通过设备树描述虚拟平台要支持不同板卡、不同ARM实现不能默认所有平台都带完整的虚拟化定时器扩展。更重要的是即使硬件支持Guest对定时器的访问仍然会产生虚拟中断hypervisor必须参与中断路由。直接透传最典型的灾难是Guest把定时器的触发周期设置得极短比如每10微秒产生一次中断而且没有经过hypervisor的rate limiting物理CPU就被中断风暴占满其他vCPU完全饿死。又比如Guest在suspend时把定时器关闭但hypervisor不知道导致唤醒后时间完全错乱。所以xvisor的总体思路很明确虚拟定时器必须由hypervisor统一管理Guest不能直接拿到物理定时器的全部控制权。2.3 时间虚拟化的三个基本操作Offset、Dilation、Injection抛开硬件细节时间虚拟化本质上就是对Guest可见的时间轴做三个操作。第一个是Offset偏移。虚拟机的启动时间点通常不等于宿主机的时间零点而且多个虚拟机可能要求看到不同的“当前时间”。hypervisor维护一个基准时间点为每个Guest计算一个偏移量。Guest读时间时hypervisor返回“物理时间 offset”。第二个是Dilation缩放。虚拟机的时钟速率不一定跟物理时钟1:1。调试场景里经常需要“放慢”Guest的时间比如把Guest的时钟减速到0.5倍速这样跑定时器相关的竞态条件时更容易复现。反过来性能测试时又想让Guest时间尽量贴近物理时间。实现缩放最简单的方法是修改虚拟定时器的周期——物理定时器每10ms触发一次但告诉Guest每次都过了20msGuest感知的时间就被拉伸了两倍。第三个是Injection注入。虚拟定时器到期后hypervisor不能直接让物理中断打到Guest的向量表里而是需要构造一个虚拟中断通过中断控制器的虚拟化接口注入给指定的vCPU。注入的时机、优先级、目标vCPU选择都有讲究做不好就会出现中断丢失或者重复注入。xvisor的时间虚拟化本质就是一套把这三件事做扎实的框架。3. xvisor的时间虚拟化设计拆解3.1 两层定时器Host Timer与Virtual Timer先区分两个概念xvisor代码里明确区分了“主机定时器”host timer和“虚拟定时器”virtual timer。这个概念如果不厘清代码会越看越乱。Host Timer是物理定时器由xvisor自身使用用于产生hypervisor内部的心跳tick——调度器的时间片轮转、延迟任务、超时检查都靠它。xvisor把物理定时器配置成固定周期比如配置CONFIG_SCHED_PERIOD相关的值每次中断到来时xvisor的时间子系统统一处理。Virtual Timer是给每个Guest vCPU仿真的定时器。每个vCPU维护自己的虚拟定时器状态——什么时候到期、周期多少、中断注入给谁。Guest在设备树里看到的定时器节点就是xvisor为它创建的一个虚拟设备。这样的两层设计有一个明显好处物理定时器只有一个中断频率可控可预测虚拟定时器数量可以任意多且互相隔离。Guest无论怎么折腾自己的定时器配置影响的只是它自己的虚拟定时器上下文物理中断的稳定节奏始终由hypervisor掌握。3.2 核心对象timer、timer_event、vcpu_timexvisor的时间虚拟化核心代码在core/time.c和对应架构的arch/arm/time.cARM平台。语言用的是C但我先不贴大段源码而是把里面的关键对象捋清楚。第一个核心对象是struct timer代表一个“到点需要处理”的注册项。它包含回调函数、参数、到期时间、周期标志等。xvisor的定时器管理类似内核的timer wheel但精简得多基本是一个按到期时间排序的单向链表。注册一个定时器意味着把回调挂到链表上时间一到回调执行。第二个核心对象是struct timer_event这是虚拟定时器的事件描述。每个vCPU维护一个timer_event代表该vCPU当前正在等待的虚拟定时器到期点。Guest设置CNTP_TVAL或CNTV_TVAL时xvisor把它翻译成一个绝对到期时间挂到timer_event上。第三个核心对象可以叫vcpu_time context它记录了虚拟时间与物理时间的换算关系offset、dilation系数、上次同步点等。每次Guest读取虚拟计数器时xvisor根据这个context计算出返回值。这三个对象的关系是物理tick到达 → host timer链表中找到最近的timer_event → 判断哪个vCPU的虚拟定时器到期 → 更新vcpu_time context → 构造虚拟中断注入给目标vCPU。3.3 虚拟中断的流转路径从物理中断到Guest中断向量中断路径是时间虚拟化里最容易出bug的地方。我梳理一下xvisor在这条链路上做了什么。首先是物理中断入口。ARM架构下定时器中断是PPIPrivate Peripheral Interrupt每个CPU独立。xvisor在启动阶段把物理定时器中断注册到自己的中断处理框架里中断到来先进入hypervisor的异常处理向量。然后是host timer处理。xvisor的时间子系统调用timer_expire之类逻辑遍历定时器链表找出所有到期项。这里有一个实现细节链表项不是简单地删除而是会先判断是单次定时器还是周期定时器。单次定时器直接摘除周期定时器则重新计算下一次到期时间并重新入链。接着是虚拟到期判定。每个timer_event到期并不意味着Guest一定需要中断。xvisor会先比较虚拟定时器的到期时间和当前虚拟时间如果还没有真正到期说明是host timer提前醒了的“伪唤醒”直接跳过。只有真正的到期才进入下一步。最后是虚拟中断注入。xvisor调用中断控制器的虚拟化接口把虚拟定时器中断置为pending状态。对于ARM GIC这通常涉及GICD_ISPENDR或者使用GIC的虚拟化扩展GICv2/v3的list register机制。注入的目标必须是定时器所属vCPU当前正在运行的物理CPU否则还要考虑vCPU迁移带来的中断路由问题。这条路径如果捋清楚了调试时间虚拟化就有抓手。每次“Guest时间不准”的问题本质上可以归因到这条链路中的某一环offset计算错、虚拟到期判定错、中断注入丢、物理中断频率抖动。3.4 设计取舍为什么这么精简代价是什么xvisor的时间虚拟化设计主打精简这是有代价的理解取舍比记代码更有价值。第一个取舍是用链表而不是红黑树或时间轮管理定时器。xvisor面向嵌入式场景虚拟机的数量、定时器数量都不会特别大链表在几十个节点规模下表现足够而且实现简单、便于验证。代价是定时器数量多时插入复杂度为O(n)但实际场景中很少成为瓶颈。第二个取舍是把虚拟时间映射做成线性关系offset dilation而不是像KVM那样做复杂的时钟源切换。线性映射的优点是计算开销小、Guest看到的时间连续缺点是无法精确模拟“Guest暂停期间时间不走”这类的特殊语义只能靠offset调整来补偿。第三个取舍是以tick为基本驱动而不是完全事件驱动。xvisor的host timer按固定周期走周期内所有虚拟定时器到期行为都会延迟到下一个tick统一处理。这样会引入最多一个tick周期的延迟但对大多数嵌入式Guest来说完全可接受而换来的是实现简单、不用频繁配置物理定时器。这三个取舍合在一起就是xvisor时间虚拟化“小而美”的底层逻辑。你要真想给别人讲清楚xvisor的时间虚拟化把这几个取舍讲明白比背代码更有说服力。4. 实操从编译配置到时间正确性验证4.1 配置xvisor时间相关选项理论讲完动手环节还是要走的。我在QEMU模拟的ARM平台用virt机器上做过完整的xvisor Linux Guest验证。第一步是编译xvisor时间虚拟化主要关系到几个配置宏。xvisor的配置在config/arm/下一般先复制默认配置再改。时间相关的关键项大致有CONFIG_SCHED_PERIOD物理调度周期也就是host timer的tick周期。默认值通常是1000000纳秒1ms。这个值直接决定定时器中断频率影响虚拟定时器到期精度。CONFIG_TIMER_FREQ虚拟定时器频率基准ARM平台往往对应Generic Timer的频率通常在设备树里声明。CONFIG_MAX_VCPUS_PER_GUEST单Guest的最大vCPU数会影响每vCPU定时器上下文的分配。我实测时把CONFIG_SCHED_PERIOD调成1000000Guest的dmesg里时钟源是arch_sys_countertick稳定在1000Hz左右。如果你想要更高精度的虚拟定时器可以把CONFIG_SCHED_PERIOD调小到100000100微秒但代价是hypervisor自身的中断开销会明显变大跑高负载时会看到宿主机CPU占用上升。注意xvisor的配置项不是每个版本都一样源码版本不同宏名可能略有差异。建议先查config/arm/*.conf里的实际定义再对照include/下的头文件确认宏作用别盲目照抄老文章的配置。编译命令我用的经典三连cd xvisor make ARCHarm CROSS_COMPILEaarch64-linux-gnu- defconfig # 手动调整.config里时间相关项 make ARCHarm CROSS_COMPILEaarch64-linux-gnu- -j8编译产物是xvisor二进制和xvisor.dtb。虚拟机镜像和文件系统我是用buildroot现成做的这里就不展开了。4.2 设备树里的定时器节点Guest看到的虚拟定时器xvisor通过设备树向Guest描述虚拟硬件。在xvisor的dts/目录下你可以找到arm虚拟平台的设备树模板定时器节点大概长这样timer { compatible arm,armv8-timer; interrupts GIC_PPI 13 IRQ_TYPE_LEVEL_LOW, GIC_PPI 14 IRQ_TYPE_LEVEL_LOW, GIC_PPI 11 IRQ_TYPE_LEVEL_LOW, GIC_PPI 10 IRQ_TYPE_LEVEL_LOW; clock-frequency 62500000; };这个节点对Guest来说就是它的物理定时器但实际上xvisor会把clock-frequency声明的频率作为虚拟定时器的频率而不是直接透传真实频率。这里有个容易踩的坑clock-frequency的值必须跟CONFIG_TIMER_FREQ配合好。如果设备树里声明的是62.5MHz而xvisor内部用的基准是50MHzGuest里的clock_gettime跑一段时间后就会出现整数倍的漂移。建议的做法是把设备树里的clock-frequency跟实际板卡的真实timer频率解耦单独定义一个对齐xvisor内部频率的值保证虚拟计数器换算关系闭合。Guest内核起来后可以通过以下命令确认它看到的定时器信息cat /sys/devices/system/clockevents/clockevent0/current_device cat /proc/interrupts | grep arch_timer cat /proc/timer_list | head -50正常情况下Guest应该能看到arch_timer事件设备并且中断计数会随时间稳定增长。如果中断计数不动说明虚拟定时器中断没有注入成功需要优先检查GIC的虚拟中断配置。4.3 验证时间正确性的三板斧配置好之后怎么判断时间虚拟化是“对的”我总结了三板斧。第一板斧是单调性验证。在Guest里写一个简单循环反复读取CLOCK_MONOTONIC确认返回值严格递增不倒退。时间虚拟化做得粗糙时最容易出现“时间倒流”——因为虚机在suspend时没做好offset补偿恢复瞬间Guest看到的时间比暂停前还要早。我用的一个极简脚本思路大致是#!/bin/bash prev0 for i in $(seq 1 100000); do cur$(date %s%N) if [ $cur -lt $prev ]; then echo time go backwards: $prev - $cur break fi prev$cur done第二板斧是对比验证。同时记录宿主机和Guest的时间换算成同一个参考系打点对比。不是要求完全一致而是要求偏移量在一段时间内保持相对稳定波动在可接受范围。如果Guest时间线性偏离宿主机通常是dilation参数不对如果忽快忽慢通常是虚拟定时器到期处理不稳定。第三板斧是压力验证。在Guest里跑周期性任务比如每10ms打印一个时间戳连续跑一小时。人为给宿主机加负载比如同时跑多个CPU密集型vCPU或实时任务观察打印间隔是否稳定。时间虚拟化最怕的就是“邻居干扰”——别的vCPU忙的时候你的虚拟定时器到期被拖后造成Guest端的定时器延迟。xvisor在这块的短板是它的调度器比较简单虚拟定时器到期后如果目标vCPU不在运行中中断注入后要等该vCPU被调度才能被处理延迟会叠加。实测中2个vCPU的Guest在高负载下定时器周期的抖动大概在几个毫秒量级对于非实时场景能接受。4.4 用QEMU的虚拟时间做快速验证如果手上没有真实板卡QEMU是一个很趁手的验证环境。QEMU自身支持时间虚拟化相关参数比如-rtc、-icount。在调试xvisor时间虚拟化时我通常用如下命令启动qemu-system-aarch64 -machine virt -cpu cortex-a57 \ -kernel xvisor -dtb xvisor.dtb -m 1024 \ -nographic \ -append consolettyAMA0启动后进入xvisor的shell再加载Guest镜像。这种环境下验证时间虚拟化的好处是QEMU的时钟可控可以通过-icount控制虚拟CPU的指令执行速度方便制造时间压力场景。我曾在-icount 2大约每2条指令一个时钟tick下跑Guest明显看到Guest的定时器中断被拉伸这正是验证dilation语义的好场景。提示QEMU环境下时间虚拟化的表现跟真实硬件差异很大尤其是ARM Generic Timer的虚拟化扩展在QEMU里是模拟的性能不能代表真实板卡。它适合验证逻辑正确性不适合做性能评估。5. 常见问题与排查实录5.1 Guest系统时间漂移严重现象Guest里date显示的时间比实际时间慢或快很多运行越久差得越多。排查步骤先区分是墙钟时间漂移还是单调时钟漂移。在Guest里分别执行date和cat /proc/uptime对比两者的增长速度。如果uptime正常而date偏慢问题在墙钟时间同步——NTP或者RTC虚拟化没做好如果两者都偏问题更可能在虚拟计数器频率配置不对。解决方案墙钟问题优先检查xvisor的RTC虚拟化是否工作Guest里是否有/dev/rtc。频率问题检查设备树的clock-frequency和xvisor的CONFIG_TIMER_FREQ是否对齐。我在板卡上遇到过一次典型问题设备树时钟频率写的是62.5MHz但xvisor内部CONFIG_TIMER_FREQ还是默认的50MHz导致Guest时间线性慢20%修改后恢复正常。5.2 vCPU卡死或中断风暴现象Guest启动后频繁出现soft lockup/proc/interrupts里arch_timer中断数增长异常快严重时整个Guest无响应。排查步骤先用串口进xvisor shell查看vCPU状态vcpu list观察目标vCPU是否反复进出hypervisor。然后查host timer的实际触发频率确定是不是某个虚拟定时器被反复注册成短周期。xvisor的timer命令通常可以dump当前的定时器链表timer如果发现大量到期时间相等的短周期定时器基本就是Guest的定时器配置被错误透传或者虚拟中断注入后Guest没有正确应答导致中断反复重注入。解决方案多数情况是虚拟中断的清除语义没做对。ARM Generic Timer的虚拟中断是level-sensitive的Guest必须清除CNTV_CTL的enable位或重写CNTV_TVAL才能拉低中断线。如果xvisor在注入后没有同步更新定时器状态Guest清了中断但hypervisor认为还没清就会陷入注入-清除-再注入的循环。检查arch/arm/time.c里的timer_event更新逻辑确保在注入虚拟中断前已经把该定时器标记为“已到期并等待Guest处理”而不是“周期性重新触发”。5.3 挂起/恢复后时间跳变现象Guest执行suspend比如echo mem /sys/power/state到resume后系统时间要么跳到未来要么完全不动。排查步骤这个问题的本质是挂起期间物理定时器停了但虚拟时间还在按物理时间累加或者反过来。xvisor时间子系统需要感知Guest的电源状态变化。解决方案在xvisor里增加一个电源状态迁移时的“时间冻结”处理Guest进入suspend时记录当前虚拟时间快照Guest resume时重新校准vcpu_time context的offset使Guest看到的单调时间在挂起期间保持不变或按预期补偿。这部分的实现复杂度取决于虚拟平台怎么建模电源管理。我的建议是优先保证offset补偿也就是suspend期间不走虚拟时间把这次挂起产生的物理时间差不做累加。5.4 和KVM、Xen的时间虚拟化对比聊完xvisor的排查实录再横向对比一下其他方案能更清楚xvisor的定位。对比维度xvisorKVMLinux内核Xen时钟源虚拟化方式线性映射offsetdilationkvm-clock半虚拟化 TSC/ACPI虚拟化Xen共享信息页 hypercall定时器中断注入虚拟中断直接注入vCPU通过KVM的irqchip vcpu定时器事件通道注入精度上限依赖host tick频率约1ms量级可支持ns级精度kvm-clock亚毫秒级复杂度低便于阅读和二次开发高功能全面但代码庞大高分域模型复杂典型场景嵌入式、资源受限、需要高可控性的场景服务器、云主机、桌面虚拟化服务器虚拟化、云基础设施这个对比不是要分高下而是说明xvisor在时间虚拟化上的设计目标是“够用、可控、可移植”。如果你需要纳秒级精度的时间虚拟化xvisor确实不是首选但你要是想在嵌入式场景里搞清楚时间虚拟化是怎么回事xvisor的代码是很好的入门教材。5.5 那些“嵌套虚拟化”报错其实也和时间相关热搜词里有一堆“vmware嵌套虚拟化失败”“此平台不支持虚拟化AMD-V”“模块hv启动失败”之类的报错很多人以为是CPU特性问题其实有一部分和时间虚拟化脱不了干系。举个例子VMware Workstation启用了嵌套虚拟化后如果虚拟机里的系统读到的TSC频率跟宿主机的实际TSC频率不一致高精度定时器就会错乱轻则报错重则直接无法启动。另一类“HV启动失败”的场景里时间同步驱动VMware Tools的time sync和hypervisor的时钟源冲突也是常见原因。这类报错给的提示往往只有一句话但排查思路是类似的先确认宿主机的虚拟化特性是否完整egrep -c (vmx|svm) /proc/cpuinfo再确认虚拟机内部的时间源、时钟源信息是否正常最后检查hypervisor的时间同步机制是否被安全软件或系统策略关闭了。这些经验跟xvisor的时间虚拟化本质上是相通的——时间源冲突、频率不一致、中断注入失败是虚拟化领域共通的坑。6. 调试xvisor时间子系统的几个技巧6.1 用日志定位时间问题xvisor自带日志系统可以通过配置打开精细日志。排查时间虚拟化问题时我习惯先把这几个日志开关打开# xvisor启动参数或config里开启 log-level debug重点关注两类日志一类是定时器中断处理路径的日志能看到物理tick何时到、虚拟定时器何时到期另一类是虚拟中断注入日志——每次给vCPU注入timer中断时的目标vCPU和当前物理CPU。实际调试中我发现最有效的方法是在vcpu_time context的每次更新处加一条临时打印记录物理时间、虚拟时间、offset三个值。把一组数据抓下来数据之间的线性关系一目了然偏移量是常量还是变化量、速率是否偏差这些都能直接算出来。等到问题确认后再把打印关掉。6.2 构造最小复现场景时间虚拟化的问题往往要跑很久才暴露复现困难。我的经验是把场景“时间压缩”把虚拟定时器的周期调到极短把host tick调大人为放大时间偏差。比如正常场景虚拟定时器是10ms周期调试时改成1ms连续跑10秒相当于把问题放大了10倍。另外同时跑两个周期相差很远的Guest也是好办法。一个Guest跑快速tick另一个Guest跑慢速tick两者在同一个host timer上竞争很容易暴露出调度器和时间管理器之间的优先级问题。6.3 如果我要二次开发从哪入手如果你读完这篇文章想在xvisor基础上改时间虚拟化我建议按这个顺序入手第一步先熟悉vcpu_time context的维护逻辑动手改offset和dilation的换算跑Guest观察时间变化。这个改动最小、验证最快能建立直观感觉。第二步改虚拟定时器的到期处理比如把单次触发改成周期触发或者加入“定时器追赶”逻辑。这一步能让你理解物理tick到虚拟中断的完整路径。第三步再动中断注入的细节比如调整虚拟中断的优先级、目标vCPU选择策略。这一步最容易引入回归务必配合第4节的验证三板斧做充分测试。总的来说xvisor的时间虚拟化虽然精简但五脏俱全。它的设计没有太多花活却把时间虚拟化的核心问题——时间线管理、硬件定时器抽象、虚拟中断注入——都表达清楚了。读它的代码比读大型hypervisor里动辄上万行的时钟框架要友好得多。如果你打算深入研究虚拟化从xvisor的时间子系统入手再对照KVM的实现去看会有事半功倍的效果。
返回列表