Inter-Process Communication的缩写,含义是进程间通信,是指两个进程间交换数据的过程。

哲学家进餐问题

概述

  • 哲学家进餐/思考
  • 进餐需要两把叉子
  • 每次拿一把叉子
  • 如何预防死锁

问题简介

哲学家的生活包括两个不同的阶段:吃饭和思考

当一个哲学家觉得饿时,他就试图去取他左边和右边的叉子,每次拿一把,但是部分次序,如果成功地获得了两把叉子,他就吃一会儿,然后放下叉子继续思考。

关键的问题就是:为每个哲学家写一段程序来描述其行为而且不能思索,你可以做到吗?

一种不正确的解法

#define N 5		//哲学家的数目
void philosopher(int i) { //i是哲学家的编号,从0到4
while (true) {
think(); //哲学家正在思考
take_fork(i); //取左边的叉子
take_fork((i + 1) % N); //取右面叉子:%表示取模运算
eat(); //空心粉味道不错
put_fork(i); //把左面叉子放回桌子
put_fork((i + 1) % N) //把右面叉子放回桌子
}
}

但是当所有哲学家同时拿起左边的叉子,无法得到右边的叉子——死锁

程序稍作修改:所有的哲学家都同时拿起左叉,看到右叉不可用,又都放下左叉,等一会儿,又同时拿起左叉,如此这般,永远重复。对于这种情况,即所有的程序都在无限期地运行,但是都无法取得任何进展,就成为饥饿(starvation)。

对上例算法可做一点改进,它既不会死锁也不会饥饿:即,使用一个二进制信号量对五个think之后的语句进行保护。在开始拿叉子之前,哲学家先对信号量mutex执行down操作。在放回叉子后,他要对mutex执行up操作。

从理论上讲,该解法是可行的。但是从实力角度来看,有性能上的缺陷:任意时刻只能有一个哲学家进餐。而五把叉子实际上允许有两位哲学将同时进餐。

解法

#define N  5                        /* 哲学家数目 */
#define LEFT (i+N-1)%N /* i的左邻编号 */
#define RIGHT (i+1)%N /* i的右邻编号 */
#define THINKING 0 /* 哲学家在思考 */
#define HUNGRY 1 /* 哲学家试图拿起叉子 */
#define EATING 2 /* 哲学家进餐 */
typedef int semaphore; /* 信号量 */
int state[N]; /* 记录每位哲学家状态 */
semaphore mutex = 1; /* 临界区的互斥 */
semaphore s[N]; /* 每位哲学家一个信号量 */ /* i: 哲学家编号,从0到N-1 */
void philosopher(int i) {
while (TRUE) { /* 无限循环 */
think(); /* 哲学家思考 */
take_forks(i); /* 需要两个叉子, */
eat(); /* 哲学家进餐 */
put_forks(i); /* 将叉子放回到桌子上 */
}
} void take_forks(int i) {
down(&mutex); /* 进入临界区 */
state[i] = HUNGRY; /* 记录哲学家i处于饥饿状态 */
test(i); /* 尝试获取两把叉子 */
up(&mutex); /* 退出临界区 */
down(&s[i]); /* 如果得不到需要的叉子则阻塞 */
} void put_forks(i) {
down(&mutex); /* 进入临界区 */
state[i] = THINKING; /* 哲学家进餐完毕 */
test(LEFT); /* 测试左邻是否可以吃 */
test(RIGHT); /* 测试右邻是否可以吃 */
up(&mutex); /* 离开临界区 */
} void test(i) {
//我是饥饿的但是左右都不再吃的时候
if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) {
state[i] = EATING;
up(&s[i]);
}
}

上面给出的解法是没有死锁的,而且对于任意多位哲学家的情况都能获得最大的并行度。它使用一个数组state来记录哲学家是在吃饭、思考还是饿了。一个哲学家只有在两个邻座都不在进餐时,才允许转换到进餐状态。哲学家i的邻居是由宏LEFTRIGHT定义。

该程序使用了一个信号量数组,每个信号量对应于一位哲学家,这样,所需的叉子被占用时,饥饿的哲学家就可以被阻塞。注意每个进程将历程philosopher作为朱代码运行,而其他例程,如take_forksput_forkstest都只是普通的例程,而不是单独的进程。

读者/写者问题

另一个著名的问题是读者—写者问题,它建模了对数据库的访问。

例如,设想一个飞机定票系统,其中有许多竞争的进程试图读写其中的数据。多个进程同时读是可以接受的,但如果一个进程正在更新数据库,则所有其他进程都不能访问数据库,即使读操作也不行。这里的问题是:如何对读者和写者进行编程?

进程A操作 进程B操作 是否允许
允许
互斥
互斥
typedef int semaphore;
semaphore mutex = 1; /* 控制对RC的访问 */
semaphore db = 1; /* 控制对数据库的访问 */
int rc = 0; /* 正在读或想要读的进程数 */ void reader(void) {
while (TRUE) { /* 无限循环 */
down(&mutex); /* 排斥对RC的访问*/
rc = rc + 1; /* 又多了一个读者 */
/*如果这是第一个读者,那么......*/
if (rc == 1)
//只要有一个读者在读书编者就不能编书
//当前有进程在读取数据库
down(&db);
up(&mutex); /*恢复对RC的访问*/
read_data_base(); /*访问数据*/
down(&mutex); /*排斥对RC的访问*/
rc = rc - 1; /*读者又少了一个*/
/*如果这是最后一个读者,那么......*/
if (rc == 0)
up(&db);
use_data_read(); /*非临界区操作*/
}
} void writer(void) {
while (TRUE) {
think_up_data(); /*非临界区操作*/
down(&db); /*排斥访问*/
write_data_base(); /*修改数据*/
up(&db); /*恢复访问*/
}
}

