リアルタイムプロセスのスケジュール
Rev.2を表示中。最新版はこちら。
スケジューラ(CPF)のクラスとして、sched_rt/sched_fair/sched_idelの3つのクラスを有していて、プロセス毎に設定されるクラスに応じたスケジュールクラス(コールバック関数群)が設定されます。またプロセス毎に、リアルタイプ用/通常用のランキューのノードとなるメンバーを有しています。__setscheduler()は、sched_setschedulerシステムコールからコールされます。
struct task_struct {
:
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
:
}
static void
__setscheduler(struct rq *rq, struct task_struct *p, int policy, int prio)
{
p->policy = policy;
p->rt_priority = prio;
p->normal_prio = normal_prio(p);
/* we are holding p->pi_lock already */
p->prio = rt_mutex_getprio(p);
if (rt_prio(p->prio))
p->sched_class = &rt_sched_class;
else
p->sched_class = &fair_sched_class;
set_load_weight(p);
}
リアルタイムプロセスはrt_sched_classで管理/操作されることになります。このプロセスはFIFOとRR(ラウンドロビン)の2つのタイプがあります。FIFOはリアルタイププロセス下でプライオリティの高いプロセスが無い限り、動作しつづけます。RRはFIFOと同じですが、タイムスライス間動作するとスイッチします。FIFOは、割り込みでそれより高いリアルタイムプライオリティが動作するとか、自ら実行件を放棄しないかぎり、動作しつづけることになります。タイマー割り込みが発生すると、scheduler_tick()から、クラスに応じた.task_tickに設定されたコールバック関数がコールされます。リアルタイププロセスなら task_tick_r()がコールされます。
プロセスがRRでないなら、実行件を放棄する必要がありません。RRならタイムスライスをデクリメントします。それが0でないならそのまま動作することになります。0なら他のリアルタイムプロセスに実行権を移す必要があります。この場合次の実行にそなえて、実行権をなくしたプロセスのタイムスライスにDEF_TIMESLICEを設定します。これはリアルタイムプライオリティに関係なく100 msecs固定値です。
次に、リアルタイム用プロセスのランキューに、他のプロセスがあるなら、requeue_task_rt()でリアルタイム用プロセスのランキューからこのプロセスを削除し、set_tsk_need_resched()でそのプロセスにTIF_NEED_RESCHEDします。従ったタイマー割り込みから復帰するときに、スケジュールの処理がコールされ、次のプロセスがに切り替わることになります。
#define DEF_TIMESLICE (100 * HZ / 1000)
static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued)
{
update_curr_rt(rq);
watchdog(rq, p);
if (p->policy != SCHED_RR)
return;
if (--p->rt.time_slice)
return;
p->rt.time_slice = DEF_TIMESLICE;
if (p->rt.run_list.prev != p->rt.run_list.next) {
requeue_task_rt(rq, p, 0);
set_tsk_need_resched(p);
}
}
なお、リアルタイムのランキュは、fair_schedと異なり、動作時間管理する必要がないため、単純な双方向リストとなっているようです。(たぶん)上でみてきたようにリアルタイププロセスの挙動は動作時間に関係ないのですが、update_curr_rt()はプロセスの動作時間管理をしています。これはリアルタイムプロセスがグループとなっていた場合、そのグループ間でスイッチングするためのようで・・・す。






