BusyBoxとは何か?
(specular.fi)- BusyBox は、複数のコマンドを1つの実行ファイルで提供する マルチコールバイナリ であり、Alpine のデフォルト
wgetも BusyBox 経由で実行される - Alpine コンテナの
/usr/bin/wgetは実際のバイナリではなく、/bin/busyboxを指す シンボリックリンク だった - BusyBox は実行時に
argv[0]から呼び出し名を読み取り、パスの最後の名前から実行する アプレット を決定する - 各アプレットは名前で検索され、対応する
main関数に入る。wgetはwget.cに実装され、最終的にwget_mainが実行される busybox --listでコンパイルされたコマンドを確認でき、Alpine の例では 304個 が表示され、各ユーティリティは縮小版のように見える
BusyBoxの動作の仕組み
- BusyBox は、複数のコマンドを1つの実行ファイルで提供する マルチコールバイナリ(multicall binary) 構造を採用している
- Alpine コンテナでは、
/usr/bin/wgetは実際の実行ファイルではなく、/bin/busyboxを指す シンボリックリンク だったdocker run --rm -it alpine sh / # which wget /usr/bin/wget / # ls -lah /usr/bin/wget lrwxrwxrwx 1 root root 12 Apr 15 04:51 /usr/bin/wget -> /bin/busybox /usr/binには130個を超える実行ファイルが、1つのバイナリから派生したもののように見え、BusyBox のマルチコールバイナリ構造と関係している- BusyBox は実行時に
argv[0]から呼び出された名前を取得し、パスの最後の名前だけを取り出して、どの アプレット(applet) を実行するかを決定するapplet_name = argv[0]; if (applet_name[0] == '-') applet_name++; applet_name = bb_basename(applet_name); busybox ls -1のように明示的にアプレット名を渡しても動作し、存在しない名前を与えるとapplet not foundと表示される/ # busybox ls -1 bin dev etc home / # busybox meheh meheh: applet not found
アプレット構成とインストール方式
- BusyBox は名前でアプレットを検索したあと、対応するアプレットの
main関数を実行するint applet = find_applet_by_name(name); // ... run_applet_no_and_exit(applet, name, argv); // ... xfunc_error_retval = applet_main[applet_no](argc, argv); - 各アプレットは個別の C ファイルを持ち、
wgetはwget.cに実装されている - アプレットごとの設定はコードコメントの形で定義されており、
WGET設定にはwget (41 kb)、デフォルト有効、HTTP と FTP サーバーからファイルを非対話的にダウンロードするユーティリティというヘルプ、ビルド対象wget.oが含まれる//config:config WGET //config: bool "wget (41 kb)" //config: default y //config: help //config: wget is a utility for non-interactive download of files from HTTP //config: and FTP servers. //applet:IF_WGET(APPLET(wget, BB_DIR_USR_BIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_WGET) += wget.o - 最終的に
wgetアプレットはwget_mainに入るint wget_main(int argc UNUSED_PARAM, char **argv) - BusyBox はハードリンクもサポートしており、
busybox --install -sの-sは シンボリックリンク の作成を意味するbusybox --install -s - コンパイルに含まれるコマンド一覧は
busybox --listで確認でき、Alpine の例の環境では 304個 が表示される/ # busybox --list | wc -l 304 - Alpine の多くのコマンドは BusyBox ベースのバイナリへのインターフェースのように動作し、各ユーティリティは完全な元のユーティリティよりやや 縮小版 のように見える
1件のコメント
Lobste.rsの意見
引用された質問への答えとしては、再実装に近い
BusyBox の紹介は https://busybox.net/about.html にあり、ソースは https://github.com/vda-linux/busybox_mirror にある
プログラムが実際とは違う名前を名乗るのはかなり気になる
macOS で
gccを実行したら実際には clang が出てくるのが最悪で、PowerShell も似たような動きをする。単に別の名前を使ってほしいNixpkgs は https://github.com/NixOS/nixpkgs/… のようなパッチを大量に当てる必要があり、sqlite のような有名プロジェクトも例外ではない。一方で macOS は単に ごまかしのパス を選んだというわけだ
ccを知らないことがあるgccがハードコードされた makefile は多いだろうし、ほかの文脈でも同様だ。たとえば既存プログラムの多くは、正しい解決策であるterminfoデータベースの代わりにTERM環境変数のxterm-*を確認するので、別の名前を選ぶ方式は通用しないコンテナで妙な問題を診断するときは、
podman cp /usr/bin/busybox-static somecontainer:/binをよく使うtoybox も似た構造になっている
一部のツールは別の場所から持ってきたり移植したりしたもののようだが、かなりの数は BusyBox 向けに新たに実装されており、小さく保ちつつ静的リンク時にも小さくコンパイルできるよう、限られた libc 機能しか使わないことを目標にしている。もう一つの利点は、シェルスクリプトでこうしたツールを組み込みコマンドのように使える点だ。一部は fork+exec ではなく fork+jump で実行され、いくつかは fork なしで通常の関数呼び出しとしても実行される
追記すると、Toybox on Wikipedia によれば、「Toybox は Rob Landley が BusyBox の元の作者である Bruce Perens との対立により BusyBox のメンテナー役を辞めた後、2006 年初頭に始めた」とある
実際には 再実装 だ。ただしライセンスが許すなら、元の大きな実装から一部コードを借りていたとしても驚きはしない
From the 'pedia: によれば、BusyBox は 1995 年に Bruce Perens が最初に書き、1996 年には意図した用途に対して完成したと宣言された。当初の目標は、Debian ディストリビューションの復旧ディスク兼インストーラーとして機能する完全に起動可能なシステムを、フロッピーディスク 1 枚に収めることだった。その後、組み込み Linux デバイスや Linux ディストリビューションのインストーラーにおける事実上の標準コアユーザー空間ツール群へと拡大し、Linux 実行ファイルは 1 つごとに数 KB のオーバーヘッドを必要とするため、200 を超えるプログラムを 1 つにまとめればディスク容量とメモリを大きく節約できる
関連して Toybox も構造と哲学は似ているが BSD ライセンスだ。主開発者の Rob Landley は、商業的に受け入れられやすいライセンスであれば Android デバイスに組み込まれる可能性があり、そうなればすべての Android スマートフォンとタブレットが Unix 風の開発環境へ変わる潜在力を持つと考えていた、と記憶している。Toybox は今も Android の一部のようだが、Termux のように上位レイヤーで補助するツールがなければ、Android を Unix のように使うのは簡単ではない
とくに Google が長年にわたり Termux を潰すと脅してきたことまで考えると、なおさらだ
Windows 向けポートもある: https://github.com/rmyorston/busybox-w32
BusyBox の小ささのおかげで、こうしたポートはより現実的になっている。ただ、いまでは Windows の中で Linux をそのまま動かせるので、関連性はやや薄れているようだ