web-dev-qa-db-ja.com

Linuxを完全に起動するために必要な最小ルートファイルシステムアプリケーションは何ですか?

ユーザー空間のアプリケーションについての質問ですが、聞いてください!

Linuxの機能的なディストリビューションを起動するには、いわば3つの「アプリケーション」が必要です。

  1. ブートローダー-組み込みの場合、通常はU-Bootですが、必須要件ではありません。

  2. カーネル-とても簡単です。

  3. ルートファイルシステム-それがないとシェルから起動できません。カーネルが起動するファイルシステムが含まれ、initはformと呼ばれます。

私の質問は#3に関するものです。誰かが非常に最小限のrootfsを構築したいと思った場合(この質問ではGUIがないとしましょう、シェルのみ)、シェルから起動するにはどのファイル/プログラムが必要ですか?

17
MDMoore313

それはあなたがあなたのデバイスでどのようなサービスを望んでいるかに完全に依存します。

プログラム

Linuxを直接Shellにブートできます。シェルをそこに置いておきたいだけの運用環境ではあまり役に立ちませんが、インタラクティブなブートローダーがある場合の介入メカニズムとしては役立ちます。カーネルコマンドラインにinit=/bin/shを渡します。すべてのLinuxシステム(およびすべてのUNIXシステム)には、/bin/shにBourne/POSIXスタイルのシェルがあります。

Shellユーティリティのセットが必要です。 BusyBox は非常に一般的な選択です。シェルとファイルおよびテキスト操作用の一般的なユーティリティ(cpgrep、…)、ネットワーク設定(pingifconfig、…)、プロセス操作(psNice、…)、およびその他のさまざまなシステムツール(fdiskmountsyslogd、…)。 BusyBoxは非常に構成可能です。コンパイル時に必要なツールや個々の機能を選択して、アプリケーションの適切なサイズ/機能の妥協点を得ることができます。 shを除いて、実際に何もせずに何もできない最低限のものはmountumountおよびhaltですが、また、catcpmvrmmkdirrmdirpssyncなど。 BusyBoxは、busyboxという単一のバイナリとしてインストールされ、各ユーティリティのシンボリックリンクが含まれています。

通常のUNIXシステムでの最初のプロセスは、initと呼ばれます。その仕事は他のサービスを開始することです。 BusyBoxにはinitシステムが含まれています。 initバイナリ(通常は/sbinにあります)に加えて、その構成ファイル(通常は/etc/inittabと呼ばれます)が必要です。いくつかの最新のinit置換では、このファイルは使用されませんが、どのサービスをいつ開始するかを示す小さな組み込みシステムでは見つかりません)。 BusyBoxの場合、/etc/inittabはオプションです。見つからない場合は、コンソールにルートシェルが表示され、ブート時にスクリプト/etc/init.d/rcS(デフォルトの場所)が実行されます。

もちろんそれだけで十分です。もちろん、デバイスを便利にするプログラムを超えています。たとえば、 OpenWrt バリアントを実行しているホームルーターでの唯一のプログラムは、BusyBox、nvram(NVRAMの設定の読み取りと変更用)、およびネットワークユーティリティです。

すべての実行可能ファイルが静的にリンクされていない限り、ダイナミックローダー(ld.so、libcの選択とプロセッサアーキテクチャによって異なる名前で呼び出される可能性があります)とすべての動的ライブラリ/lib/lib*.so、おそらくこれらのいくつかは/usr/libにあります)これらの実行可能ファイルに必要です。

ディレクトリ構造

Filesystem Hierarchy Standard は、Linuxシステムの一般的なディレクトリ構造について説明しています。デスクトップとサーバーのインストール向けです。組み込みシステムでは、その多くを省略できます。ここに典型的な最小値があります。

  • /bin:実行可能プログラム(一部は/usr/binにある場合があります)。
  • /dev:デバイスノード(下記参照)
  • /etc:構成ファイル
  • /lib:ダイナミックローダーを含む共有ライブラリ(すべての実行可能ファイルが静的にリンクされていない場合)
  • /procprocファイルシステムのマウントポイント
  • /sbin:実行可能プログラム。 /binとの違いは、/sbinはシステム管理者にのみ役立つプログラム用ですが、この違いは組み込みデバイスでは意味がありません。 /sbin/binへのシンボリックリンクにすることができます。
  • /mnt:メンテナンス中にスクラッチマウントポイントとして読み取り専用のルートファイルシステムに置くと便利です
  • /syssysfsファイルシステムのマウントポイント
  • /tmp:一時ファイルの場所(多くの場合、tmpfsマウント)
  • /usr:サブディレクトリbinlibおよびsbinが含まれています。 /usrは、ルートファイルシステム上にない追加ファイル用に存在します。それがない場合は、/usrをルートディレクトリへのシンボリックリンクにすることができます。

デバイスファイル

