PK
パーネルカニック研究所
今日もどこかでkernel panic。
root@pk-lab:~#

PID 1: すべてはここから始まる、Linuxプロセスの大いなる祖先

連載ナビ

この連載は「Linuxを使えるようになる」ではなく、「Linuxがどう動いているのか」を理解するための実践シリーズです。

今回は Linuxを理解する 〜 LFSで学ぶLinux内部構造 〜 の Part 2 / 第4回として、仕組みのつながりを意識しながら学びます。

—

PID 1は、カーネルが直接起動する唯一のユーザー空間プロセス

前回、すべてのプロセスは既存プロセスのfork(複製)で生まれると学びました。では、一番最初のプロセスは誰が作るのでしょうか。

結論はこうです。カーネルは自身の初期化を終えると、ユーザー空間で最初のプロセスをただ1つだけ直接起動します。それがPID 1(通称init)であり、以後のすべてのプロセスはPID 1の子孫としてforkで生まれます。pstree の根に必ず表示されるのはこのためです。

PID 1は「最初に起動される」だけでなく、カーネルから特別扱いされるプロセスでもあります。

  • PID 1が終了すると、カーネルはシステムを続行できずカーネルパニックになります(Kernel panic - not syncing: Attempted to kill init! というメッセージが有名です)。
  • 通常のプロセスと違い、PID 1は自分でハンドラを登録していないシグナルを受け取っても死にません。kill -9 1 を実行しても(通常の環境では)何も起きないのはこのためです。

PID 1には、他のプロセスにない3つの責務があるから

なぜカーネルはPID 1をここまで特別扱いするのか。理由は、システムの成立に不可欠な3つの責務をPID 1に委ねているからです。

1. システムの起動とサービス管理

カーネルが用意するのは「プロセスが動ける環境」までです。ログインを受け付ける、ネットワークを設定する、sshdやcronを立ち上げる——こうしたユーザー空間の初期化はすべて、PID 1が設定に従って子プロセスを起動することで実現されます。

2. 孤児プロセスの引き取り(reparenting)

親プロセスが子より先に終了すると、残された子は「孤児プロセス」になります。カーネルは孤児の親を自動的にPID 1に付け替えます。nohup で起動したプロセスがシェルを閉じても動き続け、ps -ef で見るとPPIDが1になっているのは、この仕組みの現れです。

3. ゾンビプロセスの回収(reaping)

前回学んだとおり、終了したプロセスは親が wait() で終了ステータスを回収するまでゾンビ (Z) として残ります。PID 1は、引き取った孤児が終了したら確実に wait() を呼ぶ「最後の回収役」です。PID 1がこれを怠ると、誰にも回収されないゾンビがプロセステーブルに溜まり続けます。

この3つが揃って初めて、「プロセスの木構造が破綻しないシステム」が成立します。

現代のPID 1は、ほとんどの環境でsystemd

かつてPID 1は、SysV initと呼ばれるシンプルなプログラムが担い、シェルスクリプトを順番に実行してサービスを起動していました。現在は、Ubuntu・Debian・Red Hat系など主要ディストリビューションのほとんどで systemd がPID 1です。自分の環境では次のコマンドで確認できます。


ps -p 1 -o pid,comm,args
#   PID COMMAND         COMMAND
#     1 systemd         /sbin/init splash   ← /sbin/initがsystemdへのリンクの場合

systemdの主な特徴は次のとおりです。

  • unitファイルによる宣言的なサービス定義 — サービスの起動方法や依存関係を /usr/lib/systemd/system/*.service などに記述します。
  • 依存関係に基づく並列起動 — 依存のないサービスを同時に立ち上げ、起動を高速化します。
  • サービスの監視と自動再起動 — Restart=on-failure と書くだけで、プロセスが異常終了したときに自動で再起動されます。

普段使う systemctl start nginx は、実体としては「PID 1であるsystemdに、nginx.serviceの起動を依頼する」操作です。サービスの親をたどるとsystemdに行き着くことも、systemctl status nginx の表示や pstree -p | head で確認できます。

DockerコンテナのPID 1問題は、この責務の理解で説明できる

PID 1の知識が現場で最も効くのが、コンテナ環境です。コンテナは独立したPID空間(PID namespace)を持つため、コンテナ内で最初に起動したコマンドがそのコンテナのPID 1になります。docker run のCMDに指定したアプリケーションが、そのままPID 1として動くわけです。

ここで2つの問題が起きます。

  • ゾンビが回収されない — 普通のアプリケーションは、自分がPID 1になって孤児の wait() までやることを想定して書かれていません。アプリが子プロセスを作る構成だと、回収されないゾンビが溜まっていくことがあります。
  • シグナルで止まらない — 前述のとおりPID 1はハンドラ未登録のシグナルを無視します。そのため docker stop(SIGTERMを送信)にアプリが反応せず、タイムアウト後のSIGKILLで強制終了される、という挙動になりがちです。

この対策として広く使われるのが tini のような軽量initです。tiniをPID 1として起動し、本来のアプリをその子プロセスとして動かすことで、ゾンビ回収とシグナル転送をtiniが肩代わりします。Dockerには docker run --init というオプションがあり、これを付けるだけでtini相当のinitが自動的にPID 1に据えられます。「なぜ --init を付けるのか」を説明できるかどうかは、まさに今回の内容の理解度そのものです。

プロセスの木の根を理解すれば、起動の全体像まであと一歩

PID 1はカーネルが直接起動する唯一のユーザー空間プロセスであり、サービスの起動・孤児の引き取り・ゾンビの回収という3つの責務を担う。現代のLinuxではsystemdがその役を務め、コンテナではアプリ自身がPID 1になるため専用の考慮(tini、--init)が要る——これが今回の結論です。

次回はPart 2の総仕上げとして、「電源ONからPID 1が起動されるまで」に何が起きているのか、つまりLinuxの起動シーケンス全体を図解します。今回の内容は、その最終ステージにそのままつながります。

コマンドでPID 1を確認する


# PID 1として動いているプログラムの正体を確認する
ps -p 1 -o pid,comm,args

# PID 1のステータスを /proc から直接確認する
head -n 10 /proc/1/status

# プロセスツリーの根がPID 1であることを確認する
pstree -p | head -n 15

# systemd環境なら、管理下のサービス一覧を確認する
systemctl list-units --type=service --state=running | head

チェックポイント

  • PID 1がカーネルによって最初に起動されるユーザー空間プロセスであることを説明できる
  • PID 1の重要な責務(サービス起動、孤児プロセスの引き取り、ゾンビの回収)を2つ以上挙げられる
  • あなたのLinux環境でPID 1として動作しているプログラムの名前を答えられる(systemd など)
  • Dockerの --init オプションが何のためにあるかを説明できる

今回理解できたこと

  • PID 1が全てのユーザープロセスの祖先であり、カーネルが直接起動する唯一のユーザー空間プロセスであること
  • 親を失ったプロセス(孤児プロセス)はPID 1に引き取られ、終了時に確実に回収される仕組みになっていること
  • systemd は、現代のLinuxにおいてPID 1として動作する、高機能なサービスマネージャーであること
  • コンテナではアプリ自身がPID 1になるため、ゾンビ回収やシグナル処理の問題(PID 1問題)が起き得ること