macOS/XNUカーネルとMach・BSDのハイブリッド構造
macOSやiOSが堅牢さと速さを両立できる理由は、XNUがMachとBSDを1つのアドレス空間に同居させた折衷設計にあります。Machポートの仕組みからI/O Kitまで内側を解き明かします。
- 1.XNUは純粋なマイクロカーネルではなく、Mach(IPC・仮想メモリ・スケジューリング)とBSD(POSIX・ネットワーク・VFS)を同一カーネル空間に同居させたハイブリッドである。
- 2.Machの中核はポートを介したメッセージパッシングで、ポートは権利(send/receive権)で守られた一方向のメッセージキューであり、Mig が生成するスタブでRPCとして使われる。
- 3.デバイスドライバは I/O Kit という C++ フレームワーク上に書かれ、ドライバ同士をオブジェクトとして接続する I/O Registry がデバイスツリーと電源管理を統括する。
XNUは「2つのカーネルを縫い合わせた」ハイブリッド
macOS・iOS・iPadOS などを支える Apple のカーネルが XNU(X is Not Unix)です。名前に反して中身は2つの系譜の合成で、よく「ハイブリッドカーネル」と呼ばれます。中核は3層から成ります。
- Mach:CMU 由来のマイクロカーネル。IPC(メッセージパッシング)・仮想メモリ・タスク/スレッドとスケジューリングという最下層の機構を担う。
- BSD(FreeBSD 由来):POSIX API・プロセスモデル・シグナル・ネットワークスタック・VFS(ファイルシステム抽象)・ソケットを提供する上位層。
- I/O Kit:C++ で書かれたデバイスドライバフレームワーク。
ここで誤解しやすいのは「Mach ベース=マイクロカーネル」という連想です。本来のマイクロカーネルはドライバや FS をユーザー空間のサーバーへ追い出し、IPC で繋ぎます。しかし XNU は性能のため BSD もドライバも Mach と同じカーネル空間に同居させ、内部では IPC ではなく直接の関数呼び出しで連携する。だから純粋なマイクロカーネルではなく、設計上の位置づけは マイクロカーネルとモノリシックの比較 でいうハイブリッドです。Mach のメッセージパッシングのコストを、境界越えを減らすことで回避しているわけです。
歴史的経緯です。NeXTSTEP が Mach 2.5 に BSD を載せた構成を採り、それが Apple に買収されて Mac OS X の基盤になりました。Mach だけでは POSIX 互換の豊富な API が揃わず、BSD だけでは Mach の柔軟な VM・IPC・タスク抽象が得られない。両方の資産を捨てずに使うための現実的な折衷が XNU です。
Mach の中核:タスク・スレッド・ポート
Mach の世界では実行とリソースの単位が BSD と別建てです。**タスク(task)**がアドレス空間とリソースの容れ物、**スレッド(thread)**が実際の実行単位で、CPU にスケジュールされるのはスレッドです。BSD の proc(プロセス)は内部的に Mach の task と対応づけられ、1つの実体を2つの顔(BSD の PID と Mach の task port)から見る格好になります。
Mach のあらゆる機能はポート(port)を通じて操作されます。ポートとは、カーネルが管理する一方向のメッセージキューです。重要なのは、ポートが権利(port right)で保護されていることです。
| ポート権利 | 意味 | 性質 |
|---|---|---|
| receive権 | そのポートからメッセージを取り出せる | 原則1つのタスクだけが保持(受信者は一意) |
| send権 | そのポートへメッセージを送れる | 複数タスクが保持可能、譲渡・複製できる |
| send-once権 | 一度だけ送れる使い捨ての送信権 | 返信用ポートなどに使う |
タスクが扱うのは生のポインタではなく、自タスク内でのみ有効な port name(整数のハンドル。file descriptor に似た添字)です。カーネルがその name を本物のポート構造体へ翻訳します。だからポート権利はケーパビリティとして働き、send 権を持たないタスクはそのポートへ手出しできません。これが Mach のセキュリティと隔離の土台です。タスク自身を表す task port(mach_task_self())の send 権を奪われることは、そのタスクの完全な乗っ取りに等しく、それゆえ厳重に守られます。
Mach メッセージと Mig:IPC を RPC に見せる
Mach IPC の生 API は mach_msg() ただ1つで、送信・受信・その往復(RPC 的な送って待つ)を1コールでこなします。メッセージは固定ヘッダ+本体(データやポート権利の並び)です。本体には2種類のデータが載ります。
mach_msg():
ヘッダ { 宛先ポート, 返信ポート, サイズ, メッセージID }
本体 ├─ inline データ(小さい値はメッセージにそのままコピー)
└─ out-of-line データ / ポート権利
└ 大きなデータは VM のページマッピングで「移送」
(物理コピーせず受信側アドレス空間へ写す)
ポイントは、メッセージにポート権利そのものを載せて渡せることです。送信側が「返信はこのポートへ」と返信ポートの send-once 権を同梱すれば、受信側はそこへ結果を返せる。これでリクエスト/レスポンス型の RPC が成立します。大きなデータは物理コピーを避け、仮想メモリ の機構でページを受信側にコピーオンライト的にマッピングして渡します。Mach IPC と VM が深く結合しているのはこのためです。
ただし mach_msg を手で組み立てるのは煩雑なので、実際には Mig(Mach Interface Generator)を使います。Mig は IDL(インターフェース定義)からクライアント側スタブとサーバー側スケルトンを生成し、関数呼び出しを自動でメッセージへ整列化(マーシャリング)します。呼ぶ側は普通の関数を呼んでいるように見えて、裏で mach_msg が走り、別タスク(しばしばカーネル自身)で処理される——構造としては プロセス間通信(IPC) の上に RPC を被せたものです。host_info や task_for_pid といった Mach の各種サービスはこの形で呼ばれます。
out-of-line データの「移送」は、ページテーブルの操作だけで完結します。送信側のページを受信側アドレス空間へマップし、書き込みが起きるまで実体を共有する(コピーオンライト)。巨大なメッセージでもメモリ帯域を食わずに渡せるのは、Mach の IPC がメッセージング機構でありながら同時に VM 機構でもあるからです。L4 がレジスタ渡しで小メッセージを速くしたのに対し、Mach は大メッセージをページ移送で軽くする方向に最適化しています。
BSD 層:POSIX の顔とネットワーク・VFS
ユーザー空間のアプリが触れる「Unix らしさ」はほぼ BSD 層が担います。fork/exec/wait、シグナル、pthread、socket、ファイル I/O、そして VFS によるファイルシステム抽象——これらは FreeBSD 由来のコードがカーネル内で提供します。だから macOS では、同じカーネル機能を2つの API レイヤから呼べます。
| 観点 | Mach 層 | BSD 層 |
|---|---|---|
| 実行単位 | task / thread | process(proc)/ pthread |
| 識別子 | port name(ハンドル) | PID / file descriptor |
| 主な責務 | IPC・VM・スケジューリング | POSIX API・NW・VFS・シグナル |
| 呼び出し様式 | mach_msg によるメッセージ | 従来型のシステムコール(trap) |
両者はシステムコールの番号空間でも区別されます。XNU では、システムコール番号が正なら BSD(Unix)系、負なら Mach トラップとして振り分けられます。open や read は前者、mach_msg_trap などは後者です。BSD のプロセス管理は内部で必ず対応する Mach task/thread を操作するので、fork 一つとっても「BSD が proc を作り、Mach が task とアドレス空間を用意する」という二層の協調になります。スレッドが CPU を得るかどうかを最終的に決めるのは Mach のスケジューラで、BSD はその上に POSIX 的な見え方を被せているにすぎません。
同じ実体に2つの ID 体系があるため、PID と task port の対応づけ(task_for_pid)は強い権限です。任意プロセスの task port を取れれば、そのアドレス空間の読み書きや実行制御ができてしまう——デバッガが可能なのもこれが理由です。だから近年の macOS では SIP(System Integrity Protection)や署名・エンタイトルメントで task_for_pid を厳しく制限し、他プロセスへの侵入経路を塞いでいます。
I/O Kit:オブジェクト指向のドライバ機構
XNU のデバイスドライバは、Mach でも BSD でもなく I/O Kit という独立フレームワーク上に書かれます。言語は Embedded C++(例外・多重継承・RTTI などを外した C++ サブセット)で、ドライバをクラスのインスタンスとして表現するのが特徴です。Linux の デバイスドライバモデル が構造体とコールバックで組むのに対し、I/O Kit はクラス継承とオブジェクト接続でドライバを構築します。
中核概念は次の通りです。
- IOService:すべてのドライバ/デバイスの基底クラス。
probe→start→stopというライフサイクルを持つ。 - マッチング:新しいデバイスが現れると、I/O Kit はそのプロパティ(ベンダID等)にマッチする driver クラスの候補を探し、
probeでスコアを競わせ、最良のものをstartする。 - I/O Registry:起動中の IOService インスタンスを生きたツリーとして保持する動的データベース。デバイス間の親子(provider/client)関係がここに記録され、
ioregコマンドで覗ける。 - 電源管理:provider/client のツリーをたどって、依存するデバイスの電源状態を整合的に上げ下げする。
I/O Registry(抜粋イメージ):
IOPCIDevice (GPU)
└─ provider/client 関係でぶら下がる
IOFramebuffer ──> ディスプレイ
IOUSBHostDevice
└─ IOUSBInterface ──> HID ドライバ
I/O Registry が「マッチング済みのドライバの接続グラフ」を保持するからこそ、ホットプラグでデバイスが現れた瞬間に候補ドライバが start され、抜ければ stop されて木から外れる、という動的な追従が成立します。ドライバ間の依存(あるドライバが下位ドライバを provider として要求する)もこのツリー上の関係として表現され、電源やリソースの整合性が保たれます。
押さえどころは4つ。(1) XNU は Mach+BSD+I/O Kit のハイブリッドで、純粋マイクロカーネルではなく内部は関数呼び出しで連携する。(2) Mach の IPC はポート(receive 権を持つ受信者は一意、send 権は複製可能なケーパビリティ)へのメッセージパッシングで、mach_msg 1コールに集約され、Mig がそれを RPC に見せる。(3) 大データは VM のページ移送(COW)で渡され、IPC と VM が一体。(4) ドライバは I/O Kit の C++ オブジェクトで、I/O Registry がデバイスツリーと電源管理を統括する。
まとめ
XNU はMach(IPC・VM・スケジューリング)・BSD(POSIX・NW・VFS)・I/O Kit(ドライバ)を同一カーネル空間に同居させたハイブリッドです。Mach は本来マイクロカーネルですが、XNU は性能のため境界越えを IPC ではなく関数呼び出しに置き換えて取り込みました。Mach の中核はポートを介したメッセージパッシングで、ポートは send/receive 権というケーパビリティで守られ、mach_msg に集約された IPC を Mig が RPC として見せます。大きなメッセージは VM のページ移送で軽く渡され、IPC と VM が一体である点が Mach らしさです。一方の BSD 層は同じ実体に PID/fd という Unix の顔を与え、ドライバは I/O Kit のオブジェクトとして I/O Registry のツリー上で接続・電源管理されます。2つの系譜を縫い合わせた折衷こそが、堅牢さと豊富な POSIX 互換、そして高い性能を両立させる XNU の正体です。
設計の位置づけは マイクロカーネルとモノリシックの比較、通信の基礎は プロセス間通信(IPC)、ドライバ機構の対比は デバイスドライバモデル も合わせてどうぞ。
OS Article
macOS/XNUカーネルとMach・BSDのハイブリッド構造を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
XNU
比較で見る軸
難易度: advanced / カテゴリ: OS / タグ数: 6
導入後に効く点
Machの中核はポートを介したメッセージパッシングで、ポートは権利(send/receive権)で守られた一方向のメッセージキューであり、Mig が生成するスタブでRPCとして使われる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- OS
- タグ数
- 6
判断チェックリスト
- 自社の用途が「XNU / Mach」に近いか確認する。
- 強みである「XNUは純粋なマイクロカーネルではなく、Mach(IPC・仮想メモリ・スケジューリング)とBSD(POSIX・ネットワーク・VFS)を同一カーネル空間に同居させたハイブリッドである。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。