进程间通信之面包师问题

来源:互联网 发布:vscode 文件路径提示 编辑:程序博客网 时间:2024/04/30 09:24

作者:潘家邦

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_problemcustomer: customer.o semaphore.o    g++ -o customer customer.o semaphore.osalesman: salesman.o semaphore.o    g++ -o salesman salesman.o semaphore.ostate:state.o semaphore.o    g++ -o state state.o semaphore.ostate_clear: state_clear.o semaphore.o    g++ -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/pythonimport oswhile True:    os.system('sudo ./customer')    os.system('sleep 5')

<script type="text/javascript"><!--google_ad_client = "ca-pub-1944176156128447";/* cnblogs 首页横幅 */google_ad_slot = "5419468456";google_ad_width = 728;google_ad_height = 90;//--></script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>