经典的IPC问题
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的邻居是由宏LEFT和RIGHT定义。
该程序使用了一个信号量数组,每个信号量对应于一位哲学家,这样,所需的叉子被占用时,饥饿的哲学家就可以被阻塞。注意每个进程将历程philosopher作为朱代码运行,而其他例程,如take_forks、put_forks和test都只是普通的例程,而不是单独的进程。
读者/写者问题
另一个著名的问题是读者—写者问题,它建模了对数据库的访问。
例如,设想一个飞机定票系统,其中有许多竞争的进程试图读写其中的数据。多个进程同时读是可以接受的,但如果一个进程正在更新数据库,则所有其他进程都不能访问数据库,即使读操作也不行。这里的问题是:如何对读者和写者进行编程?
| 进程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问题的更多相关文章
- 操作系统之进程篇(4)--经典进程间通信(IPC)问题
1. 哲学家进餐问题: 问题描述: 五个哲学家在一个圆桌上进餐,每人的面前放了一盘意大利面,两个盘子之间有一个叉子,但是由于盘子里面的面条十分光滑,需要两个叉子才能进行就餐行为.餐桌的布局如下图所示: ...
- #干货#小微信贷风控中类IPC模式和集中审批模式
浅析小微信贷风控中类IPC模式和集中审批模式 席占斌 常言道瑕不掩瑜,反过来讲瑜自然也不能掩瑕,看问题需要客观公正辩证. 在小微信贷中,风控模式依旧是核心,目前比较流行和占比较大的风控模式有很经典的I ...
- 一次完整的从webshell到域控的探索之路
前言 内网渗透测试资料基本上都是很多大牛的文章告诉我们思路如何,但是对于我等小菜一直是云里雾里. 于是使用什么样的工具才内网才能畅通无阻,成了大家一直以来的渴求. 今天小菜我本着所有师傅们无私分享的精 ...
- Metasploit域渗透测试全程实录(终结篇)
本文作者:i春秋签约作家——shuteer 前言 内网渗透测试资料基本上都是很多大牛的文章告诉我们思路如何,但是对于我等小菜一直是云里雾里.于是使用什么样的工具才内网才能畅通无阻,成了大家一直以来的渴 ...
- python多进程-----multiprocessing包
multiprocessing并非是python的一个模块,而是python中多进程管理的一个包,在学习的时候可以与threading这个模块作类比,正如我们在上一篇转载的文章中所提,python的多 ...
- Linux 进程间通信(一)(经典IPC:消息队列、信号量、共享存储)
有3种称作XSI IPC的IPC:消息队列.信号量.共享存储.这种类型的IPC有如下共同的特性. 每个内核中的IPC都用一个非负整数标志.标识符是IPC对象的内部名称,为了使多个合作进程能够在同一IP ...
- Linux 进程间通信(一)(经典IPC:管道、FIFO)
管道 管道是Unix系统IPC的最古老方式,有两种局限性: (1) 历史上它们是半双工的(即数据只能在一个方向上流动),虽然现在某些系统提供了全双工管道,但是为了可移植性,不要抱有绝对的全双工假设 ...
- Classic IPC Problems 经典的进程间通信问题
The Producer-Consumer Problem Presenter Notes: 生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bo ...
- IPC 经典问题:Reader & Writer Problem
完整代码实现: #include <stdio.h> #include <unistd.h> #include <time.h> #include <stdl ...
随机推荐
- hihoCoder 1052 基因工程 最详细的解题报告
题目来源:基因工程 解题思路:假设基因序列长度为N,则需要计算基因序列前K个和后K个相同所需要的最少改变次数sum. 假设基因序列为 ATACGTCT (即M=8),K=6:interval=M-K= ...
- CSS上划线、下划线、删除线等方法
text-decoration:underline; 下划线 text-decoration:overline; 顶划线 text-decoration:line-through; 删 ...
- Python Hacking Tools - Password Sniffing
Password Sniffing with Scapy 1. Download and install the Scapy first. pip install scapy https://scap ...
- scratch编程体感游戏
体感游戏有很多种,最常见的就是摄像头和声控了,今天我们要用scratch编写一系列的体感游戏!!!是不是很激动呢? 首先我们来编摄像头类的: No.1拳头打幽灵 挥动头就能打到幽灵了哟! 具体程序如下 ...
- 一起聊聊PHP的几个设计模式
工厂模式 1.简单工厂模式 目的 简单工厂模式是一个精简版的工厂模式. 它与静态工厂模式最大的区别是它不是『静态』的.因为非静态,所以你可以拥有多个不同参数的工厂,你可以为其创建子类.甚至可以模拟 ...
- Python 简明教程 --- 25,Python 目录操作
微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 做技术一定要一颗恒心,这样才不会半途而废. 目录 上一节我们介绍了文件相关的操作,本节我们来介绍目录 ...
- boot camp要求独立的fat分区
先在windows把u盘用diskpart clean掉分区表 然后在苹果里面,给u盘重新分区,分区表不要选择GUID,选择主引导记录,格式选择fat,OK
- node学习第一天
创建服务器 利用require引入http模块:var http=require("http") 利用http模块创建server服务器; 创建服务器:var server=htt ...
- netty 使用字典提升短文本的压缩效果
1 问题 术语:压缩率,compression ratio,压缩后的大小/压缩前的大小,越小说明压缩效果越好. 在使用netty的JdkZlibEncoder进行压缩时,发现了一个问题:它对于短文本( ...
- 构建一个基于事件分发驱动的EventLoop线程模型
在之前的文章中我们详细介绍过Netty中的NioEventLoop,NioEventLoop从本质上讲是一个事件循环执行器,每个NioEventLoop都会绑定一个对应的线程通过一个for(;;)循环 ...