进程间的通信:信号量


前言

信号量,同一时刻有几个线程/进程可以进行访问,可以实现进程/线程之间的同步互斥访问资源的一种方式
由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
(1)P(sv):如果sv的值大于零,就给它减1,往下执行;如果它的值为零,就挂起该进程的执行
(2)V(sv):给sv的值加1,如果有其他进程因等待sv而被挂起,就让它恢复运行
在信号量进行PV操作时都为原子操作(因为它需要保护临界资源)

信号量和互斥锁(mutex)的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。


一、POSIX信号量:有名sem_open/无名sem_init

在这里插入图片描述

1、有名信号量sem_open()

适合fork进程之间使用

①、创建打开信号量sem_open()

#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,
               mode_t mode, unsigned int value);/* 这两参数为可选*/
success:返回指向sem_t类型的地址   error: 返回SEM_FAILED
name:信号的名称 注意,不能指定路径名。因为有名信号量,默认放在/dev/shm 里
oflag: 0  访问已有的信号
	   O_CREAT 不存在则创建,指定这个就需要下面mode/value参数。
	   O_EXCL| O_CREAT  并且信号量存在,那么会sem_open失败	
mode:可选项,权限码 0666
value:可选项,新信号量的初始值

Link with -pthread.

②、关闭sem_close()/删除sem_unlink()信号量

关闭:

#include <semaphore.h>
关闭与这个信号量的关联,并不是删除

int sem_close(sem_t *sem);
success: 0   error: -1
sem: open时返回的描述符

删除:

彻底摧毁这个信号量
int sem_unlink(const char *name);
success: 0   error: -1
name: 信号量的名称

2、无名信号量sem_init()

适合使用在线程之间

①、初始化信号量 sem_init()

int sem_init(sem_t *sem, int pshared, unsigned int value);
success:0  error: -1
sem:指向信号量对象
pshared:  0:用于同一多线程的同步   > 0 用于多个相关进程间的同步配合共享内存使用
value:初始信号量的值

②、摧毁信号量sem_destroy()

int sem_destroy(sem_t *sem);
success:0  error: -1
sem:要销毁的信号量

3、无/有名共用等待( P)sem_wait/发布(V)sem_post()

①、等待信号量,如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞。相当于P操作。

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

②、释放信号量,让信号量的值加1。相当于V操作

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

4、sem_trywait()/sem_timedwait()

int sem_trywait(sem_t *sem);
sem减一小于0,不阻塞,直接返回,error为EAGAIN

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
等待一段时间,如果这段时间内sem>0返回成功,超过则不阻塞返回error为ETIMEDOUT

5、例子

①、使用无名信号量,对共享变量的访问

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

int number; // 被保护的全局变量

sem_t sem_id1, sem_id2;

void* thread_one_fun(void *arg){
	while(1){
		sem_wait(&sem_id1); 
		number++;
		printf("thread_one_fun1 number:%d\n",number); 
		sleep(1);
		printf("thread_one_fun2 number:%d\n",number); 
		sem_post(&sem_id2);
		sleep(1);
	}
}
void* thread_two_fun(void *arg){
	while(1){
		sem_wait(&sem_id2);
		printf("thread_two_fu1 number:%d\n",number); 
		sleep(1);
		printf("thread_two_fu2 number:%d\n",number); 
		sem_post(&sem_id1); 
		sleep(1);
	}
}

int main(int argc,char *argv[]){

	pthread_t id1, id2;
	
	sem_init(&sem_id1, 0, 1); 
	sem_init(&sem_id2, 0, 0); 

	pthread_create(&id1,NULL,thread_one_fun, NULL);
	pthread_create(&id2,NULL,thread_two_fun, NULL);
	pthread_join(id1,NULL);
	pthread_join(id2,NULL);

	sem_destroy(&sem_id1);
	sem_destroy(&sem_id2);
	return 0;
}

②、有名信号量

