linux进程间通信:POSIX信号量

概念描述
  • 英文:semaphore 简称SEM,主要用来进行进程间同步
  • 本质:内核维护的一个正整数,可对其进行各种+/-操作
  • 分类:systemV 信号量、POSIX 有名信号量、POSIX 无名信号量
  • 用途:用来标示系统中可用资源的的个数,协调各个进程有序的访问资源,防止发生冲突
  • P操作:程序在进入临界区之前要对资源进行申请,对资源的引用计数要-1,当资源个数为0时进程p操作会发生阻塞
  • V操作:程序离开临界区后要释放相应的资源,对资源对引用计数要+1
编程接口

以下接口编码过程中都可以通过man sem_open这种方式查看接口对具体使用方式,详细信息则不列举

sem_t *sem_open(const char *name, int oflag); //使用字符串创建一个信号量
sem_t *sem_open(const char *name, int oflag,
            mode_t mode, unsigned int value); //使用字符串创一个一个信号量,并初始化信号量的值
int sem_close(sem_t *sem);
int sem_post(sem_t *sem); //信号量的v操作 +1
int sem_wait(sem_t *sem); //信号量的p操作 -1
int sem_trywait(sem_t *sem); //信号量进行p操作,但是值已经为0,此时会立即返回错而非阻塞

//信号量进行p操作,如信号量的值为0,则阻塞abs_timeout结构体中的时间内还是无法执行p操作,则返回错误
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); 
int sem_unlink(const char *name);//删除名称为name的信号量
int sem_getvalue(sem_t *sem, int *sval);//获取信号量sem所代表的value数值
注意事项
  • 以上编程接口包含头文件: #include <semaphore.h>
  • 编译时需指定:-lpthread进行编译
编程案例

linux系统中创建的信号量存放路径为/dev/shm

信号量基本接口使用案例

sem_demo.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>

int main (){
	//创建信号量并初始化信号量的value值为4
	unsigned int sem_value = 4;
	sem_t *sem = sem_open("posix_sem", O_RDWR | O_CREAT,0777,sem_value);
	if (sem == SEM_FAILED) {
		printf("sem_open failed\n");
		_exit(-1);
	}
	
	//获取信号量的value值
	if (sem_getvalue(sem,&sem_value) != -1) {
		printf("sem_getvalue is %d\n",sem_value);
	}

	//对信号量进行p(-1)操作
	sem_wait(sem);
	sem_wait(sem);
	sem_wait(sem);
	sem_wait(sem);
	//sem_trywait(sem);//并不会发生阻塞,此时sem值已经为0,无法再进行p操作

	if(sem_getvalue(sem,&sem_value) != -1) {
		printf("sem_getvalue after sem_wait is %d\n",sem_value);
	}

	//对信号量进行v(+1)操作
	sem_post(sem);
	sem_post(sem);
	sem_post(sem);
	sem_post(sem);
	
	if(sem_getvalue(sem,&sem_value) != -1) {
		printf("sem_getvalue after sem_post is %d\n",sem_value);
	}

	if(sem_close(sem) != -1) {
		printf("sem_close success\n");
	}

	printf("wait for unlink sem\n");
	sleep(10);
	
	//删除名称为posix_sem信号量
	if(sem_unlink("posix_sem") != -1) {
		printf("sem_unlink success \n");
	}
	
	return 0;
}

编译gcc sem_demo.c -lpthread 输出如下:

sem_getvalue is 4
sem_getvalue after sem_wait is 0
sem_getvalue after sem_post is 4
sem_close success
wait for unlink sem
sem_unlink success 

同时在等待10秒删除期间我们查看/dev/shm目录下的sem文件存在,当10秒过后sem文件则被删除
在这里插入图片描述

信号量父子进程间通信

sem_comu.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>

#define SEM_NAME "posix_sem_comu"

int main (){
	int i = 0, j = 0,sem_val = 0,ret;
	sem_t *sem;
	sem = sem_open(SEM_NAME, O_CREAT,0666,1);

	ret = fork();
	if (ret == -1) {
		printf("sem_open failed \n");
		sem_close(sem);
		sem_unlink(SEM_NAME);
		_exit(-1);
	}
	//创建的子进程,执行10次p操作,每次间隔1秒
	else if(ret == 0) {
		while (i++ <= 10) {
			sem_wait(sem);
			if (sem_getvalue(sem,&sem_val) != -1) {
				printf("child process :sem value is %d\n",sem_val);
				sleep(1);
			}
		}
		_exit(1);
	}
	//父进程执行10次v操作,每次间隔两秒
	else {
		while (j++ <=10) {
			sem_post(sem);
			if (sem_getvalue(sem,&sem_val) != -1) {
				printf("prarent process :sem value is %d\n",sem_val);
				sleep(2);
			}
		}
	}
	
	//最终删除sem信号量
	sem_close(sem);
	if (sem_unlink(SEM_NAME) != -1) {
		printf("sem_unlink success \n");
	}
	return 0;
}

编译:gcc sem_comu.c -o comu -lpthread
输出如下,可以很明显的看到子即使子进程的p操作频率快于父进程v操作,但是对同一信号量,他们仍然能够有序同步访问。因为子进程p操作频率较快,此时信号量的value会很快减为0,子进程此时再进行p操作会发生阻塞;直到父进程执行v操作将信号量的值变为1子进程才能继续访问

prarent process :sem value is 2
child process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
sem_unlink success 
信号量实现 两进程之间通信

两个进程,使用同一个信号量共同访问一个临界区,利用信号量的pv操作,实现同步访问
sem_post.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>

int main(){
	char *name = "/posix_sem";
	int j = 0, sem_value = 4;
	sem_t *sem;
	sem = sem_open(name, O_CREAT, 0666, sem_value);
	if (sem == SEM_FAILED) {
		printf("sem open failed \n");
		_exit(-1);
	}
	printf("sem_open %s success \n",name);

	//对信号量进行5次 v操作,每次间隔5秒
	while(j++ <= 10) {
		if(sem_post(sem) == -1) {
			printf("sem _posd failed\n");
			_exit(-1);
		}
		if(sem_getvalue(sem,&sem_value) != -1) {
			printf("sem post success %d\n",sem_value);
		}
		sleep(5);
	}

	sleep(10);
	if(sem_close(sem) != -1) {
		printf("sem_close success \n");
	}
	if (sem_unlink(name) != -1) {
		printf("sem_unlink success \n");
	}
	return 0;
}

sem_wait.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>

int main(){
	char *name = "/posix_sem";
	int j = 0, sem_value = 4;
	sem_t *sem;
	sem = sem_open(name, O_CREAT, 0666, sem_value);
	if (sem == SEM_FAILED) {
		printf("sem open failed \n");
		_exit(-1);
	}
	printf("sem_open %s success \n",name);

	//对信号量进行p操作,每次间隔5秒,总共进行10次
	while(j++ <= 10) {
		if(sem_post(sem) == -1) {
			printf("sem _posd failed\n");
			_exit(-1);
		}
		if(sem_getvalue(sem,&sem_value) != -1) {
			printf("sem post success %d\n",sem_value);
		}
		sleep(5);
	}

	//执行结束后关闭信号量,并删除信名称为name的信号量
	sleep(10);
	if(sem_close(sem) != -1) {
		printf("sem_close success \n");
	}
	if (sem_unlink(name) != -1) {
		printf("sem_unlink success \n");
	}
	return 0;
}

编译:
gcc sem_post.c -o post -lpthread
gcc sem_wait.c -o wait -lpthread
可以看到输出如下,两进程是能够正常进行信号量value值的访问同步
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值