Bash on Windows のアーキテクチャーを妄想してみる

Facebooktwittermail

Bash on Windows が発表されました

いま開催中のイベント Build 2016 の初日に、Windows で Bash を実行できるようにするよ、という発表がありました。この記事を書いている時点では、以下のような公開情報がありますね。

Run Bash on Ubuntu on Windows
https://blogs.windows.com/buildingapps/2016/03/30/run-bash-on-ubuntu-on-windows/

Running Bash on Ubuntu on Windows!
https://channel9.msdn.com/Events/Build/2016/P488

Linux Command Line on Windows
https://channel9.msdn.com/Events/Build/2016/C906

Developers can run Bash Shell and user-mode Ubuntu Linux binaries on Windows 10
http://www.hanselman.com/blog/DevelopersCanRunBashShellAndUsermodeUbuntuLinuxBinariesOnWindows10.aspx

何やら、「Linux のための Windows サブシステム」 というものを Windows に実装するそうです。

Windows サブシステムって何ですかね?

とりあえず、Windows のアーキテクチャーをデバッグしてみましょう。デバッグすれば全てが分かります。デバッグ最高。

コマンド プロンプトから tree コマンドを実行した時の様子をデバッグしてみました。tree は、フォルダーの中身をツリー構造で表示してくれるコマンドです。こんな感じ。

201603311

tree を実行した時のコマンド プロンプトの様子をデバッグしてみます。すると、以下のように、コマンド プロンプトの中では CreateProcessW という API を呼んでいることが分かります。この CreateProcessW に与えられている引数を見ると、”C:\Windows\System32\tree.com” というファイルのパスが与えられています。つまり、tree コマンドを実行すると、”C:\Windows\System32\tree.com” というファイルから tree のプロセスが作られるということですね。

201603312

ここで Csrss というプロセスをデバッグしてみます。この Csrss というのが Windows サブシステムです。ここ重要です。

コマンド プロンプトから tree プロセス起動する時に、Windows サブシステムの Csrss とどんなやり取りをしているのか見てみましょう。

Csrss をデバッグしてみると、コマンド プロンプトから CreateProcessW を呼び出した直後に、Csrss の中では BaseSrvCreateProcess という API を呼び出しています。どうやら、Csrss の中でもプロセスを作る処理をしているようです。

201603313

Csrss が BaseSrvCreateProcess の処理を行っている間、コマンド プロンプトは何をしているのか見てみましょう。時系列で言うと、以下の 3 番目の状態をデバッグしてみます。

1. コマンド プロンプトで tree を実行すると、CreateProcessW を呼んでプロセスを作ろうとしている
2. コマンド プロンプトが CreateProcessW を呼び出した直後、Csrss の中でも BaseSrvCreateProcess という API を呼んでプロセスを作る処理をしようとしている
3. Csrss がプロセスを作る処理をしている間、コマンド プロンプトは何をしているんだろうか?

最初のコマンド プロンプトの状態と比べると、CreateProcessW の上の方に、何やらいろんな API が増えていますね。これの見方ですが、CreateProcessW の中で CreateProcessInternalW を呼び、さらに CreateProcessInternalW の中では CsrClientCallServer を呼び、さらに CsrClientCallServer の中では・・・ という見方をします。一言でいうと「スタック」です。このスタックの中を見てみると、CreateProcessW の処理が進み、CsrClientCallServer の先で NtRequestWaitReplyPort というような API が呼ばれています。API に Csr なんて名前が付いていて、さらに WaitReplyPort という API を呼んでいるので、どうやらコマンド プロンプトは Csrss が実行している BaseSrvCreateProcess の処理が終わるのを待っているようです。

201603314

というわけで、コマンド プロンプトは、Csrss に助けてもらわないと tree のプロセスを作ることができないことが分かりました。

コマンド プロンプトから tree コマンドを実行すると、コマンド プロンプトだけで tree プロセスを作れるわけではなく、Windows サブシステムがプロセスやスレッドを作成する処理を手伝います。この例ではコマンド プロンプトからプロセスを作成する仕組みをデバッグしましたが、他のケースでも同様です。スタート メニューから Excel を立ち上げる、エクスプローラーからテキスト ファイルを開く等の Windows の基本動作を行うと必ずプロセスが作成されますが、これらの処理は Windows サブシステムの助けがなくては成り立ちません。Windows サブシステムはプロセスの作成以外にも様々な処理で Windows アプリケーションを支えています。Windows サブシステムがないとアプリケーションが動作できないくらい、Windows アーキテクチャーにとっては超超超重要なプロセスが Csrss です。

