五、并发:互斥与同步(二)

4.2实现互斥的方法

Dekker算法

  • 1965年的荷兰数学家T.J.Dekker
  • 避免“无原则”的礼让
  • 规定各程序进入临界区的顺序
  • 全局数组变量flag表示进入临界区的“意愿”
  • 全局变量turn解决进入顺序
    image
    image
  • 算法的问题
    • 逻辑复杂
    • 正确性难证明
    • 存在轮流问题
    • 存在忙等待

Peterson算法

  • 1981年数学家G.L.Peterson
  • 简单出色(不存在轮流问题)
  • flag和turn含义与Dekker相同
  • 先设turn=别人,只有“flag[别人]”和“turn=别人”同时为真时才循环等待
    image
    image

硬件实现方法

  • 中断禁用
    • 中断禁用(关中断)原理
      • 单CPU体系结构
      • 如果进程访问临界资源时(执行临界区的代码)不被中断,就能保证互斥的访问
    • 途径
      • 使用关/开中断指令
      • x86的开/关指令为STI/CLI
    • 限制了处理器交替执行各程序的能力,不能用于多核处理器结构
  • 专用指令
    • 适用范围
      • 单处理器或共享主存多核处理器结构
      • 对统一存储单元的访问是互斥的
    • 软件算法第二种尝试失败的原因:检测flag[1]和置位flag[0]在一个指令周期完成不会出错
      image
  • TestSet指令(TS)
    • 定义(逻辑)————比较并交换指令的bool特例
    • 原子指令
    • 指令功能
      • 测试内存变量var1某一位的值:
        • 0:设置标志寄存器的zf标志置位(1),而且var1这位变1
        • 1:设置标志寄存器的zf标志复位(0),而且var1这位变1
          image
    • 解决方案
      • 利用TestSet实现锁机制
      • 锁机制自旋锁
        • 内核自用的一种互斥机制
        • 一个锁变量(其实是一个二进制位)lockbit
        • 加锁操作lock
        • 解锁操作unlock
          image
  • exchange指令
    • 定义:交换一个寄存器和内存单元的内容
    • 原子操作
      image
    • x86 CPU的对应指令为XCHG
    • 指令功能:根据寄存器reg1与内存单元var1的值对换,并根据结果设置标志寄存器的z标志。
    • 利用xchg实现锁机制
      image
    • 互斥方法
      • 设置锁变量lockvar,初值为0
      • 临界区前,lock
      • 临界区后,unlock

机器指令方法的优缺点

  • 优点
    • 适用于单处理器或共享多核处理器系统,进程数量随意
    • 简单且易于证明
    • 可以使用多个变量支持多个临界区
  • 缺点
    • 忙等待/自旋等待
    • 可能饥饿
    • 可能死锁

常用并发机制

image

