主调度流程
__schedule()
函数
主调度的函数原型为 static void __schedule(unsigned int sched_mode)
。
- 该主调度函数在内部执行了任务选择、任务切换的主要工作;
- 参数 sched_mode 可以为:
- SM_NONE:无特殊的调度模式
- SM_PREEMPT:可抢占的调度模式
- SM_RTLOCK_WAIT:等待实时锁的调度模式
调度主流程
-
调度是针对每个 CPU 的,所以首先要获取当前 CPU 的运行队列;
-
运行到此处,说明任务实际已经没有在运行(实际运行的是调度程序),所以停掉运行队列的 hrtick ;
-
调度的过程需要关闭本地中断;
-
上报 RCU 的静止状态,以及清除当前任务持有的 RCU 信息 —— RCU 锁不允许跨调度持有;
-
下面要操作运行队列了,这里首先要对运行队列上锁;而且要使用内存写屏障来保证在以下操作之前,前面的操作已经完成,防止由于乱序执行造成的竞争问题;
-
如果 rq 设置了跳过时钟统计请求就将其升级为忽略时钟统计——这里采用了移位的方式来做标志位的更改;
-
更新 rq 的时间戳和运行时间;
-
获取当前进程的状态;
-
当当前任务是自愿调度时:
-
如果有待处理的信号时,保持当前任务为 TASK_RUNNING 状态;
-
否则根据是否要将当前任务计算到 rq 的负载,来更新 rq 的不可中断任务的数量;
-
将任务去激活:
- 标记任务的 on_rq 的状态;
- 将任务从运行队列上出队;
-
如果当前任务处于 iowait ,增加 rq 等待 io 的数量;
-
更新当前任务的切换(自愿调度/非自愿调度)统计;
-
-
选择下一个待运行的任务;
-
因为要进行任务选择已经完成,这时可以清除重新调度标记和抢占标记;
-
在任务切换前,存在两种情况:
-
选择的下一个任务不是当前任务;
- 更新运行队列的切换次数;
- 更新运行队列的当前任务指针;
- 更新当前进程切换次数;
- 禁止当前任务迁移;
- 切换压力任务切;
- 进行上下文切换 —— 此时 CPU 资源就让渡给新的任务;
-
选择的下一个任务就是当前任务;
- 清除 rq 的忽略时钟更新标志;
- 进行一次负载均衡
-
任务的 on_rq 状态:
- 0:如果为 “0” 表示此任务没有在运行队列上;
- TASK_ON_RQ_QUEUED :此任务在运行队列上;
- TASK_ON_RQ_MIGRATING:此任务正在迁移中;