EDIT^7: blink と box86、FEX。
EDIT^6: Unikraft 。
EDIT^5: Tilck 。
EDIT^4: コメント。gVisor はすっかり忘れていました!Linuxを拡張するためにLinuxを実装した良い例だと思います。LINE有りましたね。。 SF.netのCVSはもう死んでしまったので除外にしました。。 OSvのバイナリ互換 はPIEであることが要求なので。。といっても世間的にはもうLinux = Debian/Ubuntu で良いですかね。。表現を調整しました。
EDIT^3: Noah忘れてた!
EDIT^2: Cygwinは 下書き段階で削ってしまった 。。 qemuを移植したとき に互換性がイマイチだったので。。特殊fdやprocfsの充実ぶりとかを考えると "かなりLinux" と言って良いとは思うけど、 mmap
等でLinuxとWindowsの挙動が衝突する場合はWindows側の挙動を採っているので何がなんでもLinuxを目指しているというわけでも無さそう。Win32との共存のために wchar_t
が16bitsでLLP64だし。。
EDIT: Qiitaの関連リンク機能が cyanurus を見つけてきたので追記
LinuxがAPIのLingua francaとなりつつある。Linux以外のPOSIX環境への投資を正当化するのは今後も難しくなりつづけるのではないだろうか。
もちろんエスペラント語のごとく何か中立的な環境を定義してそちらに寄せるというアイデアもある。CloudABI( https://cloudabi.org/ )が正にそれで、これのWebAssembly版としてWASI( https://wasi.dev/ )が出発していることを考えるとまぁ悪くないアイデアではあるのだけど、 https://github.com/WebAssembly/WASI/issues/294 で Ed Schouten (CloudABIの開発者でFreeBSD/LLVMのコントリビュータでもある) が書いているようにCloudABIはあまり関心を引けなかった。
WSL2でWindowsがLinuxカーネルをWindowsコンポーネントの一部として取り込んでしまったことからもわかるように、 LinuxはOSの多様性を駆逐しつつあり 、経済的な合理性からこの流れを変えるのは難しいだろう。逆に、今後もシステムソフトウェアやソフトウェアプラットフォームの研究を継続するならば、実用性の面からはLinuxを意識せざるを得ないのではないだろうか。
というわけで、LinuxカーネルじゃないLinux実装特集。
カーネル/マイクロカーネル/ライブラリOS
もっともよくあるパターンはカーネルにLinuxの各種syscallを実装してLinux向けのアプリケーションを実行しようというもの。要するにLinux互換OSを作ることになる。
互換のレベルは API互換 (ソースコードをリビルドすれば実行できる) と バイナリ互換 (リビルド不要でそのまま動作する) に大別できる。いわゆる互換OSとして通常イメージされるのはWindowsのWSL1のような バイナリ互換 戦略と言える。
API互換
既存のOSに後付けでLinux機能を実装するのではなく、Linux向けのアプリケーションを動作させることを目的としたLinux互換実装。通常の意味でのバイナリ互換を実現しているのはHermiTuxのみで、OSvは中間的な実装、managarmはAPI互換実装となっている。
managarm
managarm は、64bit PCプラットフォームで動作するマイクロカーネルで、Linuxを意識したsyscall実装を提供しアプリケーションの移植性に配慮している。
-
https://managarm.org/
- プロジェクト
-
https://github.com/managarm/managarm/blob/4c6cfc4406c16c82865a34a9bc90226f2a9a87a2/protocols/posix/posix.bragi#L20
- syscallプロトコルの宣言 -- POSIXとファイル名に入っているが、Timerfdのサポート等Linuxを強く意識しているのがわかる
実装しているsyscallは数十に留まり、完全な互換の実現には距離があるがそれでもCoreutilsやBashのようなアプリケーションが移植されて動作しているのは興味深い。
いわゆるホビーOSの枠でPOSIXを目指すものはSerenityOS( https://github.com/SerenityOS/serenity )などいくつかあるが、明示的にLinuxを目指すのは珍しい気がする。
HermiTux
ヤドカリがペンギンを挟んでいる可愛らしいロゴが印象的なHermiTuxはLinuxバイナリ互換を目指したUnikernelで、HermitCoreの上に構築されている。このHermitCoreがヤドカリをキャラクターにしている。
-
https://ssrg-vt.github.io/hermitux/
- ヤドカリとペンギン
-
https://hermitcore.org/
- ヤドカリ
-
https://github.com/ssrg-vt/hermitux-kernel/blob/e1a0dcaf763403ae7bf7d0c202da74f43cd1d409/arch/x86/kernel/isrs.c#L257
- syscallディスパッチの実装 -- 超でかい
switch
になっている
- syscallディスパッチの実装 -- 超でかい
-
https://www.ssrg.ece.vt.edu/papers/vee2019.pdf
- VEE2019の論文 -- OSvと比較している
-
https://github.com/danchiba/syscall-identification
- 開発者によるLinuxバイナリで使用されるsyscallを抽出するツール
同じUnikernelであるOSvとの違いとして、HermiTuxはバイナリ互換であることを推している。カーネル自体を通常ユーザーランドで使用される下位側のアドレスに配置することで、アプリケーションのメモリ配置を保ったままUnikernel化を行っている。
論文では特化カーネルの構築に言及していて、そのためのバイナリ解析ツール syscall-identification も公開されている。このツールはDyninst( https://www.dyninst.org/ )でバイナリの実行領域とsyscall命令の位置、RAXレジスタの内容を検出してそれを列挙するとしている。
OSv
EDIT: 現状のバージョン はバイナリ互換としていて JREのDockerイメージから本体を抜き出して実行する デモもある。
OSvはLinuxバイナリ互換を目指したUnikernelとしては初期のもので、実行ファイルが動的executableであることを前提に直接ELFをロードして実行するデザインとなっている。このデザインを取ることで、OpenJDKのような大きなバイナリも動作させている。
-
http://osv.io/
- 公式
-
https://www.iij-ii.co.jp/en/activities/seminars/pdf/iijlab-seminar-20141204-0.pdf
- 紹介スライド(日本語)
OSvのLinux互換は相当な完成度で、glibcのsyscall界面やif制御等をそれなりに実装している。しかし、HermiTuxの論文で触れられているように完全なバイナリ互換を提供するものではなく、単にlibcをライブラリOSに置き換えるものと言える。このため、syscallテーブルは存在せず、ダイナミックリンカが直接executableからのAPI呼び出しをOSにリンクすることになる。
個人的にはOSvのようなデザイン(syscallレベルではなくC APIレベルのバイナリ互換)は他にも出てきて良いと思うんだけど、後述のようなアプリケーションレベル実装の方が可能性はありそうかなという気もする。
Cyanurus
UNIX/Linux互換OSをゼロから作った話 で紹介されている Cyanurus はARM32用のLinux互換カーネルのようだ。
-
https://github.com/redcap97/cyanurus/blob/233ef4d1f34bc7a1478f228d9aad00b04500e478/src/kernel/syscall.c#L538
- syscall table
実装範囲は非常に限定的なものの、この規模の実装でCoreutils動くのかと感動した。
Tilck
-
https://github.com/vvaltchev/tilck
- 本家
-
https://github.com/vvaltchev/tilck/blob/65c7ca6f5d142cced8c9c8c16c06e1021aea63c9/include/tilck/kernel/syscalls.h
- syscallの宣言
i686向けのLinux互換カーネル、実装しているsyscallは多くないもののvim等を動作させている。
Linuxでビルドすることを前提に、 ホストのsyscallテーブルを直接 #include
する 割と独特なデザインになっている。まぁ互換だし良いか。
Kerla と Resea
- https://github.com/nuta/kerla
-
https://github.com/nuta/kerla/blob/917c8e818902e8a8d1a25eb3151ea9253de77a1e/kernel/syscalls/mod.rs#L225-L367
- syscallのディスパッチ
- https://github.com/nuta/resea
-
https://github.com/nuta/resea/blob/b98d60626a6b86a1c402e617f8418325eb354b90/servers/minlin/syscall.c#L438-L525
- syscallのディスパッチ(Linux -- マイクロカーネル的なsyscallは別に存在する)
-
https://news.ycombinator.com/item?id=28986229
- HNのスレッド
KerlaはRust製でHNで話題に。自前のTCPスタック。
Unikraft
NEC Labs出身のunikernelでベアメタル動作がなくXenまたはKVM+QEMUと、(特徴的な)Linuxユーザランドでのエミュレーションがある。ページでは高速性を主張しているが、これを書いている時点ではSMPサポートが無く、ファイルシステムもramfsか9p程度しかない。TCP/IPスタックはpluggableで標準はlwIP。Linux userlandでの動作を実現していることからわかるように、実装手法自体はsyscallエミュレーションに近い。
syscallエミュレーション
WSL1の登場で一躍有名になったカーネルレベルでのLinuxエミュレーションは、もっとも歴史と伝統のあるLinux互換環境の実現方法と言える。
FreeBSD
WSL1の登場以前に最も著名だったのはFreeBSDのLinuxulatorではないかと思うが、最新のLinuxカーネルにはあまり良く追従していない印象がある。
- https://wiki.freebsd.org/action/show/Linuxulator
- FreeBSD wikiのページ
- https://github.com/freebsd/freebsd/blob/7dac5ce5e017abf4e5de33121ac9e1da2158ceaa/sys/amd64/linux/linux_sysent.c#L18
- syscallテーブル
syscall命令によって処理されるカーネル内のサブルーチンを差し替えることでLinuxバイナリ互換を実現している。
NetBSD
NetBSDにもLinux互換インターフェースが存在する。FreeBSDとは独立した実装となっている。
実装方法はFreeBSDと同じくsyscall tableの差し替えによって実装している。
Solaris / SmartOS
OpenSolarisのLinuxエミュレーション環境であるLxはJoyent(node.jsの開発元として知られる)が中心となって近年復活している。
-
https://www.slideshare.net/bcantrill/illumos-lx
- 2014年当時の状況スライド -- 復活の経緯等興味深い内容
-
https://github.com/joyent/illumos-joyent/blob/87623b574a10b8e5de5a8117193027774557f6e7/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c#L1017
- syscall table(エミュレーション側)
Lxが独特なのは完全なカーネル実装としてLinuxエミュレーションを提供するのではなく、一部のsyscallについてはユーザ側にそのまま引き戻してライブラリレベルでのエミュレーションを行う点と言える。 inotify
や futex
のような一部機能は相当機能をカーネル側に実装しそれを利用している。
Windows(WSL1)
WSL1(Windows Subsystem for Linux)はモバイル向Windowsが一時期Android互換を目指していた時期の副産物で、結局MSはモバイル市場から降りる形で放棄される形となった。
-
https://raw.githubusercontent.com/ionescu007/lxss/6a3040fadff5ce43d7bfd638a4e5d7dfe8780143/WSL-BlueHat-Final.pdf
- リリース当時に公開された解析資料 -- https://github.com/ionescu007/lxss/ プロジェクトの一部
-
https://github.com/MicrosoftDocs/WSL/blob/8d57a4263fd16c22e790cb8637d5f1c911995fc6/LTP_Results/15014/Syscalls.txt
- テストされているsyscallの資料
現在はVMベースで通常のLinuxカーネルを使用するWSL2が推奨されている。対して、WSL1はFreeBSDやNetBSDの実装のような完全なLinux syscallエミュレータで、これらのカーネルが実現しているsyscall tableの差し替えやABIの適合をPicoProcessと呼んでいる抽象化で実装している。
アプリケーションレベル実装
OSとしてのLinux全体ではなく、既存のLinuxアプリケーションを単発で動作させることを目的としたエミュレータ / API互換実装がいくつかある。
バイナリ互換
amd64なLinuxバイナリをWindowsで動かすというのはあまり自明なことではなく、単にAPIをエミュレーションするだけでなくスタックの使用方法を合わせるなど追加の考察が必要である種のバイナリ変換かWSL1のような特殊プロセスの導入は避けられない。この点がLinux上でWindowsバイナリを動作させるWineとの違いと言える。
Boxedwine
Boxedwine https://t.co/VyW37lx3eT めっちゃ熱い。Wine専用のLinux再実装+x86エミュで、 @abagames 先生の傑作まさしくんハイ!が256色で走るし、テネレッツァ(のベンチ)も走る。まだEmscriptenビルドにはGLが無いけど、以前作った https://t.co/yZzYJuaZIc DOOM専用WASM環境に移植したいな。3DMark! pic.twitter.com/T1jDnqF6rI
— okuoku (@okuoku) May 29, 2020
BoxedwineはWine専用のx86 Linuxエミュレータで、WindowsだけではなくLinuxやemscriptenにも移植されている。特にemscripten移植は最近かなりの進捗が見られ、Web上でWindowsゲームが遊べる環境として話題になるのは時間の問題だろう。
- https://github.com/danoon2/Boxedwine
- http://www.boxedwine.org/
- 公式サイト -- 配布バイナリは少々古いのでSourceforgeの方を参照した方が良さそう
- https://github.com/danoon2/Boxedwine/blob/06ccdac707431fb16bff3ceef37d908eea91b0a2/source/kernel/syscall.cpp#L1554
- syscall table
Wineをそれなりに利用可能な形で動作させるために、オーディオやOpenGLのシリアライズ等も実装している。
qemu-user
qemuにはユーザランドバイナリのエミュレーション機能があり、ARM Linuxバイナリをx86 Linuxで動作させるといったことが可能になっている。
-
https://github.com/qemu/qemu/blob/28b7d5fd59c158eeaf814f92da385f6986de5c66/linux-user/x86_64/syscall_64.tbl#L11
- syscall table
例えば、Dockerを使ってARM Linuxを動作させるといったテクニックで使用される。qemu-userは基本的にsyscallのプロトコル変換機能なので、LinuxバイナリをLinuxで動作させる方向にしか使用できない。(比較して、ここで挙げているUsercornを除く他のプロジェクトは全てLinuxアプリをWindowsで動作させるのを目指している)
blink
- https://github.com/jart/blink
-
https://github.com/jart/blink/blob/7ca9010909c3bd630c35a0c67e945d7e2d8839c6/blink/syscall.c#L2531
- syscall table 。
syscall
命令の実装の一部になっている。
- syscall table 。
blinkはコンパクトなx86-64インタプリタ/Direct-threadなJITCにCで書かれたLinux syscallエミュレータを組み合わせたもので、現状では静的リンクされたLinuxバイナリを動作させられる。同作者の"ユニバーサルlibc"である https://github.com/jart/cosmopolitan が著名だろう。blinkもこの流れで、本物のWrite Once Run Anywhereを目指している。
box86 / box64
- https://box86.org/
- https://github.com/ptitSeb/box86
-
https://github.com/ptitSeb/box86/blob/2b2478381a72a8e6d95299c9f8291edc706566c3/src/emu/x86syscall.c#L368
- syscall table
-
https://github.com/ptitSeb/box86/tree/2b2478381a72a8e6d95299c9f8291edc706566c3/src/wrapped
- ユーザランドライブラリのwrapper
box86やbox64はLinuxアプリケーションを動作させることを目的としたARM用のx86/x86-64 JITCエミュレータで、ユーザランドの動的ライブラリをwrapしてネイティブ側のものを無理矢理使うというなかなかのテクニックを実現している。
FEX
- https://github.com/FEX-Emu/FEX
-
https://github.com/FEX-Emu/FEX/tree/ecf489108751ecf86c7934741fe748951d798856/Source/Tests/LinuxSyscalls/Syscalls
- syscall HLE実装
-
https://github.com/FEX-Emu/FEX/tree/70d4a436cf259d5e7d2a70c7eee2b9fd1611e4c1/ThunkLibs
- wrapper
ランタイムアセンブラには vixl https://github.com/Linaro/vixl を採用している(ただ、自前のemitterに置き換えつつあるようだ)。syscall引数のうち未使用のものはIRレベルで除去するなどかなり気合の入った最適化が入っているが気合の割には..という印象を受ける。
Foreign Linux
Foreign Linuxは(CPUエミュレーションを伴わない)動的バイナリ変換でLinuxバイナリをWindows上で動作させることを目指すもので、残念ながらWSL1の登場以降開発が止まってしまっているようだ。
- https://github.com/wishstudio/flinux
-
https://github.com/wishstudio/flinux/wiki/Difference-between-Linux-and-Windows
- Windows上でLinuxバイナリを動作させる際の課題の説明。参考になる。
-
https://github.com/wishstudio/flinux/blob/a041253e8706aa5e0543bbadfc0ff7b9f819c6af/src/syscall/syscall_table_x64.h#L1
- syscall table
Usercorn
UsercornはqemuをベースとしたCPUエミュレータフレームワークであるUnicorn( https://www.unicorn-engine.org/ )上に構築されたカーネルエミュレータライブラリで、Linux以外にDarwinやMS-DOS( ! )まで実装している。
- https://github.com/lunixbochs/usercorn
- https://github.com/lunixbochs/ghostrace/blob/eccf95b50fc0983f4a35f99b568764aa05fbc86e/ghost/sys/num/linux_x86_64_sys.go
- 使用しているsyscall番号表は別ライブラリに分離されている。これら全てが実装されているわけではないようだ。
qemu-userと同じく完全なCPUエミュレーションを行うが、qemuとは違い Go でLinuxのAPIを再実装しているため、Linux以外でもLinuxバイナリを動作させることが可能になっている。
実装範囲はかなり限定的に見える。
Atratus
詳細不明。
LBW
InterixをWindows上のPOSIXレイヤとして使用していた。というわけでもう動作しない。
CheerpX for Flash
- https://www.leaningtech.com/pages/cheerpxflash.html
- https://medium.com/leaningtech/extreme-webassembly-1-pushing-browsers-to-their-absolute-limits-56a393435323
As a first technical application of CheerpX, we are currently focusing on Linux binaries and in particular on virtualizing the Flash binary plugin,
商用。Flash互換実装のLightspark( http://lightspark.github.io/ )の作者によるCheerpX for Flashは、Flashのx86 Linuxバイナリを実行するために自社のWebAssembly上に実装されたx86エミュレータを使用していると主張している。
同社によるLLVM backendとしてのWebAssemblyコンパイラはオープンソース提供されていて( https://www.leaningtech.com/pages/cheerp.html )、ランタイムライブラリはGPL2と商用ライセンスを提供している。
かんそう
LinuxはDockerなりなんなりの形でABIの資産価値が高まってしまっているため、ABIが今後大きく変化することは考えずらい。このため、Linuxある種の安定期を迎えていると考えることができ、Linuxを実装したい向きにはチャンスなのではないかと思う。
もっとも、今後自分が共通I/Fを実装することになるとしたらWASIを選ぶかな。。Linuxアプリをちゃんと動かすためには /proc
とか ioctl
なりなんなりも実装する必要があり、割と面倒なのではないだろうか。
Linux自体をアプリケーションにする試みとしては 以前USB/IPのために使ってみた LKL ( https://github.com/lkl/linux ) があるが、これにはsyscall shimもあり既存のバイナリを実行するための考察もある。なのでLKLをLinux互換レイヤのベースにするのは良いかもしれない。
今さらカーネルとか実装してどうすんのというのは有るかもしれないが、プロセスのチェックポイントやマイグレーション、再最適化のような、"Linuxを改造するよりもLinux風OSを作った方が早い" 領域があるかもしれない。Linux風環境の知見を研究することで、real worldアプリケーションと研究の距離を縮められるのではないだろうか。