2017年06月16日

システムの起動

システムの起動の流れをざっくりと書くと、

電源ON・電源スイッチONでパワーコネクタの PS_ON の信号をONにする。
・電源ユニットは PS_ON の信号を受けるとパソコン各部に電源供給を開始。
・給電直後は電圧が不安定なので、クロック回路がCPUへのリセット信号をONにしCPUの処理を抑止する。
・電源出力が安定すると PWR_OK の信号をONにする。
・クロック回路は PWR_OK の信号を受けるとリセット信号をOFFにしCPUの動作を許可する。
BIOSの起動・M/BのフラッシュROMに書き込まれたBIOS(Basic Input/Output System)というソフトウェアが起動。
・POST(Power On Self Test)実行。
・起動するためのデバイスを探す。
MBRをメモリ上にロードし、MBR領域にあるプログラムに制御を移す。
1st Boot Loaderパーティションテーブルから起動フラグのあるパーティションを探す。
・IPL(Initial Program Loader)に制御を移す。
2nd Boot Loader・ブートイメージ(vmlinuz)をメモリ上にロード。
・イニシャルRAMディスク(initrd)をメモリ上にロード。
・制御をカーネルに移す。
カーネルの起動・ハードウェアリソースの初期設定。
・ルートファイルシステムのマウント。
・初期化プロセス。



Windowsでも、Linuxでも、だいたいこんな感じ。
が、もはやBIOSではなく、UEFIになっている。BIOSは古いもの。


とは言っても、基本となる流れのイメージは大きくはずれてないので、
抽象的にはOKとして、もっとざっくりと書くと、

電源ON
起動プログラムの実行
Boot Loaderの実行
カーネルの起動


「Boot Loaderの実行」は、Ubuntuの標準であれば「GRUB2の実行」となる。
GRUBは、/boot が存在するパーティションのファイルシステムをマウントするためのドライバを持ち、ブートイメージ(/boot/vmlinuz-*.*.*)とイニシャルRAMディスク(/boot/initrd.img-*.*.*)をメモリ上にロードすることができる。






ブートイメージについて


ブートイメージは、"vmlinuz-*.*.*"。

古くから、UNIXのカーネルイメージはunixというファイル名であり、cp (作成したイメージのファイル名) /unixのようにコピーしてインストールされていた。BSDで仮想記憶が実装され、仮想記憶をサポートしたカーネルであることを示すvm-という接頭辞を付けvmunixという名前が使われるようになった。vmlinuxという名前はそのvmunixを基にした命名法によるものである。さらにLinuxでは圧縮イメージという機能も追加され、vmlinuzという名前が付けられた。vmlinuxを圧縮したものであることを表す文字z(zipped)を後尾に付している。
引用:Wikipedia



vmlinuzのファイルタイプを見てみる。
$ sudo file vmlinuz-4.4.0-79-generic
vmlinuz-4.4.0-79-generic: Linux kernel x86 boot executable bzImage, version 4.4.0-79-generic (buildd@lcy01-30) #100-Ubuntu SMP Wed May 17 1, RO-rootFS, swap_dev 0x6, Normal VGA


bzImageだった。
vmlinuz.png

bzImage内のカーネルイメージ "bvmlinux.out" を取り出してみる。
作業ディレクトを作って、そこで作業。

$ mkdir /tmp/work
$ cd /tmp/work



/boot/vmlinuz-*.*.* を コピーしてリネーム。read許可しておく。

$ sudo cp /boot/vmlinuz-4.4.0-79-generic ./vmlinuz
$ sudo chmod +r ./vmlinuz



カーネルイメージは gzip 圧縮されていて、自己解凍形式になっている。
gzipなので、gzipヘッダーを探す('1f 8b 08 00')。
odコマンドを使う。-Ad で表示されるオフセットの基数を10進数に、-tx1 で1byte単位で16進数出力。

$ od -Ad -tx1 vmlinuz | grep '1f 8b 08 00'

0018864 ac fe ff ff 1f 8b 08 00 00 00 00 00 02 03 ec fd


18864 byte から 4 byte目に発見。
つまりこのブートイメージでは、18864 + 4 = 18868 byte目からがカーネルイメージ。
18868 byte以降を抽出する。
バイナリファイルなので、dd コマンドで開始位置までskipし、それを zcat に渡して展開し、vmlinux ファイルとして保存する。

$ dd if=vmlinuz bs=1 skip=18868 | zcat > vmlinux