次に、最小限の/devの一般的なエントリをいくつか示します。

  • console
  • full(それに書き込むと、常に「デバイスに空き領域がない」と報告されます)
  • log(プログラムがログエントリを送信するために使用するソケット)、 syslogd デーモン(BusyBoxのような)から読み取る場合
  • null (常に空のファイルのように動作します)
  • ptmxptsディレクトリpseudo-terminals を使用する場合(つまりコンソール以外の任意の端末)-例デバイスがネットワークに接続されており、TelnetまたはSSHで接続する場合
  • random(ランダムなバイトを返す、ブロックされる危険性あり)
  • tty(常にプログラムの端末を指定します)
  • urandom(ランダムにバイトを返し、ブロックすることはありませんが、新しく起動したデバイスではランダムではない場合があります)
  • zero(nullバイトの無限シーケンスが含まれます)

さらに、ハードウェアのエントリが必要になります(ネットワークインターフェイスを除き、これらは/devにエントリを取得しません):シリアルポート、ストレージなど。

組み込みデバイスの場合、通常はルートファイルシステムに直接デバイスエントリを作成します。ハイエンドシステムにはMAKEDEVというスクリプトがあり、/devエントリを作成しますが、組み込みシステムでは、スクリプトがイメージにバンドルされていないことがよくあります。一部のハードウェアをホットプラグできる場合(デバイスにUSBホストポートがある場合など)、/devudev によって管理される必要があります(ルートファイルシステムに最小限のセットが残っている可能性があります)。

起動時のアクション

ルートファイルシステムのほかに、通常の操作ではさらにいくつかマウントする必要があります。

  • procfs on /proc(かなり必要不可欠)
  • /sysのsysfs (かなり不可欠)
  • tmpfsファイルシステム/tmp(フラッシュまたは読み取り専用のルートファイルシステムではなく、RAMにある一時ファイルをプログラムが作成できるようにするため)
  • tmpfs、devfs、または動的な場合は/devのdevtmpfs(上記の「デバイスファイル」のudevを参照)
  • devpts on /dev/pts [pseudo-terminals(上記のptsに関する注記を参照)を使用する場合)

/etc/fstab ファイルを作成してmount -aを呼び出すか、mountを手動で実行できます。

syslog デーモンを起動します(klogdプログラムが処理しない場合は、カーネルログのsyslogdも同様)。ログを書き込む場所があります。

この後、デバイスはアプリケーション固有のサービスを開始する準備ができています。

ルートファイルシステムを作る方法

これは長くて多様な物語なので、ここで行うことはいくつかの指針を与えることだけです。

