作者:潘家邦

2012年12月6日

面包师问题是进程间通信的经典问题。本文就面包师问题进行讨论,并在linux上编程实现。平台说明:Linux Mint 14,G++ 4.7.2。

问题描述

面包师有很多面包和蛋糕,由n个销售人员销售。每个顾客进店后先取一个号,并且等着叫号。当一个销售人员空闲下来,就叫下一个号。请分别编写销售人员和顾客进程的程序。

问题分析

使用信号量解决该问题。首先分析客户和销售人员之间的关系。从问题描述中我们可以看出,客户是排队等待的,而且只有一条队列。当新出现一个客户,则等待队列的长度加一。当销售人员叫号,则等待队列长度减一。客户排队从另一种角度来看,可以认为客户进程被阻塞了,在PV操作中,V操作是不会导致阻塞的,所以把队列长度当作信号量是不会导致客户进程被阻塞的。注意到一共有n个销售人员,当一个销售人员空闲下来,就叫下一个号。换个角度看,销售人员最大空闲人数为n,每出现一个客户,则空闲人数减一,当空闲人数为0时,新出现的客户被阻塞,直到空闲人数大于0,阻塞的客户才被唤醒。我们还发现,不止客户在排队,销售人员在排队的情况也会发生。假设客户的等待队列长度为0,即没有客户在排队,那么这时销售人员应该被阻塞,直到新来一个客户,将销售人员唤醒。于是队列长度成为第二个信号量。

问题求解

算法描述

模仿《现代操作系统(第二版)》的格式,下面分别给出销售人员和客户的伪代码。

typedef int Semaphore;
Semaphore num_of_free = 0;
Semaphore num_of_waiting = 0;
void customer()
{get_code();up(&num_of_waiting);down(&num_of_free);buy_cake();up(&num_of_free);
}void salesman()
{up(&num_of_free);while(true){down(&num_of_waiting);sell_cake();}
}

接下来将解释这段伪代码的逻辑。每启动一个salesman, 则空闲的salesman数目增加。在某一时刻,没有customer在等待,num_of_waiting为0,则salesman在执行到循环体的down语句将被阻塞。如果来了一个customer,则customer的第一个up语句将有可能唤醒某个处于阻塞态的salesman,然后执行down语句。如果没有salesman处于“空闲”,即num_of_free为0,则customer在down语句内被阻塞,直到某个salesman被启动或者某个customer完成了buy_cake并发起up操作来唤醒其他被阻塞在num_of_free上的customer。于是这就保证了num_of_free这个信号量不会大于n。

具体实现

使用Linux的System V风格信号量,主要可执行文件为cumtomer、salesman,分别是客户和销售人员的模拟程序。另外还有可执行文件state,用于输出当前信号量的值;可执行文件state_clear,用于将信号量置零。Python脚本coming.py,用于模拟客户到来的情况。所有可执行文件需要在root权限下执行。文件列表:coming.py makefile semaphore.cpp state_clear.cpp customer.cpp salesman.cpp semaphore.h state.cpp

semaphore.h

我对linux的信号量的操作做了一些封装,隐藏其复杂性,使之在本问题的主要代码中看起来简洁。这是封装之后的接口。

#ifndef SEMAPHORE_H
#define SEMAPHORE_H#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>union semun
{int val;struct semid_ds *buf;unsigned short int *array;struct seminfo *__buf;
};
int semaphore_create();
int output_semaphore(int sid);
int up_num_of_waiting(int sid);
int down_num_of_waiting(int sid);
int up_num_of_free(int sid);
int down_num_of_free(int sid);#endif

semaphore.cpp

这是semaphore接口的实现。

#include "semaphore.h"
#include <stdio.h>int semaphore_create()
{int key = ftok(".", 1);return semget(key, 2, IPC_CREAT);
}int output_semaphore(int sid)
{printf("num of waiting: %d\n", semctl(sid, 0, GETVAL));printf("num of free   : %d\n", semctl(sid, 1, GETVAL));
}int up_num_of_waiting(int sid)
{sembuf operation = {0, 1, SEM_UNDO};return semop(sid, &operation, 1);
}int down_num_of_waiting(int sid)
{sembuf operation = {0, -1, SEM_UNDO};return semop(sid, &operation, 1);
}int up_num_of_free(int sid)
{sembuf operation = {1, 1, SEM_UNDO};return semop(sid, &operation, 1);
}int down_num_of_free(int sid)
{sembuf operation = {1, -1, SEM_UNDO};return semop(sid, &operation, 1);
}

customer.cpp

这是客户程序的实现。

#include <unistd.h>
#include <iostream>
#include "semaphore.h"
using namespace std;int main()
{int pid = getpid();cout << "a customer come, get pid " << pid << endl;int sid = semaphore_create();up_num_of_waiting(sid);down_num_of_free(sid);cout << "a salesman is free, pid "<< pid << " go for her" << endl;up_num_of_free(sid);return 0;
}

salesman.cpp

这是销售人员的的实现。

#include <iostream>
#include "semaphore.h"
using namespace std;
int main()
{int sid = semaphore_create();up_num_of_free(sid);while(true){down_num_of_waiting(sid);cout << "a customer is waiting, I call him" << endl;}return 0;
}

state.cpp

可以方便的查看信号量的值。

#include "semaphore.h"int main()
{int sid = semaphore_create();output_semaphore(sid);return 0;
}

state_clear.cpp

将所有信号量置零。方便重复调试。

