【Linux-进程间通信】了解信号量 + 共享内存 + 消息队列的应用

信号量(了解)

1.概念理论渗透

1.多个执行流(进程),能看到的同一份资源:共享资源

2.被保护起来的资源-----临界资源---同步和互斥----用互斥的方式保护共享资源----临界资源

3.互斥:任何时刻只能有一个进程在访问共享资源

4.资源要被程序员访问,资源被访问就是通过代码访问-----代码 = 访问共享资源的代码 (临界区)+ 不访问共享资源的代码(非临界区)

5.所谓的对共享资源进行保护--临界资源--本质是对访问共享资源的代码进行保护(通过加锁保护)

2.什么是信号量

信号量(Semaphore)是操作系统和并发编程中一种用于协调不同进程或线程对共享资源访问的同步机制。信号量的核心思想是通过计数的方式来控制资源的使用。它可以保证多个进程或线程不会在同一时间对同一共享资源进行访问,从而避免竞争条件(race condition)和资源冲突。

3.信号量的类型

1.计数信号量(Counting Semaphore)

计数信号量有一个整型计数器,表示可用资源的数量。当计数器值为正时,表示还有资源可以被分配;为0时,表示所有资源都已经被占用;为负时,表示有等待的线程或进程。

适用于限制多个进程/线程访问一组相同的资源,例如限制最多有N个线程可以访问某个数据库连接池。

2.二元信号量(Binary Semaphore)

又称为互斥信号量(Mutex),其值只有0和1,主要用于实现互斥锁(Mutex)。值为1时表示资源空闲,0表示资源已被占用。

适用于只有一个线程可以访问某一资源的场景(类似于锁机制)。

4.信号量的主要操作

P 操作(wait/Down/Proberen):将信号量的值减1。当信号量的值大于0时,线程可以继续执行;如果信号量值为0或负值,则线程进入等待队列,直到信号量的值大于0。

V 操作(signal/Up/Verhogen):将信号量的值加1,并唤醒一个正在等待的线程(如果有)。

申请信号量的本质:就是对公共资源的一种预定机制

System V 信号量的基本操作:

  • 创建/获取信号量:semget

int semget(key_t key, int nsems, int semflg);

 

功能

•semget 用于创建或获取一个信号量集。每个信号量集可以包含多个信号量。

•如果指定的信号量集不存在且 IPC_CREAT 标志被设置,则创建一个新的信号量集;否则,返回已存在的信号量集的标识符。

参数

•key: 信号量集的键值,标识符由此生成。如果为 IPC_PRIVATE,则创建一个新的信号量集,且只能被调用进程访问。

•nsems: 要创建的信号量的数量。如果获取一个已存在的信号量集,则此参数将被忽略。

•semflg: 权限标志和控制选项,通常是 0666 | IPC_CREAT,其中权限类似于文件权限。

返回值

•成功返回信号量集的标识符(正整数),失败返回 -1。

key_t key = ftok("somefile", 65); // 获取唯一key
int semid = semget(key, 1, 0666 | IPC_CREAT); // 创建一个信号量集,包含1个信号量
if (semid == -1) {perror("semget failed");
}
  • P/V 操作:semop

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

功能

•semop 用于执行对信号量的操作,包括 P 操作(等待/减1操作)和 V 操作(释放/加1操作)。

•semop 可以执行一个或多个信号量操作,通过一个 struct sembuf 数组指定每个信号量的操作。

参数

•semid: 信号量集的标识符,由 semget 返回。

•sops: 指向信号量操作数组的指针,每个元素是一个 struct sembuf 结构体,定义信号量的具体操作。

•nsops: 操作的数量,即 sops 数组的大小。

返回值:成功返回 0,失败返回 -1。

struct sembuf 结构体

【示例】

 

  • 设置信号量值:semctl

int semctl(int semid, int semnum, int cmd, ...);

功能

•semctl 用于控制信号量集或单个信号量。它可以获取、设置信号量的值,还可以删除信号量集。

•常用操作包括设置信号量的值、获取信号量的值和删除信号量集。

参数

•semid: 信号量集的标识符。

•semnum: 信号量集中的信号量编号。对整个信号量集操作时,此值可以被忽略。

•cmd: 指定要执行的控制命令。常见命令有:

