操作系统实验五——进程互斥实验

实验内容:
理发店问题:假设理发店的理发室中有 3 个理发椅⼦和 3 个理发师,有⼀个可容纳4 个顾客坐等理发的
沙发。此外还有⼀间等候室,可容纳13 位顾客等候进⼊理发室。顾客如果发现理发店中顾客已满(超过
20 ⼈),就不进⼊理发店。
在理发店内,理发师⼀旦有空就为坐在沙发上等待时间最⻓的顾客理发,同时空出的沙发让在等候室中
等待时间最⻓的的顾客就坐。顾客理完发后,可向任何⼀位理发师付款。但理发店只有⼀本现⾦登记
册,在任⼀时刻只能记录⼀个顾客的付款。理发师在没有顾客的时候就坐在理发椅⼦上睡眠。理发师的
时间就⽤在理发、收款、睡眠上。
请利⽤ linux 系统提供的 IPC 进程通信机制实验并实现理发店问题的⼀个解法。

ipc.h


#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>
#include <unistd.h>
#include <time.h>
#define BUFSZ 256
#define MAXVAL 100
#define STRSIZ 8
#define WRITERQUEST 1 //写请求标识
#define READERQUEST 2 //读请求标识
#define FINISHED 3 //读写完成标识
#define SOFA 4//沙发
#define WAIT 5//等候室
/*信号灯控制用的共同体*/
typedef union semuns
{
    int val;
} Sem_uns;
/* 消息结构体*/
typedef struct msgbuf
{
    long mtype;
    int mid;
} Msg_buf;

key_t buff_key;
int buff_num;
char *buff_ptr;
int shm_flg;

int quest_flg;
key_t quest_key;
int quest_id;

int respond_flg;
key_t respond_key;
int respond_id;

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);

int sem_flg;

key_t s_account_key;
int s_account_val;
int s_account_sem;

key_t s_customer_key;
int s_customer_val;
int s_customer_sem;

int q_flg;

key_t q_sofa_key;
int q_sofa_id;

key_t q_wait_key;
int q_wait_id;

顾客源代码:

