Windows 10上でELFバイナリを実行できる理由

Windows 10上のLinux環境「Bash on Ubuntu on Windows(BUW)」は、仮想マシンでもコンテナでもなく、「Windows Subsystem for Linux(WSL)」というサブシステムで稼働している。我々が普段使うアプリケーションは「Win32サブシステム」上で動作し、Windows 10の源流に当たるWindows NTでは、InterixやOS/2といったサブシステムを搭載して、各環境のソフトウェアを実行できた。これらサブシステムはWindowsのカーネルモード上に存在する。

WSLの大まかな概要(公式ブログより抜粋)

実際にBashなどのLinuxコマンドを実行するのは上図にあるとおり、ユーザーモードだ。LinuxではELFバイナリと呼ばれる実行形式のため、Windows 10では実行できない。そこで「LXSS(Session manager Service)」を経由し、カーネルモードにあるWSLが用意したシステムコールを介して動作させている。

上図をご覧になって首を傾(かし)げるのは「Pico」というキーワードではないだろうか。こちらに関してはこちらの公式ブログで説明しているとおり、ユーザーモード上で最小限のプロセスとして稼働し、Win32(NT)プロセスと違ってアドレス空間の設定をスキップしながらシステムコールを実現するというもの。

Linuxの各コマンドを実行する際の土台となるPicoプロセス(公式ブログより抜粋)

Picoプロセスの実装に伴い、MicrosoftはWindows 10のカーネルにも手を加えている。プロセスのコピーを生成するforkのサポート(Windowsでは通例的にforkに相当するシステムコールが存在しなかった)や、メモリー管理の単位として4KB(キロバイト)をサポートし、大文字と小文字の区別などが加わった。

もっとも、この辺りのロジックは理解しなくても一切の問題はない。Windows 10でBashを始めとするLinuxコマンド各種が動作するが、その互換性はLXSSと連動してシステムコールなどを受け付けるWSLの完成度向上が重要だ。本稿を執筆している時点では、どの程度向上するのか判断できないが、2016年6月上旬には、pty(Pseudo Terminal)およびtty(Physical Terminal)をサポートし、TmuxやGNU Screenの使用を可能にしている。8月2日のAnniversary Updateまで、さらなる完成度の向上を開発陣には期待したい。

Tmuxで端末を多重ウィンドウ化した状態。Build 2016で公開したBUWでは不可能だった

「apt-get update -y」を実行し、リポジトリの状態を最新にしてから、「apt-get install imagemagick」と入力して、パッケージをインストールする

シェルスクリプトでJPEG形式に変換する

それでは今回のシェルスクリプトを紹介しよう。前回はフォルダー内にあるJPEGファイルのファイル名をリネームするシェルスクリプトを紹介したが、JPEG形式以外のファイルがある場合には対応できない。そこでLinuxでは定番の画像形式変換コマンドである「convert」を使用する。

このコマンドは「ImageMagick」というパッケージに含まれているため、最初にインストールを実行しなければならない。ただし、執筆時点のBUWは開発途中版のためか、「apt-get」コマンドでアップデートを実行すると、整合性エラーが発生してしまう。エラーメッセージを見ると一部のシステムコールにアクセスできないようだ。また、別の環境ではライブラリがバージョンアップし、それに追従していないパッケージが存在する。もっとも、ImageMagick自体はインストールできるので安心してほしい。

それでは下記シェルスクリプトを前回と同様に「vim」など使って作成し、実行権限を与えておこう。

 #!/bin/bash

 Dir=/mnt/c/Users/kaz/Desktop/Work1
 Number=1

 cd $Dir

 for OldFile in $(ls *.jpg *.png *.tif *.bmp); do
    NewFile=$(printf %03d.jpg $Number)
    convert $OldFile $NewFile
    rm $OldFile
    echo "Convert and Rename $OldFile to $NewFile."
    Number=$((Number+1))
 done;

前回と異なるのは、最初に設定していた対象となるファイルタイプ変数として指定する行、「mv」コマンドを実行する行を削除している(理由は後述)。また、8行目にあるforループの内容を変更し、10~11行目に新たなコマンドを追加した。

まずfor文だが、今回はワイルドカードでJPEGファイルを指定するのではなく、対象となるディレクトリ内の画像ファイルを「ls」コマンドで取得し、変数OldFileに渡している。このようにfor文はコマンドの標準出力をそのまま利用することが可能だ。もっともこの方法はスマートではない(理由は後述)ため、今後の連載で改善したい。

10行目に加えた「convert $OldFile $NewFile」は前述したコマンドを使い、任意の画像ファイルをJPEG形式に変更している。本来は「${変数名##*/}」など実行してファイル名を取得し、「.jpg」をつけるべきだが、9行目のprintf文で出力形式をJPEG形式と決め打ちしているため、今回はシンプルにまとめてみた。11行目で変換元となる古いファイルを削除する「rm」コマンドを実行しているのは、前回「mv」コマンドでファイルを"移動"していたため、元のファイルが残らなかったからである。

シェルスクリプトを実行すると、for文で指定した拡張子を持つ画像ファイルがある場合は問題ないが、上図のようにビットマップ形式ファイル(bmp)が存在しない場合、エラーメッセージが現れてしまい、あまり美しくない。そのため、ここは他の方法を今後用いることになる。

スクリプトを実行した状態。ディレクトリ(フォルダー)内にビットマップファイルが存在しないため、エラーとなる

こちらは実行後のフォルダーをエクスプローラーが見た状態。「ls」コマンドで列挙した順番で連番のファイル名が付けられ、JPEG形式に変換される

阿久津良和(Cactus)