进程间通信(下)

system V共享内存

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据

共享内存示意图

image-20221103164840280

通过上面的图,我们不难想到,共享内存肯定有下面的操作:

1、创建共享内存 -- 删除共享内存(操作系统)

2、关联共享内存 -- 去关联共享内存(进程,当然,这个本质上还是操作系统做的)

注意:共享内存,因为它自身的特性,没有任何的访问控制,共享内存可以被通信的进程双方看到,属于双方的用户空间,可以直接通信,但是不安全。共享内存是所有进程间通信,速度最快的。

共享内存数据结构

struct shmid_ds {struct ipc_perm shm_perm; /* operation perms */int shm_segsz; /* size of segment (bytes) */__kernel_time_t shm_atime; /* last attach time */__kernel_time_t shm_dtime; /* last detach time */__kernel_time_t shm_ctime; /* last change time */__kernel_ipc_pid_t shm_cpid; /* pid of creator */__kernel_ipc_pid_t shm_lpid; /* pid of last operator */unsigned short shm_nattch; /* no. of current attaches */unsigned short shm_unused; /* compatibility */void *shm_unused2; /* ditto - used by DIPC */void *shm_unused3; /* unused */
};

共享内存生命周期

system V下的共享内存,生命周期是随内核的,如果不显式删除,只能通过kernel(OS)重启来解决。

共享内存函数

shmget函数

image-20221103171444745

功能:用来创建共享内存
原型int shmget(key_t key, size_t size, int shmflg);
参数key:这个共享内存段名字size:共享内存大小,最好是设置成页(4KB)的整数倍,如果我们申请的不是页的整数倍该怎么办呢(以4097为例)?实际操作系统会为我们申请两个page(8194字节),但是我们可以实际使用的共享内存大小只有4097个空间shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
shmflg:IPC_CREAT:创建共享内存,如果已经存在了,就获取,如果不存在就创建(设置为0默认行为就是IPC_CREAT)IPC_EXCL:不单独使用,必须和IPC_CREAT配合(按位或),如果不存在指定的共享内存,就创建,如果存在,就出错返回,作用:如果shmget函数调用成功,一定是一个全新的共享内存
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

注意:

当只有IPC_CREAT选项打开时,不管是否已经存在该块共享内存,则都返回该共享内存的ID,若不存在则创建共享内存

当只有IPC_EXCL选项打开时,不管有没有该共享内存,shmget()都返回-1

所以当IPC_CREAT | IPC_EXCL时, 如果没有该块共享内存,则创建,并返回共享内存ID。若已有该块共享内存,则返回-1;

问:标识共享内存是否存在的标识符存在哪?是怎么进行管理起来的?我们如何知道共享内存是否存在?

答:标识共享内存是否存在的标识符存在于内核中。内核会帮我们维护共享内存的结构,通过结构体来存储共享内存的各种属性(具体结构如上面的共享内存的数据结构所示)。下面是用户层面的数据结构(通过查找比对下面的key就可以判断共享内存是否存在):

注意:这个标识共享内存的唯一值是由用户提供的。为什么一定要由用户提供?进程间通信的前提:不同的进程能够看到同一份资源。共享内存在内核中,让不同的进程看到同一份共享内存,做法是:让他们能够看到同一份key。

补充知识:操作系统把内存分为很多4KB的页,比如4GB的内存,操作系统会将其划分为220个页,然后通过类似数组的方式对整个内存空间进行管理:

struct page//每个页
{//page的属性
}
struct page mem[2^20];

ftok函数

image-20221104111430591

参数说明:

id:由自己来进行设置,一般是0~255中的一个数。

path:一个文件路径,一般是当前文件的路径。

作用:转换一个文件路径和一个id标识符形成一个唯一的数字,我们用其来形成key。

shmctl函数

image-20221104150829441

功能:用于控制共享内存
原型int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数shmid:由shmget返回的共享内存标识码cmd:将要采取的动作(有三个可取值)buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1
命令说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID删除共享内存段

shmat/shmdt函数

shmat函数

image-20221104154935187

功能:将共享内存段连接到进程地址空间
原型void *shmat(int shmid, const void *shmaddr, int shmflg);
参数shmid: 共享内存标识shmaddr:指定我们想要挂接的地址(我们一般将其设置为nullptr)shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY(一般默认设置为0,以读写方式)
返回值:成功返回一个指针,指向共享内存第一个字节;失败返回-1

说明:

shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - 
(shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
shmdt函数
功能:将共享内存段与当前进程脱离
原型int shmdt(const void *shmaddr);
参数shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

共享内存相关的命令行操作

查看共享内存

ipcs -m:查看当前的共享内存

使用举例:

image-20221104150019461

perms:该共享内存的权限(类似文件一样,有权限的,如果该共享内存的权限是0,那么就没有人或者进程能够去读取他)
nattch:挂接该共享内存的进程数

删除共享内存

ipcrm -m [shmid]:删除共享内存

使用举例:

image-20221104150504985

代码练习

Comm.hpp文件

#pragma once
#include<iostream>
#include<sys/types.h>
#include<sys/ipc.h>
#include<cstdlib>
#include<cstring>
#include<cerrno>
#include<unistd.h>
#include<sys/shm.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<cassert>
#include"Log.hpp"
using namespace std;
​
#define PATH_NAME "/home/ljg/linux_for_practice/2022_11_4/"
#define PROJ_ID 0x666
#define SHM_SIZE 4096
#define FIFO_FILE ".fifo"
#define READER O_RDONLY
#define WRITER O_WRONLY
key_t CreatKey()
{key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){std::cerr << "fork:" << strerror(errno) << std::endl;exit(1);}return key;
}
​
void CreatFifo()
{umask(0);if(mkfifo(FIFO_FILE, 0666) < 0){Log() << strerror(errno) << endl;exit(2);}
}
​
int Open(const std::string &filename, int flags)
{return open(filename.c_str(), flags);
}
​
​
int Wait(int fd)
{uint32_t values = 0;ssize_t s = read(fd, &values, sizeof(uint32_t));return s;
}
int Signal(int fd)
{uint32_t cmd = 1;write(fd, &cmd, sizeof(cmd));
}
​
int Close(int fd, const std::string& filename)
{close(fd);unlink(filename.c_str());
}

IpcShmSer.cpp

#include"Comm.hpp"
#include"Log.hpp"
using namespace std;
const int flags = IPC_CREAT | IPC_EXCL;//创建全新的共享内存
int main()
{CreatFifo();cout << "open begin" << endl;int fd = Open(FIFO_FILE, READER);cout << "open end" << endl;assert(fd >= 0);key_t key = CreatKey();Log() << "key:" << key << endl;int shmid = shmget(key, SHM_SIZE, flags | 0666);Log() << "create shm begin, shmid:" << shmid << endl;if(shmid < 0){Log() << "shmget:" << strerror(errno) << endl;return 2;}Log() << "create shm success, shmid:" << shmid << endl;//用它
​//1.将共享内存和自己的进程产生关联char* str = (char*)shmat(shmid, nullptr, 0);Log() << "attach shm success" << endl;sleep(5);while(true){//让读端进行等待if(Wait(fd) <= 0){break;}printf("%s\n", str);sleep(1);}Log() << "deattach shm sucess" << endl;shmdt(str);//删它shmctl(shmid, IPC_RMID, nullptr);Log() << "delete shmid success" << endl;Close(fd, FIFO_FILE);return 0;
}

IpcShmCli.cpp文件

#include "Comm.hpp"
#include "Log.hpp"
using namespace std;
int main()
{int fd = Open(FIFO_FILE, WRITER);assert(fd >= 0);//创建相同的key值key_t key = CreatKey();Log() << "key:" << key << endl;
​int shmid = shmget(key, SHM_SIZE, IPC_CREAT);if (shmid < 0){Log() << "shmget:" << strerror(errno) << endl;return 2;}//挂接char* str = (char*)shmat(shmid, nullptr, 0);sleep(5);//使用共享内存while(true){printf("Please Enter#");fflush(stdout);ssize_t s = read(0, str, SHM_SIZE);//将输入的数据写到共享内存中if(s > 0){str[s] = '\0';}Signal(fd);}
​shmdt(str);return 0;
}

Log.hpp文件

#pragma once
#include<iostream>
#include<ctime>
​
std::ostream& Log()
{std::cout << "For Debug | " << "timestamp:" << (uint64_t)time(nullptr) << " ";return std::cout;
}

Makefile文件

.PHONY:all
all:IpcShmCli IpcShmSer
IpcShmCli:IpcShmCli.cppg++ -o $@ $^ -std=c++11
IpcShmSer:IpcShmSer.cppg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm  IpcShmCli IpcShmSer

扩展

mmap

image-20221105141932675

将进程中的个区域和特定的文件建立关联和映射。

1、mmap将一个文件或者其它对象映射进内存。mmap操作提供了一种机制,让用户程序直接访问设备内存,这种机制,相比较在用户空间和内核空间互相拷贝数据,效率更高。在要求高性能的应用中比较常用。mmap映射内存必须是页面大小的整数倍,面向流的设备不能进行mmap,mmap的实现和硬件有关。

2、两个进程可以通过映射普通文件实现共享内存通信。

3、在网络下载中,可以通过映射整个文件,使文件减少一次从内核态到用户态的拷贝,从而加快传输的效率。

一些概念了解

临界资源:能够被多个进程看到的公共资源就是临界资源

