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. Django框架07 /cookie和session

    Django框架07 /cookie和session 目录 Django框架07 /cookie和session 1. django请求生命周期 2. cookie 3. session 4. 总结 ...

  2. dataframe一次小实验

    老师搞不清,一个ndarray[df['columns']==a],返回的是什么,目前看来应该是df[] == a 的索引

  3. 使用QtCreator遇到的一些问题

    0. 背景 最近在学习QtCreator(版本:4.8.1:编译器:MSVC 2017 64-bit),遇到了一些问题,特记录如下.( 1. 引用库 QtCreator可以直接包含Windows.h, ...

  4. DEX文件解析--7、类及其类数据解析(完结篇)

    一.前言    前置技能链接:       DEX文件解析---1.dex文件头解析       DEX文件解析---2.Dex文件checksum(校验和)解析       DEX文件解析--3.d ...

  5. 集训作业 洛谷P1101 单词方阵

    这个题的长度真的有点长,我直接放图片吧 这个题又是一个和谐的搜索,找到yizhong的y就开始8面搜索,遇见正确的字母就继续搜索,不正确就果断放弃,果然又是一个和谐的搜索呢. #include< ...

  6. 手把手撸套框架-ORM框架的选择

    目录 一,为什么选择SqlSugar? 在.net core ORM框架中,能选择的方案其实有很多,包括以下方案: 1,EF-Core 2,Dapper 3,FreeSql 4,SqlSugar 为什 ...

  7. ztree : checkbox 选中/不选中时动态添加/删除DOM元素

    先上代码. var IDMark_Switch = "_switch", IDMark_Icon = "_ico", IDMark_Span = "_ ...

  8. layui 数据表格自带的导出Excel,身份证等E+/000问题解决

    layui数据表格的工具栏自带导出Excel 会将身份证等 长整数的 自动变成E+并且 后面有000.从而导致数据不能完整导出. 解决方案: 1.先下载Excel的插件包.将压缩包内的两个js放到 l ...

  9. 利用华为eNSP模拟器实现vlan之间的通信

    eNSP交换机配置VLAN 1. 搭建网络拓扑结构 运行eNSP>新建拓扑>搭建如下图的拓扑结构>启动设备.利用调色板将划分的vlan进行区分. 2. pc机IP地址配置 pc1的I ...

  10. Spark入门(第1讲)

    一.Spark是什么 引用官方文档的一句话 Apache Spark is a unified analytics engine for large-scale data processing. Ap ...