基礎●●○○○

カーネル

Kernel

OSの中核としてハードウェアを直接管理し、プロセスにCPU・メモリ・IOを配分する特権プログラム。

概要

カーネルはOSの中核をなすプログラムで、ハードウェアを直接管理し、その資源を各プログラムに配分する「支配人」です。CPU・メモリ・ディスク・ネットワークといった限られた資源を、複数のプロセスが奪い合わないように取り仕切り、どのプログラムにいつCPUを使わせ、どれだけのメモリを割り当てるかを一手に決めています。OSという言葉が指す範囲は広く、GUIやコマンド群まで含みますが、その一番奥で常に動き続けている特権的な部分がカーネルです。

カーネルの決定的な特徴は、コンピュータの中でハードウェアに直接触れることを許された唯一の存在である、という点です。ふだん私たちが書くアプリケーションは、ファイルを開くのもネットワークに繋ぐのもメモリを確保するのも、自力ではできません。すべてカーネルに「お願い」して代行してもらいます。この「お願い」の唯一の窓口がシステムコールです。

普段カーネルの存在を意識することは少ないかもしれませんが、Linuxカーネルのバージョン、dmesg に流れるメッセージ、コンテナがホストと共有しているもの——こうした場面の中心には常にカーネルがいます。障害調査を突き詰めると、最後はこの層に行き着きます。

なぜ生まれたか

初期のコンピュータには、資源を仲介する存在がありませんでした。プログラムはハードウェアを直接叩き、メモリの好きな番地を読み書きし、ディスクやプリンタを自分で制御していました。この「全員がハードウェアに直接手を伸ばせる」世界では、1本のプログラムのバグが機械全体を巻き込みます。誤ったアドレスに書き込めば他のプログラムやOS本体のデータを破壊し、無限ループに陥ればCPUを独占して他が一切動けなくなる。1台の計算機で複数の仕事を安全に走らせることが、原理的に不可能だったのです。

そこで考え出されたのが、「ハードウェア管理を一人の仲介者に独占させ、他のプログラムはその仲介者にお願いする立場に落とす」という設計です。CPUに特権モードという仕組みを設け、そこで動く仲介者=カーネルだけがハードウェアを直接操作でき、アプリケーションは制限された権限しか持たないようにしました。行儀の悪いプログラムが暴れても、その被害を自分の領域の中に閉じ込められる——この「触れる者を一人に絞る」という発想が、マルチタスクで安定して動く現代のコンピュータの土台になりました。

詳細

カーネル空間とユーザー空間

カーネルによる保護の実体は、CPUが持つ「特権レベル」というハードウェア機能です。多くのCPUは少なくとも2段階の権限を持ち、最も強い権限(特権モード、Intel系では Ring 0)で動くコードだけがハードウェアの直接操作や全メモリへのアクセスを許されます。カーネルはこの特権モードで動きます。一方、アプリケーションは弱い権限(ユーザーモード、Ring 3)で動き、危険な命令を実行しようとするとCPUがそれを弾いてカーネルに制御を移します。

この権限の違いに対応して、メモリ空間も「カーネル空間」と「ユーザー空間」に分かれています。ユーザー空間で動くアプリケーションは、カーネル空間のメモリを覗くことも壊すこともできません。両者の間を渡る道はただ一つ、システムコールという正式な入口だけです。この一本道があるからこそ、カーネルは「誰が何を要求してきたか」をすべて検査でき、不正な操作を水際で止められます。

ユーザー空間(弱い権限・ユーザーモード)プロセスAプロセスBプロセスCシステムコール(唯一の入口)カーネル空間(特権モード)カーネルプロセス管理・メモリ管理・入出力・ファイルハードウェア(CPU・メモリ・ディスク・NIC)
ユーザー空間とカーネル空間の二層。境界を渡る唯一の道がシステムコールで、ハードウェアに触れるのはカーネルだけ

カーネルの主な仕事

カーネルの担う仕事は、大きく4つに整理できます。第一にプロセス管理です。多数のプロセスやスレッドを短い時間で切り替えて(スケジューラが担当)、1個のCPUコアをあたかも複数あるように見せます。切り替えのきっかけの多くは、タイマーや周辺機器からの割り込みです。第二にメモリ管理です。仮想メモリという仕組みで、各プロセスに「自分だけがメモリ全体を占有している」ように見える独立空間を与えます。第三に入出力(I/O)です。デバイスドライバを通じてディスクやネットワークカードを操作し、その差異を吸収して統一的なインターフェースにします。第四にファイル管理です。ディスク上のただのビット列を、ファイルシステムとしてディレクトリとファイルの扱いやすい形に抽象化します。

これらはすべて、ユーザー空間からはシステムコール越しにしか触れられません。「ファイルを開く」も「メモリを確保する」も「別マシンと通信する」も、最終的にカーネルのこの4つの機能のどれかに帰着します。

モノリシックカーネルとマイクロカーネル

カーネルをどう作るかには、大きく2つの設計思想があります。モノリシックカーネルは、プロセス管理・メモリ管理・ドライバ・ファイルシステムまで、上で挙げた機能をすべて一つの大きな特権プログラムに詰め込む方式です。すべてがカーネル空間の中で直接関数を呼び合うため高速ですが、その分カーネルは巨大になり、一つのドライバのバグがカーネル全体をクラッシュさせ得ます。Linux はこの方式の代表です(ただしモジュールとして機能を動的に付け外しできる工夫があります)。

対するマイクロカーネルは、カーネル空間には「最小限の中核」(プロセス切り替え、メモリの基本管理、メッセージのやり取り)だけを残し、ドライバやファイルシステムはユーザー空間の別プロセスとして動かす方式です。障害を各サービスに封じ込められ、壊れた部分だけを再起動できる堅牢さが魅力ですが、機能同士がメッセージを介してやり取りするぶんオーバーヘッドが増えます。両者の中間をとる「ハイブリッドカーネル」(macOS の XNU など)も広く使われています。

モノリシックカーネルユーザー空間アプリケーションカーネル空間(特権モード)スケジューラ・メモリ管理ドライバ・ファイルシステムネットワークスタックすべてを一つに詰め込むマイクロカーネルユーザー空間アプリケーションドライバ・FS・ネットをサービス化カーネル空間(特権モード)中核のみ切り替え・基本メモリ・メッセージ
モノリシックとマイクロの対比。前者は全機能を特権空間に詰め、後者は中核だけを残して機能をユーザー空間に追い出す

コンテナはカーネル共有、VMはカーネル別

カーネルという単位は、現代のインフラで「隔離の境界がどこにあるか」を理解する鍵になります。仮想マシン(VM)は、ハードウェアそのものを仮想化し、その上でゲストOSが自前のカーネルを丸ごと起動します。VMごとにカーネルが独立しているため隔離は非常に強いですが、OS一式を起動するぶん重く、立ち上がりも遅くなります。

一方、コンテナはカーネルを共有します。1つのホストカーネルの上で、namespace(見える範囲の分離)や cgroup(使える資源量の制限)というカーネル機能を使って、プロセスから見える世界を区切っているだけです。だからコンテナは「隔離を強化されたただのプロセス」であり、一瞬で起動でき軽量です。反面、全コンテナが同じカーネルを共有するため、カーネルの脆弱性はすべてのコンテナに影響します。「カーネルを別に持つか、共有するか」——この一点が、VMとコンテナの重さ・速さ・隔離の強さの違いをすべて説明します。