🖥️
OSとプログラミング:機械語から高水準言語まで
周辺機器・割り込み・OSの役割から、機械語→アセンブリ→コンパイラ→OSの抽象化の積み重ねまで。H.G. Wellsの「World Brain」とインターネットの接点も。CODE第2版 Ch.24-28。
ここまでの積み上げ
信号とコード → 電気と論理ゲート → 数値体系
→ 算術回路(加算・減算)→ 記憶(FF・RAM)→ CPU
CPU が完成した。次の問い:「この機械に何をさせるか」
周辺機器とI/O
CPU と外の世界(キーボード・ディスプレイ・ストレージ)をつなぐ仕組み。
【I/O の2方式】
メモリマップドI/O:
I/O デバイスをメモリアドレスの一部として扱う
「アドレス 0xFF00 に書き込む」= キーボードコントローラへの指示
アドレスバスとデータバスを共用できる
ポートマップドI/O(Intel 8080方式):
IN 命令:指定ポートからデータを読む
OUT 命令:指定ポートへデータを書く
メモリアクセスとは別の制御線で区別
割り込み(Interrupt)
問題:CPUはキーボード入力がいつ来るかわからない
解決策1(ポーリング):ループで「何か入力があったか?」を繰り返し確認
→ CPU時間を無駄に消費
解決策2(割り込み):
デバイス → CPU に INT 信号を送る
CPU → 現在の作業を中断 → 現在の状態(レジスタ)をスタックへPUSH
→ 割り込みハンドラ(Interrupt Service Routine)へジャンプ
→ ハンドラ完了後 → RETI で元の作業に復帰
現代OS の I/O 処理はほぼすべて割り込みベース
オペレーティングシステムの役割
CPU・メモリ・デバイスを直接操作すると、アプリ開発者はハードウェアの詳細を全部知る必要がある。OS はその複雑さを隠す。
OSの主な役割:
1. リソース管理
CPU時間の分配(スケジューリング)
メモリの割り当て・回収
デバイスアクセスの調停
2. 抽象化(API の提供)
「ファイルを読む」→ 実際の物理セクタ位置はOSが管理
「ソケットで送信」→ NICドライバ・TCP/IP スタックはOSが隠蔽
3. 保護(プロセス間の隔離)
あるプロセスが他のプロセスのメモリを壊せないようにする
カーネルモード / ユーザーモードの分離
4. ファイルシステム
バイト列の羅列を「ファイルとディレクトリ」として見せる
機械語:CPUが直接理解する言語
CPU が実行できるのは機械語のバイト列のみ。Intel 8080 の例:
メモリ上の機械語(16進数で表示):
アドレス バイト
0000: 3E 05 ; MVI A, 5 (A ← 5)
0002: 06 03 ; MVI B, 3 (B ← 3)
0004: 80 ; ADD B (A ← A + B = 8)
0005: 32 10 00; STA 0x0010 (0x0010番地 ← A)
0008: 76 ; HLT
人間には読みにくい。また:
・アドレスを手で計算する必要がある
・CPU が変わると全部書き直し
アセンブリ言語
機械語の「人間向けニーモニック表現」。アセンブラ(プログラム)が機械語に変換する。
機械語 → アセンブリ言語
─────────────────────────────
3E 05 → MVI A, 5
06 03 → MVI B, 3
80 → ADD B
32 10 00 → STA result
76 → HLT
特徴:
1命令 = 1機械語命令(ほぼ1対1)
ラベルが使える(アドレスを自動計算)
人間に読みやすいが CPU 依存
用途:デバイスドライバ・BIOS・高速化が必要な部分
高水準言語とコンパイラ
機械語やアセンブリはCPU依存で可搬性がない。高水準言語は CPU を意識せずに書ける。
抽象化の積み重ね:
if (x > 0) { result = x * 2; } ← C言語
コンパイラ(GCC, Clangなど)が変換
CMP x, 0 ← アセンブリ
JLE end
MOV result, x
SHL result, 1 ← ×2 は左シフトで実現
end:
機械語バイト列 ← CPU が実行
コンパイラの仕事:
字句解析 → 構文解析 → 意味解析 → 最適化 → コード生成
インタープリタとの違い
コンパイラ:
実行前にソース全体を機械語へ変換
実行速度が速い(C, Rust, Go など)
インタープリタ:
実行時に1行ずつ解釈・実行
開発が柔軟(Python, Ruby など)
現代は JIT コンパイルで速度向上(V8 の JavaScript など)
ジャンプとループとサブルーチン(Ch.24)
高水準言語の制御構造は、すべてジャンプ命令に変換される。
if文:
if (A == 0) { ... } else { ... }
→ CMP A, 0
JNZ else_label
...(thenブランチ)
JMP end
else_label:
...(elseブランチ)
end:
forループ:
for (i = 0; i < 10; i++) { ... }
→ MVI i, 0
loop:
CMP i, 10
JGE end
...(ループ本体)
INR i
JMP loop
end:
関数呼び出し:
func(a, b)
→ 引数をスタックにPUSH
→ CALL func_addr
→ 戻り値をレジスタ or スタックから受け取り
OS の歴史的な積み重ね
1940年代:プログラムを手でパネルに打ち込む
↓
1950年代:バッチ処理OS(IBSYS)
カードリーダーから一括入力、順番に実行
↓
1960年代:タイムシェアリング(Multics, Unix)
複数ユーザーが1台を同時に使う
プロセス・スケジューリングの誕生
↓
1970年代:Unix の広まり
「すべてはファイル」という哲学
C言語での OS 記述 → 移植性の革命
↓
1980年代:パーソナルコンピュータ(CP/M, MS-DOS)
一人1台の時代。GUI(Macintosh, Windows)
↓
1990年代〜:Linux, Windows NT
ネットワーク対応・マルチタスク・保護メモリ
The World Brain:最終章
本書の締めくくり(第28章)は技術から少し離れ、思想的な展望に触れる。
H.G. Wells(1866-1946)の「World Brain」(1938):
「世界中の知識を一箇所に集め、誰でもアクセスできる
百科事典のような組織を作るべきだ」
Wells は電子コンピュータが生まれる前にこのビジョンを提唱した。
→ インターネット(1969 ARPANET)
→ World Wide Web(1991, Tim Berners-Lee)
→ Wikipedia(2001)
→ 検索エンジン・LLM(現代)
「一人の人間が持てる知識は限られている。
しかし世界の知識を繋いだネットワークなら?」
28章のボトムアップを振り返る
懐中電灯の点滅
↓ スイッチを電気で制御する
リレー・論理ゲート
↓ ゲートで演算する
加算器・ALU
↓ 値を記憶する
フリップフロップ・RAM
↓ 演算と記憶を統合する
CPU(フェッチ→デコード→実行)
↓ CPU に命令を与える
機械語・アセンブリ・高水準言語
↓ 多くのプログラムを協調させる
オペレーティングシステム
↓ コンピュータをネットワークで繋ぐ
インターネット・World Wide Web
「すべては 0 と 1 から始まった」
参考:現代との接点
本書が扱うのは 1970〜80年代の技術(Intel 8080)だが、原理は現代も同じ。
| 本書の概念 | 現代の実装 |
|---|---|
| リレー → 論理ゲート | CMOS トランジスタ(数十億個/チップ) |
| 8ビット ALU | 64ビット AVX-512 SIMD(1命令で512ビット演算) |
| 8080 の 8 レジスタ | x86-64 の 16本の汎用レジスタ + SIMD |
| リプルキャリーアダー | キャリー先見加算器(Carry Lookahead Adder) |
| 単一CPUサイクル | アウトオブオーダー実行・パイプライン・投機的実行 |
| シリアル I/O | PCIe Gen5(〜128GB/s)・USB4 |
| 64KB アドレス空間 | 48ビット仮想アドレス(256TB) |
- 1. 📖CODE:コンピュータのからくり — シリーズ概要
- 2. 📡信号とコード:点字・モールス・情報伝達の原理
- 3. ⚡電気とリレー:懐中電灯から論理ゲートへ
- 4. 🔢数の体系:2進数・16進数・ASCIIからUnicodeへ
- 5. ➕算術回路:加算器・2の補数・減算の実装
- 6. 💾記憶と時計:フリップフロップ・クロック・RAM
- 7. 🖥️CPUの構造:ALU・レジスタ・バス・制御信号
- 8. 🖥️OSとプログラミング:機械語から高水準言語まで
出典: https://www.amazon.co.jp/dp/4296080245