Huney

応用情報(AP) / ソフトウェア

タスク管理とは?状態遷移・スケジューリング・排他制御を基礎から理解しよう

OSのタスク管理について、タスクの状態遷移、コンテキストスイッチ、スケジューリング、同期制御、セマフォ、デッドロック、プロセスとスレッドまで初心者向けに解説します。

読了時間:約13分
  • #ソフトウェア
  • #OS
  • #タスク管理
  • #プロセス
  • #スケジューリング
  • #コンテキストスイッチ
  • #セマフォ
  • #デッドロック
  • #応用情報技術者試験

タスク管理とは?状態遷移・スケジューリング・排他制御を基礎から理解しよう

はじめに

PCでは、

  • Webブラウザを開く
  • 音楽を再生する
  • ファイルをダウンロードする

といった複数の処理を同時に行えます。

しかし、1つのCPUコアがある瞬間に実行できる処理は基本的に1つです。

そこでOSは、

どのタスクを、いつ、どれくらいCPUで実行するか

を管理しています。

これがタスク管理です。


1. タスクとは

**タスク(Task)**とは、

OSによって実行・管理される処理の単位

です。

OSによって用語や管理単位には違いがありますが、基本情報・応用情報ではプロセスと関連付けて理解すると分かりやすいです。

例えば、

ブラウザ
音楽プレイヤー
テキストエディタ

など複数の処理が動いている場合、OSはそれぞれのタスクを管理しながらCPUを割り当てます。


2. タスク管理

OSはタスクに対して、

  • タスクの生成
  • CPUの割当て
  • 実行状態の管理
  • タスクの切り替え
  • タスクの終了

などを管理します。

つまりタスク管理は、

複数のタスクを効率よく実行するためのOSの管理機能

です。


3. タスクの状態遷移

タスクは常にCPUで実行されているわけではありません。

基本的には、

実行可能状態
    ↓
実行状態
    ↓
待ち状態

という状態を行き来します。

代表的な3つの状態を押さえておきましょう。

実行可能状態

CPUが割り当てられれば、すぐに実行できる状態です。

Ready(レディ)状態とも呼ばれます。

実行状態

実際にCPUを使用して処理している状態です。

Running(ランニング)状態とも呼ばれます。

待ち状態

入出力処理などが完了するのを待っている状態です。

Waiting(ウェイティング)状態などと呼ばれます。


4. 状態はどう変化する?

基本的な状態遷移は次のようになります。

              CPUが割り当てられる
実行可能状態 ─────────→ 実行状態
     ↑                     │
     │                     │
     │                     ├─ CPU使用時間終了
     │                     │      ↓
     │                     └→ 実行可能状態
     │
     │ 入出力完了
     │
   待ち状態
     ↑
     │
     └──── 入出力待ちなど
              ↑
           実行状態

例えばファイルを読み込む場合、

① 実行可能
   ↓
② CPUを取得
   ↓
③ 実行状態
   ↓
④ ディスクからデータを読みたい
   ↓
⑤ 待ち状態
   ↓
⑥ 読み込み完了
   ↓
⑦ 実行可能状態

となります。

🍯 「待ち状態 → 実行状態」に直接戻るのではなく、基本的には一度「実行可能状態」に戻るのがポイントです。


5. タスクの切り替え

複数のタスクを動かすためには、

タスクA
 ↓
タスクB
 ↓
タスクC
 ↓
タスクA

のようにCPUで実行するタスクを切り替える必要があります。

しかし、単純に切り替えるだけではありません。

タスクAを途中で止めた場合、

どこまで処理したのか

を覚えておかなければ、続きから再開できません。


6. タスク情報の保持

タスクを再開するためには、

  • CPUのレジスタ
  • プログラムカウンタ
  • スタックポインタ
  • タスクの状態

などの情報を保存しておく必要があります。

このような、

タスクを実行・再開するために必要なCPUなどの状態情報

を**コンテキスト(Context)**と呼びます。


7. コンテキストスイッチ

CPUで実行するタスクを変更するとき、

タスクAの状態を保存
        ↓
タスクBの状態を読み出す
        ↓
タスクBを実行

という処理を行います。

これを、

コンテキストスイッチ(Context Switch)

と呼びます。

つまり、

実行中タスクのコンテキストを保存し、別のタスクのコンテキストを復元してCPUを切り替える処理

です。

コンテキストスイッチそのものは利用者の仕事を直接処理しているわけではないため、切り替えが多すぎるとオーバーヘッドになります。


8. タスク切り替えのタイミング

タスクが切り替わる代表的なタイミングには、

  • タスクが入出力待ちになった
  • タスクの処理が終了した
  • 割り当てられたCPU時間を使い切った
  • より優先度の高いタスクが実行可能になった
  • 割込みが発生した

などがあります。

