schedule -> __schedule
static void __sched notrace __schedule(bool preempt)
{
struct task_struct *prev, *next;
unsigned long *switch_count;
struct rq_flags rf;
struct rq *rq;
int cpu;
cpu = smp_processor_id();
smp_processor_id
在ARM32架构中展开为current_thread_info()->cpu,在ARM64中有专门的per-cpu变量cpu_number,
为什么ARM32中不使用per-cpu?毕竟per-cpu属于高速缓存,应该挺快的,不过current_thread_info是寄存器取值再位移,也非常快
rq = cpu_rq(cpu);
找当前cpu的rq,这个也是per-cpu
prev = rq->curr;
当前cpu执行的进程
schedule_debug(prev);
schedule时机的debug,包括栈指针,抢占检测,rcu检查
if (sched_feat(HRTICK))
hrtick_clear(rq);
如果使用了Hrtick,就先清除hrtick,hrtick应该是高精度定时器的进程tick,
注意到这个hrtick是rq上的,主要在进程使用完理想时间片后触发的,用来被动启动schedule切换进程
local_irq_disable();
失能irq,再往下就是临界代码区了,必须完整运行,
rcu_note_context_switch(preempt);
rcu机制,暂时不太清楚具体的功能,以后再说
rq_lock(rq, &rf);
自旋锁定rq,rf应该是死锁检测功能的一部分,以后再看
smp_mb__after_spinlock();
内存屏障,有意思的是只有在ARM64中才有实现,ARM32位的没找到
rq->clock_update_flags <<= 1;
clock_update_flags 左移一位,clock_update_flags包含三个值:
RQCF_REQ_SKIP 0x01 下一次schedule的不更新rq_clock
RQCF_ACT_SKIP 0x02 这一次schedule的不更新rq_clock
RQCF_UPDATED 0x04 指示上一次rq_lock以来更新rq_clock没有
这个flag有啥用,为啥这样搞还不清楚
update_rq_clock(rq);
更新rq的时间戳,貌似也更新了rq的计算量
switch_count = &prev->nivcsw;
不知道是啥含义的切换次数,
应该是非自愿切换次数,也就是被抢占的次数
if (!preempt && prev->state) {
当不是内核抢占且当前进程不可运行时执行之后代码,
也就是这是内核抢占省下来的一段代码,
if (signal_pending_state(prev->state, prev)) {
prev->state = TASK_RUNNING;
当进程有待处理的信号,并且进程可以被信号唤醒或者存在KILL信号,
就是强行唤醒进程,记得前面是因为prev->state不等于0才进入这里,这里又设为0了
不过该进程还没有离队,所以不用实际唤醒,只有设置state就行,之后也就不会离队了
} else {
deactivate_task(rq, prev, DEQUEUE_SLEEP | DEQUEUE_NOCLOCK);
这个就是进程prev的真正离队 DEQUEUE_SLEEP 表示这是进程sleep的离队
DEQUEUE_NOCLOCK表示不需要更新rq_clock
if (prev->in_iowait) {
atomic_inc(&rq->nr_iowait);
delayacct_blkio_start();
}
当前进程是否在io_wait?如果是,那么就增加rq中的计数,
并且开始记录当前进程的io延迟即current->delays->blkio_start = ktime_get_ns();
}
switch_count = &prev->nvcsw;
转化成自愿切换次数,也就是自己时间片用尽或者阻塞后的切换次数
}
next = pick_next_task(rq, prev, &rf);
依靠sched_class的pick函数,选出下一个该执行的进程
clear_tsk_need_resched(prev);
清除进程的need_resched的flag,因为接下来就会切换了,就不需要need_resched了
clear_preempt_need_resched();
这个preempt.need_resched不清楚什么意思
if (likely(prev != next)) {
选出了的next进程不是prev,选出来的进程还是prev这个可能性有点不大,不清楚啥情况下会发生
rq->nr_switches++;
rq->curr = next;
++*switch_count;
trace_sched_switch(preempt, prev, next);
ftrace系统,用于故障调试或性能分析,不详谈
rq = context_switch(rq, prev, next, &rf);
实际的切换过程,主要包括mm与寄存器的切换
} else {
rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
如果next还是prev,那么清除RQCF_ACT_SKIP|RQCF_REQ_SKIP,不清楚啥意思
rq_unlock_irq(rq, &rf);
}
balance_callback(rq);
SMP系统特有的,回调函数,主要包含dl_class与rt_class的pull与push函数
在需要的地方注册进回调链表,在这里执行
注册pull的时机为: rq从有rt进程转变为没有rt进程且有其他进程时,注册pull rt 为什么不直接执行pull?不太清楚
rt进程的优先级增大时,注册pull 为啥不直接pull?
rt_class的pick函数中会注册pusk
rq从没有rt进程转变为有rt进程时,并且当前rq超载overload时,注册push那么什么时刻主动pull和push呢?rt_class的pick时会判断是否需要主动pull
rootdomain中的rto_push_work备注为rto_push_irq_work_func,会判断push
task_woken_rt中也会判断是否push,这里条件比较复杂
}