• 微信搜索:编程笔记本
  • 微信搜索:编程笔记本
  • 微信搜索:编程笔记本

点击上方蓝字关注我,我们一起学编程
欢迎小伙伴们分享、转载、私信、赞赏

小伙伴儿们看完以后可不可以帮我点亮一下在看呀~

信号量与进程同步、互斥

文章目录

  • 信号量与进程同步、互斥
  • 1 进程同步
  • 2 进程互斥
  • 3 信号量机制
    • 3.1 整型信号量
    • 3.2 记录型信号量
  • 4 用信号量实现进程互斥、同步
    • 4.1 用信号量实现进程互斥
    • 4.2 用信号量实现进程同步
  • 5 哲学家进餐问题

1 进程同步

我们知道,进程具有异步性的特征。异步性是指:各并发执行的进程以各自独立的、不可预知的速度向前推进。

而同步又称直接制约关系,它是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而产生的制约关系。

2 进程互斥

我们把一个时间段内只允许一个进程使用的资源称为临界资源。许多物理设备(摄像头、打印机等)都属于临界资源。对临界资源的访问,必须互斥地进行。

互斥亦称间接制约关系。**进程互斥是指当一个进程访问某临界资源时,另一个想要访问该临界资源地进程必须等待。**当前访问临界资源的进程访问结束,释放该资源之后,另一个进程才能去访问临界资源。

3 信号量机制

用户进程可以通过使用操作系统提供的一对原语来对信号量进行操作,从而很方便地实现进程互斥与进程同步。

原语:是一种特殊的程序段,其执行只能一气呵成,不可被中断。原语是由关中断/开中断指令实现的。
信号量: 其实就是一个变量(可以是一个整型变量,也可以是更复杂的记录型变量),可以用一个信号量来表示系统中某种资源的数量。

上面提到的操作系统提供的一对原语,是指:wait(S)原语和signal(S)原语。这一对原语常被分别称之为 P、V 操作wait(S)signal(S)两个操作分别写为 P(s)V(S)

3.1 整型信号量

用一个整型变量作为信号量,用来表示系统中某种资源的数量。

下面用一个例子来说明:某计算机系统中有一台打印机。

int S = 1;             // 初始化整型信号量S,表示当前系统中可用的打印机资源数void wait(int S)       // wait原语,相当于“进入区”
{while (S <= 0);    // 如果资源数不够,就一直循环等待--S;               // 如果资源数够,就占用一个资源
}void signal (int S)    // signal原语,相当于“退出区”
{++S;               // 使用资源后,在退出区释放资源
}/* 进程P0 */
...
wait(S);               // 进入区,申请资源
使用打印机资源          // 临界区,使用资源
signal(S);             // 退出区,释放资源
.../* 进程P1 */
...
wait(S);               // 进入区,申请资源
使用打印机资源          // 临界区,使用资源
signal(S);             // 退出区,释放资源
...
  • 微信搜索:编程笔记本
  • 微信搜索:编程笔记本
  • 微信搜索:编程笔记本

3.2 记录型信号量

上面介绍的整型信号量有一个缺陷是:当资源不可用时,执行原地盲等。为了弥补这个不足,人们又提出“记录型信号量”,即用记录型数据结构表示的信号量

/* 记录型信号量的定义 */
typedef struct {int value;            // 剩余资源数struct process *L;    // 等待队列
} semaphore;/* 某进程需要使用资源时,通过wait原语申请 */
void wait(semaphore S)
{--S.value;            // 拟占用资源if (S.value < 0) {    // 若资源不可用则阻塞等待资源block(S.L);}
}/* 进程使用完资源后,通过signal原语释放 */
void signal(semaphore S)
{++S.value;             // 释放资源if (S.value <= 0) {    // 若有进程正等待该类资源则将其唤醒wakeup(S.L);}
}

wait 中,若执行 --S.value 后其值小于 0 了,说明之前就已经是小于等于 0 了,也就是说没有可用的资源了,那么就使用 block 原语使进程从运行态进入阻塞态,并将其挂到信号量 S 的等待队列当中。

signal 中,若执行 ++S.value 后其值仍小于 0 ,说明仍然有进程在等待这种资源,那么就使用 wakeup 原语唤醒一个进程,通知其使用资源。

下面来看一个例子:某计算机系统中有两台打印机。

/* 初始化 */
typedef struct {int value;            // 2struct process *L;    // nullptr
} semaphore;/* 进程P0* 剩余资源数:1* 等待队列:空*/
...
wait(S);
使用打印机资源
signal(S);
.../* 进程P1* 剩余资源数:0* 等待队列:空*/
...
wait(S);
使用打印机资源
signal(S);
.../* 进程P2* 剩余资源数:-1* 等待队列:P2*/
...
wait(S);
使用打印机资源
signal(S);
...

在原语的表达中,wait(S) 可以记为 P(S) ,用于资源的申请; signal(S) 可以记为 V(S) ,用于资源的释放。

  • S.value 的初值表示系统中某种资源的数目。

  • 对信号量 S 的一次 P 操作意味着进程请求一个单位的该类资源,因此需要执行 --S.value ,表示资源数减少 1 个单位。当 S.value < 0 时表明该类资源早已分配完毕,因此进程调用 block 原语进行自我阻塞(放弃了处理机的使用权,这点不同于整型信号量)。

  • 对信号量 S 的一次 V 操作意味着进程释放一个单位的该类资源,因此需要执行 ++S.value ,表示资源数增加 1 个单位。若 S.value ≤ 0 表明依然有进程在等待该类资源,因此调用 wakeup 原语唤醒队列中的第一个进程。

  • 微信搜索:编程笔记本

  • 微信搜索:编程笔记本

  • 微信搜索:编程笔记本

4 用信号量实现进程互斥、同步

4.1 用信号量实现进程互斥

步骤:

  • (1) 分析并发进程的关键活动,划定临界区
  • (2) 设置互斥信号量 mutex ,初值为 1

我们将临界区视作为一种特殊的资源,并且只有 1 个这样的资源。只有其他进程退出(释放资源)临界区,另一个进程才能进入临界区。

  • 在临界区之前执行 P(mutex)
  • 在临界区之后执行 v(mutex)

上述步骤,在临界区之前执行 P(mutex) ,可以申请临界区资源,如果有这样的资源则占用,若没有则等待。当一个进程退出临界区时,先执行 V(mutex) 操作释放临界区资源,再判断是否有进程正在等待进入临界区,有的话则唤醒该进程。

4.2 用信号量实现进程同步

进程同步,即要求各并发执行的进程按要求有序地推进。

步骤:

  • 分析何处需要实现“同步关系”,即保证“一前一后”执行的两个操作
  • 设置同步信号量 S ,初始值为 0
  • 在“前操作”之后执行 V(S)
  • 在“后操作”之前执行 P(S)

为何要将信号量初始化为 0 呢?
因为,我们默认系统中没有这样的资源。那么我们在“后操作”之前执行 P 操作申请这样的资源就会失败,所以“后操作”不能被执行。只有在“前操作”完成之后,我们执行 V 操作,这样会导致我们“释放一个”该类资源(其实是凭空产生)。此时“后操作”等待的资源已具备,因而“后操作”得以顺利执行。因而实现了两个操作的前后顺序控制。

5 哲学家进餐问题

问题描述:

一张圆桌上坐着 5 名哲学家,每两个哲学家之间有一根筷子,桌子的中间是一份餐食。哲学家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响其他人。只有哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)进餐。如果要拿的筷子已在他人手上,则需等待。用餐完毕后,将筷子放回原处。

  • 关系分析
    系统中有 5 个哲学家进程,5 位哲学家与左右邻居对其中间筷子的访问是互斥关系
  • 整理思路
    每个哲学家需要同时持有两个互斥资源才能开始用餐。如何避免临界资源分配不当造成的死锁现象,是哲学家进餐问题的精髓。
  • 信号量设置
    定义互斥信号量数组 chopstick[5] = {1,1,1,1,1} 用于实现对 5 根筷子的互斥访问。

哲学家 i 左边的筷子编号为 i ,右边的筷子编号为 (i+1)%5 。

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]);    // 放右思考}
}

若 5 个哲学家并发地拿起了自己左手边地筷子,又在循环等待右手边地筷子,这就导致了死锁。

为了解决这个问题,我们可以做出一些限制:在同一时间,只允许一位哲学家尝试拿起筷子进餐。为此,我们需要设置一个初始值为 1 的互斥信号量。

semaphore chopstick[5] = {1, 1, 1, 1, 1};
semaphore mutex = 1;
Pi() {                                // 哲学家i的进程while (1) {P(mutex);                     // 互斥地取筷子P(chopstick[i]);              // 拿左P(chopstick[(i + 1) % 5]);    // 拿右V(mutex);                     // 已经取好筷子吃饭V(chopstick[i]);              // 放左V(chopstick[(i + 1) % 5]);    // 放右思考}
}
  • 微信搜索:编程笔记本
  • 微信搜索:编程笔记本
  • 微信搜索:编程笔记本

