操作系统——进程同步与信号量

一、CPU调度策略(进程调度)

调度最终的目标是合理的使用CPU!!!

1.调度种类:

  • 高级调度:(High-Level Scheduling)又称为作业调度,它决定把后备作业调入内存运行
  • 中级调度:(Intermediate-Level Scheduling)又称为在虚拟存储器中引入,在内、外存对换区进行进程对换
  • 低级调度:(Low-Level Scheduling)又称为进程调度,它决定把就绪队列的某进程获得CPU

2. 非抢占式调度与抢占式调度

非抢占式

  • 分派程序一旦把处理机分配给某进程后便让它一直运行下去,直到进程完成或发生进程 调度进程 调度某事件 而阻塞时,才把处理机分配给另一个进程

抢占式

  • 操作系统将正在运行的进程强行暂停,由调度程序将CPU分配给其他就绪进程的调度方式

3.调度策略的设计

  • 周转时间 = 作业完成时间 - 作业提交时间
  • 响应时间:从操作发生到响应
  • 内耗时间:吞吐量(完成的任务量

吞吐量与响应时间的矛盾:

  • 响应时间小 ---> 切换次数多 ---> 系统内耗大 ---> 吞吐量小

I/O约束型任务:CPU使用短区间多

CPU约束型任务:I/O使用短区间多

二、CPU调度算法

1. 先来先服务(FCFS) :公平、简单、非抢占、不适合交互式

任务 到达时间 CPU区间(ms)
P1 0.0001 10
P2 0.0002 29
P3 0.0003 3
P4 0.0004 7
P5 0.0005 12

平均周转时间:(10+39+42+49+61)/5 = 40.2

2.短作业优先(SJF):保证最小的平均等待时间;不适合交互式

  • SJF的可抢占版本,比SJF更有优势

如果调度结果为P1P2...PN,则平均周转时间为:

  • P1+P1+P2+P1+P2+P3+...= ∑ ( n +1 - i ) Pi = nP1 + (n-1)P2+...
  • 前面作业越短,平均周转时间越短

3. 优先权调度

  • 每个任务关联一个优先权,调度优先权最高的任务
  • 优先权太低的任务一直就绪,得不到运行,出现“饥饿”现象
  • FCFS是RR的特例,SJF是优先权调度的特例。这些调度算法都不适合于交互式系统

4. 时间片轮转调度(RR)

  • 时间片大:响应时间太长
  • 时间片小:吞吐量小 
  • 折中:时间片 10 - 100ms ;切换时间为 0.1-1ms(1%)

  • 优先级调度:前台任务>后台任务 ---> 后台任务产生饥饿

5. 多级队列调度:(没法区分I/O bound和CPU bound、存在一定程度的“饥饿”现象)

  • 按照一定的规则建立多个进程队列
  • 不同的队列有固定的优先级(高优先级有抢占权)
  • 不同的队列可以给不同的时间片和采用不同的调度方法

6. 多级反馈队列

  • 在多级队列的基础上,任务可以在队列之间移动,更细致的区分任务
  • 可以根据“享用”CPU时间多少来移动队列,阻止“饥饿”
  • 最通用的调度算法,多数OS都使用该方法或其变形,如UNIX、Windows等

三、Linux0.11中schedule():

 

 阻塞的进程再就绪以后优先级高于非阻塞进程

counter作用整理:

  • counter保证了响应时间的界:每个进程时间片最常为2P

c(0) = P

c(t) = c(t-1)/2 + P

c(∞) = P+P/2+P/4+...<=2P

经过I/O以后,counter就会变大,I/O时间越长,counter越大(优先级提高),照顾了I/O进程,变相照顾了前台进程

后台进程一直按照 counter 轮转,近似SJF调度

每一个进程只用维护一个counter变脸,简单、高效

  • 固定优先级调度:后台任务产生饥饿
  • 动态优先级调度:后台任务优先级提高,前台任务响应时间长

四、进程同步与信号量(多进程合作)

1.生产者 - 消费者实例(用户态程序):

进程同步核心:等待

进程走走停停就是进程同步保证多进程合作的合理有序

  •  单纯的counter不能知道有多少个生产者被沉睡
  • 导致后面进来的生产可能永远被sleep
  • 所以需要信号量来决定是否发信息,即根据被sleep的生产者数量来决定发多少次唤醒
  • 信号量:发信号 + 记录等待的进程(对应睡眠和唤醒)

2.信号量开始工作:

信号量:

  • 一个确定的二元组(s,q),其中s是一个具有非负初值的整形变量,q是一个初始状态为空的队列,整形变量s表示系统中某类资源的数目:
    • 当其值 ≥ 0 时,表示系统中当前可用资源的数目
    • 当其值 < 0 时,其绝对值表示系统中因请求该类资源而被阻塞的进程数目
  • 除信号量的初值外,信号量的值仅能由P操作和V操作更改,操作系统利用它的状态对进程和资源进行管理
缓冲区满,P1执行 P1 sleep      sem = -1
P2执行 P2 sleep sem = -2
消费者C执行1次循环 wakeup P1 sem = -1
C再执行1次 wakeup P2 sem = 0
C再执行1次 sem = 1
P3执行 sem = 0
  • 信号量 <=0 ,等待;信号量 > 0 ,执行
  • 根据信号量的值,决定进程何时走何时停

3.信号量定义:

信号量:特殊整型变量,量用来记录,信号用来sleep和wakeup

 V(semaphore s)

{

        s.value ++

        if(s.value <= 0){

                wakeup(s.queue);}

}

 

4.信号量临界区保护

  • 信号量保护:上锁
  • 竞争条件:和调度有关的共享数据语义错误语义错误和调度顺序有关
  • 临界区:一次只允许一个进程进入该进程的那一段代码

为什么要进行信号量保护:

  • 信号量表达同步必须是语义正确
  • 保证语义正确必须是临界区
  • 故读写信号量的代码一定是临界区

 

 

  • 进入区:查看临界区是否可访问,如果可以访问,则转到步骤二,否则进程会被阻塞
  • 临界区:在临界区做操作
  • 退出区:清除临界区被占用的标志
  • 剩余区:进程与临界区不相关部分的代码

临界区代码的保护原则:

  • 基本原则:互斥进入
  • 好的临界区保护原则:有空让进、有限等待

(1)上锁

  • 轮转法(互斥进入):

  • 标记法(空等,都进入不了临界区): 

  • 非对称标记(Peterson算法):轮转法+标记法(两个进程) 

 &&:第一个表达式为false时,结果为false(具有短路功能)

         第一个表达式为false时,后面才会继续计算

  • 面包店算法(软件方法、多个进程):轮转+标记:
    • 如何轮转:每个进程都获得一个序号
    • 如何标记:进程离开时序号为0,不为0的序号即为标记
    • 面包店:每个进入商店的客户都获得一个号码;号码最小的先得到服务;号码相同时,名字靠前的先服务

(2)临界区保护:组织调度

  • 关中断:多CPU(多核)不好使
    • 因为关中断时,只是对执行该条件语句的CPU进行关中断,当中断来临时,执行P1语句的CPU继续执行(关中断),执行其他语句的CPU中断想回那个,执行P2语句,最后P1和P2只会导致信号量歧义

  •  硬件原子指令法:

 五、死锁处理

 死锁:互相等待对方持有的资源

死锁的4个必要条件:

  • 互斥使用
  • 不可抢占
  • 请求和保持
  • 循环等待(环路等待)

死锁处理方法:

  • 死锁预防:破坏死锁出现的条件
    • 不占有资源:一次性申请所有需要的资源,资源申请必须按序进行(资源浪费、编程困难)
  • 死锁避免:检测每个资源请求,如果造成死锁就拒绝
    • 判断此次请求是否引起死锁,即是否为安全状态
    • 安全状态:所有进程存在一个可完成的执行序列P1P2P3...Pn
    • 银行家算法:找出安全序列(T(n)= O(mn^2);m:资源个数,n:进程个数):找出安全序列
    • 请求出现时:首先将假装分配,其次调用银行家算法,若无安全序列,则拒绝资源申请
  • 死锁检测+恢复:检测到死锁出现,让进程 roll back(回滚),让出资源
    • 发现问题再处理:roll back难实现
  • 死锁忽略:就像没有死锁一样
    • 代价最小,可重启解决

六、进程间通信:

本地进程间通信的方式有很多,可以总结为下面四类:

  • 消息传递(管道、FIFO、消息队列)
  • 同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)
  • 共享内存(匿名的和具名的)
  • 远程过程调用(Solaris门和Sun RPC)

猜你喜欢

转载自blog.csdn.net/weixin_45864705/article/details/127865272