マルチプロセッサ用リアルタイムOSの解説

第14回 ロックの詳細(1)ロックの単位とコンフィギュレーションパターン

 

これから3回にわたって,ロックの詳細について解説します.
今回は,ロックの単位,コンフィギュレーションパターンについて,次回以降は,FMPカーネルでの実装方法について解説します.
※この章の内容は,第4回で解説済みです.
サービスコールの実行には,プロセッサ間での排他制御とプロセッサ内での排他制御が必要となります.FMPカーネルでは以下の方法で排他制御を実現しています.

・プロセッサ内排他制御
割込み禁止
・プロセッサ間排他制御
ロックの取得(アトミック命令を用いたスピンロックで実現)

この2つの排他制御はお互いに関連しており,リアルタイム性を確保するためには適切に扱わなければなりません.たとえば,ロックの取得→割込み禁止という順に実行すると,ロックを取得してから割込み禁止にする間に,割込みが入り,それを受け付ける可能性があります.割込みを受け付けるとロックを取得しているため,割込み処理中他のプロセッサを待たせてしまうことになり,プロセッサ数に対するスケーラビリティが確保できません.一方,割込み禁止→ロックの取得という順に実行をすると,ロックの取得を試みる間,割込み禁止となり割込み禁止時間が長くなってしまいます.このようにどちらを先に実行しても問題が発生してしまいます.理想的な実現方法は二つを同時にロックすることですが,1命令でロックの取得と割込み禁止を行うことのできるプロセッサは存在しないため,実現は難しいので,擬似的にソフトウェアで実現します.

FMPカーネルでの実装の概要は以下のようになっています.
ロックの取得は,割込み禁止にしてプロセッサ内排他制御を行ってから行います.しかし,そのままではロックの取得がすぐに行われない場合は,割込み遅延時間がどんどん長くなってしまいます.そこで,ロックの取得に失敗した場合は,いったん割込みを許可しています.
割込みを先に禁止することにより,ロックを取得したまま割込みハンドラを実行することを防ぎ,ロックの取得に失敗した場合は,いったん割込みを許可することにより割込み応答性の悪化を防いでいます.

詳細は次回解説します.
FMPカーネルの主なデータ構造には以下のものがあります.
・タスクコントロールブロック(TCB)
・レディキュー
・オブジェクトのコントロールブロック(セマフォコントロールブロックなど)
・タイマイベントキュー

サービスコール実行時に,これらのデータ構造に対してどのようなアクセスパターンがあるかを調査し,タスクロック,オブジェクトロックの2つのロックを設けることが適切であると判断されました.

◎タスクロック
TCB,レディキュー,タイマイベントキューに対するロック単位
TCBとレディキューは同時にアクセスされる場合が多い.また,タイマイベントキューをアクセスする場合には,TCBやレディキューをアクセスする場合が多い.よって,TCB,レディキュー,タイマイベントキューを同じロック単位に入れる.

◎オブジェクトロック
同期・通信オブジェクトのコントロールブロックに対するロック単位

このタスクロック,オブジェクトロックを使用して,たとえば図1のように,プロセッサ毎にタスクロック,オブジェクト毎にオブジェクトロックを用いる方式が想定されます.この方式は細粒度ロック方式といいます.詳細は後述します.



[図1 細粒度ロック方式]

タスクロック,オブジェクトロックの両方の取得が必要なパターン
サービスコールを分析したところ,大部分のサービスコールはタスクロックかオブジェクトロックのどちらかを取得するのみであることがわかりました.一方,タスクロック,オブジェクトロックの両方をとる必要があるのは,以下の3つのパターンであることがわかりました.

.織好に対する特殊操作
アクセス対象のタスクのTCBおよびレディキューと,そのタスクが持っている同期・通信オブジェクトのコントロールブロックをアクセスする.
(例)ter_tsk()であるタスクを強制終了させる際,そのタスクがオブジェクト待ち状態の場合には,そのオブジェクトのコントロールブロックにもアクセスする必要があります.この場合はタスクロックをとって初めて,どのオブジェクトの待ち状態になっているかを知るため,タスクロック→オブジェクトロックの順にロックを取得します.

同期・通信オブジェクトに対する待ちを伴う操作
アクセス対象の,同期・通信オブジェクトのコントロールブロックをアクセスし,自タスクが待ちに入る場合には自タスクのTCBおよびレディキューにアクセスする.この場合は,オブジェクトロック→タスクロックの順にロックを取得する.
(例)wai_sem()を発行した場合,オブジェクトロックを取得し,セマフォの獲得を試みます.そこで,セマフォを獲得できなかった場合は,タスクロックを取得し自タスクを待ち状態に遷移します.

F唄・通信オブジェクトに対するタスクの待ち解除を伴う操作
アクセス対象の同期・通信オブジェクトのコントロールブロックと,そのオブジェクトを待っているタスクがあれば,そのTCBおよびレディキューにアクセスする.この場合は,オブジェクトロック→タスクロックの順にロックを取得する.
(例)sig_sem()を発行した場合,まずオブジェクトロックを取得し,セマフォ待ちキューを参照し,待ち状態のタスクがあれば,そのタスクのタスクロックを取得して待ち解除します.

オブジェクトロック→タスクロックの順が原則
タスクロック,オブジェクトロックの2つを取得する必要がある場合には,デッドロック回避のため,取得順序を一意に決める必要があります.

そこで,サービスコールを分析したところ,以下のことがわかりました.
・タスクロック→オブジェクトロックの順で取得
    ,離機璽咼好魁璽襦3個
・オブジェクトロック→タスクロックの順で取得
    △離機璽咼好魁璽襪17個
    のサービスコールは27個

以上より,オブジェクトロック→タスクロックの順で取得する方が多いため,オブジェクトロック→タスクロックの順で取得することを原則としています.
そのため,,離轡好謄爛魁璽襪亮汰に際しては,デッドロック回避の仕組みを入れています.
FMPカーネルでは,タスクロックとオブジェクトロックを用いて排他制御を行います.前述したプロセッサごとにタスクロックを,オブジェクトごとにオブジェクトロックを設ける細粒度ロック方式が理想です.しかしこの方式はオブジェクトごとにオブジェクトロックが必要なため,用いることができるロック数に上限のないターゲットを使用している場合のみ採用できます.しかし,ロック数に上限があるターゲットも存在するため,少ないロック数でも実現できるコンフィギュレーションパターンを用意しています.

・ジャイアントロック方式
システムで1つのロックを使用する方法です[図2]. 1個のロック変数で実現できます.



[図2 ジャイアントロック方式]

・プロセッサロック方式
プロセッサ毎にタスクロックとオブジェクトロックを用いる方式です[図3]. プロセッサ×2個のロック変数が必要です.



[図3 プロセッサロック方式]

・細粒度ロック方式
プロセッサ毎にタスクロック,オブジェクト毎にオブジェクトロックを用いる方式です[図1].ロック変数に上限がない場合に採用できます.



[図1(再掲) 細粒度ロック方式]


※ロック変数の実体
コンフィギュレーションパターンによって,それぞれのロック変数の定義方法が異なります.

・ジャイアントロック方式
    グローバル変数として定義

・プロセッサロック方式
    ・タスクロック: PCB内に定義
    ・オブジェクトロック: PCB内に定義
     各オブジェクトの初期化ブロックにPCB内に確保したオブジェクトロックへのポインタを保持する領域を用意.

・細粒度ロック方式
    ・タスクロック:PCB内に定義
    ・オブジェクトロック: 各オブジェクトのコントロールブロック内に定義