ブートプロセスの裏側

注意注意
 

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

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

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

このMBRには、インストールしたブートローダーに応じて、GRUBLILOLInux LOader)いずれかのロード方法に関する命令が含まれています。MBRにブートローダーがインストールされていれば、MBRは次にブートローダーをロードし、ブートローダーはこのプロセスを引き継ぎます。Red Hat Linuxのデフォルト設定では、GRUB、LILOのいずれかがMBRの設定に基づいてブートオプションを表示し、実際に起動するオペレーティングシステムについてユーザー入力によって決めさせます。

しかしこの場合、MBRが読み込まれたときに何をするかということをMBR内のブートローダーがどのようにして知るかということが問題になります。実は、LILOはすでにliloプログラムを使い、/etc/lilo.conf設定ファイルを順に読んで、そこに命令を書き込んでいるのです。GRUBのブート命令は/boot/grub/grub.conf設定ファイルに書き込まれています。GRUBの詳細については、第4章を参照してください。

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

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

警告警告
 

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

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

boot=/dev/hda
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が唯一のオプションとして表示されます。SMPカーネルサポートをインストールしている場合には、オプションとしてlinux-upが表示されます。他のオペレーティングシステムもブートできるようにLILOをセットアップしている場合は、この画面でどのオペレーティングシステムをブートするかを選ぶことになります。ブートしようとするオペレーティングシステムを矢印キーで強調表示にし、[Enter]キーを押します。

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

LILOがLinuxをブートする場合は、まずカーネルがメモリにロードされますが、そのカーネルは/boot/ディレクトリにあるvmlinuzファイル(たとえばvmlinuz-2.4.0-xxなど)です。つづいてカーネルは制御をinitに渡します。

警告Warning
 

絶対に、/initrd/ディレクトリは削除しないで下さい。削除すると、ブート出来ずにカーネルパニックを起こす原因となります。

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

Init

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

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

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

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

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

サービスの起動と停止を行うスクリプトはいずれも、実際には/etc/rc.d/rc<x>.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
S99local -> ../rc.local

これらのシンボリックリンクはinitに、以下のプロセスをkillする必要があると指示します。 pppoe, innd, ntpd, httpd, mysqld, pvmd, rarpd, bootparamd, nfs, rstatd, rusersd, rwalld, rwhod, squid, amd, mcserv, yppasswdd, dhcpd, smb, vncserver, arpwatch, named, snmpd, pxe, routed, mars-nwe, ldap, kadmin, kprop, krb524, krb5kdc, gated, nscd, ypserv, ups, irda すべてのプロセスがkillされると、initは同じディレクトリを探し、以下のプロセスのスタートスクリプトを見つけます。 kudzu, reconfig, ipchains, portmap, nfslock, autofs, random, netfs, apmd, identd, atd, pcmcia, sshd, rawdevices, xinetd, lpd, keytable, isdn, sendmail, gpm, canna, crond, FreeWnn, xfs, anacron, rhnsd 最後にinit/etc/rc.d/rc.localを実行し、このホストのために設定された特別なスクリプトがあれば実行させます。この時点ではシステムはランレベル5と考えられます。

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

また/etc/inittabinitに、コンソールでユーザーが[Ctrl]-[Alt]-[Delete]キーを押した場合の対処法を指示します。Red Hat Linuxではそのような場合すぐに電源切断/再投入を行わず、適切にシャットダウンリスタートを行わなければならないので、ユーザーがこれらのキーを押すと、initに対しコマンド/sbin/shutdown -t3 -r nowを実行するように指示されます。さらに、/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

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

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は、各initランレベルをそれぞれ独立のディレクトリで表し、実際にシステムがランレベル間を移動するときに、各ディレクトリの中のinitとシンボリックリンクを使ってサービスの停止と開始を行います。

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

  1. カーネルが/sbin/ディレクトリの中でinitコマンドを検索する。

  2. init/etc/rc.d/rc.sysinitスクリプトを実行する。

  3. rc.sysinitがブートローダーのプロセスの大部分を処理した後、(あれば)rc.serialを実行する。

  4. initがデフォルトランレベルのすべてのスクリプトを実行する。

  5. init/etc/rc.d/rc.localスクリプトを実行する。

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

id:3:initdefault:

この例ではデフォルトランレベルは最初のコロンの後ろの数字、つまり3です。変更したい場合は、/etc/inittabを手作業で編集することができます。inittabファイルを編集する際には十分注意してください。というのは、タイプミスをするとブートディスクやレスキューモードを使わずにはブートできなくなる原因となります。

ブートローダーとしてLILOを使用している場合には、再起動し、[Ctrl]-[X]キーを押してboot:プロンプトに切り替え、以下のように入力すればinittabファイルを修正することができます。

boot:  linux single

ブートローダーとしてGRUBを使用している場合には、以下のステップを実行すればinittabファイルを修正することができます。

ティップ重要
 

GRUBを使って起動時のランレベルを変更するには、上記方法で、singleを例えば、35に置き換える事で可能となります。残りのステップは上記と全く同じです。

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

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

注意

[1]

ttyデバイスの詳細は、/proc/tty/項第2章を参照して下さい。