信号量的简单使用

信号量是多任务操作系统中用来实现多个进程或线程之间同步于互斥的,在操作系统中使用PV操作实现信号量的操作。

在本例中,首先创建一个子进程,然后利用信号量实现控制父子进程的运行顺序:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/sem.h>
#include <sys/ipc.h>

/*为了得到更好的观察效果,设置延时*/
#define DELAY_TIME 3 

int init_sem(int sem_id,int init_value);
int del_sem(int sem_id);
int sem_p(int sem_id);
int sem_v(int sem_id);

/*semctl函数在对信号量进行操作时,最后一个参数需要一个union结构的共同体,一般操作系统自定义
 * 而这个共同体在本系统(CentOS 6.3)上没有没有定义*/
union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
};

int main(void)
{
	pid_t ret;
	int sem_id;

/*调用semget函数创建信号量*/
	sem_id = semget(ftok(".",'a'),1,IPC_CREAT | 0666);
/*调用自定义的函数init_sem初始化信号量*/
	init_sem(sem_id,1);
/*创建子进程*/
	ret = fork();
	if( ret == -1 )
	{
		perror("fork");
		exit(1);
	}
/*在子进程中*/
	else if(ret == 0 )
	{
		printf("Child process will wait for seconds......
");
/*子进程P操作抢占资源*/
		sem_p(sem_id);
/*休眠3秒,得到更好的实验效果*/
		sleep(3);
		printf("The returned value is %d in the child process(PID = %d)
",ret,getpid());
/*子进程V操作释放资源*/
		sem_v(sem_id);
	}
/*在父进程中*/
	else
	{
/*为了保证子进程先得到资源,让父进程睡眠1秒*/
		sleep(1);
/*在子进程释放了资源后,申请资源*/
		sem_p(sem_id);
		printf("The returned value is %d int the parent process(PID = %d)
",ret,getpid());
/*释放资源*/
		sem_v(sem_id);
/*删除信号量*/
		del_sem(sem_id);
	}
	
	return 0;
}

/*自定义函数初始化信号量*/
int init_sem(int sem_id,int init_value)
{
	union semun sem_union;

	sem_union.val = init_value;
/*初始化信号量为init_value*/
	if( semctl(sem_id,0,SETVAL,sem_union) == -1 )
	{
		perror("semctl");
		return -1;
	}
	return 0;
}

/*自定义函数删除信号量*/
int del_sem(int sem_id)
{
	union semun sem_union;

/*删除信号量*/
	if( semctl(sem_id,0,IPC_RMID,sem_union) == -1 )
	{
		perror("semctl");
		return -1;
	}
	return 0;
}

/*自定义函数进行P操作*/
int sem_p(int sem_id)
{
/*调用系统定义的结构体创建对象
 * semop函数是对这个结构体制定操作方式的*/
	struct sembuf sem_b;

/*单个信号的编号应该为0*/
	sem_b.sem_num = 0;
/*定义操作,减1,也就是P操作*/
	sem_b.sem_op = -1;
/*指定标志,系统将自动释放系统中残留的信号量*/
	sem_b.sem_flg = SEM_UNDO;

/*semop函数利用结构体进行P操作*/
	if( semop(sem_id,&sem_b,1) == -1 )
	{
		perror("P operation");
		return -1;
	}
	
	return 0;
}

/*定义函数进行V操作*/
int sem_v(int sem_id)
{
	struct sembuf sem_b;

	sem_b.sem_num = 0;
	sem_b.sem_op = 1;
	sem_b.sem_flg = SEM_UNDO;

	if( semop(sem_id,&sem_b,1) == -1 )
	{
		perror("V operation");
		return -1;
	}
	
	return 0;
}


执行结果:


可以看到是子进程先退出,父进程后退出,而fork出来的父子进程的执行顺序是不定的!

在上面的例子中,我们先让父进程睡眠了1秒,如果不这么做的话,由于fork的执行顺序不定,可能是父进程先抢占了资源,然后很快的子进程又申请,这时子进程就会报错:invalid argument,如下图:



原文地址:https://www.cnblogs.com/suncoolcat/p/3322959.html