默认分类

hyper-v 限制宿主机核心 并绑定虚拟机核心配置

Hyper-V 绑定 CPU 教程:宿主机 bcdeditcpugroups.exe 实战配置

在做工业控制、实时通信、低抖动虚拟化测试时,很多人会希望把 Hyper-V 虚拟机尽量固定到指定 CPU 上运行,减少宿主机对虚拟机的干扰。本文介绍一套比较实用的做法,包括:

  • 使用 bcdedit /set hypervisorrootproc 2 给宿主机预留 CPU
  • 使用 bcdedit /set hypervisorschedulertype Core 设置 Hyper-V 调度器
  • 使用 cpugroups.exe 将虚拟机绑定到指定逻辑处理器

这套方法适合 Windows Server 宿主机上的 Hyper-V 场景,尤其适合对时延、抖动比较敏感的工作负载。

一、先理解三个概念

1. hypervisorschedulertype

用于设置 Hyper-V 的调度方式,常见值有:

  • Core
  • Classic

在 Windows Server 2019 及之后版本中,通常建议优先使用 Core

2. hypervisorrootproc

用于限制 Root Partition,也就是宿主机自己可以使用的 Root VP 数量。

例如:

bcdedit /set hypervisorrootproc 2

表示尽量把宿主机控制在 2 个 Root 处理器上运行。

3. CPU Groups

这是 Hyper-V 更细粒度的 CPU 隔离机制。通过 cpugroups.exe,可以把某台虚拟机限制到一组指定的逻辑处理器上,例如只让虚拟机跑在 LP 2,3 上。

二、为什么要做 CPU 绑定

默认情况下,Hyper-V 会自动调度宿主机和虚拟机到不同 CPU 上运行。对于普通办公和服务器虚拟化,这样没问题;但对于一些对抖动敏感的场景,默认调度不一定够稳。

典型目的包括:

  • 减少宿主机线程抢占虚拟机
  • 减少虚拟机运行位置漂移
  • 给实时任务保留更稳定的 CPU
  • 提高 EtherCAT、运动控制、采集等场景的稳定性

需要注意的是,这种优化只能“降低干扰”,不能把 Hyper-V 直接变成硬实时平台。

三、查看当前宿主机 CPU 拓扑

先在宿主机管理员 PowerShell 中执行:

CpuGroups.exe GetCpuTopology

示例输出:

LpIndex NodeNumber PackageId CoreId RootVpIndex
0       0          0         0      0
1       0          0         1      1
2       0          0         2     -1
3       0          0         3     -1

这里最关键的是:

  • LpIndex:逻辑处理器编号
  • RootVpIndex >= 0:表示这个逻辑处理器已经分配给宿主机 Root Partition
  • RootVpIndex = -1:表示这个逻辑处理器当前不属于 Root,可优先供 Guest 使用

例如上面的拓扑可以理解为:

  • LP 0,1 给宿主机
  • LP 2,3 可优先给虚拟机

四、配置宿主机 BCD 参数

先设置 Hyper-V 调度器为 Core

bcdedit /set hypervisorschedulertype Core

再设置宿主机 Root Partition 只保留 2 个 Root 处理器:

bcdedit /set hypervisorrootproc 2

最后确认 Hyper-V 会正常启动:

bcdedit /set hypervisorlaunchtype auto

改完后重启宿主机:

shutdown /r /t 0

重启后可查看当前配置:

bcdedit /enum

你会看到类似:

hypervisorlaunchtype    Auto
hypervisorrootproc      2
hypervisorschedulertype Core

五、创建 CPU Group 并绑定虚拟机

假设现在想把某台虚拟机绑定到逻辑处理器 2,3

先创建 CPU Group:

CpuGroups.exe CreateGroup /GroupId:36AB08CB-3A76-4B38-992E-000000000001 /GroupAffinity:2,3

说明:

  • GroupId 可以自定义一个 GUID
  • GroupAffinity:2,3 表示这个组只包含 LP 2,3

然后把虚拟机绑定到这个组:

CpuGroups.exe SetVmGroup /VmName:1 /GroupId:36AB08CB-3A76-4B38-992E-000000000001

这里的 1 是虚拟机名称,请改成你自己的虚拟机名。

查看绑定结果:

CpuGroups.exe GetVmGroup

示例输出:

VmName   VmId                                   CpuGroupId
1        471D946F-C7CD-4A72-A770-52EF3287DA36   36AB08CB-3A76-4B38-992E-000000000001

再查看这个组的配置:

CpuGroups.exe GetGroups

示例输出:

CpuGroupId                           CpuCap   LpIndexes
36AB08CB-3A76-4B38-992E-000000000001 65536    2,3

这就说明:

  • 这台虚拟机已经绑定到该 CPU Group
  • 该组只允许它运行在 LP 2,3

六、建议同时限制虚拟机 vCPU 数量

如果 CPU Group 只给了 2,3 两个逻辑处理器,那么建议虚拟机 vCPU 数也设为 2,不要分配太多。

例如:

Set-VMProcessor -VMName "1" -Count 2

这样配置更一致,也更容易控制调度行为。

七、如何取消绑定

如果想解绑虚拟机与 CPU Group:

CpuGroups.exe SetVmGroup /VmName:1 /GroupId:00000000-0000-0000-0000-000000000000

然后查看:

CpuGroups.exe GetVmGroup

如果显示全零的 CpuGroupId,说明已经解绑。

如果还想删除这个 CPU Group:

CpuGroups.exe DeleteGroup /GroupId:36AB08CB-3A76-4B38-992E-000000000001