参考:https://blog.csdn.net/tennysonsky/article/details/46500417
int print1:

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>
#include <stdio.h>
#include <unistd.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);
		sleep(1);
		sem_post(print2);
		if(i == 5)
			break;
	}
}


void sem_del(char *name)
{
	int ret;
 
	ret = sem_unlink(name);
	if(ret < 0)
	{
		perror("sem_unlink");
	}
}
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);
	

	sem_del("sem_print1"); //删除信号量文件sem_print1
	sem_del("sem_print2"); //删除信号量文件sem_print2
	return 0;
}

int print2:

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>
#include <stdio.h>
#include <unistd.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);
		if(i == 5)
			break;
	}
}
 
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;
}

二、System V 信号量

1.信号量创建打开 semget()

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);
返回标识符
key:所创建或打开信号量集的键值。可为IPC_PRIVATE
nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效。
semflg:调用函数的操作类型,也可用于设置信号量集的访问权限,IPC_CREAT |IPC_EXCL |0666


创建信号量集合:
sem_id = semget((key_t)0x1234, (int)ValLen, 0666 |IPC_EXCL | IPC_CREAT);  
 printf("0InitSemVal,%d\r\n", sem_id);
 if(errno == EEXIST)
 {
     printf("1InitSemVal,%d\r\n", sem_id);
     return 1;
 }
如果单独使用IPC_CREAT,则semget()要么返回新创建的信号量集的标识符,
要么返回系统中已经存在的同样的关键字值的信号量的标识符

2.信号量控制操作semctl()

int semctl(int semid, int semnum, int cmd, .../*union semun arg*/);
功能:控制信号量的信息。
返回值:成功返回0,失败返回-1;
参数:
	_semid   信号量的标志码(ID),也就是semget()函数的返回值;
	_semnum,  操作信号在信号集中的编号。从0开始。
	_cmd    命令,表示要进行的操作。
	
下面列出的这些命令来源于百度!
参数cmd中可以使用的命令如下:
	IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
	IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf
	参数。
	IPC_RMID将信号量集从内存中删除。
	GETALL用于读取信号量集中的所有信号量的值。
	GETNCNT返回正在等待资源的进程数目。
	GETPID返回最后一个执行semop操作的进程的PID。
	GETVAL返回信号量集中的一个单个的信号量的值。
	GETZCNT返回这在等待完全空闲的资源的进程数目。
	SETALL设置信号量集中的所有的信号量的值。
	SETVAL设置信号量集中的一个单独的信号量的值。
	
arg;4个参数是可选的;semunion :union semun的实例。
union semun {
 
   int   val;
   struct   semid_ds  *buf;
   unsigned short   *arrary;
};

3.信号量操作semop(P与V)

int    semop(int   semid ,struct    sembuf   *_sops ,size_t  _nsops);
功能:用户改变信号量的值。也就是使用资源还是释放资源使用权。
返回值:成功返回0,失败返回-1;
参数:
_semid : 信号量的标识码。也就是semget()的返回值。
_sops:是一个指向结构体数组的指针。	
		struct   sembuf{
		   unsigned short  sem_num;//第几个信号量,第一个信号量为0;
		   short  sem_op;//对该信号量的操作。
		   short _semflg;
		};
 
sem_num:  操作信号在信号集中的编号。第一个信号的编号为0;
sem_op : 
	如果其值为正数:		该值会加到现有的信号内含值中。通常用于释放所控资源的使用权
	如果sem_op的值为负数:而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或
						、等于sem_op的绝对值。通常用于获取资源的使用权;
	如果sem_op的值为0:   则操作将暂时阻塞,直到信号的值变为0。
_semflg: IPC_NOWAIT //对信号的操作不能满足时,semop()  
					不会阻塞,并立即返回,同时设定错误信息。累世trylock
		 IPC_UNDO //程序结束时(不论正常或不正常),保证信号值会被重设为semop()
			调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源
			解锁,造成该资源永远锁定。
 
nsops:操作结构的数量,恒大于或等于1

system v 例程


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值