【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.xdnf.cn/news/10457.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Normal-GS: 3D Gaussian Splatting with Normal-Involved Rendering 论文解读

看这篇论文之前可以看一下Ref-NeRF&#xff1a;https://arxiv.org/pdf/2112.03907 目录 ​编辑 一、概述 二、相关工作 1、辐射场 2、3DGS在几何和外观上的应用 三、Normal-GS 1、3DGS 2、引入法线的策略 3、训练过程 4、损失函数 四、实验 1、渲染质量的量化对比实…

【Linux探索学习】第十弹——Linux工具篇(五):详解Linux 中 Git 工具的使用与相关知识点

Linux学习笔记&#xff1a;https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言&#xff1a; Git 是一个分布式版本控制系统&#xff0c;广泛应用于软件开发中。它能够有效地管理项目的源代码&#xff0c;支持多种工作流&#xff0c;帮…

【系统文档】系统安全保障措施,安全运营保障,系统应急预案,系统验收相关资料(word原件)

一、身份鉴别 二、访问控制 三、通信完整性、保密性 四、抗抵赖 五、数据完整性 六、数据保密性 七、应用安全支撑系统设计 软件资料获取及全资料学习获取&#xff1a;本文末个人名片或进主页。

Windows Server修改 SID 操作说明

操作场景 Windows操作系统对计算机和用户的识别是通过安全标识符&#xff08;SID&#xff09;进行区分。由于基于同一镜像生产的云服务器实例 SID 相同&#xff0c;会引起无法入域的问题。如果您需要搭建 Windows 域环境&#xff0c;则需要通过修改 SID 以达到入域的目的。 本…

Java项目实战II基于Spring Boot的疗养院管理系统设计与实现(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着人口老…

Canny边缘检测中Hysteresis Thresholding(滞后阈值)名字的由来

Hysteresis Thresholding直译是滞后阈值。注意区分hysteresis、heuristic、hypothesis。 Hysteresis&#xff1a;在物理学中指滞后现象。 Canny边缘检测中滞后阈值这个名字来源于物理学中的滞后现象。通过设置两个不同的阈值来决定哪些像素属于边缘。这两个阈值分别是高阈值&…

[ Linux 命令基础 6 ] Linux 命令详解-权限和用户管理命令

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

# ubuntu创建新用户和它的家目录

ubuntu创建新用户和它的家目录 一、使用 useradd命令创建新用户和它的家目录&#xff1a; 1、命令如下&#xff1a; #sudo useradd -r -m -s /bin/bash userName #如&#xff1a;sudo useradd -r -m -s /bin/bash zhangsan2、命令参数解释 -r : 建立系统账号。 -m : 自动建…

网线类别线芯含义和传输距离以及水晶头制作标准

网线八芯每根的含义&#xff1a; 网线的八根线芯&#xff0c;也被称为RJ45网线中的8芯&#xff0c;网线采用8根线芯&#xff0c;这八根线芯各自承担着特定的功能。这8根线芯被分为4对&#xff0c;每对以特定的方式绞合在一起&#xff0c;8芯网线主要是为了减少电磁信号的相互干…

HTB:Sightless[WriteUP]

目录 连接至HTB服务器并启动靶机 使用nmap对靶机TCP端口进行开放扫描 继续使用nmap对靶机开放的TCP端口进行脚本、服务扫描 首先尝试对靶机FTP服务进行匿名登录 使用curl访问靶机80端口 使用浏览器可以直接访问该域名 使用浏览器直接访问该子域 Getshell 横向移动 查…

Golang | Leetcode Golang题解之第554题砖墙

题目&#xff1a; 题解&#xff1a; func leastBricks(wall [][]int) int {cnt : map[int]int{}for _, widths : range wall {sum : 0for _, width : range widths[:len(widths)-1] {sum widthcnt[sum]}}maxCnt : 0for _, c : range cnt {if c > maxCnt {maxCnt c}}retur…

斯坦福泡茶机器人DexCap源码解析:涵盖收集数据、处理数据、模型训练三大阶段

前言 因为我司「七月在线」关于dexcap的复现/优化接近尾声了&#xff0c;故准备把dexcap的源码也分析下。​下周则分析下iDP3的源码——为队伍「iDP3人形的复现/优化」助力 最开始&#xff0c;dexcap的源码分析属于此文《DexCap——斯坦福李飞飞团队泡茶机器人&#xff1a;带…

高中数学:概率-随机实验、样本空间、随机事件

文章目录 一、随机实验二、样本空间三、随机事件例题 四、事件运算 一、随机实验 二、样本空间 三、随机事件 例如 样本空间 Ω { a , b , c , d , e , f } 则&#xff0c;事件 A { a , b , c } &#xff0c;是一个随机事件 事件 B { d } 是一个基本事件 样本空间Ω\{a,b,c…

w~大模型~合集21

我自己的原文哦~ https://blog.51cto.com/whaosoft/12459590 #大模型~微调~用带反馈的自训练 面对当前微调大模型主要依赖人类生成数据的普遍做法&#xff0c;谷歌 DeepMind 探索出了一种减少这种依赖的更高效方法。大模型微调非得依赖人类数据吗&#xff1f;用带反馈的自训…

利用 Vue.js 开发动态组件的实战指南

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; Vue.js 是现代 Web 开发中非常流行的框架&#xff0c;以其渐进式架构和组件化设计受到广泛欢迎。组件化开发是 Vue.js 的核心优势…

Jmeter中的配置原件(一)

配置原件 1--CSV Data Set Config 用途 参数化测试&#xff1a;从CSV文件中读取数据&#xff0c;为每个请求提供不同的参数值。数据驱动测试&#xff1a;使用外部数据文件来驱动测试&#xff0c;使测试更加灵活和可扩展。 配置步骤 准备CSV文件 创建一个CSV文件&#xff0c…

MCU的OTA升级(未完-持续更新)

1.术语 ISP : In-System Programming 在系统编程&#xff0c;是一种通过MCU&#xff08;微控制器单元&#xff09;上的内置引导程序&#xff08;BootLoader&#xff09;来实现对芯片内部存储器&#xff08;如Flash&#xff09;进行编程的技术。 华大目前对应的ISP IAP&…

让redis一直开启服务/自动启动

文章目录 你的redis是怎么打开的黑窗不能关?必须要自动启动吗?再说说mysql 本文的所有指令都建议在管理员权限下打开cmd控制台 推荐的以管理员身份打开控制台的方式 Win R 打开运行 输入cmdShift Ctrl Enter 你的redis是怎么打开的 安装过redis的朋友都知道, redis的安…

从认识 VNode VDOM 到实现 mini-vue

前言 现有框架几乎都引入了虚拟 DOM 来对真实 DOM 进行抽象&#xff0c;也就是现在大家所熟知的 VNode 和 VDOM&#xff0c;那么为什么需要引入虚拟 DOM 呢&#xff1f;下面就一起来了解下吧&#xff01;&#xff01;&#xff01; VNode & VDOM VNode 和 VDOM 是什么&am…

vue项目实战

1.项目文件夹添加&#xff08;结构如下&#xff09; 2.页面构建 安装路由 npm install react-router-dom 3.页面基本模板 router文件夹下index.js的模板 // 引入组件 import Login from "../views/login"; // 注册路由数组 const routes [{// 首页默认是/path: …