
カーネルは、コンピュータのオペレーティングシステムの中核を成すコンピュータ プログラムであり、システム内のすべてを常に完全に制御します。カーネルはまた、異なるプロセス間の競合を防止および軽減する役割も担っています。[ 1 ]これは、常にメモリに常駐するオペレーティングシステムの部分であり[ 2 ]、ハードウェア コンポーネントとソフトウェア コンポーネント間の相互作用を促進します。完全なカーネルは、デバイス ドライバを介してすべてのハードウェア リソース ( I/O、メモリ、暗号化など) を制御し、そのようなリソースに関するプロセス間の競合を仲裁し、 CPU、キャッシュ、ファイルシステム、ネットワーク ソケットなどの共通リソースの使用を最適化します。ほとんどのシステムでは、カーネルは起動時に最初にロードされるプログラム(ブート ローダの後) の 1 つです。カーネルは、起動の残りの部分と、ソフトウェアからのメモリ、周辺機器、および入出力(I/O) 要求を処理し、それらを中央処理装置 (CPU)のデータ処理命令に変換します。
カーネルの重要なコードは通常、アプリケーション ソフトウェアやオペレーティングシステムの重要度の低い部分からのアクセスから保護された、メモリの別の領域にロードされます。カーネルは、プロセスのスケジューリング、ハードディスクなどのハードウェア デバイスの管理、割り込みの処理などのタスクを、この保護されたカーネル スペースで実行します。一方、ブラウザ、ワード プロセッサ、オーディオ プレーヤーやビデオ プレーヤーなどのアプリケーション プログラムは、ユーザー スペースと呼ばれる別のメモリ領域を使用します。これにより、ユーザー データとカーネル データが互いに干渉して不安定性や動作の遅さを引き起こすことを防ぎ、[ 1 ]また、誤動作したアプリケーションが他のアプリケーションに影響を与えたり、オペレーティングシステム全体をクラッシュさせたりすることを防ぎます。カーネルがアプリケーションのアドレス空間に含まれているシステムでも、メモリ保護を使用して、許可されていないアプリケーションがカーネルを変更することを防止します。
カーネルのインターフェースは低レベルの抽象化レイヤーです。プロセスがカーネルにサービスを要求する場合、通常はラッパー関数を介してシステムコールを呼び出す必要があります。
カーネルのアーキテクチャ設計にはさまざまな種類があります。モノリシックカーネルは、主に速度向上を目的として、単一のアドレス空間で完全に実行され、 CPUはスーパーバイザモードで実行されます。マイクロカーネルは、主に耐障害性とモジュール性を目的として、ユーザープロセスと同様に、サービスのほとんどすべてではなく、ユーザー空間で実行されます。[ 3 ] MINIX 3は、マイクロカーネル設計の注目すべき例です。Linuxカーネルなどの一部のカーネルは、実行時にロード可能なカーネルモジュールを挿入および削除できるため、モノリシックかつモジュール式です。
コンピュータシステムの中核を成すこの構成要素は、プログラムの実行を担います。カーネルは、実行中の多数のプログラムのうち、どのプログラムをプロセッサに割り当てるかを常に決定する責任を負います。
ランダムアクセスメモリ(RAM)は、プログラム命令とデータの両方を格納するために使用されます。[ a ]通常、プログラムを実行するには、両方がメモリに存在する必要があります。多くの場合、複数のプログラムがメモリへのアクセスを必要とし、コンピュータが利用できるメモリよりも多くのメモリを要求することがよくあります。カーネルは、各プロセスが使用できるメモリを決定し、メモリが不足している場合にどうするかを決定する役割を担っています。
入出力デバイスには、キーボード、マウス、ディスクドライブ、プリンタ、USBデバイス、ネットワークアダプタ、ディスプレイデバイスなどの周辺機器が含まれますが、これらに限定されません。カーネルは、アプリケーションがこれらのデバイスを使用するための便利な方法を提供します。これらのデバイスは通常、カーネルによって抽象化されているため、アプリケーションは実装の詳細を知る必要がありません。
リソース管理に必要な主要な側面は、実行ドメイン(アドレス空間)の定義と、ドメイン内のリソースへのアクセスを仲介するために使用される保護メカニズムです。 [ 5 ]カーネルは、同期とプロセス間通信(IPC)のためのメソッドも提供します。これらの実装はカーネル自体の中にある場合もあれば、カーネルが実行中の他のプロセスに依存する場合もあります。カーネルは、互いが提供する機能へのアクセスを提供するためにIPCを提供する必要がありますが、カーネルは実行中のプログラムにこれらの機能へのアクセスを要求する方法も提供する必要があります。カーネルは、プロセス間またはスレッド間のコンテキスト切り替えも担当します。
カーネルはシステムのメモリに完全にアクセスでき、プロセスが必要に応じてこのメモリに安全にアクセスできるようにする必要があります。多くの場合、これを行う最初のステップは仮想アドレス指定であり、通常はページングやセグメンテーションによって実現されます。仮想アドレス指定により、カーネルは特定の物理アドレスを別のアドレス、つまり仮想アドレスのように見せかけることができます。仮想アドレス空間はプロセスごとに異なる場合があります。あるプロセスが特定の(仮想)アドレスでアクセスするメモリは、別のプロセスが同じアドレスでアクセスするメモリとは異なる場合があります。これにより、すべてのプログラムは(カーネルを除いて)実行されている唯一のプログラムであるかのように動作し、アプリケーション同士がクラッシュするのを防ぐことができます。[ 6 ]
多くのシステムでは、プログラムの仮想アドレスが、現在メモリに存在しないデータを参照する場合があります。仮想アドレス指定によって提供される間接参照層により、オペレーティングシステムは、本来メインメモリ(RAM )に保持しなければならないデータを、ハードドライブなどの他のデータストアに保存することができます。その結果、オペレーティングシステムは、システムが物理的に利用可能なメモリよりも多くのメモリをプログラムが使用することを許可できます。プログラムが現在RAMに存在しないデータを必要とする場合、CPUはカーネルにその旨を通知し、カーネルは、必要に応じて非アクティブなメモリブロックの内容をディスクに書き込み、プログラムが要求したデータで置き換えることで応答します。その後、プログラムは停止した時点から再開できます。この仕組みは一般にデマンドページングと呼ばれます。
仮想アドレス指定により、メモリを2つの独立した領域に仮想的に分割することが可能になります。1つはカーネル(カーネル空間)用に、もう1つはアプリケーション(ユーザー空間)用に確保されます。プロセッサはアプリケーションがカーネルメモリにアクセスすることを許可しないため、アプリケーションが実行中のカーネルを損傷するのを防ぐことができます。この基本的なメモリ空間の分割は、現在の汎用カーネルの設計に大きく貢献しており、そのようなシステムではほぼ普遍的に採用されていますが、一部の研究用カーネル(例:Singularity)は別のアプローチを採用しています。
有用な機能を実行するために、プロセスはコンピュータに接続された周辺機器にアクセスする必要があります。周辺機器は、カーネルによってデバイスドライバを介して制御されます。デバイスドライバは、OSに代わってハードウェアデバイスをカプセル化、監視、制御するコンピュータプログラムです(ハードウェア/ソフトウェアインターフェイス(HSI)を介して)。デバイスドライバは、オペレーティングシステムに、特定のハードウェアを制御して通信する方法に関するAPI、手順、および情報を提供します。デバイスドライバは、すべてのOSとそのアプリケーションにとって重要かつ不可欠な依存関係です。ドライバの設計目標は抽象化です。ドライバの機能は、OSによって義務付けられた抽象的な関数呼び出し(プログラミング呼び出し)をデバイス固有の呼び出しに変換することです。理論的には、デバイスは適切なドライバで正しく動作するはずです。デバイスドライバは、ホストアダプタ、ビデオカード、サウンドカード、プリンタ、スキャナ、モデム、ネットワークインターフェイスコントローラなどに使用されます。
ハードウェアレベルでは、デバイスドライバの一般的な抽象化には以下のようなものがあります。
ソフトウェアレベルでは、デバイスドライバの抽象化には以下が含まれます。
例えば、ユーザーに画面上に何かを表示するために、アプリケーションはカーネルにリクエストを送信し、カーネルはそのリクエストをディスプレイドライバに転送し、ディスプレイドライバが実際に文字/ピクセルを描画する責任を負います。[ 6 ]
カーネルは、使用可能なデバイスのリストを保持する必要があります。このリストは、事前にわかっている場合(たとえば、使用可能なハードウェアが変更された場合にカーネルが書き換えられる組み込みシステムの場合)、ユーザーによって構成される場合(古いPCや個人使用向けに設計されていないシステムで一般的)、または実行時にオペレーティングシステムによって検出される場合(通常はプラグアンドプレイと呼ばれる)があります。プラグアンドプレイシステムでは、デバイスマネージャはまず、 PCI(Peripheral Component Interconnect )やUSB( Universal Serial Bus )などのさまざまな周辺バスをスキャンしてインストールされているデバイスを検出し、次に適切なドライバを検索します。
デバイス管理はOS固有のトピックであるため、これらのドライバはカーネル設計の種類によって異なる方法で処理されますが、いずれの場合も、カーネルはドライバがポートまたはメモリ位置を介して物理的にデバイスにアクセスできるようにI/Oを提供する必要があります。デバイス管理システムを設計する際には重要な決定を下す必要があります。設計によっては、アクセスにコンテキストスイッチが伴う場合があり、処理が非常にCPU負荷の高いものとなり、パフォーマンスに大きなオーバーヘッドが発生する可能性があるためです。
コンピューティングにおいて、システムコールとは、プロセスが通常実行権限を持たないサービスをオペレーティングシステムのカーネルに要求する方法です。システムコールは、プロセスとオペレーティングシステム間のインターフェースを提供します。システムとやり取りするほとんどの操作には、ユーザーレベルのプロセスには利用できない権限が必要です。例えば、システム上に存在するデバイスとの入出力処理や、他のプロセスとのあらゆる形式の通信には、システムコールの使用が不可欠です。
システムコールとは、アプリケーションプログラムがオペレーティングシステムにサービスを要求するために使用するメカニズムです。システムコールは、プロセッサのモードを変更するマシンコード命令を使用します。たとえば、スーパーバイザモードからプロテクトモードへの切り替えなどが挙げられます。このモードでは、オペレーティングシステムがハードウェアデバイスやメモリ管理ユニットへのアクセスなどの処理を実行します。一般的に、オペレーティングシステムは、オペレーティングシステムと通常のユーザープログラムの間にあるライブラリを提供します。通常は、GlibcやWindows APIなどのCライブラリです。このライブラリは、カーネルへの情報伝達やスーパーバイザモードへの切り替えといった低レベルの詳細を処理します。システムコールには、close、open、read、wait、writeなどがあります。
実際に有用な作業を行うには、プロセスはカーネルが提供するサービスにアクセスできる必要があります。これはカーネルごとに実装方法が異なりますが、ほとんどのカーネルはCライブラリまたはAPIを提供し、それによって関連するカーネル関数が呼び出されます。[ 7 ]
カーネル関数を呼び出す方法はカーネルによって異なります。メモリ分離が使用されている場合、プロセッサのアクセス制御規則に違反するため、ユーザープロセスがカーネルを直接呼び出すことはできません。いくつかの可能性を以下に示します。
カーネルの設計において重要な考慮事項は、障害からの保護(フォールトトレランス)と悪意のある動作からの保護(セキュリティ)に対するサポートです。これら 2 つの側面は通常明確に区別されておらず、カーネルの設計でこの区別を採用すると、保護のための階層構造が拒否されます。[ 5 ]
カーネルが提供するメカニズムやポリシーは、以下のようないくつかの基準に基づいて分類できます。
階層的な保護ドメイン[ 13 ]のサポートは、通常CPUモードを使用して実装されます。
多くのカーネルは「機能」を実装しています。これは、カーネルが管理する基となるオブジェクトへの限定的なアクセスをユーザーコードに提供するオブジェクトです。一般的な例としてファイル処理が挙げられます。ファイルは、永続ストレージデバイスに保存された情報の表現です。カーネルは、読み取り、書き込み、削除、実行など、さまざまな操作を実行できますが、ユーザーレベルのアプリケーションは、これらの操作の一部のみを実行できる場合があります(たとえば、ファイルの読み取りのみが許可される場合があります)。この一般的な実装では、カーネルがアプリケーションにオブジェクト(通常は「ファイルハンドル」と呼ばれます)を提供し、アプリケーションはそのオブジェクトに対して操作を実行できます。カーネルは、操作が要求された時点で、このオブジェクトの有効性をチェックします。このようなシステムは、カーネルが管理するすべてのオブジェクト、さらには他のユーザーアプリケーションが提供するオブジェクトにも拡張できます。
機能のハードウェアサポートを提供する効率的でシンプルな方法は、メモリ管理ユニット(MMU)にすべてのメモリアクセスに対するアクセス権のチェックの責任を委任することであり、これは機能ベースのアドレッシングと呼ばれるメカニズムです。[ 14 ]ほとんどの商用コンピュータアーキテクチャには、このような機能のMMUサポートがありません。機能ハードウェア拡張RISC命令(CHERI)プロジェクトは、いくつかのアーキテクチャ向けに機能ベースのメカニズムを開発しています。
別の方法として、一般的にサポートされている階層ドメインを使用して機能をシミュレートする方法があります。この方法では、保護された各オブジェクトは、アプリケーションがアクセスできないアドレス空間に存在する必要があります。カーネルは、そのようなメモリ内に機能のリストも保持します。アプリケーションが機能によって保護されたオブジェクトにアクセスする必要がある場合、システムコールを実行し、カーネルはアプリケーションの機能が要求されたアクションを実行する許可を与えているかどうかを確認し、許可されている場合は、そのオブジェクトに対してアクセスを実行します(直接アクセスするか、要求を別のユーザーレベルプロセスに委任します)。アドレス空間切り替えのパフォーマンスコストにより、オブジェクト間の複雑な相互作用があるシステムではこの方法の実用性が制限されますが、現在のオペレーティングシステムでは、頻繁にアクセスされないオブジェクトや、高速な動作が期待されないオブジェクトに対して使用されています。[ 15 ] [ 16 ]
ファームウェアが保護メカニズムをサポートしていない場合、ページテーブルを操作して機能をシミュレートするなど、より高レベルで保護をシミュレートすることは可能ですが、パフォーマンスに影響があります。[ 17 ]ただし、言語ベースの保護を使用することを選択するシステムでは、ハードウェアのサポートがないことは問題にならない場合があります。[ 18 ]
カーネル設計における重要な決定事項は、セキュリティメカニズムとポリシーを実装する抽象化レベルの選択です。カーネルセキュリティメカニズムは、より高いレベルでのセキュリティをサポートする上で重要な役割を果たします。[ 14 ] [ 19 ] [ 20 ] [ 21 ] [ 22 ]
一つのアプローチは、耐障害性のためにファームウェアとカーネルのサポートを利用し(上記参照)、その上に悪意のある動作に対するセキュリティポリシーを構築する(必要に応じて暗号化メカニズムなどの機能を追加し)、コンパイラに一部の責任を委任することです。セキュリティポリシーの適用をコンパイラやアプリケーションレベルに委任するアプローチは、しばしば言語ベースのセキュリティと呼ばれます。
現在の主流のオペレーティングシステムには多くの重要なセキュリティメカニズムが欠けているため、アプリケーション抽象化レベルで適切なセキュリティ ポリシーを実装することが困難です。[ 19 ]実際、コンピュータ セキュリティにおけるよくある誤解は、カーネル サポートに関係なく、アプリケーションで任意のセキュリティ ポリシーを実装できるというものです。[ 19 ]
Mars Research Groupの開発者によると、分離の欠如はカーネルのセキュリティを損なう主な要因の1つである。[ 23 ]彼らは、主にLinuxカーネルにおける保護のために、ドライバ分離フレームワークを提案している。[ 24 ] [ 25 ]
今日の一般的なコンピュータシステムでは、どのプログラムがどのデータにアクセスできるかについて、ハードウェアによって強制されるルールが使用されています。プロセッサは実行を監視し、カーネルメモリへの書き込みを試みるユーザープロセスなど、ルールに違反するプログラムを停止します。機能のサポートがないシステムでは、プロセスは別々のアドレス空間を使用することで互いに分離されます。[ 26 ]ユーザープロセスからカーネルへの呼び出しは、上記のシステムコール方法のいずれかを使用することを要求することによって規制されます。
別の方法として、言語ベースの保護を使用する方法があります。言語ベースの保護システムでは、カーネルは信頼できる言語コンパイラによって生成されたコードの実行のみを許可します。言語は、プログラマがセキュリティ要件に違反するようなことを指示できないように設計できます。[ 18 ]
このアプローチの利点は以下のとおりです。
デメリットとしては、以下のような点が挙げられます。
言語ベースの保護機能を備えたシステムの例としては、JXやマイクロソフトのSingularityなどが挙げられる。
Edsger Dijkstra は、論理的な観点から、バイナリセマフォ上で動作するアトミックロックおよびアンロック操作は、プロセス協調のあらゆる機能を表現するのに十分なプリミティブであることを証明しました。[ 27 ]それにもかかわらず、このアプローチは一般的に安全性と効率性の点で不十分であると考えられており、メッセージ パッシングアプローチの方が柔軟性があります。[ 28 ] 他にも多くのアプローチ (低レベルまたは高レベル) が利用可能であり、多くの最新のカーネルは共有メモリやリモート プロシージャ コールなどのシステムをサポートしています。
I/O デバイスが他のプロセスと並列に協調するプロセスとして統一的に処理されるカーネルのアイデアは、Brinch Hansenによって最初に提案され、実装されました(ただし、同様のアイデアは 1967 年に提案されていました[ 29 ] [ 30 ] )。Hansen のこの説明では、「共通」プロセスは内部プロセスと呼ばれ、I/O デバイスは外部プロセスと呼ばれています。[ 28 ]
物理メモリと同様に、アプリケーションがコントローラのポートやレジスタに直接アクセスできるようにすると、コントローラが誤動作したり、システムがクラッシュしたりする可能性があります。そのため、デバイスの複雑さによっては、プログラミングが驚くほど複雑になり、複数の異なるコントローラを使用するデバイスもあります。このため、デバイスを管理するためのより抽象的なインターフェースを提供することが重要です。このインターフェースは通常、デバイスドライバまたはハードウェア抽象化レイヤーによって実現されます。アプリケーションは、これらのデバイスへのアクセスを必要とすることがよくあります。カーネルは、何らかの方法でシステムに問い合わせて、これらのデバイスのリストを維持する必要があります。これは、BIOS を介して、またはさまざまなシステムバス (PCI/PCIE や USB など) を介して行うことができます。ビデオドライバを例にとると、アプリケーションが文字の表示などのデバイス操作を要求すると、カーネルはこの要求を現在アクティブなビデオドライバに送信する必要があります。ビデオドライバは、この要求を実行する必要があります。これは、プロセス間通信(IPC) の一例です。
上記に挙げたタスクや機能は、設計や実装において互いに異なる様々な方法で提供することができる。
メカニズムとポリシーの分離の原則は、マイクロカーネルとモノリシックカーネルという2つの主要な哲学の本質的な違いである。[ 31 ] [ 32 ]ここで、メカニズムは多くの異なるポリシーの実装を可能にするサポートであり、ポリシーは特定の「動作モード」である。例:
メカニズムとポリシーが分離されているため、ポリシーは簡単に変更でき、たとえばセキュリティトークンの使用を必須にすることができます。
最小限のマイクロカーネルにはごく基本的なポリシーのみが含まれており、[ 32 ]そのメカニズムにより、カーネル上で実行されているもの(オペレーティングシステムの残りの部分と他のアプリケーション)が、どのポリシーを採用するかを決定できます(メモリ管理、高レベルのプロセススケジューリング、ファイルシステム管理など)。[ 5 ] [ 28 ]一方、モノリシックカーネルには多くのポリシーが含まれる傾向があり、そのためシステムの残りの部分はそれらに依存するように制限されます。
コンピュータ科学者のパー・ブリンチ・ハンセンは、メカニズムとポリシーの分離を主張した。[ 5 ] [ 28 ]この分離を適切に実行できないことが、既存のオペレーティングシステムにおける実質的なイノベーションの欠如の主な原因の 1 つは、コンピュータ アーキテクチャでよく見られる問題である。[ 5 ] [ 33 ] [ 34 ] [ 35 ]モノリシック設計は、従来の商用システムで一般的な保護に対する「カーネル モード」/「ユーザー モード」のアーキテクチャ アプローチ (技術的には階層的保護ドメインと呼ばれる) によって誘発される。[ 36 ]実際、保護を必要とするすべてのモジュールは、カーネルに含めることが望ましい。[ 36 ]このモノリシック設計と「特権モード」の間のリンクは、メカニズムとポリシーの分離という重要な問題に再帰することができる。[ 5 ]実際、「特権モード」アーキテクチャアプローチは保護メカニズムとセキュリティポリシーを融合させていますが、主要な代替アーキテクチャアプローチである能力ベースのアドレッシングは、この2つを明確に区別し、マイクロカーネル設計へと自然に導きます。[ 5 ]
モノリシックカーネルはすべてのコードを同じアドレス空間(カーネル空間)で実行するのに対し、マイクロカーネルは保守性とモジュール性を向上させる目的で、サービスのほとんどをユーザー空間で実行しようとします。[ 4 ]ほとんどのカーネルはこれらのカテゴリのどちらかに正確には当てはまらず、むしろこの2つの設計の中間に位置します。これらはハイブリッドカーネルと呼ばれます。ナノカーネルやエクソカーネルなどのより特殊な設計も存在しますが、本番システムで使用されることはほとんどありません。たとえば、Xenハイパーバイザはエクソカーネルです。

