操作系统之进程篇(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再从内核缓冲 ...
随机推荐
- key_t键和ftok函数
系统建立IPC通讯(如消息队列.共享内存时)必须指定一个ID值.通常情况下,该id值通过ftok函数得到. ftok原型如下: key_t ftok( char * fname, int id ) f ...
- nyoj 27 水池数目
水池数目 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 南阳理工学院校园里有一些小河和一些湖泊,现在,我们把它们通一看成水池,假设有一张我们学校的某处的地图,这个地 ...
- JS多态
面向对象语言有三大特征,前面介绍了封装和继承,那么JS作为一门面向对象语言,有多态么,又怎么实现多态呢? 我们先看看多态的概念: 多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果. ...
- JAVA之装饰者模式
装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰模式的结构 装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任.换言之,客户 ...
- MSSQLSERVER数据库- 杂记
查看语句执行时间: SET STATISTICS IO ONSET STATISTICS TIME ON SELECT * FROM dbo.NewsInfo Update的表关联例子: UPDATE ...
- SQL Server:OA权限管理设计的实现 下
SQL Server:OA权限管理设计的实现 下 OA系统权限管理设计方案 不同职责的人员,对于系统操作的权限应该是不同的.优秀的业务系统,这是最基本的功能. 可以对“组”进行权限 ...
- twitter的snowflake算法(C#版本)
转自:http://blog.csdn.net/kinwyb/article/details/50238505 使用twitter的snowflake算法生成唯一ID. 在分布式系统中,需要生成全局U ...
- 【JAVA - 基础】之Annotation注解浅析
注解在JAVA中,尤其是一些ORM框架(如Hibernate等)中是比较常用的一种机制. 注解是JAVA 1.5之后引入的新功能,正确来说是反射的一部分,没有反射,注解也就无法正常使用.注解可以理解成 ...
- FrankFan7你问我答之一
作者:范军 (Frank Fan) 新浪微博:@frankfan7 微信:frankfan7 最近网友留言很多,你既然看得起我问了,我就说说个人浅见.看看就行了,也别认真. Q:你具体工作是什么? ...
- 【转】Java Thread.join()详解
http://www.open-open.com/lib/view/open1371741636171.html 一.使用方式. join是Thread类的一个方法,启动线程后直接调用,例如: ? 1 ...