- Authors

- Name
- Youngju Kim
- @fjvbn20031
CPUスケジューリングの基本概念
CPUバーストとI/Oバースト
プロセスの実行はCPUバーストとI/Oバーストの交代で構成される。
[プロセス実行サイクル]
CPUバースト -> I/Oバースト -> CPUバースト -> I/Oバースト -> ...
|-------| |--------| |--|
CPU実行 I/O待ち CPU実行
(計算) (ディスク読み取り) (計算)
CPUバウンドプロセス:長いCPUバースト、少ないI/Oバースト
I/Oバウンドプロセス:短いCPUバースト、多いI/Oバースト
プリエンプティブ vs ノンプリエンプティブスケジューリング
- ノンプリエンプティブ(Non-preemptive): プロセスが自発的にCPUを返すまで実行を継続
- プリエンプティブ(Preemptive): OSが実行中のプロセスからCPUを強制的に回収可能
CPUスケジューリングが必要な4つの状況がある。
- 実行状態から待機状態への遷移(I/O要求)- ノンプリエンプティブ
- 実行状態から準備状態への遷移(割り込み)- プリエンプティブ
- 待機状態から準備状態への遷移(I/O完了)- プリエンプティブ
- プロセスの終了 - ノンプリエンプティブ
ディスパッチャ(Dispatcher)
ディスパッチャはCPUスケジューラが選択したプロセスに実際にCPUを渡すモジュールである。
- コンテキストスイッチの実行
- ユーザーモードへの切り替え
- プログラムの適切な位置へのジャンプ
ディスパッチレイテンシ(dispatch latency)はできるだけ短くなければならない。
スケジューリング基準
| 基準 | 説明 | 最適化方向 |
|---|---|---|
| CPU利用率 | CPUが動作している時間の比率 | 最大化 |
| スループット(Throughput) | 単位時間あたりの完了プロセス数 | 最大化 |
| ターンアラウンドタイム | 提出から完了までの時間 | 最小化 |
| 待ち時間(Waiting) | レディキューで待機した総時間 | 最小化 |
| 応答時間(Response) | 提出から最初の応答までの時間 | 最小化 |
スケジューリングアルゴリズム
1. FCFS(First-Come, First-Served)
最もシンプルなスケジューリング。先に到着したプロセスが先に実行される。
[FCFS例]
プロセス 到着時間 CPUバースト
P1 0 24
P2 0 3
P3 0 3
実行順序:P1 -> P2 -> P3
ガントチャート:
|----P1----|--P2--|--P3--|
0 24 27 30
待ち時間:P1=0, P2=24, P3=27
平均待ち時間:(0+24+27)/3 = 17
P2, P3, P1の順序だったら?
|P2|P3|----P1----|
0 3 6 30
待ち時間:P1=6, P2=0, P3=3
平均待ち時間:(6+0+3)/3 = 3(大きな差!)
コンボイ効果(Convoy Effect): 長いプロセスの後ろに短いプロセスが並ぶ現象。FCFSの代表的な欠点である。
2. SJF(Shortest-Job-First)
CPUバーストが最も短いプロセスを先に実行する。理論的に最適な平均待ち時間を保証する。
[SJF例 - ノンプリエンプティブ]
プロセス 到着時間 CPUバースト
P1 0 6
P2 2 8
P3 4 7
P4 5 3
ガントチャート:
|--P1--|P4|---P3---|----P2----|
0 6 9 16 24
待ち時間:P1=0, P2=16, P3=5, P4=1
平均待ち時間:(0+16+5+1)/4 = 5.5
[SRTF(Shortest Remaining Time First)- プリエンプティブSJF]
プロセス 到着時間 CPUバースト
P1 0 6
P2 2 8
P3 4 7
P4 5 3
時間 0: P1開始(残り:6)
時間 2: P2到着(残り:8)-> P1継続(残り:4)
時間 4: P3到着(残り:7)-> P1継続(残り:2)
時間 5: P4到着(残り:3)-> P1継続(残り:1)
時間 6: P1完了 -> P4開始(残り:3)
時間 9: P4完了 -> P3開始(残り:7)
時間 16: P3完了 -> P2開始(残り:8)
時間 24: P2完了
ガントチャート:
|--P1--|P4|---P3---|----P2----|
0 6 9 16 24
SJFの核心的な問題は、次のCPUバースト長を事前に知ることができないことである。実際には過去のバーストに基づく**指数平均(exponential averaging)**で予測する。
次の予測値 = alpha * 最近の実測値 + (1 - alpha) * 前回の予測値
alpha = 0 なら最近の値を無視(前回の予測値のみ使用)
alpha = 1 なら前回の予測を無視(最近の実測値のみ使用)
通常 alpha = 0.5 を使用
3. 優先度スケジューリング(Priority Scheduling)
各プロセスに優先度を付与し、最も高い優先度のプロセスを先に実行する。
[優先度スケジューリング例](小さい数値 = 高い優先度)
プロセス CPUバースト 優先度
P1 10 3
P2 1 1
P3 2 4
P4 1 5
P5 5 2
実行順序:P2 -> P5 -> P1 -> P3 -> P4
ガントチャート:
|P2|--P5--|----P1----|P3|P4|
0 1 6 16 18 19
平均待ち時間:(6+0+16+18+1)/5 = 8.2
飢餓(Starvation): 低い優先度のプロセスが永遠に実行されない問題。**エイジング(Aging)**技法で解決する。時間の経過とともに待機中のプロセスの優先度を徐々に上げる。
4. ラウンドロビン(Round Robin)
タイムシェアリングシステム用のスケジューリング。各プロセスに固定タイムクォンタム(time quantum)を付与する。
[ラウンドロビン例](タイムクォンタム = 4)
プロセス CPUバースト
P1 24
P2 3
P3 3
ガントチャート:
|P1|P2|P3|P1|P1|P1|P1|P1|
0 4 7 10 14 18 22 26 30
P1:4単位実行 -> レディキューの末尾へ
P2:3単位実行(完了)
P3:3単位実行(完了)
P1:残り20単位を4ずつ分けて実行
待ち時間:P1=6, P2=4, P3=7
平均待ち時間:(6+4+7)/3 = 5.67
タイムクォンタム(q)の選択が重要である。
- qが大きすぎると:FCFSと同じ
- qが小さすぎると:コンテキストスイッチオーバーヘッド増加
- 経験的にCPUバーストの80%がq以内に完了するように設定
5. 多段キュースケジューリング(Multilevel Queue)
レディキューを複数のキューに分離し、各キューに異なるスケジューリングアルゴリズムを適用する。
[多段キュースケジューリング]
高優先度
+------------------------------------------+
| システムプロセス | FCFS |
+------------------------------------------+
| 対話型プロセス | RR (q=8) |
+------------------------------------------+
| 対話型編集プロセス | RR (q=16) |
+------------------------------------------+
| バッチプロセス | FCFS |
+------------------------------------------+
| 学生プロセス | FCFS |
+------------------------------------------+
低優先度
上位キューが空になって初めて下位キューを実行(固定優先度)
またはキュー間の時間配分(例:80%対話型、20%バッチ)
6. 多段フィードバックキュー(Multilevel Feedback Queue)
プロセスがキュー間を移動できる。最も柔軟なスケジューリングアルゴリズムである。
[多段フィードバックキュー]
キュー 0: RR (q=8) <--- 新しいプロセスがここに入る
|
| q=8以内に未完了ならキュー1へ移動
v
キュー 1: RR (q=16)
|
| q=16以内に未完了ならキュー2へ移動
v
キュー 2: FCFS
動作例:
- CPUバーストが短いプロセス:キュー0ですぐに完了(速い応答)
- CPUバウンドプロセス:キュー0 -> キュー1 -> キュー2へ下がる
- I/O完了後に復帰するプロセス:再びキュー0に上がる
結果:I/Oバウンド+対話型プロセスに高い優先度
CPUバウンドプロセスは低いキューで長い時間割り当て
スレッドスケジューリング
- PCS(Process-Contention Scope): ユーザーレベルで同じプロセスのスレッド同士が競合
- SCS(System-Contention Scope): システムレベルですべてのスレッドが競合
LinuxとWindowsはSCSを使用する。カーネルスレッドのみがCPUにスケジューリングされる。
マルチプロセッサスケジューリング
アプローチ
- 非対称マルチプロセッシング: 1つのプロセッサがすべてのスケジューリング決定(単純だがボトルネック)
- 対称マルチプロセッシング(SMP): 各プロセッサが独立してスケジューリング(現代の標準)
プロセッサアフィニティ(Processor Affinity)
プロセスは現在実行中のプロセッサに留まる傾向がある。キャッシュにデータが載っているためである。
- ソフトアフィニティ: できるだけ同じプロセッサで実行するが保証しない
- ハードアフィニティ: 特定のプロセッサセットでの実行を保証
// Linuxでのプロセッサアフィニティ設定
#define _GNU_SOURCE
#include <sched.h>
void set_cpu_affinity(int cpu_id) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpu_id, &cpuset);
// 現在のスレッドを特定のCPUにバインド
sched_setaffinity(0, sizeof(cpuset), &cpuset);
}
負荷分散(Load Balancing)
SMPシステムですべてのプロセッサに作業を均等に分配する。
- Push移行: 定期的に過負荷プロセッサの作業を移動
- Pull移行: アイドルプロセッサが忙しいプロセッサから作業を取得
Linux CFS(Completely Fair Scheduler)
Linux 2.6.23からデフォルトスケジューラとして使用されるCFSは、各プロセスに「公平な」CPU時間を分配する。
核心概念:仮想実行時間(vruntime)
[CFS動作原理]
各プロセスのvruntimeを追跡
vruntimeが最も小さいプロセスを次に実行
vruntime増加率 = 実際の実行時間 / 重み(nice値ベース)
nice値が低い(優先度が高い)プロセス:
-> 重みが大きい -> vruntimeがゆっくり増加 -> より頻繁にスケジューリング
nice値が高い(優先度が低い)プロセス:
-> 重みが小さい -> vruntimeが速く増加 -> あまりスケジューリングされない
Red-Blackツリー
CFSはRed-Blackツリーを使用してプロセスをvruntime基準で管理する。
[CFSのRed-Blackツリー]
vruntimeでソート
(P3: 50)
/ \
(P1: 30) (P5: 70)
/ \ \
(P2: 20) (P4: 40) (P6: 80)
最も左のノード(P2, vruntime=20)が次の実行対象
挿入/削除/検索:O(log N)保証
// CFSで次に実行するプロセスを選択(概念的コード)
struct task_struct *pick_next_task_fair(struct rq *rq) {
struct cfs_rq *cfs_rq = &rq->cfs;
struct rb_node *left;
// Red-Blackツリーで最も左のノードを選択
// = vruntimeが最も小さいプロセス
left = rb_first_cached(&cfs_rq->tasks_timeline);
if (!left)
return NULL;
struct sched_entity *se = rb_entry(left,
struct sched_entity,
run_node);
return container_of(se, struct task_struct, se);
}
ターゲットレイテンシ(Target Latency)
CFSはターゲットレイテンシ内にすべての実行可能なプロセスが最低1回は実行されることを保証する。
例:ターゲットレイテンシ = 20ms、実行可能プロセス = 4個
同一優先度なら各プロセスに5ms割り当て
プロセスが多すぎる場合は最小単位(min_granularity)を保証
アルゴリズム比較まとめ
[スケジューリングアルゴリズム比較]
アルゴリズム プリエンプ 飢餓 利点 欠点
--------- -------- ---- ---- ----
FCFS X X シンプル コンボイ効果
SJF X O 最適待ち時間 バースト予測困難
SRTF O O SJFより良い性能 バースト予測困難
優先度 両方 O 柔軟 飢餓(エイジング必要)
ラウンドロビン O X 公平、良い応答 q選択が重要
多段キュー O O 差別化サービス キュー間移動不可
多段FBキュー O 可能 最も柔軟 パラメータ設定複雑
CFS O X 公平なCPU分配 実装が複雑
まとめ
CPUスケジューリングはマルチプログラミングOSの核心である。FCFS、SJF、優先度、ラウンドロビンなど様々なアルゴリズムがあり、それぞれスループット、応答時間、公平性の間で異なるトレードオフを提供する。LinuxのCFSはRed-Blackツリーと仮想実行時間の概念を活用して、効率的かつ公平なスケジューリングを実現する。