- 姓名:林伟强
- 学号:201821121010
- 班级:计算1811
1. 问题
5个哲学家,5个筷子。5个哲学家围坐在一张桌子上,筷子放在分别放在每个哲学家的两旁,哲学家必须同时有2个筷子才能吃饭。如果所有哲学家在某个时刻同时拿起左边的筷子,那么右边的筷子就都被其他的哲学家拿了,造成大家都无法吃饭。
2. 给出伪代码
容易出现死锁用的是记录型信号量,伪代码为:
semaphore chopstick chopstick[5] = {1,1,1,1,1};//先创建一个信号集,该信号集里面有5个信号量
do
{
//think
wait(chopstick[i]);//拿起一个筷子
wait(chopstick[(i+1)%5]);//拿起下一个筷子
//eat
signal(chopstick[i]);//释放一个筷子
signal(chopstick[(i+1)%5]);//释放另一个筷子
}while(true)
没有死锁用的是AND信号量解决的,伪代码为:
semaphore chopstick chopstick[5] = {1,1,1,1,1};//先创建一个信号集,该信号集里面有5个信号量
do
{
//think
Sswait(chopstick[i],chopstick[(i+1)%5]);//一次拿起左右2边的筷子
//eat
Ssignal(chopstick[i],chopstick[(i+1)%5]);//吃完后放下2个筷子
}while(true)
3. 给出完整代码
#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) 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} }; semop(semid,buf,2); } 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 ",no); sleep(DELAY); printf("%d is hungry ",no); wait_for_2fork(no,semid); printf("%d is eating ",no); sleep(DELAY); free_2fork(no,semid); #else int left = no; int right = (no + 1) % 5; printf("%d is thinking ",no); sleep(DELAY); printf("%d is hungry ",no); // 感觉到饥饿 wait_1fork(left,semid); // 拿起左叉子,现在是只要有一个资源,就申请 sleep(DELAY); wait_1fork(right,semid); // 拿到右叉子 printf("%d is eating ",no); // 吃饭 sleep(DELAY); free_1fork(left,semid); // 释放左叉子 free_1fork(right,semid); // 释放右叉子 #endif } } int main(int argc,char *argv[]) { int semid; 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); } 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. 运行结果并解释
拿4做个例子,4饿了,拿起筷子吃东西,这句之后紧接着0和3都饿了,但是0和3的有一把筷子被4使用了,所以3和0没有立即拿起筷子,因为信号量不够 之后可以看见4放下筷子后,0直接拿起筷子吃东西,并且2放下筷子后3也拿起了筷子吃东西。就这样进程一直保持进行,一直循环下去就不会死锁,所以说解决这个问题有个办法是在拿起筷子前先判断左右两个筷子是否可用,可用才能拿,而且是同时拿,这样不相邻的哲学家就可以吃上饭,不会造成死锁。