#include"ipc.h"
/*
* get_ipc_id() 从/proc/sysvipc/⽂件系统中获取 IPC 的 id 号
* pfile: 对应/proc/sysvipc/⽬录中的 IPC ⽂件分别为
* msg-消息队列,sem-信号量,shm-共享内存
* 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:操作信号灯的结构
*/
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 信号灯数组中信号灯的个数
* sem_flag 信号等数组的存取权限
*/
int set_sem(key_t sem_key, int sem_val, int sem_flg) {
 int sem_id;
 Sem_uns sem_arg;
 //测试由 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 个字节 的共享内存区
* 如果建⽴成功,返回 ⼀个指向该内存区⾸地址的指针 shm_buf
* 输⼊参数:
* shm_key 共享内存的键值
* shm_val 共享内存字节的⻓度
* shm_flag 共享内存的存取权限
*/
char *set_shm(key_t shm_key, int shm_num, int shm_flg) {
 int i, shm_id;
 char *shm_buf;
 //测试由 shm_key 标识的共享内存区是否已经建⽴
 if ((shm_id = get_ipc_id("/proc/sysvipc/shm", shm_key)) < 0 ) {
 //shmget 新建 ⼀个⻓度为 shm_num 字节的共享内存,其标号返回shm_id
 if ((shm_id = shmget(shm_key, shm_num, shm_flg)) < 0) {
 perror("shareMemory set error"); exit(EXIT_FAILURE);
 }
 //shmat 将由 shm_id 标识的共享内存附加给指针 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; //初始为 0
 }
 //shm_key 标识的共享内存区已经建⽴,将由 shm_id 标识的共享内存附加给指针 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 消息队列的存取权限
*/
int set_msq(key_t msq_key, int msq_flg) {
 int msq_id;
 //测试由 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; 
 }

int main(int argc,char *argv[])
{
    srand(time(NULL));
    Msg_buf msg_arg;
    struct msqid_ds msg_sofa;
    struct msqid_ds msg_wait;
//建立沙发消息队列
    q_flg=IPC_CREAT | 0644;
    q_sofa_key=100;
    q_sofa_id=set_msq(q_sofa_key,q_flg);
//建立等候室消息队列
    q_wait_key=200;
    q_wait_id=set_msq(q_wait_key,q_flg);
//建立一个同步顾客信号量
    sem_flg=IPC_CREAT | 0644;
    s_customer_key=300;
    s_customer_val=0;
    s_customer_sem=set_sem(s_customer_key,s_customer_val,sem_flg);
//建立一个互斥帐本信号量
    s_account_key=400;
    s_account_val=1;
    s_account_sem=set_sem(s_account_key,s_account_val,sem_flg);

    int customernum=1;//顾客序号
    while(1)
    {
        //IPC_STAT 为⾮破坏性读,从队列中读出⼀个 msgid_ds 结构填充缓冲 buf
        msgctl(q_sofa_id,IPC_STAT,&msg_sofa);
        if(msg_sofa.msg_qnum<4)
        {
            quest_flg=IPC_NOWAIT;//以非阻塞方式接收消息
            if(msgrcv(q_wait_id,&msg_arg,sizeof(msg_arg),WAIT,quest_flg)>=0)
            {
                msg_arg.mtype=SOFA;
                printf("%d 号新顾客坐入沙发\n",msg_arg.mid);
                msgsnd(q_sofa_id,&msg_arg,sizeof(msg_arg),IPC_NOWAIT);
            }
            else
            {
                msg_arg.mtype=SOFA;
                msg_arg.mid=customernum;
                customernum++;
                printf("%d 号新顾客坐入沙发\n",msg_arg.mid);
                msgsnd(q_sofa_id,&msg_arg,sizeof(msg_arg),IPC_NOWAIT);
            }
        }
        else
        {
            msgctl(q_wait_id,IPC_STAT,&msg_wait);
            if(msg_wait.msg_qnum<13)
            {
                msg_arg.mtype=WAIT;
                msg_arg.mid=customernum;
                customernum++;
                printf("沙发座满,%d 号新顾客进入等候室\n",msg_arg.mid);
                msgsnd(q_wait_id,&msg_arg,sizeof(msg_arg),IPC_NOWAIT);
            }
            else
            {
                printf("等候室满,%d 号新顾客没有进入理发店\n",customernum);
                down(s_customer_sem);
            }
        }
        //用进程休眠一个随机时间模拟顾客到达的时间间隔
        sleep(rand()%5+1);
    }
    return EXIT_SUCCESS;
}

理发师源代码:

#include"ipc.h"
/*
* get_ipc_id() 从/proc/sysvipc/⽂件系统中获取 IPC 的 id 号
* pfile: 对应/proc/sysvipc/⽬录中的 IPC ⽂件分别为
* msg-消息队列,sem-信号量,shm-共享内存
* 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:操作信号灯的结构
*/
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 信号灯数组中信号灯的个数
* sem_flag 信号等数组的存取权限
*/
int set_sem(key_t sem_key, int sem_val, int sem_flg) {
 int sem_id;
 Sem_uns sem_arg;
 //测试由 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 个字节 的共享内存区
* 如果建⽴成功,返回 ⼀个指向该内存区⾸地址的指针 shm_buf
* 输⼊参数:
* shm_key 共享内存的键值
* shm_val 共享内存字节的⻓度
* shm_flag 共享内存的存取权限
*/
char *set_shm(key_t shm_key, int shm_num, int shm_flg) {
 int i, shm_id;
 char *shm_buf;
 //测试由 shm_key 标识的共享内存区是否已经建⽴
 if ((shm_id = get_ipc_id("/proc/sysvipc/shm", shm_key)) < 0 ) {
 //shmget 新建 ⼀个⻓度为 shm_num 字节的共享内存,其标号返回shm_id
 if ((shm_id = shmget(shm_key, shm_num, shm_flg)) < 0) {
 perror("shareMemory set error"); exit(EXIT_FAILURE);
 }
 //shmat 将由 shm_id 标识的共享内存附加给指针 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; //初始为 0
 }
 //shm_key 标识的共享内存区已经建⽴,将由 shm_id 标识的共享内存附加给指针 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 消息队列的存取权限
*/
int set_msq(key_t msq_key, int msq_flg) {
 int msq_id;
 //测试由 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; }

int main(int argc,char *argv[])
{
    srand(time(NULL));
    Msg_buf msg_arg;
    struct msqid_ds msg_sofa;
    struct msqid_ds msg_wait;
//建立沙发消息队列
    q_flg=IPC_CREAT | 0644;
    q_sofa_key=100;
    q_sofa_id=set_msq(q_sofa_key,q_flg);
//建立等候室消息队列
    q_wait_key=200;
    q_wait_id=set_msq(q_wait_key,q_flg);
//建立一个同步顾客信号量
    sem_flg=IPC_CREAT | 0644;
    s_customer_key=300;
    s_customer_val=0;
    s_customer_sem=set_sem(s_customer_key,s_customer_val,sem_flg);
//建立一个互斥帐本信号量
    s_account_key=400;
    s_account_val=1;
    s_account_sem=set_sem(s_account_key,s_account_val,sem_flg);

//建立 3 个理发师进程
    int pid[3];
    int i;
    for(i=0;i<3;i++)
    {
        pid[i]=fork();
        if(pid[i]==0)
        {
            while(1)
            {
                msgctl(q_sofa_id,IPC_STAT,&msg_sofa);
                if(msg_sofa.msg_qnum==0)
                    printf("%d 号理发师睡眠\n",getpid()); 
                //以阻塞方式从沙发队列接收一条消息
                msgrcv(q_sofa_id,&msg_arg,sizeof(msg_arg),SOFA,0);
                //唤醒顾客进程,让下一顾客坐入沙发
                up(s_customer_sem);
                printf("%d 号理发师给 %d 号顾客理发\n",getpid(),msg_arg.mid);
                //用进程休眠一个随机时间模拟理发过程。
                sleep(rand()%4+10);
                //若有理发师正在收钱,则阻塞
                down(s_account_sem);
                printf("%d 号理发师向 %d 号顾客收费\n",getpid(),msg_arg.mid);
                up(s_account_sem);
            }
        }
    }
    return EXIT_SUCCESS;
}
  • 8
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值