如果没有对临界资源进行任何保护,对于临界资源的访问,双方进程在进行访问的时候,就都是乱序的,可能会因为读写交叉而导致的各种乱码和访问控制方面的问题。(比如两个进程通过printf向显示器上打印资源)


临界区:对多个进程而言,访问临界资源的代码,就是临界区。(比如printf代码,read代码,write代码等等)

对于共享内存来说,共享内存就是临界资源,两个进程中对共享内存进行读写的代码部分就是临界区。


原子性:一件事情,要么没做,要么做完了,没有中间状态,我们称其为原子性

从代码层面上来说:cnt++、++cnt、x = y(读取y至寄存器,再把该值写入x)都不是原子操作。

image-20221105115518791

只有x=1是原子操作(直接将一个常量值加载到内存中)。


互斥:任何时刻,只允许一个进程,访问临界资源

system V消息队列

  • 消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法

  • 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值

  • 特性方面

    • IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核

命令行操作:

ipcs -q:查看消息队列
ipcrm -q [msid]:删除消息队列

system V信号量

本质

信号量的本质就是一个计数器,我们想要访问临界资源,就要先访问信号量,当然,信号量本身也是一种临界资源。

注意:信号量所对应的操作是原子的,这是由系统所封装好的

函数操作

信号量的获取:

image-20221105110549812

信号量的控制:

image-20221105110629275

信号量的加减操作:

image-20221105110723836

命令行操作

ipcs -s
ipcrm -s [semid]

共享内存/消息队列/信号量函数和命令行对比

函数对比

共享内存消息队列信号量
获取shmget()msgget()semget()
控制shmctl()msgctl()semctl
操作shmat/shmdtmsgsnd/msgrcvsemop

命令行操作对比

ipcs -m/-q/-s
ipcrm -m/-q/-s

内核中IPC资源的管理

image-20221105144822738

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

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

相关文章

linux的学习(三):用户权限,查找,压缩命令

简介 关于用户权限&#xff0c;查找和压缩解压缩命令的简单使用 用户管理命令 useradd useradd&#xff1a;添加新用户&#xff0c;要root权限才能使用 useradd -g 组名 用户名&#xff1a;可以添加到组 创建成功会在 /home下有用户的主目录 passwd passwd 用户名&#x…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第60集-agent训练资讯APP重点推荐AI资讯内容(含视频)

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第60集-agent训练资讯APP重点推荐AI资讯内容&#xff08;含视频&#xff09; 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。d…

php反序列化--2--PHP反序列化漏洞基础知识

一、什么是反序列化&#xff1f; 反序列化是将序列化的字符串还原为PHP的值的过程。 二、如何反序列化 使用unserialize()函数来执行反序列化操作 代码1&#xff1a; $serializedStr O:8:"stdClass":1:{s:4:"data";s:6:"sample";}; $origina…

Android Service的解析

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 Android服务&#xff0c;即Service&#xff0c;是Android四大组件之一&#xff0c;是一种程序后台运行的方案&am…

新增支持GIS地图、数据模型引擎升级、增强数据分析处理能力

为了帮助企业提升数据分析处理能力&#xff0c;Smartbi重点围绕产品易用性、用户体验、操作便捷性进行了更新迭代&#xff0c;同时重磅更新了体验中心。用更加匹配项目及业务需求的Smartbi&#xff0c;帮助企业真正发挥数据的价值&#xff0c;赋能决策经营与管理。 Smartbi用户…

js中使用原型链增加方法后,遍历对象的key-value时会遍历出方法

原因&#xff1a;js使用原型链实现方法时&#xff0c;这个方法默认是可迭代的&#xff0c;所以在遍历时就会被遍历出来&#xff0c; 例&#xff1a; Array.prototype.remove function(n){return this.slice(0,n).concat(this.slice(n1,this.length));}var cc ["cccaaaa…

wifi信号处理的CRC8、CRC32

&#x1f9d1;&#x1f3fb;个人简介&#xff1a;具有3年工作经验&#xff0c;擅长通信算法的MATLAB仿真和FPGA实现。代码事宜&#xff0c;私信博主&#xff0c;程序定制、设计指导。 &#x1f680;wifi信号处理的CRC8、CRC32 目录 &#x1f680;1.CRC概述 &#x1f680;1.C…

定时器的计数模式 定时器中断时钟配置

目录 一&#xff0c;定时器的计数模式 二&#xff0c;定时器中断时钟的配置 三&#xff0c;输入和输出原理 四&#xff0c;PWM波的小简介 一&#xff0c;定时器的计数模式 1.1 定时器的计数模式分别有三种 1.2 定时器溢出的时间&#xff08;中断&#xff0c;事件产生的时间…

QT多线程下,信号槽分别在什么线程中执行,如何控制?

