- 物理機リージョン/AZ
- ハイパーバイザVM exit で捕まえる
- VMゲストOS
- コンテナnamespace / cgroup
- プロセスアプリ / DB今ここ
第10期の初回です。第13週『システムコール — アプリとカーネルの境界線』で、アプリとカーネルの間に境界があることを見ました。今回はその向こう側で、コードがどの文脈で走っているのかを見ます。
まず結論
第10期は、アプリの下でカーネルが実際に何をどう実行しているかを扱います。最初に押さえるのは、カーネルが「ずっと走り続けている常駐プログラム」ではないことです。
カーネルのコードは呼ばれたときだけ走り、システムコールで入ったときは呼んだプロセスの文脈を借りたまま走ります。だから `ps` の一覧に「カーネル」という名前の行はありません。
Linux で `ps` の一覧を全部たどると、「カーネル本体」にあたる常駐プロセスが1つ見つかるでしょうか。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
カーネルは「走り続けているもの」ではない
第13週『システムコール — アプリとカーネルの境界線』で、アプリとカーネルの間に境界があることを見ました。第10期はその向こう側を扱います。最初の問いは——境界の向こうでコードを実行しているのは、誰なのか。
kernel.org の Unreliable Guide To Hacking The Linux Kernel は、CPU が取りうる状態を次のように列挙しています。
At any time each of the CPUs in a system can be:
- not associated with any process, serving a hardware interrupt;
- not associated with any process, serving a softirq or tasklet;
- running in kernel space, associated with a process (user context);
- running a process in user space.
列挙されているのは CPU の状態であって、プロセスの一覧ではありません。「カーネルという名のプロセス」は1つも出てきません。3番目の "running in kernel space, associated with a process (user context)" が、システムコールで入ったときの状態です。プロセスに紐づいたまま、カーネル空間で走っている。
同文書は user context をこう定義しています。
User context is when you are coming in from a system call or other trap: like userspace, you can be preempted by more important tasks and by interrupts.
カーネルのコードは、呼ばれたときだけ、呼んだタスクの文脈を借りて走ります。 別のプロセスに処理が渡るのではありません。
実物の `ps` を、2つの場所から見る
実測環境は Apple Silicon 上の Docker Desktop(kernel 6.10.14-linuxkit / aarch64 / 2026-09-17 取得)。linuxkit の VM なので一般的な Linux ディストリビューションとは違う点があります(PID 1 が systemd ではなく /init であるなど)。まずコンテナの中から見た一覧です。
UID PID PPID C STIME TTY TIME CMDroot 1 0 0 21:22 ? 00:00:00 sh -c apt-get install -y -qq procps ... ; ps -efroot 133 1 0 21:22 ? 00:00:00 ps -ef
自分が起動した2つしかなく、カーネルスレッドは見えません。ここで「このホストにカーネルスレッドは無い」と結論すると誤りです。同じホストで --pid=host を付け、ホスト側の PID 名前空間から見ると、こうなります。
PID PPID STAT COMMAND COMMAND1 0 Sl init /init2 0 S kthreadd [kthreadd]3 2 S pool_workqueue_ [pool_workqueue_release]4 2 I< kworker/R-rcu_g [kworker/R-rcu_gp]5 2 I< kworker/R-sync_ [kworker/R-sync_wq]6 2 I< kworker/R-slub_ [kworker/R-slub_flushwq]7 2 I< kworker/R-netns [kworker/R-netns]9 2 I< kworker/0:0H-kb [kworker/0:0H-kblockd]11 2 I kworker/u40:0-e [kworker/u40:0-ext4-rsv-conversion]12 2 I< kworker/R-mm_pe [kworker/R-mm_percpu_wq]13 2 I rcu_tasks_rude_ [rcu_tasks_rude_kthread]14 2 I rcu_tasks_trace [rcu_tasks_trace_kthread]15 2 S ksoftirqd/0 [ksoftirqd/0]16 2 I rcu_sched [rcu_sched]17 2 S rcuog/0 [rcuog/0]18 2 S rcuos/0 [rcuos/0]21 2 S migration/0 [migration/0]22 2 S cpuhp/0 [cpuhp/0]25 2 S ksoftirqd/1 [ksoftirqd/1]...
読み取れることは3つです。
- PID 1 は
/init、PID 2 は[kthreadd]。 どちらも「カーネル」という名前ではありません。予測の答え合わせです。 - 角括弧付きの行の PPID はすべて 2、つまり
kthreaddが親で、kthreadd自身の PPID は 0 です。 [ksoftirqd/0][ksoftirqd/1]のように CPU ごとに1本ずつ並びます。 単一の常駐体ではなく、仕事ごと・CPU ごとに分かれたタスクの集まりです。
コンテナの中で `ps -ef` を実行しても `[kthreadd]` のような行が1つも出ませんでした。どう解釈するのが適切ですか。
「プロセスではない」と「カーネルスレッドはある」を両立させる
- カーネルは スケジューリングの単位としての「カーネルというプロセス」を持ちません。
- システムコールで入ったときは、呼んだタスクの文脈(user context)のままカーネルのコードが走ります。 上で引いた "running in kernel space, associated with a process" がその状態です。
- 一方で カーネルスレッドは実在します。 ただし「カーネル本体」ではなく、カーネルが自分の仕事の一部を実行するために作った、ユーザ空間を持たないタスクです。
- ・「カーネル」という1つの常駐プロセスは、どの段にも存在しない
- ・引用は kernel.org hacking.rst の The Players 節
第12週『OSの役割 — 1つのCPUを、みんなで分け合う』で見た「切り替える主体」も、待機している常駐プログラムではありません。切り替えのコードもまた、呼ばれた時点の文脈で走るカーネルのコードです。
角括弧は何を意味していたのか
通説を実物で検算します。「ps の角括弧はカーネルスレッドの印」——これは誤りです。 ps(1)(procps-ng v4.0.4, 2023-08-19)の args の説明を読みます。
Sometimes the process args will be unavailable; when this happens, ps will instead print the executable name in brackets.
定義されているのは「引数が取得できないとき、代わりに実行ファイル名を角括弧で囲む」だけで、カーネルスレッドという語は出てきません。カーネルスレッドがそう見えるのは、引数を持たず args が取れないという結果です。
この推論が破れる実例があります。C で fork を1回だけ行い、親が wait しないまま子を終了させてゾンビを作ったときの出力です。
PID PPID STAT COMMAND COMMAND16 1 Z forkdemo <defun [forkdemo] <defunct>
[forkdemo] <defunct> — カーネルスレッドではない、ただの終了した子プロセスが角括弧付きで出ています。 ゾンビは引数を読み出せなくなるため、規定どおり角括弧になります。
そもそも角括弧は Linux の ps の表示のしかたで、UNIX 一般の規約でもありません。同じ日に macOS 側(Darwin 25.5.0 / arm64)で数えた結果です。
0$ ps -axo pid,ppid,stat,comm | awk '$1<=2'1 0 Ss /sbin/launchd
角括弧付きのエントリは 0 件、PID 0 もこの出力には現れませんでした。macOS 側の内部の扱いは本教材では確かめていません。言えるのは、この表示上の観測事実までです。
`ps` の出力で角括弧が付いた行は、何を意味していますか。
システムコールを出したとき
同じタスクのまま特権が変わってカーネルのコードが走ります。hacking.rst の言い方では user context で、戻り先は呼んだプロセスです。
状態 紐づくプロセスrunning in kernel space, associated with a process ある(呼んだタスク)running a process in user space ある
割り込みが来たとき
外の装置の都合で入ってくるため、そのとき走っていたプロセスとは関係がありません。hacking.rst はこの状態を「どのプロセスにも紐づかない」と明記しています。
状態 紐づくプロセスnot associated with any process, serving a hardware interrupt ないnot associated with any process, serving a softirq or tasklet ない
どちらもカーネルのコードが CPU 上で走っています。2つの違いを一言で言うと何でしょうか。
「終わったあと、どこへ戻るのか」を両方で考えてみてください。
この章のまとめ
- カーネルは常駐プロセスではない。kernel.org の hacking.rst が列挙しているのは CPU の状態で、「カーネルという名のプロセス」は出てこない。
- システムコールで入ったときは、呼んだタスクに紐づいたままカーネル空間で走る(文書の言い方では user context)。割り込みで入ったときはどのプロセスにも紐づかない。
- カーネルスレッドは実在する。
[kthreadd](PID 2)を親として[ksoftirqd/0]などが CPU ごとに並ぶ。ただし「カーネル本体」ではなく、ユーザ空間を持たないタスク。 - 角括弧はカーネルスレッドの印ではない。
ps(1)の規定は「引数が取れないとき実行ファイル名を角括弧で囲む」。ゾンビも[forkdemo] <defunct>と角括弧付きで出た。 - コンテナの
psに出ないことは、存在しないことを意味しない。どの名前空間から見た一覧かを必ず添える。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
「システムコールを呼ぶと、カーネルというプロセスに処理が渡される」という説明の評価はどれですか。
用語集(4語)
- user context(ユーザ文脈)
- システムコールなどでカーネル空間に入り、呼んだタスクに紐づいたまま走っている状態。kernel.org の hacking.rst はこの語を見出しに使い「User context is when you are coming in from a system call or other trap」と定義している。日本語では「プロセスコンテキスト」とも呼ばれるが、この一次ソースの見出しは User Context。
- カーネルスレッド / kthreadd
- カーネルが自分の仕事を実行するために作った、ユーザ空間を持たないタスク。実測では `kthreadd` が PID 2 / PPID 0 で、他のカーネルスレッドの PPID はすべて 2 だった。`[ksoftirqd/N]` のように CPU ごと・仕事ごとに分かれており、「カーネル本体」ではない。
- PID 名前空間
- プロセス番号の見え方を区切る仕組み。コンテナの中からは、その名前空間に属するタスクしか `ps` に出てこない。「一覧に無い」が「存在しない」を意味しない理由。
- ゾンビ
- 終了したが親にまだ回収されていない子プロセス。`ps` の STAT が `Z`、表示は `<defunct>` になる。引数が取れず実行ファイル名が角括弧で囲まれる。