Dev Study
← OSとカーネルの土台
レッスン 1 / 10·約 9 分·第10期 OSとカーネルの土台

カーネルはどこで動いているのか — プロセス一覧に出ない理由

アプリと DB の下に何が積まれているか(左が下、右が上)
  1. 物理機リージョン/AZ
  2. ハイパーバイザVM exit で捕まえる
  3. VMゲストOS
  4. コンテナnamespace / cgroup
  5. プロセスアプリ / 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 であるなど)。まずコンテナの中から見た一覧です。

$ docker run --rm debian:13 sh -c '... ps -ef'(既定の PID 名前空間)コマンド出力
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 21:22 ? 00:00:00 sh -c apt-get install -y -qq procps ... ; ps -ef
root 133 1 0 21:22 ? 00:00:00 ps -ef

自分が起動した2つしかなく、カーネルスレッドは見えません。ここで「このホストにカーネルスレッドは無い」と結論すると誤りです。同じホストで --pid=host を付け、ホスト側の PID 名前空間から見ると、こうなります。

$ docker run --rm --pid=host alpine:3 sh -c 'ps -eo pid,ppid,stat,comm,args | head -40'(冒頭のみ、以下略)コマンド出力
PID PPID STAT COMMAND COMMAND
1 0 Sl init /init
2 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つです。

  1. PID 1 は /init、PID 2 は [kthreadd]。 どちらも「カーネル」という名前ではありません。予測の答え合わせです。
  2. 角括弧付きの行の PPID はすべて 2、つまり kthreadd が親で、kthreadd 自身の PPID は 0 です。
  3. [ksoftirqd/0] [ksoftirqd/1] のように CPU ごとに1本ずつ並びます。 単一の常駐体ではなく、仕事ごと・CPU ごとに分かれたタスクの集まりです。
✓ ここまでの確認

コンテナの中で `ps -ef` を実行しても `[kthreadd]` のような行が1つも出ませんでした。どう解釈するのが適切ですか。

抽象原理

「プロセスではない」と「カーネルスレッドはある」を両立させる

  • カーネルは スケジューリングの単位としての「カーネルというプロセス」を持ちません。
  • システムコールで入ったときは、呼んだタスクの文脈(user context)のままカーネルのコードが走ります。 上で引いた "running in kernel space, associated with a process" がその状態です。
  • 一方で カーネルスレッドは実在します。 ただし「カーネル本体」ではなく、カーネルが自分の仕事の一部を実行するために作った、ユーザ空間を持たないタスクです。
同じカーネルのコードが、どの文脈で走っているか
プロセスユーザ空間で実行中
アイドルの CPU誰の文脈でもない
システムコール / 外から来る割り込み
user context同じタスクのまま特権が変わる
戻り先は呼んだプロセス
割り込みの文脈not associated with any process
誰の文脈でもない
後で実行する分は、専用のタスクへ
カーネルスレッド[kthreadd] とその配下
ps に並ぶが「カーネル本体」ではない
  • ・「カーネル」という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 が小さい)コマンド出力
PID PPID STAT COMMAND COMMAND
16 1 Z forkdemo <defun [forkdemo] <defunct>

[forkdemo] <defunct> — カーネルスレッドではない、ただの終了した子プロセスが角括弧付きで出ています。 ゾンビは引数を読み出せなくなるため、規定どおり角括弧になります。

そもそも角括弧は Linux の ps の表示のしかたで、UNIX 一般の規約でもありません。同じ日に macOS 側(Darwin 25.5.0 / arm64)で数えた結果です。

$ ps -axo pid,command | grep -c "^ *[0-9]* \["(macOS ホスト)コマンド出力
0
 
$ ps -axo pid,ppid,stat,comm | awk '$1<=2'
1 0 Ss /sbin/launchd

角括弧付きのエントリは 0 件、PID 0 もこの出力には現れませんでした。macOS 側の内部の扱いは本教材では確かめていません。言えるのは、この表示上の観測事実までです。

✓ ここまでの確認

`ps` の出力で角括弧が付いた行は、何を意味していますか。

⇄ 2つを見比べる
具体例 A

システムコールを出したとき

同じタスクのまま特権が変わってカーネルのコードが走ります。hacking.rst の言い方では user context で、戻り先は呼んだプロセスです。

CPU の状態(hacking.rst の列挙より)表
状態 紐づくプロセス
running in kernel space, associated with a process ある(呼んだタスク)
running a process in user space ある
具体例 B

割り込みが来たとき

外の装置の都合で入ってくるため、そのとき走っていたプロセスとは関係がありません。hacking.rst はこの状態を「どのプロセスにも紐づかない」と明記しています。

CPU の状態(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 に出ないことは、存在しないことを意味しない。どの名前空間から見た一覧かを必ず添える。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

環境:

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 3正解: 0

「システムコールを呼ぶと、カーネルというプロセスに処理が渡される」という説明の評価はどれですか。

用語集(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>` になる。引数が取れず実行ファイル名が角括弧で囲まれる。