4.3信号量

  • 信号量机制

    • 荷兰计算机科学家 E. W. Dijkstra提出 (1965),解决并发进程问题的第一个重要进展,需操作系统支持
    • 两个或多个进程可以通过传递信号进行合作,从而可以迫使进程在指定位置暂停,直到它接收到特定信号
    • 信号量机制可以满足任何复杂的合作要求
    • 信号量值用来表示可用资源数目(非负整数)
  • 信号量机制的组成

    • 操作系统提供的用于线程并发控制的特殊数据结构
    • 含有一个非负整数变量和三个专门操作
      image
      • 初始化:通常将信号量的值初始化为非负整数(=可用资源数)
      • P操作/semWait操作/Down操作
        • 信号量值减1
        • 若信号量的值变成负数,则请求执行P操作的进程被阻塞
      • V操作/semSignal操作/Up操作
        • 信号量值加1
        • 如果信号量的值不是正数(其绝对值=现在被阻塞的进程数[等待队列长度]),则使一个因执行P操作阻塞的进程接触阻塞(唤醒)
  • 除此之外,无其他检查和修改信号量值的操作

  • 信号量和P、V原语的描述
    image

  • 二元信号量

    • 信号量的值只能是0或者1
    • 和一般信号量具有相同的表达能力
    • 因count不能小于0,需要其他方法判断等待队列是否为空
    • 二元信号量的操作和原语描述
      image
  • 信号量的实际含义

    • 信号量s(s.count)的初值
      • 系统中某些资源的数目,应该≥0
    • 进程执行P(s)/semWait(s)操作
      • 申请一个单位的资源(s.count--)
      • 若s.count < 0,资源已经分配完毕,进程被系统阻塞在s的队列上----让权等待
    • 进程执行V(s)/semSignal(s)操作
      • 释放一个单位资源(s.count++)
      • 若s.count≤0,则唤醒一个等待进程
    • s.count
      • ≥0:可用的资源数/可以执行P(s)而不会阻塞的进程数
      • <0:|s.count|为在队列中等待的进程数
  • 信号量的实现

    • 基本要求:保证P和V操作的原子性,实现信号量操作的互斥
    • 软件方案:Dekker算法、Peterson算法
    • 硬件支持方案:可以采用TS指令、关中断
  • 信号量的TestSet指令实现
    image

  • 信号量的关中断实现
    image

  • 信号量的优缺点

    • 优点:简单,表达能力强,用P、V操作可解决多种类型的同步、互斥问题
    • 缺点:不够安全、P、V操作使用不当可能产生死锁,遇到复杂的同步问题时实现复杂
  • 信号量机制的应用

    • 实现互斥
    • 进程同步
    • 生产者-消费者问题
    • 读者-写者问题
    • 其他

4.3.1用信号量实现互斥/同步

  • 实现互斥
    • n个进程访问同一个共享资源
    • 设置信号量s,初始化为1
    • 每个进程进入临界区之前执行P操作
    • 进程离开临界区时执行V操作
    • 保证最多只有一个进程在临界区,从而实现共享资源的互斥访问

image

  • 实现同步
    • 进程的同步
      • 系统中一些进程需要相互合作共同完成一项任务:一个进程运行到某一点时需要另一伙伴进程提供消息
      • 未获得消息之前,该进程属于阻塞状态,获得消息后被唤醒进入阻塞态
    • 用信号量解决同步关系的方案
      • 两个进程P1、P2,分别有操作a和b,a先于b执行
      • 设置信号量s,初始化0或具体资源限制值
      • 进程P1的操作a后执行V操作
      • 进程P2的操作b前执行P操作
      • 每执行一次操作a后,才可以执行b操作,从而实现进程同步
    • 用信号量实现进程同步(例)
      • 三个进程并发运行,合作完成数据输入、计算和打印输出工作
        image
        • 进程Pi将输入的数据写入缓冲区B1
        • 进程Pc读出B1中的数据,完成计算,把结果写入缓冲区B2
        • 进程Pp读出B2中的结果,打印输出
        • 同步要求:(读出数据后缓冲区为空)
          • 先写后读(不能读空缓冲区)
          • 未读完不能写(不能写非空缓冲区)
      • 四个信号量empty1、full1、empty1、full2,初始值分别为1、0、1、0(两个缓冲区都为空)
        image

4.3.2生产者/消费者问题

  • 若干进程通过有限的共享缓冲区交换数据
    • 一组“生产者”进程不断写入
    • 另一组“消费者”进程不断读出
    • 共享缓冲区 无限/共有N个
    • 任何时刻只能有一个进程可对共享缓冲区进行操作
      image
有限缓冲区的生产者/消费者问题
对象 被阻塞事件 解除阻塞事件
生产者 插入满缓冲区 消费者移出一项
消费者 从空缓冲区中移出 生产者插入一项

image

  • 解决方案
    image
posted @ 2024-11-15 20:44  韦飞  阅读(46)  评论(0)    收藏  举报