ソースコード・リテラシーのススメ

第13回起動の仕組みを読む

前回は/etc/rc.d/rc.sysinitという起動時に実行されるスクリプトを読んでみました。その際に簡単に触れたように、ほとんどのLinuxディストリビューションではsysvinitと呼ばれるソフトウェアを使ってシステムの起動処理を行っています。

このsysvinitの処理を含めた起動時の流れを把握しておけば、何らかのトラブルが発生して正しく起動しなくなった場合にも原因追求が容易になるでしょう。そこで今回はsysvinitが起動するまでの流れを解説し、その際に使用される各種設定ファイルを読んでみます。

Linuxの起動の流れ

あらかじめお断りしておきますが、筆者はx86系(いわゆるIBM-PC互換機)以外のハードウェア(組み込み環境とか大型コンピュータとか)については無知なので、今回紹介する話題はx86系のハードウェアを前提とした話になります。カーネルの起動以後の処理はそれほど違いはないはずですが、カーネルをロードするための仕組みはハードウェア環境に大きく依存しているため、組み込み用のLinuxや大型コンピュータ用のLinuxでは今回紹介するgrubとは異なる、それぞれ独自の仕組みが用意されているはずです。本連載ではそれらについては扱わないので別途適切な資料を探してください。

さて、いわゆるPC互換機の環境では、システムの起動は「ブートローダ段階⁠⁠、⁠カーネル段階⁠⁠、⁠/sbin/init段階」の3つのステップに分けることができます。ブートローダ段階とは、電源ONからliloやgrubといったブートローダソフトウェアが起動し、指定されたカーネルイメージをメモリに読み込む段階、カーネル段階とはメモリ上に読み込まれたカーネルが起動し、CPUやメモリ、周辺機器の認識や初期化を行う段階、/sbin/init段階とはハードウェアの初期化を終えたカーネルから処理を委ねられた/sbin/initが/etc/inittabの設定に従ってシステムの動作に必要な各種サービスを起動していく段階を言います。以下ではこれら3つのステップそれぞれについて、制御する設定ファイルなどを読みながら紹介していくことにします。

ブートローダ段階

PC互換機の電源を入れると、まずマザーボード上のフラッシュメモリに書き込まれたBIOS(Basic Input/Output System)と呼ばれるソフトウェアが動き始めます。BIOSはマザーボード上のCPUやメモリ、接続されている各種拡張カードを認識、初期化していきます。接続されたハードウェアの初期化処理を完了すると、最初のHDDの先頭のセクタMBR:マスターブートレコードに書き込まえたソフトウェアを起動して以後の処理を委ねます。このMBRの512バイトの部分に書き込まれているのがブートローダと呼ばれるソフトウェアです。

ブートローダはOSごとに異なり、Linuxの中でもliloやgrubなどいくつかの種類がありますが、今回は最も広く使われているgrubを取りあげます。

grubは起動するカーネルやカーネルパラメータを対話的なメニューから指定したり、さまざまな種類のファイルシステムを理解してカーネルをファイル名で読み出すことができるなど高機能なブートローダです。一方、MBRは512バイト分しか使えないので大規模なソフトウェアを保存することはできません。そのためgrubでは機能を分割し、本来の機能はstage2と呼ばれる部分に置いてファイルシステム上に保存し、MBRにはstage2を読み込むだけの機能を持ったstage1と呼ばれる部分を置くようになっています。

