目录
1、信号量介绍
2、信号量SystemV介绍
2.1 信号量函数
2.2 信号量C++代码实现
3、信号量Posix介绍
3.1 无名信号量
3.2 有名信号量
信号量是一种计数器,用在多进程、多线程的共享资源访问控制上面,防止多进程、多线程对共享资源的同时读写。信号量种类有SystemV信号量和Posix信号量。
信号量SystemV是Linux操作系统的内核对象,由内核同一管理,它的生命周期不依赖创建的进程,即使进程退出,信号量依然存在。信号量SystemV常用于多进程间的同步。最简单的信号信号量是只能取0和1的变量,也叫做二进制信号量,可以去多个正数的信号量被称为通用信号量,本文主要讲解二进制信号量。
头文件sem.h
(1)semget函数
/*
* @brief: 创建一个新信号量或取得一个已有的信号量。
* @param __key: 信号量标识符,值具有唯一性,不相关进程可以通过它访问一个信号量,常用16进制表示
* @param __nsems: 指定创建信号量数目,常为1,获取已有信号量时值为0.
* @param __semflg:一组标志,它由信号量的访问权限和创建标志组成。
常用的标志:(1)0640|IPC_CREAT 创建一个新信号量,即使创建的信号量已存在也不会报错;
(2)0640|IPC_CREAT|IPC_EXCL 创建的新信号量存在时,如果创建的信号量存在,会报错。
* @return int :成功,返回创建成功的信号量标识符;失败:返回-1,并且将errno设置为2
*/
int semget (key_t __key, int __nsems, int __semflg)
(2)semctl函数
/*
* @brief 用来控制信号量的信息,比如信号量值设置、从系统中删除信号量
* @param __semid:semget函数返回的信号量标识符
* @param __semun:代表给该信号量集中的第几个信号量设置初始值。
* @param __cmd: 信号量操作命令,常用的命令有SETVAL、IPC_RMID
一般cmd有以下几种:
IPC_RMID 将信号量集从内存中删除。
SETVAL 设置信号量集中的一个单独的信号量的值。
IPC_STAT 读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
IPC_SET 设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
GETALL 用于读取信号量集中的所有信号量的值。
GETNCNT 返回正在等待资源的进程数目。
GETPID 返回最后一个执行semop操作的进程的PID。
GETVAL 返回信号量集中的一个单个的信号量的值。
GETZCNT 返回这在等待完全空闲的资源的进程数目。
SETALL 设置信号量集中的所有的信号量的值。
@param ...:如果有第四个参数,则该参数格式通常为union semum结构,定义如下:
union semun
{
int val; /* Value for SETVAL */通常就要它就够了
struct semid_ds *buf;
unsigned short *arry;
};
赋值形式:semun.val = 1 //设置信号灯的值为1,表示有一个资源可用
*/
int semctl (int __semid, int __semnum, int __cmd, ...)
(3)semop函数
struct sembuf
{
unsigned short int sem_num; /* semaphore number */ 信号在信号集中索引,0代表第一个信号
short int sem_op; /* semaphore operation */ 操作类型。
P操作:sem_op = -1,
V操作:sem_op = 1
short int sem_flg; /* operation flag */ 操作标志。 通常为SEM_UNDO,使操作系统跟踪信号,
并在进程没有释放该信号量而终止时,操作系统释放信号量
};
/上面sem_op参数补充//
sem_op > 0 信号加上 sem_op 的值,表示进程释放控制的资源;
sem_op = 0 如果没有设置 IPC_NOWAIT,则调用进程进入睡眠状态,直到信号量的值为0;否则进程不会睡
眠,直接返回 EAGAIN
sem_op < 0 信号加上 sem_op 的值。若没有设置 IPC_NOWAIT ,则调用进程阻塞,直到资源可用;否则进
程直接返回EAGAIN
///
/*
* @brief:用来改变信号量的值,该函数具有原子性的。
* @param __semid:由semget函数返回的信号量标识符
* @param __sops: 指向由sembuf结构表示的信号量操作数组。
* @param __nspos: 数组元素的个数
* @return int 成功返回0,失败返回-1
*/
int semop (int __semid, struct sembuf *__sops, size_t __nsops)
#include
#include
#include
#include
#include
#include
class Semaphore
{
private:
union semVar
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
int sem_id;
public:
bool init(key_t key);
bool wait();
bool post();
bool destroy();
};
bool Semaphore::init(key_t key)
{
sem_id = semget(key,0,0640);
if(-1 == sem_id)
{
if(2 == errno)
{
sem_id = semget(key,1,0640|IPC_CREAT);
if(-1 == sem_id)
{
std::cout << "init 1 semget() error" << std::endl;
return false;
}else
{
union semVar semTmp;
semTmp.val = 1;
if(semctl(sem_id,0,SETVAL,semTmp) < 0)
{
std::cout << "init 1 semctl() error" << std::endl;
return false;
}else
{
return true;
}
}
}else
{
std::cout << "init 2 semget() error" << std::endl;
return false;
}
}else
{
return true;
}
}
bool Semaphore::wait(){
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if(-1 == semop(sem_id,&sem_b,1))
{
std::cout << "wait semop failed." << std::endl;
return false;
}
return true;
}
bool Semaphore::post()
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if(-1 == semop(sem_id,&sem_b,1))
{
std::cout << "post semop failed." << std::endl;
return false;
}
return true;
}
bool Semaphore::destroy()
{
if(semctl(sem_id,0,IPC_RMID) == -1)
{
std::cout << "destroy semctl failed." << std::endl;
return false;
}
return true;
}
int main()
{
Semaphore sem;
//初始化信号灯
if(false == sem.init(0x5000))
{
std::cout << "sem init failed." << std::endl;
return -1;
}
std::cout << "sem init ok." << std::endl;
//等待信号灯挂出,等待成功后,将持有锁
if(false == sem.wait())
{
std::cout << "sem wait failed." << std::endl;
return -1;
}
std::cout << "sem wait ok." << std::endl;
sleep(5);
//挂出信号灯
if(false == sem.post())
{
std::cout << "sem post failed." << std::endl;
return -1;
}
std::cout << "sem post ok." << std::endl;
//最后进程结束时,销毁信号灯
//if(false == sem.destroy())
//{
// std::cout << "sem destroy failed." << std::endl;
// return -1;
//}
//std::cout << "sem destroy ok." << std::endl;
return 0;
}
运行结果如下:
Posix是“可移植操作系统接口(Portable Operating System Interface )的首字母简写,但它并不是一个单一的标准,而是一个电气与电子工程学会即IEEE开发的一系列标准,它还是由ISO(国际标准化组织)和IEC(国际电工委员会)采纳的国际标准。Posix信号分为无名信号量和有名信号量。
Posix的无名信号量一般用于线程同步, 无名信号量是基于用户空间内存的, 它的存续与内存的存续直接相关,无名信号量的api为:sem_init、sem_destroy。
Posix的有名信号量一般用于进程同步, 它也是由内核统一管理的,因此有名信号量的存续也是由内核决定的,与进程是否退出无关。有名信号量的api为:sem_open、sem_close、sem_unlink。
附加:
信号量知识也可参考下面链接:
信号量详解_信号量取值范围怎么求_FangYwang的博客-CSDN博客
信号量--System V信号量 与 Posix信号量-云社区-华为云 (huaweicloud.com)
System V信号量 与 Posix 信号量_分别使用 posix 信号量机制和 system v 信号量机制实现。_luren2015的博客-CSDN博客
SystemV信号量与POSIX信号量简介_执假以为真的博客-CSDN博客
Linux IPC 信号量:PV原语,PV操作,函数semget,函数semop、函数semctl、生产者和消费者模型_linux semop_不会code的菜鸟的博客-CSDN博客
信号量的操作——semop函数 - 小葫芦藤 - 博客园 (cnblogs.com)
用信号量为共享内存添加同步机制 - tp_16b - 博客园 (cnblogs.com)