在介绍KVM操作之前,先了解几个重要的数据对象:
对应设备文件/dev/kvm
static struct file_operations kvm_chardev_ops = {
.unlocked_ioctl = kvm_dev_ioctl,
.compat_ioctl = kvm_dev_ioctl,
.llseek = noop_llseek,
};
对应文件描述符fd,管理虚拟机使用
static struct file_operations kvm_vm_fops = { .release = kvm_vm_release, .unlocked_ioctl = kvm_vm_ioctl, #ifdef CONFIG_KVM_COMPAT .compat_ioctl = kvm_vm_compat_ioctl, #endif .llseek = noop_llseek, };
对应文件描述符fd,管理CPU虚拟化使用
static struct file_operations kvm_vcpu_fops = {
.release = kvm_vcpu_release,
.unlocked_ioctl = kvm_vcpu_ioctl,
#ifdef CONFIG_KVM_COMPAT
.compat_ioctl = kvm_vcpu_compat_ioctl,
#endif
.mmap = kvm_vcpu_mmap,
.llseek = noop_llseek,
};
对应文件描述符fd,管理设备虚拟化使用,如IO,timer等。
static const struct file_operations kvm_device_fops = {
.unlocked_ioctl = kvm_device_ioctl,
#ifdef CONFIG_KVM_COMPAT
.compat_ioctl = kvm_device_ioctl,
#endif
.release = kvm_device_release,
};
可以看到,对于KVM来说,在通过用户空间创建虚拟机时,它是通过设备文件和一些普通文件来与内核交互的。
我们下面了解一些主要操作:
在用户空间工具打开/dev/kvm后,使用ioctl操作内核的kvm模块,对于qemu-kvm的介绍我们后面进行。 static long kvm_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { long r = -EINVAL;
switch (ioctl) { case KVM_GET_API_VERSION: if (arg) goto out; r = KVM_API_VERSION; break; case KVM_CREATE_VM: r = kvm_dev_ioctl_create_vm(arg); break; case KVM_CHECK_EXTENSION: r = kvm_vm_ioctl_check_extension_generic(NULL, arg); break; case KVM_GET_VCPU_MMAP_SIZE: if (arg) goto out; r = PAGE_SIZE; /* struct kvm_run */ #ifdef CONFIG_X86 r += PAGE_SIZE; /* pio data page */ #endif #ifdef KVM_COALESCED_MMIO_PAGE_OFFSET r += PAGE_SIZE; /* coalesced mmio ring page */ #endif break; case KVM_TRACE_ENABLE: case KVM_TRACE_PAUSE: case KVM_TRACE_DISABLE: r = -EOPNOTSUPP; break; default: return kvm_arch_dev_ioctl(filp, ioctl, arg); } out: return r; }
可以看到,这里ioctl需要提供KVM_CREATE_VM,之后内核调用函数kvm_dev_ioctl_create_vm()函数 static int kvm_dev_ioctl_create_vm(unsigned long type) { int r; struct kvm *kvm; struct file *file;
kvm = kvm_create_vm(type);分配虚拟机对象的内存 if (IS_ERR(kvm)) return PTR_ERR(kvm); #ifdef KVM_COALESCED_MMIO_PAGE_OFFSET r = kvm_coalesced_mmio_init(kvm); if (r < 0) { kvm_put_kvm(kvm); return r; } #endif r = get_unused_fd_flags(O_CLOEXEC);获取一个未使用的fd,即文件描述fd,这是个整数。 if (r < 0) { kvm_put_kvm(kvm); return r; } file = anon_inode_getfile("kvm-vm", &kvm_vm_fops, kvm, O_RDWR);我们创建一个文件,这个是anon类型的文件系统,
这个文件我们称之为管理虚拟机的文件,即kvm-vm文件。这个文件对外提供的操作为kvm_vm_fops。这样我们可以采用类似文件的操作来管理虚拟机了。 if (IS_ERR(file)) { put_unused_fd(r); kvm_put_kvm(kvm); return PTR_ERR(file); }
if (kvm_create_vm_debugfs(kvm, r) < 0) { put_unused_fd(r); fput(file); return -ENOMEM; }
fd_install(r, file);我们刚刚创建了一个文件file对象,并且也申请了一个fd,这里我们把两者关联起来,即安装到进程的files_struct的files表里面。
这样我们就可以通过文件描述符fd来管理kvm-vm的虚拟机了。 return r; }
从上面可以看到,在通过ioctl创建虚拟机时,内核的kvm模块创建了struct kvm对象,并创建一个文件,文件是用户空间管理内核kvm的接口,函数ioctl返回这个文件的fd。
这里对虚拟机kvm对象的分配和初始化由kvm_create_vm()处理。我们在后面继续介绍。
: