カーネル

概要

全てではないが、多くのオペレーティングシステム (OS) はカーネルを内包する。ハードウェアとソフトウェアの間の通信を管理するソフトウェアとしてのカーネルは、性能、メモリ効率、セキュリティ、プロセッサのアーキテクチャなどが複雑に絡んだ問題への妥協的解答である。
多くの場合、ブートローダーがカーネルを特権モードのプロセスとして起動する。しかし、初期化が完了すると、カーネルはいわゆるプロセスとしては存在せず、ディスクアクセスなどの高い特権レベルを必要とする処理を必要としたときにユーザプログラムから呼び出される機能の集合体として存在することになる。カーネルの処理の流れはユーザープロセスの処理の流れの延長上にあり、システムコールによってカーネルに処理が渡り、終了するとユーザーに戻っていく。初期化時のコンテキストはそのまま消えるようにする設計もあるが、「アイドルプロセス」とか「collects」と呼ばれる、プロセッサが何もすることがない時に実行されるコードに流用される設計とすることもある。省電力のため、プロセッサが「休む」ような命令を繰り返すようなコードとすることも多い。
カーネル開発はプログラミングの中でも複雑で難しいタスクのひとつと考えられる。オペレーティングシステムの中核部であるということは、高い性能を要求される最重要なソフトウェアであり、正しく設計し実装することは難しい。カーネルはユーザプログラムの互換性や移植性を考慮する必要などから、設計が制限されることもあり、そのことがさらに開発を難しくしている。

プロセス管理

カーネルの主な仕事はアプリケーションの実行を許可し、ハードウェア抽象化などの機能によってそれをサポートすることである。
プロセスは、アプリケーションがアクセスできるメモリの範囲を定義する。カーネルのプロセス管理は、ハードウェアの持つメモリ保護機構を考慮しなければならない。
カーネルはアプリケーションを実行するためアドレス空間を設定し、アプリケーションのコードを含むファイルをメモリにロードし、プログラムのためのコールスタックを設定し、そのプログラムの所定の位置に制御を渡すことで実行を開始する。
マルチタスク可能なカーネルは、ユーザーから見て実際にそのコンピュータが同時実行できるプロセス数よりも、多数のプロセスが同時並行して実行されているかのように見せかける。一般にシステムが同時並行して実行できるプロセス数は、そのシステムの持つCPU数に等しい（同時マルチスレッディングをサポートしている場合はその限りではない）。
プリエンプティブ・マルチタスクシステムでは、カーネルは各プログラムにタイムスライス（そのプログラムがCPU上で実行される連続時間）を与え、プロセスからプロセスへと高速に切り換えていくので、ユーザーから見ればそれらのプロセスが同時並行して実行されているように見えるのである。カーネルは次に実行すべきプロセスを決定し、タイムスライスの長さを決定するスケジューリングアルゴリズムを持つ。一般にプロセスには優先度が設定される。カーネルはそれらのプロセス間の通信手段も提供する。これはプロセス間通信 (IPC) と呼ばれ、パイプ、共有メモリ、メッセージ、RPC、ソフトウェア割り込みなどがある。
他に協調型マルチタスクもあり、各プロセスは自らカーネルに制御を戻すまで割り込まれずに実行を続けることができる。制御をカーネルに戻すことを "yielding" と呼び、プロセス間通信の際や何らかのイベントを待つ際に行われ、そのときにカーネルが別のプロセスを動作させる。古い Windows や Mac OS はこの方式だったが、コンピュータの性能向上に伴ってプリエンプティブ方式に切り換えた。
オペレーティングシステムは、マルチプロセッシング（SMPやNUMA）をサポートすることもある。この場合、複数のプログラムやスレッドが複数のプロセッサ上で動作する。そのようなシステムでカーネルを動作させる場合、「リエントラント（再入可能）」あるいは「割り込み可能」になるよう大幅な改造が必要となる。これはつまり、何か処理をしている最中に他からも要求を受け付けるということである。この改造ができれば、異なるプロセッサ上で動作するプログラムが同時にカーネルを呼び出しても大丈夫になる。カーネルは複数のプロセッサからのメモリアクセスを同期させる方法（スピンロックなど）も提供しなければならない。これはメモリ管理とプロセス管理にまたがる問題である。

