【学习时间:1小时45分 撰写博客时间:2小时10分钟】算法
【学习内容:Linux的进程调度实现、抢占和上下文切换、与调度相关的系统调用】安全
调度程序负责决定将哪一个进程投入运行,什么时候运行以及运行多长时间。进程调度程序可看作在可运行态进程之间分配有限的处理器时间资源的内核子系统。服务器
最大限度利用处理器时间的原则:只要有能够执行的进程,那么总会有程序正在执行。网络
1.概念:多任务操做系统就是能同时并发地交互执行多个进程的操做系统,在单处理器机器上这会产生多个进程在同时运行的幻觉,在多处理器机器上,这会使多个进程在不一样的处理机上真正同时、并行地运行。并发
2. 分类:多任务系统能够划分为两类函数
在Linux 2.5开发系列的内核中,调度程序作了大手术,开始采用了一种叫作O(1)调度程序的新调度程序——它是由于其算法的行为而得名的。性能
策略决定调度程序在什么时候让什么进程运行。学习
1. I/O消耗型进程优化
2. 处理器耗费型spa
3. 调度策略一般要在两个矛盾的目标中间寻找平衡:进程响应迅速(响应时间短)和最大系统利用率(高吞吐量),为了知足上述需求,调度程序一般采用一套很是复杂的算法来决定最值得运行的进程投入运行,可是它每每并不保证低优先级进程会被公平对待,Unⅸ系统的调度程序更倾向于I/O消耗型程序,以提供更好的程序响应速度,Linux为了保证交互式应用和桌面系统的性能,因此对进程的响应作了优化(缩短响应想间)更倾向于优先调度I/O消耗型进程,虽然如此,调度程序也并未忽略处理器消耗型的进程。
1. 基于优先级的调度:优先极高的进程先运行,相同优先级的进程按照轮转方式进行调度。
2. 优先级分为两类:
注:两者互不交互。
CFS采用的方法是对时间片分配方式进行根本性的从新设计(就进程调度器而言)彻底摒弃时间片而是分配给进程一个处理器使用比重,经过这种方式,CFS的确保了进程调度中能有恒定的公平性,而将切换频率置于不断变更中。
CFS基于一个简单的理念:进程调度的效果应当如同系统具有一个理想中的完美任务处理器。CFS的作法以下:
在理想状况下,完美的多任务处理器模型应该是这样的:咱们能在5ms内同时运行两个进程,它们各自使用处理器一半的能力。
CFS相关代码位于kernel/sched_fair.c中。它有四个组成部分:
全部的调度器都必须对进程运行时间作记帐。多数Unix系统,分配一个时间片给每个进程。那么当每次系统时钟节拍发生时,时间片都会被减小一个节拍周期。
1. 调度器实体结构
调度器实体结构做为一个名为se的成员变量,嵌入在进程描述符struct task_ struct内。
2. 虚拟实时
1. CFS算法核心:选择具备最小vrntime的任务
2. 具体作法:利用红黑树rbtree(以节点形式存储数据的二叉树)
3. 举例:
1. 等待队列:休眠经过等待队列进行处理,等待队列是由某些事件发生的进程组成的简单链表。
2. 唤醒
唤醒操做由函数wake_ up()进行:
上下文切换,就是从一个可执行进程切换到另外一个可执行进程,由定义在kernel/schedule.c中的context_ switch()函数负责处理。完成了两项工做:
在内核返回用户空间的时候,它知道本身是安全的,由于既然它能够继续去执行当前进程,那么它固然能够再去选择一个新的进程去执行。因此,内核不管是在中断处理程序仍是在系统调用后返回,都会检查need_resched标志,若是它被设置了,那么,内核会选择一个其余(更合适的进程投入运行。从中断处理程序或系统调用返回的返回路径都是跟体系结构相关的,在entry.S(此文件不只包含内核入口部分的程序,内核退出部分的相关代码也在其中)文件中经过汇编语言来实现。简而言之,用户抢占在如下状况时产生:
与其余大部分的Unⅸ变体和其余大部分的操做系统不一样,Linux完整地支持内核抢占,在不支持内核抢占的内核中,内核代码能够一直执行,到它完成为止,也就是说,调度程序没有办法在一个内核级的任务正在执行的时候从新调度——内核中的各任务是以协做方式调度的不具有抢占性。内核代码一直要执行到完成(返回用户空间)或明显的阻塞为止,在2.6版的内核中,内核引入了抢占能力;如今,只要从新调度是安全的,内核就能够在任什么时候间抢占正在执行的任务。
Linux的实时调度算法提供了―种软实时工做方式,软实时的含义是,内核调度进程,尽力使进程在它的限定时间到来前运行,但内核不保证总能知足这些进程的要求。相反,硬实时系统保证在必定条件下,能够知足任何调度的要求。Linux对于实时任务的调度不作任何保证。虽然不能保证硬实时工做方式,但Linux的实时调度算法的性能仍是很不错的。2.6版的内核能够知足严格的时间要求。
Linux调度程序提供强制的处理器绑定机制。虽然它尽力经过一种软的(或者说天然的)亲和性试图使进程尽可能在同一个处理器上运行,但它也容许用户强制指定“这个进程不管如何都必须在这些处理器上运行”。这种强制的亲和性保存在进程的一个位掩码标志中。该掩码标志的每一位对应一个系统可用的处理器,默认状况下全部的位都被设置。
Linux经过sched_ yield()系统调用,提供了一种让进程显式地将处理器时间让给其余等待执行进程的机制,它是经过将进程从活动队列中(由于进程正在执行,因此它确定位于此队列当中)移到过时队列中实现的,由此产生的效果不只抢占了该进程并将其放入优先级队列的最后面,还将其放入过时队列中—这样能确保在一段时间内它都不会再被执行了,因为实时进程不会过时,因此属于例外,它们只被移动到其优先级队列的最后面(不会放到过时队列中)。
在Linux以的早期版本中,进程只会被放置到优先级队列的末尾,放弃的时间每每不会太长,如今,应用程序甚至内核代码在调用sched_ yield()前,应该仔细考虑是否真的但愿放弃处理器时间。内核代码为了方便,能够直接调用sched_ yield(),先要肯定给定进程确实处于可执行状态,而后再调用sched_ yield(),用户空间的应用程序直接使用sched_ yield()系统调用就能够。
经过对本章进程调度的学习,我了解到进程调度程序是内核重要的组成部分,由于运行着的进程首先在使用计算机。可是知足进程调度的各类须要是较难实现的。例如公平调度中,越小的调度周期就会表现出越好的交互性,也更接近于“同时完成多任务”这一目标。然而系统必须承受更高的切换代价和更差的系统吞吐量,即鱼与熊掌不可兼得。不过,Linux内核的新CFS调度程序尽可能知足了各个方面的需求,并以较完善的可伸缩性和新颖的方法提供了最佳的解决方案。