为了弄清楚在多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 < rq->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 < this_rq->rt.highest_prio.curr)) { WARN_ON(p == src_rq->curr); WARN_ON(!p->on_rq); if (p->prio < src_rq->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; }