メモリ管理

カーネルはシステムの全メモリへの無制限のアクセスが可能で、ユーザープロセスの要求に応じて安全なメモリアクセスを提供しなければならない。このための第一歩はページング方式やセグメント方式による仮想アドレッシングである。仮想記憶方式では、カーネルは物理アドレスを別のアドレス、つまり仮想アドレスに変換する。これにより、各プログラムは（カーネル以外では）仮想空間上唯一のコードに見え、プログラムが互いに他のプログラムを破壊することを防止する。
多くのシステムで、あるプログラムの仮想アドレスはメモリ上にないデータを指していることがある。仮想アドレッシングによるインダイレクション層は、本来なら主記憶 (RAM) になければならないデータをハードディスクなどの補助記憶装置に退避させることを可能にする。結果としてOSは物理的な容量以上のメモリをプログラム群に提供可能となる。RAMにないデータがあるプログラムで必要になった場合、CPUはカーネルにそれを知らせ（ページフォールト）、（必要なら）カーネルが使われていないメモリブロックの内容をディスクに退避させ、必要なデータをそのメモリブロックに復帰させる（ページ置換アルゴリズム）。すると、プログラムは要求を行った時点から処理を再開させることができる。これをデマンドページングと呼ぶ。
仮想アドレッシング方式では、仮想空間をカーネル用の部分（カーネル空間）とアプリケーション用の部分（ユーザー空間）に分けることが出来る。アプリケーションはカーネル用メモリにアクセスできないので、アプリケーションにバグがあったとしてもカーネルにダメージを与えることはない。この根本的な分離は多くの汎用カーネルで実際に使われているが、別な方式を採用したカーネルの研究も行われている（例えば、Singularity）。
メモリ管理のもうひとつの機能として、カーネル内の各モジュールやデバイスドライバが使用するメモリの割り当てがある（動的メモリアロケーション）。

プロセスの協調作動

エドガー・ダイクストラは論理的観点から、バイナリセマフォにおける不可分なロックとアンロック操作だけで、プロセス間の任意の協調作動を実現できることを証明した。しかしそのような方式は一般に安全性や効率性が欠如しており、メッセージパッシング方式の方が柔軟性が高い。他の方式もいくつかあり、現代のカーネルでは共有メモリやRPCなどのシステムをサポートしていることが多い。

入出力デバイス管理

入出力デバイスを並行して協調作動する他のプロセス群から一様に扱えるようにするというカーネルの考え方は、Per Brinch Hansen が提唱し実装したのが最初である（似たような考え方は1967年にも示唆されていた）。Hansen はその説明で、「共通の」プロセス群を「内部プロセス」、入出力デバイスを「外部プロセス」と呼んでいる。
物理メモリと同様、アプリケーションがコントローラのポートやレジスタに直接アクセスすることを許可すると、コントローラが不正作動したり、システムがクラッシュすることになる。それに加えて、デバイスの複雑さに応じて対応するプログラムは非常に複雑化することがあり、しかも複数の異なるコントローラを使うことがある。そのため、デバイスを管理するためのより抽象化されたインタフェースを提供することが重要である。この抽象化を提供するのは一般にデバイスドライバや Hardware Abstraction Layer (HAL) である。アプリケーションは必要なら頻繁にデバイスへのアクセスを要求する。カーネルはシステムに接続されたデバイスの一覧を何らかの方法で保持しなければならない。これはBIOSや各種システムバスの機能（PCI/PCIeやUSB）を使ってなされる。あるアプリケーションがあるデバイス操作を要求すると（例えばディスプレイに文字を表示する）、カーネルは対応するドライバ（例えばビデオドライバ）に要求を送らなければならない。するとそのドライバがデバイスに対して必要な処理を行う。マイクロカーネルの場合、この際にプロセス間通信 (IPC) が使われる。

