2016/12/01

Pico process

とある事情で少々調べたので、ここにメモしておく。

だいたいの元ネタは、以下のページになります。
https://blogs.msdn.microsoft.com/wsl/2016/05/23/pico-process-overview/


● PE と ELF

Windows は Portable Executable (PE)という実行ファイル形式をサポートしている。逆に言えば、PE形式のバイナリしか起動させることができない。NT系の Windows では、環境サブシステム(Environment Subsystem) という名前のパーソナリティを、NT Executive というマイクロカーネル上に複数実装できるようになっており、実際に OS/2, POSIX, Interix などのサブシステムが存在した。

しかし、実はこれらの、Win32とは異なるサブシステム上のバイナリも、実質的に PE形式のバイナリ(OS/2については変種だが)を実行するものであった。Service for UNIX の UNIXコマンドも PE形式だったわけだ。

一方、Linux は ELF形式のバイナリを実行するようになっている。

Windows Service for Linux (WSL) が Ubuntu のバイナリを無変更で実行していると言うことは、とりもなおさず ELF形式の、本来実行できないバイナリを実行していることになる。

これはどうやって実現しているのだろうか?

● pico process

WSL のキモが、この pico process を使った ELF バイナリの実行だ。
pico process というものは Microsoft Research で開発された Drawbridge より持ち込まれた、より軽量なプロセスを指す。下図の右側が通常の Windows でのアプリケーションプロセスだ。

NT Process, Minimal Process, Pico Proess
https://blogs.msdn.microsoft.com/wsl/2016/05/23/pico-process-overview/  より抜粋

Windows のカーネルは、アプリケーションの起動時、アプリケーションが必要とするメモリを確保した後、アプリケーションの実行に必要なモジュールをそのメモリ内にロードする。全てのプロセスで共通の NTDLL.DLL や、アプリケーション間で共有されるユーザの情報、スレッドが実行するのに必要な情報(TEB)等々だ。もちろん、アプリケーションのバイナリやDLLもこのプロセス内に展開される。アプリケーションが実行をはじめた時には、アプリケーションが必要な情報はメモリに全部用意された状態になっている。

これは便利だが、言い換えれば、Windows のお仕着せの方式でメモリが利用されてしまっている。何らかの理由でメモリ上の位置を最初から変えておきたい場合、これは不都合が多い。

Windows10 では、Minimal Process と Pico process というこれまでとは違うメモリの使い方をしたプロセスを用意した。


Minimal Process はメモリ内のお仕着せの準備を一切やめた。プロセスが用意されたときはメモリはすっからかんで用意される。スレッドも用意されないので、このプロセスはほっとくとメモリを食ってるだけでCPUがスケジュールされず、つまり実行されない。それ以前に実行すべきプログラムもメモリ上に展開されていないのでCPUが割り当てられても実行することができない。
ただ、環境サブシステムなど特権的なプロセスはこのメモリ内に色々書き込むことができる。適切なコードを割り当て、スレッドを作れば実行もできるようになる。通常の Windows のアプリケーションとは別種のものを実行しつつ、最低限の管理は NT カーネル 側で実施されるわけだ。
Windows10 では、メモリ圧縮や「Device Guard」「Credential Guard」といった仮想化を利用したセキュリティ機能で、この Minimal Process が利用されている。


Minimal Process はメモリはすっからかんでも、特定の手順(システムコール)でOSを呼び出させば、他のアプリケーションと同じく Windows のOSの機能を呼び出すことができた。
Windows も Linux も、最近の x86-64 (x64) の OS では、CPU のもつ sysenter もしくは syscall という命令を使用する。呼び出したいシステムコールの番号をCPUのレジスタに記録、指定したあと sysenter 命令を実行する。sysenter を実行した瞬間にアプリケーションは停止、各OSのカーネル側に処理がうつり、CPUのレジスタの中を見てアプリケーションがどのシステムコールを呼ぼうとしたか、どういうデータをわたそうとしたかを確認、処理を行う。ただ、どのシステムコール番号がどの処理に対応しているかとか、そもそも処理の有無や機能がOSごとに異なっているわけだ。


通常のプロセスでも Minimal Process でも、sysenter 命令が実行された場合の処理は同じ NTカーネルが担う。

