ここでは,act_tsk()等で使用するt_acquire_tsk_lock,とmig_tsk(),mact_tsk()等で使用するt_acquire_dual_tsk_lockについて解説します.
ジャイアントロック方式では,タスクロック,オブジェクトロックの区別なく全体を一つのロック変数で排他制御を行います.よって,タスクロック,オブジェクトロックで排他制御をおこなうプロセッサロック方式,細粒度ロック方式とは実装方法が大きく異なります.ジャイアントロック方式は実装が簡単なため,ここではプロセッサロック方式,細粒度ロック方式での実装をご紹介します.

t_acquire_tsk_lock():任意タスクのタスクロックの取得
act_tsk()では以下のように,t_acquire_tsk_lock()を呼び出します.

281 ER 
282 act_tsk(ID tskid)
283 {
284 TCB *p_tcb;
285 ER ercd;
286 bool_t dspreq = false;
287 PCB *p_pcb;
288
289 LOG_ACT_TSK_ENTER(tskid);
290 CHECK_TSKCTX_UNL();
291 CHECK_TSKID_SELF(tskid);
292
293 t_lock_cpu();
294 p_tcb = get_tcb_self(tskid, get_my_p_pcb());
295 p_pcb = t_acquire_tsk_lock(p_tcb);


p_tcbは起動するタスクのTCBを指すポインタです.よって,295行目では起動するタスクを指定してタスクロックを取得します(任意タスクのタスクロックの取得).

・プロセッサロック方式,細粒度ロック方式
[kernel/mp.c]
348 PCB* 
349 t_acquire_tsk_lock(TCB *p_tcb)
350 {
351 PCB *p_pcb;
352
353 while(true) {
354 p_pcb = p_tcb->p_pcb;
355 t_acquire_lock(&(p_pcb->tsk_lock));
356 if (p_pcb != p_tcb->p_pcb) {
357 /* 対象タスクがマイグレートした場合 */
358 x_release_lock(&(p_pcb->tsk_lock));
359 } else {
360 return(p_pcb);
361 }
362 }
363 }


354行目:指定したタスクの割り付けられているプロセッサのPCBを取得する.→p_pcb
355行目:p_pcbのタスクロックを取得する.
356行目:ロック取得後,対象タスクの割り付けられているプロセッサのPCBがロックを主とするときに使用したp_pcbと同じであるか確認する.→マイグレーションチェック(第15回参照)
358行目:対象タスクの割り付けられているプロセッサが,ロックを取得するときに使用したp_pcbと異なる場合,タスクがマイグレーションされているので,355行目で取得したタスクロックは無効であるため,解放し,353行目から再度処理を行う.
360行目:マイグレーションしていないので,タスクロックの取得は成功し,p_pcbを返す.

t_acquire_dual_tsk_lock():2個のタスクロックの取得
mig_tsk()では,t_acquire_dual_tsk_lock()を以下のように呼び出します.

150 ER 
151 mig_tsk(ID tskid, ID prcid)
152 {
153 TCB *p_tcb;
154 ER ercd = E_OK;
155 PCB *t_p_pcb;
156 PCB *f_p_pcb;
157 bool_t dspreq = false;
158 PCB *my_p_pcb;
162
163 LOG_MIG_TSK_ENTER(tskid, prcid);
164 CHECK_TSKCTX_UNL();
165 CHECK_TSKID_SELF(tskid);
166 CHECK_PRCID_INI(prcid);
167
168 t_lock_cpu();
169 p_tcb = get_tcb_self(tskid, get_my_p_pcb());
170 prcid = (prcid == TPRC_INI)? p_tcb->p_tinib->iaffinity : prcid;
171 T_CHECK_MIG(p_tcb->p_tinib->affinity_mask, prcid);
172
173 * 現在割り付けられているプロセッサと移動先のプロセッサのタスクロックを取得 */
174 t_acquire_dual_tsk_lock(p_tcb, prcid, &f_p_pcb, &t_p_pcb);
プロセッサ1に割り付けられているタスク(TASK1_1)が,プロセッサ1に割り付けられているタスク(TASK1_2)に対して,プロセッサ2に割り付けプロセッサを変更するように,サービスコールmig_tsk(TASK1_2,2)を発行した例で考えます.
この例の場合は,174行目で,以下のように呼び出されます.

t_acquire_dual_tsk_lock(TASK1_2, 2, &f_p_pcb, &t_p_pcb);


・プロセッサロック方式,細粒度ロック方式
[kernel/mp.c]
447 void  
448 t_acquire_dual_tsk_lock(TCB *p_tcb, ID dstprcid,
449 PCB **pp_srcpcb, PCB **pp_dstpcb)
450 {
451 PCB* my_p_pcb;
452
453 *pp_dstpcb = get_mp_p_pcb(dstprcid);
454
455 while(true) {
456 *pp_srcpcb = p_tcb->p_pcb;
457 if ((*pp_srcpcb)->prcid > dstprcid) {
458 /* 1段目のロックを取得 */
459 t_acquire_lock(&((*pp_dstpcb)->tsk_lock));
460 my_p_pcb = get_my_p_pcb();
461 my_p_pcb->p_firstlock = &((*pp_dstpcb)->tsk_lock);
462 if (!t_acquire_nested_lock(&((*pp_srcpcb)->tsk_lock))) {
463 /* 2段目のロックが取得できた場合 */
464 my_p_pcb->p_firstlock = NULL;
465 if (*pp_srcpcb != p_tcb->p_pcb) {
466 /* 対象タスクがマイグレートした場合 */
467 x_release_lock(&((*pp_srcpcb)->tsk_lock));
468 x_release_lock(&((*pp_dstpcb)->tsk_lock));
469 } else {
470 return;
471 }
472 }
473 } else if ((*pp_srcpcb)->prcid < dstprcid) {
474 /* 1段目のロックを取得 */
475 t_acquire_lock(&((*pp_srcpcb)->tsk_lock));
476 my_p_pcb = get_my_p_pcb();
477 my_p_pcb->p_firstlock = &((*pp_srcpcb)->tsk_lock);
478 if (!t_acquire_nested_lock(&((*pp_dstpcb)->tsk_lock))) {
479 my_p_pcb->p_firstlock = NULL;
480 if (*pp_srcpcb != p_tcb->p_pcb) {
481 /* 対象タスクがマイグレートした場合 */
482 x_release_lock(&((*pp_dstpcb)->tsk_lock));
483 x_release_lock(&((*pp_srcpcb)->tsk_lock));
484 } else {
485 return;
486 }
487 }
488 }
489 else {
490 t_acquire_lock(&((*pp_srcpcb)->tsk_lock));
491 if (*pp_srcpcb != p_tcb->p_pcb) {
492 /* 対象タスクがマイグレートした場合 */
493 x_release_lock(&((*pp_srcpcb)->tsk_lock));
494 } else {
495 return;
496 }
497 }
498 }
499 }


全体の流れを,図10に示します.



[図10 t_acquire_dual_tsk_lock()全体の流れ]

453行目:第4引数で渡した,t_p_pcbに移動先プロセッサ(プロセッサ2)のPCBアドレスを格納します.
456行目:第3引数で渡した,f_p_pcbに移動元プロセッサであるTASK1_2の現在の割り付けプロセッサ(プロセッサ1)のPCBアドレスを格納します.

デッドロック回避
デッドロックを回避するため,タスクIDの小さいタスクからロックを取得します.
(a)457行目以降:移動元プロセッサID>移動先プロセッサID
  移動先プロセッサ→移動元プロセッサの順にロックを取得
(b)473行目以降:移動元プロセッサID<移動先プロセッサID
  移動元プロセッサ→移動先プロセッサの順にロックを取得
(c)489行目以降:移動元プロセッサID=移動先プロセッサID
  ロックは1つ(移動元もしくは移動先プロセッサ)しか取得しない

今回の例では,移動元プロセッサID(1)<移動先プロセッサID(2)となるので,(b)の場合を見ていきます.

475行目:1段目のロックとして,移動元プロセッサ(プロセッサ1)のPCB内にあるタスクロック変数のアドレスを,1段目のロック取得関数(t_acquire_lock())に渡します.
476行目:my_p_pcbに自プロセッサのPCBアドレスを保存します.
477行目:自プロセッサのp_firstlockに,取得した1段目のロックを登録します.
478行目:移動先プロセッサ(プロセッサ2)のタスクロック変数のアドレスを,2段目のロック取得関数(t_acquire_nested_lock())に渡します.
※2段目のロック取得t_acquire_nested_lock()の結果
false:2段目のロック取得成功
true:2段目のロック取得失敗,かつ,割込みが入った

2段目のロック取得に成功した場合
479行目:自プロセッサのp_firstlockをクリアします.
480行目:456行目で格納した,ロック取得前のTASK1_2の割り付けられているプロセッサと,ロック取得後のTASK1_2の割り付けられているプロセッサが違っていると,TASK1_2がマイグレーションしたことになります.→マイグレーションチェック(第15回参照)
482,483行目:マイグレーションした場合,2段目,1段目のロックを解放し,455行目からの処理を繰り返します.
485行目:マイグレーションしなかった場合,処理は終了です.

2段目のロック取得中に割込みが入った場合
t_acquire_nested_lock()中で,2段目のロック取得に失敗した場合には,一旦割込みを許可します.その間に割込みが入った場合は,t_acquire_nested_lock()の戻り値はtrueとなるので,もう一度455行目から処理を繰り返します.割込み処理の中で,ロックが取得されていれば,ロックを解放して,p_firstlockをNULLにしています.
2段目のロック取得中に割込みが入った場合の処理の流れについて,図11に示します.



[図11 2段目のロック取得中に割込みが入った場合]

(参考)
以下に,ジャイアントロック方式の場合のソースコードを参考までに掲載します.
ジャイアントロックの場合はロック取得のための処理が少ないため,実行効率を考慮してインライン関数として,実装しています.

[kernel/mp.h]
Inline  PCB* 
191 t_acquire_tsk_lock(TCB *p_tcb)
192 {
193 t_acquire_lock(&giant_lock);
194 return(p_tcb->p_pcb);
195 }


[kernel/mp.h]
Inline void 
238 t_acquire_dual_tsk_lock(TCB *p_tcb, ID dstprcid,
239 PCB **pp_srcpcb, PCB **pp_dstpcb)
240 {
241 t_acquire_lock(&giant_lock);
242 *pp_srcpcb = p_tcb->p_pcb;
243 *pp_dstpcb = get_mp_p_pcb(dstprcid);
244 }