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

父进程打印输出一个o的字样,子进程打印输出x的字样,父进程打印输出o之后睡眠,睡眠一个随机的时间,然后再次输出o,子进程也是同样的动作,只是输出的是x。

父子进程执行流程如下:

父进程

子进程

P

P

O(print)

X(print)

sleep

sleep

O(print)

X(print)

V

V

sleep

sleep

从图中可以看出, O或X总是成对出现的, 要么两个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左右边刀叉的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;
}

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值