《Linux内核设计与实现》——进程调度

来源:互联网 发布:矩阵特征空间 编辑:程序博客网 时间:2024/05/09 15:36

一、多任务

 1、进程调度负责决定将那个进程投入运行,何时运行以及运行多长时间。


 2、最大限度地利用处理器的原则是,只要有可以执行的进程,那么就总会有进程正在执行。但是只要系统中可运行的进程的数目比处理器的个数多,就注定某一给定时刻会有一

       些进程进程不能执行。


 3、多任务操作系统就是能同时并发的交互执行多个进程的操作系统。


 4、多任务系统可以划分为两类:非抢占式多任务抢占式多任务

 

 5、在抢占式多任务模式下,由调度来决定什么时候停止一个进程的运行,以便其他进程能够得到执行的机会,这个强制的挂起动作就叫做抢占


 6、进程被抢占之前能够运行能够运行的时间是预先设置好的,而且有一个专门的名字,叫进程的时间片。时间片实际上就是分配给每个可运行进程的处理器时间段。


 7、在抢占多任务模式下,除非自己主动停止运行,否则它会一直执行。进程主动挂起自己的操作称为让步



二、Linux的进程调度

 1、O(1)调度程序。


 2、完全公平调度算法(CFS)



三、策略

 1、策略决定调度程序在何时让什么程序运行。调度器的策略往往就决定系统的整体印象,并且,还要负责优化使用处理器时间。


 2、I/O消耗型和处理器消耗型的进程

   1)、进程可以被分为I/O消耗型处理器消耗型

   2)、I/O消耗型指进程的大部分时间用来提交I/O请求或是等待I/O请求。

   3)、处理器消耗型进程把时间大多用在执行代码上。除非被抢占,否则它会不停地运行。从系统响应速度考虑,调度器不应该经常让它们运行。对于处理器消耗型的进程,

             调度策略往往是尽量降低它们的调度频率,而延长其运行时间。

   4)、I/O消耗型和处理器消耗型划分并不是绝对的。有的进程既是I/O消耗型,也是处理器消耗型。还有些进程可以是I/O消耗型,但属于处理器消耗型活动范围。

   5)、调度策略通要在两个矛盾的目标中间寻找平衡:进程响应迅速(响应时间短)和最大系统利用率(高吞吐量)。

   6)、Llinux为了保证交互式应用和桌面系统的性能,所以对进程的影响做了优化,更趋向于优先调度I/O消耗型进程。


 3、进程优先级

   1)、对进程分级的通常做法是(其并未被Linux系统完全采用)优先级高的进程先运行,低的后运行,相同优先级的进程按轮转方式进行调度。

   2)、Linux采用两种不同的优先级范围:

        I、第一种是nice值,它的范围是从-20到+19,默认值为0;越大的nice意味着更低的优先级。相比搞nice值得进程,低nice值得进程可以获得更多的处理器时间。

        II、第二种是实时优先级,其值是可配置的,默认情况下它的变化范围是0-99。与nice相反,越高的实时优先级数代表进程优先级越高。

        III、任何实时优先级进程的优先级都高于普通的进程,也就是说实时优先级和nice优先级处于互不相交的两个范畴。


 4、时间片

   1)、时间片是一个数值,它表明进程在被抢占前所能持续运行的时间。

   2)、Linux的CFS调度器并没有直接分配时间片到进程,它是将处理器的使用比划分给了进程。Linux中的1抢占时机取决于新的可运行程序消耗了多少处理器使用比。如果消

             耗的使用比比当前进程小,则新进程立刻投入运行,抢占当前进程。否则,将推迟其运行。


 5、调度策略的活动

   1)、Linux操作系统对多进程的处理方法:不再给定的时间片和优先级,而是分配一个给定的处理器使用比。



