简介

信号量本质上是一个计数器,用于协调多个进程(包括但不限于父子进程)对共享数据对象的读/写。

它不以传送数据为目的,主要是用来保护共享资源(共享内存、消息队列、socket连接池、数据库连接池等),保证共享资源在一个时刻只有一个进程独享。

信号量是一个特殊的变量,只允许进程对它进行等待信号和发送信号操作。最简单的信号量是取值0和1的二元信号量,这是信号量最常见的形式。

通用信号量(可以取多个正整数值)和信号量集方面的知识比较复杂,应用场景也比较少。

参考链接

https://freecplus.net/91049192da9e435a92209b287a220af8.html

https://www.zhihu.com/question/40562993/answer/87204567

Q&A

信号量和锁有什么区别

那互斥锁和数量为1的信号量有什么区别?

信号量(Semaphore)信号量就是一个停车场。当前值是停车场里还剩下多少个空车位。最大值是停车场里最多能容纳多少个车位。当汽车进入停车场时,首先要在门口排队(sem_wait),得到进入许可后才能进入。排队顺序原则上先到先得。每进一辆车,停车场就少了1个停车位,即信号量当前值-1。当前值为0时,停车场停满了,所有车不得进入统统在门口排队等。当一辆车离开后,释放其所占据的停车位(sem_post),信号量当前值+1信号量值得到释放后,如果门口有正在排队的车,那么就放进来,每放进来一个就重复前面的步骤。2. 互斥锁(Mutex)Mutex就是厕所的隔间门开着的时候谁都可以进但是一次只能进一个人进去后就从里面锁上门厕所里有人的时候其他人就要排队等,直到里面的人出来.

本质上信号量和锁是同一种东西

答主的评论

本质上没有区别。实际上在posix中是没有mutex只有semaphore的,stl通过自己的封装提供了std::mutex,std::recursive_mutex等互斥锁类以及std::lock_guard, std::unique_lock等辅助类来实现mutex功能。

code

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/sem.h> class CSEM {
private :
union semun{ // 用于信号操作的共同体
int val;
struct semid_ds *buf;
unsigned short *array;
};
int sem_id; // 信号量描述符号
public:
bool init(key_t key); // 如果信号量已经存在,获取信号量; 如果信号量不存在,则创建信号量并初始化。
bool wait(); // 等待信号量
bool post(); // 产生信号量
bool destory(); // 销毁信号量
}; int main(int argc, char **argv) {
CSEM sem; // 初始信号量
if (sem.init(0x5000) == false) {
printf("sem.init failed.\n");
return -1;
}
printf("sem.init ok\n"); // 等待信号量,等待成功后,将持有信号量,我感觉信号量也像锁一样
if (sem.wait() == false) {
printf("sem.wait failed.\n");
return -1;
}
printf("sem.wait ok\n"); sleep(30); // 在sleep的过程中,其他需要这个信号量的程序将会等待锁 // 挂出信号量,释放锁
if (sem.post() == false) {
printf("sem.post failed.\n");
return -1;
}
printf("sem.post ok\n"); // 销毁信号量
// if(sem.destory() == false)
// {
// printf("sem.destory failed.\n");
// return -1;
// }
// printf("sem.destory ok\n");
} bool CSEM::init(key_t key){
// 获取信号量
if((sem_id=semget(key, 1, 0640)) == -1){
// 如果信号量不存在,创建它
if(errno == 2){
if((sem_id = semget(key, 1, 0640 | IPC_CREAT)) == -1) {
perror("init 1 semget()");
return false;
}
// 信号量创建成功后,还需要把它初始化成可用的状态
union semun sem_union;
sem_union.val = 1;
if(semctl(sem_id, 0, SETVAL, sem_union) < 0) {
perror("init semctl()");
return false;
}
}else {
perror("init 2 semget()");
return false;
}
}
return true;
} bool CSEM::destory() {
if(semctl(sem_id, 0, IPC_RMID) == -1) {
perror("destroy semctl()");
return false;
}
return true;
} bool CSEM::wait() {
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id, &sem_b, 1) == -1) {
perror("wait semop()");
return false;
}
return true;
} bool CSEM::post() {
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
perror("post semop()");
return false;
}
return true;
}