•SETVAL: 设置信号量的值。

•GETVAL: 获取信号量的值。

•IPC_RMID: 删除信号量集。

返回值:根据不同的命令,返回值可能是信号量的值、执行操作的状态等。如果失败,返回 -1。

示例 - 设置信号量值

semctl(semid, 0, SETVAL, 1); // 将第0个信号量的值设置为1

示例 - 获取信号量值

int val = semctl(semid, 0, GETVAL); // 获取第0个信号量的值
printf("Semaphore value: %d\n", val);
  • 删除信号量:

semctl(semid, 0, IPC_RMID); // 删除信号量集

【代码示例】

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>int main() {// 1. 创建/获取信号量key_t key = ftok("somefile", 65); // 获取唯一keyint semid = semget(key, 1, 0666 | IPC_CREAT); // 创建一个信号量集,包含1个信号量// 2. 初始化信号量值semctl(semid, 0, SETVAL, 1); // 将信号量设为1// 3. P 操作(减1)struct sembuf sb = {0, -1, 0}; // 操作第0个信号量,执行减1操作semop(semid, &sb, 1); // 执行信号量操作printf("Entering critical section...\n");// 4. 执行临界区代码sleep(2);// 5. V 操作(加1)sb.sem_op = 1; // 将信号量加1semop(semid, &sb, 1); // 退出临界区printf("Leaving critical section...\n");// 6. 删除信号量semctl(semid, 0, IPC_RMID); // 删除信号量return 0;
}

System V 信号量的相关操作说明:

•semget: 创建或获取一个信号量集(可以包含多个信号量)。

•semop: 对信号量执行 P 或 V 操作。

•semctl: 控制信号量,可以用于设置信号量的值、获取信号量值,或者删除信号量。

共享内存应用--Sever&Client通信

client.cc

#include "Shm.hpp"
#include "namedPipe.hpp"int main()
{// 1. 创建共享内存Shm shm(gpathname, gproj_id, gUser);shm.Zero();char *shmaddr = (char *)shm.Addr();sleep(3);// 2. 打开管道NamePiped fifo(comm_path, User);fifo.OpenForWrite();// 当成stringchar ch = 'A';while (ch <= 'Z'){shmaddr[ch - 'A'] = ch;std::string temp = "wakeup";std::cout << "add " << ch << " into Shm, " << "wakeup reader" << std::endl;fifo.WriteNamedPipe(temp);sleep(2);ch++;}return 0;
}

namedPipe.hpp

#pragma once#include <iostream>
#include <cstdio>
#include <cerrno>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>const std::string comm_path = "./myfifo";
#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096class NamePiped
{
private:bool OpenNamedPipe(int mode){_fd = open(_fifo_path.c_str(), mode);if (_fd < 0)return false;return true;}public:NamePiped(const std::string &path, int who): _fifo_path(path), _id(who), _fd(DefaultFd){if (_id == Creater){int res = mkfifo(_fifo_path.c_str(), 0666);if (res != 0){perror("mkfifo");}std::cout << "creater create named pipe" << std::endl;}}bool OpenForRead(){return OpenNamedPipe(Read);}bool OpenForWrite(){return OpenNamedPipe(Write);}// const &: const std::string &XXX// *      : std::string *// &      : std::string & int ReadNamedPipe(std::string *out){char buffer[BaseSize];int n = read(_fd, buffer, sizeof(buffer));if(n > 0){buffer[n] = 0;*out = buffer;}return n;}int WriteNamedPipe(const std::string &in){return write(_fd, in.c_str(), in.size());}~NamePiped(){if (_id == Creater){int res = unlink(_fifo_path.c_str());if (res != 0){perror("unlink");}std::cout << "creater free named pipe" << std::endl;}if(_fd != DefaultFd) close(_fd);}private:const std::string _fifo_path;int _id;int _fd;
};

server.cc

#include "Shm.hpp"
#include "namedPipe.hpp"int main()
{// 1. 创建共享内存Shm shm(gpathname, gproj_id, gCreater);char *shmaddr = (char*)shm.Addr();shm.DebugShm();// // 2. 创建管道// NamePiped fifo(comm_path, Creater);// fifo.OpenForRead();// while(true)// {//     // std::string temp;//     // fifo.ReadNamedPipe(&temp);//     std::cout << "shm memory content: " << shmaddr << std::endl;// }sleep(5);return 0;
}

