OSの役割 — ハードとアプリのあいだに立つ仲介者

アプリは「ファイルを読む」と書くだけで、ディスクがどこのセクタを回転させているかを一切知りません。その面倒をぜんぶ引き受けているのがOS(オペレーティングシステム)です。アプリとハードウェアのあいだに立ち、システムコールという関所で橋渡しし、詳細を隠し、限られた資源を配り、勝手な操作から守る — その4つの仕事を、動かしながら見ていきます。

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つの仕事

一歩先へ — カーネルの設計思想 この4つの仕事を「全部カーネルに詰め込む」のがモノリシックカーネル(Linux)。対して、最小限だけカーネルに残し、ファイルシステムやドライバをユーザー空間のサービスに追い出すのがマイクロカーネル(一部の組込み・研究OS)。前者は速く、後者は壊れにくい。どこまでを特権側に置くか — これはOS設計の永遠のトレードオフだ。次のレッスンでは、この上で動く「実行の単位」=プロセスとスレッドを分解する。