相关信号量的函数

Linux中提供了一组函数用于操作信号量,程序中需要包含以下头文件:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
  1. semget函数

    semget函数用来获取或创建信号量,它的原型如下:
int semget(key_t key, int nsems, int semflg);

1)参数key是信号量的键值,typedef unsigned int key_t,是信号量在系统中的编号,不同信号量的编号不能相同,这一点由程序员保证。key用十六进制表示比较好。

2)参数nsems是创建信号量集中信号量的个数,该参数只在创建信号量集时有效,这里固定填1。

3)参数sem_flags是一组标志,如果希望信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。如果没有设置IPC_CREAT标志并且信号量不存在,就会返错误(errno的值为2,No such file or directory)。

4)如果semget函数成功,返回信号量集的标识;失败返回-1,错误原因存于error中。

示例:

1)获取键值为0x5000的信号量,如果该信号量不存在,就创建它,代码如下:

int semid=semget(0x5000,1,0640|IPC_CREAT);

2) 获取键值为0x5000的信号量,如果该信号量不存在,返回-1,errno的值被设置为2,代码如下:

int semid= semget(0x5000,1,0640);

2. semctl 函数

该函数用来控制信号量(常用语设置信号量的初始值和销毁信号量),他的原型如下

int semctl(int semid, int sem_num, int command, ...);

1)参数semid是由semget函数返回的信号量标识。

2)参数sem_num是信号量集数组上的下标,表示某一个信号量,填0。

3)参数cmd是对信号量操作的命令种类,常用的有以下两个:

IPC_RMID:销毁信号量,不需要第四个参数;

SETVAL:初始化信号量的值(信号量成功创建后,需要设置初始值),这个值由第四个参数决定。第四参数是一个自定义的共同体,如下:

// 用于信号灯操作的共同体。
union semun
{
int val;
struct semid_ds *buf;
unsigned short *arry;
};

4)如果semctl函数调用失败返回-1;如果成功,返回值比较复杂,暂时不关心它。

示例:

1)销毁信号量。

semctl(semid,0,IPC_RMID);

2)初始化信号量的值为1,信号量可用。

  union semun sem_union;
sem_union.val = 1;
semctl(semid,0,SETVAL,sem_union);

3、semop函数

该函数有两个功能:1)等待信号量的值变为1,如果等待成功,立即把信号量的值置为0,这个过程也称之为等待锁;2)把信号量的值置为1,这个过程也称之为释放锁。

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

1)参数semid是由semget函数返回的信号量标识。

2)参数nsops是操作信号量的个数,即sops结构变量的个数,设置它的为1(只对一个信号量的操作)。

3)参数sops是一个结构体,如下:

struct sembuf
{
short sem_num; // 信号量集的个数,单个信号量设置为0。
short sem_op; // 信号量在本次操作中需要改变的数据:-1-等待操作;1-发送操作。
short sem_flg; // 把此标志设置为SEM_UNDO,操作系统将跟踪这个信号量。
// 如果当前进程退出时没有释放信号量,操作系统将释放信号量,避免资源被死锁。
};

示例:

1)等待信号量的值变为1,如果等待成功,立即把信号量的值置为0;

  struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
semop(sem_id, &sem_b, 1);

2)把信号量的值置为1。

  struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
semop(sem_id, &sem_b, 1);

其他操作命令

用ipcs -s可以查看系统的信号量,内容有键值(key),信号量编号(semid),创建者(owner),权限(perms),信号量数(nsems)。

用ipcrm sem 信号量编号,可以手工删除信号量

code