もうちょっとつっこんで Windows サブシステムの仕組みを知りたい方は、Azure CTO Mark Russinovich の著書、Windows Internals Part 1 を読んでみてください。Windows サブシステムがどれほど重要なものなのか、詳細に詳細に詳細に説明されています。

また、一部の章はインターネットで公開されています。今回デバッグした Windows の中でプロセスが作成されるフローについては、以下で説明されています。

Processes, Threads, and Jobs in the Windows Operating System
Flow of CreateProcess
https://www.microsoftpressstore.com/articles/article.aspx?p=2233328&seqNum=3

上のページの中に、以下のようなフロー図が掲載されています。今回デバッグしたのは、このフローのうちの Stage 5 の辺りになります。”Creating Process” のフローがコマンド プロンプトで実行されていて、”Windows subsystem” のフローが Csrss で実行されています。

201603315

Linux のための Windows サブシステム

Csrss は Windows サブシステムです。Windows のネイティブアプリケーションを動かすために必要なサブシステムです。

Build で発表された Bash の話は、Linux のバイナリーをネイティブで動かすためのサブシステムを新しく用意して Windows に組み込むよ、という話です。つまり、Csrss 相当の超重要なサブシステムを、Linux のために組み込むことをマイクロソフトはやります、ということです。

Bash を動かすだけであれば、Bash のコマンド類を Windows アプリケーションとして移植すれば済みます。今回の発表は、そういう話ではありません。Linux 用のサブシステムを組み込むよ、ということが大きなポイントです。Build で行われたデモを見ると、ssh/Emacs/vi/gcc でビルドしたバイナリー等、Linux のネイティブ バイナリーを Windows で実行しています。gcc でビルドしたバイナリーは、Windows のネイティブ アプリケーション形式ではなく、Linux のネイティブ アプリケーション形式としてビルドされます。「Windows で Bash を動くようにしたよ!」 というイメージが強いですが、正確には、「Bash をはじめとした Linux ネイティブのバイナリーを動かすためのサブシステムを組み込んだよ!」 になります。

この記事を書いている時点では僕も冒頭に紹介した情報しか持ち合わせていませんので、下に書いてることはハズれてるかもしれません。

様々な発表を見ていると、「Lxss」 という単語がちらほら見受けられます。Csrss は “Client/Server Runtime Subsystem” の略ですが、おそらく Lxss は “Linux Subsystem” の略でしょう。この Lxss が Csrss と同様に Windows のサブシステムとして組み込まれるということだと思います。

また、「Linux のネイティブ バイナリーをユーザー モードで動かせるようにするため、Linux のシステム コールをカーネル モードに実装したよ」 という言い方をしているセッションもありました。ユーザー モードとは、Windows で言えば dll とか exe で構成されるアプリケーションが動くメモリ空間、カーネル モードはカーネルや sys のようなドライバー系が動くメモリ空間になります。

Windows サブシステムの場合、win32k.sys という Win32 API の機能を提供するドライバーがカーネル モードで動いています。例えば、アプリケーションから描画をする際に、gdi32.dll で提供される API をアプリケーションから呼び出すことがあります。ですが、ユーザー モードから直接ビデオ デバイスを操作することは出来ません。ビデオ デバイスのドライバーはカーネル モードで動いているためです。そのため、gdi32.dll の API をユーザー モードから呼び出すと、gdi32.dll から win32k.sys に対して描画のリクエストが渡され、win32k.sys がビデオ デバイスとやり取りします。同じような仕組みで、Linux のユーザー モード側のバイナリーがカーネル モード側にアクセスするための Linux サブシステム用のドライバーを提供しますよ、という話だと思います。

まとめ

  • Bash が Windows で動くようになるよ、という発表がありました
  • Windows にはサブシステムというアーキテクチャーがあり、Csrss という Windows サブシステムが存在することで Windows のアプリケーションは動作できます
  • Linux のバイナリーをネイティブで動かすためのサブシステムを Windows に組み込むことにしました。ここが重要なポイントです。
  • このサブシステムのおかげで、Bash をはじめとした Linux のバイナリが Windows で動くようになります
  • Microsoft loves Linux。Linux のためなら Windows のアーキテクチャーに大幅な変更を加えるくらい Linux が好きです。

 

久しぶりに Windows をデバッグしたら Csrss の API 群が変わっていたので、少し悩みました。まいったまいった。

Facebooktwittermail