System V信号量实现进程互斥和解决哲学家就餐问题

父进程打印输出一个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;
}

哲学家就餐问题

 

有五个哲学家,他们的生活方式是交替地进行思考和进餐。他们共用一张圆桌,分别坐在五张椅子上。

在圆桌上有五个刀叉,平时一个哲学家进行思考,饥饿时便试图取用其左、右刀叉,只有在他拿到两个刀叉时才能进餐。进餐完毕,放下刀叉又继续思考。

                                                     System V信号量实现进程互斥和解决哲学家就餐问题_第1张图片

黄色的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;
}

 

你可能感兴趣的:(进程/线程通信)