カーネル全体の設計方針

もちろん、上述したタスク群や機能群の提供方法は設計や実装の面で様々である。
「機構と方針の分離」の原則は、マイクロカーネルとモノリシックカーネルの哲学の間でかなり大きな相違がある。ここで、「機構」は様々な「方針」の実装を可能とするものであり、「方針」は特定の「操作のモード」である。例えば「機構」面では、ユーザーがログインしようとしたとき認証サーバを呼び出してアクセスを認めるべきか否かを決定するということが考えられる。一方「方針」面では、認証サーバがパスワードを要求し、データベース内の暗号化されたパスワードと照合するかもしれない。機構が汎用的であれば、機構と方針が同一モジュールに統合されている場合よりも方針の変更（例えば、パスワードの代わりにセキュリティトークンを使うなど）がより容易になる。
最小のマイクロカーネルでは非常に基本的な方針のみが含まれ、その機構はカーネル上で動作させるもの（OSの残りの部分やアプリケーション群）自身がどのような方針（メモリ管理、高度なプロセススケジューリング、ファイルシステム管理など）を採用するか決定することを可能にする。一方モノリシックカーネルは方針の大部分をカーネル内に含む傾向があり、結果としてその上の部分の自由度は制限される。
Per Brinch Hansen は機構と方針の分離のための主張を展開した。すなわち、この分離が不適切であることが既存のOSで本質的技術革新が見られないことの主要因だとし、コンピュータアーキテクチャにおける共通課題だとした。モノリシック設計は、従来の商用システムで一般的な保護技法である「カーネルモード」と「ユーザーモード」に分離するアーキテクチャ（いわゆるリングプロテクション）から生まれた。そのアーキテクチャでは、保護（プロテクション）を必要とするモジュールを可能な限りカーネルに含めようとする。このようなモノリシック設計と特権モードの関係が機構と方針の分離における重要な問題として再注目されている。実際「特権モード」のアーキテクチャ技法は保護機構とセキュリティ方針を融合させる傾向があるが、これとは大きく異なるアーキテクチャ技法であるケイパビリティベースドアドレッシングではその2つを明確に区別し、自然にマイクロカーネル設計が可能となる。
モノリシックカーネルはカーネルの全コードを同じアドレス空間（カーネル空間）で実行するが、マイクロカーネルでは多くのサービスをユーザー空間で実行しようとし、コードベースの保守性とモジュール性を向上させようとしている。多くのカーネルは明確にどちらかに分類できるわけではなく、その中間の実装とも言うべきハイブリッドカーネルになっている。さらに特殊な設計としてナノカーネルやエクソカーネルが研究されているが、広く使われるまでには至っていない。エクソカーネルの例としてXenハイパーバイザがある。

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

カーネルが巨大化するにつれて、様々な問題が明らかになってきた。最も明らかな問題はカーネルの大きさ（メモリ使用量）の増大である。これは仮想記憶をカーネル空間にも適用することである程度まで和らげられるが、全てのコンピュータ・アーキテクチャが仮想記憶をサポートできるわけではない。カーネルのサイズを削減するため、不要なコードを削除するなどの改善が必要となるが、これはカーネルの各モジュール間の明らかにされていない依存関係があるために非常に困難である。
マイクロカーネルと比較したときのモノリシックカーネルの様々な欠点から、1990年代の初期までにモノリシックカーネルは時代遅れと考えられるに至った。結果としてLinuxがモノリシックカーネルを採用したことでリーナス・トーバルズとアンドリュー・タネンバウムの間で有名な論争が発生した（アンドリュー・タネンバウムとリーナス・トーバルズの議論）。この議論では、両者の言い分にそれぞれメリットがある。
モノリシックカーネルは設計が容易で、マイクロカーネルよりも迅速に成長することが期待できる。しかし、モノリシックカーネル内のバグは一般にシステムクラッシュを引き起こすのに対して、マイクロカーネルでは一部のサーバに問題が限定される。モノリシックカーネルの支持者は、不正なコードがカーネルに無ければマイクロカーネルの利点はほとんどないと論じる。どちらの側にも成功例がある。マイクロカーネルはロボットや医療用システムで使われており、各コンポーネントが別々の保護されたメモリ空間で動作する。これは最新のモジュールロード方式であってもモノリシックカーネルには不可能であろう。モノリシックカーネルは共有型カーネルメモリを使用するよう最適化されていて、マイクロカーネルのような低速なメッセージ渡しとは異なる。

