操作系统第6次实验报告:使用信号量解决进程互斥访问
0. 个人信息
- 姓名 雷坛春
- 学号 201821121030
- 班级 计算1811
1. 哲学家进餐问题
五个哲学家围在一个圆桌就餐,每个人都必须拿起左右两把叉子才能进餐,当每个人都先拿起左叉子,等待右叉子的时候就会造成死锁。
假设哲学家的编号是A、B、C、D、E,叉子编号是1、2、3、4、5,哲学家和筷子围成一圈如下图所示:
2. 给出伪代码
哲学家饥饿时,总是先去拿他左边的筷子,即执行wait(chopstick);成功后,再去拿他右边的筷子,即执行wait(chopstick[(i+ 1) mod 51]);
再成功后便可进餐.进餐完毕,又先放下他左边的筷子,然后放下他右边的筷子.
emaphore chopstick chopstick[5] = {1,1,1,1,1}; do { //思考 wait(chopstick[i]); wait(chopstick[(i+1)%5]); //进餐 signal(chopstick[i]); signal(chopstick[(i+1)%5]); }while(true)
虽然上述解法可保证不会有两个相临的哲学家同时进餐但起死锁是可能的.假如五个哲学家同时饥饿而各自拿起右边的筷子时,就会使五个信号量chopstick均为0;
当他们试图去拿右边的筷子时,都将因无筷子可拿而无限期地等待.对于这样的死锁问题可按照如下修改。
semaphore chopstick chopstick[5] = {1,1,1,1,1}; do { //think Sswait(chopstick[i],chopstick[(i+1)%5]); //eat Ssignal(chopstick[i],chopstick[(i+1)%5]); }while(true)
3. 完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <stdbool.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/wait.h> union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) //申请一个资源 int wait_1fork(int no,int semid) { struct sembuf sb = {no,-1,0}; int ret; ret = semop(semid,&sb,1); if(ret < 0) { ERR_EXIT("semop"); } return ret; } // 释放一个资源 int free_1fork(int no,int semid) { struct sembuf sb = {no,1,0}; int ret; ret = semop(semid,&sb,1); if(ret < 0) { ERR_EXIT("semop"); } return ret; } //这里表明叉子是一个临界资源 #define DELAY (rand() % 5 + 1) //相当于P操作 void wait_for_2fork(int no,int semid) { //哲学家左边的刀叉编号和哲学家是一样的 int left = no; //右边的刀叉 int right = (no + 1) % 5; //刀叉值是两个 //注意第一个参数是编号 //操作的是两个信号量,即两种资源都满足,才进行操作 struct sembuf buf[2] = { {left,-1,0}, {right,-1,0} }; //信号集中有5个信号量,只是对其中的资源sembuf进行操作 semop(semid,buf,2); } //相当于V操作 ,释放刀叉 void free_2fork(int no,int semid) { int left = no; int right = (no + 1) % 5; struct sembuf buf[2] = { {left,1,0}, {right,1,0} }; semop(semid,buf,2); } //哲学家要做的事 void philosophere(int no,int semid) { srand(getpid()); for(;;) { #if 1 //这里采取的措施是当两把刀叉都可用的时候(即两种资源都满足的时候) //哲学家才能吃饭,这样不相邻的哲学家就可吃上饭 printf("%d is thinking\n",no); // 思考中 sleep(DELAY); printf("%d is hungry\n",no); // 感觉到饥饿 wait_for_2fork(no,semid);//拿到两把叉子才能吃饭 printf("%d is eating\n",no); // 吃饭 sleep(DELAY); free_2fork(no,semid);//释放两把叉子 #else //这段代码可能会造成死锁 int left = no; int right = (no + 1) % 5; printf("%d is thinking\n",no); // 思考中 sleep(DELAY); printf("%d is hungry\n",no); // 感觉到饥饿 wait_1fork(left,semid); // 拿起左叉子,现在是只要有一个资源,就申请 sleep(DELAY); wait_1fork(right,semid); // 拿到右叉子 printf("%d is eating\n",no); // 吃饭 sleep(DELAY); free_1fork(left,semid); // 释放左叉子 free_1fork(right,semid); // 释放右叉子 #endif } } int main(int argc,char *argv[]) { int semid; //创建信号量 //信号量集中5个信号量 semid = semget(IPC_PRIVATE,5,IPC_CREAT | 0666); if(semid < 0) { ERR_EXIT("semid"); } union semun su; su.val = 1; int i; for(i = 0;i < 5;++i) { //注意第二个参数也是索引 semctl(semid,i,SETVAL,su); } //创建4个子进程 int num = 0; pid_t pid; for(i = 1;i < 5;++i) { pid = fork(); if(pid < 0) { ERR_EXIT("fork"); } if(0 == pid) // 子进程 { num = i; break; } } //这里就是哲学家要做的事情 philosophere(num,semid); return 0; }
4. 运行结果并解释
结果解释:
在结果中可以观察到就餐过程一直执行没有停止,说明没有发生死锁现象。
5.参考资料
【1】https://blog.csdn.net/u014304293/article/details/46004677