信号量--System V信号量 与 Posix信号量

时间:2022-07-23
本文章向大家介绍信号量--System V信号量 与 Posix信号量,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

信号量是什么

信号量是一种计数器,用来控制对多个进程/线程共享的资源进行访问。常和锁一同使用。 在某个进程/线程正在对某个资源进行访问时,信号量可以阻止另一个进程/线程去打扰。 生产者和消费者模型是信号量的典型使用。

为什么信号量分两套(两套有什么区别)

简要的说,Posix是“可移植操作系统接口(Portable Operating System Interface )的首字母简写,但它并不是一个单一的标准,而是一个电气与电子工程学会即IEEE开发的一系列标准,它还是由ISO(国际标准化组织)和IEC(国际电工委员会)采纳的国际标准。而System v是Unix操作系统众多版本的一个分支,它最初是由AT&T在1983年第一次发布,System v一共有四个版本,而最成功的是System V Release 4,或者称为SVR4。这样看来,一个是Unix 的标准之一(另一个标准是Open Group),一个是Unix众多版本的分支之一(其他的分支还有Linux跟BSD),应该来说,Posix标准正变得越来越流行,很多厂家开始采用这一标准。

那么两者有什么区别,或者说,应用场景: 1、POSIX信号量常用于线程;system v信号量常用于进程的同步。

2、从使用的角度,System V 信号量的使用比较复杂,而 POSIX 信号量使用起来相对简单。

3、对 POSIX 来说,信号量是个非负整数。而 System V 信号量则是一个或多个信号量的集合,它对应的是一个信号量结构体,这个结构体是为 System V IPC 服务的,信号量只不过是它的一部分。

4、Posix信号量是基于内存的,即信号量值是放在共享内存中的,它是由可能与文件系统中的路径名对应的名字来标识的。而System v信号量则是基于内核的,它放在内核里面。

5、POSIX 信号量的头文件是 <semaphore.h>,而 System V 信号量的头文件是 <sys/sem.h>。

6、Posix还有有名信号量,一般用于进程同步, 有名信号量是内核持续的。

【本文不对有名Posix做过多介绍】

怎么实现呢?

1、新建信号量

System V

Posix(无名)

int semget(key_t key,int nsems,int semflg);

int sem_init(sem_t *sem,int pshared,int values);

  • system V
#include<sys/type.h>
#include<sys/ipc.h>
#include<sys/sem.h>

int semget(key_t key,int nsems,int semflg);
/*
nsems:创建多少个
semflg:IPC_CREAT、IPC_EXCL;
*/

利用System V函数包装建立信号量的代码;

typedef int sem_t;
union semun
{
	int val;				
	struct semid_ds *buf;	//semid_ds的指针
	unsigned short *array;	//数组类型
} arg;						//定义一个全局变量

sem_t CreateSem(key_t key,int value)
{
	union semun sem;	//信号量结构变量
	sem_t sem_id;
	sem.val = value;	//设置初始值

	sem_id = semget(key,0,IPC_CREAT|0666);	//获取信号量id
	if(sem_id == -1)
	{
		printf("create sem failedn");
	 	exit(-1);
	}
	semctl(sem_id,0,SETVAL,sem);			//发送命令,建立value个初始信号量
	return sem_id;
}
  • Posix信号量:直接用那个函数就好了,可以加一个报错保险。 【后面的栗子都一样,Posix就这么简单】

2、PV操作(增减信号量)

System V

Posix(无名)

int semop(int semid,struct sembuf *sops,unsigned nsops);

sem_post(sem_t *sem); sem_wait(sem_t *sem);

  • System V
/*
参数释义:
struct sembuf
{
	ushort sem_num;	//信号量的编号
	short sem_op;	//信号量的操作 {正负零}	
	short sem_flg;	//信号量的操作标志 {NOWAIT}
};
//sem_op取0表示将信号量设为睡眠状态,直到信号量的值为0为止

nsops:该数组中操作的个数
*/
int Sem_P(sem_t semid)
{
	struct sembuf sops = {0,+1,IPC_NOWAIT};
	return (semop(semid,&sops,1));
}
intSem_V(sem_t semid)
{
	struct sembuf sops = {0,-1,IPC_NOWAIT};
	return (semop(semid,&sops,1));
}

P操作:进行增加一个信号量的值的操作 V操作:进行减少一个信号量的值的操作

  • Posix P:sem_post(sem_t *sem); V:sem_wait(sem_t *sem);

3、控制信号量参数

System V

Posix

int semctl(int semid,int semnum,int cmd,…) ;

/

#include<sys/type.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int semctl(int semid,int semnum,int cmd,...);

/*
该函数是在信号量的集合上执行控制操作函数。
参数释义:
semnum:将要被执行操作的信号量编号。对于集合中的第一个信号量,它的值为0。
cmd:
	IPC_STAT:获取某个集合的semid_ds结构,并把它存在semun联合体的buf中。
	IPC_SET:设置某个集合的semid_ds中的ipc_perm成员的值。该命令所取值从buf中获取。
	IPC_RMID:从内核删除该集合。
	GETTALL:用于获取集合中所有的信号量的值,存放在semun联合体的array中。
	GETPID:返回最后一次调用semop的PID。
	GETVAL:返回集合中某个信号量的值。
	GETZCNT:返回正在等待资源利用率达到百分百的进程的数目。
	SETALL:把集合中所有信号量的值设置为semun联合体中array中的值。
	SETVAL:把集合中某个信号量的值设置为semun联合体中val的值。
*/

//栗子就不放了

4、销毁信号量

System V

Posix(无名)

ctl自定义

int sem_destory(sem_t *sem);

  • System V
void DestroySem(sem_t semid)
{
	union semun sem;
	sem.val = 0;
	se,ctl(semid,0,IPC_RMID,sem);
}

最后,放一串生产消费者的代码

//实现线程互斥

#include <iostream>
#include<unistd.h>
#include<stdlib.h>
#include<semaphore.h>
#include<errno.h>
#include<pthread.h>
using namespace std;
 
sem_t sem;
void* productor(void* arg)
{
    while(1)
    {
        sem_wait(&sem);
        cout << "create noodle!!!" << endl;
        sem_post(&sem);
        usleep(10);
    }
    return NULL;
}
 
void* consumer(void* arg)
{
    while(1)
    {
        sem_wait(&sem);
        cout << "eat noodle!!!" << endl;
        sem_post(&sem);
        usleep(10);
    }
    return NULL;
}
int main()
{
    pthread_t tid1,tid2;
    
    sem_init(&sem, 0, 1);
 
    pthread_create(&tid1, NULL, productor, NULL);
    pthread_create(&tid2, NULL, consumer, NULL);
 
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    
    sem_destroy(&sem);
    return 0;
}