使用信号量来访问共享内存

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h> class CSEM {
private :
union semun{ // 用于信号操作的共同体
int val;
struct semid_ds *buf;
unsigned short *array;
};
int sem_id; // 信号量描述符号
public:
bool init(key_t key); // 如果信号量已经存在,获取信号量; 如果信号量不存在,则创建信号量并初始化。
bool wait(); // 等待信号量
bool post(); // 产生信号量
bool destory(); // 销毁信号量
}; int main(int argc, char **argv) {
CSEM sem;
int shmid; // 共享内存标识符
// 创建共享内存,键值为0x5000, 共1024字节
if( (shmid = shmget((key_t)0x5000, 1024, 0640|IPC_CREAT) ) == -1){
printf("shmat(0x5000) failed\n");
return -1;
}
char *ptext = 0; // 用于指向共享内存的指针 // 将共享内存链接到当前进程的地址空间, 由ptext 指针指向它
ptext = (char *)shmat(shmid, 0, 0);
// 初始信号量
if (sem.init(0x5000) == false) {
printf("sem.init failed.\n");
return -1;
}
printf("sem.init ok\n"); // 等待信号量,等待成功后,将持有信号量,我感觉信号量也像锁一样
if (sem.wait() == false) {
printf("sem.wait failed.\n");
return -1;
}
printf("sem.wait ok\n"); // 操作本程序的ptext指针,就是操作共享内存
printf("写入前: %s\n", ptext);
// sprintf(ptext, "本程序的进程号是: %d", getpid());
strcpy(ptext, argv[1]);
// sprintf(ptext, "本程序的进程号是: %d", getpid());
printf("写入后: %s\n", ptext); sleep(30); // 在sleep的过程中,其他需要这个信号量的程序将会等待锁 // 挂出信号量,释放锁
if (sem.post() == false) {
printf("sem.post failed.\n");
return -1;
}
printf("sem.post ok\n"); // 把共享内存从当前进程中分离
shmdt(ptext); // 销毁信号量
// if(sem.destory() == false)
// {
// printf("sem.destory failed.\n");
// return -1;
// }
// printf("sem.destory ok\n");
} bool CSEM::init(key_t key){
// 获取信号量
if((sem_id=semget(key, 1, 0640)) == -1){
// 如果信号量不存在,创建它
if(errno == 2){
if((sem_id = semget(key, 1, 0640 | IPC_CREAT)) == -1) {
perror("init 1 semget()");
return false;
}
// 信号量创建成功后,还需要把它初始化成可用的状态
union semun sem_union;
sem_union.val = 1;
if(semctl(sem_id, 0, SETVAL, sem_union) < 0) {
perror("init semctl()");
return false;
}
}else {
perror("init 2 semget()");
return false;
}
}
return true;
} bool CSEM::destory() {
if(semctl(sem_id, 0, IPC_RMID) == -1) {
perror("destroy semctl()");
return false;
}
return true;
} bool CSEM::wait() {
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id, &sem_b, 1) == -1) {
perror("wait semop()");
return false;
}
return true;
} bool CSEM::post() {
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
perror("post semop()");
return false;
}
return true;
}

C++ linux 信号量的更多相关文章

  1. Linux信号量详解

    1.什么是信号量信号量是一种特殊的变量,访问具有原子性.只允许对它进行两个操作:1)等待信号量当信号量值为0时,程序等待:当信号量值大于0时,信号量减1,程序继续运行.2)发送信号量将信号量值加1. ...

  2. Java中处理Linux信号量

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/5976361. ...

  3. Linux信号量同步共享内存实验.

    Linux信号量同步共享内存实验. Linux信号量同步共享内存实验. 简述 程序流程 信号量和共享内存的系统函数 信号量系统函数及接口 共享内存系统函数及接口 写程序 读程序 简述 本文主要内容是自 ...

  4. linux信号量之进程间同步

    概念 linux信号量: 允许多个线程同时进入临界区,可以用于进程间的同步. 和互斥锁(mutex)的区别: 互斥锁只允许一个线程进入临界区. 所在头文件: semaphore.h 主要函数 初始化函 ...

  5. Linux 信号量同步编程

    前一篇文章概述了Linux 系统中信号量互斥编程,这篇文章正好是前一篇的姊妹篇----信号量同步.说它们是姊妹篇是因为它们都是利用了内核的信号量机制实现了进程间的通信.因为两者所解决的问题不同,因此它 ...

  6. Linux 信号量互斥编程

    所谓信号量,其实就是一个数字.内核给这个数字赋予一定的含义,让它等于不同的值时所表示的意义不同.这样就可以用它来标示某种资源是否正被使用.信号的分类其实挺多的,主要还是二值和计数器.这里讨论二值 现在 ...

  7. [转] linux 信号量之SIGNAL

    我们可以使用kill -l查看所有的信号量解释,但是没有看到SIGNAL 0的解释. [root@testdb~]# kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) ...

  8. linux信号量与完成量

    信号量:    是用于保护临界区的一种常用方法,它的使用和自旋锁类似.与自旋锁相同,只有得到信号量的进程才能执行 临界区的代码.但是与自旋锁不同的是,当获取不到信号量时,进程不会原地打转而是进入休眠等 ...

  9. linux信号量(转载)

    本文转载自http://blog.csdn.net/qinxiongxu/article/details/7830537 信号量一.什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个时刻只 ...

  10. 最全面的linux信号量解析

    信号量 一.什么是信号量 信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程) 所拥有. 信号量的值为正的时候,说明它空闲.所测试的线程可以锁定而使用它.若为0,说明 它被占用, ...

