1 ポイント 投稿者 GN⁺ 2 시간 전 | 1件のコメント | WhatsAppで共有
  • BusyBox は、複数のコマンドを1つの実行ファイルで提供する マルチコールバイナリ であり、Alpine のデフォルト wget も BusyBox 経由で実行される
  • Alpine コンテナの /usr/bin/wget は実際のバイナリではなく、/bin/busybox を指す シンボリックリンク だった
  • BusyBox は実行時に argv[0] から呼び出し名を読み取り、パスの最後の名前から実行する アプレット を決定する
  • 各アプレットは名前で検索され、対応する main 関数に入る。wgetwget.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 ファイルを持ち、wgetwget.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件のコメント

 
GN⁺ 2 시간 전
Lobste.rsの意見
  • 引用された質問への答えとしては、再実装に近い
    BusyBox の紹介は https://busybox.net/about.html にあり、ソースは https://github.com/vda-linux/busybox_mirror にある

  • プログラムが実際とは違う名前を名乗るのはかなり気になる
    macOS で gcc を実行したら実際には clang が出てくるのが最悪で、PowerShell も似たような動きをする。単に別の名前を使ってほしい

    • 多くの開発者が clang を知らなかったり、テスト用の Mac がなかったり、現実的な代替手段がなかった時代に始まった問題でもある
      Nixpkgs は https://github.com/NixOS/nixpkgs/… のようなパッチを大量に当てる必要があり、sqlite のような有名プロジェクトも例外ではない。一方で macOS は単に ごまかしのパス を選んだというわけだ
    • 多くのソフトウェアの makefile を書く人は cc を知らないことがある
    • いら立ちは理解できるが、たいていのビルドスクリプトを回せるだけの 十分な互換性 があれば、Linux と複数の Unix 系で同じスクリプトをある程度再利用できるため広まった可能性が高い
    • ときには現実的に不可能でもある
      gcc がハードコードされた makefile は多いだろうし、ほかの文脈でも同様だ。たとえば既存プログラムの多くは、正しい解決策である terminfo データベースの代わりに TERM 環境変数の xterm-* を確認するので、別の名前を選ぶ方式は通用しない
  • コンテナで妙な問題を診断するときは、podman cp /usr/bin/busybox-static somecontainer:/bin をよく使う

  • toybox も似た構造になっている
    一部のツールは別の場所から持ってきたり移植したりしたもののようだが、かなりの数は BusyBox 向けに新たに実装されており、小さく保ちつつ静的リンク時にも小さくコンパイルできるよう、限られた libc 機能しか使わないことを目標にしている。もう一つの利点は、シェルスクリプトでこうしたツールを組み込みコマンドのように使える点だ。一部は fork+exec ではなく fork+jump で実行され、いくつかは fork なしで通常の関数呼び出しとしても実行される

    • toybox はもともと、GPL ライセンスの BusyBox に対する BSD ライセンスの代替 として作られたものだと理解している
      追記すると、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 のような企業に 無償労働 を提供することが、どう自分の首を絞めるかを示す教科書的な事例として、これ以上完璧なものはなかなかない
      とくに Google が長年にわたり Termux を潰すと脅してきたことまで考えると、なおさらだ
  • Windows 向けポートもある: https://github.com/rmyorston/busybox-w32
    BusyBox の小ささのおかげで、こうしたポートはより現実的になっている。ただ、いまでは Windows の中で Linux をそのまま動かせるので、関連性はやや薄れているようだ