一方、Pico Process では、Pico Provider と呼ばれるカーネルのモジュールが sysenter 命令の処理を行う。Windows に代わってお仕置き...ではなく対応を行うわけだ。WSL の場合、カーネルに組み込まれる LxCore.DLL, LXSS.DLL のどちらか(多分 LxCore)が Pico Provider になっている。ここには Linux のシステムコールが「そのまんま」実装されている。つまり Windows のシステムコールではなく、Linux のシステムコールの番号として判断され、適切な処理が呼び出される。


● bash 起動から、Linux コマンドの実行まで

bash.exe をアイコンから実行すると、これはただのコンソールアプリケーションのため、コマンドプロンプトウィンドウが開き、その中でコマンドが実行される。LxCore, LXSS が必要に応じてNTカーネルに読み込まれ、LxssManager が起動される。

(多分)LxCore が pico process を生成し、(多分)LXSS とサービスとして起動される LxssManager が協力してファイルシステムにある ELF形式の init や bash のバイナリを読み込み、すっからかんのプロセスメモリ内に「さもLinux のように」展開を行い、スレッドを作ってCPUを割り当てる。通常の命令はCPUによりそのまま実行される。


ファイルを読み込んだりテキストを出力を試みると、これはOSの力を借りることになるのでシステムコールが呼ばれる。このとき、sysenter の結果は NTカーネルではなく(多分)LxCore が引き取り、Linux のシステムコールとして処理がなされる。簡単なものなら (多分)LxCore に記述された Linux と同じ処理が行われて、結果が pico process に返される。コンソールへの出力の場合は、LXSS 経由で LxssManager が呼び出され、bash.exe にわたされ、コマンドプロンプトウィンドウに出力がなされる。


Linuxのバイナリは何一つ書き換えられることなく、Linux カーネルの上で実行されていたのと同じように、Windows10 のNTカーネル+LxCore+LXSS の上で実行、できてしまうわけだ。


2016/05/15

Fusion 上の仮想マシンのネットワークとパケットキャプチャ


Fusion 上の仮想マシンは、通常、以下3種類のいずれかで外部ネットワークと接続している

NAT :  仮想的なスイッチに接続、外部とはアドレス変換(NAT)を通して接続
ブリッジ: 指定のNICをホストOS(Mac)と共有、NICを通じて直接接続
ホストオンリー: ホストOS(Mac)とだけ接続

デフォルトでは、NATのネットワークは VNET8, ホストオンリーは VNET1 と定義されており、OS X 側の vmnet8 というインターフェイスが VNET8 に接続、vmnet1 が VNET1 に接続されている。

VNET8 については、さらに vmnet-dhcpd と vmnet-natd が起動しており、それぞれ DHCPによるIPアドレスの割り当てと、NATの制御を行っている。

VNET1 は vmnet-dhcpd のみが起動しており NATが行われない。このためホストOS(Mac)としかつながらない。

Fusion の仮想ネットワーク構成
なお、Fusion Professional ではVNET1.8以外のネットワークをカスタム構成できる
さて、仮想マシンがどういったパケットを送出しているかを確認したいときがある。
OS X にはデフォルトで tcpdump が用意されているので、ターミナルから以下のように実行することでパケットのキャプチャを行うことができる
sudo tcpdump -i vmnet8 port 80
なお、Fusion 8.0.1 まででは上記のコマンドではパケットキャプチャができず、以下のように pktap デバイスを併用する必要があった。(こちらのブログに詳しい説明あり)
sudo tcpdump -k pktap,vmnet8 port 80
これは VMware の提供する vmnet デバイスが tcpdump などパケットキャプチャツールが使用する Berkley Packet Filter (BPF) をサポートしていなかったためだ。

この問題は Fusion 8.0.2 にて解消されている









2016/02/06

vExpert 2016 受賞しました

転職したのもあって今回ばっかりは無理かなぁと思ってましたが、なんとか今年も vExpert 2016 を受賞できました。ありがとうございます。

http://blogs.vmware.com/vmtn/2016/02/vexpert-2016-award-announcement.html

2016/01/31

Fusion でリカバリモードで起動する

VMware Fusion 上の OS X でリカバリモードでの再起動をおこなう場合だが、KBによると実機のOS Xと同じく、起動画面で Command-R を押すといいとある。

しかし、実際に試すとこれがなかなかうまくいかない。VMware の提供する EFIの BootManager が表示されてしまう。もっと確実に起動できる手順はないか、ということで行ったのが次の方法だ。

