コンピュータを起動する際、コンソール画面にスクロールされる多くのメッセージには、実行されている多くの自動初期化と自動設定に関する情報が表示されます。この段階の挙動を少し変えたいと思うことがあるかもしれません。これは起動処理をよく理解する必要があることを意味しています。この節の目的は起動処理をよく理解することにあります。
「init の実体」は現在 systemd によって提供されています。この節ではこの init システムに関して説明します。
Systemd はシステムの設定を担当している複数のプロセスを実行します。具体的に言えば、キーボード、ドライバ、ファイルシステム、ネットワーク、サービスなどの要素が設定されます。Systemd はシステム全体におよぶ包括的視点と各要素の要求条件を満足させながらプロセスを実行します。各要素は「ユニットファイル」によって定義されています (「ユニットファイル」以外のファイルが必要な場合もあります)。ユニットファイルの一般的な構文は広く使われている「*.ini ファイル」の構文から派生したもので、[section]
ヘッダでグループ化された key = value
ペアを使います。ユニットファイルは /lib/systemd/system/
と /etc/systemd/system/
の下に保存されます。ユニットファイルにはいくつかの形式がありますが、ここでは「service」型と「target」型に注目します。
systemd の「service ファイル」は systemd が管理するプロセスを設定するファイルです。「service ファイル」には大ざっぱに言って古いスタイルの init スクリプトと同じ情報が含まれていますが、宣言的な方法 (そしてより簡潔な方法) を使ってその情報が記述されています。systemd は大半の繰り返しタスク (プロセスの開始と停止、状態確認、ログ記録、特権の取り消しなど) を処理しますので、service ファイルに記入する情報は対象プロセスに特有の情報だけで十分です。たとえば以下は SSH 用の service ファイルです。
[Unit]
Description=OpenBSD Secure Shell server
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
Alias=sshd.service
ご覧の通り、コードはとても少なく宣言だけで構成されています。systemd は作業進行状況を表示したり、プロセスを監視したり、必要な時にプロセスを再起動することを担当します。
systemd の「target ファイル」はシステムの状態を記述するファイルで、管理者はこのファイルを使って利用できる状態にされていなければならないサービス群を指定します。「target ファイル」は昔ながらのランレベルに相当するものとして考えることが可能です。target ファイルの 1 つに local-fs.target
があります。local-fs.target
で定義された状態に到達した場合、残りのシステムはすべてのローカルファイルシステムがマウントされアクセスできるようになっている状態を仮定することが可能です。また別の target ファイルに network-online.target
と sound.target
があります。ある target ファイルの依存関係は対象の target ファイルの中 (Requires=
行) に依存関係にある service ファイルを記述するか、対象の /lib/systemd/system/targetname.target.wants/
ディレクトリの中に依存関係にある service ファイルを指すシンボリックリンクを作ることで指定します。たとえば、/etc/systemd/system/printer.target.wants/
には /lib/systemd/system/cups.service
へのリンクが含まれます。そしてこのため systemd は printer.target
を処理する前に CUPS が実行されていることを保証するでしょう。
ユニットファイルは宣言型の設定ファイルでありスクリプトやプログラムではないため、ユニットファイルを直接実行することは不可能で、ユニットファイルは systemd によってのみ解釈されます。いくつかのユーティリティを使うことで、管理者は systemd と情報をやり取りして、システムおよびシステム部品の状態を制御することが可能です。
systemd と情報をやり取りする 1 番目のユーティリティとして systemctl
が挙げられます。systemctl
を引数なしで実行した場合、systemd が把握しているすべてのユニットファイル (無効化されているものを除きます) およびその状態が表示されます。systemctl status
を使うことで、サービスおよび関連するプロセスをよりわかりやすく表示することが可能です。サービスの名前を指定した場合 (systemctl status ntp.service
のように指定した場合)、systemctl
はさらに詳しい情報および指定したサービスに関連するログの最後の数行を表示します (後から詳しく説明します)。
手作業でサービスを開始するのは簡単で、systemctl start servicename.service
を実行するだけです。予想通り、サービスを停止するには systemctl stop servicename.service
を使います。また、他のサブコマンドには reload
と restart
があります。
サービスを有効化するには (たとえば起動時に自動的にサービスを開始するには)、systemctl enable servicename.service
を使います (無効化するには disable
を使います)。is-enabled
を使えば、サービスの状態を確認することが可能です。
systemd が備える興味深い機能として、journald
と名付けられたログ記録機能が挙げられます。journald
は syslogd
などのより伝統的なログ記録システムを補完するために誕生しましたが、サービスとサービスが生成したメッセージを正しく関連付けたり初期化シーケンスが生成するエラーメッセージを保存する能力などの興味深い機能を追加しています。journalctl
コマンドの助けを少し借りるだけで、メッセージを後から表示することが可能になります。引数なしで実行した場合、journalctl
は起動後に発生したすべてのログメッセージを表示しますが、引数を与えずに実行することはほとんどないでしょう。ほとんどの場合、以下のようにサービス識別子を与えて journalctl
を実行します。
#
journalctl -u ssh.service
-- Logs begin at 火 2015-03-31 17:08:49 JST, end at 水 2015-04-01 00:06:02 JST. --
3月 31 17:08:55 mirtuel sshd[430]: Server listening on 0.0.0.0 port 22.
3月 31 17:08:55 mirtuel sshd[430]: Server listening on :: port 22.
3月 31 17:09:00 mirtuel sshd[430]: Received SIGHUP; restarting.
3月 31 17:09:00 mirtuel sshd[430]: Server listening on 0.0.0.0 port 22.
3月 31 17:09:00 mirtuel sshd[430]: Server listening on :: port 22.
3月 31 17:09:32 mirtuel sshd[1151]: Accepted password for roland from 192.168.1.129 port 53394 ssh2
3月 31 17:09:32 mirtuel sshd[1151]: pam_unix(sshd:session): session opened for user roland by (uid=0)
その他の役に立つコマンドラインオプションとして -f
が挙げられます。これを使った場合、journalctl
は新しいメッセージを受け取ったらそのメッセージを表示し続けます (これは tail -f file
とほぼ同じやり方です)。
サービスが期待通りに動いていないように見える場合、問題解決に向けて手始めに systemctl status
を実行し、今現在サービスが動いているか否かを確認します。サービスが実行されておらず、systemctl status
の表示したメッセージが問題の原因を解明するのに十分でない場合、journald が収集したサービスに関連するログを確認します。たとえば、SSH サービスが動いていないと仮定すると、以下の手順で状況を確認します。
#
systemctl status ssh.service
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled)
Active: failed (Result: start-limit) since 水 2015-04-01 00:30:36 JST; 1s ago
Process: 1023 ExecReload=/bin/kill -HUP $MAINPID (code=exited, status=0/SUCCESS)
Process: 1188 ExecStart=/usr/sbin/sshd -D $SSHD_OPTS (code=exited, status=255)
Main PID: 1188 (code=exited, status=255)
4月 1 00:30:36 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
4月 1 00:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
4月 1 00:30:36 mirtuel systemd[1]: ssh.service start request repeated too quickly, refusing to start.
4月 1 00:30:36 mirtuel systemd[1]: Failed to start OpenBSD Secure Shell server.
4月 1 00:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
#
journalctl -u ssh.service
-- Logs begin at 水 2015-04-01 00:29:27 JST, end at 水 2015-04-01 00:30:36 JST. --
4月 1 00:29:27 mirtuel sshd[424]: Server listening on 0.0.0.0 port 22.
4月 1 00:29:27 mirtuel sshd[424]: Server listening on :: port 22.
4月 1 00:29:29 mirtuel sshd[424]: Received SIGHUP; restarting.
4月 1 00:29:29 mirtuel sshd[424]: Server listening on 0.0.0.0 port 22.
4月 1 00:29:29 mirtuel sshd[424]: Server listening on :: port 22.
4月 1 00:30:10 mirtuel sshd[1147]: Accepted password for roland from 192.168.1.129 port 38742 ssh2
4月 1 00:30:10 mirtuel sshd[1147]: pam_unix(sshd:session): session opened for user roland by (uid=0)
4月 1 00:30:35 mirtuel sshd[1180]: /etc/ssh/sshd_config line 28: unsupported option "yess".
4月 1 00:30:35 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
4月 1 00:30:35 mirtuel systemd[1]: Unit ssh.service entered failed state.
4月 1 00:30:35 mirtuel sshd[1182]: /etc/ssh/sshd_config line 28: unsupported option "yess".
4月 1 00:30:35 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
4月 1 00:30:35 mirtuel systemd[1]: Unit ssh.service entered failed state.
4月 1 00:30:35 mirtuel sshd[1184]: /etc/ssh/sshd_config line 28: unsupported option "yess".
4月 1 00:30:35 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
4月 1 00:30:35 mirtuel systemd[1]: Unit ssh.service entered failed state.
4月 1 00:30:36 mirtuel sshd[1186]: /etc/ssh/sshd_config line 28: unsupported option "yess".
4月 1 00:30:36 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
4月 1 00:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
4月 1 00:30:36 mirtuel sshd[1188]: /etc/ssh/sshd_config line 28: unsupported option "yess".
4月 1 00:30:36 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
4月 1 00:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
4月 1 00:30:36 mirtuel systemd[1]: ssh.service start request repeated too quickly, refusing to start.
4月 1 00:30:36 mirtuel systemd[1]: Failed to start OpenBSD Secure Shell server.
4月 1 00:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
#
vi /etc/ssh/sshd_config
#
systemctl start ssh.service
#
systemctl status ssh.service
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled)
Active: active (running) since 水 2015-04-01 00:31:09 JST; 2s ago
Process: 1023 ExecReload=/bin/kill -HUP $MAINPID (code=exited, status=0/SUCCESS)
Main PID: 1222 (sshd)
CGroup: /system.slice/ssh.service
└─1222 /usr/sbin/sshd -D
#
ここでは SSH サービスの状態が失敗状態であることを確認した後、ログの確認作業に進みました。その結果、ログは設定ファイル内にエラーがあることを示しています。そこで設定ファイルを編集してエラーを修正した後、SSH サービスを再起動し、SSH サービスが本当に動いていることを確認しました。
9.1.2. System V init システム
System V init システム (これを短縮して init と呼びます) は
/etc/inittab
ファイルの指示に従って複数のプロセスを実行します。実行される最初のプログラム (
sysinit 段階に相当します) は
/etc/init.d/rcS
で、これは
/etc/rcS.d/
ディレクトリに含まれるすべてのプログラムを起動するスクリプトです。
/etc/rcS.d/
ディレクトリに含まれるスクリプトは特に以下の点を担当します。
/etc/init.d/rcS
の完了後、init
が起動処理を引き継いで、デフォルトランレベル (通常ランレベル 2) で有効化されたプログラムを起動します。init
は /etc/init.d/rc 2
を実行します。これは /etc/rc2.d/
に置かれて「S」で始まる名前のすべてのサービスを開始するコマンドです。歴史的なことを言えば、「S」の後に続く 2 桁の数字は起動するサービスの順番を定義するために使われていました。しかし現在では、デフォルトの起動システムは insserv
を使ってスクリプト同士の依存関係に従った起動順を自動的に決定します。このため、各起動スクリプトはサービスを起動または終了させる時に満足しなければいけない条件を宣言します (たとえば、あるサービスは他のサービスの前または後に起動しなければいけないなどの条件を宣言します)。そして、init
は条件を満足するようにサービスを起動します。このため、スクリプトの静的な番号付けはもはや考慮されません (しかしスクリプトの名前は必ず「S」で始まり、その後ろに 2 桁の番号と条件を宣言する際に使われる実際のスクリプトの名前を付けなければいけません)。一般に、基盤サービス (ログ記録を担当している rsyslog
やポート割り当てを担当している portmap
) が最初に起動され、その後に標準的なサービスとグラフィカルインターフェース (gdm3
) が起動されます。
この依存関係に基づく起動システムのおかげで、起動順を自動的に再定義することが可能になります。これは手作業でやるにはちょっと退屈な作業で、人的ミスの危険性があります。なぜなら、起動順は宣言された依存関係に従って定義されるからです。他の長所として、他のサービスに依存しないサービスは並列して開始できるという点があります。このことにより、起動処理を加速できます。
init
はランレベルを見分けて処理を分岐させます。ランレベルを切り替えるには telinit new-level
コマンドを使います。このコマンドを実行すると init
は即座に新しいランレベルを引数にして /etc/init.d/rc
を再実行します。/etc/init.d/rc
は欠けているサービスを開始し、既に不要となったサービスを停止します。これを行うために、/etc/init.d/rc
は /etc/rcX.d
ディレクトリの内容を参照します (ここで X は新しいランレベルです)。このディレクトリに含まれる (「Start」の)「S」で始まるスクリプトは開始されるサービスで、(「Kill」の)「K」で始まるスクリプトは停止されるサービスです。/etc/init.d/rc
は切り替え前のランレベルで既に起動されているサービスは開始しません。
デフォルトで、Debian の System V init は 4 種類のランレベルを使います。
レベル 0。これはコンピュータの電源を切る際に一時的に使われるだけです。このため「K」スクリプトしか含まれません。
レベル 1。これはシングルユーザモードとしても知られ、システムの機能抑制モードに相当します。このモードは基本的なサービスだけを提供し、一般ユーザがマシンを利用していないメンテナンスの際に使われることを意図しています。
レベル 2。これは通常動作用のモードで、ネットワークサービス、グラフィカルインターフェース、ユーザログインなどの機能を使うことが可能です。
レベル 6。これはレベル 0 と似ていますが、再起動前のシャットダウン段階中に使われる点が異なります。
その他のレベル。レベル 3 からレベル 5 までも存在します。デフォルトでこれらのランレベルはレベル 2 と同様に動作しますが、管理者はこれらのレベルを特定の要求に順応させるためのレベルに書き換えることが可能です (ランレベルに対応する /etc/rcX.d
ディレクトリにスクリプトを追加したりおよび削除したりする必要があります)。
各 /etc/rcX.d
ディレクトリに含まれるすべてのスクリプトは /etc/init.d/
に格納されたスクリプトの実体を指すシンボリックリンクに過ぎません (シンボリックリンクはパッケージのインストール時に update-rc.d
プログラムによって作られます)。各ランレベルで利用できるサービスを微調整するには、管理者が調整パラメータを与えて update-rc.d
を再実行する必要があります。update-rc.d
の構文は update-rc.d(1) マニュアルページに詳しく説明されています。サービスを無効化したい場合に (remove
パラメータを与えて) すべてのシンボリックリンクを削除するのは悪い方法であるという点に注意してください。remove
パラメータを使う代わりに、単純に希望するランレベルでそのサービスを起動しないよう disable
を使って設定するべきです (同時に、万が一切り替え前のランレベルで対象のサービスが実行されている場合に備えて、切り替え後のランレベルでサービスを停止するために必要なシンボリックリンクを確保しておくべきです)。update-rc.d
は複雑なインターフェースを持っているため、rcconf
(rcconf パッケージに含まれます) を使いたいと思うかもしれません。rcconf
はユーザにとってさらに使い勝手の良いインターフェースを提供します。
最後に、init
はさまざまな仮想コンソール用の制御プログラム (getty
) を開始します。getty
はプロンプトを表示し、ユーザ名の入力を待ち、セッションを開始するために login user
を実行します。