主にイベントループとKVM周り.
QEMU Main Loop
- QEMUの主処理はGLibを使ったイベントループ.
- ゲストのvCPUは別のスレッドで実行(後述)
vl.c:main() =>
vl.c:main_loop() =>
util/main-loop.c:main_loop_wait() =>
loop {
slirp_pollfds_fill();
os_host_main_loop_wait() =>
g_main_context_acquire(context)
glib_pollfds_fill() =>
g_main_context_prepare()
g_main_context_query()
qemu_mutex_unlock_iothread()
qemu_poll_ns() =>
g_poll()
qemu_mutex_lock_iothread()
glib_pollfds_poll() =>
g_main_cotext_disapatch()
g_main_context_release(context)
slirp_pollfds_poll()
}
- QEMUはiothreadに関してlockを持っている
- イベントのpoll中はこのiothreadのlockを外す
- イベントループ処理の流れを理解するにはGLibの動作を知る必要がある.
GLibメモ
GSource
- http://maemo.org/api_refs/4.0/glib/glib-The-Main-Event-Loop.html#GSource
- GLibにおけるイベントを表現
- file descriptors
- file
- pipe
- sockets
- ...
- timeout event
- file descriptors
- sourceの主要な関数
prepare()poll()の前に呼ばれる- sourceが(pollすることなく)準備完了であればtrueを返す
- 例
- timeout eventでtimeoutしてたらtrue
- file descriptorの場合は,通常はpollするのでfalse
- 例
check()- poll()後に呼ばれる
- dispatchすることができるならtrueを返す
dispatch()- イベントに対応付けられたcallback関数を実行
- 各sourceにはpriorityが存在
基本的なイベント処理の流れ
g_source_attach()でcontextに対してsourceを追加- メインループ
g_main_context_prepare(context, &max_priority)- コンテキストが持つ各GSourceのprepare()を呼ぶ
- ここで,max_priorityにprepare()がtrueの中から最大のpriorityが設定される.
g_main_context_query(context, max_priority, ..., fds)- max_priorityを持つsourceのfile descriptorを取得
- この関数によって実際にpoll()するfdを選択する
g_poll()- poll
g_main_context_check()- dispatch可能なfdをチェック
g_main_cotext_disapatch()- コールバック関数を呼ぶ
I/O handler
- 仮想デバイスを処理するためのハンドラ (GSource)
- 初期化
util/main-loop.c:qemu_init_main_loop()iohandler_init() => util/async.c:aio_context_new()
g_source_attach()でiohandlerをdefault contextのsourceに追加
- コールバック関数の登録
util/iohandler.c:qemu_set_fd_handler()
KVM 初期化
- 全体の初期化
accel/kvm/kvm_all.c:kvm_init()
vl.c:main() =>
accel/accel.c:configure_accelerator() =>
accel_init_machine() =>
acc->init_machine() = accel/kvm/kvm_all.c:kvm_init()
- マシン初期化時に,vCPUごとにスレッドが作成される.
target/i386/cpu.c:x86_cpu_realizefn() =>
cpus.c:qemu_init_vcpu() =>
qemu_kvm_start_vcpu()
qemu_kvm_start_vcpu() =>
qemu_thread_create(..., qemu_kvm_cpu_thread_fn, ...)
KVM vCPU event loop
cpus.c:qemu_kvm_cpu_thread_fn =>
qemu_mutex_lock_iothread()
loop {
accel/kvm/kvm-all.c:kvm_cpu_exec() =>
qemu_mutex_unlock_iothread()
do {
kvm_vmrun_ioctl(cpu, KVM_RUN, 0)
ret = handle_vmexit()
}while(ret == 0);
qemu_mutex_lock_iothread()
qemu_wait_io_event()
}
qemu_mutex_unlock_iothread()
- 各vCPUは
iothreadのロックを持つ KVM_RUN実行中はiothreadのロックを外す
KVMRUN
- ※ これはLinux側の処理
virt/kvm/kvm_main.c:kvm_vcpu_ioctl() =>
arch/x86/kvm/x86.c:kvm_arch_vcpu_ioctl_run() =>
vcpu_run() =>
loop {
ret = {
vcpu_enter_guest() =>
kvm_x86_ops->run(vcpu) = arch/x86/kvm/vmx.c:vmx_vcpu_run() =>
VMLAUNCH
kvm_x86_ops->handle_exit(vcpu) = arch/x86/kvm/vmx.c:vmx_handle_exit() =>
kvm_vmx_exit_handlers[exit_reason](vcpu)
}
if (ret >= 0) break
}
- VMCALLの処理
arch/x86/kvm/vmx.c:handle_vmcall() =>
arch/x86/kvm/x86.c:kvm_emulate_hypercall() =>
handle_vm_call or kvm_skip_emulated_instruction()
続く?