进程调度基本概念

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

需要进行CPU调度的情况可以分为四种:

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

如果调度只能发生在第一种和第四种情况下,那么调度方案称为非抢占的或协作的;否则调度方案就是抢占的。

调度准则:

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

进程调度算法

先到先服务调度(FCFS)

先请求CPU的进程首先分配到CPU。FCFS策略可以通过FIFO队列容易的实现,当一个进程进入就绪队列时它的PCB被链接到队列尾部。当CPU空闲时,它会分配给位于就绪队列头部的进程,并将这个进程从队列中移除。

缺点:平均等待时间很长

进程 执行时间
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}τn​

值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}tn−1​ + … + (1−β)j(1 -β)^j(1−β)jβtn−jt_{n - j}tn−j​ + …+ (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); }

OS之进程管理---进程调度和多线程调度的更多相关文章

  1. Python::OS 模块 -- 进程管理

    os模块的简介参看 Python::OS 模块 -- 简介 os模块的文件相关操作参看 Python::OS 模块 -- 文件和目录操作 os模块的进程参数 Python::OS 模块 -- 进程参数 ...

  2. OS之进程管理---多线程模型和线程库(POSIX PTread)

    多线程简介 线程是CPU使用的基本单元,包括线程ID,程序计数器.寄存器组.各自的堆栈等,在相同线程组中,所有线程共享进程代码段,数据段和其他系统资源. 传统的的单线程模式是每一个进程只能单个控制线程 ...

  3. OS之进程管理---多处理器调度

    引言 之前我们所学习的操作系统进程调度策略的前提条件是单处理器系统下的CPU调度程序.如果系统中存在多个CPU,那么负载分配就成为可能,但是相应的调度问题就会更加复杂. 多处理器调度方法 对于多处理器 ...

  4. OS之进程管理---实时CPU调度

    引言 一般来说,我们将实时操作系统区分为软实时系统(soft real-time system)和硬实时系统(hard real-time system).软实时系统不保证会调度关键实时进程,而只保证 ...

  5. OS之进程管理 --- 死锁

    什么是死锁 在正常操作模式下,进程按如下顺序来使用资源: 申请:进程请求资源 使用:进程对资源进行操作 释放:进程释放资源 当一组进程中的每一个进程度在等待一个事件,而这事件只能有一组进程的另一个进程 ...

  6. OS之进程管理---孤儿进程和僵尸进程

    僵尸进程 当一个进程终止时,操作系统会释放其资源,不过它位于进程表中的条目还是在的,直到它的父进程调用wait():这是因为进程表中包含了进程的退出状态.当进程已经终止,但是其父进尚未调用wait() ...

  7. Linux内核——进程管理与调度

    进程的管理与调度 进程管理 进程描写叙述符及任务结构 进程存放在叫做任务队列(tasklist)的双向循环链表中.链表中的每一项包括一个详细进程的全部信息,类型为task_struct,称为进程描写叙 ...

  8. Python::OS 模块 -- 进程参数

    os模块的简介请参看 Python::OS 模块 -- 简介 os模块的文件和目录操作 Python::OS 模块 -- 文件和目录操作 os模块的进程管理 Python::OS 模块 -- 进程管理 ...

  9. Linux进程管理 (2)CFS调度器

    关键词: 目录: Linux进程管理 (1)进程的诞生 Linux进程管理 (2)CFS调度器 Linux进程管理 (3)SMP负载均衡 Linux进程管理 (4)HMP调度器 Linux进程管理 ( ...

随机推荐

  1. 构造函数constructor 与析构函数destructor(五)

    我们知道当调用默认拷贝构造函数时,一个对象对另一个对象初始化时,这时的赋值时逐成员赋值.这就是浅拷贝,当成员变量有指针时,浅拷贝就会在析构函数那里出现问题.例如下面的例子: //test.h #ifn ...

  2. 2018.09.28 bzoj1563: [NOI2009]诗人小G(决策单调性优化dp)

    传送门 决策单调性优化dp板子题. 感觉队列的写法比栈好写. 所谓决策单调性优化就是每次状态转移的决策都是在向前单调递增的. 所以我们用一个记录三元组(l,r,id)(l,r,id)(l,r,id)的 ...

  3. 2018.07.26NOIP模拟 魔法数字(数位dp)

    魔法数字 题目背景 ASDFZ-NOIP2016模拟 题目描述 在数论领域中,人们研究的基础莫过于数字的整除关系.一般情况下,我们说整除总在两个数字间进行,例如 a | b(a能整除b)表示 b 除以 ...

  4. KindEditor解决上传视频不能在手机端显示的问题

    KindEditor自带的上传视频生成的HTML代码为<embed>,在手机端并不支持.于是可以自己在控件里增加生成video标签相关代码. 参考https://www.jianshu.c ...

  5. C语言之预处理命令与用typedef命名已有类型

    预处理命令 主要是改进程序设计环境,以提高编程效率,不属于c语言本身的组成部分,不能直接对它们进行编译,必须在对 程序编译之前,先对程序中的这些特殊命令进行“预处理”.比如头文件. 有以下三类:宏定义 ...

  6. DataFrame按行读取:DataFrame之values

    http://blog.csdn.net/u014607457/article/details/51290582 def fill_core(self): RatingTable=pd.read_cs ...

  7. VBA替换函数

    Sub test() On Error Resume Next Dim arr1, arr2, i, j arr1 = Range("T1:EI3") arr2 = Range(& ...

  8. Vivado 常见报错

    1.[Synth 8-2543] port connections cannot be mixed ordered and named 说明例化时最后一个信号添加了一个逗号. 2. 原因:报告说明有一 ...

  9. 第二届普适计算和信号处理及应用国际会议论文2016年 The 2nd Conference on Pervasive Computing, Signal Processing and Applications(PCSPA, 2016)

    A New Method for Mutual Coupling Correction of Array Output Signal 一种阵列输出信号互耦校正的新方法 Research of Robu ...

  10. (线段树)Mayor's posters --poj -- 2528

    链接: http://poj.org/problem?id=2528 覆盖问题, 要从后往前找, 如果已经被覆盖就不能再覆盖了,否则就可以覆盖 递归呀递归什么时候我才能吃透你 代码: #include ...