操作系统学习- 二 -同步-信号量(semaphore)
信号量(semaphore)
锁的机制保证了临界区的基本要求,也帮助我们初步解决了同步互斥问题。但是基础的锁只能解决两个进程之间的同步问题在实际开发中,我们常常会遇到类似一个进程要与多个进程同步的情况,比如读和写的操作。如果读操作一次只能执行一个,那样的话效率太低,所以只是单纯的读取的话我们可以让系统分配更多资源并发执行,只锁住一个写操作(写有中断操作,所以相对复杂)。
为此我们先引出信号量的概念;
一个信号量是一个整形S,表示系统所剩资源。
除此以外,我们还需要两个函数,wait()和signal()。
这两个函数名来自于信号量概念的提出者 Dijkstra(图灵奖获得者,大佬)所最开始取名的P(proberen,表减少)和V(verhogen, 表增加)。在这里,wait()表示减少一个信号量,signal()表示增加。
wait(S){while(S<=0);S--;
}
signal(S){S++;
}
到这里,就完成了wait和signal的基本定义。可以看出,当进来的信号是为0或1的时候,这个机制的功能就和之前所学的锁几乎没有什么区别,对于这种情况,我们又称为二进制信号量,与之相对应的, 叫计数信号量。
信号量的实现
如果使用上文的实现方法实现互斥锁的话将会陷入忙等(busy waiting),所以我们在这里换一个思路,采用队列的方式存储等待中的进程,block()方法挂起(suspand)调用它的进程,wakup()用来重新启动进程。
ps:由于我查了半天都没有找到block()和wakeup()的系统调用所以我怀疑书上写的是类c的伪代码。
//信号量定义
typedef struct{int value;struct process*list;//等待队列
}semaphore;
wait(semaphore *S){s->value--;if(s->value<0){////将当前进程放到队列中//block();//非系统API,需自己实现}
}
signal(semaphore *S){S->value++;if(S->value<=0){//移出队列wakeup();}
}
死锁
当有两个或以上的进程等待一个事件,且该事件是由这些正在等待的进程来产生的话,就会出现死锁(deadlock)。
P0 P1
wait(S) wait(Q)
wait(Q) wait(S)signal(S) signal(Q)
signal(Q) signal(S)
像上述这种情况p0执行S,p1执行Q,可如果不先signal其中一个的话,那两边就同时处在了一种等待状态,这就行成了死锁。
死锁问题是操作系统问题的一个大快,这里只是简单的介绍。
哲学家就餐问题(dining-philosophers problem)
哲学家就餐问题是一个经典的同步问题,这里采用原文描述:
假设有 5 个哲学家,他们的生活只是思考和吃饭。这些哲学家共用一个圆桌,每位都有一把椅子。在桌子中央有一碗米饭,在桌子上放着 5 根筷子(图 1 )。
图 1 就餐哲学家的情景
当一位哲学家思考时,他与其他同事不交流。时而,他会感到饥饿,并试图拿起与他相近的两根筷子(筷子在他和他的左或右邻居之间)。一个哲学家一次只能拿起一根筷子。显然,他不能从其他哲学家手里拿走筷子。当一个饥饿的哲学家同时拥有两根筷子时,他就能吃。在吃完后,他会放下两根筷子,并开始思考。
哲学家就餐问题是一个经典的同步问题,这不是因为其本身的实际重要性,也不是因为计算机科学家不喜欢哲学家,而是因为它是大量并发控制问题的一个例子。这个代表型的例子满足:在多个进程之间分配多个资源,而且不会出现死锁和饥饿。
一种简单的解决方法是每只筷子都用一个信号量来表示。一个哲学家通过执行操作 wait() 试图获取相应的筷子,他会通过执行操作 signal() 以释放相应的筷子。
因此,共享数据为:
semaphore chopstick[5];
其中,chopstick 的所有元素都初始化为 1。哲学家 i 的结构如下所示:
do {wait(chopstick[i]);wait(chopstick[(i+1) % 5]);/* eat for awhile */signal(chopstick[i]);signal(chopstick[(i+1) % 5]);/* think for awhile */
} while (true);
虽然这一解决方案保证两个邻居不能同时进食,但是它可能导致死锁,因此还是应被拒绝的。假若所有 5 个哲学家同时饥饿并拿起左边的筷子。所有筷子的信号量现在均为 0。当每个哲学家试图拿右边的筷子时,他会被永远推迟。
死锁问题有多种可能的补救措施:
- 允许最多 4 个哲学家同时坐在桌子上。
- 只有一个哲学家的两根筷子都可用时,他才能拿起它们(他必须在临界区内拿起两根 辕子)。
- 使用非对称解决方案。即单号的哲学家先拿起左边的筷子,接着右边的筷子;而双 号的哲学家先拿起右边的筷子,接着左边的筷子。
管程
管程可以简单的理解为能够放弃当前代码临界区的进程。
除了进程队列以外,管程还加入了条件变量(condition variable),条件变量的作用是把当前进程中不满足条件的进程强制强制block,让其他进程先运行,当条件满足时在返回当前进程继续。
哲学家就餐问题管程解决方案
此段为书上写的伪代码,主要思路是只有同时满足两边的哲学家都不是在EATING的状态时才可以拿起筷子。但这个方案只解决了互斥问题,不会导致死锁,但可能导致某个哲学家过度饥饿而死。
monitor dp{enum {THINKING, HUNGRY, EATING} state[5];condition self[5];void pickup(int i){state[i] = HUNGRY;test(i);if (state[i] != EATING)self[i].wait();}void putdown(int i){state[i] = THINKING;test((i-1) % 5);test((i+1) % 5);}void test(int i){if ((state[(i-1)%5] != EATING) && (state[(i+1)%5] != EATING)){state[i] = EATING;self[i].signal();}}initialization_code(){for (int i = 0; i < 5; i++)state[i] = THINKING;}
}
一个改进版的 Monitor 解决方案如下。筷子本身并不属于 monitor 的一部分,否则同时只能有一个哲学家在进餐。代码中 NUM_PHILS 是哲学家数目。此代码解决了哲学家饥饿问题,来自西弗吉尼亚大学。
monitor dp{condition self[NUM_PHILS];enum states {THINKING, HUNGRY, EATING} state[NUM_PHILS-1];int index;initialization_code(){for (index=0; index<NUM_PHILS; index++)flags[index] = THINKING;}void pickup(int i) {state[i] = HUNGRY;if ((state[(i-1)%NUM_PHILS] != EATING) &&(state[(i+1)%NUM_PHILS] != EATING))state[i] = EATING;else {// 挂起,等待相邻哲学家改变状态时唤醒self[i].wait;// wait 操作被唤醒后可以改变状态为 EATINGstate[i] = EATING;}}void putdown(int i) {state[i] = THINKING;// 唤醒左侧哲学家if ((state [(i-1)%NUM_PHILS] == HUNGRY) &&(state [(i-2)%NUM_PHILS] != EATING))self[(i-1)%NUM_PHILS].signal;// 唤醒右侧哲学家if ((state [(i+1)%NUM_PHILS] == HUNGRY) &&(state [(i+2)%NUM_PHILS] != EATING))self[(i+1)%NUM_PHILS].signal;}
}
操作系统学习- 二 -同步-信号量(semaphore)相关推荐
- 秒杀多线程第八篇 经典线程同步 信号量Semaphore
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...
- 经典线程同步 信号量Semaphore
信号量Semaphore常用有三个函数,使用很方便.下面是这几个函数的原型和使用说明. 第一个 CreateSemaphore 函数功能:创建信号量 函数原型: HANDLE CreateSemaph ...
- 哈工大操作系统学习笔记十——信号量与死锁
哈工大os学习笔记十(信号量与死锁) 文章目录 哈工大os学习笔记十(信号量与死锁) 一. 信号量临界区保护 1.为什么要保护信号量 2.临界区 3.保护信号量的方法 3.1 轮换法 3.2 标记法 ...
- Linux 学习笔记16 信号量
Linux 学习笔记16 信号量Semaphore 信号量概念 信号量(或信号灯)是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语. 信号量是控制进程(或线程)同步(谁先执行,谁后执行 ...
- windows 多线程 (六) 信号量Semaphore
首先也来看看如何使用信号量,信号量Semaphore常用有三个函数,使用很方便.下面是这几个函数的原型和使用说明. 第一个 CreateSemaphore 函数功能:创建信号量 函数原型: HANDL ...
- 操作系统(二十二)用信号量机制实现进程互斥、同步、前驱关系
2.3.5 用信号量机制实现进程互斥.同步.前驱关系 目录 2.3.5 用信号量机制实现进程互斥.同步.前驱关系 2.3.5.1 用信号量机制实现进程互斥 2.3.5.2 用信号量机制实现进程同步 2 ...
- 操作系统学习-6. 信号量
写在前面: 这一篇博客将讨论信号量(Semaphores)机制.将学习三种基本类型的信号量,然后将用信号量实现互斥与前趋两种进程关系. 该机制由荷兰学者 Dijkstra 提出,是一种卓有成效的进程同 ...
- 【操作系统学习笔记】—— 【二】进程、线程、死锁
本文参考: JavaGuide 王道考研-操作系统 CS-Notes 文章目录 一.进程的概念.组成.特征 1. 概念 2. 进程的组成 PCB 程序段 数据段 3. 进程的特征 二.进程的状态 三. ...
- 南京邮电大学操作系统实验二:线程的互斥与同步
实验原理及内容 基于互斥锁的临界区管理 使用编辑器gedit 2_1.c,新建一个2_1.c源文件,创建双线程并发完成订票操作,输入后面的范例代码: #include <stdio.h> ...
最新文章
- Java基础看jvm,JAVA基础知识|java虚拟机(JVM)
- 深入理解Spark Streaming执行模型
- Jquery中post与get之间的区别详细介绍
- android怎么换小米系统更新,miui8怎么更新 miui8更新升级方法汇总
- nginx php 扩展,源码安装Nginx+PHP-FPM及扩展
- vc通过ADO连接sql server 2000的核心代码
- ios中生成uuid
- jsf刷新页面_JSF页面生命周期管理
- MyBatis官方文档——XML配置部分
- Java学习笔记目录索引 (持续更新中)
- 多易教育大数据课程学费调整通知
- minidump详细介绍
- 范美忠妻子:美忠是个好男人
- 【前端知识之JS】JS的作用域链
- Hexo NexT 评论系统 Valine 的使用
- 记录使用4G模块SIM7600CE的一些问题
- 深圳大学大学计算机考试科目,深圳大学计算机考研科目有哪些
- rk 平台实现 otg 软切换
- 用php造了一个地址自动识别功能
- 计算机专业看重CPU还是显卡,电脑大神告诉你处理器和显卡哪个重要