まず、EFI の BootManager を表示させる。これは、Command-R を押しながら起動して失敗した場合でもいいし、「ファームウェアをパワーオン」を使ってもいい。


ここで、「Enter Setup」を選択する


次の画面で「Configure Boot Option」を選択する


さらに、「Add Boot Option」を選択する

3番目に「Recovery HD」があるはずなので、これを選択する。
なお、OS X をインストールすると三つのパーティションが作られる。一つが EFI システム領域、一つが OS X そのもののインストール領域、そして最後が Recovery HD だ。

選択したパーティション内のファイル一覧が表示される。


ここでは、<com.apple.recovery.boot> フォルダを選択


し、その中の「boot.efi」を選択する。

boot.efi を使った起動の設定画面が表示されるので、「Input the description」を選択、この設定の名前を入力する。ここでは、「Recovery HD」とした。


その後、下の Commit changes and exit を選択し、元のメニューに戻る。


さらに、Exit the Boot Maintenance Manager を選択、元の起動メニューに戻る。


すると、先ほど作った「Recovery HD」がメニューに増えているのでこれを選択する。

後は起動を待つだけだ。
起動に時間がかかるので待つべし

起動してしまえば、実機のリカバリモードと同じように使用できる。


なお、二度目からはメニューに登録されているので、単に「ファームウェアをパワーオン」を選択、Recovery HD を選ぶだけになる。

【余談】メモリ解放とかメモリ最適化という迷信

OS X のメモリ管理は十分に近代化されており、アプリケーションに対して最適な配置をし、IOの性能確保にディスクキャッシュを行い、アクセス頻度の低い部分を圧縮したり、ディスクに書き出して解放したりを自動的に行う。

非常に端的に言って、性能を持つためにユーザが何か操作する必要はない。まったくない。ほっておけば自動的に行われる。

しかし、OS X のリリース以来、メモリの最適化とかメモリ解放などの操作や、それを行うアプリケーションが取りざたされてきた。定量的な性能評価もなくただ印象で「速くなった」って感想と共に、ね。

古典的なソフトのよくある挙動は、ただひたすらメモリを要求し、実際にアクセスして確保するというものだ。Mach はメモリの要求(vm_allocate)を行っても実際の物理メモリを確保しない。アクセスされるときにはじめて確保動作が行われる。なぜって?プログラマは往々にして多めに多めにメモリを要求するものだ。要求道理に渡しても使われないことは多々ある。だったら、使われる段に割り当てた方が遙かに効率的な訳だ。

メモリを要求し、ひたすら物理メモリを確保する、その後、終了すると大量のメモリが一気に空く。対価としてそのアプリを実行が終わるまでの待ち時間と、全てディスク上に追い出され読み込み直しになるバックグラウンドアプリの時間を引き替えに。そんな気休めアプリが有償であったりもしたのだ。

そして、今度は「システム標準のメモリ解放コマンドがある」という噴飯物の記事を見かけた訳だ。

https://book.mynavi.jp/macfan/detail_summary/id=37786 より

...この人、とりあえず man を見てないことだけは理解した。
以下が、purge の man の結果だ。

man purge

見ての通り、「ディスクキャッシュの解放」をするだけで、malloc や vm_allocate で確保されたアプリケーションのメモリには何ら影響がないことが明言されている。

てか、先の記事の解放内容も見ると分かる。この記事の書き手は、使用済みメモリが 14.90GB から 13.12GB に減ったことをわざわざ赤枠で示しているが、その横のアプリケーションメモリを見ると 10.92GB から 11.03GB に「増えて」いる。その代わりファイルキャッシュが 2.21GB から 268.9MB に減ってる。
2GB弱のディスクキャッシュを吹っ飛ばして、その分の空きが増えたことを喜んでるのだ。

先のページの結果を上下に並べて表示
purge コマンドの内容通りの結果になってるのが分かる。

そして、横にあるアプリケーションメモリとかファイルキャッシュの変動に気がついてもいない、訳だ。なお、アプリケーションメモリが増えればディスクキャッシュは自然と解放される、つまり必要のないことだ。

これでどのような効果があるか? せいぜい、再び同じIOがあったときにディスクを読み直すのでIO性能が劣化する、ぐらいだろうね。

Mac 系の記事には、このような無知と願望にもとづいたものが非常に多い。

ライターの頭は治しようがないので、せめて読み手がよく気をつけることだ。


2015/12/23

