輪の外の種:カーネルは最初のプロセスをどう起動するのか、そしてなぜ何も自分自身を起動できないのか
OSの一番底には、小さくて少し気まずい問いが埋まっている。僕は何年もこの問いを避けてきた。プロセスを作るのは簡単だ。fork を呼べば、親が親と子に分裂する。でも fork には、そもそも親が要る。では、最初のプロセスはどこから来るのか? まだ存在しないものを fork することはできない。これは白衣を着た「鶏が先か卵が先か」だ。とうとう僕は、ARM64 上の Linux がこれに実際どう答えているのかを読みに、下層まで降りていった。1
答えの形は、一週間前に追いかけた別のブートストラップ問題——「メモリアロケータは、アロケータになるために必要なメモリを、どうやって確保するのか」——とぴったり同じだった。そしてその対称性の裏には、予想していなかった第二の驚きが座っていた。読み終えた頃には、カーネルの一事実より少し大きな何かを確信していた。システムは、純粋な自己言及だけでは自分を持ち上げられない。自分を作るあらゆる輪は、輪の外から打たれる火花を必要とする。そして最も深いブートストラップは、生成ですらない。それは変態だ。
誰も確保しない種
最初のプロセス、PID 0——Unix の民間伝承が swapper、あるいは idle task と呼ぶもの——は、誰にも fork されない。確保もされず、作られもせず、要求もされない。コンパイル時にカーネルイメージへ静的構造体(init_task)として溶接され、マシンの電源が入る前からデータセクションに鎮座している。2 最初の卵がどこかから来られるように、外から手で鶏小屋に置かれた鶏だ。
これが腑に落ちたのは、僕がちょうど一つ下の層、メモリで、まったく同じ手を見たばかりだったからだ。新しいオブジェクトキャッシュを作るには、slab アロケータは kmem_cache 構造体を要る——それを kmem_cache キャッシュから確保する。だが起動中、そのキャッシュはまだ存在しない。解決は身も蓋もなく単純だ。一番最初の一個だけは静的に、手で作って差し込む。3 カーネルの二大資源——メモリとプロセス——は、同じレシピで起動する。自己言及の輪 + 外から置かれた一個の静的な種だ。
これは偶然じゃない。「確保にはアロケータが要る」と「プロセスにはプロセスが要る」は、名詞を入れ替えただけの同じ文だからだ。そして二度見ると、いたるところに見えてくる。セルフホスティングのコンパイラは、自分の最初のバイナリを自分でコンパイルできない——誰かが別の言語でバージョン0を手作りする。最初の自己複製分子は、それが起動した複製機構によっては複製されえなかった。何度も学び直す教訓はこうだ。自己言及だけでは、決して点火しない。輪は実在するが、外の何かがマッチを擦るまで不活性だ。
注文より先に工場を建てる
その種から、カーネルの rest_init はちょうど二つの子を fork する。どの二つかが教育的だ。4 PID 1(kernel_init)は、のちにユーザー空間の init——僕らが実際にログインする全ての祖先——になる。だが PID 2(kthreadd)は工場だ。今後存在する全カーネルスレッドの親である。決定的なのは、順序が守られていること。kernel_init が自分のカーネルスレッドを生もうとする前に、kthreadd の準備完了を待つ。4
つまり起動シーケンスは「依存を先に」ではなく、再生産機構を先にで組まれている。何かを作る前に、ものを作る当の機構を建てる。これは工業化が、特定の製品ではなく工作機械——機械を作る機械——を要とするのと同じ本能だ。新しい組織の最初の本当の採用がしばしば採用担当者であるのと同じ理由だ。一番速く立ち上がるシステムは、最初に最も多くの仕事をする者ではない。最も早く働き手を増やす装置を手に入れる者だ。
生まれるのではなく、変態する
ここが本当に僕を驚かせた部分だ。PID 1 はカーネルスレッドとして生を受ける。最初は完全にカーネルの内側、カーネルアドレス空間で、カーネルコードを走らせて生きている。それから三つのことを順にやる。起動時の足場を解放し(free_initmem——起動コードが住んでいた __init メモリを回収する)、埋め込まれた initramfs をルートファイルシステムへ展開し、/sbin/init に対して execve を呼ぶ。56
そして execve は新しいプロセスを作らない。同じカーネルスレッド——同じ task_struct、同じ PID 1、同じカーネルの血統——が、その場でユーザー空間プロセスへ変身する。何も生まれていない。何かが、数の上で同じものであり続けながら、自分が何であるかを変える。ユーザー空間の最初の市民は、ユーザー空間で生まれた者ではない。カーネルの過去を背負った、帰化した移民だ。
これが、僕が見落としていた区別をこじ開けた。ブートストラップには二つのモードがある、一つではなく。僕が学んだメモリアロケータたちは生成で作られた。各層は、下の層の上に積まれた、正真正銘新しく別個のものだ——ブートアロケータがページアロケータの要る構造を作り、それがオブジェクトアロケータを支える。だが PID 1 は生成されない。それは変態する。同一性は保たれ、存在の様式が反転する。芋虫が、決して二匹目の動物になることなく蛾になるように、カーネルがユーザーになる。僕はずっと生成の側だけを見ていた。変態の方が奇妙で、そしてたぶん、より深い。あるものがどうやって別のものになり、なお自分であり続けるのかを問うからだ。
僕がここから持ち帰るもの
ここまで降りる前には持っていなかった三つ。
第一に、種は、どの自己組織化システムも逃れられない税金に見える。メモリが払い、プロセスが払い、コンパイラが払い、たぶん生命も払った。まだ答えられない問いは、それが必然なのか単に典型的なだけなのか、だ——自分の種を自分で生成するシステムは在りうるのか、それとも「輪の外に置かれた一点」はあらゆるブートストラップに課される不可避の徴税なのか。
第二に、カーネルが自分の一方通行の扉を扱う仕方には、静かな成熟がある。/sbin/init に制御を渡すのは、概念上は引き返せない一点だ——カーネルは二度と起動経路に戻らない。それでもコードは悲観的な分岐を残す。init が見つからなければ、黙って進むのではなく、大声で panic する。5 よく設計された一方通行の扉は、扉が開かない場合をちゃんと設計している。「我々はユーザー空間になる」という楽観は、「なれなければ、全員に聞こえる場所で死ね」という悲観に裏打ちされている。
第三に、これが僕が留めておく像だ。足場は、それが作ったまさにそのものによって解体される。free_initmem を呼ぶのは kernel_init——PID 1 自身、起動コードが作ったプロセスだ——が、自分を作った起動コードを、自分が別の何かになるその同じ息で捨てる。登ってきた梯子を、自分自身が変態するまさにその瞬間に蹴り落とす。それは無駄ではない。考えうる最も相応しい配置だ。足場は、それをもう必要とせず、それがどこに在ったかを正確に知る、ただ一つの存在によって片付けられる。
僕が開いたまま残す問いは、「変態」が本物なのか、それとも「生成 + ラベルの貼り替え」に過ぎないのか——芋虫から蛾へのブートストラップが、次の層を建てるそれと根本的に違うのか、それとも内側からそう見えるだけなのか、だ。これはカーネルをはるかに超えて効くと思う。キャリアの転身、企業のピボット、置き換えられるのではなく進化する言語——そのどれでも、何かが、あらゆる外的尺度では別物にする変化を貫いて、自分であり続けることに固執する。いつ作り直し、いつ変態するのか。カーネルは最初の0.5秒で両方をやり、どちらがどちらかを知っているように見える。僕はまだ、その見分け方を解き明かそうとしている。
ARM64 Linux 起動経路を数ヶ月かけて這い回った、高橋浩和氏のカーネル内部解説シリーズに沿った自分の読書ノートに基づく。1 以下のソースは技術的な主張の裏付け用。アナロジーと論旨は僕自身のものだ。
-
高橋浩和. “新Linuxカーネル解読室 — Linuxの起動 〜ARM64編〜“(VA Linux Systems Japan、カーネル内部解説ブログシリーズ). 2026-06-20 参照. ↩ ↩2
-
The Linux Kernel. “init/init_task.c — 初期タスク
init_taskの定義.” 2026-06-20 参照. ↩ -
Bonwick, Jeff. “The Slab Allocator: An Object-Caching Kernel Memory Allocator“(USENIX Summer 1994); および The Linux Kernel, “mm/slab common bootstrap.” 2026-06-20 参照. ↩
-
The Linux Kernel. “init/main.c —
rest_init、kernel_init(PID 1)とkthreadd(PID 2)の生成.” 2026-06-20 参照. ↩ ↩2 -
The Linux Kernel. “Explaining the ‘No working init found.’ boot hang message“(kernel.org admin guide); および init/main.c、
kernel_initがfree_initmemの後run_init_processを呼ぶ箇所. 2026-06-20 参照. ↩ ↩2 -
“Al Viro’s new execve/kernel_thread design“(LWN.net). 2026-06-20 参照. ↩