本文主要介绍linux虚拟化中的kvm部分。也是linux虚拟化三部马车里的最后一部。
当qemu执行kvm_vcpu_ioctl()时,进入内核态函数。具体切换至内核态是通过ioctl系统调用执行的。
ioctl系统调用sys_ioctl->ksys_ioctl->do_vfs_ioctl->vfs_ioctl->unlocked_ioctl->kvm_vcpu_ioctl
#define KVMIO 0xAE
kvm_vcpu_ioctl()第二个参数是int类型的,即KVM_RUN是个int,在qemu代码中KVM_RUN是个宏,看下这个宏具体是多少。
#define KVM_RUN _IO(0xAE, 0x80)
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IO(0xAE,0x80) _IOC(0,(0xAE),(0x80),0)
_IOC也是个宏
#define _IOC(dir,type,nr,size) \
(((dir) « _IOC_DIRSHIFT) | \ |
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
将参数带入展开如下:
#define _IOC(0,0xAE,0x80,0) \
(((0) « _IOC_DIRSHIFT) | \ |
((0xAE) << _IOC_TYPESHIFT) | \
((0x80) << _IOC_NRSHIFT) |\
((0) << _IOC_SIZESHIFT))
接下来计算下_IOC_DIRSHIFT, _IOC_TYPESHIFT, _IOC_NRSHIFT,_IOC_SIZESHIFT
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
_IOC_TYPESHIFT = 0 + 8 = 8
_IOC_SIZESHIFT =_IOC_NRSHIFT+_IOC_NRBITS+_IOC_TYPEBITS = 0 + 8 + 8 = 16
_IOC_DIRSHIFT = _IOC_SIZESHIFT +_IOC_SIZEBITS = 16 + 14 = 30
#define _IOC(0,0xAE,0x80,0) \
(((0) « 30) | \ |
((0xAE) << 8) | \
((0x80) << 0) | \
((0) << 16))
#define _IOC(0,0xAE,0x80,0) ((0)«30) | ((0xAE) « 8) | ((0x80) « 0) | ((0) « 16) |
因此,最后得到KVM_RUN为0xAE80(十进制是44672)
#define _IOC(0,0xAE,0x80,0) 0xAE80
其中,type=0xAE80
KVM逻辑:
vcpu_enter_guest()函数核心逻辑是
kvm_x86_ops是个struct型指针。
从上面结构体可以看到,所有成员函数都是函数指针。
真正实现回调函数在arch/x86/kvm/vmx/vmx.c【注意:vmx是针对Intel VT-x,svm是针对AMD】
因此,kvm_x86_ops->run(vcpu)实际调用的函数是vmx_vcpu_run
这段汇编指令主要实现进入客户机模式以及从客户机模式退出。依赖于Intel的硬件虚拟化技术(AMD的参考svm.c的svm_vcpu_run())
下面截图是测试环境qemu进程的stack情况 总结下qemu进入通过ioctl进入内核态和guest进行通信的逻辑图: