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)问题的更多相关文章

  1. 【windows 操作系统】进程间通信(IPC)简述|无名管道和命名管道 消息队列、信号量、共享存储、Socket、Streams等

    一.进程间通信简述 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进 ...

  2. Android进程间通信IPC

    一.IPC的说明 IPC是Inter-Process Communication的缩写,含义为进程间通信或跨进程通信,是指两个进程之间进行数据交换的过程. IPC不是Android独有的,任何一个操作 ...

  3. Python3 与 C# 并发编程之~ 进程篇

      上次说了很多Linux下进程相关知识,这边不再复述,下面来说说Python的并发编程,如有错误欢迎提出- 如果遇到听不懂的可以看上一次的文章:https://www.cnblogs.com/dot ...

  4. 进程间通信IPC -- 管道, 队列

    进程间通信--IPC(Inter-Process Communication) 管道 from multiprocessing import Pipecon1,con2 = Pipe()管道是不安全的 ...

  5. python 操作系统和进程

    一. 操作系统介绍 多道程序系统 多道程序设计技术       所谓多道程序设计技术,就是指允许多个程序同时进入内存并运行.即同时把多个程序放入内存,并允许它们交替在CPU中运行,它们共享系统中的各种 ...

  6. 进程间通信IPC、LPC、RPC

    进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法.进程是计算机系统分配资源的最小单位.每个进程都有自己的一部分独立的系 ...

  7. 【Android】进程间通信IPC——AIDL

    AIDL官网定义AIDL(Android 接口定义语言)与您可能使用过的其他 IDL 类似. 您可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口. 在 Androi ...

  8. 操作系统:进程管理和IO控制

    一.进程管理 进程管理包括进程控制,进程调度,进程同步与通信,死锁控制四个内容. (一)进程控制 进程是操作系统中运行的基本单位,包括程序段,数据段和进程控制段.操作系统通过进程控制块(PCB)管理进 ...

  9. 进程间通信IPC之--共享内存

    每个进程各自有不同的用户地址空间,任何一个进 程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲 区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲 ...

随机推荐

  1. 使用Hadoop打造私有云盘之API操作

    项目介绍:使用hadoop实现云盘的增删读获取列表功能,hadoop不支持数据修改,特性是一次写入多次读取.主流的网盘也不支持该功能.今天我们用hdfs的FileSystem实现这些操作. 1.上传功 ...

  2. puppet重申证书

    直接上步骤,由于测试用的是PE3.X版本,在网上搜的命令几乎与PE相关的puppet命令不同了, 1.在PE-Client操作,停止pe-puppet,pe-mcollective资源; puppet ...

  3. C# WinForm多线程(三)---- Control.Invoke[转]

    开发中遇到更新Winform中控件假死问题,通过看了这篇文章了解了原理,感谢!此处标记起来,以备查看! 原文地址:http://www.cnblogs.com/joechen/archive/2009 ...

  4. nginx + lua 构建网站防护waf(一)

    最近在帮朋友维护一个站点.这个站点是一个Php网站.坑爹的是用IIS做代理.出了无数问题之后忍无可忍,于是要我帮他切换到nginx上面,前期被不断的扫描和CC.最后找到了waf这样一个解决方案缓解一下 ...

  5. Google正确搜索方法

    以下是目前所有的Google搜索命令语法,它不同于Google的帮助文档,因为这里介绍了几个Google不推荐使用的命令语法.大多数的Google搜索命令语法有它特有的使用格式,希望大家能正确使用.我 ...

  6. 字符串匹配之KMP算法

    KMP算法使用前缀函数来模拟有限自动机的后缀函数,前缀函数通过计算模式与其自身的偏移匹配的信息,本身的证明很复杂,关键在于弄懂其核心思想,下面就不赘述了,仅仅贴出代码: #include <io ...

  7. 如何通过进程名获取进程ID

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:如何通过进程名获取进程ID.

  8. hdu2157之矩阵快速幂

    How many ways?? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  9. get-random生成电话号码

    "138"+((0..9|Get-Random -count 10) -join $null) From:http://blog.csdn.net/shrekz/article/d ...

  10. JavaScript【5】高级特性(作用域、闭包、对象)

    笔记来自<Node.js开发指南>BYVoid编著 1.作用域 if (true) { var somevar = 'value'; } console.log(somevar); Jav ...