Intel CPUで16bitと32bitのモードが廃止されるという記事が出ていました。
Intel、新「X86-S」アーキテクチャで8086互換を切り捨て
新しい仕様は、X86-Sアーキテクチャと言うそうです。
パソコンがブートすると、いきなり64bitのモードに入って、32bitはアプリケーションのためにCompatibility modeのみ残るそうです。
長く親しんだモードがなくなるのは寂しいですね。
実際の製品になるのはいつになるのでしょうか。
Intel CPUで16bitと32bitのモードが廃止されるという記事が出ていました。
Intel、新「X86-S」アーキテクチャで8086互換を切り捨て
新しい仕様は、X86-Sアーキテクチャと言うそうです。
パソコンがブートすると、いきなり64bitのモードに入って、32bitはアプリケーションのためにCompatibility modeのみ残るそうです。
長く親しんだモードがなくなるのは寂しいですね。
実際の製品になるのはいつになるのでしょうか。
QEMU/KVMでセキュアブートを利用する方法が紹介されています。
セキュアブートを実現するには、3つも鍵が必要なんですね。公開鍵と秘密鍵を作って、それを利用して鍵をNVRAM領域に書き込む必要があるそうです。
少し手間がかかります。QEMUを使って体験してみると、セキュアブートの理解が進んでいいのではないでしょうか。
QEMU/KVMでUEFIファームウェアを使う方法が紹介されています。
OVMFというオープンソースのファームウェアを使っています。
UEFIを使うメリットとしては、2TB以上のハードディスクを使う場合ぐらいしか現状ではないかもしれませんが。
でも、UEFIを開発するにはデバッグもできていいのでしょうね。
このところVirtualBoxを契機として、準仮想化クロックについて調べてきました。
ところが、SUSEのマニュアルがゲストOSでNTPを使わないことを推奨していることの理由がどうしてもわかりませんでした。
昔QEMUにかかわったとき、Linuxの割り込み頻度が1000Hzから250Hzに変わったことを思い出しました。
すると、こんな記事が見つかりました。
最近のLinuxカーネルはティックレスになっていて、仮想化しても時刻がずれないというのです。そこで、ティックレスカーネルについて調べてみました。
VMwareのサイトにLinuxの割り込み頻度の変遷がまとめられています。
Linuxの2.4ではHZ=100Hzだったのが、2.6ではHZ=1000Hzに変わり、2.6.14以降はHZ=250Hzになっています。
ティックレスカーネルのサポートはLinux 2.6.18より始まっています。
昔はPIT(Programmable Interval Timer)を使って定期的に割り込みをかけていました。100Hzなら10msに一度カーネルに割り込みが発生するようにプログラムします。割り込みが発生したら10msたったとして時計を進めていたわけです。
Linuxの割り込み頻度は、多いほうが時計の精度が高いように思います。しかし割り込みの時間内に処理が終わらず次の割り込みが起こってしまうと割り込みが失われた状態になり時計が狂ってきます。
仮想化環境で時計の狂いが大きかったのはこれが大きな原因です。
ティックレスカーネルは時間を決めて定期的に割り込みをかけることをやめてしまうものです。その代わり1度きりのone shot timerというタイマーを使って割り込みまでの時間を設定して割り込みをかけます。動いているプロセスが少ないときは長い時間割り込まないようにします。動いているときは、定期的に割り込みます。
時間があらかじめ決まっていないので、カーネルがsleepしてどれくらい時間がかかったかを毎度ハードウェアをチェックする必要があります。それをクロックソースと呼んでいます。
クロックソースとして代表的なものが、TSC(Time Stamp Counter)、ACPI PM(ACPI Power Management timer)、HPET(High Precision Event Timer)などがあります。
Linuxのディストリビューションがティックレスカーネルを採用した時期はまちまちです。以下にまとめてみました。
ディストリビューション | バージョン | 時期 |
---|---|---|
Red Hat Enterprise Linux | 6 | 2010-11-09 |
CentOS | 6 | 2011-07-10 |
Fedora | 7 x86 | 2007-05-31 |
Fedora | 8 x86_64 | 2007-11-8 |
Ubuntu | 7.10 | 2007-10-18 |
SUSE Linux Enterprise Server | 12 | 2014-10-28 |
UbuntuとFedoraが同時期で、Red HatとCentOSが次で、SUSEの順番です。
使っている人が多いと思われるCentOSは6からのサポートです。最近になって時計の話題を見かけることが少なくなったことはこのためだと思われます。
また、Ubuntuの方が時計が正確だという報告があったのですがティックレスカーネルの採用時期が早かったためでしょう。
SUSEは本当に最近のサポートですね。
ティックレスカーネルになってクロックソースを指定できるようになりました。最近のディストリビューションでは準仮想化クロックを採用しているところばかりです。準仮想化クロックといってもこれまで調べたようにその中身はTSCを使っています。ゲストOSの時計が正確になったのは、CPUの周波数の変化によってTSCの周波数が変化しなくなっていることも大きいと思います。
Linuxカーネルの進化とCPUの進化によって時計の正確さが増したということでしょう。
以前、準仮想化クロック(kvm-clock)は起動してからの時間とTSCの値をメモリに書き込んでいることがわかりました。
また、その書き込むタイミングはホストOSからゲストOSに制御が移る前だということもわかりました。
ただ、書き込む頻度についてはよくわかりませんでした。
そこで、LinuxのKVMのソースコードを読んで詳しく調べてみました。
ソースコードはarch/x86/kvm/x86.cで、kvm_guest_time_update()でシステムタイムとTSCのメモリの値を更新しています。
この関数は、vcpu_enter_guest()で呼ばれます。ホストOSからゲストOSに制御を移す関数です。
ただ、KVM_REQ_CLOCK_UPDATEという名前のリクエストがあったときのみkvm_guest_time_update()は呼ばれますので、どういう条件か探してみます。
わかったところを列挙すると次のようになります。
現在のパソコンのシステムを考えると、最初の3つくらいがよく起こることだと思います。でも、思いのほか準仮想化クロックの値の更新がなされません。Core iシリーズ以降の最近のCPUでは、CPUの周波数が変わったときでもTSCの周波数は変わらないです。プロセスの移動もそう頻繁にはないです。システムが起動してから、あまり頻繁には準仮想化クロックは更新されていないということがわかります。
実際に、ホストOSの時刻を変更してもゲストOSの時刻が同期して変わることはありません。
このことから準仮想化クロックは頻繁には更新されず、主にTSCによって現在時刻を計算していることがわかりました。
前回、準仮想化クロックが5分で6秒くらい遅れることがわかりました。
少し細かな話になりますが、ソースコードを探索して原因の追究ととりあえずの対処法を紹介します。
まずはVirtualBoxのソースコードをダウンロードします。以前いじったときから5年ほど時間がたっていました。
Googleの検索でparavirtualizationを検索しているとき、VirtualBox 5.0 Beta2 releasedのリリースノートに
VMM: added support for using Paravirtualization providers with raw-mode VMs
とありました。
raw-mode VMsはなんだろうということで、svn logでraw-modeを検索してみます。
すると、
r55068 disble GIM for raw-mode VMs
とあって、GIMは何だろうということになりました。そこでソースコードを検索してみると、
vbox/src/VBox/VMM/VMMR3/GIM.cppに説明がありました。
GIM providerはparavirtualization interfaceとして参照される。
準仮想化インターフェースはソースコードではGIMと呼ばれているんですね。GIMとは、Guest Interface Manager Deviceの略で準仮想化インターフェースのすべてを扱っています。
ソースコードには次のような説明があります。
準仮想化の1つのゴールはゲストをもっと正確に、もっと効率的にすることです。たとえばゲストOSは、ホストプロセッサの正しいTSCの周波数がGIM providerから供給されることに依存します。これによりゲストはTSC自身をキャリブレートすることを避けることができ、正確さと効率がよくなります。
ということは、VirtualBoxがTSCの周波数を供給していることになります。
また、ゲストOSの設定ファイルのところにあるVBox.logというログを眺めて、TSCを探します。すると、TSCモードをVirtTscEmulatedからRealTSCOffsetに変えると出ています。このRealTSCOffsetがあやしいです。
これは、vbox/src/VBox/VMM/VMMR3/TM.cppにあります。
このファイルを見てみると、u64CpuHzがCPUの周波数を設定しているようです。また、tmR3CalibrateTSC()という関数がTSCのキャリブレーションをしていそうです。
また、kvm-clockはメモリーにTSCと、それをシステム時間に変換するスケールファクターを保存していたことを頼りに、どこで設定しているかを探します。
すると、vbox/src/VBox/VMM/include/GIMKvmInternal.hにstruct GIMKVMSYSTEMTIMEという構造体のu32TscScaleがTSCをシステム時間に変換するスケールファクターであることがわかります。今度は、u32TscScaleを設定しているところをソースコードで探します。
u32TscScaleは、vbox/src/VBox/VMM/VMMR3/GIMKvm.cppでpVM->cTscTicksPerSecondを代入しています。
また、いろいろな情報をログファイルに出力していることに気づき、VirtualBoxのゲストの設定ファイルのところにあるVBox.logをGIMやTSCを目安に眺めてみるとcTscTicksPerSecondも数値が記録されていることがわかります。
このように、関連するであろうファイル、変数やマクロ名をソースコードをから探し出しました。
完全に理解するところまでは行かなかったのですが、重要と思われる変数などは抜き出せたように思います。
VirtualBoxと平行して使っていたVMware Playerでdmesg |grep tscとdmesg |grep TSCをやると次のように出ます。
kazu@Mars:~$ dmesg |grep tsc
[ 0.000000] tsc: Detected 3400.109 MHz processor
[ 1.595236] Switched to clocksource tsc
kazu@Mars:~$ dmesg |grep TSC
[ 0.000000] TSC freq read from hypervisor : 3400.109 MHz
CPUの周波数3400MHzとでて、TSCの周波数はhypervisorから供給されていると出ています。これは、今使っているCPUの定格の周波数です。
じゃあVirtualBoxの場合はどうなんだとなりました。ログファイルでcTscTicksPerSecondを見てみると、
00:00:01.277674 TM: cTSCTicksPerSecond=3 478 274 491 (0xcf5241bb) enmTSCMode=1 (VirtTscEmulated)
3478274491Hzということみたいです。3478MHzということで、VMware Playerより大きいです。
また、この違いが1分でどれくらいの差になるのか計算すると、
60 * 3400109000 / 3478274491 = 58.65 (秒)
となりました。ほぼ、5分で6から7秒の遅れということで、前回の計測とあっています。
ということがわかりました。
VirtualBoxのフォーラムをGoogleでcTSCTicksPerSecondを検索すると、VBoxManageでゲストOSのTSCTicksPerSecondを設定する項目があるという情報を見つけました。
この項目が、準仮想化にも関係するのかよくわかりませんでしたが、次のように設定してから立ち上げました。
VBoxManage setextradata "[VMName]" "VBoxInternal/TM/TSCTicksPerSecond" 3400109000
周波数の値は、VMware Playerの値を使っています。わからなければ、CPUの定格の周波数でいいと思います。すると、時計の遅れはなくなりました。
これは、とりあえずの対処になります。TSCの周波数を決め打ちしています。TSCの周波数がCPUの電源管理状態によらず一定でないとできない対処です。最近のCore iシリーズ以降であれば使えると思います。でもこれで、VirtualBoxのTSCの周波数を決めるところにバグがあることがわかりました。
原因がつかめたようでよかったです。早くバグが修正されるといいなと思います。
このところずっとVirtualBoxを使っていて、ゲストOSの時計が遅れていることに気がつきました。なんか変だということで、精度を簡単に測ってみることにしました。
準仮想化クロックkvm-clockは、/sys/devices/system/clocksource/clocksource0/current_clocksourceでクロックソースとして指定しています。その他にも変えられるものがあるので、準仮想化ではなくなってしまいますがクロックソースを変えてみます。
$ sudo sh -c "echo tsc >/sys/devices/system/clocksource/clocksource0/current_clocksource"
で変えられます。
変えられるのは、cat /sys/devices/system/clocksource/clocksource0/available_clocksourceで確認できます。
tsc(Time Stamp Counter)というのは、CPUの刻む時計です。acpi_pm(ACPI Power Management Timer)というのは、ACPIというパソコンの規格で定められている精度の高いタイマーです。
また、VirtualBoxの準仮想化インターフェースでKVMのほかに、Legacyと”なし”にしたときも同様に測ってみます。
計測は手元のストップウォッチで5分を計測して、dateコマンドで秒単位の変化を見ました。
ホストWindows 7 x86_64 ultimate、ゲストUbuntu 12.04 x86_64です。VirtualBoxは5.0で、GuestAdditionsを入れてあります。
準仮想化インターフェースをKVMにしてかかった時間を計測します。
実時間 | kvm-clock | tsc | acpi_pm |
---|---|---|---|
5分00秒 | 4分54秒 | 4分54秒 | 5分00秒 |
kvm-clockとtscのときは、5分経過したとき6秒も時計が遅れていました。acpi_pmにしたときは、ほぼ5分になります。
kvm-clockはtscをつかっているので、kvm-clockがずれているのはtscが原因だと思います。
準仮想化インターフェースのLegacyがよくわからなかったので、計測してみました。
実時間 | tsc | acpi_pm |
---|---|---|
5分00秒 | 5分00秒 | 5分00秒 |
tsc、acpi_pmの両方とも正確に測れています。
準仮想化インターフェースをなしにしたときも測定してみました。
実時間 | tsc | acpi_pm |
---|---|---|
5分00秒 | 5分00秒 | 5分00秒 |
tsc、acpi_pmともに時計は正確でした。
時計の遅れがゲストOSのkvm-clockドライバーの問題なのか、VirtualBoxの準仮想化インターフェースの問題なのかはっきりさせるため、KVMを使ってみました。
LinuxのホストマシンがないのでVMware Playerを使ってKVMを仮想環境で動かしました。
ホストOSは、Windows 7 x64 Ultimate上でVMware Player 7.1.2のIntel VT-x/EPTまたはAMD-V/RVIを仮想化という機能を使ってUbuntu 14.04 x86_64を動かしました。
Ubuntu 14.04 x86_64上でQEMUの開発バージョンでKVMを使います。
ゲストOSは、Ubuntu 12.04 x86_64です。
実時間 | kvm-clock |
---|---|
5分00秒 | 5分00秒 |
とくに大きな遅れは見られなかったです。ホストOSが複雑なので難しいのですが、kvm-clockが遅れの原因ではなさそうです。
残念ですね。せっかく準仮想化クロックを導入しても時計が正確ではないです。VirtualBoxが1分で1秒以上遅れてしまうのは問題ですね。
マニュアルを読むとGuestAdditionsが時計を補正するとあります。どういう場合に補正されるのかが気になります。少なくとも準仮想化クロックをKVMにしたときは、補正されていないです。
準仮想化インターフェースのLegacyは、VBoxManageのヘルプを読むとVirtualBox 5.0以前に作られた仮想マシンを5.0以降で起動すると自動的に設定されるとあります。VirtualBoxのログを読むとLegacyが選ばれたときは準仮想化インターフェースとしてnone(なし)が選ばれています。そのため、Legacyとnoneは実質的な違いはないと思います。
何が原因で時計が遅れているのかを調べるため、次回ソースコードを追いかけて検討しようと思います。
準仮想化クロック(kvm-clock)を使用しているとき、UbuntuゲストでNTPを使用しなくてもいいというような記事がありました。
それでいろいろ調べてみました。ところが、よく見るとUbuntuのWikiのヘルプページはひんぱんに書き換えられているらしいです。最近のヘルプではUbuntuをゲストOSに使っているときもNTPを使ったほうがよいと書いてあります。ゲストでNTPを使わなくてもkvm-clockは正確だそうです。
kvm-clockだけ正確でもシステムクロックが正確にならないと意味ないと思うのですが、後は自分でやってねというスタンスでしょう。
kvm-clockは、システムが起動してからの経過時間を供給しますが、閏秒などの情報を供給しません。
というわけで、kvm-clockを使っているときも時計を正確にしたければNTPを使いましょう。
SUSEのマニュアルだと、ゲストに使ったときはNTPを使わないようにという説明があるのですが、理由がはっきりしません。
Xenの場合はカーネルパラメータにxen.independent_wallclockがあります。これを使う場合はNTPを使うと2重に時計を管理することになり不具合が出るという報告があります。
また、Xenを使っているときでも、xen.independent_wallclock=1にしてNTPを使ったほうがいいというメールもあります。
sistemad telling me "Time has been changed", only on xen guests?
システムクロックを同期する方法が他にあるかと思いましたが、いくら検索してみても情報が出てきません。NTPなしで使用してみて問題が出るようであればNTPを使ったほうがいいと思います。kvm-clockのしくみから見ても、そのほうが自然です。
準仮想化クロックのしくみについて解説してるブログがありました。
まず、ゲストOSが読めるメモリーをMSRを使って登録しておきます。そこへKVMがホストの起動してからの時間とTSC(Time Stamp Counter)を書き込みます。ゲストOSは時間を知りたいときに、まず現在のTSCを読みます。そして、メモリー上の起動してからの時間とTSCの差分の値を使って現在時間を割り出します。
ホストの起動したときの時刻は別に保存してあるそうです。
メモリーを使って値のやり取りをしていることと、TSCを使っているのが特徴かなと思います。
メモリーの値がアップデートされるのはホストOSからゲストOSに制御が移るVM eventの前だそうです。
時計がどれだけ正確化は、ゲストOSがどういうタイミングで時計をチェックして更新するかによります。
Linuxのシステムクロックについては、こちらの解説がくわしいです。4回の連載になっていて、kvm-clockについてもpvclockとして解説があります。
kvm-clockはRTCのようにハードウェアクロックの1種とされているようですが。
ハードウェアの時計とシステムクロックの同期は、システムの起動時とサスペンド、レジューム時に行われるそうです。その他に、getnstimeofday()/ktime_get_real()はハードウェアの時計も参照するとなっています。、gettimeofday()はハードウェアの時計を参照するようです。
カーネルにおけるタイマー事情 ~第3回 IAマシンのもつ各種計時ハードウェア~
準仮想化クロックを使っているときは、ホストOSにNTPを動かしていることが大切ですね。ゲストOSで動かしたほうがいいかどうかはディストリビューションによって意見が分かれるようです。
KVMにおいて ホストとゲストの時間管理はNTPを用いるべきか?
ゲストOSでNTPを使わなかった場合、ハードウェアの時計であるkvm-clockをゲストOSのシステムクロックに反映させる必要があります。UbuntuやSUSEにそういう仕組みがありましたっけ。確認が必要です。
以上簡単ですが準仮想化クロックについて調べてみました。
追記
ハードウェアクロックというとRTC(Real Time Clock)のことを指すそうです。kvm-clockは、ハードウェアの時計と書き直しました。
追記(2015/08/09)
KVMがメモリに値を書き込む頻度についてLinuxのカーネルソースを調べたところ、そう頻繁には値が更新されていないことがわかりました。
今まで、VirtualBoxでUbuntu 12.04を動かしていました。そのUbuntuの上でKVMを動かしてみたくなって試したら、あっさり動きました。
まず、ハードディスクイメージのvdiのファイルをvmdkに変換しました。
VBoxManage clonehd --format VMDK "Ubuntu 12.04 x64.vdi" "Ubuntu 12.04 x64.vmdk"
その後で、VMware Playerで新しく仮想マシンを作りました。このとき、ハードディスクは分割しないようにしました。そして、できたハードディスクのイメージ.vmdkファイルをUbuntu 12.04 x64.vmdkにとりかえたらうまく起動しました。
VMware Player上でVT-xを有効にするのはCPUの設定をするだけでした。 ”仮想マシン設定の編集”で”プロセッサ”を選び、”Intel VT-x/EPTまたはAMD-V/RVIを仮想化”をチェックしました。
その後で、ゲストOS上でKVMのドライバーをロードしました。
$ sudo modprobe kvm_intel
そして、QEMUを動かしたらちゃんとkvmが有効になっていました。カーネルモジュールの自動起動をどうしたらいいかいまいちわからないけど。
これで、OpenStackとかCloudStackとかOpenFlowとかいろいろ試せます。
参考にしたサイトは、
VirtualBoxのvdiからVMware Playerのvmdkへ変換
VMwarePlayer+Ubuntu/KVM/OVSでOpenFlowテスト環境をつくる(1)
です。ありがとうございます。