ブートプロセスの裏側

Note注意
 

このセクションではとくにx86ブートプロセスについて検討します。 ユーザーのシステムのアーキテクチャによっては、ユーザーのブートプロセスがわずかに違うことがあります。 しかし、システムがカーネルを見つけてロードすれば、デフォルトのRed Hat Linuxブートプロセスはすべてのアーキテクチャでまったく同じです。 x86でないブートプロセスの詳細についてはthe section called 他のアーキテクチャの起動プロセスにおける違いを参照してください。

 コンピュータをブートするとき、プロセッサはシステムメモリの最後部で BIOS (Basic Input/Output System)を検索し、実行します。 BIOSプログラムは読み込み専用の不揮発メモリに書き込まれ、いつでも実行可能な状態になっています。 BIOSは周辺機器に対する最低レベルのインタフェースを提供し、ブートプロセスの最初のステップを制御します。

 BIOSはシステムをテストし、周辺機器の検索とチェックを行ない、つづいてシステムをブートするために使用するドライブを調べます。 BIOSは通常、フロッピーディスクドライブ(最近のシステムではCD-ROMドライブも)が存在すればそれをチェックし、次にハードディスクドライブを調べます。 ブートするために使用されるドライブの順序は、通常、そのシステム上の具体的なBIOS設定によって制御されます。 Red Hat Linuxがシステムのハードディスクドライブにインストールされていると、BIOSは、先頭ハードディスクドライブの先頭セクタから マスタブートレコード (MBR)の検索を開始し、その内容をメモリにロードし、それに制御を渡します。

 ついでこのMBRコードは先頭のアクティブパーティションを探し、そのパーティションのブートレコードを読み込みます。 ブートレコードには、ブートローダー、つまり LILO ( LI Nux LO ader)のロード方法に関する命令が含まれています。 (MBRにLILOがインストールされていれば)、MBRは次にLILOをロードし、LILOはこのプロセスを引き継ぎます。 デフォルトのRed Hat Linux設定では、LILOはMBRの設定に基づいてブートオプションを表示し、実際に起動するオペレーティングシステムについてユーザー入力によって決めさせます。

 しかしこの場合、MBRが読み込まれたときに何をするかということをMBR内のLILOがどのようにして知るかということが問題になります。 実は、LILOはすでに Lilo/etc/lilo.conf 設定ファイルを使って、そこに命令を書き込んでいるのです。

/etc/lilo.conf内のオプション

 新しくインストールしたオペレーティングシステムをブートしなければならない場合や、新しいカーネルを使用するような場合を除いて、ハードディスクドライブ上のマスタブートレコードを変更する必要はほとんどないでしょう。 LILOを使用するが別の設定を使用する新しいMBRを作成する必要が生じた場合は、 /etc/lilo.conf を編集し、再度 Lilo を実行しなければなりません。

Warning警告
 

もし /etc/lilo.conf を編集するのであれば、少しでも変更を加える前に必ずこのファイルのバックアップコピーを作成しておきます。 また必ず作業用ブートフロッピーを用意しておき、問題がある場合にシステムをブートしてMBRに変更を加えることができるようにしておきます。 ブートディスクの作成に関する詳細については、マニュアルの Mkbootdisk に関するページを参照してください。

 ファイル /etc/lilo.confLilo コマンドで使用されます。これによって、どのオペレーティングシステムを利用するか、どのカーネルを起動するかが決められ、またシステムのインストール先が決められます(たとえば先頭のIDEハードディスクドライブである /dev/had など)。 サンプルファイル /etc/lilo.conf は以下のようになっています。

boot=/dev/had
map=/boot/map
install=/boot/boot.b
Prompt
timeout=50
message=/boot/message
lba32
default=linux

image=/boot/vmlinuz-2.4.0-0.43.6
label=linux
initrd=/boot/initrd-2.4.0-0.43.6.img
read-only
root=/dev/hda5

