OS之进程管理---进程调度和多线程调度

进程调度基本概念

多道程序的目标就是始终容许某个进程运行以最大化CPU利用率,多个进程通时存在于内存中,操做系统经过进程调度程序按特定的调度算法来调度就绪队列中的进程到CPU,从而最大限度的利用CPU。算法

须要进行CPU调度的状况能够分为四种:函数

  • 当一个进程从运行状态切换到等待状态时(如I/O请求,wait()调用以便等待一个子进程的结束)
  • 当一个进程从运行状态切换到就绪状态时(如出现了中断)
  • 当一个进程从等待状态切换到就绪状态时(如I/O完成)
  • 当一个进程终止时

若是调度只能发生在第一种和第四种状况下,那么调度方案称为非抢占的或协做的;不然调度方案就是抢占的。性能

调度准则:this

  • CPU使用率
  • 吞吐量
  • 周转时间
  • 响应时间
  • 等待时间

进程调度算法

先到先服务调度(FCFS)

先请求CPU的进程首先分配到CPU。FCFS策略能够经过FIFO队列容易的实现,当一个进程进入就绪队列时它的PCB被连接到队列尾部。当CPU空闲时,它会分配给位于就绪队列头部的进程,并将这个进程从队列中移除。
缺点:平均等待时间很长spa

进程 执行时间
P1P_1P1 24
P2P_2P2 3
P3P_3P3 3

若是上面三个进程顺序到达,且按FCFS顺序处理,那么平均等待时间为(0+24+27)/3=17(ms)
若是是:操作系统

进程 执行时间
P2P_2P2 3
P3P_3P3 3
P1P_1P1 24

那么平均等待时间就是(6+0+3)/3=3(ms),因此进程的CPU执行时间变化波动很大的话,那么采用这种调度策略的平均等待时间变化也会很大。
另外,考虑动态状况下的FCFS调度性能,假设一个CPU密集型进程和多个I/O密集型进程随着进程在系统中的运行,可能发生:CPU密集进程获得CPU,并使用。在这段时间中,全部其余进程会处理他们的I/O,并转移到就绪队列来进行等待CPU。当这些进程在等待时,I/O设备一直处于空闲状态。当CPU密集型进程完成CPU执行并移到I/O设备,全部I/O密集型进程,因为只有很短的cpu执行,因此很快执行完并一会到I/O队列。这时CPU处于空闲。这种因为其余进程都等待一个大进程释放CPU的现象叫作护航效果线程

注意,FCFS调度算法是非抢占式的,一旦CPU分配给了一个进程,该进程就会使用CPU直到释放为止,因此FCFS不适用于分时系统。设计

最短做业优先调度(SJF)

该算法将每一个进程与下次CPU执行的长度关联起来。当CPU空闲时,它会被赋给具备最短CPU执行的进程。若是两个进程具备一样长度的CPU执行,能够有FCFS来处理。
能够证实SJF调度算法是最优的。由于对于给定的一组进程,SJF算法的平均等待时间最小。经过将短进程移到长进程以前,短进程的等待时间减小大于长进程的等待时间增长,因此平均等待时间减小。
可是如何才能知道下次CPU执行的长度?对于批处理系统的长期调度,可将用户提交做业时指定的进程时限做为长度。SJF调度经常使用于长期调度。
虽然SJF是最优的,可是它不能在短时间CPU调度级别上加以实现,由于没有办法知道下次CPU执行的长度。一种方法是试图近似SJF调度,经过计算下一个CPU执行长度的近似值,能够选择具备预测最短CPU执行的进程来运行。
下次CPU执行一般预测为之前CPU执行的测量长度的指数平均。设tnt_ntn为第n个CPU执行长度,设τn+1\tau_{n+1}τn+1为下次CPU执行预测值。对于β\betaβ,0 ≤\leq β\betaβ ≤\leq 1,定义:指针

τn+1\tau_{n+1}τn+1 = β\betaβ tnt_ntn + (1 - β\betaβ)τn\tau_{n}τncode

