操作系统之进程篇(4)--经典进程间通信(IPC)问题
1. 哲学家进餐问题:
问题描述: 五个哲学家在一个圆桌上进餐,每人的面前放了一盘意大利面,两个盘子之间有一个叉子,但是由于盘子里面的面条十分光滑,需要两个叉子才能进行就餐行为。餐桌的布局如下图所示:
假设哲学家的生活中只有两个活动:吃饭和思考[吃饭维持自身之生存,思考探究生存之意义],当然这样的哲学家在现实之中是不存在的。当一个哲学家在殚精竭虑之时,饥饿感随之而来,这是他会拿起左右手边的两个叉子来想享用这俗世之中的美味。酒足饭饱之后,又"躲进小楼成一统,管他春夏与秋冬"去了。问题是: 怎样才能保证每个哲学家都能拿到两个筷子而不会出现死锁的情形呢?[一个典型的死锁情形是: 每个哲学家同时拿起右手边的叉子,都得不到左手边的叉子。]
上述死锁情形可以通过下面的代码描述:
#define N 5 /* number of philosophers */ void philosopher(int i) /* i: philosopher number: from 0 to 4 */
{
while(TRUE):
{
think(); /* philosppher is thinking */
take_fork(i); /* take the left fork */
take_fork((i+) % N); /* take right fork; % is a modulo operator */
eat(); /* yum-yum, spaghetti */
put_fork(i); /* put back the left fork on the tabel */
put_fork((i+) % N); /* put back the right fork on the table */
}
}
那么如何解决这个问题呢? 我们经过一番思考得到下面一些方案:
方案1: 当一个哲学家拿起左手边的叉子的时候,判断他是否可以拿到右手边的叉子; 如果右手边的叉子正被别人使用着,那么他就放下左手边的叉子,等待一段时间之后,重复上面的过程。[这个方案仍然解决不了死锁问题,如果五个哲学家同时执行上述过程,都得不到叉子,然后放下,等待相同的一段时间后在重复上述过程......然后是没完没了的重复:Until the end of the world(直到世界末日)]
方案2 : 将上述方案中等待一定量的时间改为等待随机一段时间。[看上去这个方案可以解决死锁问题,但是我们不能依赖这个随机值,况且计算机世界里面哪有什么绝对的随机数呢?我们不能因为一件事发生的概率极小而断定这件事不会发生,这在赌徒们的牌桌上是可以存在的,但作为一个理性的人,这个念头是荒谬可笑的,就像是将头埋在沙子里的鸵鸟。试想一段控制核电站堆芯工作的程序使用上述策略,那么人类的灭亡也就不远了。]
方案3: 在上面的代码的think()语句作为一个关键代码段,用一个互斥信号量(mutex)来控制每个哲学家进程对关键代码段的访问。[从理论上来说,这个方案是可行的;但从实际效率上考虑,这个方案是不合理的:一段时间内至多只有一个哲学家在进餐。]
方案4:
#define N 5 /* number of philosophers */
#define LEFT (i-1) % N /* number of i's left neighbor */
#define RIGHT (i+1) % N /* number of i's right neighbor */
#define THINKING 0 /* philosopher is thinking */
#define HUNGRY 1 /* philosopher is trying to get forks */
#define EATING 2 /* philosopher is eating */ typedef int semaphore; /* semaphores are a special kind of int */
int state[N]; /* array to keep track of every philospphers' state */
semaphore mutex = ; /* mutual exclusion for critical regions */
semaphore s[N]; /* one semaphore per philosopher */ void philosopher(int i)
{
while(TRUE)
{
think();
take_forks(i);
eat();
put_forks(i);
}
} void take_forks(int i)
{
down(&mutex);
state[i] = HUNGRY;
test(i);
up(&mutex);
down(&s[i]); [如过没有请求到叉子,那么阻塞]
} void put_forks(int i)
{
down(&mutex);
state[i] = THINKING;
test(LEFT(i));
test(RIGHT(i));
up(&mutex);
} void test(i)
{
if(state[i] == HUNGRY && state[LEFT(i)] != EATING && state[RIGHT(i)] != EATING)
{
state[i] = EATING;
up(&s[i]);
}
}
[注意这里并没有将一个哲学家所能执行的所有动作都放在一个关键代码段中,而是用互斥信号量控制take_forks和get_forks过程,以保证每次只有一个哲学家在申请叉子或释放叉子。但可以有多个哲学家处于EATING的状态。]
2. 读者和写者问题:
哲学家进餐问题描述的是多个进程对有限资源的互斥访问问题,另外一个问题是"读者--写者"问题,描述的是多个进程对数据访问的问题。
要求: 可以有多个用户同时读取数据,但一个时刻只能有一个用户在更新数据,并且在更新数据期间,不容许其他用户更新或读取数据。
下面是解决读者--写者问题的一个方案:
typedef int semaphore;
semaphore mutex = ; /* [控制对读者计数器rc的访问] */
semaphore db = ; /* [控制对数据的访问] */
int rc = ; /* [读者数计数器,初始化为0] */ void reader(void)
{
while(true) /* ["久到离谱,一直停不下来"] */
{
down(&mutex); /* [对读者数计数器互斥访问] */
rc = rc + ; /* [读者计数器+1] */
if (rc == ) down(&db); /* [当有用户在读取数据时,没有人可以修改数据] */
up(&mutex); read_data(); /* [读取数据] */ down(&mutex);
rc = rc - ; /* [读取数据完毕,离开] */
if(rc == ) up(&db); /* [如果当前没有用户读取数据,那么容许其他用户的修改] */
up(&mutex);
use_data_read();
}
} void writer(void)
{
while(true) /* [you konw that] */
{
think_up_data(); /* [准备好要更新的数据] */
down(&db);
write_data();
up(&db);
}
}
当然,这个方案也存在一些问题:当有读者在读取数据,并且有其他读者源源不断的到来的时候,写者进程将永远处于阻塞状态。[当每2秒出现一个读者,而每个读者的平均读取时间是5秒时就会出现这个情形。]
解决这个问题的一种方案是当写者进程出现时,写者进程之后到来的读者进程都被阻塞,当先前读者读取完毕后写者就可以修改数据了。这个方案虽然可以保证写者不会处于饥饿状态,但却以破坏系统中程序的并发性为代价。
另一种解决方案参见"读者--写者"问题的提出者Courtois的论文: cs.nyu.edu/~lerner/spring12/Read04-ReadersWriters.pdf;
3. 睡觉的理发师问题:
问题描述: 一个理发店有1个理发师,一把理发椅,和有n把椅子的休息区。如果没有客户过来理发,这个理发师久躺在理发椅上呼呼大睡[工作无聊乎?]; 当一个客户到来时,它必须侥幸这个理发师[如果这个家伙在现实生活中估计早就被炒鱿鱼了!]。当理发师理发的时候如果由其他客户到来,那么这个客户可以选择在休息区等待(当休息区未满的时候); 也可以选择离开(当休息区没有空闲座位的时候)。问题是:如何编写客户和理发师代码而不出现竞争情形?下面是这个问题的解决方案:
#define CHAIRS 5 /* [等待区座椅数目] */ typedef int semaphore; semaphore customers = ; /* [客户信号量:初始化为0] */
semaphore barbers = ; /* [理发师信号量: 初始化为0] */
semaphore nutex = ; /* [互斥信号量: 初始化为1] */ void barber(void)
{
while(true)
{
down(&customers); /* [如果customers = 0 那么理发师就回去睡觉] */
down(&mutex); /* [获取对waiting变量的访问权限] */
waiting = waiting - ;
up(&barbers); /* [一个理发师来给用户理发] */
up(&mutex);
cut_hair();
}
} void customer(void)
{
down(&mutex);
if(waiting < CHAIRS) /* [如果空闲区没有椅子,那么客户离开] */
{
waiting = waiting + ; /* [增加等待理发的客户数目] */
up(customers); /* [唤醒理发师] */
up(mutex);
down(barbers); /* [使用一个理发师] */
get_haircut();
}
else
{
up(&mutex);
}
}
理发师问题可以做这样的类比: 操作系统中提供服务的进程有限[理发师],而请求服务的进程无限[顾客]。以优先之服务供给无限之需求,其中公平和效率兼顾的考量不可缺少!
Ok! This is the end of this artile! Thank you very much for reading it! Good luck!
操作系统之进程篇(4)--经典进程间通信(IPC)问题的更多相关文章
- 【windows 操作系统】进程间通信(IPC)简述|无名管道和命名管道 消息队列、信号量、共享存储、Socket、Streams等
一.进程间通信简述 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进 ...
- Android进程间通信IPC
一.IPC的说明 IPC是Inter-Process Communication的缩写,含义为进程间通信或跨进程通信,是指两个进程之间进行数据交换的过程. IPC不是Android独有的,任何一个操作 ...
- Python3 与 C# 并发编程之~ 进程篇
上次说了很多Linux下进程相关知识,这边不再复述,下面来说说Python的并发编程,如有错误欢迎提出- 如果遇到听不懂的可以看上一次的文章:https://www.cnblogs.com/dot ...
- 进程间通信IPC -- 管道, 队列
进程间通信--IPC(Inter-Process Communication) 管道 from multiprocessing import Pipecon1,con2 = Pipe()管道是不安全的 ...
- python 操作系统和进程
一. 操作系统介绍 多道程序系统 多道程序设计技术 所谓多道程序设计技术,就是指允许多个程序同时进入内存并运行.即同时把多个程序放入内存,并允许它们交替在CPU中运行,它们共享系统中的各种 ...
- 进程间通信IPC、LPC、RPC
进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法.进程是计算机系统分配资源的最小单位.每个进程都有自己的一部分独立的系 ...
- 【Android】进程间通信IPC——AIDL
AIDL官网定义AIDL(Android 接口定义语言)与您可能使用过的其他 IDL 类似. 您可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口. 在 Androi ...
- 操作系统:进程管理和IO控制
一.进程管理 进程管理包括进程控制,进程调度,进程同步与通信,死锁控制四个内容. (一)进程控制 进程是操作系统中运行的基本单位,包括程序段,数据段和进程控制段.操作系统通过进程控制块(PCB)管理进 ...
- 进程间通信IPC之--共享内存
每个进程各自有不同的用户地址空间,任何一个进 程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲 区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲 ...
随机推荐
- HW3.21
import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...
- [Locked] Range Sum Query 2D - Mutable
Range Sum Query 2D - Mutable Given a 2D matrix matrix, find the sum of the elements inside the recta ...
- POJ2192 - Zipper(区间DP)
题目大意 给定三个字符串s1,s2,s3,判断由s1和s2的字符能否组成字符串s3,并且要求组合后的字符串必须是s1,s2中原来的顺序. 题解 用dp[i][j]表示s1的前i个字符和s2的前j个字符 ...
- Windows Service installutil 部署时,出错的解决办法-原创
出错信息如下: ---------------------------------------------------------- ~~~~~... The Rollback phase compl ...
- (qsf文件 、 tcl文件 和 csv(txt)文件的区别) FPGA管脚分配文件保存、导入导出方法
FPGA管脚分配文件保存方法 使用别人的工程时,有时找不到他的管脚文件,但可以把他已经绑定好的管脚保存下来,输出到文件里. 方法一: 查看引脚绑定情况,quartus -> assignment ...
- python 一些重要的内建异常类
- 以管理员身份启动ClickOnce部署的应用程序
ClickOnce方式部署应用简单方便,估计很多人都用过,但这种方式存在一定的“缺陷”,即以管理员方式启动应用的问题,虽然出于安全考虑可以理解,但给需要管理员权限才能正常运行的程序带来了一定的麻烦,这 ...
- chrmoe debug
一.右击,点击审查元素 二.打开后,每个tab是干什么用的呢? 三.调试样式 选中Elemes后,右边有个窗口,这里是CSS的样式,可以直接选中元素,并且在这里修改样式.这样调试很方便,效率也很高.当 ...
- 14周事情总结-机器人-大数据hadoop
14周随着考试的进行,其他该准备的事情也在并行的处理着,考试内容这里不赘述了 首先说下,关于机器人大赛的事情,受益颇多,机器人的制作需要机械和电控两方面 昨天参与舵机的测试,遇到的问题:舵机不动 排查 ...
- 每天进步一点达——MySQL——myisampack
一. 简单介绍 myisampack是一个压缩使用MyISAM引擎表的工具,通常会压缩40%~70%,当须要訪问数据.server会将所须要的信息读入到内存中.所以当訪问详细记录时,性能 ...