other=/dev/hda1
label=dos

 この例は、2つのオペレーティングシステム、Red Hat LinuxとDOSをブートするように設定されているシステムを示しています。 以下に、このファイルの数行をもっと詳しく調べてみましょう(ユーザーの /etc/lilo.conf はすこし違うかもしれません)。

 次にLILOは、ブートするように設定されている別のオペレーティングシステムまたはカーネルのRed Hat Linux初期画面を表示します。 Red Hat Linuxだけしかインストールされていず、 /etc/lilo.conf にいっさい変更が加えられていない場合は、 Linux が唯一のオプションとして表示されます。 他のオペレーティングシステムもブートできるようにLILOをセットアップしている場合は、この画面でどのオペレーティングシステムをブートするかを選ぶことになります。 ブートしようとするオペレーティングシステムを矢印キーで強調表示にし、 Enter キーを押します。

  LILOに対するコマンドをコマンドプロンプトで入力したい場合は、 Cntl-X キーを押します。 LILOは画面に LILO: プロンプトを表示し、既定の時間のあいだユーザーによる入力を待機します。 (LILOが待機する時間は /etc/lilo.conf ファイルの Timeout 行で設定されます。) ユーザーの /etc/lilo.conf がLILOでオペレーティングシステムを選択できるように設定されていると、ここでブートしようとするオペレーティングシステムのラベルを入力することができます。

 LILOが Linux をブートする場合は、まずカーネルがメモリにロードされますが、そのカーネルは /boot ディレクトリにある Vmlinuz ファイル(これにバージョンナンバーがついて、例えば) located in the Vmlinuz-2.4.0-xx など)です。 つづいてカーネルは制御を Init に渡します。

 この時点で、カーネルがメモリにロードされて作動状態になり、Linux が非常に基礎的なレベルですが始動しています。 しかし、カーネルを利用しているアプリケーションはなく、ユーザーはシステムに対して何らか意味ある入力を行なうことができず、この段階でできることは非常に限られています。 Init プログラムが、システムがその役割をはたすことをサポートする、各種のサービスを起動することによって、この問題を解決します。

Init

  カーネルは Init/sbin で見つけ、それを実行します。これ以後は Init がひきついでブートプロセスを処理します。

  Init が始動すると、それは、ユーザーのRed Hat Linuxシステム上で自動的に起動するすべてのプロセスの親または親の親になります。 それはまず、 /etc/rc.d/rc.sysinit スクリプトを実行します。これはパスの設定、スワッピングの開始、ファイルシステムのチェックなどを実行します。 基本的に rc.sysinit は、システム初期化時に行なっておく必要のあるすべてのことを取り扱います。 たとえば、ネットワークに接続したシステムの場合、 rc.sysinit/etc/sysconfig/network ファイルの情報を使用してネットワークプロセスを初期化します。 ほとんどのシステムはクロックを使用するので、 rc.sysinit/etc/sysconfig/clock ファイルを使用してクロックを初期化します。 初期化する必要のあるシリアルポートがある場合には、 rc.sysinitRc.serial を実行することもあります。

 次に Init は、 /etc/inittab スクリプトを実行します。このファイルには、各 ランレベル でシステムをどのようにセットアップするかが記述され、デフォルトのランレベルが設定されています。 (init ランレベルの詳しいことについてはthe section called Init実行レベル を参照してください。) このファイルには、ランレベルの開始時には必ず /sbin/update を実行する必要があることが記述されています。 Update プログラムは、変更されたデータを含むバッファをディスクへフラッシュバックします。

 ランレベルが変化すると、 Init/etc/rc.d/init.d のスクリプトを使用して、ユーザーのウェブサーバやDNSサーバなどのような様々なサービスの起動と停止を必ず行ないます。 まず、 Init はシステム用のソースファンクションライブラリを設定します(一般には /etc/rc.d/init.d/functions )。このライブラリには、プログラムをStart/Killする方法やプログラムのPIDを見つける方法が記述されています。 次に、 Init は現在のランレベルと直前のランレベルを調べます。

 つづいて、 Init は、そのランレベル用の Rc ディレクトリ( /etc/rc.d/rc <x> .d< X > は0-6の番号が付いている)を検索して、システムが動作するために必要なバックグラウンドプロセスをすべて起動します。 Init はあらゆるKillスクリプト(ファイル名は K で始まっている)を Stop パラメータで実行します。 次に、 Init は適切なランレベルディレクトリのすべてのStart スクリプト(ファイル名は S で始まっている)を Start で実行し、こうしてすべてのサービスとアプリケーションが正しく起動されます。 また、システムのブートが終了してから、ルートとしてログインする /etc/rc.d/init.d/httpd stopservice httpd stop のようなコマンドによって、これらの同じスクリプトを手動で実行することができます。 これで Httpd サーバを停止します。

