为了弄清楚在多cpu系统中是如何实现实时调度的,先引入以下几个概念:
cpu的状态:
我们知道,在linux系统中,任务的优先级为0~140。
INVALID:(-1)该cpu不可用
IDLE(0):140
NORMAL(1):100~139对应于普通任务的优先级
RT0~RT99(2~102):对应于实时任务的优先级
进程优先级:
在linux内核中,每个进程都有一个task_struct,其中与优先级相关的属性包括
1、prio:对于非实时进程而言prio==normal_prio=99-rt_priority(prio的值越大,则进程的优先级越小)
2、normal_prio:99-rt_priority
3、static_prio:=100+nice
4、rt_priority:0:非实时任务;[1,99]实时任务,值越大,优先级越高。
在调度时使用了prio,其数值0对应最高优先级,99为最低实时优先级。Prio和normal_prio数值越大优先级越小,而rt_priority的数值越大优先级越大。
task balance:
在多cpu系统中,为了保证各个cpu上实时任务的负载均衡,引入了push和pull操作:
1、push操作:
当当前cpu上有多于一个的实时任务,那么需要使用pull操作看看是否可以将还未运行的实时任务移动到其他的cpu上,主要操作由push_rt_task函数完成。static int push_rt_task(struct rq *rq)
{
struct task_struct *next_task;
struct rq *lowest_rq;
int ret = 0;
///如果当前队列不是超载状态,则直接返回
if (!rq->rt.overloaded)
return 0;
///选择rq中下一个进程
next_task = pick_next_pushable_task(rq);
if (!next_task)
return 0;
retry:
......
///如果下一个进程的优先级比当前进程的优先级高,那么需要执行的不是push操作而是重新调度
if (unlikely(next_task->prio curr->prio)) {
resched_task(rq->curr);
return 0;
}
......
///寻找那个cpu的rq符合条件,将其rq上锁
lowest_rq = find_lock_lowest_rq(next_task, rq);
///如果没有找到合适的rq,我们则需要判断:到底还要不要再找,因为在find_lock_lowest_rq函数中释放了当前rq上的锁,因此可能会导致当前rq上没有需要push的任务,在这种情况下我们就不用再试,如果需要push的任务还在,那么则进入retry继续尝试。
if (!lowest_rq) {
struct task_struct *task;
task = pick_next_pushable_task(rq);
if (task_cpu(next_task) == rq->cpu && task == next_task) {
goto out;
}
if (!task)
goto out;
put_task_struct(next_task);
next_task = task;
goto retry;
}
///执行任务迁移相关的工作。
deactivate_task(rq, next_task, 0);
set_task_cpu(next_task, lowest_rq->cpu);
activate_task(lowest_rq, next_task, 0);
ret = 1;
resched_task(lowest_rq->curr);
double_unlock_balance(rq, lowest_rq);
out:
put_task_struct(next_task);
return ret;
}
2、pull操作
与push操作相反,pull操作用于当前cpu的rq比较空闲,想要主动调入实时任务,该操作主要由
pull_rt_task完成。static int pull_rt_task(struct rq *this_rq)
{
int this_cpu = this_rq->cpu, ret = 0, cpu;
struct task_struct *p;
struct rq *src_rq;
if (likely(!rt_overloaded(this_rq)))
return 0;
///查找每一个超载的cpu
for_each_cpu(cpu, this_rq->rd->rto_mask) {
......
///src_rq:需要pull的cpu。
src_rq = cpu_rq(cpu);
///如果src_rq的下一个任务的优先级高于当前cpu的优先级,则什么都不用做,因为src_cpu会主动执行pull操作。
if (src_rq->rt.highest_prio.next >=
this_rq->rt.highest_prio.curr)
continue;
double_lock_balance(this_rq, src_rq);
///判断是否有需要被pull的任务,没有则退出
if (src_rq->rt.rt_nr_running <= 1)
goto skip;
///找到src_rq上最高优先级的任务
p = pick_next_highest_task_rt(src_rq, this_cpu);
if (p && (p->prio rt.highest_prio.curr)) {
WARN_ON(p == src_rq->curr);
WARN_ON(!p->on_rq);
if (p->prio curr->prio)
goto skip;
ret = 1;
deactivate_task(src_rq, p, 0);
set_task_cpu(p, this_cpu);
activate_task(this_rq, p, 0);
}
skip:
double_unlock_balance(this_rq, src_rq);
}
return ret;
}