这几天学习了虚拟机在创建和运行过程中,QEMU和KVM的核心执行流程。当然只是大概过程,并没有做到流程中的每个函数都分析。
很喜欢侯捷老师的一句话:源码之前,了无秘密。我阅读的源码是qemu-6.2.0和linux-5.15.39。
编译安装qemu的过程很简单,参考官方文档就行。
可以直接用gdb命令行调试qemu,也可以vscode搭配gdb,调试属于基本能力,不多说。
动态调试qemu,并结合qemu源码分析流程。
启动参数如下:
qemu-6.2.0/softmmu/vl.c,line 2765
在vl.c文件2765行是入口,对运行程序传入的参数进行解析。
qemu-6.2.0/accel/kvm/kvm-all.c line 2306
在kvm-all.c文件2306行是kvm_init
调用栈:
主要函数kvm_init
其主要功能是保存了kvm设备描述符s->fd,创建的虚拟机的描述符s->vmfd。
在2.1节中有提到,qmp_x_exit_preconfig函数与虚拟cpu的创建有关。
动态调试跟踪分析
qmp_x_exit_preconfig qemu-6.2.0\softmmu\vl.c:2740
--> qemu_init_board qemu-6.2.0\softmmu\vl.c:2652
--> machine_run_board_init qemu-6.2.0\hw\core\machine.c:1181
--> pc_q35_init qemu-6.2.0\hw\i386\pc_q35.c:182
--> x86_cpus_init qemu-6.2.0\hw\i386\x86.c:141
--> x86_cpu_new qemu-6.2.0\hw\i386\x86.c:114
在machine_run_board_init函数中根据参数中给的机器类型调用不同的pc_machine_init函数
machine_class->init(machine)----pc_q35_init
在x86_cpu_new中继续虚拟cpu的创建
x86_cpu_new
--> qdev_realize qemu-6.2.0\hw\core\qdev.c:333
--> device_set_realized qemu-6.2.0\hw\core\qdev.c:531
--> x86_cpu_realizefn qemu-6.2.0\target\i386\cpu.c:6447
--> qemu_init_vcpu qemu-6.2.0\softmmu\cpus.c:613
在x86_cpu_realizefn中调用qemu_init_vcpu对创建的虚拟cpu进行初始化
qemu-6.2.0/softmmu/cpus.c line 611
在cpus.c文件611行qemu_init_vcpu中初始化虚拟cpu,创建执行线程。
虚拟机的运行就是kvm_cpu_exec中的do()while(ret == 0)的循环,该循环体中主要通过KVM_RUN启动虚拟机,进入了kvm的内核处理阶段,并等待返回结果。
当虚拟机退出,会根据返回的原因进行相应处理,最后将处理结果返回。
而kvm_cpu_exec自身也处于vcpu线程函数kvm_vcpu_thread_fn的循环当中,所以虚拟机的运行就是在这两个循环中不断进行。
解析参数,创建虚拟机,创建虚拟cpu,并获取三个最主要的描述符kvmfd、vmfd以及vcpufd。
根据vcpu数量创建具体的执行线程。
在线程中通过KVM_RUN启动虚拟机,进入内核KVM的处理流程。
重复循环KVM_RUN阶段。
在用户层QEMU阶段有提到通过函数kvm_vcpu_ioctl(cpu, KVM_RUN, 0)进入到内核KVM处理阶段。
linux-5.15.39/virt/kvm/kvm_main.c,line 3764
在kvm_main.c文件3764行找到内核中实际的kvm_vcpu_ioctl函数。
那调用流程就是
kvm_vcpu_ioctl --> kvm_arch_vcpu_ioctl_run
--> vcpu_run --> vcpu_enter_guest
--> static_call(kvm_x86_run)(vcpu)
在arch/x86/kvm/vmx/vmx.c line 7584
定义了一系列架构相关的操作函数
关注运行相关的
.run = vmx_vcpu_run,
在arch/x86/kvm/vmx/vmx.c line 7584
定义了一系列架构相关的操作函数
关注退出处理相关的
.handle_exit = vmx_handle_exit,
进入guest世界的准备工作。
正式进入guest执行。
根据guest退出原因进行处理,KVM先自行处理,
若kvm不能完全处理,则返回到用户层由QEMU处理。
QEMU处理后再次通过KVM_RUN进入到内核KVM流程。
.《Intel® Volume 3 System Programming Guide》
.《系统虚拟化:原理与实现》
.《处理器虚拟化技术》
. faeK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0k6r3&6Q4x3X3g2C8k6i4u0F1k6h3I4Q4x3X3g2G2M7X3N6Q4x3V1k6H3N6h3u0Q4x3V1k6D9K9h3&6#2P5q4)9J5c8X3E0W2M7X3&6W2L8q4)9J5c8Y4j5#2i4K6u0W2P5q4)9J5c8X3I4A6L8Y4g2^5i4K6u0V1y4g2)9J5k6e0p5#2i4K6u0W2x3K6W2Q4x3X3g2@1j5i4u0Q4x3X3g2^5P5R3`.`.
. 8bbK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8%4N6F1L8r3!0S2k6q4)9J5k6i4q4W2L8i4g2Q4x3X3g2G2M7X3N6Q4x3V1k6I4k6h3#2#2i4K6u0V1y4W2)9J5k6e0u0Q4x3X3f1H3i4K6u0W2N6r3q4J5i4K6u0W2P5s2Z5`.
$ ./qemu-system-x86_64 \
--enable-kvm \
-machine q35 \
-cpu host,+vmx \
-smp 1 \
-m 2048 \
-name ubuntu \
-hda /opt/vms/ubuntu.qcow2 \
-cdrom /opt/vms/ubuntu.iso
$ ./qemu-system-x86_64 \
--enable-kvm \
-machine q35 \
-cpu host,+vmx \
-smp 1 \
-m 2048 \
-name ubuntu \
-hda /opt/vms/ubuntu.qcow2 \
-cdrom /opt/vms/ubuntu.iso
void qemu_init(int argc, char **argv, char **envp)
{
//...
// 对参数进行解析
for(;;) {
if (optind >= argc)
break;
if (argv[optind][0] != '-') {
loc_set_cmdline(argv, optind, 1);
drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
} else {
const QEMUOption *popt;
popt = lookup_opt(argc, argv, &optarg, &optind);
if (!(popt->arch_mask & arch_type)) {
error_report("Option not supported for this target");
exit(1);
}
switch(popt->index) {
case QEMU_OPTION_cpu:
/* hw initialization will check this */
cpu_option = optarg;
break;
//...
// 主要关注下面几个参数
case QEMU_OPTION_m:
opts = qemu_opts_parse_noisily(qemu_find_opts("memory"),
optarg, true);
if (!opts) {
exit(EXIT_FAILURE);
}
break;
case QEMU_OPTION_enable_kvm:
qdict_put_str(machine_opts_dict, "accel", "kvm");
break;
case QEMU_OPTION_M:
case QEMU_OPTION_machine:
{
bool help;
keyval_parse_into(machine_opts_dict, optarg, "type", &help, &error_fatal);
if (help) {
machine_help_func(machine_opts_dict);
exit(EXIT_SUCCESS);
}
break;
}
case QEMU_OPTION_smp:
machine_parse_property_opt(qemu_find_opts("smp-opts"),
"smp", optarg);
break;
}
}
}
//...
// 根据accel设置accelerators = kvm
qemu_apply_legacy_machine_options(machine_opts_dict);
qemu_apply_machine_options(machine_opts_dict);
// 也会根据进程名判断可用的加速类型
configure_accelerators(argv[0]);
// 内部调用了do_configure_accelerator --> accel_init_machine
// accel_init_machine --> kvm_init
// 初始化具体的accel类(这里是kvm)
// 在qemu-6.2.0/accel/kvm/kvm-all.c line 3629
// 函数kvm_accel_class_init内部找到真正的初始化函数
// ac->init_machine = kvm_init;
//...
// 在qmp_x_exit_preconfig与虚拟cpu创建有关
if (!preconfig_requested) {
qmp_x_exit_preconfig(&error_fatal);
}
qemu_init_displays();
// 设置accel
accel_setup_post(current_machine);
os_setup_post();
resume_mux_open();
}
void qemu_init(int argc, char **argv, char **envp)
{
//...
// 对参数进行解析
for(;;) {
if (optind >= argc)
break;
if (argv[optind][0] != '-') {
loc_set_cmdline(argv, optind, 1);
drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
} else {
const QEMUOption *popt;
popt = lookup_opt(argc, argv, &optarg, &optind);
if (!(popt->arch_mask & arch_type)) {
error_report("Option not supported for this target");
exit(1);
}
switch(popt->index) {
case QEMU_OPTION_cpu:
/* hw initialization will check this */
cpu_option = optarg;
break;
//...
// 主要关注下面几个参数
case QEMU_OPTION_m:
opts = qemu_opts_parse_noisily(qemu_find_opts("memory"),
optarg, true);
if (!opts) {
exit(EXIT_FAILURE);
}
break;
case QEMU_OPTION_enable_kvm:
qdict_put_str(machine_opts_dict, "accel", "kvm");
break;
case QEMU_OPTION_M:
case QEMU_OPTION_machine:
{
bool help;
keyval_parse_into(machine_opts_dict, optarg, "type", &help, &error_fatal);
if (help) {
machine_help_func(machine_opts_dict);
exit(EXIT_SUCCESS);
}
break;
}
case QEMU_OPTION_smp:
machine_parse_property_opt(qemu_find_opts("smp-opts"),
"smp", optarg);
break;
}
}
}
//...
// 根据accel设置accelerators = kvm
qemu_apply_legacy_machine_options(machine_opts_dict);
qemu_apply_machine_options(machine_opts_dict);
// 也会根据进程名判断可用的加速类型
configure_accelerators(argv[0]);
// 内部调用了do_configure_accelerator --> accel_init_machine
// accel_init_machine --> kvm_init
// 初始化具体的accel类(这里是kvm)
// 在qemu-6.2.0/accel/kvm/kvm-all.c line 3629
// 函数kvm_accel_class_init内部找到真正的初始化函数
// ac->init_machine = kvm_init;
//...
// 在qmp_x_exit_preconfig与虚拟cpu创建有关
if (!preconfig_requested) {
qmp_x_exit_preconfig(&error_fatal);
}
qemu_init_displays();
// 设置accel
accel_setup_post(current_machine);
os_setup_post();
resume_mux_open();
}
| 参数 |
描述 |
| QEMU_OPTION_m |
虚拟机内存大小 |
| QEMU_OPTION_enable_kvm |
启用kvm加速 |
| QEMU_OPTION_machine |
虚拟机机器类型 |
| QEMU_OPTION_smp |
虚拟机cpu数量 |
kvm_init(MachineState * ms) (qemu-6.2.0\accel\kvm\kvm-all.c:2308)
accel_init_machine(AccelState * accel, MachineState * ms) (qemu-6.2.0\accel\accel-softmmu.c:39)
do_configure_accelerator(void * opaque, QemuOpts * opts, Error ** errp) (qemu-6.2.0\softmmu\vl.c:2348)
qemu_opts_foreach(QemuOptsList * list, qemu_opts_loopfunc func, void * opaque, Error ** errp) (qemu-6.2.0\util\qemu-option.c:1135)
configure_accelerators(const char * progname) (qemu-6.2.0\softmmu\vl.c:2414)
qemu_init(int argc, char ** argv, char ** envp) (qemu-6.2.0\softmmu\vl.c:3724)
main(int argc, char ** argv, char ** envp) (qemu-6.2.0\softmmu\main.c:49)
kvm_init(MachineState * ms) (qemu-6.2.0\accel\kvm\kvm-all.c:2308)
accel_init_machine(AccelState * accel, MachineState * ms) (qemu-6.2.0\accel\accel-softmmu.c:39)
do_configure_accelerator(void * opaque, QemuOpts * opts, Error ** errp) (qemu-6.2.0\softmmu\vl.c:2348)
qemu_opts_foreach(QemuOptsList * list, qemu_opts_loopfunc func, void * opaque, Error ** errp) (qemu-6.2.0\util\qemu-option.c:1135)
configure_accelerators(const char * progname) (qemu-6.2.0\softmmu\vl.c:2414)
qemu_init(int argc, char ** argv, char ** envp) (qemu-6.2.0\softmmu\vl.c:3724)
main(int argc, char ** argv, char ** envp) (qemu-6.2.0\softmmu\main.c:49)
static int kvm_init(MachineState *ms)
{
MachineClass *mc = MACHINE_GET_CLASS(ms);
static const char upgrade_note[] =
"Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n"
"(see 510K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4y4G2N6i4u0U0k6h3k6G2M7X3N6W2i4K6u0W2L8X3g2@1i4K6u0r3M7s2u0G2K9X3g2U0N6s2y4Q4x3V1k6C8N6X3#2Q4x3U0W2Q4x3X3g2Q4y4f1y4F1";
//...
QLIST_INIT(&s->kvm_parked_vcpus);
// 开始使用kvm之前的标准流程
// 打开设备/dev/kvm,检查kvm API版本
// 保存了kvm设备描述符s->fd
s->fd = qemu_open_old("/dev/kvm", O_RDWR);
if (s->fd == -1) {
fprintf(stderr, "Could not access KVM kernel module: %m\n");
ret = -errno;
goto err;
}
ret = kvm_ioctl(s, KVM_GET_API_VERSION, 0);
if (ret < KVM_API_VERSION) {
if (ret >= 0) {
ret = -EINVAL;
}
fprintf(stderr, "kvm version too old\n");
goto err;
}
if (ret > KVM_API_VERSION) {
ret = -EINVAL;
fprintf(stderr, "kvm version not supported\n");
goto err;
}
//...
// 创建虚拟机,保存虚拟机描述符s->vmfd
do {
ret = kvm_ioctl(s, KVM_CREATE_VM, type);
} while (ret == -EINTR);
//...
s->vmfd = ret;
//...
}
static int kvm_init(MachineState *ms)
{
MachineClass *mc = MACHINE_GET_CLASS(ms);
static const char upgrade_note[] =
"Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n"
"(see eecK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4y4G2N6i4u0U0k6h3k6G2M7X3N6W2i4K6u0W2L8X3g2@1i4K6u0r3M7s2u0G2K9X3g2U0N6s2y4Q4x3V1k6C8N6X3#2Q4x3U0W2Q4x3X3g2Q4y4f1y4F1";
//...
QLIST_INIT(&s->kvm_parked_vcpus);
// 开始使用kvm之前的标准流程
// 打开设备/dev/kvm,检查kvm API版本
// 保存了kvm设备描述符s->fd
s->fd = qemu_open_old("/dev/kvm", O_RDWR);
if (s->fd == -1) {
fprintf(stderr, "Could not access KVM kernel module: %m\n");
ret = -errno;
goto err;
}
ret = kvm_ioctl(s, KVM_GET_API_VERSION, 0);
if (ret < KVM_API_VERSION) {
if (ret >= 0) {
ret = -EINVAL;
}
fprintf(stderr, "kvm version too old\n");
goto err;
}
if (ret > KVM_API_VERSION) {
ret = -EINVAL;
fprintf(stderr, "kvm version not supported\n");
goto err;
}
//...
// 创建虚拟机,保存虚拟机描述符s->vmfd
do {
ret = kvm_ioctl(s, KVM_CREATE_VM, type);
} while (ret == -EINTR);
//...
s->vmfd = ret;
//...
}
void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version)
{
//...
// 根据参数smp的值,创建对应数量的虚拟cpu
for (i = 0; i < ms->smp.cpus; i++) {
x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal);
}
}
void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version)
{
//...
// 根据参数smp的值,创建对应数量的虚拟cpu
for (i = 0; i < ms->smp.cpus; i++) {
x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal);
}
}
void qemu_init_vcpu(CPUState *cpu)
{
//...
// 调用函数kvm_start_vcpu_thread创建虚拟cpu执行线程
cpus_accel->create_vcpu_thread(cpu);
//...
}
static void kvm_start_vcpu_thread(CPUState *cpu)
{
//...
// 线程函数kvm_vcpu_thread_fn
qemu_thread_create(cpu->thread, thread_name, kvm_vcpu_thread_fn,
cpu, QEMU_THREAD_JOINABLE);
//...
}
static void *kvm_vcpu_thread_fn(void *arg)
{
//...
// kvm_init_vcpu中通过kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id)
// 获取了vcpu描述符 cpu->kvm_fd = ret;
r = kvm_init_vcpu(cpu, &error_fatal);
kvm_init_cpu_signals(cpu);
/* signal CPU creation */
cpu_thread_signal_created(cpu);
qemu_guest_random_seed_thread_part2(cpu->random_seed);
// do while循环执行kvm_cpu_exec
do {
if (cpu_can_run(cpu)) {
r = kvm_cpu_exec(cpu);
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
}
}
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
kvm_destroy_vcpu(cpu);
cpu_thread_signal_destroyed(cpu);
qemu_mutex_unlock_iothread();
rcu_unregister_thread();
return NULL;
}
int kvm_cpu_exec(CPUState *cpu)
{
//...
do {
// kvm_vcpu_ioctl(cpu, KVM_RUN, 0)
// 从这里进入kvm内核阶段,开始运行虚拟机
run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);
//...
// 根据退出原因,分发处理
switch (run->exit_reason) {
case KVM_EXIT_IO:
DPRINTF("handle_io\n");
/* Called outside BQL */
kvm_handle_io(run->io.port, attrs,
(uint8_t *)run + run->io.data_offset,
run->io.direction,
run->io.size,
run->io.count);
ret = 0;
break;
default:
DPRINTF("kvm_arch_handle_exit\n");
ret = kvm_arch_handle_exit(cpu, run);
break;
}
} while (ret == 0);
cpu_exec_end(cpu);
//...
qatomic_set(&cpu->exit_request, 0);
return ret;
}
void qemu_init_vcpu(CPUState *cpu)
{
//...
// 调用函数kvm_start_vcpu_thread创建虚拟cpu执行线程
cpus_accel->create_vcpu_thread(cpu);
//...
}
static void kvm_start_vcpu_thread(CPUState *cpu)
{
//...
// 线程函数kvm_vcpu_thread_fn
qemu_thread_create(cpu->thread, thread_name, kvm_vcpu_thread_fn,
cpu, QEMU_THREAD_JOINABLE);
//...
}
static void *kvm_vcpu_thread_fn(void *arg)
{
//...
// kvm_init_vcpu中通过kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id)
// 获取了vcpu描述符 cpu->kvm_fd = ret;
r = kvm_init_vcpu(cpu, &error_fatal);
kvm_init_cpu_signals(cpu);
/* signal CPU creation */
cpu_thread_signal_created(cpu);
qemu_guest_random_seed_thread_part2(cpu->random_seed);
// do while循环执行kvm_cpu_exec
do {
if (cpu_can_run(cpu)) {
r = kvm_cpu_exec(cpu);
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
}
}
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
kvm_destroy_vcpu(cpu);
cpu_thread_signal_destroyed(cpu);
qemu_mutex_unlock_iothread();
rcu_unregister_thread();
return NULL;
}
int kvm_cpu_exec(CPUState *cpu)
{
//...
do {
// kvm_vcpu_ioctl(cpu, KVM_RUN, 0)
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!