假设理发店的理发室中有 3 个理发椅子和 3 个理发师,有一个可容纳 4 个顾客坐等理发的沙发。此外还有一间等候室,可容纳 13 位顾客等候进入理发室。顾客如果发现理发店中顾客已满(超过 20 人),就不进入理发店。 在理发店内,理发师一旦有空就为坐在沙发上等待时间最长的顾客理发,同时空出的沙发让在等候室中等待时间最长的的顾客就坐。顾客理完后,可向任何一位理发师付款。但理发店只有一本现金登记册,在任一时刻只能记录一个顾客的付款。理发师在没有顾客的时候就坐在理发椅子上睡眠。理发师的时间就用在理发、收款、睡眠上。 请利用 linux 系统提供的 IPC 进程通信机制实验并实现理发店问题的一个解法。
参考解法伪代码:
理发师程序(Barber)
{
建立一个互斥帐本信号量: s_account,初值=1;
建立一个同步顾客信号量: s_customer,初值=0;
建立沙发消息队列: q_sofa;
建立等候室消息队列:q_wait;
建立 3 个理发师进程: b1_pid, b2_pid, b3_pid;
每个理发师进程作:
while(1)
{
以阻塞方式从沙发队列接收一条消息,
如果有消息,则消息出沙发队列(模拟一顾客理发);
唤醒顾客进程(让下一顾客坐入沙发)。
用进程休眠一个随机时间模拟理发过程。
理完发,使用帐本信号量记账。
互斥的获取账本
记账
唤醒用账本理发师者
否则没有消息(沙发上无顾客)
则理发师进程在沙发队列上睡眠;
当沙发队列有消息时被唤醒(有顾客坐入沙发)。
}
}
顾客程序(customer)
{
while(1)
{
取沙发队列消息数(查沙发上顾客数) ;
如果消息数小于 4(沙发没座满)
以非阻塞方式从等候室队列接收一条消息(查等候室有顾客否),
如果有消息将接收到的消息发送到沙发队列(等候室顾客坐入沙发);
否则发送一条消息到沙发队列(新来的顾客直接坐入沙发);
否则(沙发坐满)
取等候室队列消息数(查等候室顾客数) ;
如果消息数小于 13
发送一条消息到等候室队列(等候室没满,新顾客进等候室);
否则
在顾客同步信号量上睡眠(等候室满暂不接待新顾客);
用进程休眠一个随机时间模拟顾客到达的时间间隔。
}
}
开发调试过程: (不知为何贴上去注释有乱码,UTF-8)
新建一个文件夹,在该文件夹中建立以下名为 ipc.h 的 C 语 言头文件。 再建
立以下名为 ipc.c 的 C 语言头文件,实现头文件中申明的函数。 再建立以下名
为 barber.c 的 C 语 言 头 文 件 , 实 现 理 发 师 的 功 能 。 再 建 立 以 下 名 为
consumer.c 的 C 语言头文件,实现消费者的功能.
建立项 管理文件 Makefile 。使用 make 命令编译连接生成可执行的 barber 和
consumer 程序 ,执行并调试 producer 和 consumer 程序
/*
* Filename
* copyright
* Function
: ipc.h
: (C) 2013 by zhengdujin
: 声Θ明÷ IPC 机ú制的函ˉ数簓原-型í和í全局变量
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>
#define BUFSZ 256
#define MAXVAL 100
#define STRSIZ 2
#define REQUEST 1 //理え发ぁ请求ó标括识/*信号灯控制用的共2同体*/
typedef union semuns {
int val;
} Sem_uns;
/* 消息¢结á构1体*/
typedef struct msgbuf {
long mtype;
int mid;
} Msg_buf;
int wait_flg;
key_t wait_key;
int q_wait;
int sofa_flg;
key_t sofa_key;
int q_sofa;
int get_ipc_id(char *proc_file,key_t key);
char *set_shm(key_t shm_key,int shm_num,int shm_flag);
int set_msq(key_t msq_key,int msq_flag);
int set_sem(key_t sem_key,int sem_val,int sem_flag);
int down(int sem_id);
int up(int sem_id);
//互¥斥a账本信号量
key_t a_mtx_key;
int s_account;
//同步顾客í信号量
key_t c_syn_key;
int s_customer;
int sem_val;
int sem_flg;
********
/*
* Filename
* copyright
* Function
: ipc.c
: (C) 2013 by zhengdujin
: 一组哩建¨立ⅰIPC 机ú制的函ˉ数簓
*/
#include "ipc.h"
/*
* get_ipc_id() 从洙proc/sysvipc/文件t系μ统中D获取 IPC 的 id 号* pfile: 对应畖/proc/sysvipc/目录中D的 IPC 文件t分别纄为a
* msg-消息¢队ó列 ,sem-信号量,shm-共2享í内ú存
* key: 对应畖要癮获取的 IPC 的 id 号的键ü值μ
*/
int get_ipc_id(char *proc_file,key_t key)
{
FILE *pf;
int i,j;
char line[BUFSZ],colum[BUFSZ];
if((pf = fopen(proc_file,"r")) == NULL){
perror("Proc file not open");
exit(EXIT_FAILURE);
}
fgets(line, BUFSZ,pf);
while(!feof(pf)){
i = j = 0;
fgets(line, BUFSZ,pf);
while(line[i] == ' ') i++;
while(line[i] !=' ') colum[j++] = line[i++];
colum[j] = '\0';
if(atoi(colum) != key) continue;
j=0;
while(line[i] == ' ') i++;
while(line[i] !=' ') colum[j++] = line[i++];
colum[j] = '\0';
i = atoi(colum);
fclose(pf);
return i;
}
fclose(pf);
return -1;
}
/*
* 信号灯上的 down/up 操ù作痢
* semid:信号灯数簓组哩标括识符
* semnum:信号灯数簓组哩下标括
* buf:操ù作痢信号灯的结á构1
*/
int down(int sem_id)
{
struct sembuf buf;
buf.sem_op = -1;
buf.sem_num = 0;
buf.sem_flg = SEM_UNDO;if((semop(sem_id,&buf,1)) <0) {
perror("down error ");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
int up(int sem_id)
{
struct sembuf buf;
buf.sem_op = 1;
buf.sem_num = 0;
buf.sem_flg = SEM_UNDO;
if((semop(sem_id,&buf,1)) <0) {
perror("up error ");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
/*
* set_sem 函ˉ数簓建¨立ⅰ一个具有瓺 n 个信号灯的信号量
*
如果建¨立ⅰ成é功|,返う回 一个信号灯数簓组哩的标括识符 sem_id
*
输入参数簓:
* sem_key 信号灯数簓组哩的键ü值μ
* sem_val 信号灯数簓组哩中D信号灯的个数簓
* sem_flag 信号等台数簓组哩的存取权ā限T
*/
int set_sem(key_t sem_key,int sem_val,int sem_flg)
{
int sem_id;
Sem_uns sem_arg;
//测a试由 sem_key 标括识的信号灯数簓组哩是否已经-建¨立ⅰ
if((sem_id = get_ipc_id("/proc/sysvipc/sem",sem_key)) < 0 )
{
//semget 新建¨一个信号灯,其标括号返う回到 sem_id
if((sem_id = semget(sem_key,1,sem_flg)) < 0)
{
perror("semaphore create error");
exit(EXIT_FAILURE);
}
//设Θ置信号灯的初值μ
sem_arg.val = sem_val;
if(semctl(sem_id,0,SETVAL,sem_arg) <0){
perror("semaphore set error");
exit(EXIT_FAILURE);
}
}
return sem_id;
}
/*
* set_shm 函ˉ数簓建¨立ⅰ一个具有瓺 n 个字节ú 的共2享í内ú存区
*
如果建¨立ⅰ成é功|,返う回 一个指向ò该内ú存区首骸地址·的指针 shm_buf
*
输入参数簓:
* shm_key 共2享í内ú存的键ü值μ
* shm_val 共2享í内ú存字节ú的长¤度è
* shm_flag 共2享í内ú存的存取权ā限T
*/
char * set_shm(key_t shm_key,int shm_num,int shm_flg)
{
int i,shm_id;
char * shm_buf;
//测a试由 shm_key 标括识的共2享í内ú存区是否已经-建¨立ⅰ
if((shm_id = get_ipc_id("/proc/sysvipc/shm",shm_key)) < 0 )
{
//shmget 新建¨ 一个长¤度è为a shm_num 字节ú的共2享í内ú存,其标括号返う回到 shm_id
if((shm_id = shmget(shm_key,shm_num,shm_flg)) <0)
{
perror("shareMemory set error");
exit(EXIT_FAILURE);
}
//shmat 将由 shm_id 标括识的共2享í内ú存附加ó给指针 shm_buf
if((shm_buf = (char *)shmat(shm_id,0,0)) < (char *)0)
{
perror("get shareMemory error");
exit(EXIT_FAILURE);
}
for(i=0; i<shm_num; i++) shm_buf[i] = 0; //初始为a 0
}
//shm_key 标括识的共2享í内ú存区已经-建¨立ⅰ将由 shm_id 标括识的共2享í内ú存附加ó给指针
shm_buf
if((shm_buf = (char *)shmat(shm_id,0,0)) < (char *)0)
{
perror("get shareMemory error");
exit(EXIT_FAILURE);}
return shm_buf;
}
/*
* set_msq 函ˉ数簓建¨立ⅰ一个消息¢队ó列
* 如果建¨立ⅰ成é功|,返う回 一个消息¢队ó列 的标括识符 msq_id
* 输入参数簓:
* msq_key 消息¢队ó列 的键ü值μ
* msq_flag 消息¢队ó列 的存取权ā限T
*/
int set_msq(key_t msq_key,int msq_flg)
{
int msq_id;
//测a试由 msq_key 标括识的消息¢队ó列 是否已经-建¨立ⅰ
if((msq_id = get_ipc_id("/proc/sysvipc/msg",msq_key)) < 0 )
{
//msgget 新建¨一个消息¢队ó列 ,其标括号返う回到 msq_id
if((msq_id = msgget(msq_key,msq_flg)) < 0)
{
perror("messageQueue set error");
exit(EXIT_FAILURE);
}
}
return msq_id;
}
********
/*
* Filename
* copyright
* Function
: customer.c
: (C) 2013 by zhengdujin
: 一组哩建¨立ⅰIPC 机ú制的函ˉ数簓
*/
#include "ipc.h"
#include <signal.h>
#include <unistd.h>
#include <wait.h>
int main(int argc,char *argv[])
{
Sem_uns sem_arg;
Msg_buf msg_arg;
struct msqid_ds msg_wait_inf;struct msqid_ds msg_sofa_inf;
//进程ì自定¨义的键ü盘ì中D断信号处鋦理え函ˉ数簓
typedef void (*sighandler_t) (int);
void sigcat(){
printf("clear -s -q \n");
semctl(s_account,1,IPC_RMID,sem_arg);
semctl(s_customer,1,IPC_RMID,sem_arg);
msgctl(q_sofa, IPC_RMID,&msg_sofa_inf);
msgctl(q_wait, IPC_RMID,&msg_wait_inf);
}
a_mtx_key=202;
c_syn_key=201;
sem_flg = IPC_CREAT | 0644;
//互¥斥a账本信号量
sem_val =1;
s_account=set_sem(a_mtx_key,sem_val,sem_flg);
//同步顾客í信号量
sem_val =0;
s_customer=set_sem(c_syn_key,sem_val,sem_flg);
//建¨立ⅰ沙发ぁ消息¢队ó列
sofa_flg = IPC_CREAT| 0644;
sofa_key = 301;
q_sofa= set_msq(sofa_key,sofa_flg);
//建¨立ⅰ等台候ò室酣消息¢队ó列
wait_flg = IPC_CREAT|0644;
wait_key = 302;
q_wait = set_msq(wait_key,wait_flg);
msg_arg.mtype=REQUEST;
int count;
count=1;
signal(SIGINT,(sighandler_t)sigcat);
int rate;
rate=2;//sleep 的时骸间
while(1)
{
//取沙发ぁ队ó列 的消息¢数簓,查é看′沙发ぁ顾客í数簓
int n_sofa;
msgctl(q_sofa, IPC_STAT,&msg_sofa_inf);
n_sofa=msg_sofa_inf.msg_qnum;
//如果消息¢数簓小于 (辍沙发ぁ没座哩椅)
if(n_sofa<4){ //沙发ぁ没座哩满ú//以非阻哩塞方式从洙等台待鋣室酣队ó列 接ó收一条消息¢
//如果有瓺消息¢将接ó收到的消息¢发ぁ送í到沙发ぁ队ó列 (辍等台候ò室酣顾客í做如沙
发ぁ);
wait_flg = IPC_NOWAIT;
if(msgrcv(q_wait,&msg_arg,sizeof(msg_arg),REQUEST,wait_flg) >= 0){
int num;
num=msg_arg.mid;
msgsnd(q_sofa,&msg_arg,sizeof(msg_arg),0);
printf("%d 号顾客í从洙等台候ò室酣坐入沙发ぁ閈n",num);
}
else{
//否则ò发ぁ送í一条消息¢到沙发ぁ队ó列 (辍新来ぁ的顾客í直±接ó坐入沙发ぁ中D);
sleep(rate);
msg_arg.mid=count;
msgsnd(q_sofa,&msg_arg,sizeof(msg_arg),0);
printf("%d 号新顾客í坐入沙发ぁ閈n",count++);
}
}else{
//沙发ぁ坐满ú
//取等台候ò室酣队ó列 消息¢数簓(查é等台候ò室酣顾客í数簓)
int wait_n;
msgctl(q_wait, IPC_STAT,&msg_wait_inf);
wait_n=msg_wait_inf.msg_qnum;
//如果消息¢数簓小于 3
if(wait_n<13)
{
//发ぁ送í一条消息¢到等台候ò室酣队ó列 (辍等台候ò室酣没做满ú,新顾客í进等台候ò室酣)
int n;
sleep(rate);
msg_arg.mid=count;
msgsnd(q_wait,&msg_arg,sizeof(msg_arg),0);
printf("沙发ぁ坐满ú %d 号顾客í在ú等台候ò室酣等台候ò\n",count++);
}
else{
//在ú顾客í同步信号量上睡ˉ眠(辍等台候ò室酣满ú。£暂Y不接ó客í);
printf("等台候ò室酣满ú %d 号顾客í没有瓺进入理え发ぁ店台篭n",count++);
down(s_customer);
//用进程ì休Y眠一个随机ú时骸间模£拟a顾客í到达的时骸间间隔。£
sleep(10);}
}
}
}
********
/*
* Filename
* copyright
* Function
: barber.c
: (C) 2013 by zhengdujin
: 一组哩建¨立ⅰIPC 机ú制的函ˉ数簓
*/
#include "ipc.h"
#include <signal.h>
#include <unistd.h>
#include <wait.h>
int main(int argc,char *argv[])
{
Sem_uns sem_arg;
struct msqid_ds msg_wait_inf;
struct msqid_ds msg_sofa_inf;
Msg_buf msg_arg;
//进程ì自定¨义的键ü盘ì中D断信号处鋦理え函ˉ数簓
typedef void (*sighandler_t) (int);
void sigcat(){
printf("clear -s -q \n");
semctl(s_account,1,IPC_RMID,sem_arg);
semctl(s_customer,1,IPC_RMID,sem_arg);
msgctl(q_sofa, IPC_RMID,&msg_sofa_inf);
msgctl(q_wait, IPC_RMID,&msg_wait_inf);
}
a_mtx_key=202;
c_syn_key=201;
sem_flg = IPC_CREAT | 0644;
//互¥斥a账本信号量
sem_val =1;
s_account=set_sem(a_mtx_key,sem_val,sem_flg);
//同步顾客í信号量
sem_val =0;s_customer=set_sem(c_syn_key,sem_val,sem_flg);
//建¨立ⅰ沙发ぁ消息¢队ó列
sofa_flg = IPC_CREAT| 0644;
sofa_key = 301;
q_sofa= set_msq(sofa_key,sofa_flg);
//建¨立ⅰ等台候ò室酣消息¢队ó列
wait_flg = IPC_CREAT|0644;
wait_key = 302;
q_wait = set_msq(wait_key,wait_flg);
signal(SIGINT,(sighandler_t)sigcat);
int i;
int pid[3];
for(i=0;i<3;i++){
pid[i]=fork();
if(pid[i]==0)
{
while(1){
printf("%d号理え发ぁ师簗睡ˉ眠。£。£。£\n",getpid());
if(msgrcv(q_sofa,&msg_arg,sizeof(msg_arg),REQUEST,0) >=0)
{//如果有瓺消息¢,则ò消息¢出沙发ぁ队ó列 (模£拟a一顾客í理え发ぁ;
printf("%d 号 理 え 发 ぁ 师 簗 为 a%d 号 顾 客 í 理 え 发 ぁ 閈 n",getpid(),msg_arg.mid);
up(s_customer);
sleep(8);//模£拟a理え发ぁ时骸间
down(s_account);//理え完 发ぁ使用帐ê本信号量记账。£互¥斥a账本
printf("%d号理え发ぁ师簗收取%d号顾客í交费\n",getpid(),msg_arg.mid);
sleep(2);//模£拟a交费时骸间
up(s_account);//允ê许í下一个顾客í进入
}
}
}
}
wait(NULL);
}
Makefile:
hdrs = ipc.h
r_src = barber.c ipc.c
r_obj = barber.o ipc.o
w_src = customer.c ipc.cw_obj = customer.o ipc.o
opts = -g -c
all: barber customer
barber: $(r_obj)
gcc $(r_obj) -o barber
barber.o: $(r_src) $(hdrs)
gcc $(opts) $(r_src)
customer: $(w_obj)
gcc $(w_obj) -o customer
customer.o: $(w_src) $(hdrs)
gcc $(opts) $(w_src)
clean:
rm barber customer *.o
来源:oschina
链接:https://my.oschina.net/u/250255/blog/133269