四、Linux调度算法

 1、Linux调度器是以模块方式提供的,这样做的目的是允许不同类型的进程可以有针对性地选择调度算法。这种模块1化的结构被称为调度器类,它允许多种不同的可动态添加

       的调度算法并存,调度属于自己范畴的进程。


 2、完全公平调度(CFS)是一个针对普通进程的调度类,在Linux中称为SCHED_NORMAL。


 3、Unix系统中的进程调度

   1)、传统Unix系统的调度过程问题

 

 4、公平调度

   1)、CFS多进程实现:允许每个进程运行一段时间、循环轮转、选择运行最少额进程作为下一个运行的进程,而不再采用分配给每个进程时间片的做法,CFS在所有可运行

              进 程总数基础上计算出一个进程应该运行多久,而不是依靠nice值来计算时间片。

   2)、nice值在CFS中被作为进程获得的处理器运行比权重:越高的nice值进程获得更低的处理器使用权重,这是相对默认nice值进程的进程而言的;相反,更低的nice值得进

             程获得更高的处理器使用权重。

   3)、每个进程都按其权重在全部可运行进程中所占比例的“时间片”来运行,为了计算准确的时间片,CFS为完美多任务中的无限小调度周期的近似值设立了一个目标。而这个

             目标称作“目标延迟”,越小的调度周期将带来越好的交互性,同时也更接近完美的多任务。但是必须承受更高的切换代价和更差的系统总吞吐能力。

   4)、CFS引入每个进程获得的时间片底线,这个底线称为最小粒度。默认情况下这个值是1ms。

   5)、任何进程所获得的处理器时间是由它自己和其他所有可运行进程nice值得相对值决定的。nice值ui时间片的作用不再是算数加权,而是集合加权。任何nice值对应的绝对

             时间不再是一个绝对值,而是处理器的使用比。



五、Linux调度的实现

 1、时间记账

   1)、调度器实体结构

       I、CFS使用调度器实体结构来追踪进程运行记账:

       struct  sched_entity {

       struct  loa_weight  load;

       struct  rb_node  run_node;

       struct  list_head  group_node;

       unsigned  int  on_rq;

       u64  exec_start;

       u64  sum_exec_runtime;

       u64  vruntime;

       u64  prev_sum_exec_yuntime;

       u64  last_wakeup;

       u64  avg_overlap;

       u64  nr_migrations;

       u64  start_runtime;

       u64  avg_wakeup;

       };

       II、调度器实体结构作为一个名为se的成员变量,嵌入在进程描述符struct  task_struct内。


 2、虚拟实时

   1)、vruntime变量存放进程的虚拟运行时间,该运行时间的计算是经过了所有可运行总数的标准化。虚拟时间是以ns为单位的,所以vruntime和定时器节拍不再相关。

   2)、CFS使用vruntime变量来记录一个程序到底运行了多长时间以及它还应该再运行多久。

   3)、定义在kernel/sched_fair.c文件中的update_curr()函数实现了该记账功能。

        I、update_curr()计算了当前进程的执行时间,并且将其存放在变量delta_exec中然后他又将运行时间传递给了_ _update_curr()。由后者在根据当前可运行进程总数对时间

             进行加权计算。最终将上述的权重值与当前运行进程的vruntime相加。

        II、update_curr()是由系统定时器周期性调用的,无论是在进程处于可运行状态,还是被堵塞处于不可运行状态。根据这种方式,vrumtime可以准确地测量给定进程的运行

              时间,而且可知道谁应该是下一个被运行的进程。


 3、进程选择

    I、当CFS需要选择下一个运行进程时,它会挑一个具有最小vrumtime的进程。这其实就是CFS算法的核心:选择具有最小vruntime的任务。

    II、CFS利用红黑树来组织可运行进程队列,并利用其迅速找到最小vruntime值得进程。

    III、在Linux中,红黑树被称为rbtree,它是一个自平衡二叉搜索树。

   1)、挑选下一个任务

       I、CFS的进程选择算法可简单总结为“运行rbtree树中最左边叶子点所代表的那个进程”。实现这一过程的函数是_ _pick_next_entity()。(P45)

   2)、向树中加入进程

       I、enqueue_entity()函数实现,该函数更新运行时间和其他一些统计数据。

       II、然后调用_ _enqueue_entity()进行繁重的插入操作,把数据真正插入到红黑树中。

       III、平和二叉树的基本规则是,如果键值小于当前节点的键值,则需转向蜀的左分支;相反如果大于当前节点的键值,则转向右分支。

   3)、从树中删除进程

       I、删除工作发生在进程堵塞获知终止时:dequeue_entity()函数。

       II、从树中删除进程:_ _dequeue_entity()。


 4、调度器入口

   1)、进程调度的主要入口点是函数schedule(),它正是内核其他部分用于调用进程调度器入口:选择哪个进程可以运行,合适将其投入运行。

   2)、schedule()通常都需要和一个具体的调度类相关联,它会找到一个最高优先级的调度类——后者需要有自己的可运行队列,然后问后者谁才是下一个该运行的进程。


 5、睡眠和唤醒

   1)、休眠(被堵塞)的进程处于一个特殊的不可执行状态。

        I、休眠有两种相关的进程状态:TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE。

        II、处于TASK_UNINTERRUPTIBLE的进程会忽略信号,而处于TASK_INTERRUPTIBLE状态的进程如果接收到一个信号。会被提前唤醒并响应该信号。

        III、两种状态的进程位于同一个等待队列,等待某些事件,不能够运行。

   2)、等待队列

        I、休眠通过等待队列进行处理。等待队列是由等待某些事件发生的进程组成的简单链表。

        II、内核用wake_queue_head_t来代表等待队列。

        III、等待队列可以通过DECLRE_WAITQUEUE()静态创建,也可以通过init_waitqueue_head()动态创建。

   3)、唤醒

        I、唤醒操作通过函数wake_up()进行,它会唤醒指定的等待队列上的所有进程。

        II、关于休眠有一点需要注意,存在虚假的唤醒。