ナノカーネル

ナノカーネルは全てのサービスをデバイスドライバとして分離する。これには例えば最も基本的な割り込みコントローラやタイマーの制御も含まれる。これによりカーネルメモリはマイクロカーネルよりもさらに小さくなる。

エクソカーネル

エクソカーネル (exokernel) はまだ実験段階のOS設計技法である。他のカーネルとの違いは、物理ハードウェアのプロテクションと多重化に機能を限定している点で、アプリケーションに対して全くハードウェアの抽象化を提供しない。このようにハードウェアのプロテクションをハードウェア管理から分離することで、利用可能なハードウェアを最大限に生かすように個々のプログラムを開発できるという利点が生じる。
エクソカーネル自体は非常に小さい。しかし、通常のOSの持つ機能をアプリケーション開発者に提供するためのライブラリ型OSを伴う。エクソカーネル型システムの最大の利点は、このライブラリ型OS機能を複数用意できるという点で、それぞれが異なるAPIを提供できる。例えば同じシステム上で、高度なUIを持つアプリケーションを開発し、同時にリアルタイムシステム制御を行うアプリケーションも開発できる。

初期のOSカーネル

厳密に言えば、オペレーティングシステム（とカーネル）はコンピュータを動作させるのに必須ではない。プログラムはマシン上に直接ロードされ実行されることも可能であり、そのようなプログラムはOSのサービスや抽象化なしで記述しなければならない。多くの初期のコンピュータではそのような手法が一般的であり、プログラムを入れ替えるときにリセットとリロードが必要だった。その後、プログラムローダーやデバッガといった補助的な小さなプログラムがメモリに常駐したり、ROMからロードされるようになった。これらが初期のオペレーティングシステムのカーネルの元となった。直接実行の手法は今日でもゲーム機や組み込みシステムで使われているが、一般に最近のコンピュータではオペレーティングシステムとカーネルが使われている。
1969年の RC 4000 Multiprogramming System では、小さな中核部の上で異なる目的のOS群を整然とした方法で構築するというシステム設計哲学を導入しており、マイクロカーネル方式のさきがけとなっている。

タイムシェアリングOS

UNIX以前の10年間、コンピュータは劇的に能力を向上させ、マシンの未使用時間を使う手法が求められた。この期間の主な開発のひとつがタイムシェアリングシステム (TSS) である。TSSは何人かのユーザーがCPUのタイムスライスをそれぞれ割り当てられる。
タイムシェアリングシステムの開発は多くの問題を発生させた。ひとつの問題は大学のユーザーはCPU時間が欲しいというよりもシステムをハックしたがっているという点である。このためセキュリティやアクセス制御が1965年のMulticsプロジェクトの重要な課題となった。もうひとつの問題は計算リソースの正しい扱い方である。ユーザーは計算リソースを使わずに画面を凝視することにほとんどの時間を費やしており、タイムシェアリング方式ではそのようなCPU時間を他のユーザーに与えるべきと考えられた。最終的に、メモリ階層の多層化が進み、リソースの分割が仮想記憶システムの開発へと繋がっていったのである。

UNIX

