Linux系统编程系列之进程间通信-信号量组
一、什么是信号量组
信号量组是信号量的一种, 是system-V三种IPC对象之一,是进程间通信的一种方式。
二、信号量组的特性
信号量组不是用来传输数据的,而是作为“旗语”,用来协调各进程或者线程工作的。信号量组可以一次性在其内部设置多个信号量,而信号量本质上是一个数字,用来表征一种资源的数量,当多个进程或者线程争夺这些稀缺资源的时候,信号量用来保证他们合理地,秩序地使用这些资源,而不会陷入逻辑谬误之中。
三、信号量组的使用场景
1、生产者-消费者模式
2、进程间同步
3、进程间通信
四、函数API接口
1、创建或者打开SEM对象
// 创建或打开SEM对象 int semget(key_t key, int nsems, int semflg);// 接口说明:参数key:SEM对象键值参数nsems:信号量组内的信号量元素个数参数semflg:创建选项IPC_CREAT:如果该key对应的信号量不存在,则创建IPC_EXCL:如果该key对应的信号量已存在,则报错mode:信号量的访问权限创建信号量时,还受到以下系统信息的影响: 1、SEMMNI:系统中信号量的总数最大值 2、SEMMSL:每个信号量中信号量元素的个数最大值 3、SEMMNS:系统中所有信号量中的信号量元素的总数最大值
2、P/V操作
对于信号量而言,最重要的作用是用来表征对应资源的数量,所谓的P/V操作就是对资源数量进行 +n/-n 操作,既然只是个加减法,那么为什么不使用普通的整型数据呢,原因是:
(1)、整型数据的加减操作不具有原子性,即操作可能被中断
(2)、普通加减法无法提供阻塞特性,而申请资源不可得时应该进入阻塞
// PV操作 int semop(int semid, struct sembuf *sops, size_t nsops);// 接口说明参数semid:SEM对象ID参数sops:PV操作结构体sembuf数组参数nsops:PV操作结构体数组元素个数返回值:成功 0,失败 -1PV操作结构体定义如下: struct sembuf {unsigned short sem_num; // 信号量元素序号(数组下标)short sem_op; // 操作参数short sem_flg; // 操作选项 }根据sem_op的数值,信号量操作分成3种情况:(1)当sem_op大于0时:当进行V操作(释放),即信号量元素的值(semval)将会被加上sem_op的值。如果SEM_UNDO被设置了,那么该V操作将会被系统记录,V操作永远不会导致进程阻塞。(2)当sem_op等于0时:进行等零操作,如果此时semval恰好为零,则semop()立即成功返回,否则如果IPC_NOWAIT被设置,则立即出错返回并将errno设置为EAGAIN,否则将使得进程进入睡眠,直到以下情况发生:[1]semval变为0[2]信号量被删除 (将导致semop()出错退出,错误码为EIDRM)[3]收到信号 (将导致semop()出错退出,错误码为EINTR)(3)当sem_op小于0时(申请资源):进行P操作,即信号量元素的值(semval)将会被减去sem_op的绝对值。如果semval大于或等于sem_op的绝对值,则semop()立即成功返回,semval的值将减去sem_op的绝对值,并且如果SEM_UNDO被设置了,那么该P操作将会被系统记录。 如果semval小于sem_op的绝对值并且设置了IPC_NOWAIT,那么semop()将会出错返回且将错误码置为EAGIN,否则将使得进程进入睡眠,直到以下情况发生:[1]semval的值变得大于或者等于sem_op的绝对值[2]信号量被删除 (将导致semop()出错退出,错误码为EIDRM)[3]收到信号 (将导致semop()出错退出,错误码为EINTR)
3、删除SEM对象
// 删除SEM对象 int semctl(int semid, int semnum, int cmd, ...);// 接口说明semid:信号量组的IDsemnum:信号量组内的元素序号(从0开始)cmd;操作命令字IPC_STAT:获取信号量组的一些信息,放入结构体semid_ds中IPC_SET:将结构体semid_ds中指定的信息,设置到信号量组中IPC_RMID:删除指定的信号量组GETALL:获取所有信号量元素的值SETALL:设置所有信号量元素的值GETVAL:获取第semnum个信号量元素的值SETVAL:设置第semnum个信号量的值
五、信号量组使用步骤
1、使用ftok(),获取IPC通信对象KEY值
2、使用semget(),获取SEM对象ID,并判断是否需要进行初始化
3、使用semop(),进行P/V操作,操作信号量组
4、使用命令或者函数删除信号量组
六、案例
使用信号量组结合共享内存的方式完成两个进程的数据收发。
// 信号量组结合共享内存的案例#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/shm.h> #include <errno.h>// 编译时分两个版本,一个直接编译,另外一个把A的宏定义注释,把B的宏定义展开 #define A 1 //#define B 1 // 编译第二版本时,请去掉前面的注释,同时注释A的宏定义// 注意A进程的P信号量与B进程的V信号量相对应,所以要修改信号量序号的下标 #if A #define DATA_P_NUM 0 #define DATA_V_NUM 1 #define SPACE_P_NUM 2 #define SPACE_V_NUM 3#elif B #define DATA_P_NUM 1 #define DATA_V_NUM 0 #define SPACE_P_NUM 3 #define SPACE_V_NUM 2 #endif#define SEM_NUM 4 // 4个信号量 #define SEM_KEY 0x01 #define SHM_KEY 0x02 #define SHM_SIZE 4096int sem_id = -1; // 映射的虚拟地址 char *shm_addr = NULL;// 信号量组初始化 int sem_init(void) {// 1、获取IPC对象的KEY值key_t sem_key = ftok("./", SEM_KEY);if(sem_key == -1){perror("ftok fail");return -1;}// 2、获取SEM对象的ID, 申请4个信号量sem_id = semget(sem_key, SEM_NUM, IPC_EXCL | IPC_CREAT | 0666);// 如果已经存在就不需要初始化,直接获取if(sem_id == -1 && errno == EEXIST){// 直接获取SEM对象IDsem_id = semget(sem_key, SEM_NUM, IPC_CREAT | 0666);if(sem_id == -1){perror("semget fail");return -1;}}// 不存在则需要在获取SEM对象ID后进行初始化else if(sem_id > 0){sem_id = semget(sem_key, SEM_NUM, IPC_CREAT | 0666);if(sem_id == -1){perror("semget fail");return -1;}// 初始化semctl(sem_id, DATA_P_NUM, SETVAL, 0); // 初始值为0semctl(sem_id, DATA_V_NUM, SETVAL, 0); // 初始值为0semctl(sem_id, SPACE_P_NUM, SETVAL, 1); // 初始值为1semctl(sem_id, SPACE_V_NUM, SETVAL, 1); // 初始值为1}else{perror("semget fail");return -1;} }// 共享内存初始化 int shm_init(void) {// 1、获取KEY值key_t shm_key = ftok("./", 1);if(shm_key == -1){perror("ftok fail");return -1;}// 2、指定共享内存,获取共享内存对象IDint shm_id = shmget(shm_key, SHM_SIZE, IPC_CREAT | 0666);if(shm_id == -1){perror("shmget fail");return -1;}// 3、映射共享内存shm_addr = (char*)shmat(shm_id, NULL, 0);if(shm_addr == (void*)-1){perror("shmat fail");return -1;} }int main(int argc, char *argv[]) {int ret = 0;ret = sem_init();if(ret == -1){return -1;}ret = shm_init();if(ret == -1){return -1;}// 接收数据, 数据-1struct sembuf Data_P = {.sem_flg = SEM_UNDO,.sem_num = DATA_P_NUM,.sem_op = -1};// 发送数据, 数据+1struct sembuf Data_V = {.sem_flg = SEM_UNDO,.sem_num = DATA_V_NUM,.sem_op = 1};// 占用空间, 空间-1struct sembuf Space_P = {.sem_flg = SEM_UNDO,.sem_num = SPACE_P_NUM,.sem_op = -1};// 释放空间 空间+1struct sembuf Space_V = {.sem_flg = SEM_UNDO,.sem_num = SPACE_V_NUM,.sem_op = 1};pid_t pid = fork();// 父进程负责发送数据if(pid > 0){while(1){// 申请空间,P操作printf("wait Space_P...\n");semop(sem_id, &Space_P, 1);printf("get Space_P\n");printf("please input data: \n");fgets(shm_addr, SHM_SIZE, stdin);// 释放数据,V操作semop(sem_id, &Data_V, 1);printf("set Data_V, send data success\n");}}// 子进程负责接收数据else if(pid == 0){while(1){// 申请数据,P操作printf("wait Data_P...\n");semop(sem_id, &Data_P, 1);printf("read Data: %s", shm_addr);memset(shm_addr, 0, SHM_SIZE);// 释放空间,V操作semop(sem_id, &Space_V, 1);printf("set Space_V\n");}}else{perror("fork fail");return -1;}return 0; }
注:编译时,编译两个版本,一个直接编译,另外一个需要注释A的宏定义,然后展开B的宏定义后才能编译第二个版本。
分析:具体的PV操作这里不讲解,为什么要申请4个信号量,这个要讲明白的话,很难,有空再出另外一篇博客讲,敬请留意。
七、总结
信号量组只能作为一种信号,不能用来传递数据,多用于使用P/V操作的场景,可以同时操作多个信号量,但是要实现传递数据,必须配合其他通信方式,如共享内存。可以结合案例来加深对信号量组的理解。
相关文章:
 
