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操作的场景,可以同时操作多个信号量,但是要实现传递数据,必须配合其他通信方式,如共享内存。可以结合案例来加深对信号量组的理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/92009.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode】滑动窗口妙解无重复字符的最长子串

Problem: 3. 无重复字符的最长子串 文章目录 思路算法原理分析暴力枚举 哈希表滑动窗口 复杂度Code 思路 首先我们来分析一下本题的思路 如果读者有看过 长度最小的子数组 的话就可以清楚这个子串其实和子数组是一个道理&#xff0c;都是 连续的一段区间但是呢它们本质上还是存…

应用架构的演进:亚马逊的微服务实践

当你在亚马逊上购物时,或许不会想到,你看到的这个购物网站,其背后技术架构经历了什么样的变迁与升级。 还记得上世纪 90 年代,那个只卖书的网上书店吗?那时的亚马逊,不过是一个架构简单的网站,所有的功能都堆积在一个庞大的软件堡垒里。随着更多业务的增加、更新和迭代,这个软…

【小程序 - 基础】页面导航、页面事件、生命周期、WXS脚本_04

目录 一、页面导航 1. 什么是页面导航 2. 小程序中实现页面导航的两种方式 2.1 声明式导航 2.1.1 导航到 tabBar 页面 2.1.2 导航到非 tabBar 页面 2.1.3 后退导航 2.2 编程式导航 2.2.1 导航到 tabBar 页面 2.2.2 导航到非 tabBar 页面 2.2.3 后退导航 2.3. 导航…

从1开始的Matlab(快速入门)

MATLAB软件版本&#xff1a;MATLAB R2016b 本文是博主从零开始学Matlab的记录&#xff0c;适合第一次接触Matlab的同学阅读。 一、基础介绍 1.1界面认识 1.2变量命名 注&#xff1a;Matlab中的注释 %% 独占一行的注释&#xff08;有上下横线分割&#xff09; % 普通注释 …

C语言:选择+编程(每日一练Day9)

目录 选择题&#xff1a; 题一&#xff1a; 题二&#xff1a; 题三&#xff1a; 题四&#xff1a; 题五&#xff1a; 编程题&#xff1a; 题一&#xff1a;自除数 思路一&#xff1a; 题二&#xff1a;除自身以外数组的乘积 思路二&#xff1a; 本人实力有限可能对…

深入理解 Swift 新并发模型中 Actor 的重入(Reentrancy)问题

问题现象 我们知道,Swift 5.5 引入的新并发模型极大简化了并行逻辑代码的开发,更重要的是:使用新并发模型中的 Actor 原语可以大大降低并发数据竞争的可能性。 不过,即便 Actor 有如此神奇之功效,它也不是“万能药”,仍不能防止误用带来的问题。比如:Actor 重入(Reen…

283. 多边形,《算法竞赛进阶指南》,

283. 多边形 - AcWing题库 “多边形游戏”是一款单人益智游戏。 游戏开始时&#xff0c;给定玩家一个具有 N 个顶点 N 条边&#xff08;编号 1∼N&#xff09;的多边形&#xff0c;如图 1 所示&#xff0c;其中 N4 每个顶点上写有一个整数&#xff0c;每个边上标有一个运算符…

数据分析方法:RFM模型

一、RFM基本原理 RFM是三个单词的缩写&#xff1a; 最近一次消费时间&#xff08;Recency&#xff09;&#xff0c;取数的时候一般取最近一次消费记录到当前时间的间隔&#xff0c;比如&#xff1a;7天、30天、90天未到店消费&#xff1b;直观上&#xff0c;一个用户太久不到…

MySql进阶篇---006:存储引擎,索引,SQL优化,视图、存储过程、变量、流程控制、游标、存储函数、触发器

1. 存储引擎 1.1 MySQL体系结构 1).连接层 最上层是一些客户端和链接服务&#xff0c;包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念&#xff0c;为通过认证…

Redis与分布式-主从复制

接上文 常用中间件-OAuth2 1.主从复制 启动两个redis服务器。 修改第一个服务器地址 修改第二个redis 然后分别启动 redis-server.exe redis.windows.conf) 查看当前服务器的主从状态&#xff0c;打开客户端&#xff1a;输入info replication命令来查看当前的主从状态&am…

2023/10/1 -- ARM

今日任务&#xff1a;select实现服务器并发 ser.c&#xff1a; #include <myhead.h>#define ERR_MSG(msg) do{\printf("%d\n",__LINE__);\perror(msg);\ }while(0)#define PORT 8888#define IP "192.168.1.5"int main(int argc, const char *argv[…

ElementUI动态树,数据表格以及分页的实现

目录 前言 一. ElementUI动态树 二. 数据表格和分页 三. 后端代码 service层 controller层 前言 在上一篇博客中实现了左侧菜单栏&#xff0c;在此基础上将它变为动态的&#xff0c;即动态的展示数据库的数据。还有数据表格的实现以及分页。&#xff08;纯代码分享&#…

新闻报道的未来:自动化新闻生成与爬虫技术

概述 自动化新闻生成是一种利用自然语言处理和机器学习技术&#xff0c;从结构化数据中提取信息并生成新闻文章的方法。它可以实现大规模、高效、多样的新闻内容生产。然而&#xff0c;要实现自动化新闻生成&#xff0c;首先需要获取可靠的数据源。这就需要使用爬虫技术&#…

步进电机只响不转

我出现问题的原因是相位线接错。 我使用的滑台上示17H的步进电机&#xff0c;之前用的是57的步进电机。 57步进电机的相位线是A黑、A-绿、B红、B-蓝。 17步进电机的相位线是A红、A-绿、B黑、B-蓝。 这两天被一个问题困扰了好久&#xff0c;在调试步进电机开发板的时候电机发生…

分类预测 | Matlab实现SSA-CNN-SVM麻雀算法优化卷积支持向量机分类预测

分类预测 | Matlab实现SSA-CNN-SVM麻雀算法优化卷积支持向量机分类预测 目录 分类预测 | Matlab实现SSA-CNN-SVM麻雀算法优化卷积支持向量机分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现SSA-CNN-SVM麻雀算法优化卷积支持向量机分类预测&#xff0…

Sentinel学习(1)——CAP理论,微服务中的雪崩问题,和Hystix的解决方案 Sentinel的相关概念 + 下载运行

前言 Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件&#xff0c;主要以流量为切入点&#xff0c;从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。 本篇博客介绍CAP理论&#xff0c;微…

Plant Simulation双深位库堆垛库

Plant Simulation双深位库堆垛库视频演示 自定义多入口&#xff0c;多出口 Plant Simulation双深位库堆垛库

【面试经典150 | 矩阵】螺旋矩阵

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;模拟方法二&#xff1a;按层模拟 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于…

【数据结构】选择排序 堆排序(二)

目录 一&#xff0c;选择排序 1&#xff0c;基本思想 2&#xff0c; 基本思路 3&#xff0c;思路实现 二&#xff0c;堆排序 1&#xff0c;直接选择排序的特性总结&#xff1a; 2&#xff0c;思路实现 3&#xff0c;源代码 最后祝大家国庆快乐&#xff01; 一&#xf…

测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)

文章目录 一、任务描述二、指标分析2.1 TP/FP/FN/TN2.2 精准率2.3 召回率 三、接口处理四、数据集处理五、开始计算指标五、实用工具5.1 移动文件5.2 可视化JSON标签5.3 可视化TXT标签 一、任务描述 通过给定的算法接口&#xff0c;对算法的输出&#xff08;置信度、检测框、告…