XJTLU 24-25 CPT104-Operating Systems Concepts Week3 笔记

目录


进程同步 Process Synchronization (PS)

  • 定义
    • 一种机制,任务是:协调进程的执行,以确保在任何时刻,没有两个进程可以同时访问相同的共享数据和资源。
  • 问题
    • 数据不一致:多个进程对共享数据的并发访问可能会导致这一点。
    • 竞态条件(Race Condition): 是一种特殊的数据不一致性情况。多个进程并发地访问和修改共享数据,最终数据的值取决于哪个进程最后完成。
  • 对应方案
    • 维护数据一致性:需要机制来确保协作进程的有序执行。
    • 防止竞态条件的出现:并发进程必须同步

临界区 Critical-Section

  • 定义
    • 一种代码段。在这段代码中,进程会访问共享资源(例如共享变量、文件等)。
  • 代码形式
    do {
     entry section
     critical section - CS
     exit section
     remainder section – RS
    } while(TRUE)
  • 流行度
    • 每个并发线程 / 进程都有。
  • 特性
    • 不能在多个线程 / 进程之间交错执行:
      • 含义:同一时刻,只能有一个进程在执行临界区内的代码。
      • 目的:避免竞态条件
      • 解释:
        • Critical-Section 以“读取-更新-写入”的方式引用一个变量;
        • 如果临界区允许交错执行,就会出现:
        • 如此,只能禁止交错执行:
        • 即使我们失去了一些效率,但我们获得了正确性。

