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

计算机操作系统(十六)进程同步

计算机操作系统(十五)互斥锁,信号量机制与整型信号量

  • 前言
  • 一、进程同步问题
    • 1.1 什么是进程?
    • 1.2 为什么需要同步?
  • 二、从信号到信号量
    • 2.1 什么是信号?
    • 2.2 信号量的诞生
  • 三、临界区:不能多人同时进的"小房间"
    • 3.1 什么是临界区?
    • 3.2 临界区的规则
    • 3.3 为什么需要临界区?
  • 四、信号量的实现与使用
    • 4.1 信号量的核心操作
    • 4.2 用信号量实现互斥(二元信号量)
    • 4.3 用信号量实现同步(计数信号量)
  • 五、经典同步问题
    • 5.1 生产者-消费者问题:
    • 5.2 读者-写者问题:
    • 5.3 哲学家就餐问题:


前言

  • 在上一篇博客中,我们系统剖析了 死锁底层原理与典型场景
  • 作为并发编程中另一核心议题,进程同步机制不仅是规避死锁风险的关键手段,更是保障多进程协同工作正确性的基础框架。
  • 本篇将从同步需求的本质出发,深入探讨进程 同步问题,信号到信号量,临界区,信号量的实现与使用

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的操作系统博客专栏
https://blog.csdn.net/2402_83322742/category_12916780.html?spm=1001.2014.3001.5482


一、进程同步问题

1.1 什么是进程?

进程就是计算机里正在运行的程序,比如你同时打开微信、浏览器、Word,每个都是一个进程。

1.2 为什么需要同步?

例子:银行账户取钱
假设你和朋友同时用手机给同一个账户转账100元,理想情况账户应该增加200元,但如果没有"排队"机制,可能出现:

  1. 你先读取账户余额1000元,朋友同时也读取1000元
  2. 你转账后余额变为1100元,朋友转账后也写回1100元
  3. 最终账户只增加了100元,钱"消失"了!

这就是进程同步问题:多个进程访问共享资源时,结果可能出错,必须让它们"有序"操作。

二、从信号到信号量

2.1 什么是信号?

信号是操作系统给进程的"通知",比如按Ctrl+C就是给进程发一个"终止信号"

  • 但信号只能发通知,无法解决复杂的排队问题(比如控制多少人能同时访问资源)。

2.2 信号量的诞生

信号量就像"钥匙管理员",它做两件事:

  • 记录当前可用资源的数量(比如有3把钥匙)
  • 控制进程能否获取资源(拿到钥匙才能进门)

例子:图书馆自习室
自习室有10个座位(资源数量),信号量就像登记本,记录当前空座位数。想进自习室的人先看登记本:

  • 有空位:拿走一个座位(登记本减1),进去学习
  • 没空位:在门口等待,直到有人离开(登记本加1时被通知)

三、临界区:不能多人同时进的"小房间"

3.1 什么是临界区?

临界区是一段代码,这段代码访问共享资源(比如变量、文件)时,必须独占访问。就像厕所,一次只能进一个人,否则会尴尬(出错)。

3.2 临界区的规则

  1. 互斥性:同一时间只能一个进程在临界区
  2. 进入有限等待:不能让进程永远等在外面
  3. 有空让进:临界区空了要允许进程进入

3.3 为什么需要临界区?

回到银行转账例子,"读取余额→修改余额→写回余额"这一段就是临界区,必须保证同一时间只有一个进程执行,否则数据会错乱。

四、信号量的实现与使用

4.1 信号量的核心操作

信号量S是一个整数变量,配合两个操作:

  • P(S):申请资源(Wait操作),相当于"我要拿钥匙"
    • 如果S>0:S减1,成功进入
    • 如果S=0:阻塞等待,直到有人释放资源
  • V(S):释放资源(Signal操作),相当于"还钥匙"
    • S加1,唤醒等待的进程

4.2 用信号量实现互斥(二元信号量)

当信号量S初始化为1时,它就变成了"互斥锁":

// 伪代码:两个进程互斥访问临界区
Semaphore S = 1;  // 初始有1把钥匙进程A:           进程B:
P(S);            P(S);  // 都想拿钥匙
临界区代码;      临界区代码;
V(S);            V(S);  // 用完还钥匙

原理:S=1时,第一个进程P(S)后S=0,第二个进程P(S)时发现S=0,只能等待;第一个进程V(S)后S=1,第二个进程才能进入。

4.3 用信号量实现同步(计数信号量)

当信号量S初始化为N(N>1)时,它可以控制N个进程同时访问资源:
例子:打印机共享(3台打印机,5个进程使用)

Semaphore printer = 3;  // 初始3把钥匙进程1:           进程2: ... 进程5:
P(printer);      P(printer);
使用打印机;       使用打印机;
V(printer);      V(printer);

原理:前3个进程P(printer)后,printer从3减到0,第4、5个进程P时会阻塞,直到前3个进程中有人用完还钥匙(V操作)。

五、经典同步问题

5.1 生产者-消费者问题:

问题描述:生产者做面包放到货架,消费者从货架拿面包,货架容量有限(比如只能放10个面包)。

