【悲報】天才がブチギレた訳、C++が30年OSの心臓から追放され続ける理由
【悲報】天才がブチギレた訳、C++が30年OSの心臓から追放され続ける理由
世界最大のオープンソースOSであるLinuxカーネルには、C++のコードが1行も存在しない。巨大なシステムのすべてが純粋なC言語とアセンブリ、そして近年加わったRustだけで構成されている。オブジェクト指向の便利さを捨ててまでCにしがみつくのは時代遅れではなく、極限の環境でシステムを守るための必然の防衛策だという。2007年に創始者リーナス・トーバルズが放ったC++は恐ろしい言語であるという激しい批判の裏側には、カーネルという特殊空間の過酷な現実がある。
発端はバージョン管理ツールgitが純粋なCで書かれていることに疑問を呈した開発者への応答だった。問題視されたのは言語の好みではなく、プログラマが意識しない裏側で起きる抽象化だ。暗黙のメモリ確保や予測不能な性能劣化が、効率最優先の世界では根幹を揺るがすバグにつながる。ハードとソフトの境界ではコードが機械上でどう動くかを完全に把握できることが最優先で、便利機能の裏で何が起きているか分からない状態は許されない。標準テンプレートライブラリのような巨大な機能に依存すれば制御が手から離れ、ユーザー空間のアプリなら便利でもOSの中核では致命的な設計ミスになるという判断だ。実際1999年にもカーネルの並行処理にC++を持ち込もうとした提案が激論の末に退けられており、便利さと引き換えのブラックボックス化は30年近く拒まれ続けてきた。