UNIXの設計段階で、全ての高レベルのデバイスをファイルとして抽象化することが決定された。何故ならUNIX設計者は情報処理の目的をデータの変換であると考えていたからである。
例えば、プリンターもファイルとして抽象化され、データをそのファイルにコピーすると印字が行われる。他のシステムでは同様の機能を提供するにあたって、デバイスを低レベルに抽象化する傾向があった。デバイスもファイルも何らかの低レベルの概念の実体化である。システムをファイルのレベルで仮想化したことにより、ユーザは既存のファイル管理機能と概念で全てを扱うことができるようになり、操作が大幅に簡略化された。同じパラダイムを拡張して、UNIXはファイルを複数の小さなプログラムで操作するパイプの概念を可能とした。最終的な結果は同じであっても、このような小さなプログラム群を使うことで柔軟性が劇的に向上しただけでなく、開発も利用も容易になった。
UNIXでは、オペレーティングシステムは2つの部分で構成される。様々な操作を実行するユーティリティプログラム群とカーネルである。プログラミングの観点から見ると両者の違いは小さい。カーネルは特権モードで動作するプログラムであり、プログラムローダーとしての役割とシステムの残りの部分を構成するユーティリティプログラム群を監督する役割を持つ。そして、それらプログラムにロックと入出力サービスを提供する。つまり、カーネルはあらゆる場面に介在しているわけではなかった。
その後、計算モデルが変化し、UNIXの何でもファイルで表す手法が常に適用可能な方法ではなくなってきた。端末はファイルで表せるが（どちらも文字列を読み書きできる）、GUIはそのように扱うことはできない。コンピュータネットワークは別の問題を提起した。ネットワーク経由の通信はファイルアクセスに対応させることができるが、低レベルのパケット指向アーキテクチャはファイルというよりも離散的なデータの塊として扱う必要がある。コンピュータの機能が拡大するにつれ、UNIXのコードは増大していった。それはまた、UNIXカーネルのモジュール性が非常にスケーラブルなためでもあった。初期のカーネルのソースは10万行ほどだったが、Linuxカーネルなどでは1300万行にもなっている。
現代のUnix系OSは、モノリシックカーネルにモジュールローディング機能を加えたものとなっている。例えば、Linuxカーネルを採用した各種Linuxディストリビューションや、BSDの子孫である FreeBSD、DragonFly BSD、OpenBSD、NetBSD、macOS などがある。

Mac OS

アップルは1984年、最初のMac OSを同社のパーソナルコンピュータMacintoshに同梱して発売した。後継のmacOSはDarwinをベースとしており、4.4BSDユーザーランドとMachカーネルを統合したXNUと呼ばれるハイブリッドカーネルを採用している。

Microsoft Windows

Microsoft Windowsは1985年、MS-DOSへのアドオンとしてリリースされた。他のOSに依存していたため、Windows 95までのリリースはOSではなくオペレーティング環境とみなされている。その製品ラインは発展して、Windows 9x系となり（32ビット化やプリエンプティブ・マルチタスクといった強化を経て）、最終的に2000年にWindows Meがリリースされた。マイクロソフトはまた、ハイエンド向けにWindows NT のラインも1993年の Windows NT 3.1からスタートさせ、こちらは2000年以降も続いている。
2001年10月にリリースされたWindows XPでWindows 9x系を置換して一般ユーザー向けOSが一新された。Windows NT系のカーネルはWindow ManagerやIPC Managerとクライアント・サーバ型階層型サブシステムモデルを採用しており、ハイブリッドカーネルとみなされている。

マイクロカーネルの開発

汎用マイクロカーネルとしてはカーネギーメロン大学が1985年から1994年まで開発したMachが有名だが、特定用途向けにもいくつかのマイクロカーネルが開発された。L4はマイクロカーネルの性能が悪くないことを実証するために作られた。ここから派生した新たな実装の Fiasco や Pistachio はLinuxをその上で動作させることができる。
QNXはマイクロカーネル設計を採用したリアルタイムオペレーティングシステムであり、1980年代初期に開発され、Machよりも遥かに成功している。ソフトウェアが不正作動することが致命的な状況で使われることが多く、スペースシャトルのロボットアームの制御やガラスを精密に磨く機械の制御で使われている。