モノリシックカーネルでは、すべてのOSサービスがカーネルの一部であり、カーネルモードで実行されるため、同じメモリ領域に存在します。このアプローチにより、豊富で強力なハードウェアアクセスが可能になります。UNIX開発者のケン・トンプソンは、「モノリシックカーネルを実装する方が簡単だ」と述べています。[ 37 ]モノリシックカーネルの主な欠点は、システムコンポーネント間の依存関係(たとえば、デバイスドライバのバグがシステム全体をクラッシュさせる可能性がある)と、大規模なカーネルの保守が非常に困難になる可能性があることです。トンプソンはまた、「モノリシックカーネルは、変更されるとすぐに混乱状態に陥りやすい」とも述べています。[ 37 ]
従来Unix系オペレーティングシステムで使用されてきたモノリシックカーネルには、オペレーティングシステムのコア機能とデバイスドライバがすべて含まれています。モノリシックカーネルは、カーネル関連のあらゆるタスクを実行するために必要なコードをすべて含む単一のプログラムです。ドライバ、スケジューラ、メモリ管理、ファイルシステム、ネットワークスタックなど、プログラムからアクセスされるもののライブラリに格納できない部分はすべてカーネル空間にあります。アプリケーションがこれらのサービスすべてにアクセスできるように、多くのシステムコールが提供されています。モノリシックカーネルは、初期状態では不要なサブシステムが含まれている場合もありますが、ハードウェア専用に設計されたカーネルと同等かそれ以上の速度に調整することが可能です。ただし、より一般的な意味ではモノリシックカーネルの方が適しています。
Linuxカーネル、FreeBSDカーネル、AIXカーネル、HP-UXカーネル、Solarisカーネルなど、Unix系オペレーティングシステムに分類される最新のモノリシックカーネルは、ロード可能なカーネルモジュールをサポートしており、実行時にカーネルにモジュールをロードできるため、必要に応じてカーネルの機能を容易に拡張できるとともに、カーネル空間で実行されるコード量を最小限に抑えることができます。
モノリシックカーネルのほとんどの処理はシステムコールを介して行われます。これらは通常、表形式の構造で保持されるインターフェースであり、ディスク操作などカーネル内のサブシステムにアクセスします。基本的に、呼び出しはプログラム内で行われ、要求のチェック済みコピーがシステムコールを介して渡されます。したがって、移動距離はそれほど長くありません。モノリシックLinuxカーネルは、モジュールを動的にロードできるだけでなく、カスタマイズが容易であるため、非常に小さくすることができます。実際、多数のユーティリティやその他のプログラムを1枚のフロッピーディスクに収めても、完全に機能するオペレーティングシステムを提供できるほど小さいバージョンもいくつかあります(最も人気のあるものの1つはmuLinuxです)。カーネルを小型化できるこの能力は、組み込みシステムでのLinuxの使用の急速な増加にもつながっています。
これらのタイプのカーネルは、オペレーティングシステムのコア機能と、実行時にモジュールをロードできるデバイスドライバで構成されています。これらは、基盤となるハードウェアの豊富で強力な抽象化を提供します。シンプルなハードウェア抽象化の小さなセットを提供し、サーバーと呼ばれるアプリケーションを使用してより多くの機能を提供します。この特定のアプローチでは、ハードウェア上に高レベルの仮想インターフェースを定義し、一連のシステムコールを使用して、プロセス管理、並行処理、メモリ管理などのオペレーティングシステムサービスを、スーパーバイザモードで実行される複数のモジュールで実装します。この設計には、いくつかの欠点と制限があります。

