Hello again, linux!
背景
1996年頃、たしか「メモンドウズ」ってキーワードの記事だった気がするが
今はgoogleっても出てこない。まさかFJか?
とにかく・・・記事ではwindows95の某システムファイルがただのtext-fileで、
「explorer.exe」(だったかな?もうwin95手元にないし。)って書いてあるところを
「notepad.exe」と変わってしまった状態で再起動すると、
見慣れた画面ではなく、notepadのウィンドウが1個だけ立ち上がってくる。
notepad終了するとwin95も終了する。・・・どないせいちゅうねん???
という・・・遊び?Hack?トラブる記録?・・・をネット上の記事で見つけて試した思い出がある。
一見しょうもない遊びだが、systemの起動の仕組みの一端を実感できるという点で
まことに有意義であった。天晴。
で、ちょっとLinuxで似たような事を実験しておくと後輩に教えるときのサンプルになるかもしれないのでやってみる。
とりあえず、この前用意したqemu環境で。
linuxが最初に実行するユーザ空間プロセス
細かい話は以下の書籍を見ればいいとして:
要は、linux-2.6.28/init/main.c の init_post()の最後の方:
817 /* 818 * We try each of these until one succeeds. 819 * 820 * The Bourne shell can be used instead of init if we are 821 * trying to recover a really broken machine. 822 */ 823 if (execute_command) { 824 run_init_process(execute_command); 825 printk(KERN_WARNING "Failed to execute %s. Attempting " 826 "defaults...\n", execute_command 826 ); 827 } 828 run_init_process("/sbin/init"); 829 run_init_process("/etc/init"); 830 run_init_process("/bin/init"); 831 run_init_process("/bin/sh"); 832 833 panic("No init found. Try passing init= option to kernel."); 834 }
ここで規定の実行ファイルを順番にTryしていくわけだ。
で、ポイントは
823 if (execute_command) { 824 run_init_process(execute_command); 825 printk(KERN_WARNING "Failed to execute %s. Attempting " 826 "defaults...\n", execute_command 826 ); 827 }
ここで、 execute_command に、kernel command-line で
"init=" option で渡ってきたパスが入っていたりする。
あと、initramfsを使用していた場合はもう少し上のライン
811 if (ramdisk_execute_command) { 812 run_init_process(ramdisk_execute_command); 813 printk(KERN_WARNING "Failed to execute %s\n", 814 ramdisk_execute_command); 815 }
ramdisk_execute_commandに"/init"が入ったりする。
実験1 "Hello again" メッセージを表示しかしないシステムの構築
shellから
$ cd $BOARD_DIR $ mkdir initram.d $ cd initram.d $ nano -cw hello.c
として、hello.cにお約束のあのフォーマットを。
#includeint main(int argc, char** argv) { printf("Hello again, linux!\n"); return 0; }
然る後shellから
$ arm-none-linux-gnueabi-gcc -static -o init hello.c
ライブラリ準備が面倒なので -static にしているのがポイント。
次いで、最低限コンソールのスペシャルファイルが無いとprintfの出力先がないので(要確認)
$ mkdir dev $ sudo mknod dev/console c 5 1
こんなとこか?
$ tree ./ ./ |-- dev | `-- console |-- hello.c `-- init 1 directory, 3 files
で、今作った最低限過ぎるrootfsをinitramfsに取り込んだkernelの製作
$ cd $KBUILD_DIR $ make menuconfig
で、makeしてqemuで確認
$ make .....(略)..... $ qemu-system-arm -nographic -M versatileab \ -kernel ./arch/arm/boot/zImage \ -append "console=ttyAMA0 root=/dev/ram" -m 64
を〜、できた。
実験2 "No init found." errorメッセージを見よう
linux-2.6.28/init/main.c の init_post() のコードから、
すべてのTryに失敗すると
833 panic("No init found. Try passing init= option to kernel.");
このエラーメッセージが得られるはずである。
状況として、さっきのルートファイルシステムで
/init が実行不能だった場合が思いつく・・・
では作業開始。
$ cd ${BOARD_DIR}/initram.d $ chmod -x init $ cd $KBUILD_DIR $ make $ qemu-system-arm -nographic -M versatileab \ -kernel ./arch/arm/boot/zImage \ -append "console=ttyAMA0 root=/dev/ram" -m 64
よしよし、できた。
今日のまとめ
knowhow:
- カーネル初期化終了後、ユーザランドの最初のプロセスを呼び出す仕組みは(比較的)単純。
- linux/init/main.c を起点にcodeを追える。
- 規定の"init"がいるわけでなく、hello worldでもinitって名前にしてしまえば動く。
- 独自initを作成できたり、system customize の幅は広い。
Followup:
- 今使ってるkernelの.config等について記録しておく