可以通过connect的第五个参数进行控制信号槽执行时所在的线程 connect有几种连接方式&#xff0c;直接连接、队列连接和 自动连接 直接连接&#xff08;Qt::DirectConnection&#xff09;&#xff1a;信号槽在信号发出者所在的线程中执行 队列连接&#xff08;Qt::QueuedConn…

python初学者知识点笔记更新

文章目录 1.main函数入口2.__init__.py 文件作用3.from .applications import server解释4.变量没有修饰&#xff0c;直接创建使用1. 内置数据类型和函数2. 类和对象3.总结 5.mod app.__module__6.集合对比区分集合类型&#xff1a;混合集合类型 7.安装包失败 1.main函数入口 …

vitest 单元测试应用与配置

vitest 应用与配置 一、简介 Vitest 旨在将自己定位为 Vite 项目的首选测试框架&#xff0c;即使对于不使用 Vite 的项目也是一个可靠的替代方案。它本身也兼容一些Jest的API用法。 二、安装vitest // npm npm install -D vitest // yarn yarn add -D vitest // pnpm pnpm …

Linux 06-01:简易shell编写

考虑一下这个与shell典型的互动&#xff1a;ls、ps 用下图的时间轴来表示事件的发生次序。其中时间从左向右。shell由标识为sh的方块代表&#xff0c;它随着时间的流逝从左向右移动。shell从用户读入字符串"ls"。shell建立一个新的进程&#xff0c;然后在那个进程中运…

vs code 启动react项目,执行npm start报错原因分析

1.执行 npm start错误信息&#xff1a;npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写&#xff0c;如果包括路径&#xff0c;请确保路径正确&#xff0c;然后再试一次。 所在位置 行:1 字符: 1 npm start ~~~ CategoryInfo …

2024年5000元投影仪推荐:五千元最值得买的三款家用激光投影推荐

五千元是很多家庭购买投影仪会选择的价位&#xff0c;这个价位的投影一般属于中高端产品&#xff0c;如果懂配置&#xff0c;知道怎么选的朋友可以选到一款性价比颇高的投影&#xff0c;但是如果不会选不懂配置可能会花冤枉钱。所以五千元价位的投影该如何选择&#xff1f;市面…

企业知识库用不起来?试一下用HelpLook同步钉钉组织架构

提升企业管理和协同效率已成为增强竞争力的关键。企业通过知识管理&#xff0c;搭建内部知识库&#xff0c;将分散的经验和知识转化为系统化流程&#xff0c;减少重复解释&#xff0c;促进业务高效运作。这为企业提供了坚实的基础。 企业知识库面临的挑战 尽管传统知识库内容丰…

Jeecgboot vue3的选择部门组件JSelectDept如何实现只查询本级以及子级的部门

jeecgboot vue3的文档&#xff1a;地址 JSelectDept组件实现了弹窗然后选择部门返回的功能&#xff0c;但部门是所有数据&#xff0c;不符合需求&#xff0c;所以在原有代码上稍微改动了一下 组件属性值如下&#xff1a; 当serverTreeDatafalse的时候&#xff0c;从后端查询…

2024年7月9日~2024年7月15日周报

目录 一、前言 二、完成情况 2.1 特征图保存方法 2.1.1 定义网络模型 2.1.2 定义保存特征图的钩子函数 2.1.3 为模型层注册钩子 2.1.4 运行模型并检查特征图 2.2 实验情况 三、下周计划 一、前言 本周的7月11日~7月14日参加了机器培训的学习讨论会&#xff0c;对很多概…

通过MATLAB控制TI毫米波雷达的工作状态之TLV数据解析及绘制

前言 前一章博主介绍了如何基于设计视图中的这些组件结合MATLAB代码来实现TI毫米波雷达数据的实时采集。这一章将在此基础上实现TI毫米波雷达的TLV数据解析。过程中部分算法会涉及到一些简单的毫米波雷达相关算法,需要各位有一定的毫米波雷达基础。 TLV数据之协议解析 紧着…

数字孪生技术如何助力低空经济飞跃式发展?

一、什么是低空经济&#xff1f; 低空经济&#xff0c;是一个以通用航空产业为主导的经济形态&#xff0c;它涵盖了低空飞行、航空旅游、航空物流、应急救援等多个领域。它以垂直起降型飞机和无人驾驶航空器为载体&#xff0c;通过载人、载货及其他作业等多场景低空飞行活动&a…

React 实现五子棋

简介 本文将会基于React 实现五子棋小游戏&#xff0c;游戏规则为先让5颗棋子连成1线的一方获胜。 实现效果 技术实现 页面布局 <div><table style{{border: 1px solid #000, borderCollapse: collapse, backgroundColor: lightgray}}><tbody>{squares.ma…