Shm.hpp

#ifndef __SHM_HPP__
#define __SHM_HPP__#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>const int gCreater = 1;
const int gUser = 2;
const std::string gpathname = "/home/wuxu/day26/CS-shm";
const int gproj_id = 0x66;
const int gShmSize = 4097; // 4096*nclass Shm
{
private:key_t _key;int _shmid;std::string _pathname;int _proj_id;int _who;void *_addrshm;private:key_t GetCommKey(){key_t k = ftok(_pathname.c_str(), _proj_id);if (k < 0){perror("ftok");}return k;}int GetShmHelper(key_t key, int size, int flag){int shmid = shmget(key, size, flag);if (shmid < 0){perror("shmget");}return shmid;}std::string RoleToString(int who){if (who == gCreater)return "Creater";else if (who == gUser)return "gUser";elsereturn "None";}void *AttachShm(){if (_addrshm != nullptr)DetachShm(_addrshm);void *shmaddr = shmat(_shmid, nullptr, 0);if (shmaddr == nullptr){perror("shmat");}std::cout << "who: " << RoleToString(_who) << " attach shm..." << std::endl;return shmaddr;}void DetachShm(void *shmaddr){if (shmaddr == nullptr)return;shmdt(shmaddr);std::cout << "who: " << RoleToString(_who) << " detach shm..." << std::endl;}public:Shm(const std::string &pathname, int proj_id, int who): _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr){_key = GetCommKey();if (_who == gCreater)GetShmUseCreate();else if (_who == gUser)GetShmForUse();_addrshm = AttachShm();std::cout << "shmid: " << _shmid << std::endl;std::cout << "_key: " << ToHex(_key) << std::endl;}~Shm(){if (_who == gCreater){int res = shmctl(_shmid, IPC_RMID, nullptr);}std::cout << "shm remove done..." << std::endl;}std::string ToHex(key_t key){char buffer[128];snprintf(buffer, sizeof(buffer), "0x%x", key);return buffer;}bool GetShmUseCreate(){if (_who == gCreater){_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);if (_shmid >= 0)return true;std::cout << "shm create done..." << std::endl;}return false;}bool GetShmForUse(){if (_who == gUser){_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);if (_shmid >= 0)return true;std::cout << "shm get done..." << std::endl;}return false;}void Zero(){if (_addrshm){memset(_addrshm, 0, gShmSize);}}void *Addr(){return _addrshm;}void DebugShm(){struct shmid_ds ds;int n = shmctl(_shmid, IPC_STAT, &ds);if (n < 0)return;std::cout << "ds.shm_perm.__key : " << ToHex(ds.shm_perm.__key) << std::endl;std::cout << "ds.shm_nattch: " << ds.shm_nattch << std::endl;}
};#endif

消息队列——实现Client&Server

由于struct msgbuf中的mtype可以标识向那个进程发送消息。假设Server接收mtype为1的数据↓↓↓

Com.hpp

#pragma once
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>const std::string PATH = "/home/xiaoming";
const int PROJ_ID = 999;
const int BUFF_SIZE = 1024;enum
{MSG_CREAT_ERR = 1,MSG_GET_ERR,MSG_DELETE_ERR
};int CreateMsg()
{key_t key = ftok(PATH.c_str(), PROJ_ID);int msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);if (msgid < 0){perror("msg create error");exit(MSG_CREAT_ERR);}return msgid;
}int GetMsg()
{key_t key = ftok(PATH.c_str(), PROJ_ID);int msgid = msgget(key, IPC_CREAT | 0666);if (msgid < 0){perror("msg get error");exit(MSG_GET_ERR);}return msgid;
}

Server.cc

#include "Com.hpp"int main()
{struct msgbuf buffer;int msgid = CreateMsg();while (true){msgrcv(msgid, &buffer, BUFF_SIZE, 1, 0);std::cout << "Client say@ " << buffer.mtext << std::endl;}return 0;
}

Client.cc

