WSLの仕組みとWSLを構成するコンポーネント
WSLの仕組みとWSLを構成するコンポーネントの紹介です。サブシステムの歴史
Microsoft Windows NTは、カーネルの実装に縛られることなくアプリケーションへプログラムインタフェースを提供するため、Win32のようなサブシステム環境を実装できるように設計されていました。この設計のおかげで、初リリース時からNTカーネルはPOSIXやOS/2、Win32サブシステムをサポートしていました。
初期のサブシステム
初期のサブシステムはユーザーモードモジュールとして実装されており、アプリケーションがユーザーモードモジュールが提供するAPIを呼び出すと、ユーザーモードモジュールはその呼び出しを適切なNTカーネルのシステムコールへと変換し、機能を実現していました。かつて全てのアプリケーションはPE/COFFフォーマットの実行可能なファイルであり、サブシステムAPIを実装するためのライブラリー群やサービス群、及びNTカーネルのシステムコールを実行するNTDLLから成り立っていました。
ユーザーモードのアプリケーションが起動すると、そのアプリーケーションの実行ヘッダーに記述されている依存情報を基に、アプリーケーションが必要とする適切なサブシステムがロードされます。
最近のサブシステムとPOSIXレイヤーの置き換え
最近のサブシステムでは、POSIXレイヤーをSUA(Unix-based Applications)サブシステムで置き換えました。SUAのユーザーモードコンポーネントは、以下の機能を満たすように構成されていました。
- プロセスとシグナルの管理
- ターミナルの管理
- システムサービスの要求及びプロセス間通信
SUAの主な役割は、大きな変更作業なしにアプリケーションをWindowsへ移植できるよう移植作業を促すことでした。
SUAはNTの構造を利用し、POSIXのユーザーモードAPIを実装することで実現しました。
SUAのコンポーネントはユーザーモードで構築されたため、forkのようなカーネルモードのシステムコールと同等の性能や意味を持たせることは困難でした。
このモデルは、継続的に行われる移植作業に焦点を当て、プログラムを再コンパイルする必要性とその作業のメンテナンスコストに基づくモデルだったため、上記のような実装が行われました。
サブシステムの引退と新しいサブシステム
長い時を経て、これらのサブシステムは削除されました。しかしWindows NTカーネルは新しいサブシステムを実装できる幅のあるアーキテクチャーだったため、Windows Subsystem for Linuxを開発することが可能でした。
Windows Subsystem for Linuxの登場
新しいサブシステムとしてWindows Subsystem for Linux(WSL)が登場しました。WSLは、WindowsでELF64バイナリーの実行を可能にするコンポーネントの集まりです。
WSLには、ユーザーモードのコンポーネントとカーネルモードのコンポーネントが含まれています。
これらのコンポーネントは、主に以下のコンポーネントで構成されます。
1.LXSS管理サービス
LXSS管理サービスは、Linuxインスタンスのライフサイクルを操作・管理するコンポーネントです。LXSS管理サービスは、ユーザーモードのコンポーネントです。
2.Picoプロバイダードライバー(Pico provider driver)
Picoプロバイダードライバーは、Linuxのシステムコールを変換することでLinuxカーネルをエミュレートするドライバーです。以下のドライバーがPicoプロバイダードライバーです。
- lxss.sys
- lxcore.sys
Picoプロバイダードライバーは、カーネルモードのコンポーネントです。
3.Picoプロセス(Pico process)
「bash」などユーザーモードのLinuxプログラムをホストするプロセスです。LinuxプログラムはこのPicoプロセス内でバイナリーを変更すること無くそのまま動作します。
1つのLinuxプロセスに1つのPicoプロセスが生成されます。
Picoプロセスは、ユーザーモードのコンポーネントです。
コンポーネントの関係
PicoプロセスにLinuxバイナリーを配置することで、LinuxのシステムコールをWindowsカーネルへ渡せるようになります。Picoプロバイダードライバーは、LinuxのシステムコールをWindowsカーネルのAPIに変換したり、Linuxカーネルをエミュレートします。
LXSS管理サービス(LXSS Manager Service)
LXSS管理サービスは、Picoプロバイダードライバーを仲介するサービスです。LXSS管理サービスの役割
LXSS管理サービスは、「Bash.exe」がLinuxのバイナリー(/bin/bash)を起動する方法を提供します。またLXSS管理サービスは、「Ubuntu on Windows」のインストールやアンインストール時に同期を取ったり、1つのプロセスのみ一度に操作を可能にし、操作を保留している間、他のLinuxバイナリーの起動を阻止することにも使用されます。
LinuxのプロセスとLinuxインスタンス
ユーザーによって起動されたすべてのLinuxのプロセスは、Linuxインスタンスの中に配置されます。Linuxインスタンスはデータ構造であり、すべてのLXプロセスやスレッド、実行状態を追跡し続けるための情報が含まれています。
Linuxインスタンスの生成
NTプロセスが初めてLinuxバイナリーの起動を要求した時に、Linuxインスタンスが生成されます。Linuxインスタンスの破棄
最後のNTクライアントが終了した時に、Linuxインスタンスは破棄されます。このクライアントには、Linuxインスタンス内で起動したすべてのプロセスも含みます。
例えばgit credential cacheのようなデーモン(常駐するプロセス)も含みます。
Picoプロセス(Process)
「Drawbridge」プロジェクトの一環として、WindowsカーネルはPicoプロセスとPicoドライバーという構想を導入しました。OSのプロセスの一種
PicoプロセスはOSのプロセスの一種です。ただし、 OSのサービスに関連付けられたWin32サブシステムのPEB(Process Environment Block)のような仕掛けはありません。
加えてシステムコールとユーザーモードで発生した例外は、Picoプロセスに結び付けられたドライバーに送られます。
PicoプロセスとPicoドライバー
PicoプロセスとPicoドライバーは、WSLの基盤を提供します。PicoプロセスとPicoドライバーによりサブシステムは、Linuxの実行可能なバイナリーをプロセスのアドレス空間に読み込み、LinuxカーネルをエミュレートすることでLinuxのコードをそのまま実行できるようになります。
システムコール(System Calls)
WSLは、Windows NTカーネルの上に仮想化したLinuxカーネルのインターフェースを配置することで、ELF64バイナリーをそのまま実行します。Linuxのプログラムに提供されるLinuxカーネルのインターフェースの1つが、システムコール(syscall)です。
システムコールはカーネルによって提供されるサービスであり、ユーザーモードのプログラムから呼び出すことができます。
LinuxカーネルとWindows NTカーネルのシステムコール
LinuxカーネルとWindows NTカーネルは、数百ものシステムコールをユーザーモードのプログラムに提供しています。しかしお互いのカーネルが提供するシステムコールの意味は異なっていますし、直接的な互換性はありません。
例えば、Linuxカーネルのシステムコールには、fork、open、killといったシステムコールが含まれていますが、一方Windows NTカーネルのシステムコールではそれに相当するシステムコールは、NtCreateProcess、NtOpenFile、NtTerminateProcessです。
WSLのシステムコール
WSLはカーネルモードのドライバー(lxss.sysとlxcore.sys)を含んでいます。これらのドライバーは、Linuxのプログラムから呼ばれたLinuxのシステムコールに、Windows NTカーネルと協調して応答します。
これらのドライバーには、本物のLinuxカーネルのコードは含まれていません。
代わりに、Linuxカーネルと互換性のあるインターフェースをクリーンルームで実装しています。
Linuxでは、ユーザーモードのLinuxプログラムからLinuxのシステムコールが呼ばれた時、Linuxカーネルによってシステムコールが処理されます。
WSLでは、ユーザーモードのLinuxプログラムからLinuxのシステムコールが呼ばれた時、Windows NTカーネルはそのシステムコールを「lxcore.sys」に転送します。
「lxcore.sys」は可能であれば、LinuxのシステムコールをWindows NTカーネルのシステムコールヘ変換します。
forkシステムコール例
例えばLinuxのforkシステムコールは、Windowsのシステムコールに存在しません。WSLでは、forkが呼ばれた時に「lxcore.sys」がプロセスをコピーするための準備を行います。
そして「lxcore.sys」はLinuxのforkと同じ結果になるように、Windows NTカーネル内部のAPIを呼び出しプロセスを生成します。
その後生成されたプロセスに追加データをコピーして処理が完了します。
ファイルシステム
WSLでサポートするファイルシステムは、以下の2つのゴールに適合するように設計されました。- 完全に忠実なLinuxのファイルシステムをサポートする環境を提供する
- Windowsのドライブやファイルと相互運用できる
WSLは、本物のLinuxカーネルと同様に仮想ファイルシステムのサポートを提供します。
ユーザーシステム上のファイルのアクセスを提供するために、2種類のファイルシステムが使用されます。
2種類のファイルシステムとは、以下の2つです。
- VolFs
- DriveFs
VolFs
VolFsは、Linuxのファイルシステムが持つ機能を完全にサポートするファイルシステムです。Linuxのファイルシステムに含まれるディレクトリーやアプリケーションのファイル(/etc、/bin、/usrなど) 、ユーザーのホームフォルダーはすべてVolFsにより提供されます。
「Ubuntu on Windows」のためのファイルシステムです。
簡潔に言えば、「/mnt」以外のディレクトリーやファイルは、VolFsにより提供されます。
1.Linuxのパーミッション及びパーミッションの操作
VolFsは、Linuxのパーミッション及びパーミッションの操作を提供します。chmodやchrootにより、パーミッションの操作が可能です。
2.シンボリックリンク
VolFsは、シンボリックリンクをサポートします。3.ファイル名
Windowsのファイルシステムではファイル名に使用できない文字でも、ファイル名に使用できます。4.大文字・小文字の区別
ファイル名の大文字・小文字の区別を行います。DriveFs
DriveFsは、Windowsのファイルシステムと相互運用のために使用されるファイルシステムです。「bash」からWindowsのファイルシステムにアクセスする際、このDriveFsを経由してWindowsのファイルシステムにアクセスします。
簡潔に言えば、「/mnt」内にマウントされたWindowsのディレクトリーやファイルは、DriveFsにより提供されます。
Windowsが持つ性質に影響を受けるファイルシステム
このファイルシステムは、Windowsが持つ性質に影響を受けます。例えば、Windowsのファイルシステムでファイル名に使用できない文字をファイル名に使用することはできません。
またWindowsのセキュリティーによる保護が行われるため、Windowsのユーザーの権限を超えてファイルを操作することはできません。
VolFsと違いDriveFsは、Linuxのファイルシステムが持つ全ての機能をサポートしていません。
ファイル名の大文字・小文字を区別しない
ファイル名の大文字・小文字は区別しません。例えば、「myText.txt」ファイルがあるとします。
「myText.txt」ファイルを指定する際、大文字・小文字の区別は行われません。
従ってユーザーは、大文字・小文字が異なるだけのファイル名を持つファイルを、別々のファイルとして作成することはできません。
Windowsの固定ドライブは、/mnt以下にマウントされる
Windowsの固定ドライブ(HDDなど)は、DriveFsを使用して「/mnt」以下にマウントされます。ユーザーはすべてのWindowsのファイルにアクセスすることができます。
このおかげでユーザーは、「Visual Studio Code」のようなお気に入りのエディターを利用してファイルを編集しつつ、同時に、Bashからオープンソースのツールを使用してビルド等の操作ができます。