操作系统之进程篇(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再从内核缓冲 ...
随机推荐
- httpclient在抓取网页时出现速度慢的情况分析
问题: 最近在使用httpclient3.0 来做项目,在一台机器部署系统后,发现此机器比其它机器在抓取页面的速度上慢了大概4s,左右. 项目是部署在局域内网,所以代码中都是直接写的IP地址 .在使用 ...
- weblogic启动时日志重定向(nohup.out)
由于weblogic使用 nohup ./startWebLogic.sh & 启动时会将所有日志打印到nohup.out上,长此以往会导致该文件越来越大,不便于管理. 故下面介绍如何重 ...
- POJ1751--Highways(最小生成树,kauskal)
裸最小生成树.用kauskal做方便一些. 不得不说这么大数据用cin cout 真是作死..活该T那么多次... /***************************************** ...
- mongodb日志服务器方案
描述 目前要做的是多台服务器上的程序日志(如订购日志,交易日志,接口是否成功等)汇总到1个mongodb服务器,每日大约1亿的量,然后有图表实时展现,和报表展现日志信息 注意: 没有把所有日志放入1张 ...
- 在Linux系统中修改IP地址
在Linux系统中,通过编辑网络配置文件,设置系统IP地址,当然要在root权限下执行,具体步骤如下: 1.切换路径到/etc/sysconfig/network-scripts [root@Comp ...
- JAVA Serialization 序列化
最近在做Android 项目时用到了WebView,可悲的是,在html上有无数用户的操作,而这些操作被JS返回给了Android的内存中,当深层的Activity开启时,之前的Activity很可能 ...
- PAT 07-图6 旅游规划 (25分)
有了一张自驾旅游路线图,你会知道城市间的高速公路长度.以及该公路要收取的过路费.现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径.如果有若干条路径都是最短的,那么需要输出最便 ...
- Symfony VarDumper Component
Symfony VarDumper 类似 php var_dump() 官方文档写的安装方法 : 按照步骤 就可以在 running any PHP code 时候使用了 In order to h ...
- 从CR线下活动学到的:如何组织一个小的线下活动
作者:朱克锋 邮箱:zhukefeng@iboxpay.com 转载请注明出处:http://blog.csdn.net/linux_zkf 周末在腾讯组织了GR,活动达到了预期的收获,从这次活动我主 ...
- [Unity] How to stop camera rendering?
http://answers.unity3d.com/questions/147988/how-to-pause-the-main-camera-.html I would simply pause ...