当前位置: 首页 > news >正文

2.3进程同步与互斥

一、同步与互斥的基本概念

1.1 进程同步

1.1.1 异步性问题
  • 异步性定义:各并发执行的进程以各自独立的、不可预知的速度向前推进

  • 同步机制作用:操作系统提供"进程同步机制"解决异步问题

1.1.2 同步需求场景
进程A:读数据 → 数据预处理 → 写回缓冲区 → 其他任务
进程B:准备工作 → 读数据 → 数据后续处理

需要保证进程B在进程A完成数据预处理后才能读取数据

1.2 进程互斥

1.2.1 资源共享方式
  • 互斥共享方式:一个时间段内只允许一个进程访问该资源

  • 同时共享方式:允许一个时间段内由多个进程"同时"访问

1.2.2 临界资源与临界区
  • 临界资源:一个时间段内只允许一个进程使用的资源

    • 物理设备:打印机、摄像头等

    • 软件资源:变量、数据、内存缓冲区等

  • 临界区:访问临界资源的那段代码

1.2.3 进程互斥的四个部分
do {entry section;     // 进入区:检查是否可进入临界区,设置访问标志critical section;  // 临界区:访问临界资源的代码段exit section;      // 退出区:解除访问标志remainder section; // 剩余区:其他处理
} while(true);
1.2.4 进程互斥原则
  1. 空闲让进:临界区空闲时,允许请求进入的进程立即进入

  2. 忙则等待:当已有进程进入临界区时,其他试图进入的进程必须等待

  3. 有限等待:对请求访问的进程,应保证在有限时间内进入临界区

  4. 让权等待:当进程不能进入临界区时,应立即释放处理机,防止忙等待

二、进程互斥的软件实现方法

2.1 单标志法

int turn = 0; // 表示当前允许进入临界区的进程号// P0进程
while(turn != 0);  // 进入区
critical_section;   // 临界区
turn = 1;          // 退出区// P1进程
while(turn != 1);  // 进入区
critical_section;   // 临界区
turn = 0;          // 退出区
  • 优点:实现简单

  • 缺点:违背"空闲让进"原则,必须轮流访问

2.2 双标志先检查法

bool flag[2] = {false, false};// P0进程
while(flag[1]);     // 检查
flag[0] = true;     // 上锁
critical_section;
flag[0] = false;    // 解锁// P1进程
while(flag[0]);
flag[1] = true;
critical_section;
flag[1] = false;
  • 问题:违反"忙则等待",检查与上锁非原子操作

2.3 双标志后检查法

bool flag[2] = {false, false};// P0进程
flag[0] = true;     // 先上锁
while(flag[1]);     // 后检查
critical_section;
flag[0] = false;// P1进程
flag[1] = true;
while(flag[0]);
critical_section;
flag[1] = false;
  • 问题:违反"空闲让进"和"有限等待",可能导致饥饿

2.4 Peterson算法

bool flag[2] = {false, false};
int turn = 0;// P0进程
flag[0] = true;                 // 表达意愿
turn = 1;                       // 主动谦让
while(flag[1] && turn == 1);   // 检查
critical_section;
flag[0] = false;// P1进程
flag[1] = true;
turn = 0;
while(flag[0] && turn == 0);
critical_section;
flag[1] = false;
  • 优点:满足空闲让进、忙则等待、有限等待

  • 缺点:不满足让权等待,会发生忙等

三、进程互斥的硬件实现方法

3.1 中断屏蔽方法

关中断();
临界区代码;
开中断();
  • 优点:简单、高效

  • 缺点:只适用于单处理机;只适用于操作系统内核进程

3.2 TestAndSet指令(TSL指令)

bool TestAndSet(bool *lock) {bool old = *lock;*lock = true;return old;
}// 使用
while(TestAndSet(&lock));
临界区代码;
lock = false;
  • 优点:实现简单;适用于多处理机环境

  • 缺点:不满足"让权等待"

3.3 Swap指令(XCHG指令)

void Swap(bool *a, bool *b) {bool temp = *a;*a = *b;*b = temp;
}// 使用
bool old = true;
while(old == true)Swap(&lock, &old);
临界区代码;
lock = false;
  • 特性:逻辑上与TSL指令类似

  • 优缺点:同TSL指令

四、互斥锁

4.1 互斥锁基本概念

acquire() {while(!available);  // 忙等待available = false;  // 获得锁
}release() {available = true;   // 释放锁
}

