一个新进程在创建时,调用sched_fork来设置调度信息,其中关于时间片问题调用如下函数:
sched_fork(struct task_struct *p,int
clone_flags)
---------->set_task_cpu(p,cpu)
- void set_task_cpu(struct task_struct *p,unsigned int new_cpu)
-
{
-
int old_cpu =task_cpu(p);//父进程所在的cpu
-
struct rq *old_rq = cpu_rq(old_cpu),*new_rq = cpu_rq(new_cpu);
-
struct cfs_rq *old_cfsrq = task_cfs_rq(p),//获取负载均衡之前的运行队列
-
//负载均衡之后的运行队列
-
*new_cfsrq = cpu_cfs_rq(old_cfsrq,new_cpu);
-
u64 clock_offset;
-
clock_offset = old_rq->clock – new_rq->clock;
-
….
-
//新进程获取的运行时间(需要测试),没有看到其初始化过程
-
p->se.runtime -= old_cfsrq ->min_vruntime – new_cfsrq->min_vruntime;
-
__set_task_ cpu(p,new_cpu);
-
}
当一个新进程运行时,调用函数wake_up_new_task:
- void fastcall wake_up_new_task(struct task_struct *p,
-
unsigned long clone_flags)
-
{
-
unsigned long flags;
-
struct rq *rq;
-
rq = task_rq_lock(p,&flags);
-
BUG_ON(p->state != TASK_RUNNING);
-
update_rq_lock(rq);
-
p->prio = effective_prio(p);
-
//将任务加入运行队列中
-
if(!p->sched_class->task_new || !current->se.on_rq) {
-
activate_task(rq,p,0);
-
} else {
-
p->sched_class->task_new(rq,p);
-
inc_nr_running(p,rq);
-
}
-
check_preempt_curr(rq,p);
-
task_rq_unlock(rq,&flags);
-
}
如果是cfs调度器,就会运行下面的程序:
- static void task_new_fair(struct rq *rq,struct task_struct *p)
-
{
-
struct cfs_rq *rq = task_cfs_rq(p);
-
struct sched_entity *se = &p->se,*curr =cfs_rq->curr;
-
int this_cpu = smp_processor_id();
-
sched_info_queued(p);
-
update_curr(cfs_rq);
-
place_entity(cfs_rq,se,1);//初始化该进程的时间片
-
//让子进程在父进程之前运行,最小的运行时间,则放到红黑树的左边
-
if(sysctl_sched_child_runs_first && this_cpu == task_cpu(p) &&
-
curr && curr->vruntime < se->vruntime) {
-
swap(curr->vruntime,se->vruntime);
-
}
-
enqueue_task_fair(rq,p,0);//放入运行队列
-
resched_task(rq->curr);//设置该运行队列中当前任务可调度,让子进程运行
-
}
- static void place_entity(struct cfs_rq *cfs_rq,struct sched_entity *se,int initial)
-
{
-
u64 vruntime;
-
vruntime = cfs_rq ->min_vruntime;
-
if(sched_feat(TREE_AVG)) { //类似调度特征,静态设置,不清楚具体含义,默认为假
-
//选择最右端的进程(时间片最多的进程)
-
struct sched_entity *last = __pick_last_entity(cfs_rq);
-
if(last) {
-
vruntime += last_vruntime;
-
vruntime >>=1;
-
}
-
} else if(sched_feat(APPRO_AVG) && cfs_rq->nr_running)//默认为假
-
vruntime += sched_vslice(cfs_rq)/2;//根据当前运行队列的权重计算时间片
-
if(initial && sched_feat(START_DEBIT))
-
vruntime += sched_vslice_add(cfs_rq,se);//将新进程的权重加到队列中去,之后再计算时间片,保证其先运行
-
if(!initial) { //当将任务放入运行队列时
-
if(sched_feat(NEW_FAIR_SLEEPERS))
-
vruntime -= sysctl_sched_latency;//如果睡眠,则除去调度延迟
-
vruntime = max_vruntime(se->vruntime,vruntime);//得到最大时间
-
}
-
se->vruntime = vruntime;//真正的新进程时间片
-
}
通过权重计算运行时间片:
- //vruntime += sched_vslice(cfs_rq)/2;
-
sched_vslice(cfs_rq)
-
----->__sched_vslice(cfs_rq->load.weight,cfs_rq->nr_running);
-
static u64 __sched_vslice(unsigned long rq_weight,unsigned long nr_running)
-
{
-
u64 vslice = __sched_period(nr_running);
-
//NICE_0_LOAD-->SCHED_LOAD_SCALE-->1L<<10:1024
-
vslice *= NICE_0_LOAD;
-
//根据权重来计算每个进程应该运行的时间,权重越大,优先级越高,时间片越小,则运行的机会就越多,这里权重根本与进程无关,至取决于进程所在的运行队列
-
do_div(vslice,rq_weight);//vslice = vslice/rq_weight;
-
return vslice;
-
}
-
//决定一个延迟周期的时间,也就是所有进程都运行一次的总时间
-
static u64 __sched_period(unsigned long nr_running)
-
{
-
//sysctl_sched_latency:每个进程应该运行一次的间隔时间,默认为20ms
-
u64 period = sysctl_sched_latency;
-
//sched_nr_latency:在该间隔时间内运行的进程个数,默认为5
-
usigned long nr_latency = sched_nr_latency;
-
//一个延迟周期的总时间为:sysctl_sched_latency*nr_running/sched_nr_latency
-
if(unlikely(nr_running > nr_latency)) {
-
period *= nr_running;
-
do_div(period,nr_latency);
-
}
-
return period;
-
}
系统中,通过active_task对enqueue_task进行封装:
- static void activate_task(struct rq *rq,struct task_struct *p,int wakeup)
-
{
-
if(p->state == TASK_UNINTERRUPTIBLE)
-
rq->nr_uninterruptible--;
-
enqueue_task(rq,p,wakeup);
-
inc_nr_running(p,rq);
-
}
-
static void enqueue_task(struct rq *rq,struct task_struct *p ,int wakeup)
-
{
-
sched_info_queued(p);
-
p->sched_class->enqueue_task(rq,p,wakeup);
-
p->se.on_rq = 1;
-
}
参考资料:
linux-2.6.24-3内核源码
professional linux kernel architecture
源码交叉引用:
阅读(1691) | 评论(0) | 转发(1) |