Note注意
 

 サービスを手動で起動するとき、ユーザーはルートでなければなりません。 service httpd stop を実行するとエラーになるのであれば、/root/.bashrc (あるいはユーザー常用シェルのための正しい.rc )に/sbin をパッチしていないかもしれません。 /sbin/service httpd stop という完全なコマンドを入力するか、あるいはexport PATH="$PATH:/sbin"をユーザーのシェル.rcファイルに追加しなければなりません。 ユーザーのシェル設定ファイルを編集する場合は、ログアウトしてからルートとしてログインして、変更したシェル設定ファイルを有効にします。

 サービスの起動と停止を行なうスクリプトはいずれも、実際には/etc/rc.d/init.dには存在しません。 /etc/rc.d/rc<x>.d中のすべてのファイルは、/etc/rc.d/init.d中に存在する実際のスクリプトを指すシンボリックリンクです。 シンボリックリンクは別のファイルを指しているファイルにすぎません。シンボリックリンクがここで利用されているのは、これらを作成したり、削除したりしても、サービスのKillやStartを行なう実際のスクリプトに影響が及ばないからです。 さまざまなスクリプトに対するシンボリックリンクは特定の順序で番号が付いており、それらのスクリプトはその順序でStartします。 サービスのStartやKillを実際に行なうスクリプトを指示するシンボリックリンクの名前を変えることによって、サービスがStartしたり、Killされたりする順序を変更することができます。 特定のサービスを別のサービスの直後や直前に起動したり、停止したりする場合は、当該のサービスのシンボリックリンクに別のサービスのシンボリックリンクと同じ番号を与えます。

 たとえばランレベル5の場合、init/etc/rc.d/rc5.dディレクトリを調べ、以下のような内容を見いだすかもしれません(ユーザーのシステムと設定によって異なります)。

K01pppoe -> ../init.d/pppoe
K05innd -> ../init.d/innd
K10ntpd -> ../init.d/ntpd
K15httpd -> ../init.d/httpd
K15mysqld -> ../init.d/mysqld
K15pvmd -> ../init.d/pvmd
K16rarpd -> ../init.d/rarpd
K20bootparamd -> ../init.d/bootparamd
K20nfs -> ../init.d/nfs
K20rstatd -> ../init.d/rstatd
K20rusersd -> ../init.d/rusersd
K20rwalld -> ../init.d/rwalld
K20rwhod -> ../init.d/rwhod
K25squid -> ../init.d/squid
K28amd -> ../init.d/amd
K30mcserv -> ../init.d/mcserv
K34yppasswdd -> ../init.d/yppasswdd
K35dhcpd -> ../init.d/dhcpd
K35smb -> ../init.d/smb
K35vncserver -> ../init.d/vncserver
K45arpwatch -> ../init.d/arpwatch
K45named -> ../init.d/named
K50snmpd -> ../init.d/snmpd
K54pxe -> ../init.d/pxe
K55routed -> ../init.d/routed
K60mars-nwe -> ../init.d/mars-nwe
K61ldap -> ../init.d/ldap
K65kadmin -> ../init.d/kadmin
K65kprop -> ../init.d/kprop
K65krb524 -> ../init.d/krb524
K65krb5kdc -> ../init.d/krb5kdc
K75gated -> ../init.d/gated
K80nscd -> ../init.d/nscd
K84ypserv -> ../init.d/ypserv
K90ups -> ../init.d/ups
K96irda -> ../init.d/irda
S05kudzu -> ../init.d/kudzu
S06reconfig -> ../init.d/reconfig
S08ipchains -> ../init.d/ipchains
S10network -> ../init.d/network
S12syslog -> ../init.d/syslog
S13portmap -> ../init.d/portmap
S14nfslock -> ../init.d/nfslock
S18autofs -> ../init.d/autofs
S20random -> ../init.d/random
S25netfs -> ../init.d/netfs
S26apmd -> ../init.d/apmd
S35identd -> ../init.d/identd
S40atd -> ../init.d/atd
S45pcmcia -> ../init.d/pcmcia
S55sshd -> ../init.d/sshd
S56rawdevices -> ../init.d/rawdevices
S56xinetd -> ../init.d/xinetd
S60lpd -> ../init.d/lpd
S75keytable -> ../init.d/keytable
S80isdn -> ../init.d/isdn
S80sendmail -> ../init.d/sendmail
S85gpm -> ../init.d/gpm
S90canna -> ../init.d/canna
S90crond -> ../init.d/crond
S90FreeWnn -> ../init.d/FreeWnn
S90xfs -> ../init.d/xfs
S95anacron -> ../init.d/anacron
S97rhnsd -> ../init.d/rhnsd
S99linuxconf -> ../init.d/linuxconf
S99local -> ../rc.local

 これらのシンボリックリンクはinitに、以下のプロセスをkillする必要があると指示します。 PppoeInndNtpdHttpdMysqldPvmdRarpdBootparamdNfsRstatdRusersdRwalldRwhodSquidAmdMcservYppasswddDhcpdSmbVncserverArpwatchNamedSnmpdPxeRoutedMars-newLdapKadminKpropKrb524Krb5kdcGatedNscdYpservUpsIrda 。 すべてのプロセスがkillされると、initは同じディレクトリを除いて以下のプロセスのスタートスクリプトを検索します。 KudzuReconfigIpchainsPortmapNfslockAutofsRandomNetfsApmdIdentdAtdPcmciaSshdRawdevicesXinetdLpdKeytableIsdnSendmailGpmCannaCrondFreeWnnXfsAnacronRhnsdLinuxconf 。 最後にinit/etc/rc.d/rc.localを実行し、このホストのために設定された特別なスクリプトがあれば実行させます。 この時点ではシステムはランレベル5と考えられます。

 initがすべてのランレベルを経過すると、/etc/inittabは各ランレベル用の仮想コンソール(ログインプロンプト)についてgettyプロセスをフォークします(ランレベル2〜5では6個すべてを取得します。シングルユーザーモードであるランレベル1は、コンソールを1つだけ獲得します。ランレベル0と6は仮想コンソールを獲得しません。) 基本的に、gettyはttyラインを開き、そのモードを設定し、ログインプロンプトをプリントし、ユーザー名を取得し、そしてそのユーザーのログインプロセスを始動します。 これにより、ユーザーはシステムへの認証を獲得し、システムを使用できるようになります。

 また/etc/inittabinitに、コンソールでユーザーがCtrl-Alt-Deleteキーを打った場合の対処のしかたを指示します。 Red Hat Linuxではそのような場合すぐに電源切断−再投入を行わず、適切にシャットダウン−リスタートを適切に行わなければならないので、ユーザーがこれらのキーをたたくと、init に対しコマンド/sbin/shutdown -t3 -rnowを実行するように指示されます。 さらに、/etc/inittabには、システムにUPSユニットが接続されている場合、電源故障発生時にinitがなすべきことが書かれています。

 ランレベル5では、/etc/inittab/etc/X11/prefdmというスクリプトを実行します。 prefdmスクリプトは、/etc/sysconfig/desktopディレクトリの内容に基づき、優先のXディスプレイマネジャー(ユーザーがGNOMEを実行中であればgdm、KDEを実行中ならkdm、AnotherLevelを実行中であればxdm)を実行します。

 ここではユーザーはログインプロンプトによく注意していなければなりません。 これらすべてのことが行われるのに2,3秒しかかからないのです。