【操作系统】-- PV原语(哲学家进餐问题)相关推荐

  1. 【操作系统】进程-哲学家进餐问题

    王道考研学习笔记 问题分析 一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭.哲学 家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响他人.只有当哲学家饥饿时 ...

  2. java多线程--哲学家进餐问题(操作系统)(哲学家进餐问题:5个哲学家共用一张圆桌,分别坐在周围的5张椅子上,在圆桌上有5个碗和5只筷子(注意是5只筷子,不是5双),碗和筷子交替排列。)

    哲学家进餐问题:5个哲学家共用一张圆桌,分别坐在周围的5张椅子上,在圆桌上有5个碗和5只筷子(注意是5只筷子,不是5双),碗和筷子交替排列.他们的生活方式是交替地进行思考(thinking)和进餐(e ...

  3. 计算机操作系统学习之哲学家进餐问题

    一.问题描述 一个圆桌上坐着5名哲学家,每两个哲学家中间放着一根筷子,桌子中间放着一碗米饭. 只有当哲学家饥饿时,才会试图拿起左右两只筷子 如果筷子已经在他人的手里,则需等待 哲学家只有同时拿起两根筷 ...

  4. 操作系统 | PV操作七大经典问题 生产者消费者 读者写者 哲学家进餐 理发师理发睡觉 和尚打水 吸烟者 吃水果

    一.生产者消费者问题 生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案 ...

  5. PV操作典型——哲学家进餐问题

    目标 哲学家进餐是操作系统课程的典型PV操作问题.通过此问题学习: 1.熟悉Linux平台的基本编程 2.在Linux平台上通过系统调用使用线程.互斥量机制以及信号量机制等进行编程 哲学家进餐问题 内 ...

  6. 操作系统(四) | 经典进程的同步问题(生产者--消费者问题、哲学家进餐问题、读者--写者问题)

    文章目录 生产者--消费者问题 分析 实现 哲学家进餐问题 方法一:最多4人同时拿左筷子,最终保证一人能进餐 方法二:同时给左右筷子 解法1:AND信号量 解法2:信号量保护机制 方法三:让奇数先左后 ...

  7. 2.3.6 操作系统之进程同步与互斥经典问题(生产者-消费者问题、多生产者-多消费者问题、吸烟者问题、读者-写者问题、哲学家进餐问题)

    文章目录 0.前言 1.生产者-消费者问题 (1)问题描述 (2)问题分析 (3)如何实现? (4)实现互斥的P操作一定要在实现同步的P操作之后 (5)知识回顾与重要考点 2.多生产者-多消费者问题 ...

  8. 用PV操作写出一个不会出现死锁的哲学家进餐问题

    用PV操作写出一个不会出现死锁的哲学家进餐问题 用count限定只能有四个哲学家同时拿起第一根筷子.这样就不会出现没有筷子拿的死锁. semaphore stick[5]={1,1,1,1,1}; s ...

  9. java 第六次实验_操作系统第六次实验报告——使用信号量解决哲学家进餐问题...

    0 个人信息 张樱姿 201821121038 计算1812 1 实验目的 通过编程进一步了解信号量. 2 实验内容 在服务器上用Vim编写一个程序:使用信号量解决任一个经典PV问题,测试给出结果,并 ...

最新文章

  1. SPIEC-EASI的微生物网络构建示例
  2. 我们对时间的理解错了吗?
  3. 利用velocity.js将svg动起来
  4. java打印系统参数_Java学习(二十):获取系统参数
  5. cgi、wsgi、uwsgi 与 uWSGI
  6. python装饰器函数传参
  7. 看懂 ,学会 .NET 事件的正确姿势-简单版
  8. 【STM32】HAL库 STM32CubeMX教程六----定时器中断
  9. angularjs 循环完之后执行的事件
  10. b h em strong mark
  11. 让ssh跳转支持ipv6
  12. 用计算机探索ppt,《用计算器探索规律 2》ppt课件.ppt
  13. 精彩PuTTY 中文教程(解决乱码、X窗口、自动登陆等问题)
  14. 动效设计入门工具小手册(微交互)
  15. Android JetPack Room
  16. 谷歌聘请 macOS 老兵操盘新操作系统 Fuchsia OS
  17. js 12:00时间加上半小时,返回小时和分钟
  18. 如何批量给文件重命名?
  19. Oracle中SQL查询字段值为空时,指定默认值
  20. Kafka 的消费组时不时就会断开连接

热门文章

  1. 22考研390分上岸吉林大学计算机学硕详细复习策略
  2. 云栖大会|小米大数据运维管理体系的建设与实践
  3. go 使用gooxml 操作Excel
  4. JDK1.8 64位和32位安装包
  5. Cloudsim学习笔记——基本知识
  6. kindeditor富文本编译器
  7. jvisualvm连接tomcat
  8. 2020暑假集训 心得
  9. 1+X云计算平台运维与开发认证 资源
  10. Android 四大组件基本介绍