来看一个比完全公平调度简单N倍的实时调度RT
2.7 实时调度类
2.7.1 性质
- 实时进程:相比普通进程,实时进程实时抢占当前进程运行,适合一些实时性较高的应用
- 进程转换为实时进程,必须使用
sched_setscheduler系统调用
- 进程转换为实时进程,必须使用
- 调度特点:如果系统中有一个实时进程在运行,那么调度器总是会选中它运行,除非有一个优先级更高的实时进程
- 实时调度策略
- 轮询(
SCHED_RR):含时间片,优先级相同的实时进程依次执行,时间片到期置于队列末尾 - 先进先出(
SCHED_FIFO):不含时间片,调度器选择实时进程执行后,可以运行任意长时间
- 轮询(
2.7.2 数据结构
- 就绪队列:只需使用一个链表数组保存
active.queue:实时进程的链表数组,类似哈希表结构,如图:
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_LONG)
#define DECLARE_BITMAP(name,bits) unsigned long name[BITS_TO_LONGS(bits)]
struct rt_rq {
struct rt_prio_array active; //实时进程的链表数组
......
};
struct rt_prio_array {
DECLARE_BITMAP(bitmap, MAX_RT_PRIO+1); //位图,unsigned long bitmap[4],链表内是否有进程
struct list_head queue[MAX_RT_PRIO]; //实时进程的100个优先级对应的链表
};
- 关键函数
update_curr:相比CFS没有虚拟时钟,只需要更新实际时间即可
/* 更新实时进程时间 */
static void update_curr_rt(struct rq *rq) {
struct task_struct *curr = rq->curr;
delta_exec = rq->clock - curr->se.exec_start;
......
//当前进程在CPU上执行的时间记录到sum_exec_runtime
curr->se.sum_exec_runtime += delta_exec;
curr->se.exec_start = rq->clock;
......
}
2.7.3 调度器操作
- 进程加入队列
enqueue_task_rt()- 加入对应优先级的链表的尾部
- 设置对应优先级的比特位
static void enqueue_task_rt(struct rq *rq, struct task_struct *p, int wakeup) {
struct rt_prio_array *array = &rq->rt.active;
list_add_tail(&p->run_list, array->queue + p->prio);
__set_bit(p->prio, array->bitmap);
}
- 选择下一个要执行的进程
pick_next_task_rt()- 取最高优先级的链表中的第一个进程调度
static struct task_struct *pick_next_task_rt(struct rq *rq) {
struct rt_prio_array *array = &rq->rt.active;
struct task_struct *next;
struct list_head *queue;
int idx;
//找最高优先级的实时进程链表
idx = sched_find_first_bit(array->bitmap);
if (idx >= MAX_RT_PRIO)
return NULL;
//取链表的第一个进程调度
queue = array->queue + idx;
next = list_entry(queue->next, struct task_struct, run_list);
//进程调度时间更新
next->se.exec_start = rq->clock;
return next;
}
- 处理抢占
task_tick_rt()- RR策略:时间片与HZ有关,时间片到的时候,将进程排队到队列末尾
- FIFO策略:不处理抢占,可以运行任意长时间(类似批处理)
/*
* These are the 'tuning knobs' of the scheduler:
*
* default timeslice is 100 msecs (used only for SCHED_RR tasks).
* Timeslices get refilled after they expire.
*/
#define DEF_TIMESLICE (100 * HZ / 1000)
static void task_tick_rt(struct rq *rq, struct task_struct *p) {
//更新时间
update_curr_rt(rq);
//FIFO策略:没有时间片,不处理抢占,除非使用yield
if (p->policy != SCHED_RR)
return;
//RR策略,时间片不为0时递减
if (--p->time_slice)
return;
//时间片为0时复位,默认100ms
p->time_slice = DEF_TIMESLICE;
// 如果不是队列上的唯一成员,则重新排队到末尾
if (p->run_list.prev != p->run_list.next) {
requeue_task_rt(rq, p);
set_tsk_need_resched(p);
}
}