关键点

  • 互斥:同一时间只能一人访问货架(防止面包被抢)
  • 同步:货架满时生产者等待,货架空时消费者等待

信号量解法

Semaphore empty = 10;  // 空位置数量
Semaphore full = 0;    // 满位置数量(初始没面包)
Semaphore mutex = 1;   // 互斥访问货架生产者:                  消费者:
P(empty);               P(full);
P(mutex);               P(mutex);
放面包到货架;            从货架拿面包;
V(mutex);               V(mutex);
V(full);                V(empty);

逻辑:生产者先看有没有空位置(empty),再拿互斥锁放面包;消费者先看有没有面包(full),再拿互斥锁拿面包。

5.2 读者-写者问题:

问题描述:多个读者可以同时看书,但写者必须独占写书,且写时不能有读者和其他写者。

关键点:读者优先(或写者优先),需要记录读者数量,保证第一个读者来加锁,最后一个读者走解锁。

信号量解法(读者优先):

Semaphore rwlock = 1;   // 读写互斥锁
Semaphore mutex = 1;    // 保护读者计数器的互斥锁
int readcount = 0;      // 读者数量读者:                    写者:
P(mutex);               P(rwlock);
readcount++;            写数据;
if(readcount == 1) P(rwlock);  // 第一个读者加锁
V(mutex);               V(rwlock);
读数据;
P(mutex);
readcount--;
if(readcount == 0) V(rwlock);  // 最后一个读者解锁
V(mutex);

逻辑:读者来先增加计数器,第一个读者加锁;写者直接加锁,保证独占。

5.3 哲学家就餐问题:

问题描述:5个哲学家围坐一桌,每两人之间有一根筷子,哲学家只能同时拿左右两根筷子才能吃饭,否则思考。

关键点:避免死锁(比如所有人都拿左边筷子,然后等右边筷子,永远吃不上饭)。

避免死锁的解法

  1. 最多允许4个哲学家同时拿筷子(总有一个能吃到)
  2. 规定奇数哲学家先拿左筷子,偶数先拿右筷子(打破循环等待)

信号量解法(方案1):

Semaphore chopstick[5] = {1,1,1,1,1};  // 5根筷子
Semaphore count = 4;                   // 最多4人拿筷子哲学家i:
P(count);           // 先申请"就餐资格"
P(chopstick[i]);    // 拿左筷子
P(chopstick[(i+1)%5]);  // 拿右筷子
吃饭;
V(chopstick[(i+1)%5]);
V(chopstick[i]);
V(count);           // 释放就餐资格

逻辑:限制同时拿筷子的人数为4,保证至少有一个哲学家能拿到两根筷子。


以上就是对本次关于操作系统博客内容的总结,后续我们将深入探讨操作系统更多知识。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的操作系统博客专栏
https://blog.csdn.net/2402_83322742/category_12916780.html?spm=1001.2014.3001.5482

非常感谢您的阅读,喜欢的话记得三连哦

在这里插入图片描述

http://www.lryc.cn/news/574199.html

相关文章:

  • DMDRS部署实施手册(ORACLE=》DM)
  • Long类型返回给前端精度丢失问题(解决方案)
  • AWS S3服务器访问日志启用
  • RAG实战基础篇/windows电脑快速部署qwen3:14B
  • Python Arrow 库详解:更智能的日期时间处理
  • 制造业B端页面个性化设计案例:生产流程监控的专属布局打造
  • 探秘 Java 安全利器 ——JVMTI
  • 揭开 Git 裸仓库的神秘面纱:`git clone --mirror` 详解与使用指南
  • idea 报错:java: 非法字符: ‘\ufeff‘
  • Node.js特训专栏-实战进阶:7.Express模板引擎选型与使用
  • 大数据时代UI前端的变革:从静态展示到动态交互
  • 数字孪生:为UI前端设计带来沉浸式交互新体验
  • 【机器学习深度学习】偏置项(Bias)概念
  • 常用终端命令(Linux/macOS/bash 通用)分类速查表
  • Elasticsearch 如果保证读写一致
  • Milvus【工具 01】milvus_cli和可视化工具attu安装使用
  • 【Linux学习笔记】进程间通信之共享内存
  • Three.js 中自定义 UV 坐标贴图详解
  • Ntfs!_LFCB结构如何构建出来的--从Ntfs!NtfsMountVolume到Ntfs!LfsAllocateLfcb
  • CentOS 上安装snmp
  • 如何在 Python 中连接 Elasticsearch 并使用 Qwen3 来实现 RAG
  • AI编程再突破,文心快码发布行业首个多模态、多智能体协同AI IDE
  • 【深度学习加速探秘】Winograd 卷积算法:让计算效率 “飞” 起来
  • SpringCloud系列(33)--使用Hystrix进行通配服务降级
  • 前缀树进阶-经典案例详解
  • Ubuntu20.04安装录屏工具OBS
  • 【Leetcode】有效的括号、用栈实现队列、用队列实现栈
  • Spring Boot + Logback MDC 深度解析:实现全链路日志追踪
  • 从数据到洞察:UI前端如何利用大数据优化用户体验
  • 用Fiddler抓包工具优化API联调流程:与Postman、Wireshark协作实践分享