- 姓名:朱笃信
- 学号:201821121021
- 班级:计算1811
1. 选择哪一个问题
- 哲学家进餐问题
2. 给出伪代码
semid<-semget创建信号量 for i :=0 to 5 begin semctl将信号量初始化为1 end for i :=0 to 5 begin fork() end 打印:哲学家在思考 sleep() 打印:哲学家饿了 P()更改信号量的值为-1 打印:哲学家在吃饭 sleep() V()更改信号量的值为1
3. 给出完整代码
#include<stdio.h> #include<stdlib.h> #include<sys/ipc.h> #include<sys/msg.h> #include<sys/types.h> #include<unistd.h> #include<errno.h> #include<sys/ipc.h> #include<sys/sem.h> #include<sys/wait.h> #define Wait (rand() % 5 + 1) union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array; /* array for GETALL, SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; void P(int semid,int no); //P操作, void V(int semid,int no); //V操作 void philosoper(int semid,int no); //哲学家 int main(void) { int semid = semget(IPC_PRIVATE, 5, IPC_CREAT | 0666);//创建一个新信号量或取得一个已有信号量 if (semid == -1) //失败,进行报错 printf("semget error.\n"); union semun sm; sm.val = 1; int i; for (i = 0; i < 5; i++) { semctl(semid, i, SETVAL, sm); //直接控制信号量信息 } int no = 0; pid_t pid; for (i = 1; i < 5; i++) { pid = fork(); if (pid == -1) printf("fork error.\n"); if (pid == 0) { no = i; break; } } philosoper(semid,no); return 0; } void philosoper(int semid,int no) { srand(getpid()); while(1) { printf("第%d位哲学家在思考\n", no); sleep(Wait); printf("第%d位哲学家饿了\n", no); P(semid,no); printf("第%d位哲学家在吃饭\n", no); sleep(Wait); V(semid,no); } } void P(int semid,int no) { int left = no; int right = (no + 1) % 5; struct sembuf buf[2] ={{left, -1, 0},{right, -1, 0}}; if(semop(semid, buf, 2) == -1) { printf("P error.\n"); exit(1); } } void V(int semid,int no) { int left = no; int right = (no + 1) % 5; struct sembuf buf[2] ={{left, 1, 0},{right, 1, 0}}; if(semop(semid, buf, 2) == -1) { printf("V error.\n"); exit(1); } }
该代码中包含几个库函数:
1.int semget(key_t key, int num_sems, int sem_flags);
第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget()函数并提供一个键,再由系统生成一个相应的信号标识符(semget()函数的返回值),只有semget()函数才直接使用信号量键,所有其他的信号量函数使用由semget()函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。
第二个参数num_sems指定需要的信号量数目,它的值几乎总是1。
第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。
semget()函数成功返回一个相应信号标识符(非零),失败返回-1。
2.int semctl(int sem_id, int sem_num, int command, ...);
前两个参数与前面一个函数中的一样,command通常是下面两个值中的其中一个
SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。
3.int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
改变信号量的值
4. 运行结果并解释
程序开始先创建进程,分别对应五个哲学家,哲学家们首先进入思考状态,首先是第二位哲学家饿了,成了第一个吃螃蟹的人,他拿起了第一跟第二根筷子,然后第0位哲学家饿了,他拿起了0与4的筷子,第二位哲学家吃完了,又开始思考,此时第一与第二根筷子可以用了,第三位哲学家饿了,拿起第二与第三根筷子,第四位哲学家也饿了,但是他没有筷子可以用,第一位哲学家也饿了,但是他也缺个筷子,第0号哲学家吃好了,第一位哲学家拿到筷子,开始吃饭。