#include "Com.hpp"int main()
{int msgid = GetMsg();struct msgbuf buffer;buffer.mtype = 1;while (true){std::cout << "Says # ";std::string s;std::getline(std::cin, s);strcpy(buffer.mtext, s.c_str());msgsnd(msgid, &buffer, BUFF_SIZE, 0);}return 0;
}

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

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

相关文章

Cesium着色器的创意和方法(五——Polyline)

不接受反驳&#xff0c;线段在三维里面的渲染比多边形的渲染更复杂。因为线是没有宽度的&#xff0c;并且还需要时时刻刻向着你。 首先看下Cesium的线段的一些效果。这条线段非常宽&#xff08;20个像素&#xff09;&#xff0c;有两个需要留意观察的。一是线段并非实时面对我…

头歌——数据库系统原理(数据高级查询实验2)

文章目录 AVG() 函数的使用代码 COUNT() 函数的使用代码 MAX() 函数和 MIN() 函数的使用代码 SUM() 函数的使用代码 AVG() 函数的使用 返回某一字段的平均值 AVG() 函数通过计算某字段&#xff08;列&#xff09;内容&#xff08;行&#xff09;的个数和它们的数值之和来返回某…

【基于轻量型架构的WEB开发】课程 12.5 数据回写 Java EE企业级应用开发教程 Spring+SpringMVC+MyBatis

12.5 数据回写 12.5.1 普通字符串的回写 接下来通过HttpServletResponse输出数据的案例&#xff0c;演示普通字符串的回写&#xff0c;案例具体实现步骤如下。 1 创建一个数据回写类DataController&#xff0c;在DataController类中定义 showDataByResponse()方法&#xff…

软件测试 软考

在软件测试中&#xff0c;覆盖准则是衡量测试用例是否充分覆盖程序各个部分的标准。不同的覆盖准则有不同的强度。按覆盖强度从低到高排序&#xff0c;常见的覆盖准则如下&#xff1a; 语句覆盖&#xff08;Statement Coverage&#xff09;&#xff1a;要求测试用例至少执行一次…

DirectShow过滤器开发-写AVI视频文件过滤器

下载本过滤器DLL 本过滤器将视频流和音频流写入AVI视频文件。 过滤器信息 过滤器名称&#xff1a;写AVI 过滤器GUID&#xff1a;{2EF49957-37DF-4356-A2A0-ECBC52D1984B} DLL注册函数名&#xff1a;DllRegisterServer 删除注册函数名&#xff1a;DllUnregisterServer 过滤器有…

uniapp实现H5和微信小程序获取当前位置(腾讯地图)

之前的一个老项目&#xff0c;使用 uniapp 的 uni.getLocation 发现H5端定位不准确&#xff0c;比如余杭区会定位到临平区&#xff0c;根据官方文档初步判断是项目的uniapp的版本太低。 我选择的方式不是区更新uniapp的版本&#xff0c;是直接使用高德地图的api获取定位。 1.首…

【WRF理论第七期】WPS预处理

【WRF理论第七期】WPS预处理 运行WPS&#xff08;Running the WPS&#xff09;步骤1&#xff1a;Define model domains with geogrid步骤2&#xff1a;Extracting meteorological fields from GRIB files with ungrib步骤3&#xff1a;Horizontally interpolating meteorologic…

机器学习(一)——基本概念、模型的评估与选择

目录 1 关于2 概念2.1 基础概念2.2 学习过程2.3 预测与评估2.4 标记与分类2.4.1 标记2.4.2 分类 2.5 回归分析2.6 聚类分析2.7 学习类型2.8 泛化能力2.9 统计学概念 3 模型评估与选择3.1 经验误差与过拟合3.2 评估方法3.2.1 留出法3.2.2 交叉验证法3.2.3 自助法3.2.4 调参与最终…

小白docker入门简介

Dockerfile入门使用分享 一、docker是啥二、镜像仓库三、自定义镜像四、动手做机甲玩偶五、帮我做数学题六、计算功能的写法七、咒语翻译器八、放屁九、解决问题 一、docker是啥 最开始我和你一样&#xff0c;围着镜像、容器、docker的名词团团转&#xff0c;其实没那么复杂。…

gitlab无法创建合并请求是所有分支都不显示

点击Merge Requests ------> New merge request 创建新的合并请求时&#xff0c;在Source branch和Target branch中一个分支都不显示 排查思路&#xff1a; 1.怀疑是权限问题。 发现只有我的一个账号出现&#xff0c;检查了账号的权限&#xff0c;尝试了master、develop角色…

