信号量是什么
信号量是一种计数器,用来控制对多个进程/线程共享的资源进行访问。常和锁一同使用。
在某个进程/线程正在对某个资源进行访问时,信号量可以阻止另一个进程/线程去打扰。
生产者和消费者模型是信号量的典型使用。
为什么信号量分两套(两套有什么区别)
简要的说,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 failed\n");
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;
}