八、常见问题

1. hypervisorrootproc 设成 1 行不行?

理论上可以,但一般不建议。宿主机只剩 1 个 Root 处理器时,管理、网络、磁盘、中断都容易拥塞。多数情况下,21 更稳妥。

2. SetVmGroup 后会立即生效吗?

通常建议在虚拟机关机状态下设置,设置后重新启动虚拟机再测试,效果更稳定。

3. 这是不是等于真正物理绑核?

它更接近“把虚拟机限制在一组指定 LP 上运行”,比普通优先级设置更强,但它仍然属于 Hyper-V 调度体系,不是裸机硬实时绑核。

4. 任务管理器为什么看不到所有逻辑处理器?

如果 Root Partition 已被 hypervisorrootproc 限制,宿主机视角下可能只能看到 Root 可见处理器;这属于正常现象。

九、如何查看每个逻辑 CPU 的实际负载

如果宿主机启用了 Hyper-V,并且做了 hypervisorrootproc 或 CPU Group 隔离,那么普通的:

\Processor(*)\% Processor Time

往往只能看到 Root Partition 可见的 CPU,无法完整反映 Hyper-V 里的 Hv LP 0~17 使用情况。

这时候应该使用:

\Hyper-V Hypervisor Logical Processor(Hv LP X)\% Total Run Time

先查看宿主机支持哪些 Hyper-V 逻辑处理器计数器:

(Get-Counter -ListSet "Hyper-V Hypervisor Logical Processor").PathsWithInstances

1. 实时查看 Hv LP 0-17 的总运行占用

while ($true) {
  $paths = 0..17 | ForEach-Object { "\Hyper-V Hypervisor Logical Processor(Hv LP $_)\% Total Run Time" }
  $c = Get-Counter $paths
  $v = @{}
  foreach($s in $c.CounterSamples){
    if($s.Path -match 'Hv LP (\d+)'){
      $v[[int]$matches[1]] = [math]::Round($s.CookedValue,1)
    }
  }
  Clear-Host
  Write-Host (Get-Date -Format 'HH:mm:ss')
  Write-Host ((0..9   | ForEach-Object { "LP$_=$($v[$_] )%" }) -join '  ')
  Write-Host ((10..17 | ForEach-Object { "LP$_=$($v[$_] )%" }) -join '  ')
  Start-Sleep 1
}

这个脚本会按秒刷新,直接看到 LP0LP17 的实时负载。

2. 单独查看某几个逻辑 CPU

如果只想看 LP2LP3

while ($true) {
  $c = Get-Counter '\Hyper-V Hypervisor Logical Processor(Hv LP 2)\% Total Run Time','\Hyper-V Hypervisor Logical Processor(Hv LP 3)\% Total Run Time'
  $v = @{}
  foreach($s in $c.CounterSamples){
    if($s.Path -match 'Hv LP (\d+)'){
      $v[$matches[1]] = [math]::Round($s.CookedValue,1)
    }
  }
  Clear-Host
  "{0}  LP2={1}%  LP3={2}%" -f (Get-Date -Format 'HH:mm:ss'), $v['2'], $v['3']
  Start-Sleep 1
}

如果只想看 LP0LP1

while ($true) {
  $c = Get-Counter '\Hyper-V Hypervisor Logical Processor(Hv LP 0)\% Total Run Time','\Hyper-V Hypervisor Logical Processor(Hv LP 1)\% Total Run Time'
  $v = @{}
  foreach($s in $c.CounterSamples){
    if($s.Path -match 'Hv LP (\d+)'){
      $v[$matches[1]] = [math]::Round($s.CookedValue,1)
    }
  }
  Clear-Host
  "{0}  LP0={1}%  LP1={2}%" -f (Get-Date -Format 'HH:mm:ss'), $v['0'], $v['1']
  Start-Sleep 1
}

3. 如何理解这些计数器

  • % Total Run Time:该逻辑处理器总共忙了多少
  • % Guest Run Time:主要是虚拟机在这个逻辑处理器上的运行时间
  • % Hypervisor Run Time:主要是 Hyper-V 自己占用的运行时间

如果你想进一步分辨是 Guest 忙还是 Hyper-V 本身忙,还可以把计数器换成:

\Hyper-V Hypervisor Logical Processor(Hv LP X)\% Guest Run Time
\Hyper-V Hypervisor Logical Processor(Hv LP X)\% Hypervisor Run Time

十、推荐的一套基础配置

对于 4 逻辑处理器的小型宿主机,可以参考:

  • hypervisorschedulertype = Core
  • hypervisorrootproc = 2
  • 宿主机用 LP 0,1
  • 虚拟机绑到 LP 2,3
  • 虚拟机 vCPU 数量设为 2

这样可以形成比较清晰的隔离:

  • 宿主机:0,1
  • 虚拟机:2,3

十一、总结

Hyper-V 默认已经能很好地运行大多数虚拟机,但对于对抖动敏感的场景,可以进一步做 CPU 隔离优化。

一套常见做法就是:

  1. bcdedit /set hypervisorschedulertype Core 设置调度器
  2. bcdedit /set hypervisorrootproc 2 限制宿主机 Root CPU
  3. cpugroups.exe 把目标虚拟机绑定到指定逻辑处理器
  4. 再配合合适的 vCPU 数量,形成较稳定的 CPU 分区

需要强调的是,这套方法适合“减少干扰”,不等于把虚拟机直接变成硬实时系统。如果你的应用极度依赖确定性时延,仍应谨慎评估虚拟化平台本身的边界。

回复

This is just a placeholder img.