4.2 自旋锁特性

  • 定义:需要连续循环忙等的互斥锁

  • 包含:TSL指令、Swap指令、单标志法

  • 优点:等待期间不用切换进程上下文

  • 缺点:违反"让权等待"

  • 适用:多处理器系统,上锁时间短的场景

五、信号量机制

5.1 信号量基本概念

  • 提出者:Dijkstra(1965年)

  • 本质:变量,表示系统中某种资源的数量

  • 操作:wait(S)和signal(S)原语,简称P、V操作

5.2 整型信号量

int S = 1;  // 初始化信号量void wait(int S) {while(S <= 0);  // 忙等待S = S - 1;
}void signal(int S) {S = S + 1;
}
  • 问题:不满足"让权等待",会发生忙等

5.3 记录型信号量

typedef struct {int value;           // 剩余资源数struct process *L;   // 等待队列
} semaphore;void wait(semaphore S) {S.value--;if(S.value < 0) {block(S.L);      // 自我阻塞}
}void signal(semaphore S) {S.value++;if(S.value <= 0) {wakeup(S.L);     // 唤醒等待进程}
}
  • 优点:满足"让权等待"

  • 注意:考试中默认信号量为记录型信号量

六、用信号量实现进程互斥、同步、前驱关系

6.1 实现进程互斥

semaphore mutex = 1;  // 互斥信号量P1() {P(mutex);     // 加锁临界区代码;V(mutex);     // 解锁
}P2() {P(mutex);临界区代码;V(mutex);
}
  • 要点

    • 对不同的临界资源设置不同的互斥信号量

    • P、V操作必须成对出现

6.2 实现进程同步

semaphore S = 0;  // 同步信号量// 前操作进程
P1() {代码1;代码2;V(S);     // 前V代码3;
}// 后操作进程
P2() {P(S);     // 后P代码4;代码5;代码6;
}
  • 口诀:前V后P

  • 原理:信号量S代表"某种资源",由前操作产生,后操作消耗

6.3 实现前驱关系

S1/  \S2    S3|    |S4    S5\  /S6
semaphore a, b, c, d, e, f, g = 0;// 分别为每一对前驱关系设置同步信号量
S1() { ... V(a); V(b); }
S2() { P(a); ... V(c); }
S3() { P(b); ... V(d); }
S4() { P(c); ... V(e); }
S5() { P(d); ... V(f); }
S6() { P(e); P(f); ... }

七、生产者-消费者问题

7.1 问题描述

  • 生产者:生产产品放入缓冲区

  • 消费者:从缓冲区取出产品使用

  • 缓冲区:初始为空,大小为n的临界资源

  • 约束

    • 缓冲区未满时,生产者才能放入产品

    • 缓冲区未空时,消费者才能取出产品

    • 缓冲区必须互斥访问

7.2 信号量设置

semaphore mutex = 1;     // 互斥信号量,实现对缓冲区的互斥访问
semaphore empty = n;     // 同步信号量,表示空闲缓冲区数量
semaphore full = 0;      // 同步信号量,表示产品数量

7.3 实现代码

producer() {while(1) {生产一个产品;P(empty);    // 消耗一个空闲缓冲区P(mutex);把产品放入缓冲区;V(mutex);V(full);     // 增加一个产品}
}consumer() {while(1) {P(full);     // 消耗一个产品P(mutex);从缓冲区取出一个产品;V(mutex);V(empty);    // 增加一个空闲缓冲区使用产品;}
}

7.4 注意事项

  • P操作顺序:实现同步的P操作要在实现互斥的P操作之前,否则可能死锁

  • V操作顺序:可以交换,因为V操作不会导致进程阻塞

八、多生产者-多消费者问题

8.1 问题描述(水果盘子问题)

  • 生产者:爸爸(放苹果)、妈妈(放橘子)

  • 消费者:儿子(吃橘子)、女儿(吃苹果)

  • 缓冲区:盘子,容量为1

  • 约束

    • 盘子空时,爸爸或妈妈才能放水果

    • 盘中有对应水果时,儿子或女儿才能取水果

8.2 信号量设置

semaphore mutex = 1;     // 互斥访问盘子
semaphore apple = 0;     // 盘子中苹果数量
semaphore orange = 0;    // 盘子中橘子数量
semaphore plate = 1;     // 盘子中可放水果数量

8.3 实现代码

dad() {while(1) {准备一个苹果;P(plate);P(mutex);把苹果放入盘子;V(mutex);V(apple);}
}mom() {while(1) {准备一个橘子;P(plate);P(mutex);把橘子放入盘子;V(mutex);V(orange);}
}daughter() {while(1) {P(apple);P(mutex);从盘中取出苹果;V(mutex);V(plate);吃掉苹果;}
}son() {while(1) {P(orange);P(mutex);从盘中取出橘子;V(mutex);V(plate);吃掉橘子;}
}

8.4 特殊情况分析

  • 缓冲区大小为1:可能不需要互斥信号量(但考试建议加上)

  • 缓冲区大小>1:必须设置互斥信号量

  • 分析方法:从"事件"角度分析,而非单个进程行为

九、读者-写者问题

9.1 问题描述

  • 读者:读文件,允许多个读者同时读

  • 写者:写文件,只允许一个写者写

  • 约束

    • 允许多个读者同时对文件执行读操作

    • 只允许一个写者往文件中写信息

    • 任一写者在完成写操作前不允许其他读者或写者工作

    • 写者执行写操作前,应让已有的读者和写者全部退出

9.2 基本实现(读者优先)

semaphore rw = 1;       // 实现对文件的互斥访问
int count = 0;          // 当前读进程数
semaphore mutex = 1;    // 保证对count的互斥访问writer() {while(1) {P(rw);          // 写前加锁写文件;V(rw);          // 写后解锁}
}reader() {while(1) {P(mutex);if(count == 0)  // 第一个读进程负责加锁P(rw);count++;V(mutex);读文件;P(mutex);count--;if(count == 0)  // 最后一个读进程负责解锁V(rw);V(mutex);}
}

9.3 写者优先实现

semaphore rw = 1;
int count = 0;
semaphore mutex = 1;
semaphore w = 1;        // 实现写优先writer() {while(1) {P(w);P(rw);写文件;V(rw);V(w);}
}reader() {while(1) {P(w);P(mutex);if(count == 0)P(rw);count++;V(mutex);V(w);读文件;P(mutex);count--;if(count == 0)V(rw);V(mutex);}
}

9.4 核心思想

  • 设置计数器count记录当前读进程数

  • 用count判断是否是第一个/最后一个读进程

  • 对count的访问需要互斥

  • 可通过额外信号量解决写进程饥饿问题

十、哲学家进餐问题

10.1 问题描述

  • 5名哲学家围坐圆桌,交替进行思考和进餐

  • 每两个哲学家间有一根筷子,共5根筷子

  • 哲学家饥饿时,需要同时拿起左右两根筷子才能进餐

  • 进餐完毕后放下筷子继续思考

10.2 错误实现(会导致死锁)

semaphore chopstick[5] = {1,1,1,1,1};Pi() {  // i号哲学家进程while(1) {P(chopstick[i]);           // 拿左筷子P(chopstick[(i+1)%5]);     // 拿右筷子进餐;V(chopstick[i]);V(chopstick[(i+1)%5]);思考;}
}

问题:所有哲学家同时拿起左筷子,导致死锁

10.3 解决方案

10.3.1 限制哲学家数量
semaphore count = 4;  // 最多允许4个哲学家同时进餐Pi() {while(1) {P(count);P(chopstick[i]);P(chopstick[(i+1)%5]);进餐;V(chopstick[i]);V(chopstick[(i+1)%5]);V(count);思考;}
}
10.3.2 奇偶号哲学家拿筷子顺序不同
  • 奇数号:先拿左筷子,再拿右筷子

  • 偶数号:先拿右筷子,再拿左筷子

10.3.3 同时拿起两根筷子(互斥法)
semaphore mutex = 1;  // 互斥地取筷子Pi() {while(1) {P(mutex);P(chopstick[i]);P(chopstick[(i+1)%5]);V(mutex);进餐;V(chopstick[i]);V(chopstick[(i+1)%5]);思考;}
}