ルートファイルシステムはRAM((通常は圧縮された)イメージからROMまたはフラッシュ)からロードされた)、またはディスクベースのファイルシステム(保存されたROM or flash)、またはネットワークからロードされます(多くの場合 [〜#〜] tftp [〜#〜] )。ルートファイルシステムがRAMにある場合は、それを initramfs にしてください— a RAMブート時にコンテンツが作成されるファイルシステム。

組み込みシステムのルートイメージをアセンブルするための多くのフレームワークが存在します。 BusyBox FAQ にはいくつかのポインタがあります。 Buildroot は人気のあるもので、LinuxカーネルやBusyBoxと同様の設定でルート全体のイメージを構築できます。 OpenEmbedded もそのようなフレームワークです。

ウィキペディアには、人気のある 組み込みLinuxディストリビューション の(不完全な)リストがあります。近くにある可能性のある組み込みLinuxの例は、ネットワークアプライアンス用の OpenWrt ファミリのオペレーティングシステム(いじくり屋のホームルーターで人気)です。経験から学びたい場合は、 Linuxを最初から試してみてください が、組み込みデバイスではなく、愛好家のためのデスクトップシステム向けです。

LinuxとLinuxカーネルに関する注意

Linuxカーネルに組み込まれた唯一の動作は、起動時に起動される最初のプログラムです。 (ここでは initrdinitramfs の微妙な点には触れません)このプログラムは、伝統的に と呼ばれていました。 init 、プロセスID 1を持ち、特定の特権( KILLシグナルに対する耐性 )と責任(リーピング orphans )。 Linuxカーネルを使用してシステムを実行し、最初のプロセスとして必要なものを開始できますが、使用しているのはLinuxカーネルに基づくオペレーティングシステムであり、通常「Linux」と呼ばれるものではありません— Linux は、一般的な意味では、 Unix に似たオペレーティングシステムで、カーネルは Linuxカーネルです 。たとえば、Androidは、Unixのようなオペレーティングシステムではありませんが、Linuxカーネルに基づいています。

必要なのは、静的にリンクされた1つの実行可能ファイルであり、ファイルシステムに単独で配置されます。他のファイルは必要ありません。その実行可能ファイルはinitプロセスです。それはbusyboxすることができます。これにより、シェルと他のユーティリティのホストがすべて提供されます。完全に機能するシステムにアクセスするには、busyboxでコマンドを手動で実行して、ルートファイルシステムを読み書き可能にマウントし、/ devノードを作成し、実際のinitを実行します。

5

シェルユーティリティが必要ない場合は、静的にリンクされたmkshバイナリ(たとえば、Linux/i386ではklibc – 130Kに対して)で十分です。ループで/linuxrcを呼び出すだけの/initまたは/sbin/initまたはmksh -l -T!/dev/tty1スクリプトが必要です。

#!/bin/mksh
while true; do
    /bin/mksh -l -T!/dev/tty1
done

-T!$ttyオプションはmkshへの最近の追加で、指定された端末で新しいシェルを起動して待機するように指示します。 (それ以前は、プログラムをデーモン化するための-T-と、ターミナルで生成するがそれを待たないための-T$ttyしかありませんでした。これはそれほどいいことではありませんでした。)-lオプションは単にそれを伝えるだけです。ログインシェルを実行します(/etc/profile~/.profile~/.mkshrcを読み取ります)。

これは、あなたの端末が/dev/tty1であると仮定しています。 (より多くの魔法を使うと、端末を自動的に見つけることができます。/dev/consoleは完全なジョブ制御を提供しません。)

これを機能させるには、/devにいくつかのファイルが必要です。

  • / dev/console
  • / dev/null
  • / dev/tty
  • / dev/tty1

カーネルオプションdevtmpfs.mount=1で起動すると、入力済みの/devが不要になり、空のディレクトリ(マウントポイントとしての使用に適しています)にするだけです。

通常、いくつかのユーティリティ(klibc、busybox、beastiebox、toybox、またはツールボックス)が必要ですが、実際には必要ありません。

$ PS1といくつかの基本的なシェルのエイリアスと関数を設定する~/.mkshrcファイルを追加することができます。

Linux/m68k用に、mksh(およびそのサンプルのmkshrcファイル)とklibc-utilsのみを使用して、171K圧縮(371K非圧縮)initrdを作成しました。 (ただし、これは-T!がシェルに追加される前だったため、代わりに/dev/tty2でログインシェルを生成し、コンソールにメッセージをエコーし​​て、ユーザーに端末を切り替えるように指示しました。)正常に動作します。

これは本当に最低限の設定です。他の回答は、やや機能的なシステムに対する優れたアドバイスを提供します。これは本当に特別なケースです。

免責事項:私はmksh開発者です。

4
mirabilos

最小限のinit hello worldプログラムのステップバイステップ

enter image description here

無限ループで終了する依存関係のないHello Worldをコンパイルします。 init.S

.global _start
_start:
    mov $1, %rax
    mov $1, %rdi
    mov $message, %rsi
    mov $message_len, %rdx
    syscall
    jmp .
    message: .ascii "FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR\n"
    .equ message_len, . - message

sys_exitを使用できません。そうしないと、カーネルパニックが発生します。

次に:

mkdir d
as --64 -o init.o init.S
ld -o init d/init.o
cd d
find . | cpio -o -H newc | gzip > ../rootfs.cpio.gz
ROOTFS_PATH="$(pwd)/../rootfs.cpio.gz"

これにより、/initにhello worldを含むファイルシステムが作成されます。これは、カーネルが実行する最初のユーザーランドプログラムです。また、d/にさらにファイルを追加して、カーネルの実行時に/initプログラムからアクセスできるようにすることもできます。

次にcdをLinuxカーネルツリーに入れ、ビルドは通常どおり、QEMUで実行します。

git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
git checkout v4.9
make mrproper
make defconfig
make -j"$(nproc)"
qemu-system-x86_64 -kernel Arch/x86/boot/bzImage -initrd "$ROOTFS_PATH"

そして、あなたはラインを見るはずです:

FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR

エミュレータ画面で!これは最後の行ではないので、もう少し上を調べなければならないことに注意してください。

静的にリンクしている場合は、Cプログラムを使用することもできます。

#include <stdio.h>
#include <unistd.h>

int main() {
    printf("FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR\n");
    sleep(0xFFFFFFFF);
    return 0;
}

と:

gcc -static init.c -o init

/dev/sdXのUSBを使用して、実際のハードウェアで実行できます。

make isoimage FDINITRD="$ROOTFS_PATH"
Sudo dd if=Arch/x86/boot/image.iso of=/dev/sdX

このテーマに関する素晴らしい情報源: http://landley.net/writing/rootfs-howto.html Linuxカーネルソースツリーのスクリプトであるgen_initramfs_list.shの使用方法についても説明していますプロセスの自動化に役立ちます。

次のステップ:システムと対話できるようにBusyBoxをセットアップします: https://github.com/cirosantilli/runlinux

Ubuntu 16.10、QEMU 2.6.1でテスト済み。