どのタスクへCPUを割り当てるかを決める仕組みが、次のスケジューリングです。


9. スケジューリングとは

**スケジューリング(Scheduling)**とは、

実行可能なタスクの中から、次にCPUを割り当てるタスクを決めること

です。

OSでは目的に応じて、さまざまなスケジューリング方式が利用されます。


10. 到着順方式

到着順方式は、

実行可能になった順番に処理する方式

です。

到着

A → B → C

実行

A → B → C

考え方が単純で分かりやすい方式です。

**FCFS(First Come First Served)**とも呼ばれます。


11. 優先順位方式

優先順位方式は、

優先度の高いタスクから実行する方式

です。

例えば、

タスクA 優先度:低
タスクB 優先度:高
タスクC 優先度:中

なら、

B → C → A

のように実行します。

ただし、高優先度のタスクが次々と実行されると、低優先度のタスクがなかなか実行されない場合があります。

この状態を**スタベーション(飢餓状態)**と呼びます。


12. ラウンドロビン方式

**ラウンドロビン方式(Round Robin)**では、

一定時間ずつ順番にCPUを割り当てる

方式です。

A → B → C → A → B → C → …

各タスクへ割り当てる一定時間を、

タイムクォンタム(Time Quantum)

またはタイムスライスと呼びます。

1つのタスクがCPUを長時間独占することを防ぎやすい方式です。


13. フィードバック待ち行列方式

フィードバック待ち行列方式では、優先度の異なる複数の待ち行列を用意します。

例えば、

高優先度  ─ タスクA
              ↓
中優先度  ─ タスクB
              ↓
低優先度  ─ タスクC

CPUを長く使用するタスクの優先度を下げるなど、

タスクの実行状況に応じて優先度を変化させる

方式です。

**多段フィードバック待ち行列(Multilevel Feedback Queue)**とも呼ばれます。


14. 処理時間順方式

処理時間順方式は、

予想される処理時間が短いタスクから実行する方式

です。

例えば、

A:10秒
B:2秒
C:5秒

なら、

B → C → A

のように処理します。

**SJF(Shortest Job First)**とも呼ばれます。

平均待ち時間を短くしやすい一方で、事前に処理時間を正確に把握するのが難しい場合があります。


15. スケジューリング方式を整理

方式 考え方
到着順方式 先に来たタスクから実行
優先順位方式 優先度の高いタスクから実行
ラウンドロビン方式 一定時間ずつ順番に実行
フィードバック待ち行列方式 実行状況によって優先度を変更
処理時間順方式 処理時間の短いタスクから実行

🍯 方式名だけではなく「次のタスクを何を基準に選ぶのか」で覚えると整理しやすいです。


16. リアルタイムOSのスケジューリング

**リアルタイムOS(RTOS:Real-Time Operating System)**では、

単純に処理が速いことだけではなく、

決められた時間までに処理を完了できること

が重要です。

例えば、

  • 自動車制御
  • ロボット
  • 工場設備
  • 組込みシステム

などでは、処理が遅れること自体が問題になります。


17. リアルタイムOSの代表的な方式

代表的なスケジューリング方式として、

Rate Monotonic Scheduling

**RMS(Rate Monotonic Scheduling)**は、

実行周期が短いタスクほど高い優先度を与える方式

です。

優先度は基本的に固定されます。

Earliest Deadline First

**EDF(Earliest Deadline First)**は、

締切時刻が近いタスクから優先して実行する方式

です。

こちらは締切に応じて優先度が変化します。

RMS
→ 周期が短いものを優先

EDF
→ 締切が近いものを優先

と覚えると分かりやすいです。


18. 同期制御

複数のタスクが連携して動く場合、

「タスクAの処理が終わってからタスクBを実行する」

など、処理のタイミングを合わせる必要があります。

これを、

同期制御(Synchronization)

と呼びます。


19. イベントフラグ

**イベントフラグ(Event Flag)**は、

あるイベントが発生したことをタスクへ知らせるための仕組み

です。

例えば、

タスクA
データ読み込み完了
      ↓
イベントフラグを設定
      ↓
タスクB
処理開始

というように利用できます。

タスクBはフラグが設定されるまで待つことで、タスクAとの処理順序を合わせられます。


20. WAIT/POST

タスク間の同期では、

WAIT

と

POST

という操作が使われることがあります。

WAIT

条件が満たされるまでタスクを待たせます。

POST

待っているタスクへ、

処理を進めてもよい

ことを通知します。

イメージすると、

タスクA
   ↓
WAIT
   ↓
待ち状態

タスクB
   ↓
POST
   ↓
タスクAを起こす

となります。

WAIT/POSTはセマフォなどを操作するときにも使われます。


21. タスク間の通信手段

タスク同士でデータをやり取りする方法には、さまざまなものがあります。

代表例として、

  • 共有メモリ
  • メッセージ
  • メッセージキュー
  • パイプ

