在雲端、容器、嵌入式開發等場景,程式常需感知自身是否運行於虛擬化環境,並識別具體 Hypervisor 類型(KVM、Xen、VMware、VirtualBox 等)。本文系統整理 /proc、/sys、CPUID、DMI/SMBIOS、ACPI 等多層面偵測手法,並提供可直接部署的 Python、C、Shell 檢測腳本。
/proc 特徵檔| 虛擬化類型 | 特徵路徑 | 判斷邏輯 |
|---|---|---|
| OpenVZ / Virtuozzo 容器 | /proc/vz 存在、/proc/bc 不存在 | 容器內無 /proc/bc;Host 節點兩者皆有 |
| Xen PV (Paravirtualized) | /proc/xen/capabilities | 檔案大小 > 0 為 Dom0,= 0 為 DomU |
| User-Mode Linux (UML) | /proc/cpuinfo | 包含 UML 關鍵字 |
| LXC/Docker 容器 | /proc/1/environ、/.dockerenv | PID 1 環境變數含 container= 或根目錄有 .dockerenv |
#!/usr/bin/env python3
import os, sys
def main():
if os.geteuid() != 0:
print("WARN: 建議 root 執行以獲得完整資訊", file=sys.stderr)
# OpenVZ
if os.path.exists("/proc/vz"):
print("openvz container" if not os.path.exists("/proc/bc") else "openvz host(node)")
# Xen PV
xen_cap = "/proc/xen/capabilities"
if os.path.exists(xen_cap):
print("xen dom0" if os.path.getsize(xen_cap) > 0 else "xen domU")
# UML
try:
with open("/proc/cpuinfo") as f:
if "UML" in f.read():
print("uml")
except OSError:
pass
if __name__ == "__main__":
main()
Intel/AMD 定義 CPUID leaf 0x40000000 回傳 Hypervisor 簽名(EBX/ECX/EDX 拼成 12 字元字串)。常見簽名對照:
| Hypervisor | 簽名字串 | 備註 |
|---|---|---|
| Xen HVM | XenVMMXenVMM | |
| VMware | VMwareVMware | |
| KVM | KVMKVMKVM | QEMU/KVM 預設 |
| Microsoft Hyper-V | Microsoft Hv | |
| Parallels | lrpepyh vr | 實為 prl hyperv 反序 |
| VirtualBox | VBoxVBoxVBox | 較新版本支援 |
/* detect_hv.c */
#include
#include
#include
#define CPUID(in, a, b, c, d) \
__asm__ volatile ( \
"cpuid" \
: "=a" (a), "=b" (b), "=c" (c), "=d" (d) \
: "0" (in) \
)
int main(void) {
uint32_t eax, ebx, ecx, edx;
char sig[13] = {0};
CPUID(0x40000000, eax, ebx, ecx, edx);
*(uint32_t*)(sig + 0) = ebx;
*(uint32_t*)(sig + 4) = ecx;
*(uint32_t*)(sig + 8) = edx;
if (strncmp(sig, "XenVMMXenVMM", 12) == 0) puts("xen hvm");
else if (strncmp(sig, "VMwareVMware", 12) == 0) puts("vmware");
else if (strncmp(sig, "KVMKVMKVM", 12) == 0) puts("kvm");
else if (strncmp(sig, "Microsoft Hv", 12) == 0) puts("hyper-v");
else if (strncmp(sig, "VBoxVBoxVBox", 12) == 0) puts("virtualbox");
else puts("bare metal / unknown");
return 0;
}
編譯執行:gcc -o detect_hv detect_hv.c && ./detect_hv
# VirtualBox
dmidecode -s system-manufacturer # → innotek GmbH
dmidecode -s system-product-name # → VirtualBox
# VMware
dmidecode -s system-manufacturer # → VMware, Inc.
dmidecode -s system-product-name # → VMware Virtual Platform
# Hyper-V
dmidecode -s system-manufacturer # → Microsoft Corporation
dmidecode -s system-product-name # → Virtual Machine
# QEMU/KVM (預設)
dmidecode -s system-manufacturer # → QEMU
dmidecode -s system-product-name # → Standard PC (i440FX + PIIX, 1996)
腳本化判斷:
#!/bin/bash
vendor=$(dmidecode -s system-manufacturer 2>/dev/null | tr '[:upper:]' '[:lower:]')
case "$vendor" in
*innotek*) echo "virtualbox";;
*vmware*) echo "vmware";;
*microsoft*) echo "hyper-v";;
*qemu*) echo "kvm/qemu";;
*) echo "physical or unknown";;
esac
systemd-detect-virtsystemd-detect-virt
# 輸出範例:kvm, vmware, xen, oracle, virtualbox, wsl, docker, lxc, openvz, qemu, uml, none
內部綜合上述所有機制,為現代 Linux 發布版首選標準工具。
容器共享宿主機核心,/proc/cpuinfo、CPUID 反映宿主機狀態。結合 systemd-detect-virt --container 可區分「容器內」與「宿主機層」虛擬化類型。
/proc/cpuinfo flags(如 hypervisor bit)不足以區分具體 Hypervisor。dmidecode、cpuid 指令需 root;容器需 --privileged 或 CAP_SYS_RAWIO。systemd-detect-virt → 失敗則 CPUID → 再失敗則 DMI → 最後回退 /proc 特徵檔。掌握多層次偵測技術,即可在自動化部署、授權驗證、效能調優、安全審計等場景精準識別執行環境,編寫更健壯的基礎設施代碼。