tnt_ntn包括最近信息,而τn\tau_{n}τn存储了过去历史,参数β\betaβ控制最近和过去历史在预测中的权重。若是β\betaβ=0,那么 τn+1\tau_{n+1}τn+1τn\tau_{n}τn,最近历史没有影响(当前情形是瞬态);若是β\betaβ=1,那么τn+1\tau_{n+1}τn+1=tnt_ntn ,只有最近CPU执行才重要(过去历史被认为是陈旧的,无关的)。通常状况下,定义β\betaβ=12\frac {1}{2}21
为了理解指数平均行为,经过替换τn\tau_{n}τn,能够展开τn+1\tau_{n+1}τn+1,从而获得:

τn+1\tau_{n+1}τn+1=β\betaβtnt_ntn + (1 - β\betaβ)β\betaβtn−1t_{n-1}tn1 + … + (1−β)j(1 -β)^j(1β)jβtn−jt_{n - j}tnj + …+ (1−β)n+1(1 -β)^{n + 1}(1β)n+1τ0\tau_{0}τ0

一般β\betaβ和(1 - β\betaβ)小于1,因此后面项的权重值比前面项的权重要小。

SJF算法能够是抢占的也能够是非抢占的。新进程的下次CPU执行,与当前运行进程的还没有完成的CPU执行相比,可能还要小,抢占SJF算法会抢占当前运行进程,而非抢占SJF算法会容许当前运行进程以先完成CPU执行。抢占SJF调度有时称为最短剩余时间优先调度
例子

进程 到达时间 执行时间
P1P_1P1 0 8
P2P_2P2 1 4
P3P_3P3 2 9
P4P_4P4 3 5

按照SJF调度,这个例子的平均等待时间是[(10 - 1) + (1 - 1) + (17 - 2) + (5 - 3)]/4=26/4=6.5ms。若是使用非抢占SJF调度算法,平均等待时间为7.75ms。

优先级调度

SJF算法就是经过优先级调度算法的一个特例,每一个进程都有一个优先级与之关联,而具备最高优先级的进程会被分配到CPU,具备相同优先级的进程按照FCFS顺序调度。
优先级的定义能够是分为内部的或外部的。内部定义的优先级采用一些测量数据来计算进程优先级。外部定义的优先级采用操做系统以外的准则,如进程重要性等。
优先级调用能够是抢占式的也能够是非抢占式的。对于抢占式的那么较高优先级进程来时会抢占CPU;对于非抢占式的,只是将新进程加到就绪队列的头部。

优先级调度算法的一个主要问题就是无穷堵塞饥饿。即让某个低优先级进程无穷等待CPU。常见的解决方式是老化,老化逐渐增长在系统中等待很长时间的进程的优先级。

轮转调度(RR)

轮转调度算法是专门为分时系统设计德尔,相似与FCFS调度,并增长了抢占以切换进程。将一个较小的时间单元定义为时间量或时间片。时间片的大小一般为10ms-100ms。就绪队列做为一个循环队列,CPU调度程序循环整个就绪队列,为每一个进程分配不超过一个时间片的CPU。
在RR调度算法中,没有进程会被连续分配超过一个时间片的CPU(除非是惟一可运行的进程),若是进程的CPU执行超过了一个时间片,那么该进程会被抢占,并被放回就绪队列中,因此RR调度算法是抢占的。
RR调度算法的性能很大程度上取决于时间片的大小,若是时间片很是大,那么RR算法与FCFS算法同样,若是时间片很小,那么会形成大量的上下文切换。若是上下文切换时间约为时间片的10%,那么越10%的CPU时间会被浪费在上下文切换上,上下文切换的时间通常少于10ms。

多级队列调度

多级队列调度算法就是将就绪队列分红多个单独队列,根据进程属性(如内存大小,进程优先级等)将每一个进程永久分到一个队列中,每一个队列有本身的调度算法。此外,队列之间应该页存在调度,一般采用固定优先级抢占调度。

多级反馈队列调度

