父进程打印输出一个o的字样,子进程打印输出x的字样,父进程打印输出o之后睡眠,睡眠一个随机的时间,然后再次输出o,子进程也是同样的动作,只是输出的是x。
实现代码
#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ }while(0) union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */ }; //封装一个方法,创建信号量级 int sem_create(key_t key) { int semid; semid=semget(key,1,IPC_CREAT | IPC_EXCL | 0666); if(semid==-1) ERR_EXIT("semget"); return semid; } //封装一个方法,只用于打开一个已经创建的信号量级 int sem_open(key_t key) { int semid; //可以都写为0打开已创建的信号量级 semid=semget(key,0,0); if(semid==-1) ERR_EXIT("semget"); return semid; } //对一个信号量进行设置 int sem_setval(int semid,int val) { union semun su; su.val=val; int ret; //对第0个信号量进行操作 //su是设置的参数 ret=semctl(semid,0,SETVAL,su); if(ret==-1) ERR_EXIT("sem_setval"); return 0; } //封装删除信号量级的函数 int sem_d(int semid) { int ret; ret=semctl(semid,0,IPC_RMID,0); if(ret==-1) ERR_EXIT("semctl"); return 0; } //P操作 int sem_p(int semid) { //0表示对信号量级中的第一个信号量进行操作 //-1表示对信号量的操作是P操作 //最后一个0表示操作将会阻塞直到信号量的计数不为0才返回 struct sembuf sb={0,-1,0}; int ret; ret=semop(semid,&sb,1); if(ret==-1) ERR_EXIT("semop"); return ret; } //V操作 int sem_v(int semid) { //0表示对信号量级中的第一个信号量进行操作 //1表示对信号量的操作是V操作 struct sembuf sb={0,1,0}; int ret; ret=semop(semid,&sb,1); if(ret==-1) ERR_EXIT("semop"); return ret; } int semid; void print(char op_char) { //睡眠时间 int pause_time; //配合rand()产生伪随机数序列 srand(getpid()); int i; for(i=0;i<10;i++) { //输出之前进行P操作 sem_p(semid); printf("%c",op_char); //刷新缓冲区把缓冲区里面的数据输出到设备 fflush(stdout); pause_time=rand()%3; sleep(pause_time); printf("%c",op_char); fflush(stdout); //输出之后进行V操作 sem_v(semid); pause_time=rand()%2; sleep(pause_time); } } //两个进程间互斥的使用一个临界区 int main(int argc,char *argv[]) { //创建一个私有的信号量级 semid=sem_create(IPC_PRIVATE); //设置一个初始值 sem_setval(semid,0); pid_t pid; pid=fork(); if(pid==-1) ERR_EXIT("fork"); if(pid>0) { //父进程此时能够进入临界区 sem_setval(semid,1); print('o'); wait(NULL); sem_d(semid); } else { print('x'); } return 0; }
哲学家就餐问题
有五个哲学家,他们的生活方式是交替地进行思考和进餐。他们共用一张圆桌,分别坐在五张椅子上。
在圆桌上有五个刀叉,平时一个哲学家进行思考,饥饿时便试图取用其左、右刀叉,只有在他拿到两个刀叉时才能进餐。进餐完毕,放下刀叉又继续思考。
黄色的5个数字代表5个哲学家,蓝色代表5把刀叉。
避免死锁的整体思路就是:要么拿不到两边的刀叉,要不都能拿到,而不会出现只拿一边。
实现代码
#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ }while(0) union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */ }; #define DELAY (rand()%5+1) int semid; //等待2把刀叉 void wait_for_2fork(int no) { //左边的刀叉编码 int left=no; //右边的刀叉编码 int right=(no+1)%5; //对哲学家no左右边刀叉的P操作 struct sembuf buf[2]={ {left,-1,0}, {right,-1,0} }; //当前信号集中有5个信号量,只对2个(2把刀叉)进行操作 //2把刀叉要么同时得到,要么都得不到,这样就不会死锁 semop(semid,buf,2); } //释放2把刀叉 void free_for_2fork(int no) { //左边的刀叉编码 int left=no; //右边的刀叉编码 int right=(no+1)%5; //对哲学家no左右边刀叉的P操作 struct sembuf buf[2]={ {left,1,0}, {right,1,0} }; //当前信号集中有5个信号量,只对2个(2把刀叉)进行操作 //2把刀叉要么同时得到,要么都得不到,这样就不会死锁 semop(semid,buf,2); } //哲学家的行为 void philosophere(int no) { srand(getpid()); for(;;) { printf("%d is thinking\n",no); //思考一段时间 sleep(DELAY); printf("%d is hungry\n",no); //获取一对刀叉 wait_for_2fork(no); printf("%d is eating\n",no); //吃一段事件 sleep(DELAY); //释放一对刀叉 free_for_2fork(no); } } //哲学家就餐问题 int main(int argc,char *argv[]) { //创建一个私有的信号集,5表示5把刀叉(5个信号量) //使用IPC_private创建的IPC对象,key值属性为0,只用于有亲缘关系的进程间 semid=semget(IPC_PRIVATE,5,IPC_CREAT | 0666); if(semid==-1) ERR_EXIT("semget"); union semun su; su.val=1; int i; for(i=0;i<5;i++) { //5把刀叉都是处于可用的状态 semctl(semid,i,SETVAL,su); } //进程编号 int no=0; pid_t pid; //创建4个子进程,加上一个主进程一共5个进程,表示5个哲学家 for(i=1;i<5;i++) { pid=fork(); if(pid==-1) ERR_EXIT("fork"); if(pid==0) { no=i; break; } } //下面就是哲学家要做的事情 philosophere(no); return 0; }