这几天学习了虚拟机在创建和运行过程中,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》
.《系统虚拟化:原理与实现》
.《处理器虚拟化技术》
. https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.39.tar.xz
. https://download.qemu.org/qemu-6.2.0.tar.xz
$ .
/
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 http://sourceforge.net/projects/kvm).\n"
;
/
/
...
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 http://sourceforge.net/projects/kvm).\n"
;
/
/
...
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
)
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课