《现代操作系统》——第6章 同步互斥机制(二)、进程间通信机制

  1. 管程 —— 管理共享资源的机制,例如pthread中的Pthread_cond_init()、Pthread_cond_wait()、Pthread_cond_signal()等这样一套同步机制,以及Pthread_mutex_init()...等这样一套互斥机制
    • 定义:管程是一个特殊模块,有一个名字,是由关于共享资源的数据结构及在其上操作的一组过程组成。
    • 产生背景:由于信号量机制的不足——程序编写困难、易出错,因此在程序设计语言中引入一种高级同步机制,即管程。
    • 进程与管程:进程只能通过调用管程中的过程来间接地访问管程中的数据结构。(这点有点像C++类中定义的私有数据只能通过类中的函数来操作)
    • 管程要解决的问题:
      • 互斥:保证管程中数据结构的数据完整性。管程是互斥进入的,其互斥性由编译器负责保证
      • 同步:管程中设置条件变量及等待/唤醒操作以解决同步问题。可以让一个进程或线程在条件变量上等待(此时应先释放管程的使用权),也可以通过发送信号将等待在条件变量上的进程或线程唤醒。
    • 管程应用过程中存在的问题及解决方案:
      • 问题:是否会出现多个进程同时出现在管程中?
      • 场景:
        • 当一个进入管程的进程执行等待操作时,他应当释放管程的互斥权
        • 当后面进入管程的进程P发现Q等待的条件满足并执行唤醒Q的操作时,管程中便同时存在两个处于活动状态的进程
      • 解决方案:
        • P等待Q执行——Hoare
        • Q等待P继续执行——MESA
        • 规定唤醒操作为管程中最后一个可执行的操作——Hansen,并发Pascal
    • Hoare管程:
      • Hoare管程说明:

        •  因为管程是互斥进入的,所以当一个进程试图进入一个已被占用的管程时,应当在管程的入口处等待。为此,管程的入口处设置一个进程等待队列,称作入口等待队列

        • 如果进程P唤醒进程Q,则P等待Q执行;如果进程Q执行中又唤醒了进程R,则Q等待R执行;...,如此,在管程内部可能出现多个等待进程,因此在Hoare管程机制在管程内设置一个进程等待队列,称为紧急等待队列,紧急等待队列的优先级高于入口等待队列的优先级。
      • Hoare管程——条件变量的实现
        • 条件变量:在管程内部说明和使用的一种特殊类型的变量。对于条件变量,可以执行wait和signal操作。
        • wait(c):如果紧急等待队列非空,则唤醒第一个等待者;否则释放管程的互斥权,执行此操作的进程进入c链的末尾(这里c是一个条件变量condition,c链指的是条件变量的等待队列)。注意这里先检查紧急等待队列是否有等待的进程,因为紧急等待队列中的进程是signal别的进程而自己主动让位的进程,因此具有优先权,其优先权高于入口等待队列的进程。当然,如果紧急等待队列中没有等待进程,释放管程的互斥权后,入口等待队列中的第一个会获得该管程的互斥权(使用权)。
        • signal(c):如果c链为空,则相当于空操作,执行此操作的进程继续执行;否则唤醒c链上第一个等待着,执行此操作的进程进入紧急等待队列的末尾。
    • 管程的应用:
      • 管程的实现方式:
        • 直接构造:效率高
        • 间接构造:用某种已实现的同步机制构造管程,如用信号量及PV操作
      • 管程的实现和使用方式——以生产者消费者问题为例:
      • 说明:这里实现管程的思路和信号量及PV操作类似,但具体实现根据不同的机制有所差别(比如按HOARE和MESA机制对wait和signal的内部实现肯定不同)。这里体现了使用管程相比于直接用信号量及PV操作的好处:(1)用户只需要使用insert和remove这样的操作即可,非常方便;(2)不用将复杂的进程同步语句写在应用代码里,逻辑清晰,代码简洁。
    • MESA管程:
      • 产生背景:HOARE管程存在一个缺点——两次额外的进程切换———P唤醒Q,Q执行完再唤醒P。
      • 解决:signal->notify。notify:当一个正在管程中的进程执行Notify(x)时,它使得x条件队列得到通知,发信号的进程继续执行
      • MESA管程的缺点:
        • Notify的结果:位于条件队列头的进程不是立即执行,而是在将来合适的时候且当处理器可用时才能恢复执行
        • 由于不能保证在它之前没有其他进程进入管程而改变了条件,因此这个进程必须重新检查条件——用while循环代替if语句对条件进行检查
        • 因此导致对条件变量至少多一次额外的检测,并且对等待进程在notify之后何时运行没有任何限制
      • MESA管程机制解决生产者消费者问题时的管程实现:
      • 说明:采用MESA管程机制的时候,对条件的判断必须用while而不能用if,在POSIX的pthread库中,管程的实现也是采用了MESA机制,后面将会看到
      • 对Notify的改进:
        • 给每个条件原语关联一个监视计时器,不论是否被通知,一个等待超时的进程将被设为就绪态,防止当某些进程在产生相关条件的信号之前失败时,等待该条件的进程被无限期地推迟而饿死
        • 当等待超时而被设为就绪态的进程被调度时,会再次检查相关条件,如果条件满足则执行。
      • BroadCast的引入:使所有等待在该条件上的进程都被释放并进入就绪队列。当一个进程不知道有多少进程将被激活时,这种方式非常方便(例如生产者不知道生产出的产品能满足多少个消费者时),当一个进程难以准确判断该激活哪个进程时,也可以使用广播BroadCast。
    • HOARE管程与MESA管程的比较:
      • MESA管程比HOARE管程出错少
      • 在MESA管程中,由于每个进程在收到信号后都重新检查管程变量,并且由于使用了while结构,一个进程不正确的broadcast广播或发信号Notify,不会导致收到信号的程序出错
    • pthread中的同步互斥机制:
      • pthread解决生产者消费者问题:
      • 说明:
        • 这里为什么在wait之前上锁?——防止wait操作在完成将线程加入条件变量等待队列之前,切换到另一个进程并加入到该条件的等待队列,这样就会产生顺序错误,破坏了条件变量的等待队列。(APUE上说是为了保护条件变量,因为后面对buffer的修改代码属于临界区)
        • 注意这里使用了while循环判断条件,这是因为pthread管程用的是MESA机制
        • 关于wait操作:wait的执行分为3步:(1)释放锁;(2)进入条件变量的等待队列;(3)当收到signal或broadcast时获取锁
  2. 进程间通信——IPC
    • 有了信号量和管程,为什么还需要其他通信机制?
      • 信号量和管程的不足:二者只能传递简单消息,无法传递大量数据;
      • 管程不使用与多处理器情况
    • 基本通信机制:
      • 消息传递
      • 共享内存
      • 管道
      • 套接字
      • 远程过程调用
    • 典型操作系统的进程同步/通信机制:


      • 原子操作简介:
        • 不可分割,在执行完之前不会被其他任务或事件中断
        • 常用与实现资源的引用计数
      • 屏障:
        • 一种同步机制(又称栅栏、关卡)
        • 用于对一组线程进行协调
        • 应用场景:一组线程协同完成一项任务,需要所有线程到达一个汇合点后再一起向前推进
posted @ 2020-07-02 16:51  凉风SK  阅读(520)  评论(0编辑  收藏  举报