10.4 解决思路总结

  1. 限制数量:最多允许4个哲学家同时进餐

  2. 改变顺序:奇偶号哲学家拿筷子顺序不同

  3. 原子操作:仅当左右筷子都可用时才允许拿起

十一、管程

11.1 引入管程的原因

  • 信号量机制问题:编写程序困难、易出错

  • 管程目标:让程序员无需关注复杂的P、V操作

11.2 管程的定义与特征

monitor MonitorName// 1. 局部于管程的共享数据结构说明// 2. 对该数据结构进行操作的一组过程// 3. 对局部于管程的共享数据设置初始值的语句// 4. 管程有一个名字
end monitor;

基本特征

  1. 局部于管程的数据只能被局部于管程的过程访问

  2. 进程只有通过调用管程内的过程才能进入管程访问共享数据

  3. 每次仅允许一个进程在管程内执行某个内部过程

11.3 用管程解决生产者-消费者问题

monitor ProducerConsumercondition full, empty;  // 条件变量int count = 0;          // 缓冲区中的产品数void insert(Item item) {if(count == N)wait(full);     // 缓冲区满,等待count++;insert_item(item);if(count == 1)signal(empty);  // 唤醒等待的消费者}Item remove() {if(count == 0)wait(empty);    // 缓冲区空,等待count--;if(count == N-1)signal(full);   // 唤醒等待的生产者return remove_item();}
end monitor;// 生产者进程
producer() {while(1) {item = 生产产品;ProducerConsumer.insert(item);}
}// 消费者进程
consumer() {while(1) {item = ProducerConsumer.remove();使用产品;}
}