在多级队列调度算法中,进程被永久的分配给一个队列,这种设置的优势是调度开销小,缺点是不够灵活。
多级反馈队列调度算法容许进程在队列之间迁移。这种想法是根据不一样CPU执行的特色来区分进程,若是进程使用过多的CPU时间,那么将会被迁移到更低优先级的队列。这种方案将I/O密集型和交互进程放在更高优先级队列上。此外在较低优先级队列中等待过长的进程会被一到更高优先级队列中,这种形式的老化阻止饥饿的发生。
一般状况下,多级反馈调度程序可由下列参数来进行定义:
队列的数量
每一个队列的调度算法
用以肯定什么时候升级到更高优先级队列的方法
用以肯定什么时候降级到更低优先级队列的方法
用以肯定进程在须要服务时将会进入哪一个队列中的方法

线程调度

在支持线程的操做系统上,内核级线程才是操做系统所调度的。用户级线程是由线程库来进行管理的,而内核并不知道他们。用户级线程为了运行在CPU上最终应映射到相关的内核级线程上,这种映射不是直接的,可能采用轻量级进程(LWP)
用户级线程和内核级线程的一个区别就是他们如何调度的。
对于多对一和多对多模型的系统线程库会调用用户级线程,以便在可用的LWP上运行。这种方案成为进程竞争范围(PCS),由于竞争CPU是发生在同一进程的线程之间。
为了决定哪一个内核级线程调度到一个处理器上,内核采用系统竞争范围(SCS)。采用SCS调度来竞争CPU,发生在系统内全部线程之间。采用一对一模型的系统,如Window、Linux、Solaris,只采用SCS调度。
一般状况下,PCS一般采用优先级调用,即调度程序选择具备最高优先级的,可运行的线程,且容许一个更高优先级的线程来抢占当前运行的线程。

Pthreads调度

在经过POSIX Pthreads来建立线程时容许指定PCS或SCS。Pthreads采用以下竞争范围的值:

PTHREAD_SCOPE_PROCESS :按PCS来调度线程
    PTHREAD_SCOPE_SYSTEM:按SCS来调度线程

对于实现多对多模型的系统,PTHREAD_SCOPE_PROCESS策略调度用户级线程可用LWP,LWP的数量经过线程库来维护。PTHREAD_SCOPE_SYSTEM调度策略会建立一个LWP,并将多对多模型系统的每一个用户级线程绑定到LWP,实际采用一对一策略来映射线程。
Pthreads IPC提供两个函数,用来获取和设置竞争范围策略:

pthread_attr_setscope(pthread_attr_t *attr, int scope)
    pthread_attr_getscope(pthread_attr_t *attr, int *scope)

这两个函数第一个参数是包含线程属性值的指针,pthread_attr_setscope()第二个参数的值是PTHREAD_SCOPE_PROCESS或PTHREAD_SCOPE_SYSTEM
,pthread_attr_getscope()第二个参数的值是int值的指针,用于获取竞争范围的当前值。
示例:

#include <stdio.h>
#include <pthread.h>
#define NUM_THREADS 5


void *runner(void *param);

int main(int argc, char *argv[])
{
    int i, scope;
    pthread_t tid[NUM_THREADS];
    pthread_attr_t attr;

    pthread_attr_init(&attr);

    if(pthread_attr_getscope(&attr, &scope) != 0) {
        fprintf(stderr, "Unable to get scheduling scope\n");

    }
    else {
        if(scope == PTHREAD_SCOPE_PROCESS) {
            printf("PTHREAD_SCOPE_PROCESS\n");
        }
        else if(scope == PTHREAD_SCOPE_SYSTEM) {
            printf("PTHREAD_SCOPE_SYSTEM\n");
        }
        else {
            fprintf(stderr, "Illegal scope value.\n");
        }

    }

    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

    for(i = 0; i < NUM_THREADS; i++) {
        pthread_create(&tid[i], &attr, runner, NULL);
    }

    for(i = 0; i < NUM_THREADS; i++) {
        pthread_join(tid[i], NULL);
    }

    return 0;
}

void *runner(void *param)
{
    int i;
    for(i = 0; i<500000; i++) {};

    printf("this is thread\n");
    pthread_exit(0);

}
相关文章
相关标签/搜索