ユーザー空間のアプリケーションについての質問ですが、聞いてください!
Linuxの機能的なディストリビューションを起動するには、いわば3つの「アプリケーション」が必要です。
ブートローダー-組み込みの場合、通常はU-Bootですが、必須要件ではありません。
カーネル-とても簡単です。
ルートファイルシステム-それがないとシェルから起動できません。カーネルが起動するファイルシステムが含まれ、init
はformと呼ばれます。
私の質問は#3に関するものです。誰かが非常に最小限のrootfsを構築したいと思った場合(この質問ではGUIがないとしましょう、シェルのみ)、シェルから起動するにはどのファイル/プログラムが必要ですか?
それはあなたがあなたのデバイスでどのようなサービスを望んでいるかに完全に依存します。
Linuxを直接Shellにブートできます。シェルをそこに置いておきたいだけの運用環境ではあまり役に立ちませんが、インタラクティブなブートローダーがある場合の介入メカニズムとしては役立ちます。カーネルコマンドラインにinit=/bin/sh
を渡します。すべてのLinuxシステム(およびすべてのUNIXシステム)には、/bin/sh
にBourne/POSIXスタイルのシェルがあります。
Shellユーティリティのセットが必要です。 BusyBox は非常に一般的な選択です。シェルとファイルおよびテキスト操作用の一般的なユーティリティ(cp
、grep
、…)、ネットワーク設定(ping
、ifconfig
、…)、プロセス操作(ps
、Nice
、…)、およびその他のさまざまなシステムツール(fdisk
、mount
、syslogd
、…)。 BusyBoxは非常に構成可能です。コンパイル時に必要なツールや個々の機能を選択して、アプリケーションの適切なサイズ/機能の妥協点を得ることができます。 sh
を除いて、実際に何もせずに何もできない最低限のものはmount
、umount
およびhalt
ですが、また、cat
、cp
、mv
、rm
、mkdir
、rmdir
、ps
、sync
など。 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
:ダイナミックローダーを含む共有ライブラリ(すべての実行可能ファイルが静的にリンクされていない場合)/proc
: procファイルシステムのマウントポイント/sbin
:実行可能プログラム。 /bin
との違いは、/sbin
はシステム管理者にのみ役立つプログラム用ですが、この違いは組み込みデバイスでは意味がありません。 /sbin
を/bin
へのシンボリックリンクにすることができます。/mnt
:メンテナンス中にスクラッチマウントポイントとして読み取り専用のルートファイルシステムに置くと便利です/sys
: sysfsファイルシステムのマウントポイント/tmp
:一時ファイルの場所(多くの場合、tmpfs
マウント)/usr
:サブディレクトリbin
、lib
およびsbin
が含まれています。 /usr
は、ルートファイルシステム上にない追加ファイル用に存在します。それがない場合は、/usr
をルートディレクトリへのシンボリックリンクにすることができます。次に、最小限の/dev
の一般的なエントリをいくつか示します。
console
full
(それに書き込むと、常に「デバイスに空き領域がない」と報告されます)log
(プログラムがログエントリを送信するために使用するソケット)、 syslogd
デーモン(BusyBoxのような)から読み取る場合null
(常に空のファイルのように動作します)ptmx
と pts
ディレクトリ 、 pseudo-terminals を使用する場合(つまりコンソール以外の任意の端末)-例デバイスがネットワークに接続されており、TelnetまたはSSHで接続する場合random
(ランダムなバイトを返す、ブロックされる危険性あり)tty
(常にプログラムの端末を指定します)urandom
(ランダムにバイトを返し、ブロックすることはありませんが、新しく起動したデバイスではランダムではない場合があります)zero
(nullバイトの無限シーケンスが含まれます)さらに、ハードウェアのエントリが必要になります(ネットワークインターフェイスを除き、これらは/dev
にエントリを取得しません):シリアルポート、ストレージなど。
組み込みデバイスの場合、通常はルートファイルシステムに直接デバイスエントリを作成します。ハイエンドシステムにはMAKEDEV
というスクリプトがあり、/dev
エントリを作成しますが、組み込みシステムでは、スクリプトがイメージにバンドルされていないことがよくあります。一部のハードウェアをホットプラグできる場合(デバイスにUSBホストポートがある場合など)、/dev
はudev によって管理される必要があります(ルートファイルシステムに最小限のセットが残っている可能性があります)。
ルートファイルシステムのほかに、通常の操作ではさらにいくつかマウントする必要があります。
/proc
(かなり必要不可欠)/sys
のsysfs (かなり不可欠)tmpfs
ファイルシステム/tmp
(フラッシュまたは読み取り専用のルートファイルシステムではなく、RAMにある一時ファイルをプログラムが作成できるようにするため)/dev
のdevtmpfs(上記の「デバイスファイル」のudevを参照)/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カーネルに組み込まれた唯一の動作は、起動時に起動される最初のプログラムです。 (ここでは initrd と initramfs の微妙な点には触れません)このプログラムは、伝統的に と呼ばれていました。 init 、プロセスID 1を持ち、特定の特権( KILLシグナルに対する耐性 )と責任(リーピング orphans )。 Linuxカーネルを使用してシステムを実行し、最初のプロセスとして必要なものを開始できますが、使用しているのはLinuxカーネルに基づくオペレーティングシステムであり、通常「Linux」と呼ばれるものではありません— Linux は、一般的な意味では、 Unix に似たオペレーティングシステムで、カーネルは Linuxカーネルです 。たとえば、Androidは、Unixのようなオペレーティングシステムではありませんが、Linuxカーネルに基づいています。
必要なのは、静的にリンクされた1つの実行可能ファイルであり、ファイルシステムに単独で配置されます。他のファイルは必要ありません。その実行可能ファイルはinitプロセスです。それはbusyboxすることができます。これにより、シェルと他のユーティリティのホストがすべて提供されます。完全に機能するシステムにアクセスするには、busyboxでコマンドを手動で実行して、ルートファイルシステムを読み書き可能にマウントし、/ devノードを作成し、実際のinitを実行します。
シェルユーティリティが必要ない場合は、静的にリンクされた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
にいくつかのファイルが必要です。
カーネルオプションdevtmpfs.mount=1
で起動すると、入力済みの/dev
が不要になり、空のディレクトリ(マウントポイントとしての使用に適しています)にするだけです。
通常、いくつかのユーティリティ(klibc、busybox、beastiebox、toybox、またはツールボックス)が必要ですが、実際には必要ありません。
$ PS1といくつかの基本的なシェルのエイリアスと関数を設定する~/.mkshrc
ファイルを追加することができます。
Linux/m68k用に、mksh(およびそのサンプルのmkshrcファイル)とklibc-utilsのみを使用して、171K圧縮(371K非圧縮)initrdを作成しました。 (ただし、これは-T!がシェルに追加される前だったため、代わりに/dev/tty2
でログインシェルを生成し、コンソールにメッセージをエコーして、ユーザーに端末を切り替えるように指示しました。)正常に動作します。
これは本当に最低限の設定です。他の回答は、やや機能的なシステムに対する優れたアドバイスを提供します。これは本当に特別なケースです。
免責事項:私はmksh開発者です。
最小限のinit hello worldプログラムのステップバイステップ
無限ループで終了する依存関係のない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でテスト済み。