Linux系统编程系列之进程间通信-信号量组
一、什么是信号量组 信号量组是信号量的一种, 是system-V三种IPC对象之一,是进程间通信的一种方式。 二、信号量组的特性 信号量组不是用来传输数据的,而是作为“旗语”,用来协调各进程或者线程工作的。信号量组可以一次性在其内…...
 
centos 6使用yum安装软件
1. 执行以下命令,查看当前操作系统 CentOS 版本。 cat /etc/centos-release返回结果如下图所示,则说明当前操作系统版本为 CentOS 6.9。 2. 执行以下命令,编辑 CentOS-Base.repo 和CentOS-Epel.repo文件。 vim /etc/yum.repos.d/CentOS-Bas…...
 
maven无法下载时的解决方法——笔记
右键项目然后点击创建setting.xml(因为现在创建了,所以没显示了,可以直接点击打开setting.xml) 然后添加 <mirror><id>nexus-aliyun</id><mirrorOf>*,!jeecg,!jeecg-snapshots</mirrorOf><name…...
Java Spring Boot 开发框架
Spring Boot是一种基于Java编程语言的开发框架,它的目标是简化Java应用程序的开发过程。Spring Boot提供了一种快速、易于使用的方式来创建独立的、生产级别的Java应用程序。本文将介绍Spring Boot的特性、优势以及如何使用它来开发高效、可靠的应用程序。 一、简介…...
Pytorch学习记录-1-张量
1. 张量 (Tensor): 数学中指的是多维数组; torch.Tensor data: 被封装的 Tensor dtype: 张量的数据类型 shape: 张量的形状 device: 张量所在的设备,GPU/CPU requires_grad: 指示是否需要计算梯度 grad: data 的梯度 grad_fn: 创建 Tensor 的 Functio…...
 