随机推荐

  1. 基于源码分析 HikariCP 常见参数的具体含义

    HikariCP 是目前风头最劲的 JDBC 连接池,号称性能最佳,SpringBoot 2.0 也将 HikariCP 作为默认的数据库连接池. 要想用好 HikariCP,理解常见参数的具体含义至 ...

  2. MySQL 的覆盖索引是什么?

    MySQL 的覆盖索引是什么? 覆盖索引(Covering Index)是指索引本身包含了查询所需的所有字段数据,从而无需再回表查询的数据访问方式.这种优化能够显著提升查询性能. 1. 覆盖索引的特点 ...

  3. 移动开发框架,Hammer.js 移动设备触摸手势js库

    原文:https://www.cnblogs.com/zhwl/p/3525238.html hammer.js是一个多点触摸手势库,能够为网页加入Tap.Double Tap.Swipe.Hold. ...

  4. 使用“一次开发,多端部署”,实现Pura X阔折叠的全新设计

    3月20日,华为发布业界首款阔折叠手机Pura X,其独特的16:10阔型屏设计,为用户带来焕然一新的体验.然而随着鸿蒙生态设备类型的日益丰富,不同类型设备的应用适配成为开发者面临的一大挑战.为此,华 ...

  5. 【记录】Docker|Ubuntu Docker 修改dockerfile换源、主机共享网络解决apt update 失败

    本文测试环境:虚拟机 Ubuntu20.04 Docker20 1 修改dockerfile dockerfile第二行后面加如下内容,一键换源: RUN sed -i s:/archive.ubun ...

  6. P2779 [AHOI2016初中组] 黑白序列题解

    题意: 小可可准备了一个未完成的黑白序列,用 B 和 W 表示黑色和白色,用 ? 表示尚未确定. 他希望知道一共有多少种不同的方法,在决定了每一个 ? 位置的颜色后可以得到一个小雪喜欢的黑白序列. 其 ...

  7. 仿EXCEL插件,智表ZCELL产品V2.1 版本发布,增加列标、行标自定义设置及单元格属性自定义相关功能,优化公式随动功能

    详细请移步 智表(ZCELL)官网www.zcell.net 更新说明  这次更新主要应用户要求,增加列标.行标自定义设置及单元格属性自定义相关功能,优化公式随动功能 ,欢迎大家体验使用. 本次版本更 ...

  8. RPC实战与核心原理之时钟轮

    时钟轮在RPC中的应用 回顾 在分布式环境下,RPC 框架自身以及服务提供方的业务逻辑实现,都应该对异常进行合理地封装,让使用方可以根据异常快速地定位问题:而在依赖关系复杂且涉及多个部门合作的分布式系 ...

  9. RPC实战与核心原理之分布式环境下如何快速定位问题

    分布式环境下如何快速定位 回顾 如何建立可靠的安全体系,关键点就是"鉴权",我们可以通过统一的鉴权服务动态生成秘钥,提高 RPC 调用的安全性. 分布式环境下定位问题有哪些困难 举 ...

  10. Windows系统 C/C++程序编译后首次执行时间很长 断网则正常执行 的解决方法

    Windows系统 C/C++程序编译后首次执行时间很长 断网则正常执行 的解决方法 如果您遇到此类问题,可以与我联系.我可尝试帮助您解决此问题,2SADFC4F%2$@!0$#@$%2!@#$3#! ...