gzip: stdin: decompression OK, trailing garbage ignored


ファイルタイプを見てみる。
$ file vmlinux
vmlinux: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=29e4c9d663eaf39dc69806020ec82421b1086729, stripped


version を確認してみる。
バイナリデータだが、"Linux version"文字列は含まれているはずなので、
$ strings vmlinux | grep "Linux version"
Linux version 4.4.0-79-generic (buildd@lcy01-30) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) #100-Ubuntu SMP Wed May 17 19:58:14 UTC 2017 (Ubuntu 4.4.0-79.100-generic 4.4.67)



GRUBから起動されたカーネルは、
自分自身で展開し、次のinitrdファイルをマウントする。





イニシャルRAMディスクについて


initrdファイルと書いたが、実のところは、initramfs。

ブート時に、ルートファイルシステムをマウントするためには、格納先が様々な媒体だったり、RAIDや暗号化されていたりなどの仕組みから、読み出すためのドライバをサポートしていなければいけない。
すべてに対応していくとカーネルが巨大化してしまう。
そこで、カーネルにドライバの対応コード追加していくことを避け、ブートに必要なモジュール群をまとめたもの置いておく一時的なルートファイルシステム(ミニルート)を準備し、それをブート時にマウントして使う、initrd (Initial RAM Disk) という仕組みを作った。

ブートローダは、カーネルを起動するときに合わせて initrdイメージ をロードしたメモリアドレスも渡す。
カーネルは initrd イメージの先頭を読み込み、そのフォーマットを判断する。

initrd はブロックデバイス(RAM ディスク)をgzip圧縮したもの。
Linux 2.6からは、RAM上に直接ファイルシステムを作る ramfs を用いたinitramfsを導入。
initramfsはファイルシステムをcpio形式でアーカイブしたうえで、gzipで圧縮したファイル。


展開してみる。
また作業ディレクトを作って、そこで作業。

$ mkdir /tmp/initramfs
$ cd /tmp/initramf



/boot/initrd.img-*.*.* を コピーしてリネーム。

$ sudo cp /boot/initrd.img-4.4.0-79-generic ./initrd.img



中身を展開する。

$ zcat ./initrd.img | cpio -id

gzip: ./initrd.img: not in gzip format
cpio: 予期しない書庫終了です


あれ、gzip じゃないのか?

ファイルの先頭を見てみる。
$ hexdump -C -n 512 ./initrd.img

ADDRESS  00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 30 37 30 37 30 31 30 30 30 30 30 30 36 35 30 30 |0707010000006500|
00000010 30 30 34 31 45 44 30 30 30 30 30 30 30 30 30 30 |0041ED0000000000|
00000020 30 30 30 30 30 30 30 30 30 30 30 30 30 32 35 39 |0000000000000259|
00000030 33 37 37 45 34 36 30 30 30 30 30 30 30 30 30 30 |377E460000000000|
00000040 30 30 30 30 30 33 30 30 30 30 30 30 30 31 30 30 |0000030000000100|
00000050 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
00000060 30 30 30 30 30 37 30 30 30 30 30 30 30 30 6b 65 |00000700000000ke|
00000070 72 6e 65 6c 00 00 00 00 30 37 30 37 30 31 30 30 |rnel....07070100|
00000080 30 30 30 30 36 36 30 30 30 30 34 31 45 44 30 30 |000066000041ED00|
00000090 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
000000a0 30 30 30 30 30 32 35 39 33 37 37 45 34 36 30 30 |00000259377E4600|
000000b0 30 30 30 30 30 30 30 30 30 30 30 30 30 33 30 30 |0000000000000300|
000000c0 30 30 30 30 30 31 30 30 30 30 30 30 30 30 30 30 |0000010000000000|
000000d0 30 30 30 30 30 30 30 30 30 30 30 30 30 42 30 30 |0000000000000B00|
000000e0 30 30 30 30 30 30 6b 65 72 6e 65 6c 2f 78 38 36 |000000kernel/x86|
000000f0 00 00 00 00 30 37 30 37 30 31 30 30 30 30 30 30 |....070701000000|
00000100 36 37 30 30 30 30 34 31 45 44 30 30 30 30 30 30 |67000041ED000000|
00000110 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
00000120 30 32 35 39 33 37 37 45 34 36 30 30 30 30 30 30 |0259377E46000000|
00000130 30 30 30 30 30 30 30 30 30 33 30 30 30 30 30 30 |0000000003000000|
00000140 30 31 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0100000000000000|
00000150 30 30 30 30 30 30 30 30 31 35 30 30 30 30 30 30 |0000000015000000|
00000160 30 30 6b 65 72 6e 65 6c 2f 78 38 36 2f 6d 69 63 |00kernel/x86/mic|
00000170 72 6f 63 6f 64 65 00 00 30 37 30 37 30 31 30 30 |rocode..07070100|
00000180 30 30 30 30 36 38 30 30 30 30 38 31 41 34 30 30 |000068000081A400|
00000190 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
000001a0 30 30 30 30 30 31 35 39 33 37 37 45 34 36 30 30 |00000159377E4600|
000001b0 30 30 32 38 30 30 30 30 30 30 30 30 30 33 30 30 |0028000000000300|
000001c0 30 30 30 30 30 31 30 30 30 30 30 30 30 30 30 30 |0000010000000000|
000001d0 30 30 30 30 30 30 30 30 30 30 30 30 32 41 30 30 |0000000000002A00|
000001e0 30 30 30 30 30 30 6b 65 72 6e 65 6c 2f 78 38 36 |000000kernel/x86|
000001f0 2f 6d 69 63 72 6f 63 6f 64 65 2f 47 65 6e 75 69 |/microcode/Genui|