paddle2.3-基于联邦学习实现FedAVg算法-CNN
目录 1. 联邦学习介绍 2. 实验流程 3. 数据加载 4. 模型构建 5. 数据采样函数 6. 模型训练 1. 联邦学习介绍 联邦学习是一种分布式机器学习方法,中心节点为server(服务器),各分支节点为本地的client(设备&#…...
nuiapp保存canvas绘图
要保存一个 Canvas 绘图,可以使用以下步骤: 获取 Canvas 元素和其绘图上下文: var canvas document.getElementById("myCanvas"); var ctx canvas.getContext("2d");使用 Canvas 绘图 API 绘制图形。 使用 toDataUR…...
 
Object.defineProperty()方法详解,了解vue2的数据代理
假期第一篇,对于基础的知识点,我感觉自己还是很薄弱的。 趁着假期,再去复习一遍 Object.defineProperty(),对于这个方法,更多的还是停留在面试的时候,面试官问你vue2和vue3区别的时候,不免要提一提这个方法…...
 
Linux 磁盘管理
Linux 系统的磁盘管理直接关系到整个系统的性能表现。磁盘管理常用三个命令为: df、du 和 fdisk。 df df(英文全称:disk free)。df 命令用于显示磁盘空间的使用情况,包括文件系统的挂载点、总容量、已用空间、可用空间…...
 
大数据与人工智能的未来已来
大数据与人工智能的定义 大数据: 大数据指的是规模庞大、复杂性高、多样性丰富的数据集合。这些数据通常无法通过传统的数据库管理工具来捕获、存储、管理和处理。大数据的特点包括"3V": 大量(Volume):大数…...
 
【AI视野·今日Robot 机器人论文速览 第四十一期】Tue, 26 Sep 2023
AI视野今日CS.Robotics 机器人学论文速览 Tue, 26 Sep 2023 Totally 73 papers 👉上期速览✈更多精彩请移步主页 Daily Robotics Papers Extreme Parkour with Legged Robots Authors Xuxin Cheng, Kexin Shi, Ananye Agarwal, Deepak Pathak人类可以通过以高度动态…...
 
[NOIP2012 提高组] 开车旅行
[NOIP2012 提高组] 开车旅行 题目描述 小 A \text{A} A 和小 B \text{B} B 决定利用假期外出旅行,他们将想去的城市从 $1 $ 到 n n n 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 …...
 
数据库设计流程---以案例熟悉
案例名字:宠物商店系统 课程来源:点击跳转 信息->概念模型->数据模型->数据库结构模型 将现实世界中的信息转换为信息世界的概念模型(E-R模型) 业务逻辑 构建 E-R 图 确定三个实体:用户、商品、订单...
Miniconda创建paddlepaddle环境
1、conda env list 2、conda create --name paddle_env python3.8 --channel https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ 3、activate paddle_env 4、python -m pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple 5、pip install "p…...
 
postgresql实现单主单从
实现步骤 1.主库创建一个有复制权限的用户 CREATE ROLE 用户名login # 有登录权限的角色即是用户replication #复制权限 encrypted password 密码;2.主库配置开放从库外部访问权限 修改 pg_hba.conf 文件 (相当于开放防火墙) # 类型 数据库 …...
 
提取PDF数据:Documents for PDF ( GcPdf )
在当今数据驱动的世界中,从 PDF 文档中无缝提取结构化表格数据已成为开发人员的一项关键任务。借助GrapeCity Documents for PDF ( GcPdf ),您可以使用 C# 以编程方式轻松解锁这些 PDF 中隐藏的信息宝藏。 考虑一下 PDF(最常用的文档格式之一…...
adb连接切换到模拟器端口
查看连接状态 adb devices出现以下情况 C:\Users\22560>adb devices List of devices attached 127.0.0.1:5555 offline emulator-5554 device可以发现我们想要连接的雷电模拟器的5555端口目前没有连接,只有emulator-5554被连接了,此时我们需要关…...
 
为何每个开发者都在谈论Go?
目录 一、引言Go的历史回顾关键时间节点 使用场景Go的语言地位技术社群与企业支持资源投入和生态系统 二、简洁的语法结构基本组成元素变量声明与初始化代码示例 类型推断函数与返回值代码示例输出 接口与结构体:组合而非继承错误处理:明确而不是异常小结…...
 
【Leetcode】 501. 二叉搜索树中的众数
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。 如果树中有不止一个众数,可以按 任意顺序 返回。 假定 BST 满足如下定义…...
怎样给Ubuntu系统安装vmware-tools
首先我要告诉你:Ubuntu无法安装vmware-tools,之所以这么些是因为我一开始也是这样认为的,vmware-tools是给Windows系统准备的我认为,毕竟Windows占有率远远高于Linux,这也可以理解。 那么怎么样实现Ubuntu虚拟机跟Wind…...
 
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
 
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
 
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
 
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
 
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
 
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
 
消息队列系统设计与实践全解析
文章目录 🚀 消息队列系统设计与实践全解析🔍 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡💡 权衡决策框架 1.3 运维复杂度评估🔧 运维成本降低策略 🏗️ 二、典型架构设计2.1 分布式事务最终一致…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...
 
GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...