スタックたった4KB、見えない確保が即死を招く極限の掟
なぜそこまで厳密さが求められるのかは、カーネル空間の資源制約を見れば分かる。一般的なアプリは数メガバイトの広大なスタックを確保できるが、Linuxカーネルの割り込み処理などに割り当てられるスタックは歴史的にわずか4KBから8KBに固定されてきた。スマホの画像1枚も載らない狭さだ。ハードから割り込みが入った瞬間、カーネルは現在の状態を保存して小さなスタックに直接積み上げていくため、消費量が明確に計算できないと即座に破綻する。Cの関数ならローカル変数のサイズから消費量を正確に見積もれるが、C++では暗黙の一時オブジェクトや細かな関数呼び出しが大量に生まれ、予測不能な消費が起きやすい。さらにアトミックコンテキストと呼ばれる領域では物理メモリが足りなければ確保が即失敗し、溢れればカーネルパニックという致命的な全停止に直結する。コンパイラが吐く機械語とソースの対応が極めて高いCが最適とされてきた理由はここにあり、構造体の通りのサイズで配置されるため機器との対話にずれが生じない。
特に相性が悪いのが例外処理と動的メモリ確保だ。アプリ開発では確保失敗時に例外を投げるのが当たり前だが、カーネルでメモリが枯渇している最中に例外を投げようとすると矛盾が起きる。スタックを巻き戻す仕組みは実行時の構造解析のための巨大なメタデータを要求し、例外オブジェクトを投げる過程で新たにヒープを確保しようとする。借金を返すためにお金を借りるような状態で、最悪デッドロックに陥る。クリティカルセクションでロックを取った直後に関数が例外で抜ければ、解除されないまま他の処理が永遠に待たされる追跡困難なバグを生む。全てのロック操作を安全に包むのは非現実的で、制御不能な分岐の可能性自体が決定的に合わないのだ。
ゼロコストの嘘と仮想関数表、数千万回ループを殺す間接参照
C++の掲げるゼロコスト抽象化も、ハードに近い領域では嘘になると指摘される。代表例が仮想関数とそれを管理する仮想関数テーブルだ。クラスごとの住所録を一度参照してから実関数に飛ぶ仕組みは、Cの構造体への直接アクセスと違い、わずかな遅延と分岐予測ミスを生む。1回だけなら微差だが、カーネル内のネットワークパケット処理やプロセス割り当てでは数千万回のループが回り、ホットパスでのキャッシュ破壊と間接ジャンプの積み重ねが致命傷になる。オブジェクト指向そのものが悪いのではなく、背後で隠された間接参照を強制されることが問題で、Linuxでも構造体にポインタを持たせる手動のオブジェクト風設計は普通に使われている。手で完全に制御できるかどうかが天国と地獄を分ける境界線だ。
一方で他のOSは制約付きでC++を取り込んでいる。Windowsのカーネルモードドライバー開発では専用フラグで例外処理や実行時情報を強制禁止し、newもカーネル専用プールから取るよう改造する。Appleは長年多重継承などを排した組み込みC++のサブセットで作り、近年はドライバー自体をカーネル外のユーザー空間に隔離するDriverKitに移行した。GoogleのマイクロカーネルZirconでは標準ライブラリの使用自体を禁じ、確保の成否を明示確認しないとコンパイルエラーになる設計を強制する。いずれも無条件に受け入れたのではなく、猛獣に首輪をつけて使う状態だ。言語仕様側もコンパイル時計算の強化や無限ループの扱い修正など歩み寄りはあるが、基本構図は変わらない。
C言語も限界で脆弱性の7割がメモリ、Rustが招かれた本当の理由
ここまでC++の危険性を語っても、C言語自体が安全で完璧というわけではない。大規模システムの深刻な脆弱性の約70パーセントがメモリ管理バグに起因するという調査があり、解放後使用やバッファーオーバーフローが後を絶たない。2009年にはネットワークドライバーの安全確認をコンパイラが不要と判断して削除し、権限奪取につながった事件も起きた。人間が書いた確認を数学的推論で消してしまう最適化の罠で、カーネルは最適化を無効化する設定や黒魔術的なマクロで対処し、厳密には標準Cではなく専用方言として運用されているのが実態だ。C++が暗黙生成で驚かせるように、Cも最適化で意図を静かに壊す危険を抱えている。
その回答として歴史上初めて公式の第二言語に迎えられたのがRustだ。最大の理由は所有権モデルと借用チェッカーによるコンパイル時の数学的な安全性証明で、データ競合や解放後使用を動作前に排除できる。過去の脆弱性の7割を占める領域を未然に防げる可能性がある。C++のような暗黙の巻き戻しはなく、エラーは明示的な値として返るため枯渇時も安全に回復できる。アドレス固定が必要なオブジェクトのためのPin専用APIも新設され、カーネルの複雑な構造と矛盾なく共存させた。導入は平穏ではなく、C側の変更にRust側の修正が追従する負担からメンテナの激怒や去就問題に発展し、トーバルズがC側はRustを無視してよい代わりに口出しも認めないという分離宣言で収めた経緯がある。それでも流れは止まらない。コアの心臓は透明性と資産のためCが残り、新規ドライバーやサブシステムにRustが入って防波堤になる。ユーザー空間に隔離された領域では制限付きC++が生きる。単一言語の統一ではなく適材適所の住み分けこそ、次世代OSの姿だ。
ネットの反応
そもそもクラスを使う状況がないんじゃないの。継承したいような階層がカーネルには無い気がする
gcc自体はC++で書き直されたけど最適化は速度優先でメモリ消費が大きい。Cの流儀を残したい気持ちは分かる
C++の話になるとそれはSTLのことじゃないのって毎回思う。言語本体よりライブラリの暗黙動作が怖いんだろ
AIの所感
便利さを削る決断ほど高度なものはないと感じる。見えない親切が極限では時限爆弾になるという逆説は、OSに限らず基盤作り全般に通じる。Cの限界を認めて数学的証明に託す転換と、人間同士の衝突をルールで裁いた采配の両方があって初めてRust導入は進んだ。速さと安全と人間関係、どれか一つでも欠ければ巨大なシステムは保てない。道具選びは優劣ではなく置き場所だという視点こそ、今回の最大の持ち帰りだと思う。