【Linux系统编程】POSIX有名信号量

00. 目录

01. 概述

在 POSIX 标准中,信号量分两种,一种是无名信号量,一种是有名信号量。无名信号量一般用于线程间同步或互斥,而有名信号量一般用于进程间同步或互斥。它们的区别和管道及命名管道的区别类似,无名信号量则直接保存在内存中,而有名信号量要求创建一个文件。

02. 相关函数

2.1 创建有名信号量

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>

sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,
                mode_t mode, unsigned int value);

Link with -pthread.
功能:
	创建一个有名信号量。
参数:
	name:信号量文件名。注意,不能指定路径名。因为有名信号量,默认放在/dev/shm 里   
	flags:sem_open() 函数的行为标志。
	mode:文件权限(可读、可写、可执行)的设置。
	value:信号量初始值。
返回值:
	成功:信号量的地址
	失败:SEM_FAILED    

2.2 关闭有名信号量

#include <semaphore.h>
int sem_close(sem_t *sem);
Link with -pthread.
功能:
	关闭有名信号量。
参数:
	sem:指向信号量的指针。
返回值:
	成功:0
	失败:-1    

2.3 删除有名信号量

#include <semaphore.h>

int sem_unlink(const char *name);
功能:
	删除有名信号量的文件。
参数:
	name:有名信号量文件名。
返回值:
	成功:0
	失败:-1

2.4 信号量P操作

#include <semaphore.h>

int sem_wait(sem_t *sem);
功能:
	将信号量的值减 1。操作前,先检查信号量(sem)的值是否为 0,若信号量为 0,
    此函数会阻塞,直到信号量大于 0 时才进行减 1 操作。
参数:
	sem:信号量的地址。
返回值:
	成功:0
	失败: - 1

int sem_trywait(sem_t *sem);
	以非阻塞的方式来对信号量进行减 1 操作。
	若操作前,信号量的值等于 0,则对信号量的操作失败,函数立即返回。

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
	限时尝试将信号量的值减 1
	abs_timeout:绝对时间

abs_timeout补充说明

struct timespec {
	time_t tv_sec;		/* seconds */ // 秒
	long   tv_nsec;	/* nanosecondes*/ // 纳秒
}

time_t cur = time(NULL); 		//获取当前时间。
struct timespec t;				//定义timespec 结构体变量t
t.tv_sec = cur + 1;				// 定时1秒
sem_timedwait(&cond, &t);

2.5 信号量V操作

#include <semaphore.h>

int sem_post(sem_t *sem);
功能:
	将信号量的值加 1 并发出信号唤醒等待线程(sem_wait())。
参数:
	sem:信号量的地址。
返回值:
	成功:0
	失败:-1

03. 程序示例

示例一:有名信号量实现进程间互斥功能

#include<stdio.h>
#include<semaphore.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
 
void printer(sem_t *sem, char *str)
{
	sem_wait(sem);	//信号量减一
	while(*str!='\0')
	{
		putchar(*str);	
		fflush(stdout);
		str++;
		sleep(1);
	}
	printf("\n"); 
	
	sem_post(sem);	//信号量加一
}
 
int main(int argc, char *argv[])
{
	pid_t pid;
	sem_t *sem = NULL;
	
	pid = fork(); //创建进程
	if(pid<0){ //出错
		perror("fork error");
		
	}else if(pid == 0){ //子进程
	
		//跟open()打开方式很相似,不同进程只要名字一样,那么打开的就是同一个有名信号量
		sem = sem_open("name_sem", O_CREAT|O_RDWR, 0666, 1); //信号量值为 1
		if(sem == SEM_FAILED){ //有名信号量创建失败
			perror("sem_open");
			return -1;
		}
		
		char *str1 = "hello";
		printer(sem, str1); //打印
		
		sem_close(sem); //关闭有名信号量
		
		_exit(1);
	}else if(pid > 0){ //父进程
		
		//跟open()打开方式很相似,不同进程只要名字一样,那么打开的就是同一个有名信号量
		sem = sem_open("name_sem", O_CREAT|O_RDWR, 0666, 1); //信号量值为 1
		if(sem == SEM_FAILED){//有名信号量创建失败
			perror("sem_open");
			return -1;
		}
		
		char *str2 = "world";
		printer(sem, str2); //打印
		
		sem_close(sem); //关闭有名信号量
		
		wait(pid, NULL); //等待子进程结束
	}
	
	sem_unlink("name_sem");//删除有名信号量
	
	return 0;
}

测试结果:

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  
hello
world
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

示例二: 有名信号量实现进程间同步功能(print2 先打印,再到 print1 打印)

print1.c 代码如下

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>
#include <stdio.h>
 
void print(sem_t *print1, sem_t *print2)
{
	int i = 0;
	while(1)
	{
		sem_wait(print1);
		i++;
		printf("int print1 i = %d\n", i);
		sem_post(print2);
	}
}
 
int main(int argc, char **argv)
{	
	sem_t *print1, *print2;
 
	print1 = sem_open("sem_print1", O_CREAT, 0777, 0);  
	if(SEM_FAILED == print1)
	{
		perror("sem_open");
	}
	
	print2 = sem_open("sem_print2", O_CREAT, 0777, 1);    
	if(SEM_FAILED == print2)
	{
		perror("sem_open");
	}
	
	print(print1, print2);
	
	return 0;
}

print2.c 代码

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>
#include <stdio.h>
 
void print(sem_t *print1, sem_t *print2)
{
	int i = 0;
	while(1)
	{
		sem_wait(print2);
		i++;
		printf("in print2 i = %d\n", i);
		sleep(1);
		sem_post(print1);
	}
}
 
int main(int argc, char **argv)
{	
	sem_t *print1, *print2;
	
	print1 = sem_open("sem_print1", O_CREAT, 0777, 0);  
	if(SEM_FAILED == print1)
	{
		perror("sem_open");
	}
	
	print2 = sem_open("sem_print2", O_CREAT, 0777, 1);  
	if(SEM_FAILED == print2)
	{
		perror("sem_open");
	}
	
	print(print1, print2);
	
	return 0;
}

删除有名信号量的代码:

#include <semaphore.h>
#include <stdio.h>
 
void sem_del(char *name)
{
	int ret;
 
	ret = sem_unlink(name);
	if(ret < 0)
	{
		perror("sem_unlink");
	}
}
 
int main(int argc, char **argv)
{
	sem_del("sem_print1"); //删除信号量文件sem_print1
	sem_del("sem_print2"); //删除信号量文件sem_print2
	
	return 0;
}

04. 附录

发布了639 篇原创文章 · 获赞 2326 · 访问量 75万+

猜你喜欢

转载自blog.csdn.net/dengjin20104042056/article/details/102969451