SysV Init

 これまで見てきたようにinitプログラムはブート時にカーネルによって実行されます。 このプログラムの仕事は、システムと一緒に立ち上げなければならないノーマルなプロセスのすべてを起動することです。 このようなプロセスにはユーザーをログインさせるgettyプロセス、NFSデーモン、FTPデーモン、その他ユーザーがマシンのブート時に実行しようとするものがすべて含まれます。

 SysV initはLinuxワールドで標準の、ブート時のソフトウェアの起動を制御するプロセスです。(標準になっているのは)従来のBSDinitよりもパワフルブでフレキシブルだからです。

 SysV initは設定ファイルが直接/etcに常駐しているのではなく/etc/rc.dに入っているという点でも、BSD initと違います。 /etc/rc.dにはrcrc.localrc.sysinit、さらに以下のディレクトリがあります。

init.d
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d

 SysV initは、各intランレベルをそれぞれ独立のディレクトリで表し、実際にシステムがランレベル間を移動するときに、各ディレクトリの中のinitとシンボリックリンクを使ってサービスの停止/起動を行います。

 要約すれば、SysV initブートのためのイベントチェーンは以下のようになります。

  デフォルトランレベルは/etc/inittab内に指設定されています。 先頭付近に以下のような行があるはずです。

id:3:initdefault:

  この例ではデフォルトランレベルは最初のコロンの後ろの数字、つまり3です。 変更したい場合は、/etc/inittabを手作業で編集することができます。 inittabファイルを編集する際には十分注意してください。 ミスをした場合には、再起動し、Cntl-Xキーでboot:プロンプトにアクセスし、以下のように入力すれば修正することができます。

boot:
Linux single

  こうすれば当然シングルユーザーモードで起動するはずですから、inittabを再度編集して元の値に戻すことができます。

  次に、起動時にさまざまなシステムサービスに使用されるパラメータを定義している、/etc/sysconfig内のファイルに含まれる情報について検討します。