六、抢占和上下文切换

 1、上下文切换,也就是从一个可执行进程切换到另一个可执行进程,由定义在kernel/sched.g中的context_switch()函数负责处理。


 2、内核提供了一个need_resched标志来表明是否需要重新执行一次调度(表4-1)。

   1)、该标志对于内核来讲是一个信息,它表示有其他进程应当被运行们要尽快调用调度程序。

   2)、再返回用户空间以及中断返回的时候,内核也会检查need_resched标志。如果已经设置,内核会在继续执行之前调用调度程序。


 3、用户抢占

   1)、内核即将返回用户空间的时候,如果need_resched标志被设置,会导致schedule()被调用,此时就会发生用户抢占。

   2)、用户抢占在以下情况时产生:

       I、从系统调返回用户空间时。

       II、从中断处理程序返回用户空间时。


 4、内核抢占

   1)、内核抢占会发生在:

       I、中断处理程序正在执行,且返回内核空间之前。

       II、内核代码再一次具有可抢占性的时候。

       III、如果内核中的人物显式地调用schedule()。

       IV、如果内核中的人物堵塞。



七、实时调度策略

 1、Linux提供了两种实时调度策略:SCHED_FIFO和SCHE_RR。而普通的、非实时的调度策略是SCHED_NORMAL。

   1)、SCHED_FIFO实现了一种简单的、先入先出的调度算法:它不使用时间片。处于可运行状态的SCHED_FIFO级的进程回避任何SCHED_NORMAL级的进程都先等到调

              度。

   2)、SCHE_RR与SCHED_FIFO大体相同,只是SCHE_RR级的进程在耗尽事先分配给它的时间后就不能在继续执行。SCHE_RR是带时间片的SCHED_FIFO——这是一种

              实时轮流调度算法。

 2、软实时的含义是,内核调度进程,尽量使进程在它的限定时间到来前运行,但内核不保证总能满足这些进程的要求。相反,硬实时系统保证在一定条件下,可以满足任何调

       度的要求。



八、与调度相关的系统调用(P55  表4-2)

 1、与调度策略和优先级相关的系统调用


 2、与处理器绑定有关的系统调用


 3、放弃处理器时间

0 0
原创粉丝点击