マイクロカーネル(μKまたはuKと略されることもある)とは、オペレーティングシステムの設計手法の一つで、システムの機能を従来の「カーネル」から切り離し、「最小限の」カーネルを介して通信する「サーバ」群に移行することで、「システム空間」にはできるだけ少ないリソースを、「ユーザー空間」にはできるだけ多くのリソースを残すというものです。特定のプラットフォームやデバイス向けに設計されたマイクロカーネルは、動作に必要な機能のみを備えています。マイクロカーネルのアプローチは、ハードウェア上にシンプルな抽象化レイヤーを定義し、メモリ管理、マルチタスク、プロセス間通信などの最小限のOSサービスを実装するためのプリミティブやシステムコール群を用意することから成ります。ネットワークなど、通常カーネルが提供するその他のサービスは、サーバと呼ばれるユーザー空間プログラムで実装されます。マイクロカーネルはモノリシックカーネルよりも保守が容易ですが、システムコールやコンテキストスイッチの数が多いため、通常の関数呼び出しよりもオーバーヘッドが多くなり、システムの速度が低下する可能性があります。
特権モードでの実行が本当に必要な部分のみがカーネル空間にあります。例えば、IPC(プロセス間通信)、基本的なスケジューラまたはスケジューリングプリミティブ、基本的なメモリ処理、基本的なI/Oプリミティブなどです。現在、多くの重要な部分はユーザー空間で実行されています。これには、完全なスケジューラ、メモリ処理、ファイルシステム、ネットワークスタックなどが含まれます。マイクロカーネルは、従来の「モノリシック」カーネル設計への反動として考案されました。従来の設計では、すべてのシステム機能がプロセッサの特別な「システム」モードで実行される1つの静的プログラムにまとめられていました。マイクロカーネルでは、ハードウェアの一部(必ずしもすべてではない)へのアクセス、メモリの管理、プロセス間のメッセージパッシングの調整など、最も基本的なタスクのみがこのレベルで実行されます。マイクロカーネルを使用するシステムには、 QNXとGNU Hurdがあります。QNXとGNU Hurdの場合、ユーザーセッションはシステム自体の完全なスナップショット、つまり「ビュー」と呼ばれることがあります。マイクロカーネルアーキテクチャの本質は、その利点のいくつかを示しています。
ほとんどのマイクロカーネルは、あるサーバーから別のサーバーへのリクエストを処理するためにメッセージパッシングシステムを使用します。メッセージパッシングシステムは通常、マイクロカーネルとポートベースで動作します。たとえば、メモリ増設のリクエストが送信されると、マイクロカーネルでポートが開かれ、リクエストが送信されます。マイクロカーネル内では、手順はシステムコールと似ています。その根拠は、システムアーキテクチャにモジュール性をもたらし、よりクリーンなシステム、デバッグや動的な変更が容易なシステム、ユーザーのニーズに合わせてカスタマイズ可能なシステム、そしてより高いパフォーマンスを実現することでした。マイクロカーネルは、GNU Hurd、MINIX、MkLinux、QNX、Redox OSなどのオペレーティングシステムの一部です。
マイクロカーネル自体は非常に小さいものの、必要な補助コードをすべて組み合わせると、実際にはモノリシックカーネルよりも大きくなることが多い。モノリシックカーネルの支持者は、オペレーティングシステムの大部分がハードウェアと直接やり取りしないマイクロカーネルシステムの2層構造が、システム効率の面で無視できないコストを生み出すと指摘している。これらのタイプのカーネルは通常、メモリアドレス空間の定義、プロセス間通信(IPC)、プロセス管理などの最小限のサービスのみを提供する。ハードウェア プロセスの実行などの他の機能は、マイクロカーネルによって直接処理されない。マイクロカーネルの支持者は、モノリシックカーネルにはカーネルのエラーがシステム全体のクラッシュを引き起こす可能性があるという欠点があると指摘している。しかし、マイクロカーネルでは、カーネル プロセスがクラッシュした場合でも、エラーの原因となったサービスを再起動するだけで、システム全体のクラッシュを防ぐことができる。
カーネルが提供するネットワークなどのその他のサービスは、サーバーと呼ばれるユーザー空間プログラムで実装されます。サーバーを使用すると、プログラムの起動と停止だけでオペレーティングシステムを変更できます。たとえば、ネットワークをサポートしていないマシンでは、ネットワークサーバーは起動されません。さまざまなアプリケーションとサーバー間でデータを転送するためにカーネルに出入りする作業は、モノリシックカーネルと比較してマイクロカーネルの効率を損なうオーバーヘッドを生み出します。
しかし、マイクロカーネルには欠点も存在する。そのいくつかは以下のとおりである。
マイクロカーネルの欠点は、状況によって大きく異なります。例えば、小規模で単一目的(かつ重要な)システムでは、実行するプロセス数が少ないため、プロセス管理の複雑さが効果的に軽減され、マイクロカーネルはうまく機能します。
マイクロカーネルは、オペレーティングシステムの残りの部分をユーザーモードで実行されるプログラムとして実装し、同じ変更されていないカーネル上で異なるオペレーティングシステムを使用することを可能にします。また、オペレーティングシステムを動的に切り替えたり、複数のオペレーティングシステムを同時にアクティブにすることも可能です。[ 28 ]
コンピュータカーネルが大きくなるにつれて、メモリ使用量に加えて、信頼できるコンピューティング基盤のサイズと脆弱性も大きくなります。これは仮想メモリシステムを改良することである程度緩和されますが、すべてのコンピュータアーキテクチャが仮想メモリをサポートしているわけではありません。[ f ]カーネルのフットプリントを削減するには、不要なコードを慎重に削除するために大規模な編集を行う必要がありますが、数百万行のコードを持つカーネルの各部分間の相互依存関係が明らかでない場合、これは非常に困難になる可能性があります。
1990年代初頭までに、モノリシックカーネルとマイクロカーネルのさまざまな欠点により、モノリシックカーネルは事実上すべてのオペレーティングシステム研究者によって時代遅れとみなされるようになりました。その結果、Linuxをマイクロカーネルではなくモノリシックカーネルとして設計することは、 Linus TorvaldsとAndrew Tanenbaumの間で有名な議論のテーマとなりました。[ 38 ] TanenbaumとTorvaldsの議論で提示された両方の側にメリットがあります。
モノリシックカーネルは、すべてのコードが同じアドレス空間(カーネル空間)にあるように設計されており、一部の開発者は、これがシステムのパフォーマンスを向上させるために必要だと主張しています。[ 39 ]また、一部の開発者は、モノリシックシステムは適切に記述されていれば非常に効率的であると主張しています。[ 39 ]モノリシックモデルは、通常メッセージパッシングに基づくマイクロカーネル設計の低速なIPCシステムではなく、共有カーネルメモリを使用することで、より効率的になる傾向があります。 [40]
マイクロカーネルのパフォーマンスは、1980年代と1990年代初頭の両方で低かった。[ 41 ] [ 42 ]これらのマイクロカーネルのパフォーマンスを実証的に測定した研究では、このような非効率性の理由が分析されなかった。[ 41 ]このデータの説明は「民間伝承」に委ねられ、カーネルモードからユーザーモードへの切り替え頻度の増加、プロセス間通信頻度の増加、コンテキストスイッチ頻度の増加が原因であるという仮定が置かれていた。[ 41 ]
実際、1995年に推測されたように、マイクロカーネルの性能が低い理由は、(1)マイクロカーネル全体のアプローチの実際の非効率性、(2)それらのマイクロカーネルに実装されている特定の概念、(3)それらの概念の特定の実装であった可能性もある。したがって、効率的なマイクロカーネルを構築するための解決策が、これまでの試みとは異なり、正しい構築技術を適用することであったかどうかを研究する必要があった。[ 41 ]
一方、モノリシックカーネルの設計につながる階層的な保護ドメインアーキテクチャ[ 36 ]は、異なるレベルの保護間で相互作用が発生するたびに(つまり、プロセスが「ユーザーモード」と「スーパーバイザモード」の両方でデータ構造を操作する必要がある場合)、値によるメッセージコピーが必要となるため、パフォーマンスに大きな欠点がある。[ 43 ]