【Windows10】コンパクトOSでディスク占有量を削減する

Macbook Air(2012)を業務に利用しているが、3年前の機種とはいえ性能的には全く不満はない。ただ、ディスク容量が 256GB しかないため、仮想マシンを沢山作っていると容量が足りないことがままある。そこで、普段使いの Windows10 についてはできるだけ小さくインストールすることを試してみた。


● コンパクトOSとは

Windows10 では、コンパクトOSという、主に小容量ディスクのマシン向けにシステムを圧縮して占有ディスク領域を削減する機能が用意されている。詳細は、以下ページを参照して欲しい。
https://blogs.windows.com/japan/2015/04/01/how-windows-10-achieves-its-compact-footprint/
https://msdn.microsoft.com/ja-jp/library/windows/hardware/dn940129(v=vs.85).aspx


以前の Windows8.1 では、同様の目的で wimboot という機能が存在した。wimboot はシステムファイルそのものをインストールせず、リカバリパーティションにある再インストール用イメージの中のファイルをつど参照することで、システムの占有するディスク容量を節約することができた。ディスク容量の少ないネットブックやタブレットなどで便利だっだのだが、一方で様々な問題が存在した。

たとえば、WindowsUpdate で更新されたシステムファイルは通常のディスク領域に配置され、かつ圧縮されていないため、時間がたつにつれシステム領域が肥大していくという難点があった。wimboot が参照しているイメージファイルを書き換えられないことからくる制限だ。
wimboot は SSD/eMMC といったソリッドステートドライブ向けで、HDD はサポートされない、ファームウェアに EFI が必須で PC-BIOS をサポートしないなどの制限もあった。


そこで、Windows10 では wimboot ではなく、コンパクトOSという方式に切り替えられた。

コンパクトOSでは、通常と同じくシステムファイルはインストールされる。ただし、ファイルごと個別に圧縮がかかった状態になっている。WindowsUpdate がかかったときにはシステムファイルが置き換えられるが、更新されたファイルも圧縮されているので容量の肥大は緩やかにとどまる。
また、wimboot とは逆に、リカバリパーティションにはファイルそのものが配置されず、通常のシステム側のファイルを参照するだけとなっている。

これにより、システムファイルを圧縮、リカバリ用イメージとの重複を避けることでディスク領域を削減するという wimboot と同じ特徴を持ちつつ、アップデートによる肥大を避けることができるようになった。

ハードウェアについても、SSD向けであるのは変わらないとしても、PC-BIOSでもブートできるよう制限が緩和された。


● 圧縮がかかってるかの確認

圧縮がかかっているかは、compact コマンドで確認ができる。

管理者権限のコマンドプロンプトで compact コマンドを実行、引数に /compactos を
指定、query オプションをつけることで現在の状態を確認できる

wimboot も windows10 のシステムの圧縮もどちらかというとタブレットなど、ディスク容量に制約がある機器向けのもので、通常のパソコンに Windows10 をインストールするとシステムの圧縮はまず使用されていないはずだ。

圧縮してインストールするかどうかは、システムをインストールするディスクの容量と、そのマシンのCPUスペックを見るとある。システムを圧縮、展開するのはそれなりに負荷がかかるため、非力なマシンの場合はかからないことがあるようだ。

物理ハードウェアにインストールする際は、それでもいいだろう。

タダ今回は仮想マシンにインストール、さらにクローンでディスク容量を取らないようにするのが目的だ。CPUパワーは充分にあるし、ディスク容量はクローンするごとに差がでてくるので、強制的に圧縮したインストールを行った。



● 仮想マシン上での、システムの圧縮されたままでインストールする

まずは VMware Fusion などで普通にWindows10 のインストールメディアから仮想マシンを作成する。
通常のインストール手順を使わないので簡単インストールはオフにしておくこと。

仮想マシンを起動したら、最初の画面でSHIFT-F10 を押し、コマンドプロンプトウィンドウを呼び出す。

コマンドが使えるようになったら、以下コマンドを実行してインストール先となるパーティションを作成、NTFSでフォーマットしておく。

X:\Sources> diskpart
DISKPART> elect disk 0
DISKPART> create partition primary
DISKPART> select partition 1
DISKPART> format quick
DISKPART> assign letter=e
DISKPART> active
DISKPART> list partition
DISKPART> list volume
DISKPART> exit