cpioのマジックナンバー "070701" が入っている。単なるcpio形式なのかな?

だったら、直接cpioで展開してみる。
$ cpio -id < ./initrd.img
22 ブロック


が、x86のマイクロコードらしきものしか展開されない。
$ tree kernel/

kernel/
└── x86
└── microcode
└── GenuineIntel.bin

2 directories, 1 file


それなら、また、gzipヘッダーを探してみる。

$ od -Ad -tx1 initrd.img | grep '1f 8b 08 00'

0011264 1f 8b 08 00 4a 7e 37 59 00 03 d4 3b 6b 57 db 48


見つかったので展開してみる。

$ dd if=initrd.img bs=1 skip=11264 | zcat > initrmfs

gzip: stdin: decompression OK, trailing garbage ignored


cpioで展開する。

$ cpio -id < ./initrmfs

197303 ブロック


ミニルートが現れた。
$ tree -d -L 1

.
├── bin
├── conf
├── etc
├── kernel
├── lib
├── lib64
├── run
├── sbin
├── scripts
├── usr
└── var

11 directories



このミニルートをブート時のルートファイルシステムとしてマウントし、
実際のルートファイルシステムをマウントするために必要なドライバをカーネルに読み込み、それから実際のルートファイルシステムをマウントする。

やっと、ルートファイルシステム内にあるファイルを用いて初期化が実行される。
次の初期化プロセスが完了すれば、Linuxが使えるようになる。





初期化プロセスについて


カーネルが起動した後、initプロセスが起動する。
init(/sbin/init が実行)は、他のすべてのプロセスを起動するプロセスであり,すべての親プロセスになる。
最初に起動するので、PIDは必ず1になる。
/etc/inittab ファイルに書かれた ランレベルを設定し、該当する/etc/rc.d/rc*を実行していく。

というのが、System V系のinit。


Ubuntuではinitの代わりとして、Ubuntu 6.10以降で導入された、Upstartが使われていたが、
Ubuntu 14.04
$ dpkg -S /sbin/init
upstart: /sbin/init

Ubuntu 15.04以降では systemdが採用された。
(参考:SystemdForUpstartUsers
Ubuntu 16.04
$ dpkg -S /sbin/init
systemd-sysv: /sbin/init


で、systemdによるシステム起動のプロセスを確認していたが、
新たな考え方が必要になってきたので、このあたりまでにしておいて、
こんな流れでLinuxが起動して、使えるようになる。




まとめ


ざっと吐き出してみて、全体的にぼんやりとであるが流れの確認ができた。
とは言え、自身の理解は表層しかわかっておらず、
足りない所も多く、間違っていることもあるだろうし、具体的な動きはブラックボックスのまま。
その辺りに、いつか踏み込む日が来たら、そのときに改めて理解しようと思う。

いまは、こんな感じなのか、ふーん・・ という程度でよしとしておこう。




posted by Zorinos at 18:00| Comment(0) | Linux | 更新情報をチェックする
この記事へのコメント
コメントを書く
コチラをクリックしてください
ブログランキング・にほんブログ村へ
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。