操作系统第6次实验报告:使用信号量解决进程互斥访问
姓名:蔡婷婷 学号:201821121003 班级:计算1811
1. 选择哪一个问题
哲学家进餐问题:假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东西的时候,他们就停止思考,思考的时候也停止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。因为用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两只餐叉。
2. 给出伪代码
解决死锁采用的方法:仅当一个哲学家左右两边的叉子都可用时才允许他抓起叉子哲学家才能吃饭,这样不相邻的哲学家就可吃上饭。这样哲学家左右两边的叉子只有都拿起或都不拿起两种。采用AND信号量实现:
semaphore chopstick chopstick[5] = {1,1,1,1,1};
for(;;){
//哲学家思考中
Sswait(chopstick[i],chopstick[(i+1)%5]); //拿起左右刀叉
//哲学家吃饭中
Ssignal(chopstick[i],chopstick[(i+1)%5]); //释放左右刀叉
}
3. 给出完整代码
给出完整代码,适当添加注释。注意代码的可读性、可维护性。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdint.h>
5 #include <stdbool.h>
6 #include <errno.h>
7 #include <unistd.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <sys/ipc.h>
11 #include <sys/sem.h>
12 #include <sys/wait.h>
13 union semun
14 {
15 int val;
16 struct semid_ds *buf;
17 unsigned short *array;
18 struct seminfo *__buf;
19 };
20
21 #define ERR_EXIT(m) \
22 do { \
23 perror(m); \
24 exit(EXIT_FAILURE); \
25 } while(0)
26 #define DELAY (rand() % 5 + 1)//将叉子设置为一个临界资源
27
28 //拿起两边的刀叉
29 void P(int no,int semid)
30 {
31 int left = no;//哲学家左边的刀叉编号和哲学家是一样的
32 int right = (no + 1) % 5;//右边的刀
33 //同时拥有左右两个刀叉才可以吃饭
34 struct sembuf buf[2] = {{left,-1,0},{right,-1,0}};
35 //信号集中有5个信号量,对其中的资源sembuf进行操作
36 semop(semid,buf,2);
37 }
38
39 //释放刀叉
40 void V(int no,int semid)
41 {
42 int left = no;
43 int right = (no + 1) % 5;
44 struct sembuf buf[2] = {{left,1,0},{right,1,0}};
45 semop(semid,buf,2);
46 }
47
48 //哲学家
49 void philosophere(int no,int semid)
50 {
51 srand(getpid());
52 for(;;)
53 {
54 printf("第%d位哲学家:正在思考\n",no+1);
55 sleep(DELAY);
56 printf("第%d位哲学家:等待左右刀叉中\n",no+1);
57 P(no,semid);
58 printf("第%d位哲学家:吃饭中\n",no+1);
59 sleep(DELAY);
60 V(no,semid);//释放左右刀叉
61 }
62 }
63
64 int main(int argc,char *argv[])
65 {
66 int semid;
67 //创建5个信号量代表5个哲学家
68 semid = semget(IPC_PRIVATE,5,IPC_CREAT | 0666);
69 if(semid < 0)
70 ERR_EXIT("semid");
71 union semun su;
72 su.val = 1;
73 int i;
74 for(i = 0;i < 5;++i) {
75 semctl(semid,i,SETVAL,su);//第二个参数也是索引
76 }
77 //创建4个子进程
78 int num = 0;
79 pid_t pid;
80 for(i = 1;i < 5;++i)
81 {
82 pid = fork();
83 if(pid < 0)
84 ERR_EXIT("fork");
85 if(pid == 0) // 子进程
86 {
87 num = i;
88 break;
89 }
90 }
91 philosophere(num,semid); //哲学家
92 return 0;
93 }
4. 运行结果并解释
①运行结果
②解释结果
由于没有产生死锁,所以一直在输出状态,故只截图出运行结果的一小部分。一开始的时候,5位哲学家都在思考没有人拿起左右两边的刀叉,随后第3位、第1位、第5位哲学家饥饿等待刀叉后拿起左右刀叉吃饭,接着第1位哲学家不吃饭进入思考没有使用左右刀叉,第4位哲学家开始饥饿等待刀叉,但是由于第3位和第5位哲学家没有释放左右刀叉导致第4位哲学家没有可得到的刀叉,所以第4位哲学家一直在等待中,接着第2位哲学家饥饿开始等待刀叉,这时第3位哲学家开始思考释放刀叉且之前第1位哲学家也释放其刀叉进入思考中,所以第2位哲学家可以拿起左右刀叉吃饭,接着第3位哲学家又饥饿等待刀叉......