临界区问题的解决方案(简介)

  • 原则(三个)
    • 互斥 Mutual Exclusion一次只能有一个进程 / 线程进入临界区
    • 进展 Progress如果没有进程处于临界区,并且一些进程希望进入临界区,那么选择下一个进入临界区的进程不能被无限期推迟。
      • 直观理解:需要有“进展”。不能“互相谦让”,导致谁都进不去。
    • 有限等待 Bounded Waiting任何进程 / 线程都不应无限期等待进入临界区。
      • 目的:否则进程 / 线程可能会遭受“饥饿”
      • 解释:
        • “等待进入”:一种状态。即发起请求后,实际进入前。
        • “饥饿”(Starvation)现象:即一个进程一直得不到所需的资源。
  • 各种解决方案简介(按类型分)
    • 软件解决方案:仅通过软件算法来实现进程同步,不依赖于特殊的硬件指令。
    • 硬件解决方案:依赖于特殊的机器指令进行“锁定”(lock
    • 操作系统和编程语言提供的解决方案
      • 示例:Java 中有特定的函数和数据结构可以用于同步。

临界区问题的解决方案(详解)

  • 软件解决方案(Peterson算法)
    • 用途: 实现互斥(Mutual Exclusion),允许多个进程共享一个资源,而不会发生冲突。
    • 特点:
      • 仅使用共享内存进行通信。
      • 最初的 Peterson算法 只适用于两个进程。
    • 核心思想:设计临界区的“进入区”和“退出区”代码。
    • 具体内容:进程共享一些公共变量以同步它们的操作。
      • turn 一个整数,表示轮到哪个进程进入临界区。turn = 0 表示轮到进程P0。turn = 1 表示轮到进程P1。
      • flag[2] 一个布尔数组,flag[i] 表示进程Pi是否想要进入临界区。flag[i] = true 表示进程Pi想要进入临界区。flag[i] = false 表示进程Pi不想进入临界区。
      • 需要同时使用 turn 和 flag[2] 来保证互斥、进展 和 有限等待。
    • 代码:
      do {
       flag[i] = TRUE;
       turn = j;
       while ( flag[j] && turn == j);
       CRITICAL SECTION
       flag[i] = FALSE;
       REMAINDER SECTION
      }
      while (TRUE);
      
    • i 当前进程的编号(0或1)。
    • j 另一个进程的编号(如果i是0,则j是1;如果i是1,则j是0)。
    • 代码解释:
      • flag[i] = TRUE; 进程Pi将自己的flag设置为true,表示它想要进入临界区。
      • turn = j; 进程Pi将turn设置为j,表示“谦让”,让另一个进程优先进入临界区。
      • while ( flag[j] && turn == j); 这是 Peterson算法 的关键。进程Pi 会在这里等待,直到以下两个条件之一成立:
        • flag[j] == false:另一个进程不想进入临界区。
        • turn == i:轮到进程Pi进入临界区。
      • CRITICAL SECTION 进程Pi 进入临界区,执行访问共享资源的代码。
      • flag[i] = FALSE; 进程Pi 将自己的flag设置为false,表示它已经离开了临界区。
      • REMAINDER SECTION 进程Pi 执行其他非临界区代码。
    • 方案的正确性:
      • 正确,因为三个原则都得到了遵守。
  • 硬件解决方案
    • 使用“锁”
      • 流程:在进入临界区之前尝试获取需要访问的共享资源的,如果成功获取,则进入临界区;离开临界区时释放该
    • 禁用中断
      • 方案前提:单处理器环境
      • 别名:TEST AND SET SOLUTION
      • 流程:
        • 初始时,锁值设置为 0。
        • 锁值 = 0:表示临界区当前为空,没有进程在其中。
        • 锁值 = 1:表示临界区当前被占用,有一个进程在其中。
      • 优点:
        • 实现起来简单
      • 缺点:
        • 不完全“正确”:
          • 可以满足“互斥”,但是不能保证“有限等待”:
            • 可能会出现一个进程一直获取不到锁的情况。
    • COMPARE AND SWAP SOLUTION
      • 方案前提:多处理器环境
      • 解释:
        • 因为多处理器环境可以提供特殊的原子硬件指令。
        • “原子”和数据库中的原子性 (Atomicity) 的含义一致。
      • 流程:
        • 全局变量 lock 被初始化为 0。
        • 唯一可以进入 CS 的 进程Pi 是“找到” lock = 0 的那个;
        • 这个 Pi 通过将 lock 设置为 1 来排除所有其他 Pj。
      • 解释:
        • “找到”与设置:使用 CAS (Compare And Swap) 指令来做这两件事;
        • CAS 指令的功能:
          • 读取变量值;
          • 比较变量的当前值与 期望值A 是否相等。
          • 如果相等,则将变量值更新为 新值B ,并返回 true(表示操作成功)。
          • 如果不相等,则不进行任何操作,并返回 false(表示操作失败)。
    • 优点(硬件解决方案)
      • 适用于任意数量的进程,无论是在单处理器还是多处理器系统上。
      • 简单易于验证。
      • 可以支持多个临界区。
    • 缺点(硬件解决方案)
      • 机制有一些缺陷,也有陷入一些困境;
      • 忙等待(Busy-waiting): 当一个进程等待进入临界区时,它会不断地检查锁的状态,这会浪费CPU时间。
      • 饥饿(Starvation): 可能会出现一个进程长时间等待,始终无法进入临界区。
      • 死锁(Deadlock): 可能会出现多个进程互相等待对方释放资源,导致所有进程都无法继续执行的情况。
  • 操作系统与编程语言解决方案:
    • 互斥锁 / 互斥 (Mutex Locks / Mutual exclusion)
      • 定义:一种用于获取和释放对象的编程标志 (programming flag)。
      • 作用:保证一致性,确保同一时刻只有一个进程可以访问共享资源。
      • 操作:
        • 加锁(Lock): 进程在进入临界区之前尝试获取互斥锁。如果互斥锁已经被其他进程占用,则当前进程会阻塞(等待)。
        • 解锁(Unlock): 进程在离开临界区之后释放互斥锁,以便其他进程可以获取锁。
      • 实现:
        • 内核级别: 禁用中断
          • 目的:防止共享数据结构的损坏
        • 软件级别: 忙等待机制
          • 流程:进程在无限循环中执行,等待锁变量的值指示可用性
      • “自旋锁”(Spinlocks
        • 命名由来:进程在等待锁变为可用时会“自旋”(循环检查锁的状态),而不是单纯的阻塞。
        • 示例Peterson算法 中的 `while ( flag[j] && turn == j);`
        • 特点:
          • 没有阻塞,自然没有上下文切换;
          • 只能用于多处理器系统:
            • 在单处理器系统中,如果一个进程自旋等待锁,那么持有锁的进程根本没有机会运行,锁永远不会被释放。
        • 应用场景:锁被持有的时间预计非常短。
          • 预计很短,所以:“自旋”的开销 < 切换上下文的开销
    • 信号量 (Semaphore)
      • 核心:共享一个简单的非负整数值(信号量);
      • 信号量的含义:
        • 当前信号量的值指示了还允许多少几个进程进入自己的临界区。
        • 示例:如果将信号量初始化为k,则可以允许最多k个进程同时进入临界区。
      • 操作(只有三种):
        • 初始化(信号量);
        • (信号量)减量:
          • 操作:wait()
          • 功能:可能阻塞进程;
        • (信号量)增量:
          • 操作:signal()
          • 功能:可能解除阻塞。
      • 使用信号量解决临界区问题
        • 代码:
          do {
          wait(S);
          CRITICAL SECTION
          signal(S);
          RS
          } while(true)
        • 解释:
          • wait(S); 尝试获取信号量。如果信号量的值为0,则进程阻塞,直到其他进程释放信号量。
          • CRITICAL SECTION 进程进入临界区,执行访问共享资源的代码。
          • signal(S); 释放信号量,允许其他进程进入临界区。
          • RS 进程执行其他非临界区代码。
      • 信号量的类型:
        • 计数信号量(Counting Semaphore / CS):
          • 值域: 可以是任意非负整数。
          • 用途: 用于控制对具有多个实例的资源的访问。例如,一个打印机池可能有多个打印机,可以用计数信号量来表示可用打印机的数量。
          • 初始值: 通常初始化为可用资源的数量。
        • 二进制信号量(Binary Semaphore / BS):
          • 值域: 只能是 0 或 1。
          • 用途: 类似于互斥锁,用于实现多个进程对临界区的互斥访问。
          • 初始值: 通常初始化为 1,表示临界区空闲。
      • 工作流程
        • 二进制信号量被初始化为 1。
        • WAIT() 操作:
          • 检查信号量的值:
            • 如果值为 0,则执行 wait() 的进程被阻塞。
            • 如果值为 1,则将其更改为 0,进程继续执行。
        • SIGNAL() 操作:
          • 检查是否有进程因为等待该信号量而阻塞。
            • 即因信号量值等于 0,而在 WAIT() 处被阻塞。
          • 如果是这样,则唤醒一个被阻塞的进程。
          • 如果没有,那么信号量的值将被设置为1。
      • 互斥锁与二进制信号量的区别
        • 互斥锁(Mutex): 哪个进程加的锁,就必须由哪个进程解锁。
        • 二进制信号量(Binary Semaphore): 理论上,一个进程可以锁定二进制信号量,而另一个进程可以解锁它。
      • 信号量可能导致的问题
        • 饥饿(Starvation);
        • 死锁(Deadlock)。

经典的同步问题

  • 有界缓冲区问题(Bounded-Buffer Problem)/ 生产者-消费者问题(Producer-Consumer Problem
    • 描述: 一组生产者进程生成数据,放入一个缓冲区;一组消费者进程从缓冲区取出数据进行消费。缓冲区的大小是有限的。
    • 同步要求:
      • 生产者不能在缓冲区满时放入数据。
      • 消费者不能在缓冲区空时取出数据。
      • 多个生产者或多个消费者不能同时访问缓冲区。
    • 工程背景:缓冲区访问控制。
    • 解决方案
      • 使用三个信号量;
      • mutex 信号量: 用于互斥访问缓冲区, 初始化为1.
      • empty 信号量: 记录空闲缓冲区的数量, 初始化为n (缓冲区大小).
      • full 信号量: 记录已填充的缓冲区的数量, 初始化为0.

  • 读者-写者问题(Readers-Writers Problem
    • 描述: 多个进程共享一个数据集。一些进程只读取数据(读者),另一些进程需要写入数据(写者)。
    • 同步要求:
      • 多个读者可以同时读取数据。
      • 写者必须独占地访问数据,任何其他读者或写者都不能同时访问。
    • 工程背景:一个数据集在多个并发进程之间共享。
    • 解决方案
      • 将锁分类,读锁和写锁。
      • 获取读写锁时需要指定模式 (读或写)。

  • 哲学家就餐问题(Dining-Philosophers Problem
    • 描述: 五个哲学家围坐在一张圆桌旁,每人面前有一盘意大利面,两人之间有一根筷子。哲学家要么思考,要么吃饭。吃饭时,哲学家需要拿起左右两边的筷子。
    • 同步要求:
      • 避免死锁:所有哲学家都拿起左边的筷子,然后等待右边的筷子,导致所有人都无法吃饭。
      • 避免饥饿:一个哲学家可能一直无法拿到两根筷子。
    • 工程背景:如何在多个进程之间分配多个资源。
    • 可能的解决方案
      • 限制同时饥饿的哲学家数量 (例如, 最多4个).
      • 只有当两根筷子都可用时才允许拿起 (在临界区内完成).
      • 奇数编号的哲学家先拿左边的筷子, 偶数编号的哲学家先拿右边的筷子.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值