假設理髮店的理髮室中有 3 個理髮椅子和 3 個理髮師,有一個可容納 4 個顧客坐等理髮的沙發。此外還有一間等候室,可容納 13 位顧客等候進入理髮室。顧客若是發現理髮店中顧客已滿(超過 20 人),就不進入理髮店。 在理髮店內,理髮師一旦有空就爲坐在沙發上等待時間最長的顧客理髮,同時空出的沙發讓在等候室中等待時間最長的的顧客就坐。顧客理完後,可向任何一位理髮師付款。但理髮店只有一本現金登記冊,在任一時刻只能記錄一個顧客的付款。理髮師在沒有顧客的時候就坐在理髮椅子上睡眠。理髮師的時間就用在理髮、收款、睡眠上。 請利用 linux 系統提供的 IPC 進程通訊機制實驗並實現理髮店問題的一個解法。 linux
參考解法僞代碼:
理髮師程序(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
spa