父进程打印输出一个o的字样,子进程打印输出x的字样,父进程打印输出o之后睡眠,睡眠一个随机的时间,然后再次输出o,子进程也是同样的动作,只是输出的是x。
实现代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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左右边刀叉的V操作
struct sembuf buf[2]={
{left,1,0},
{right,1,0}
};
//当前信号集中有5个信号量,只对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;
}