今回は、前回紹介したWin32相互運用性を実現している仕組みを見ていくことにしよう。驚いたことにこの機能は、Windows Subsystem for Linux(WSL)内にインストールされたLinuxをほとんど変更することなく実現されている。
本文に入る前に1つ訂正させていただきたい。前回の最後でbash.exeがパイプを受け付けないとしていたが、現在のインサイダープレビューでは、正しく動作する。つまり、
dir | bash -c "grep test"
として、Windows側のdirコマンドの出力をLinuxのgrepで処理することができる。
Win32アプリかLinuxの実行ファイルかを区別する仕組み
まず、bash側でWin32アプリを判別して起動する仕組みが必要になる。しかし、WSL内では標準のUbuntuがそのまま動作しており、/bin/bashを改良するわけにはいかない。そのため、Linuxカーネル標準の機能である「binfmt_misc」を使う。
これは、ファイルのフォーマットなどからデータファイルを識別して、これを解釈実行する「インタプリタ」を起動する仕組みだ。Windowsでいえば、拡張子と実行ファイルの「関連付け」に似た機能だ。
このbinfmt_miscでは、ファイルの先頭に埋め込まれた「マジックナンバー」を識別に使うことができる。EXE形式のプログラムは先頭に必ず「MZ」という文字列がマジックナンバーとして埋め込まれている。これは、MS-DOSの頃からの定義で、当時は、同じ実行ファイル形式だったCOM形式との区別に使われていた。
その後、Windowsが登場してWindowsアプリケーションとなったが、構造的には、EXE形式のプログラムが先頭部分にあり、これを実行したあと、Windowsかどうかを判断して、その後に置かれたWin32バイナリを起動する仕組みになっていた。
このような構造になっているのは、DOSから起動したときに、Windows用であるというメッセージを表示させて停止するためだ。しかし、ウィルスの侵入路に使われてからは、先頭にあるDOSのEXE実行形式部分はまったく使われなくなった。しかし、形式としてはそのまま残っているのだ。
Linuxの実行ファイル形式ELFは、Windowsのものは違うため、マジックナンバーもまた違ったものになっている。このため、標準のLinux実行ファイルとWindowsの実行ファイルは確実に区別ができる。
WSLでは、Linuxカーネルは存在せず、WSL側にあるLXSS.SYSとLXCORE.SYSがカーネルAPIコールを受け付け、WindowsカーネルAPIに変換している。このため、WSLの実装自体がカーネルの機能に相当する。実は、RS1の実装ではこのbinfmt_miscは実装されていなかった。
この機能があるかどうかは、Linuxの/procファイルシステムで判定できる。RS1には「/proc/sys/」以下にfsディレクトリが存在していないが、RS2のWSLでは、「/proc/sys/fs/binfmt_misc」ディレクトリが存在する。
つまり、この機能は、RS1では未実装だったが、RS2では実装されているというわけだ。
RS2のbinfmt_miscには、標準で以下の画面のようなEXEファイル実行用の登録がある。
interpreter /init
flags:
offset 0
magic 4d5a
これは、対象ファイルを判定するのにファイル先頭のマジックナンバーを使い、そのパターンは16進数で「4d5a」、ASCIIコードの「MZ」で、ファイルを解釈するインタプリタは「/init」だという定義だ。
WSL内の/initは、通常のLinuxプログラムではなく、WSL専用の改良されたプログラムになっている。Linuxでは/initは、システムの起動時に最初に動作するプログラムで、環境整備などを行ったのち、bashを起動している。
このプログラムは、WSLではWindows側と通信する唯一のプログラムであり、唯一の標準でないLinuxのプログラムだ。WSLの/initは、通常、LxCore.sys、LxSS.sysと通信し、Windows側のLXSSマネージャーサービスを経由してbash.exeに結果を返す。
RS2では、WSL内で/bin/bashがEXEプログラムを起動すると、通常のコマンドライン処理(シェル変数の処理など)が行なわれたのち、LinuxのカーネルAPIコールが呼び出される。ここでbinfmt_miscが適用され、インタプリタとして/initが起動する。/initではファイルパスや標準入出力の変換が行われ、LxBUS経由でBash.exeにプロセス起動のメッセージが送られる。
なお、binfmt_miscの登録にあるように、WSL内のLinuxでは/init自体がインタプリタとして動作し、コマンドラインで引数としてEXEファイルを指定すれば、Win32実行ファイルを起動できる。ただし、このときの起動パスは、DrvFSで正しく指定する必要がある。
また、/initは、WSLが起動されたときのWindows側のPath環境変数をWSL側の/bin/bashへ持ち込む。もちろん、DrvFSに変換したうえでだ。これにより、WSL内ではWindows側のPathが初期値となり、これにWSL側のPATHが追加される。
/initは、引数で指定されたWin32バイナリをLxCore、LxSSに依頼して起動させる。
こうした通信には、LxBusと呼ばれるメカニズムが使われている。これはWSL側では、/dev/lxssと/dev/lxssclientというデバイスで、/initがこれを占有して使っている。
LxBusは、WindowsとWSLの間の複数の並行した通信を可能にする。1つは、bash.exeの背後にある「LXSS Manager Service」と/init間でメッセージ(たとえば、WSLの起動やWSL側からのWin32バイナリの起動依頼などのメッセージ)のやりとりを行なう。
もう1つは、Windows側とWSL側の標準入出力を接続するための通信路として使われる。このときには、Windows側ではファイルハンドル、Linux側はファイルディスクリプタとして動作する。つまり、どちら側も、ファイルのように扱えるオブジェクトとして振る舞うことができる。なお、これはLxBus MessagePortと呼ばれている。それぞれの環境での汎用ファイルオブジェクトなので、標準入出力に割り当てることができる。これにより、Win32相互運用性では、Windows側とWSL側の標準入出力を接続することが可能になる。
この連載の記事
-
第457回
PC
IPv6アドレスは先頭を見ればどんな種類かわかる -
第456回
PC
あらためてIPv6基本のキ -
第455回
PC
Windowsで現在どのネットワークアダプタがインターネット接続に使われているかを調べる方法 -
第454回
PC
Windows 11 24H2では「デバイスの暗号化」の条件が変わり、より多くのPCでドライブが暗号化される -
第453回
PC
Windows 11 24H2の配布開始後もすぐにはやってこない Windows UpdateとSafeguard Holds -
第452回
PC
Windows 11 Ver.24H2が登場 Copilot+ PCとそうでないPCで実質Windowsが2つに分かれる -
第451回
PC
新しいWindowsサンドボックスではコマンドラインからの制御が可能に -
第450回
PC
ユニコードで文字数を数える方法 -
第449回
PC
WSLはプレビュー版でGUIでの設定が加わった! リリース2.3.xの新機能を見る -
第448回
PC
PowerShellで面倒なオブジェクトはPSCustomObjectに変換するのが早道 -
第447回
PC
この秋登場のWindows 11の新バージョン、Ver.24H2の状況を見る - この連載の一覧へ