进程间通信之面包师问题

作者:潘家邦

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.o
    g++ -o customer customer.o semaphore.o

salesman: salesman.o semaphore.o
    g++ -o salesman salesman.o semaphore.o

state:state.o semaphore.o
    g++ -o state state.o semaphore.o

state_clear: state_clear.o semaphore.o
    g++ -o state_clear state_clear.o semaphore.o

semaphore.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 os

while True:
    os.system('sudo ./customer')
    os.system('sleep 5')

你可能感兴趣的:(进程间通信之面包师问题)