MBR上のstage1がHDD上のstage2を正しく読み込めば、stage2はgrub.confという設定ファイルを使って起動メニューを表示します。以下では前回同様、Fedora Core9のβ版の環境を元に設定ファイルの内容を紹介します(リスト1⁠⁠。Fedora Coreではgrub.confは/boot/grub/grub.confに実体が置かれ、/etc/grub.confにそこへのシンボリックリンクが置かれています。

リスト1 grub.confの内容
 1  # grub.conf generated by anaconda
 2  #
 3  # Note that you do not have to rerun grub after making changes to this file
 4  # NOTICE:  You have a /boot partition.  This means that
 5  #          all kernel and initrd paths are relative to /boot/, eg.
 6  #          root (hd0,0)
 7  #          kernel /vmlinuz-version ro root=/dev/VolGroup00/LogVol00
 8  #          initrd /initrd-version.img
 9  #boot=/dev/sda
10  default=0
11  timeout=5
12  splashimage=(hd0,0)/grub/splash.xpm.gz
13  #hiddenmenu
14  
15  title Fedora (2.6.25-0.163.rc7.git1.fc9.i686)
16          root (hd0,0)
17          kernel /vmlinuz-2.6.25-0.163.rc7.git1.fc9.i686 ro root=/dev/VolGroup00/LogVol00 rhgb quiet
18          initrd /initrd-2.6.25-0.163.rc7.git1.fc9.i686.img
19  title Fedora (2.6.25-0.121.rc5.git4.fc9)
20          root (hd0,0)
21          kernel /vmlinuz-2.6.25-0.121.rc5.git4.fc9 ro root=UUID=7b908992-10bd-46d3-95b6-1e04548399ed rhgb quiet
22          initrd /initrd-2.6.25-0.121.rc5.git4.fc9.img

grub.confも他のほとんどの設定ファイル同様、#(シャープ)以降は行末までコメントと解釈されるので、1行目から9行目、13行目はコメントとして無視されます。コメント以外の部分は大きく2つに分けることができ、10行目から12行目がgrubの動作全体を制御する設定、15行目からが起動すべきカーネルごとの設定になります。

10行目のdefault=0は、起動すべきカーネルが指定されなかった場合に起動するカーネルの指定で、数字はtitle行で指定された順番を意味し、0 ならば最初に現われたtitle行(今回は15行目)のカーネルを起動します。

timeout=5はメニュー画面を5秒間表示する指定、splashimage=(hd0,0)/grub/splash.xpm.gzはメニュー画面の背景に表示する画像ファイルの指定です。ここにも出てきていますが、grubではHDDやパーティションを/dev/hda1のような形ではなく、(hd0,0)(最初のHDDの最初のパーティション)の形式で指定しますのでご注意ください。

15行目からが起動すべきカーネルの設定で、15行目から18行目と19行目から22行目がそれぞれ異なる2つのカーネルを起動するための設定になります。

title行はgrubのメニュー画面に表示されるメッセージになると同時に、起動すべきカーネルとその設定を区別するためのラベルになります。

root行はルートパーティションになるパーティションの指定で、今回の例では(hd0,0)で最初のHDDの最初のパーティションを指定しています。この設定によりカーネルのファイル名を/vmlinuz-2.6.25.0...のように、パス名で指定できるようになります。

kernel行は起動するカーネルの指定で、(hd0,0)で指定されたパーティションの直下にあるカーネルイメージファイル/vmlinuz-2.6.25-0.163.rc7.git1.fc9.i686をメモリに読み込むことを指定しています。カーネルのファイル名の指定以降は読み込んだカーネルに渡すパラメータの設定で、ルートファイルシステムとそのマウント方法(ro root=/dev/VolGroup00/LogVol00⁠⁠、起動時にはグラフィカルなブート画面を表示し(rhgb⁠⁠、詳しいメッセージは表示しない(quiet)ことをカーネルに指示しています。

initrd行はカーネルと共に読み込む初期化用ramdisk(INITtial RamDisk)イメージの指定で、上記の例ではカーネルと同じ場所にある/initrd-2.6.25-0.163.rc7.git1.fc9.i686.imgというファイルを指定しています。この初期化用ramdiskはシステムのインストール時に作成され、HDDやルートファイルシステムを読み込む際に必要となるモジュールドライバが組み込まれています。

grubはこの設定ファイルに従って指定されたカーネルと initrdをメモリ上に読み込み、指定されたパラメータをカーネルに渡して以後の処理をカーネルに委ねます。

カーネル段階

ブートローダから処理を委ねられたカーネルは、まず初期化用ramdiskを展開、マウントして、ルートファイルシステムをマウントするために必要なモジュールドライバ類を組み込みます。

初期化用ramdiskから組み込まれるモジュールドライバは使用しているSCSIアダプタやUSBコントローラに応じたドライバ、LVM構築に必要なドライバ、ext3ファイルシステム用のドライバなどです。なお、SCSIアダプタやUSBコントローラなどのハードウェア環境に応じたドライバは、インストーラがシステムインストール時に検出したハードウェアを元に用意されるので、initrdファイルをハードウェア構成が異なる環境に持っていくと動作しない場合があります。

初期化用ramdiskから必要なモジュールドライバを組み込んだカーネルは、それらのドライバも利用しながらハードウェアの認識や初期化を進めていきます。カーネルには動作を調整するための設定ファイルはありませんが、ブートローダから渡されるカーネルパラメータでさまざまな調整が可能になっています。

起動されたカーネルはCPUやメモリ、接続されている周辺機器の認識や初期化を行い、最終的にはカーネルパラメータで指定されたルートパーティションをマウントして、そこにある /sbin/initを起動し、その後の処理を委ねます。

/sbin/init段階

ハードウェアの認識、初期化を終了したカーネルは/sbin/initを起動し、その後の処理を委ねます。/sbin/initに処理を委ねたカーネルは表の世界からは姿を消し、以後は黒子として世界を支える仕事に徹します。

一方、/sbin/initは全てのプロセスの祖として必要なプロセスを起動していきますが、その際に利用する設定ファイルが/etc/inittab(リスト2)です。

リスト2 /etc/inittabの内容
 1  #
 2  # inittab       This file describes how the INIT process should set up
 3  #               the system in a certain run-level.
 4  #
 5  # Author:       Miquel van Smoorenburg, 
 6  #               Modified for RHS Linux by Marc Ewing and Donnie Barnes
 7  #
 8  
 9  # Default runlevel. The runlevels used by RHS are:
10  #   0 - halt (Do NOT set initdefault to this)
11  #   1 - Single user mode
12  #   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
13  #   3 - Full multiuser mode
14  #   4 - unused
15  #   5 - X11
16  #   6 - reboot (Do NOT set initdefault to this)
17  # 
18  id:5:initdefault:
19  
20  # System initialization.
21  si::sysinit:/etc/rc.d/rc.sysinit
22  
23  l0:0:wait:/etc/rc.d/rc 0
24  l1:1:wait:/etc/rc.d/rc 1
25  l2:2:wait:/etc/rc.d/rc 2
26  l3:3:wait:/etc/rc.d/rc 3
27  l4:4:wait:/etc/rc.d/rc 4
28  l5:5:wait:/etc/rc.d/rc 5
29  l6:6:wait:/etc/rc.d/rc 6
30  
31  # Trap CTRL-ALT-DELETE
32  ca::ctrlaltdel:/sbin/shutdown -t3 -r now
33  
34  # When our UPS tells us power has failed, assume we have a few minutes
35  # of power left.  Schedule a shutdown for 2 minutes from now.
36  # This does, of course, assume you have powerd installed and your
37  # UPS connected and working correctly.  
38  pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"
39  
40  # If power was restored before the shutdown kicked in, cancel it.
41  pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"
42  
43  
44  # Run gettys in standard runlevels
45  1:2345:respawn:/sbin/mingetty tty1
46  2:2345:respawn:/sbin/mingetty tty2
47  3:2345:respawn:/sbin/mingetty tty3
48  4:2345:respawn:/sbin/mingetty tty4
49  5:2345:respawn:/sbin/mingetty tty5
50  6:2345:respawn:/sbin/mingetty tty6
51  
52  # Run xdm in runlevel 5
53  x:5:respawn:/etc/X11/prefdm -nodaemon

/etc/inittabも他の設定ファイル同様、#から行末までがコメントと見なされるので、17行目まではコメント、18行目が最初の設定行です。

/etc/inittabでは1つの行が1つの設定になっており、各行は :(コロン)で4つの欄に分割されています。各欄の意味は、最初の欄がそれぞれの設定を区別するためのラベル、2つめがその設定が動作するランレベルの指定、3つめがどのような動作を行なうかの指定、4つめが起動するプロセスの指定になります。

前回も簡単に触れましたが、⁠ランレベル」というのはシステムの動作モードを意味し、レベルによって設定する機能やサービスを変えて、システムを安全かつ効率よく運用するための仕組みです。それぞれのレベルの意味はディストリビューションによって多少異なりますが、ランレベル0がシステムの停止モード、6が再起動モード、1がシングルユーザモードというのは共通しています。

それ以外のモードをどのように使うかはディストリビューションごとに異なり、Red Hat系のディストリビューションでは、9行目から17行目に記されているように、2が(セキュリティ等の問題がある場合に)NFS機能を使わないマルチユーザモード、3が標準のマルチユーザモード、4は未使用で、5がログイン時にもX Window Systemを用いるGUIモードになります。

3つめの動作欄にinitdefaultと指定した18行目がデフォルトのランレベルの設定で、ここでは5がデフォルトのランレベルとなります。

動作欄にsysinitと指定した21行目がシステムの初期化処理の指定です。/sbin/initはこの行を見てシステムの初期化のために/etc/rc.d/rc.sysinitを起動します。

23行目から29行目が /etc/rc.d/rc.sysinitを実行してから、それぞれのランレベルで実行すべき処理の指定です。具体的には、それぞれのランレベル(0から6)を引数に /etc/rc.d/rcというスクリプトを実行するようになっています。これらの行では動作欄(3つめの欄)が wait とされており、指定したランレベルになった際に 4 つめの欄に指定したプロセスを実行し、プロセスの実行終了まで待つことを意味します。この処理により、それぞれのランレベルに応じたサービス(プロセス)を起動します。

32行目、38行目、41行目はそれぞれ特定の状態になった際の動作の指定です。32行目は、いわゆる「三本指の挨拶(three fingers salute⁠⁠」と呼ばれるctrl+alt+deleteの3つのキーが同時に押された際の動作の指定で、(now)から3秒待ってから(-t3⁠⁠、シャットダウン後再起動するように(-r)指定して/sbin/shutdownコマンドを起動します。

38行目はUPSから電源断の信号が送られてきた際の処理で、2分後に(+2⁠⁠、"Power Failure; System Shutting Down" というメッセージをログファイルに記録して、再起動ではなく停止状態になるように(-h)/sbin/shutdown を起動します。-f の指定は次回の起動時にはファイルシステムのチェック(fsck)を動かさないようにする指定です。

41行目は電源断の信号が届いてからシステムをシャットダウンするまでの2分間に電源回復を示す信号が届いた場合にシャットダウンをキャンセルするための処理です。

45行目から50行目は2から5までのランレベルの際に/sbin/mingettyというプロセスを起動してtty1からtty6までの端末を監視させる指定です。ランレベルは0が halt、6がrebootなので、それ以外のランレベル(動作用のランレベル)ではtty1からtty6の6つの端末からログインできるようになります。

これらの指定の動作欄に指定されているrespawnは、プロセス欄に指定したプロセスが終了すれば再度同じプロセスを起動することを意味します。この指定により、コンソール画面からログアウト(/sbin/mingetty を終了)しても、同じコンソール画面に再度ログインメッセージが表示(/sbin/mingettyの再起動)されるようになります。

53行目はX Window Systemを用いたログイン処理の指定で、/etc/X11/prefdmというコマンド(実体はシェルスクリプト)を用いて利用可能なディスプレイマネージャ(X用のログイン管理ソフトウェア)を起動しています。スクリプトの中では/etc/sysconfig/desktopの指定に基づいてGNOME用やKDE用のディスプレイマネージャを切り替えるようになっています。

以上、ざっと/etc/inittabの設定を眺めてきましたが、この設定ファイルによる/sbin/initの動作を整理してみます。

/sbin/initはまず18行目でデフォルトのランレベルが5であることを認識します。一方、21行目のsysinitの指定で、まず最初に/etc/rc.d/rc.sysinitを起動します。その後、デフォルトのランレベルである5に入って、ランレベル5の際に実行すべき処理を実行していきます。まず 28 行目で/etc/rc.d/rc 5を実行し、その終了まで待ちます(wait⁠⁠。

41行目にもランレベル5で実行すべき処理がありますが、この処理はpowerokwaitの指定でUPSから特別な信号が入ったという条件の際に実行することになります。

次に45行目から50行目の指定で/sbin/mingettyを6つの仮想端末に対して実行し、コンソールからのログインを待ちます。最後に53行目の指定により/etc/X11/prefdm経由でX Window System用のディスプレイマネージャを起動します。その際にはXウィンドウやGNOME/KDEといったデスクトップ環境も必要になるので、それらも合わせて起動されることになります。

前回紹介したrc.sysinitはこの/sbin/initの処理の流れの中で、システムの初期化用に実行される最初のスクリプトというわけです。

おすすめ記事

記事・ニュース一覧