第一个读者对信号量db执行DOWN。随后的读者给计数器rc加1。当读者离开时,它们递减这个计数器,而最后一个读者则对db执行UP这样就允许一个阻塞的写者可以访问数据库

设想当一个读者在使用数据库时,另一个读者也来访问数据库,由于同时允许多个读者同时进行读操作,所以第二个读者也被允许进入,同理第三个及随后更多的读者都被允许进入。

经典的IPC问题的更多相关文章

  1. 操作系统之进程篇(4)--经典进程间通信(IPC)问题

    1. 哲学家进餐问题: 问题描述: 五个哲学家在一个圆桌上进餐,每人的面前放了一盘意大利面,两个盘子之间有一个叉子,但是由于盘子里面的面条十分光滑,需要两个叉子才能进行就餐行为.餐桌的布局如下图所示: ...

  2. #干货#小微信贷风控中类IPC模式和集中审批模式

    浅析小微信贷风控中类IPC模式和集中审批模式 席占斌 常言道瑕不掩瑜,反过来讲瑜自然也不能掩瑕,看问题需要客观公正辩证. 在小微信贷中,风控模式依旧是核心,目前比较流行和占比较大的风控模式有很经典的I ...

  3. 一次完整的从webshell到域控的探索之路

    前言 内网渗透测试资料基本上都是很多大牛的文章告诉我们思路如何,但是对于我等小菜一直是云里雾里. 于是使用什么样的工具才内网才能畅通无阻,成了大家一直以来的渴求. 今天小菜我本着所有师傅们无私分享的精 ...

  4. Metasploit域渗透测试全程实录(终结篇)

    本文作者:i春秋签约作家——shuteer 前言 内网渗透测试资料基本上都是很多大牛的文章告诉我们思路如何,但是对于我等小菜一直是云里雾里.于是使用什么样的工具才内网才能畅通无阻,成了大家一直以来的渴 ...

  5. python多进程-----multiprocessing包

    multiprocessing并非是python的一个模块,而是python中多进程管理的一个包,在学习的时候可以与threading这个模块作类比,正如我们在上一篇转载的文章中所提,python的多 ...

  6. Linux 进程间通信(一)(经典IPC:消息队列、信号量、共享存储)

    有3种称作XSI IPC的IPC:消息队列.信号量.共享存储.这种类型的IPC有如下共同的特性. 每个内核中的IPC都用一个非负整数标志.标识符是IPC对象的内部名称,为了使多个合作进程能够在同一IP ...

  7. Linux 进程间通信(一)(经典IPC:管道、FIFO)

    管道 管道是Unix系统IPC的最古老方式,有两种局限性: (1)   历史上它们是半双工的(即数据只能在一个方向上流动),虽然现在某些系统提供了全双工管道,但是为了可移植性,不要抱有绝对的全双工假设 ...

  8. Classic IPC Problems 经典的进程间通信问题

    The Producer-Consumer Problem Presenter Notes: 生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bo ...

  9. IPC 经典问题:Reader & Writer Problem

    完整代码实现: #include <stdio.h> #include <unistd.h> #include <time.h> #include <stdl ...

随机推荐

  1. How to use the functions of apply and call

    Although  apply and  call  can implement same function. However, there is a litter different between ...

  2. bzoj2296【POJ Challenge】随机种子*

    bzoj2296[POJ Challenge]随机种子 题意: 求一个≤10^16的数,使这个数包含123456789且为x的倍数.x≤1000000. 题解: 16-6刚好等于10.因此我们可以直接 ...

  3. java中AQS源码分析

    AQS内部采用CLH队列.CLH队列是由节点组成.内部的Node节点包含的状态有 static final int CANCELLED =  1; static final int SIGNAL    ...

  4. 一张图就可以完美解决Java面试频次最高、GG最高的题目!快点收藏

    如果要问Java面试频次最高的题目,那么我想应该是HashMap相关了. 提到HahMap,必然会问到是否线程安全?然后牵扯出ConcurrentHashMap等,接着提及1.7和1.8实现上的区分, ...

  5. Eclipse默认快捷键说明

    Ctrl+1 快速修复(最经典的快捷键,就不用多说了)Ctrl+D: 删除当前行 Ctrl+Alt+↓ 复制当前行到下一行(复制增加)Ctrl+Alt+↑ 复制当前行到上一行(复制增加)Alt+↓ 当 ...

  6. 02 安装net-tools工具

    01 登录虚拟机,没错,还是那个熟悉的黑窗口 02 输入用户名密码(我还是习惯使用root用户,因为,它可以为所欲为) 小知识:注意红色框内的符号: 一般用户为限制用户,符号为:$ 超级用户,为无限制 ...

  7. git安装并与远程仓库关联相关配置

    git是当前最流行的版本控制系统,下面简单记录一下git的安装及其与远程仓库的关联. git安装 打开git官网,下载对应的安装包. 双击运行安装包,安装过程中可以直接选择默认配置,一路next下去. ...

  8. Java中goto标签的使用

    编写此文仅为以后可以复习. 最近在自学Java核心技术(很好的书,推荐!!),也是第一次从上面了解了goto,或许只是浅层了解. 错误之处希望大佬们给予批评与建议!!谢谢!!! Java核心技术中就提 ...

  9. Spring Security 实战干货:图解用户是如何登录的

    1. 前言 欢迎阅读Spring Security 实战干货系列文章,在集成Spring Security安全框架的时候我们最先处理的可能就是根据我们项目的实际需要来定制注册登录了,尤其是Http登录 ...

  10. python基础--闭包、装饰器

    装饰器的详细使用 (1)小知识点补充 在这里我们先学一个简单的知识点. li = ['alex', '银角', '女神', 'egon', '太白'] for i in enumerate(li): ...