diskpart コマンドを起動、最初のディスク(disk 0)を選択、プライマリパーティションを一つ作成する。容量を指定していないのでディスクの空き全てをそのパーティションに、つまり全領域で一つのパーティションが作成される。

select partiton 1 で作成したその最初のパーティションを指定する。なお、ディスクやボリュームと異なり、パーティションだけは1から始まるので要注意。

format quick でNTFSでのNTFSでのクイックフォーマットをかけ、仮に Eドライブと設定しておく。アクティブパーティションとサインすることで、終了だ。

list partiton ではパーティション一覧が、list volume では Windowsが最終的に認識するドライブ一覧が表示される。パーティションの先頭に * があってアクティブである事、ボリューム一覧で DドライブとEドライブがあることを確認しておこう。また、Dドライブはブートした isoイメージだ。



確認が終わったら終了をし、こんどは OS イメージの書き込みを行う。
X:\Sources> dism /apply-image:D:\source\install.wim /index:1 /applydir:e:\ /compact
最後にある /compact がシステム圧縮されたままのインストールの指定となる。忘れないように。

dismコマンドの実行が終わりイメージのインストールが終わったら、以下コマンドでブートローダをセットアップする
X:\Sources> bcdboot e:\Windows /s e:
セットアップが終わったら、一旦シャットダウンする。
再起動でなくてリブートでもいいと思うだろうが、再起動だとなぜか直後のブートに失敗することが頻発した。リセットして起動しなおすと次のフェイズに行くのだが、ここは確実性を取っていったん仮想マシンごと停止している。
X:\Sources> wpeutil shutdown
再び仮想マシンを起動し直すと、初期設定のフェイズに入る。

初期アカウントの作成、WindowsUpdate の適用などが行われ、デスクトップが表示されてインストールが完了する。

この時点での VMDK サイズだが、おおむね 5〜6 GB 程度になる。通常のインストールだと 9GB 程度になるので、3-4GB 程度、4割ぐらいが削減できたようだ。


● すでにインストールされた Windows10 をコンパクトOSにする

通常のインストールを行ったWindows10でも、後からコンパクトOSに圧縮し直すことは可能だ。管理者権限で compact /compactos:always を実行すると、システムファイルに圧縮がかけられ、コンパクトOS化される。

既存の Windows10 をコンパクトOSに変更。
それなりに時間がかかり、CPUやディスクに負荷がかかるので注意
この処理は CPUとディスクIOをかなり使う処理なので、時間と電源に余裕がある場合に実行すべきだ。上記手順でコンパクトOSとしてインストールしたものと、インストール後にコンパクトOS化では容量差はない模様。CPUと時間を節約したければ最初からコンパクトOSを、手軽さを考えるなら通常インストールからの compact /compactos:always を入れる、という感じだろう。

2015/12/15

【Fusion】ファームウェアをパワーオン

12/8 に VMware Fusion 8.1 がリリースされた。

メンテナンスリリースという事になっているが、アップデートしてからふとメニューを見ると、「仮想マシン」のメニューに「ファームウェアをパワーオン」という謎の項目が増えている。

Fusion 8.1 で増えたメニュー項目

Fusion 8.0 では存在しなかった

これは何かというと、仮想マシンを起動したときにファームウェアの設定画面に強制的に切り替えるものだ。

ファームウェアをパワーオンで起動すると、この画面が確実に表示される
仮想マシンの電源を入れてから F2 キーを押すとこの画面に入るが、タイミングがシビアであり、特に Fn キーを押しながら出ないとファンクションキーが操作できない Mac では慣れないと辛いものであった。(システム環境設定からFnキーを押さなくてもファンクションキーとして扱うよう設定は変更できるが、Fusion のためだけに変えるというのもどうかと思われる。)

.vmx ファイルに 「bios.forceSetupOnce = "TRUE"」を記入すれば同じく次回の起動に限りファームウェア設定に強制的に切り替える事ができたし、vSphereClient のGUIにはそのための設定項目が存在したが、Fusion では後で述べる 少々わかりにくい方法しかなかった。

今回の方法は分かりやすくかつ簡単であり、便利であるといえよう。


● 【参考】これまでの方法

起動ディスクにて Option キーを押すことで、「再起動」のボタンが「ファームウェアを再起動」に切り替わった。これは Fusion 8.0 でもそれ以前でも利用できる。


Option キーを押すと、「ファームウェアを再起動」のボタンに切り替わる