#include "semaphore.h"
int main()
{int sid = semaphore_create();semun sem;sem.val = 0;semctl(sid, 0, SETVAL, sem);semctl(sid, 1, SETVAL, sem);return 0;
}

makefile

自动化编译脚本。

baker_problem: customer salesman state state_clear
.PHONY : baker_problem
customer: customer.o semaphore.og++ -o customer customer.o semaphore.osalesman: salesman.o semaphore.og++ -o salesman salesman.o semaphore.ostate:state.o semaphore.og++ -o state state.o semaphore.ostate_clear: state_clear.o semaphore.og++ -o state_clear state_clear.o semaphore.osemaphore.o:customer.o:salesman.o:state.o:state_clear.o:clean:rm *.o customer salesman state state_clear

coming.py

使用python按照一定的时间间隔启动cuntomer。

#!/usr/bin/python
import oswhile True:os.system('sudo ./customer')os.system('sleep 5')

<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>

进程间通信之面包师问题相关推荐

  1. Python 多进程笔记 — 启动进程的方式、守护进程、进程间通信、进程池、进程池之间通信、多进程生产消费模型

    1 面向过程启动多进程 Python 操作进程的类都定义在 multiprocessing 模块,该模块提供了一个 Process 类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另 ...

  2. Android中Messenger进程间通信

    基于消息的进程间通信的方式,不需要编写aidl文件,使用更加简单方便. 客户端和服务端都用Messenger来发送数据,用Handler来处理数据.Messenger处理数据依靠Handler,所以是 ...

  3. Linux网络编程--进程间通信(一)

    进程间通信简介(摘自<Linux网络编程>p85) AT&T 在 UNIX System V 中引入了几种新的进程通讯方式,即消息队列( MessageQueues),信号量( s ...

  4. linux进程间通信-XSI IPC

    一 什么是XSI IPC 有三种 IPC我们称作XSI IPC,即消息队列.信号量以及共享存储器(共享内存),它们之间有很多相似之处. 二 标识符和键     每个内核中的 IPC结构(消息队列.信号 ...

  5. Linux进程间通信(IPC)-------消息队列

    消息队列是进程间通信的一种方法,他有两个操作,一个进程来发送消息(也就是向内存中写入数据),另一个是获取消息(也就是另外一个进程在内存中读取数据) 下面来看消息队列的 创建,写入,读取等需要用到的函数 ...

  6. Linux下进程间通信-------管道通信

    先来看原理: 特点 : 1.半双工的通信方式(通信期间双方都可以发送/接收文件,但是不能双方同时发送/接收数据) 2.pipe只能用于父子进程间的通信 3.mkfifo可用于任意进程间的通信 代码逐步 ...

  7. linux进程间通信:POSIX 共享内存

    文章目录 思维导图 通信原理 优势 POSIX 共享内存 编程接口 编程案例 思维导图 之前学习过sysemV 的共享内存的实现及使用原理,参考linux进程间通信:system V 共享内存 POS ...

  8. linux进程间通信:POSIX信号量

    文章目录 概念描述 编程接口 注意事项 编程案例 信号量基本接口使用案例 信号量父子进程间通信 信号量实现 两进程之间通信 概念描述 英文:semaphore 简称SEM,主要用来进行进程间同步 本质 ...

  9. linux进程间通信:POSIX 消息队列 ----异步通信

    在上一篇中linux进程间通信:POSIX 消息队列我们知道消息队列中在消息个数达到了队列所能承载的上限,就会发生消息的写阻塞. 阻塞式的通信影响系统效率,进程之间在通信收到阻塞时并不能去做其他事情, ...

最新文章

  1. 使用Python,OpenCV进行Tesseract-OCR绑定及识别
  2. 记事本写python怎么运行-Python开发简单记事本
  3. ubuntu下Django环境的搭建
  4. 关于鸿蒙工艺调查分析报告,800万芯片订单已下,5nm工艺+鸿蒙系统,华为做了双重准备...
  5. 机器学习week9 ex8 review
  6. [蓝桥杯][2015年第六届真题]密文搜索(排序+二分)
  7. MySQL表结构设计之范式化和反范式化对比
  8. 团队博客 一 需求分析
  9. 基于ExtendSim的六西格玛银行排队模型
  10. 扫描问题 无法识别计算机,我的扫描枪插进去显示无法识别怎么办
  11. 不卖菜,互联网巨头应该卖什么?
  12. 金山词霸电脑版 百度算法
  13. 太极图正确画法_《太极图》的正确画法
  14. itext生成pdf间距_[itext]Java生成PDF文件
  15. java实现 洛谷 P1427 小鱼的数字游戏
  16. 计算机加号公式,怎样在excel表格中显示加号,而不被当成是公式来计算
  17. RPA智能客服机器人,电商的好伙伴
  18. 数据结构各结构特点(数组、链表、栈、队列、树)
  19. 来吧,展示。互联网术语
  20. Delphi创建COM组件并分别用Delphi和html调用该组件的简单实例

热门文章

  1. Rabbit的工作原理
  2. vijos 1221 神秘配方 题解
  3. 2021全新Java多线程并发入门到精通,一篇就能学会
  4. Mycat的配置---雄关漫道真如铁,而今迈步从头越
  5. Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection brok
  6. 视频文件头解析--MP4-获取mp4 文件信息
  7. Android音频——音量调节
  8. python基础语法(三)
  9. sql如何根据父集编号查询多集子集
  10. 涂鸦智能宠物喂食器(鱼缸投食器)