03 Posix信号量

1. 基本概念
  • 信号量是一种用于提供不同进程或者一个给定进程的线程间同步的手段。
  • 分为两种信号量:
    1)命名信号量:有个名字,不相关的进程可以访问,随内核持续
    2)未命名信号量(基于内存的信号量):没有名字,在内存中预先商定的位置,可以在进程之间或一组线程之间共享,进程间共享时信号量必须位于共享内存区域中,线程共享时,信号量置于线程共享的内存区域中。随进程或者内核持续性。
    两种信号量的API:
    在这里插入图片描述
    其中命名使用sem_open创建信号量,使用sem_close关闭信号量的引用,sem_unlink删除信号量
    基于内存的信号量需要程序自身定义一个sem_t, 在通过sem_init来初始化,最后通过sem_destory来销毁
  • Posix信号量是一个整数
    linux把有名信号量当成小型的Posix共享内存对象,挂载在目录/dev/shm/下。
  • linux下编译include<semaphore.h>后任然出现undefined reference to ‘sem_open‘,需要加上编译选项 -pthread
2. 命名信号量
2.1 打开或者创建 sem_open

可以创建和打开一个信号量,打开的时候只需要前面两个参数

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
// oflags:O_CREAT,O_EXCL,O_RDONLY, O_RDWR,O_WRONLY   mode为权限位  value为信号量的值
sem_t *sem_open(const char *name, int oflags, ... /*mode, value*/);
	// 成功返回一个信号量描述符, 失败返回SEM_FAILED
	// sem_open会自己分配内存对象,返回指针。
#include <semaphore.h>
#include <fcntl.h>
#include <stdio.h>

int main()
{
	sem_t *sem = sem_open("test.sem", O_CREAT | O_RDWR, (S_IRUSR | S_IWUSR), 0);
	if (sem == SEM_FAILED) {
		printf("create sem fail\n");
		return -1;
	}
	
	return 0;
}
2.2 关闭一个信号量 sem_close

int sem_close(sem_t *sem); // 成功返回0,失败返回-1

2.3 删除一个信号量 sem_unlink

int sem_unlink(const char *name); // 成功返回0,失败返回-1

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

int main()
{
	sem_t *sem = sem_open("test.sem", O_RDWR);
	if (sem == SEM_FAILED) {
		printf("open sem fail\n");
		return -1;
	}
	
	if (sem_close(sem) == -1) {
		printf("close sem fail\n");
		return -1;
	}
	
	if (sem_unlink("test.sem") == -1) {
		printf("unlink sem fail\n");
		return -1;
	}
	
	printf("close and unlink sem end\n");
	return 0;
}
2.4 信号量操作: sem_wait,sem_post, sem_getvalue

信号量操作:PV操作

P – 减少信号量 – sem_wait
V – 增加信号量 – sem_post

#include <semaphore.h>
// P操作,信号量-1,如果当前信号量大于0,sem_wait直接返回,否则阻塞知道信号量大于0
int sem_wait(sem_t *sem); // 成功返回0,失败返回-1
	// 在阻塞过程中被其他信号中断,则sem_wait返回EINTER

// 非阻塞版本,如果信号量不大于0,则会失败并且返回EAGAIN错误
int sem_trywait(sem_t *sem); // 成功返回0,失败返回-1

// 指定尝试时间, 超过时间后返回失败并置错误为ETIMEDOUT
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); // 成功返回0,失败返回-1
	// 指定的时间是一个绝对值,需要相对时间需要调用clock_gettime获取CLOCK_REALTIME,再在其基础上增加时间生成一个适合的timespec结构

// 发布一个信号量,V操作,信号量+1, 唤醒阻塞的sem_wait函数
int sem_post(sem_t *sem); // 成功返回0,失败返回-1

int sem_getvalue(sem_t *sem, int *value); // // 成功返回0,失败返回-1

打开并sem_wait等待信号量

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

int main()
{
	sem_t *sem = sem_open("test.sem", O_CREAT | O_RDWR, (S_IRUSR | S_IWUSR), 1);
	if (sem == SEM_FAILED) {
		printf("open sem fail\n");
		return -1;
	}
	
	if (sem_wait(sem) == -1) {
		printf("open sem fail\n");
		return -1;
	}
	
	printf("%d, sem_wait success return \n", (long)getpid());
	return 0;
}

打开、关闭、删除信号量

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

int main()
{
	sem_t *sem = sem_open("test.sem", O_RDWR);
	if (sem == SEM_FAILED) {
		printf("open sem fail\n");
		return -1;
	}
	
	if (sem_close(sem) == -1) {
		printf("close sem fail\n");
		return -1;
	}
	
	if (sem_unlink("test.sem") == -1) {
		printf("unlink sem fail\n");
		return -1;
	}
	
	printf("close and unlink sem end\n");
	return 0;
}

打开、获取信号量数量、发送信号量

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

int main()
{
	sem_t *sem = sem_open("test.sem", O_RDWR);
	if (sem == SEM_FAILED) {
		printf("open sem fail\n");
		return -1;
	}
	
	int value = 0;
	if (sem_getvalue(sem, &value) != -1) {
		printf("cur sem value is %d \n", value);
	}
	
	if (sem_post(sem) == -1) {
		printf("post sem fail\n");
		return -1;
	}
	
	if (sem_getvalue(sem, &value) != -1) {
		printf("cur sem value is %d \n", value);
	}
	
	printf("post sem end: %d \n", (long)getpid());
	return 0;
}
3. 未命名信号量(基于内存的信号量、匿名信号量)
  • 基于内存的信号量,信号量是程序自己分配空间(命名信号量为sem_open分配空间),信号量存放的位置决定了多线程和多进程之间的共享性, 也决定了信号量是随内核还是随程序的持续性
  • 相比命名信号量增加了两个api,sem_initsem_destory(sem), sem_init是自己定义一个sem_t ,然后初始化, 命名信号量的sem_open是自己定义一个sem_t*指针,通过sem_open来初始化。这两个函数分别代替命名信号量的sem_opensem_close, sem_unlink
  • 相比命名信号量不存在权限位控制(因为不存在sem_open)。
3.1 初始化信号量

int sem_init(sem_t *sem, int pshared, unsigned int value); // 成功返回0,失败返回-1
1)pshared0表示线程共享,1表示进程间共享,进程间共享时需要为信号量分配个共享内存区域(Posix共享内存对象 – 随内核使用mmap()创建的共享映射 – fork创建的子进程会继承父进程的内存映射System V的共享内存段 – 随内核
2)对一个已经初始化过的未命名信号量再次进行初始化会导致未定义的行为。
3)value用来对未命名信号量赋值。

3.2 销毁信号量

销毁的信号量必须先经过sem_init
int sem_destory(sem_t *sem); // 成功返回0,失败返回-1

4. 其他
4.1 信号量相关宏限制

SEM_NSEMS_MAX 进程能够拥有的POSIX信号量的最大数目, >256
SEM_VALUE_MAX POSIX信号量能够取得最大值 32767

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

int main()
{
	printf("SEM_NSEMS_MAX = %ld, SEM_VALUE_MAX = %ld\n",
		sysconf(_SC_SEM_NSEMS_MAX), sysconf(_SC_SEM_VALUE_MAX));
	return 0;
}

参考:
1) 网络编程 卷2 进程间通信
2) linux/unix系统编程手册
3)linux api查询 :https://www.die.net/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值