Linux 2.4的调度算法
需要遍历可运行队列,算法O(n)
Epoch,基本时间片,动态优先级
Linux 2.6.17的调度算法(2.6.23之前)
采用双队列(Active;expire ),按照优先级组队,O(1)
Linux 2.6.26的调度算法
非实时:CFS,vruntime,红黑树
实时:优先级队列
进程类型:
Linux作为一个通用操作系统,调度器将进程分为三类:
交互式进程:
此类进程有大量的人机交互,不断地处于睡眠状态,等待用户输入。此类进程对系统响应时间要求比较高,否则用户会感觉系统反应迟缓。典型的应用比如编辑器 vi。
批处理进程:
此类进程不需要人机交互,在后台运行大量的运算。此类进程需要占用大量的系统资源,但是能够忍受响应延迟。比如编译器。
实时进程:
实时对调度延迟的要求最高,这些进程往往执行非常重要的操作,要求立即响应并执行。这类程序不能容忍长时间的调度延迟。比如视频播放软件或飞机飞行控制系统。
Linux2.4的调度器
Pick-Next算法:对Runqueue中所有进程的优先级进行依次进行比较,选择最高优先级的进程作为下一个被调度的进程。(Runqueue是 Linux内核中保存所有就绪进程的队列)。
普通进程时间片计算:每个进程被创建时都被赋予一个时间片。时钟中断递减当前运行进程的时间片,当进程的时间片被用完时,它必须等待重新赋予时间片才能有机会运行。Linux2.4调度器保证只有当所有RUNNING进程的时间片都被用完之后,才对所有进程重新分配时间片。这段时间被称为一个epoch。这种设计保证了每个进程都有机会得到执行。
普通进程优先级计算:普通进程的优先级主要由进程描述符中的Counter字段决定 (还要加上nice设定的静态优先级)。当所有RUNNING进程的时间片被用完之后,调度器将重新计算所有普通进程的Counter值(不仅包括 RUNNING进程,也包括睡眠进程)。处于睡眠状态的进程的Counter本来就没有用完,在重新计算时,他们的Counter值会加上这些原来未用完的部分,从而提高了它们的优先级。
实时进程:实时进程的优先级是静态设定的,而且始终大于普通进程的优先级。因此只有当Runqueue中没有实时进程的情况下,普通进程才能够获得调度。实时进程的时间片被用完时,重新分配时间片,并重新插入Runqueue中。
实时进程调度策略:SCHED_FIFO和SCHED_RR。FIFO采用先进先出的策略,对于所有相同优先级的进程,最先进入Runqueue的进程总能优先获得调度;Round Robin采用更加公平的轮转策略,使得相同优先级的实时进程能够轮流获得调度。
主要问题:
需要遍历可运行队列,算法O(n)
Epoch,基本时间片,动态优先级
Linux 2.6.17的调度算法(2.6.23之前)
采用双队列(Active;expire ),按照优先级组队,O(1)
Linux 2.6.26的调度算法
非实时:CFS,vruntime,红黑树
实时:优先级队列
进程类型:
Linux作为一个通用操作系统,调度器将进程分为三类:
交互式进程:
此类进程有大量的人机交互,不断地处于睡眠状态,等待用户输入。此类进程对系统响应时间要求比较高,否则用户会感觉系统反应迟缓。典型的应用比如编辑器 vi。
批处理进程:
此类进程不需要人机交互,在后台运行大量的运算。此类进程需要占用大量的系统资源,但是能够忍受响应延迟。比如编译器。
实时进程:
实时对调度延迟的要求最高,这些进程往往执行非常重要的操作,要求立即响应并执行。这类程序不能容忍长时间的调度延迟。比如视频播放软件或飞机飞行控制系统。
Linux2.4的调度器
Pick-Next算法:对Runqueue中所有进程的优先级进行依次进行比较,选择最高优先级的进程作为下一个被调度的进程。(Runqueue是 Linux内核中保存所有就绪进程的队列)。
普通进程时间片计算:每个进程被创建时都被赋予一个时间片。时钟中断递减当前运行进程的时间片,当进程的时间片被用完时,它必须等待重新赋予时间片才能有机会运行。Linux2.4调度器保证只有当所有RUNNING进程的时间片都被用完之后,才对所有进程重新分配时间片。这段时间被称为一个epoch。这种设计保证了每个进程都有机会得到执行。
普通进程优先级计算:普通进程的优先级主要由进程描述符中的Counter字段决定 (还要加上nice设定的静态优先级)。当所有RUNNING进程的时间片被用完之后,调度器将重新计算所有普通进程的Counter值(不仅包括 RUNNING进程,也包括睡眠进程)。处于睡眠状态的进程的Counter本来就没有用完,在重新计算时,他们的Counter值会加上这些原来未用完的部分,从而提高了它们的优先级。
实时进程:实时进程的优先级是静态设定的,而且始终大于普通进程的优先级。因此只有当Runqueue中没有实时进程的情况下,普通进程才能够获得调度。实时进程的时间片被用完时,重新分配时间片,并重新插入Runqueue中。
实时进程调度策略:SCHED_FIFO和SCHED_RR。FIFO采用先进先出的策略,对于所有相同优先级的进程,最先进入Runqueue的进程总能优先获得调度;Round Robin采用更加公平的轮转策略,使得相同优先级的实时进程能够轮流获得调度。
主要问题:
O(n)问题:调度器选择进程时需要遍历整个Runqueue,因此该算法的执行时间与进程数成正比。另外每次重新计算Counter所花费的时间也会随着系统中进程数的增加而线性增长。
SMP调度问题:调度器对所有的处理器都使用一个Runqueue,调度器的负载均衡策略导致进程在内核间频繁切换。同时调度器还使用了一个 Runqueue Lock,因此选择一个任务执行就会阻碍其他处理器操作这个运行队列。
实时性问题:不支持抢占。
Linux2.6的O(1)调度器
2.6版本的O(1)调度器是由 Ingo Molnar 设计并实现的,这个调度器是动态抢占的,支持负载均衡,并以恒定的速度进行 O(1),它解决了2.6 之前调度器中三个主要问题(O(n)、SMP调度、实时性)。
Pick-Next算法:调度器为每一个CPU维护了两个进程队列数组:active数组和expire数组。数组中的元素保存某一优先级的进程队列指针。系统一共有140个不同的优先级,因此这两个数组大小都是140(1-99为实时任务)。当需要选择当前最高优先级的进程时,2.6调度器不用遍历整个Runqueue,而是直接从active数组中选择当前最高优先级队列中的第一个进程。
普通进程时间片计算:每次时钟tick中断中,进程的时间片(time_slice)被减一。当time_slice为0时,调度器判断当前进程的类型(交互式进程满足 Dynamic priority ≤ 3 x static priority /4 + 28),如果是交互式进程或者实时进程,则重置其时间片并重新插入active数组。如果不是交互式进程则从active数组中移到expired数组。这样实时进程和交互式进程就总能优先获得CPU。然而这些进程不能始终留在active数组中,否则进入expire数组的进程就会产生饥饿现象。当进程已经占用CPU时间超过一个固定值后,即使它是实时进程或者交互式进程也会被移到expire数组中。
普通进程优先级计算:dynamic priority = max(100, min(static priority – bonus + 5, 139))。其中bonus取决于进程的平均睡眠时间。平均睡眠时间越长,其bonus越大,从而得到更高的优先级(提高了交互式进程的优先级)。
实时进程:实时进程的优先级由sys_sched_setschedule()设置。该值不会动态修改,而且总是比普通进程的优先级高。实时进程的时间片被用完时,重新分配时间片,并重新插入Active队列尾部。
Linux2.6 的RSDL调度器(The Rotating Staircase Deadline Schedule)
由于O(1)调度器的复杂度问题(复杂性来自动态优先级的计算,调度器根据平均睡眠时间和一些很难理解的经验公式来修正进程的优先级),Con Kolivas开发并实现了RSDL调度器。它抛弃了动态优先级的概念,而采用了一种完全公平的思路。
楼梯调度算法(Staircase Scheduler):当普通进程用完了自己的时间片后,被加入active数组的低一优先级列表中,即将其降低一个级别(任务本身的优先级不变)。当时间片再次用完,任务被再次放入更低一级优先级任务队列中。总结为,如果进程本身优先级为P,当它从第N级台阶开始下楼梯并到达底部后,将回到第N+1级台阶,并且赋予该任务N+1倍的时间片。
Minor Rotation:当进程用完了自身的Tp时,就下降到下一优先级进程组中,这个过程和楼梯调度算法相同。
Major Rotation:当active数组为空,或者所有的进程都降低到最低优先级时就会触发major rotation:。Major rotation交换active数组和expire数组,所有进程都恢复到初始状态,再一次从新开始minor rotation的过程。
Deadline:当高优先级进程组用完了它们的Tg(即组时间配额)时,无论该组中是否还有进程Tp尚未用完,所有属于该组的进程都被强制降低到下一优先级进程组中。这样低优先级任务就可以在一个可以预计的未来得到调度。从而改善了调度的公平性。
Linux2.6的CFS完全公平调度器
Ingo Molnar从RSDL/SD中吸取了完全公平的思想,在Linux Kernel2.6.23引入CFS调度器。按照作者的说法:“CFS百分之八十的工作可以用一句话概括:CFS在真实的硬件上模拟了完全理想的多任务处理器”。
在“完全理想的多任务处理器”下,每个进程都能同时获得CPU的执行时间。当系统中有两个进程时,CPU的计算时间被分成两份,每个进程获得50%。然而在实际的硬件上,当一个进程占用CPU时,其它进程就必须等待。这就产生了不公平。所以CFS将惩罚当前进程,使其它进程能够在下次调度时尽可能取代当前进程。
Pick-Next算法:CFS使用红黑树选取下一个被调度进程。所有状态为RUNABLE的进程都被插入红黑树。在每个调度点,CFS调度器都会选择红黑树的最左边的叶子节点作为下一个将获得cpu的进程(该读取操作的时间复杂度是O(LgN))。如果在调度时当前进程不再是红黑树的最左边的叶子节点,就会被抢占。
普通进程时间片计算:抛弃时间片概念。
红黑树键值计算:键值由三个因子计算而得:一是进程已经占用的CPU时间;二是当前进程的nice值;三是当前的CPU负载。键值等于进程应获得的CPU 时间减去进程的等待时间。它们的差值代表了一个进程的公平程度。该值越大代表当前进程相对于其它进程越不公平。
实时进程:CFS提供了调度器模块管理器。各种不同的调度器算法都可以作为一个模块注册到该管理器中。不同的进程可以选择使用不同的调度器模块。 2.6.23中,CFS实现了两个调度算法,CFS算法模块和实时调度模块。对应实时进程将使用实时调度模块。对应普通进程则使用CFS算法。
[Reference]
Linux 调度器发展简述:http://www.linuxsky.org/doc/dev/200712/200.html
http://www.ibm.com/developerworks/cn/linux/l-scheduler/
http://win.chinaunix.net/bbs/thread-7024-1-9.html
PS: VwWorks的调度算法
Wind内核缺省调度机制为基于优先级的抢占式调度。采用这种机制时,系统把处理机分配给优先级最高的进程,使之执行。一旦出现了另一个优先级更高的进程时,进程调度程序剥夺当前任务的执行,将处理机分配给高优先级任务。而在相同优先级的多个任务之间,采用时间片轮转调度机制。采用这种机制时,当一个任务到达时,它被排在轮转队列的后面,等待分配给自己的时间片的到来,如果在时间片内没有结束,则再等待属于自己的时间片的到来,直到任务完成。
为了任务控制的灵活性,wind内核还提供了动态优先级机制,任务的优先级在运行期间可动态地变化。同时,为了防止优先级反转,还具有优先级继承机制。
SMP调度问题:调度器对所有的处理器都使用一个Runqueue,调度器的负载均衡策略导致进程在内核间频繁切换。同时调度器还使用了一个 Runqueue Lock,因此选择一个任务执行就会阻碍其他处理器操作这个运行队列。
实时性问题:不支持抢占。
Linux2.6的O(1)调度器
2.6版本的O(1)调度器是由 Ingo Molnar 设计并实现的,这个调度器是动态抢占的,支持负载均衡,并以恒定的速度进行 O(1),它解决了2.6 之前调度器中三个主要问题(O(n)、SMP调度、实时性)。
Pick-Next算法:调度器为每一个CPU维护了两个进程队列数组:active数组和expire数组。数组中的元素保存某一优先级的进程队列指针。系统一共有140个不同的优先级,因此这两个数组大小都是140(1-99为实时任务)。当需要选择当前最高优先级的进程时,2.6调度器不用遍历整个Runqueue,而是直接从active数组中选择当前最高优先级队列中的第一个进程。
普通进程时间片计算:每次时钟tick中断中,进程的时间片(time_slice)被减一。当time_slice为0时,调度器判断当前进程的类型(交互式进程满足 Dynamic priority ≤ 3 x static priority /4 + 28),如果是交互式进程或者实时进程,则重置其时间片并重新插入active数组。如果不是交互式进程则从active数组中移到expired数组。这样实时进程和交互式进程就总能优先获得CPU。然而这些进程不能始终留在active数组中,否则进入expire数组的进程就会产生饥饿现象。当进程已经占用CPU时间超过一个固定值后,即使它是实时进程或者交互式进程也会被移到expire数组中。
普通进程优先级计算:dynamic priority = max(100, min(static priority – bonus + 5, 139))。其中bonus取决于进程的平均睡眠时间。平均睡眠时间越长,其bonus越大,从而得到更高的优先级(提高了交互式进程的优先级)。
实时进程:实时进程的优先级由sys_sched_setschedule()设置。该值不会动态修改,而且总是比普通进程的优先级高。实时进程的时间片被用完时,重新分配时间片,并重新插入Active队列尾部。
Linux2.6 的RSDL调度器(The Rotating Staircase Deadline Schedule)
由于O(1)调度器的复杂度问题(复杂性来自动态优先级的计算,调度器根据平均睡眠时间和一些很难理解的经验公式来修正进程的优先级),Con Kolivas开发并实现了RSDL调度器。它抛弃了动态优先级的概念,而采用了一种完全公平的思路。
楼梯调度算法(Staircase Scheduler):当普通进程用完了自己的时间片后,被加入active数组的低一优先级列表中,即将其降低一个级别(任务本身的优先级不变)。当时间片再次用完,任务被再次放入更低一级优先级任务队列中。总结为,如果进程本身优先级为P,当它从第N级台阶开始下楼梯并到达底部后,将回到第N+1级台阶,并且赋予该任务N+1倍的时间片。
Minor Rotation:当进程用完了自身的Tp时,就下降到下一优先级进程组中,这个过程和楼梯调度算法相同。
Major Rotation:当active数组为空,或者所有的进程都降低到最低优先级时就会触发major rotation:。Major rotation交换active数组和expire数组,所有进程都恢复到初始状态,再一次从新开始minor rotation的过程。
Deadline:当高优先级进程组用完了它们的Tg(即组时间配额)时,无论该组中是否还有进程Tp尚未用完,所有属于该组的进程都被强制降低到下一优先级进程组中。这样低优先级任务就可以在一个可以预计的未来得到调度。从而改善了调度的公平性。
Linux2.6的CFS完全公平调度器
Ingo Molnar从RSDL/SD中吸取了完全公平的思想,在Linux Kernel2.6.23引入CFS调度器。按照作者的说法:“CFS百分之八十的工作可以用一句话概括:CFS在真实的硬件上模拟了完全理想的多任务处理器”。
在“完全理想的多任务处理器”下,每个进程都能同时获得CPU的执行时间。当系统中有两个进程时,CPU的计算时间被分成两份,每个进程获得50%。然而在实际的硬件上,当一个进程占用CPU时,其它进程就必须等待。这就产生了不公平。所以CFS将惩罚当前进程,使其它进程能够在下次调度时尽可能取代当前进程。
Pick-Next算法:CFS使用红黑树选取下一个被调度进程。所有状态为RUNABLE的进程都被插入红黑树。在每个调度点,CFS调度器都会选择红黑树的最左边的叶子节点作为下一个将获得cpu的进程(该读取操作的时间复杂度是O(LgN))。如果在调度时当前进程不再是红黑树的最左边的叶子节点,就会被抢占。
普通进程时间片计算:抛弃时间片概念。
红黑树键值计算:键值由三个因子计算而得:一是进程已经占用的CPU时间;二是当前进程的nice值;三是当前的CPU负载。键值等于进程应获得的CPU 时间减去进程的等待时间。它们的差值代表了一个进程的公平程度。该值越大代表当前进程相对于其它进程越不公平。
实时进程:CFS提供了调度器模块管理器。各种不同的调度器算法都可以作为一个模块注册到该管理器中。不同的进程可以选择使用不同的调度器模块。 2.6.23中,CFS实现了两个调度算法,CFS算法模块和实时调度模块。对应实时进程将使用实时调度模块。对应普通进程则使用CFS算法。
[Reference]
Linux 调度器发展简述:http://www.linuxsky.org/doc/dev/200712/200.html
http://www.ibm.com/developerworks/cn/linux/l-scheduler/
http://win.chinaunix.net/bbs/thread-7024-1-9.html
PS: VwWorks的调度算法
Wind内核缺省调度机制为基于优先级的抢占式调度。采用这种机制时,系统把处理机分配给优先级最高的进程,使之执行。一旦出现了另一个优先级更高的进程时,进程调度程序剥夺当前任务的执行,将处理机分配给高优先级任务。而在相同优先级的多个任务之间,采用时间片轮转调度机制。采用这种机制时,当一个任务到达时,它被排在轮转队列的后面,等待分配给自己的时间片的到来,如果在时间片内没有结束,则再等待属于自己的时间片的到来,直到任务完成。
为了任务控制的灵活性,wind内核还提供了动态优先级机制,任务的优先级在运行期间可动态地变化。同时,为了防止优先级反转,还具有优先级继承机制。