などがあります。

例えば共有メモリでは、

タスクA
   ↓
共有メモリ
   ↑
タスクB

のように、複数のタスクが共通のメモリ領域を利用します。

ただし、同じデータへ同時にアクセスすると問題が発生することがあります。

そこで必要になるのが排他制御です。


22. 排他制御

例えば2つのタスクが、同じ銀行口座の残高を書き換えるとします。

残高:10,000円

タスクA → 1,000円引く
タスクB → 2,000円引く

両方が同時に、

残高 = 10,000円

を読み込んで処理すると、正しい結果にならない可能性があります。

そこで、

あるタスクが共有資源を使用している間、ほかのタスクからの利用を制限する

必要があります。

これが**排他制御(Mutual Exclusion)**です。


23. クリティカルセクション

複数のタスクから同時に実行されると問題が起こる可能性がある処理部分を、

クリティカルセクション(Critical Section)

と呼びます。

タスクA

通常処理
   ↓
┌──────────────┐
│ 共有データを更新 │ ← クリティカルセクション
└──────────────┘
   ↓
通常処理

クリティカルセクションには、基本的に複数のタスクが同時に入らないように制御します。


24. セマフォ

排他制御や同期制御を実現する代表的な仕組みが、

セマフォ(Semaphore)

です。

セマフォでは、共有資源を利用できる状態を管理します。

代表的な操作として、

WAIT
↓
資源を確保

処理

POST
↓
資源を解放

という流れがあります。

特に、値を0と1で管理して1つのタスクだけが資源を利用できるようにするものを、

バイナリセマフォ

と呼びます。

また、複数個ある同種の資源を管理する場合には、計数セマフォを利用できます。


25. デッドロック

排他制御を行うときに注意したいのが、

デッドロック(Deadlock)

です。

デッドロックとは、

複数のタスクがお互いの持っている資源が解放されるのを待ち続け、処理が進まなくなる状態

です。

例えば、

タスクA
資源Xを確保
↓
資源Yを待つ


タスクB
資源Yを確保
↓
資源Xを待つ

となった場合、

A「Yが欲しい」
B「Xが欲しい」

A「BがYを離すまで待つ」
B「AがXを離すまで待つ」

となり、どちらも処理を進められません。

これがデッドロックです。


26. プロセスとスレッド

最後に、混同しやすいプロセスとスレッドを整理します。

プロセス

**プロセス(Process)**は、

実行中のプログラムを管理する単位

です。

それぞれのプロセスは、基本的に独立したメモリ空間などの資源を持ちます。


スレッド

**スレッド(Thread)**は、

プロセスの中で実際に処理を実行する単位

です。

1つのプロセスの中に複数のスレッドを持つことができます。

プロセス
│
├─ スレッドA
├─ スレッドB
└─ スレッドC

同じプロセス内のスレッドは、メモリなどの一部の資源を共有できます。

そのため、

プロセス
→ 資源を管理する単位

スレッド
→ CPUで処理を実行する単位

と考えると整理しやすいです。


27. タスク管理の全体像

ここまでを一本につなげてみましょう。

複数のタスクが存在
      ↓
タスク管理
      ↓
状態を管理
実行可能・実行・待ち
      ↓
スケジューリング
      ↓
次に実行するタスクを決定
      ↓
コンテキストスイッチ
      ↓
CPUで実行

さらに複数タスクが協調するときには、

同期制御
├─ イベントフラグ
└─ WAIT / POST

タスク間通信
├─ 共有メモリ
├─ メッセージ
└─ パイプ

共有資源を扱う
      ↓
排他制御
      ↓
セマフォ
      ↓
デッドロックにも注意

という関係になります。


まとめ

この記事で覚えること

  • タスクはOSが実行・管理する処理の単位
  • タスクには実行可能・実行・待ちなどの状態がある
  • コンテキストはタスクを再開するために必要な状態情報
  • コンテキストスイッチによって実行するタスクを切り替える
  • スケジューリングによって次に実行するタスクを決定する
  • 到着順・優先順位・ラウンドロビン・フィードバック待ち行列・処理時間順などがある
  • リアルタイムOSでは締切を守ることが重要
  • 同期制御によって複数タスクの実行タイミングを調整する
  • 排他制御によって共有資源への同時アクセスを防ぐ
  • セマフォは同期・排他制御に利用できる
  • デッドロックはタスク同士が資源を待ち続けて停止する状態
  • プロセスは資源管理の単位、スレッドは処理実行の単位として考えると分かりやすい

🍯 はちみつメモ

タスク管理は「誰にCPUを使わせる?」を管理する仕組み。状態遷移でタスクの状況を管理し、スケジューリングで次のタスクを選び、コンテキストスイッチで実際に切り替える。複数のタスクが同じ資源を使うときは、同期・排他制御も必要になる。