エンジニアなら知っておきたい仮想マシンのしくみ

第7回 プロセッサの仮想化をソースから知る[その2:qemu-kvm]

この記事を読むのに必要な時間:およそ 4 分

qemu-kvmから追う仮想マシンの一生

前回はLinux KVMのソースコードを読んできましたが,Linux KVMの理解をより深めるために,今回はLinux KVMの呼び出し元であるqemu-kvmのソースコードも読んでみましょう。

qemu-kvmは,オープンソースのCPUエミュレータであるQEMU に,Linux KVMに対応するための修正が加えられたバージョンです。Linux KVMがユーザモードプロセスであるqemu-kvmから「どのように制御されているのか」を併せて確認すると,仮想マシンが生成・実行される流れがよくわかるでしょう。

qemu-kvmの入手先

qemu-kvmの開発プロジェクトによる一次成果物は,現在はSourceForge.netから入手できます。

kernel virtual machine - SourceForge.net
URL:http://sourceforge.net/projects/kvm/

本記事は,執筆時点の最新安定版であるqemu-kvm-0.14.0を基に解説します。

qemu-kvmの一生 = 仮想マシンの一生

qemu-kvmは,仮想マシンを1つ起動するごとに,プロセスが1つ発生します。すなわち,仮想マシンはqemu-kvmプロセスの起動と同に発生し,プロセスの終了とともに消滅します。

qemu-kvmプロセスは,Linux KVMを利用する場合,kvm_init()関数を通してLinux KVMの初期化を行います。この関数では,Linux KVMのAPIにアクセスするために,必要な/dev/kvmファイルをオープンします。以後,Linux KVMのカーネルモジュールで仮想マシンや仮想プロセッサ(vcpu)を作成したり,メモリなどの資源を割り当てたり,また仮想プロセッサを実行する操作は,ここで得られたfdに対するioctl()システムコールで実現することになります。

qemu-kvm-0.14.0/qemu-kvm.c

149 int kvm_init(void)
150 {
151     int fd;
152     int r, gsi_count;
153
154
155     fd = open("/dev/kvm", O_RDWR);
156     if (fd == -1) {
157         perror("open /dev/kvm");
158         return -1;
159     }
  《中略》 
211     return kvm_create_context();
212
213   out_close:
214     close(fd);
215     return -1;
216 }

kvm_init() 関数は,Linux KVMがオープンできたら,続けてkvm_create_context()関数を呼び出して,Linux KVMへ仮想マシンの作成を指示します。この処理は,さっそくオープンしたばかりの/dev/kvmデバイスを通してKVM_CREATE_VM APIを呼び出すことで実現されています。

qemu-kvm-0.14.0/qemu-kvm.c

308 int kvm_create_vm(kvm_context_t kvm)
309 {
310     int fd;
311 #ifdef KVM_CAP_IRQ_ROUTING
312     kvm->irq_routes = qemu_mallocz(sizeof(*kvm->irq_routes));
313     kvm->nr_allocated_irq_routes = 0;
314 #endif
315
316     fd = kvm_ioctl(kvm_state, KVM_CREATE_VM, 0);
317     if (fd < 0) {
318         fprintf(stderr, "kvm_create_vm: %m\n");
319         return -1;
320     }
321     kvm_state->vmfd = fd;
322     return 0;
323 }

この後,qemu-kvmプロセスは,仮想プロセッサの割り当てやメモリの割り当て,デバイスエミュレータの登録などさまざまな処理を行います。今回は,仮想プロセッサに着目して,仮想プロセッサがどのように実行されているかを追ってきましょう。

qemu-kvmのスレッドモデル

以下に,qemu-kvmがLinux KVMを使った仮想マシンを実行する場合のスレッドモデルを示します。

qemu-kvmは,プロセス起動後に必要な仮想マシン分の仮想プロセッサを作成し,またスレッドを立ち上げます。以後,各スレッドは仮想プロセッサとしてひとつひとつが独立して動くイメージです。

図1 qemu-kvmのスレッドモデル

図1 qemu-kvmのスレッドモデル

仮想プロセッサの生成

仮想プロセッサの初期化処理は,qemu-kvm-0.14.0/hw/pc.cにあります。pc_cpus_init()関数は,仮想マシンに割り当てられた数の仮想プロセッサを初期化します。

qemu-0.14.0/hw/pc.c

942 void pc_cpus_init(const char *cpu_model)
943 {
944     int i;
945
946     /* init CPUs */
947     for(i = 0; i < smp_cpus; i++) {
948         pc_new_cpu(cpu_model);
949     }
950 }

pc_cpus_init()関数は,各仮想プロセッサを初期化するためにpc_new_cpu()関数を呼び出していますが,その過程でLinux KVM利用時にはkvm_init_vcpu()関数に制御が移ります。

265 void qemu_init_vcpu(void *_env)
266 {
267     CPUState *env = _env;
268
269     env->nr_cores = smp_cores;
270     env->nr_threads = smp_threads;
271     if (kvm_enabled())
272         kvm_init_vcpu(env);
273     return;
274 }

kvm_init_vcpu()関数では,各仮想プロセッサごとにスレッドが生成され,実行が開始されます。

qemu-kvm-0.14.0/qemu-kvm.c

1470 int kvm_init_vcpu(CPUState *env)
1471 {
1472     pthread_create(&env->kvm_cpu_state.thread, NULL, ap_main_loop, env);
1473
1474     while (env->created == 0) {
1475         qemu_cond_wait(&qemu_vcpu_cond);
1476     }
1477
1478     return 0;
1479 }

上記の過程により,仮想プロセッサが動きだします。

著者プロフィール

長谷川猛(はせがわたけし)

(株)SRAで7年間のシステム構築&提案を経験したのち,Fusion-ioのセールスエンジニアを経て,フリーランスエンジニアとして活動中。『LDAP Super Expert』(技術評論社)に寄稿したほか,『Xen 徹底入門』(翔泳社)および『萌え萌えうにっくす!UNIX ネットワーク管理ガイド』(毎日コミュニケーションズ)の共著者のひとりである。

スノーボード,ごまラーメン,飼い犬のミニチュアシュナウザー「ラピス君」が大好き。

コメント

コメントの記入