网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
实时调度策略
SCHED_FIFO
:高优先级的进程可以抢占低优先级的进程,而相同优先级的进程,我们遵循先来先得。SCHED_RR 轮流调度算法
:采用时间片,相同优先级的任务当用完时间片会被放到队列尾部,以保证公平性,而高优先级的任务也是可以抢占低优先级的任务。SCHED_DEADLINE
:是按照任务的 deadline 进行调度的。当产生一个调度点的时候,DL 调度器总是选择其 deadline 距离当前时间点最近的那个任务,并调度它执行。
普通调度策略
SCHED_NORMAL
是普通的进程SCHED_BATCH
是后台进程,不和前台交互 , 只要不影响交互的进程 , 就降低其优先级.SCHED_IDLE
: 没什么进程的时候执行它。- 调度策略的执行逻辑:
const struct sched_class *sched_class;
sched_class 有几种实现: - stop_sched_class 优先级最高的任务会使用这种策略,会中断所有其他线程,且不会被其他任务打断;
- dl_sched_class 就对应上面的 deadline 调度策略;
- rt_sched_class 就对应 RR 算法或者 FIFO 算法的调度策略,具体调度策略由进程的 task_struct->policy 指定;
- fair_sched_class 就是普通进程的调度策略;+ idle_sched_class 就是空闲进程的调度策略。
完全公平调度算法
- 在 Linux 里面,实现了一个基于 CFS 的调度算法。CFS 全称 Completely Fair Scheduling,叫完全公平调度。
- 首先有一个记录时间的, 没过一段时间就出发一个时间中断
Tick
, CFS 会为每一个进程安排一个虚拟运行时间 vruntime。随着时间的增长,也就是一个个 tick 的到来,进程的 vruntime 将不断增大。没有得到执行的进程 vruntime 不变。对于vruntime 少的,原来受到了不公平的对待,需要给它补上,所以会优先运行这样的进程。
调度队列与调度实体
- CFS 使用的是红黑树,能够平衡查询和更新速度的是树。
- 红黑树的的节点是应该包括 vruntime 的,称为调度实体。有 实时调度实体 sched_rt_entity,Deadline 调度实体 sched_dl_entity,以及完全公平算法调度实体 sched_entity。
- 因为任何一个策略做调度的时候,都是要区分谁先运行谁后运行。
- 所有可运行的进程通过不断地插入操作最终都存储在以时间为顺序的红黑树中,vruntime 最小的在树的左侧,vruntime 最多的在树的右侧。 CFS 调度策略会选择红黑树最左边的叶子节点作为下一个将获得 cpu 的任务。
- 每个 CPU 都有自己的 struct rq 结构,其用于描述在此 CPU 上所运行的所有进程,其包括一个实时进程队列 rt_rq 和一个 CFS 运行队列 cfs_rq,在调度时,调度器首先会先去实时进程队列找是否有实时进程需要运行,如果没有才会去 CFS 运行队列找是否有进行需要运行。
调度类工作过程
在每个 CPU 上都有一个队列 rq,这个队列里面包含多个子队列,例如 rt_rq 和 cfs_rq,不同的队列有不同的实现方式,cfs_rq 就是用红黑树实现的。某个 CPU 需要找下一个任务执行的时候,会按照优先级依次调用调度类,不同的调度类操作不同的队列。
- 一个 CPU 上有一个队列,CFS 的队列是一棵红黑树,树的每一个节点都是一个 sched_entity,每个 sched_entity 都属于一个 task_struct,task_struct 里面有指针指向这个进程属于哪个调度类。
主动调度
调度, 切换运行进程, 有两种方式
- 进程调用 sleep 或等待 I/O, 主动让出 CPU
- 进程运行一段时间, 被动让出 CPU
- 主动让出 CPU 的方式, 调用 schedule(), schedule() 调用 __schedule()
- __schedule() 取出 rq; 取出当前运行进程的 task_struct , 再根据指针决定进行上下文切换 还是正式进入运行.
进程上下文切换
上下文切换主要干两件事情,一是切换进程空间,也即虚拟内存;二是切换寄存器和 CPU 上下文。
- switch_to。它就是寄存器和栈的切换, 对于 32 位操作系统来讲,切换的是栈顶指针 esp。对于 64 位操作系统来讲,切换的是栈顶指针 rsp。
- x86 体系结构中, TSS(Task State Segment,任务状态段)结构。这里面有所有的寄存器。还有一个特殊的寄存器 TR(Task Register,任务寄存器),指向某个进程的 TSS。更改 TR 的值,将会触发硬件保存 CPU 所有寄存器的值到当前进程的 TSS 中,然后从新进程的 TSS 中读出所有寄存器值,加载到 CPU 对应的寄存器中。 缺点 太多了
- Linux 中,真的参与进程切换的寄存器很少,主要的就是栈顶寄存器。还记得在系统初始化的时候,会调用 cpu_init 吗?这里面会给每一个 CPU 关联一个 TSS,然后将 TR 指向这个 TSS,然后在操作系统的运行过程中,TR 就不切换了,永远指向这个 TSS。
抢占式调度
- 一个进程执行时间太长了,是时候切换到另一个进程了
- 当前进程应该被抢占,不能直接把它踢下来,而是把它标记为应该被抢占。因为进程调度第一定律呀,一定要等待正在运行的进程调用 __schedule 才行啊,所以这里只能先标记一下。
- 进程被唤醒的时候 :当 I/O 到来的时候,进程往往会被唤醒。这个时候是一个时机。当被唤醒的进程优先级高于 CPU 上的当前进程,就会触发抢占
抢占的时机
分为用户态和内核态
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**