macOS15.1及以上系统bug:开发者证书无法打开,钥匙串访问无法打开一直出现图标后立马闪退

团队紧跟苹果最新系统发现bug:今日设备信息如下,希望能带给遇到这个问题的开发者一点帮助。 错误图如下: 点击证书文件后,先出现钥匙串访问图标,后立马闪退消失 中间试过很多方法,都是一样的表现,最后好在解决了,看网上也没有相关的帖子,这里直接写解决办法和导致原因…

python实战案例——爬取A站视频,m3u8格式视频抓取(内含完整代码!)

1、任务目标 目标网站&#xff1a;A站视频&#xff08;https://www.acfun.cn/v/ac40795151&#xff09; 要求&#xff1a;抓取该网址下的视频&#xff0c;将其存入本地&#xff0c;视频如下&#xff1a; 2、网页分析 进入目标网站&#xff0c;打开开发者模式&#xff0c;我们发…

【基于轻量型架构的WEB开发】课程 12.4 页面跳转 Java EE企业级应用开发教程 Spring+SpringMVC+MyBatis

12.4 页面跳转 12.4.1 返回值为void类型的页面跳转 返回值为void类型的页面跳转到默认页面 当Spring MVC方法的返回值为void类型&#xff0c;方法执行后会跳转到默认的页面。默认页面的路径由方法映射路径和视图解析器中的前缀、后缀拼接成&#xff0c;拼接格式为“前缀方法…

濮良贵《机械设计》第十版课后习题答案全解PDF电子版

《机械设计》(第十版)是“十二五”普通高等教育本科国家级规划教材&#xff0c; 是在《机械设计》(第九版)的基础上修订而成的。本次修订主要做了以下几项工作&#xff1a; 1. 内容的适当更新——自本书第九版出版以来&#xff0c; 机械工程及相关领域的新理论、新技术和新标准…

【Unity基础】Unity中如何导入字体?

在Unity中&#xff0c;不能像其他软件一样直接使用字体文件&#xff0c;需要通过FontAssetCreator将其转换成Texture的Asset文件&#xff0c;然后才能使用。 本文介绍了使用FontAssetCreator导入字体的过程&#xff0c;并对其参数设置进行了说明。 Font Asset Creator 是 Uni…

2024年11月8日上海帆软用户大会

2024年11月8日上海帆软用户大会 2024年11月8日&#xff0c;上海成功举办了帆软用户大会&#xff0c;主题为“数字聚力&#xff0c;绽放新机”。大会汇聚了众多行业专家和企业代表&#xff0c;共同探讨数字化转型和商业智能领域的最新趋势和实践。 大会亮点&#xff1a; 专家…

注意力机制的目的:理解语义;编码器嵌入高纬空间计算;注意力得分“得到S*V”;解码器掩码和交叉注意力层用于训练;最终的编码器和输出实现大模型

目录 注意力机制的目的:理解语义中的它是小白兔 词编码器嵌入高纬空间 计算注意力得分“得到S*V” 权重QKV:连接权重 训练阶段使用解码器:翻译后的语句 解码器掩码和交叉注意力层用于训练 最终的编码器和输出实现大模型 Transformer模型中,QKV QKV的作用 举例说明…

纯前端实现在线预览excel文件(插件: LuckyExcel、Luckysheet)

概述 在实际开发中&#xff0c;遇到需要在线预览各种文件的需求&#xff0c;最近遇到在线预览excel文件的需求&#xff0c;在此记录一下&#xff01;本文主要功能实现&#xff0c;用于插件 LuckyExcel &#xff0c;Luckysheet&#xff01;废话不多说&#xff0c;上代码&#xf…

WPF自定义翻页控件

XAML文件如下&#xff1a; <UserControlx:Class"CTMVVMDemo.View.UserControls.DataPager"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://s…

Linux中.NET读取excel组件,不会出现The type initializer for ‘Gdip‘ threw an exception异常

组件&#xff0c;可通过nuget安装&#xff0c;直接搜名字&#xff1a; ExcelDataReader using ConsoleAppReadFileData.Model; using ExcelDataReader; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Task…