11.4 管程的优势

  • 互斥特性:由编译器实现,程序员无需关心

  • 同步机制:通过条件变量及等待/唤醒操作实现

  • 封装思想:提供特定的"入口"访问共享数据

11.5 Java中的类似机制

static class Monitor {private Item buffer[] = new Item[N];private int count = 0;public synchronized void insert(Item item) {// 同一时间段内只能有一个线程调用}
}

十二、知识总结与重要考点

12.1 PV操作解题思路

  1. 关系分析:找出各进程间的同步、互斥关系

  2. 整理思路:确定P、V操作的大致顺序

  3. 设置信号量:根据题目条件确定信号量初值

12.2 信号量设置原则

  • 互斥信号量:初值一般为1

  • 同步信号量:初值看对应资源的初始值

  • 资源信号量:初值为资源数量

12.3 易错点与注意事项

  1. P操作顺序:实现同步的P操作要在实现互斥的P操作之前

  2. V操作顺序:可以交换,不会导致阻塞

  3. 信号量含义:一个信号量对应一种资源

  4. 缓冲区大小:影响是否需要互斥信号量

12.4 经典问题对比

问题类型核心矛盾解决关键典型应用
生产者-消费者生产与消费速度不匹配缓冲区管理数据缓冲、消息队列
读者-写者读并发与写互斥读者计数器文件系统、数据库
哲学家进餐资源竞争与死锁拿筷子策略资源分配、死锁避免

12.5 复习建议

  1. 理解原理:不只是记忆代码,要理解同步互斥的本质

  2. 动手练习:多做PV操作题目,熟练掌握解题思路

  3. 对比分析:对比不同解决方案的优缺点

  4. 联系实际:理解各经典问题在实际系统中的应用

核心思维:进程同步与互斥是操作系统协调并发进程的关键机制,通过信号量、管程等工具,在保证数据一致性的同时提高系统并发度。理解各种同步问题的本质和解决方案,是掌握操作系统并发管理的核心。

http://www.dtcms.com/a/473972.html

相关文章:

  • 计算机组成原理之第一章计算机系统概述
  • 无服务器架构下的ACID特性实现方案
  • 四平方和定理
  • 搜索郑州网站服装网站建设
  • 广西临桂建设局网站如何做家乡网站
  • Leetcode2166-设计位集
  • 三种方法解——力扣206.反转链表
  • 企业网站广告网站响应式是什么意思
  • 湖南省郴州市邮编东莞seo网站建设公司
  • 差分信号可以分解为共模信号与差模信号
  • **标题:发散创新:探索SSR渲染技术的深度实现****摘要**:本文将深入探讨服务端渲染(SSR)技术的原理、优势以及实
  • 计算机视觉(opencv)——MediaPipe 实现手部关键点检测与可视化
  • 贵州省建设学校官方网站昆明网络公司开发
  • 没有版权可以做视频网站吗设计之家素材
  • Tomcat是一个容器
  • Easyx图形库应用(和Server程序进行交互)
  • Python自学25 - Django快速上手
  • 太原云起时网站建设广东知名网站建设
  • AI学习日记——深度学习
  • 如何设置PostgreSQL表字段为自增主键
  • 排版工具:也说Markdown的使用方法
  • 分销网站建站wordpress调用推荐文章代码
  • 数据湖Hudi-读取流程可视化
  • 智能环境感知屏幕自适应系统:原理、架构与实现
  • 中卫网站制作公司公司网站seo怎么做
  • Python高效搜索实现:从数据海洋到精准信息的智能导航
  • 安安网站建设优惠的网站快排公司电话
  • Elasticsearch 备份:snapshot 镜像使用篇
  • 10月12日星期天今日早报简报微语报早读
  • 著名建筑网站网站建设市场价