ハイブリッドカーネルは、現在までのほとんどのバージョンのMicrosoft Windows ( NT 3.1 、NT 3.5、NT 3.51、NT 4.0、2000、XP、Vista、7、8、8.1、10、11) を含む一部の商用オペレーティングシステムで使用されています。Apple の macOS は、OSF/1 の Mach カーネル (OSFMK 7.3 ) [ 44 ]とFreeBSDのモノリシックカーネルのコードに基づいたXNUと呼ばれるハイブリッドカーネルを使用しています。ハイブリッドカーネルはマイクロカーネルに似ていますが、パフォーマンスを向上させるためにカーネル空間にいくつかの追加コードが含まれています。これらのカーネルは、モノリシックカーネルとマイクロカーネルの両方の主な利点に対応するために一部の開発者によって実装された妥協案を表しています。これらのタイプのカーネルは、モノリシックカーネルのいくつかの特性を備えたマイクロカーネルの拡張です。モノリシックカーネルとは異なり、これらのタイプのカーネルは実行時にモジュールを単独でロードすることができません。そのため、従来のマイクロカーネルのパフォーマンスオーバーヘッドを削減するために、ネットワークスタックやファイルシステムなどの一部のサービスをカーネル空間で実行する必要がありますが、デバイスドライバなどのカーネルコードは依然としてユーザー空間でサーバーとして実行されます。
多くの従来型のモノリシックカーネルは、ロード可能なカーネルモジュールをサポートしています。これらのカーネルの中で最もよく知られているのはLinuxカーネルです。モジュール型カーネルは、コアカーネルバイナリに組み込まれている部分と、必要に応じてメモリにロードされるバイナリを持つことができます。コードが汚染されたモジュールは、実行中のカーネルを不安定にする可能性があります。対照的に、マイクロカーネル用のドライバを完全に別のメモリ空間に記述し、本番環境に投入する前にテストすることが可能です。カーネルモジュールがロードされると、必要なものを追加することでモノリシック部分のメモリ空間にアクセスし、汚染の可能性が生じます。モジュール型(またはハイブリッド型)カーネルの利点は次のとおりです。
モジュールは一般的に、何らかのモジュールインターフェースを使用してカーネルと通信します。インターフェースは汎用的ですが(特定のオペレーティングシステムに固有のものですが)、常にモジュールを使用できるとは限りません。多くの場合、デバイスドライバはモジュールインターフェースが提供する以上の柔軟性を必要とする場合があります。基本的に、これは2つのシステムコールであり、モノリシックカーネルでは1回だけ実行すればよかった安全チェックが、モジュール化によって2回実行されることがよくあります。モジュール方式の欠点には、次のようなものがあります。
ナノカーネルは、割り込みコントローラやタイマーなどの最も基本的なサービスを含む、ほぼすべてのサービスをデバイスドライバに委任することで、カーネルのメモリ要件を従来のマイクロカーネルよりもさらに小さくします。[ 45 ]
エクソカーネルは、オペレーティングシステム設計におけるまだ実験段階のアプローチです。他のタイプのカーネルとは異なり、その機能はハードウェアの保護とアクセス多重化に限定されており、アプリケーション開発のためのハードウェア抽象化レイヤーは提供されません。ハードウェア保護とハードウェア管理をこのように分離することで、アプリケーション開発者は、各プログラムに対して利用可能なハードウェアを最も効率的に活用する方法を決定できます。
エクソカーネル自体は非常に小型です。しかしながら、ライブラリオペレーティングシステム(ユニカーネルも参照)が付属しており、アプリケーション開発者は従来のオペレーティングシステムと同様の機能を利用できます。エクソカーネルベースのシステムの大きな利点は、複数のライブラリオペレーティングシステムを組み込むことができ、それぞれが異なるAPIを提供できる点です。例えば、高レベルのUI開発用とリアルタイム制御用といった具合です。
マルチカーネルオペレーティングシステムは、マルチコアマシンを分散システムであるかのように、独立したコアのネットワークとして扱います。共有メモリを前提とせず、プロセス間通信をメッセージパッシングとして実装します。[ 46 ] [ 47 ] Barrelfishは、マルチカーネルとして記述された最初のオペレーティングシステムでした。
厳密に言えば、コンピュータを実行するためにオペレーティングシステム(したがってカーネル)は必要ありません。プログラムの作成者がハードウェアの抽象化やオペレーティングシステムのサポートなしで作業する意思があれば、プログラムは「ベアメタル」マシンに直接ロードして実行できます。1950年代から1960年代初頭にかけての初期のコンピュータのほとんどはこのように動作し、異なるプログラムの実行の間にリセットして再ロードしていました。最終的に、プログラムローダーやデバッガーなどの小さな補助プログラムが実行の合間にメモリに残されるか、ROMからロードされるようになりました。これらが開発されるにつれて、初期のオペレーティングシステムカーネルの基礎が形成されました。「ベアメタル」方式は、現在でも一部のビデオゲームコンソールや組み込みシステムで使用されていますが、[ 48 ]一般的に、新しいコンピュータは最新のオペレーティングシステムとカーネルを使用しています。
1969年、RC 4000マルチプログラミングシステムは、「さまざまな目的のオペレーティングシステムを秩序正しく構築できる」小さな核のシステム設計思想を導入しました[ 49 ]。これはマイクロカーネルアプローチと呼ばれるものです。
Unix が登場する前の 10 年間で、コンピュータの性能は飛躍的に向上し、コンピュータ オペレーターは、人々が余暇をコンピュータで過ごすための新しい方法を模索するようになりました。この時代の大きな発展の 1 つはタイム シェアリングで、複数のユーザーがコンピュータの時間を少しずつ割り当てられ、それぞれが自分の低速なマシンに接続しているように見えるというものでした。[ 50 ]
タイムシェアリングシステムの開発は、いくつかの問題を引き起こしました。その一つは、特にシステムが開発されていた大学のユーザーが、より多くのCPU時間を得るためにシステムをハッキングしようとしたことです。このため、セキュリティとアクセス制御は、 1965年のMulticsプロジェクトの主要な焦点となりました。 [ 51 ]もう一つの継続的な問題は、コンピューティングリソースを適切に管理することでした。ユーザーは、コンピュータのリソースを実際に使用する代わりに、ほとんどの時間を端末を見つめて入力内容を考えることに費やしており、タイムシェアリングシステムは、これらの期間中にアクティブなユーザーにCPU時間を割り当てる必要がありました。最後に、システムは通常、数層のメモリ階層を提供し、この高価なリソースを分割することで、仮想メモリシステムの大きな発展につながりました。
コモドール・アミーガは1985年に発売され、高度なカーネルアーキテクチャを採用した最初期の、そして間違いなく最も成功したホームコンピュータの一つでした。AmigaOSカーネルの実行コンポーネントであるexec.libraryはマイクロカーネルのメッセージパッシング設計を採用していますが、 graphics.libraryのようにハードウェアに直接アクセスできる他のカーネルコンポーネントも存在します。メモリ保護機能はなく、カーネルはほぼ常にユーザーモードで動作します。カーネルモードでは特別な処理のみが実行され、ユーザーモードアプリケーションはオペレーティングシステムにカーネルモードでコードを実行するように要求できます。

