1. アプリとハードのあいだ — システムコール
アプリ(ユーザープログラム)はユーザーモード という制限された立場で動いていて、ディスクやメモリを直接は操作できません。ハードを触りたいときは、OSに「お願い」をします。これがシステムコール です。呼んだ瞬間にCPUはtrap(トラップ) で特権境界を越え、カーネルモード に切り替わってカーネルが代理で実行し、終わると結果を持ってアプリへ戻ります。
下のボタンで色々なシステムコールを呼んでみてください。要求が境界を下りてカーネルに入り、ハードウェアへ届き、データが戻ってくる — その往復と、モードの切り替わり を追えます。
システムコール — 特権境界を越える往復
ファイルを読む
メモリを確保
画面に描く
時刻を得る
⏸ 一時停止
青い丸=アプリからの要求、緑の丸=戻ってくるデータ。境界(黄の破線)を越えるときに、左上の表示がユーザーモード⇄カーネルモード で切り替わります。要求が向かうハードウェア(CPU/メモリ/ディスク/デバイス)が緑に光ります。「時刻を得る」だけはハードに触れずカーネル内で完結します。
POINT — 2つのモードという安全装置
CPUにはユーザーモード とカーネルモード の2つの特権レベルがある。アプリは前者で動き、ハードを直接叩く命令は禁止されている。唯一の抜け道がシステムコールで、そこを通るときだけ一時的にカーネルモードに昇格する。この「関所を1つに絞る」設計が、暴走したアプリからシステム全体を守っている。
ユーザーモード ──[ trap(システムコール)]──▶ カーネルモード ──[ return ]──▶ ユーザーモード
x86 CPU では ring3(ユーザー)と ring0(カーネル)という特権レベルで実装される。境界を越える trap/return には数十〜数百ナノ秒のコストがかかる。
2. 詳細を隠す — 抽象化という親切
OSの2つめの仕事が抽象化 です。アプリは read("photo.jpg") と書くだけ。でもその裏では、ファイル名からinode番号を引き、論理ブロック番号に変換し、物理セクタのアドレスに直し、ディスクのヘッドを動かして回転を待つ… という長い翻訳の連鎖が走っています。各層が下の層の面倒を隠し、上の層には簡単な操作だけを見せる 。だからアプリはディスクの機種が変わっても書き換え不要です。
「詳細を隠す」にチェックを入れると、アプリから見える世界がどれだけ単純かが分かります。
抽象化の階層 — read() が下りて、データが戻る
詳細を隠す(アプリから見た世界)
↺ 最初から
⏸ 一時停止
青い丸が上から下へ降りるあいだ、各層が「同じ要求を自分の言葉に翻訳」します(名前→inode→論理ブロック→物理セクタ)。ディスクに届いたら、緑のデータが上へ戻り、各層が簡単な形に包み直してアプリへ。チェックを入れると、下の層はすべて灰色の箱に隠れ、アプリは中身を知らなくてよいことが見えます。
POINT — 「ファイル」はOSが見せている幻
ディスクの実体は「番号のついたセクタの巨大な列」でしかない。連続した名前つきのバイト列=ファイル という概念は、OSが作り出した便利な抽象 だ。プロセス・仮想メモリ・ソケットも同じで、OSは「扱いやすい嘘」を提供して、生のハードウェアの複雑さから私たちを守っている。
3. 資源を配る — 1つのCPUをみんなで使う
3つめは資源の仲介 。CPUもメモリもディスクも数に限りがあるのに、動きたいアプリは何十個もあります。OSのスケジューラ は、1個のCPUをごく短い時間(タイムスライス)ずつ順番に 各アプリへ貸し出します。切り替えが十分に速いので、私たちには「全部が同時に動いている」ように見えるのです。
アプリの数とタイムスライスの長さを変えて、CPUがどのように順番に貸し出されているか(下のタイムライン)を見てください。
CPUの時分割 — スケジューラが順番に割り当てる
各アプリの色つきバー=仕事の進み具合。「CPU占有中」のアプリだけが進み、他は待ちます。下の帯=時間の流れで、誰がCPUを使ったかの履歴。タイムスライスを短くするほど切り替えが細かくなり、より「同時」に見えます(でも切り替え自体のコストは増える)。
見かけの同時実行 = 1個のCPU × 十分に速いタイムスライス切り替え
実際にはどの瞬間も動いているアプリは1つ(コア1個の場合)。速く切り替えることで「並行(concurrent)」に見せている。本当に同時なのは複数コアの「並列(parallel)」で、これは後のレッスンの主役。
4. 勝手に触らせない — 保護と特権
最後の仕事が保護 です。もしアプリがハードや他プロセスのメモリを直接いじれたら、バグ1つでシステム全体が巻き添えになります。そこでOSは特権境界を関所にして、システムコールの門を通る正規のアクセスだけ を許します。門を通らず直接触ろうとすると、CPUが例外(一般保護違反) を発生させ、カーネルが割り込んでそのプロセスを止めます。これが有名な「セグメンテーション違反」の正体です。
特権による保護 — 門を通れば許可、直接は遮断
システムコール経由でアクセス
直接アクセスを試みる
⏸ 一時停止
緑の門=システムコール(正規ルート)。左のボタンなら要求は門を通ってカーネルに届き、安全に代理実行されて戻ります。右のボタンだと、門を通らず保護領域へ突っ込もうとして境界の壁に阻まれ 、CPUが例外を発生 → カーネルが介入 → プロセスは強制終了になります。
注意 — 保護がなければOSではない
「速さ」や「機能」より前に、OSの土台は隔離 にある。各プロセスは自分の仮想メモリ空間しか見えず、他人のメモリやカーネルには触れない。この壁があるからこそ、1つのアプリがクラッシュしても他は生き残る。逆に言えば、この保護境界を破る手口(権限昇格・境界外アクセス)はそのままセキュリティ攻撃になる。
5. まとめ — OSがしている4つの仕事
仲介(システムコール) :アプリはユーザーモードで動き、ハードを触るときだけ trap でカーネルモードへ。境界を1つの関所に絞る。
抽象化 :ファイル・プロセス・仮想メモリという「扱いやすい概念」を提供し、生のハードの複雑さを隠す。
資源の仲介 :限られたCPU・メモリ・デバイスを多数のアプリへ、公平かつ効率よく配分する。
保護 :特権レベルとメモリ隔離で、あるアプリの暴走が全体に波及しないよう守る。
一歩先へ — カーネルの設計思想
この4つの仕事を「全部カーネルに詰め込む」のがモノリシックカーネル (Linux)。対して、最小限だけカーネルに残し、ファイルシステムやドライバをユーザー空間のサービスに追い出すのがマイクロカーネル (一部の組込み・研究OS)。前者は速く、後者は壊れにくい。どこまでを特権側に置くか — これはOS設計の永遠のトレードオフだ。次のレッスンでは、この上で動く「実行の単位」=プロセスとスレッド を分解する。