Unixの設計段階で、プログラマーたちは計算の目的はデータ変換であると信じていたため、すべての高レベルデバイスをファイルとしてモデル化することに決めた。[ 52 ]
例えば、プリンタは既知の場所にある「ファイル」として表現され、データがファイルにコピーされると印刷されました。同様の機能を提供する他のシステムでは、デバイスをより低いレベルで仮想化する傾向がありました。つまり、デバイスとファイルの両方が、より低いレベルの概念のインスタンスとなるのです。システムをファイルレベルで仮想化することで、ユーザーは既存のファイル管理ユーティリティと概念を使用してシステム全体を操作できるようになり、操作が劇的に簡素化されました。同じパラダイムの拡張として、Unixでは、パイプの概念を使用して一連の小さなプログラムでファイルを操作できます。これにより、ユーザーは単一目的のツールのチェーンを通してファイルを渡すことで、操作を段階的に完了させることができました。最終的な結果は同じでしたが、このように小さなプログラムを使用することで、柔軟性と開発および使用の容易さが劇的に向上し、ユーザーはチェーンにプログラムを追加または削除することでワークフローを変更できるようになりました。
Unix モデルでは、オペレーティングシステムは2 つの部分から構成されています。1 つ目は、ほとんどの操作を実行するユーティリティ プログラムの膨大なコレクション、2 つ目は、プログラムを実行するカーネルです。[ 52 ] Unix では、プログラミングの観点から、この 2 つの区別はかなり曖昧です。カーネルは、スーパーバイザ モード[ g ]で実行されるプログラムであり、システムの残りの部分を構成する小さなユーティリティ プログラムのプログラム ローダおよびスーパーバイザとして機能し、これらのプログラムにロックおよびI/Oサービスを提供します。それ以外では、カーネルはユーザー スペースにまったく介入しません。
長年にわたり、コンピューティング モデルは変化し、Unix がすべてをファイルまたはバイト ストリームとして扱う方法は、以前ほど普遍的に適用できなくなりました。端末は、印刷または読み取りの対象となるファイルまたはバイト ストリームとして扱うことができますが、グラフィカル ユーザー インターフェイスについては同じことは当てはまらないようです。ネットワークは別の問題を引き起こしました。ネットワーク通信はファイル アクセスと比較できますが、低レベルのパケット指向アーキテクチャは、ファイル全体ではなく、データの個別のチャンクを扱います。コンピュータの能力が向上するにつれて、Unix はますますコードで煩雑になりました。これは、Unix カーネルのモジュール性が非常に拡張可能であるためでもあります。[ 53 ] 70 年代と 80 年代にはカーネルのコードが10 万行だったかもしれませんが、 LinuxやGNUのような現代の Unix の後継のカーネルは1300 万行を超えています。[ 54 ]
現代の Unix 派生システムは一般的にモジュールローディングのモノリシックカーネルに基づいています。その例としては、GNUの多くのディストリビューション、IBM AIXのLinux カーネル、およびFreeBSD、DragonFly BSD、OpenBSD、NetBSD、macOSなどのBerkeley Software Distribution派生カーネルが挙げられます。これらの代替案とは別に、アマチュア開発者は活発なオペレーティングシステム開発コミュニティを維持しており、自作の趣味のカーネルが多数存在し、それらはほとんどの場合、Linux、FreeBSD、DragonflyBSD、OpenBSD、または NetBSD カーネルと多くの機能を共有したり、それらと互換性を持ったりしています。[ 55 ]
Appleは1984年にMacintoshパーソナルコンピュータに同梱されたクラシックMac OSを初めてリリースしました。AppleはMac OS 8.6でナノカーネル設計に移行しました。これに対し、現代のmacOS(当初はMac OS Xという名前でした)はDarwinをベースにしており、 4.3BSDカーネルとMachカーネルを組み合わせて作成されたXNUと呼ばれるハイブリッドカーネルを使用しています。[ 56 ]
Microsoft Windowsは、1985年にMS-DOSのアドオンとして初めてリリースされました。他のオペレーティングシステムに依存していたため、Windows 95以前の初期バージョンのWindowsは、オペレーティング環境(オペレーティングシステムとは異なる)とみなされていました。この製品ラインは1980年代から1990年代にかけて進化を続け、Windows 9xシリーズでは32ビットアドレッシングとプリエンプティブマルチタスクが追加されましたが、 2000年のWindows Meのリリースをもって終了しました。
マイクロソフトは、非常に似たインターフェースを持つオペレーティングシステムであるWindows NTも開発しました。こちらはハイエンドユーザーやビジネスユーザー向けです。このシリーズは1993年のWindows NT 3.1のリリースから始まり、2001年10月のWindows XPのリリースで一般ユーザー向けに導入されました。Windows XPはWindows 9xに代わる、全く異なる、はるかに洗練されたオペレーティングシステムです。このシリーズはWindows 11にも引き継がれています。
Windows NT のカーネルのアーキテクチャは、カーネル自体にウィンドウマネージャや IPCマネージャなどのタスクが含まれており、クライアント/サーバ階層サブシステムモデルを採用しているため、ハイブリッドカーネルとみなされています。[ 57 ]これは、Windows NT カーネルがMach マイクロカーネルの影響を受けていますが、純粋なマイクロカーネルのすべての基準を満たしていないため、修正されたマイクロカーネルとして設計されました。
監視プログラムまたはスーパーバイザーとは、通常はオペレーティングシステムの一部であるコンピュータプログラムであり、他のルーチンの実行を制御し、作業スケジューリング、入出力操作、エラー処理、および同様の機能を調整し、データ処理システムにおける作業の流れを調整するものです。
歴史的に見ると、この用語は主にIBMのメインフレームオペレーティングシステム(OS/360以降)に関連付けられていました。他のオペレーティングシステムでは、スーパーバイザーは一般的にカーネルと呼ばれます。
IBMはCP-40とCP-67において、スーパーバイザの状態をハードウェアからさらに抽象化し、完全な仮想化、すなわち複数のオペレーティングシステムを互いに完全に独立して同一マシン上で実行できる機能を実現するハイパーバイザを開発した。
1970年代、IBMはSystem/370向けにCP/CMSをアップデートし、 Virtual Machine Facility/370 (VM/370)という名称で提供しました。そのため、最初のこのようなシステムはVirtual Machine、またはVMと呼ばれました。
カーネギーメロン大学のリチャード・ラシッドによって開発されたMachは最もよく知られた汎用マイクロカーネルですが、より特定の目的のために開発されたマイクロカーネルもあります。L4マイクロカーネルファミリー(主に L3 および L4 カーネル) は、マイクロカーネルが必ずしも遅いわけではないことを示すために作成されました。[ 58 ] FiascoやPistachioなどの新しい実装では、Linux を他の L4 プロセスと並行して別のアドレス空間で実行できます。 [ 59 ] [ 60 ]
さらに、QNXは主に組み込みシステムで使用されるマイクロカーネルであり、[ 61 ]オープンソースソフトウェアMINIXは、元々は教育目的で作成されましたが、現在は高い信頼性と自己修復機能を備えたマイクロカーネルOSとなることに重点を置いています。
すべてのシステムコールは、ライブラリプロシージャを呼び出すことによってCプログラムから呼び出されます。ライブラリプロシージャは、TRAP命令を実行してユーザーモードからカーネルモードに切り替え、実行を開始します。
過去25年間で、オペレーティングシステムのアーキテクチャに関する研究は、既存の主流
システム
に
わずか
な影響しか与えなかったことが明らかになった。
モノリシックカーネルの密結合性により、基盤となるハードウェアを非常に効率的に使用できます [...] 一方、マイクロカーネルは、コアプロセスの多くをユーザーランドで実行します。 [...] 残念ながら、これらの利点は、マイクロカーネルがコンテキストスイッチと呼ばれるプロセスを介してカーネル空間との間で多くの情報をやり取りする必要があるという代償を伴います。コンテキストスイッチはかなりのオーバーヘッドを導入するため、パフォーマンスの低下につながります。