29 共享内存

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

原理

在这里插入图片描述

系统在内存中申请一段空间,通过页表映射挂接到进程的共享区,共享区返回首地址。两个进程通信时直接访问这个首地址,实际上读取的是同一段空间。申请的这段空间操作系统会管理起来。这个过程须由操作系统来做,这样两个进程才能保持独立。如果要释放共享内存,先去关联,再释放共享内存

共享内存数据结构

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 */
};

函数

shmget

功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

选项:
在这里插入图片描述

IPC_CREAT:不存在就创建,存在获取并返回
IPC_EXCL:存在就出错返回,不单独使用
共享内存也需要权限,在flag参数加入
大小问题
如果内存4k一个基本单位访问,申请4097,虽然查询显示是4097,但实际上申请了4096*2的大小,只给4907空间使用

生成key的函数
在这里插入图片描述
这是一套算法,数值计算返回key值

挂接函数 shmat

功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

shmaddr为NULL,自动选择一个地址
shmaddr不为NULL,且shmflg无SHM_RND标记,则以shmaddr为连接地址
shmaddr不为NULL,设置了SHM_RND标记,连接的地址自动向下调整为SHMLBA的整数倍。公式:shmaddr(shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

需要传的只有第一个参数,剩下两个系统可以自动生成。权限虽然申请时设置了,但挂接的过程也可以再一次设置

去关联 shmdt

功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

控制共享内存 shmctl

功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

共享内存的管理属性
在这里插入图片描述

key

1.key是一个数字,在内核中具有唯一性,能让不同的进程进行唯一性标识
2.第一个进程可以通过key创建共享,第二个之后只要拿着同一个key就可以看到同一个共享内存
3.可以在共享内存的描述对象中
4.第一次创建的key,通过唯一的路径等参数计算获取,通信双方提前约定好参数

key和shmid

key是操作系统内标定唯一性,shmid只在你的进程内,表示资源的唯一性,shmid和文件标识符不一样,没有用一套标准,有自己的算法。不符合一切皆文件,所以用的不多。共享内存的生命周期随内核,用户不主动关闭会一直存在,除非内核重启(用户释放)

共享内存命令

查询

ipcs -m

在这里插入图片描述
删除

ipcrm -m [shmid]

用户层的操作都是用shmid,key是内核标识的。命令行也是用户层

测试

创建一个共享内存,一个进程创建,一个获取
common.hpp

#ifndef __COMMON__
#define __COMMON__#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "log.hpp"//using namespace std;
const char *pathname = "./dir";
const int proj = 123;Log log;
key_t getkey()
{key_t key = ftok(pathname, proj);if (key < 0){log.logmessage(fatal, "ftok error: %s", strerror(errno));}printf("key:%x\n", key);return key;
}int shm(int flag)
{key_t key = getkey();int shmid = shmget(key, 4096, flag);if (shmid < 0){log.logmessage(fatal, "shmget error: %s", strerror(errno));}printf("shmid:%x\n", shmid);return shmid;
}
//创建
int creatshm()
{return shm(IPC_CREAT | IPC_EXCL | 0666);
}//获取
int getshm()
{return shm(IPC_CREAT);
}
#endif

processA.cc

#include "common.hpp"int main()
{creatshm();return 0;
}

processB.cc

#include "common.hpp"int main()
{getshm();return 0;
}

查询共享内存
在这里插入图片描述

挂接之后查看nattch
在这里插入图片描述

解除挂接然后释放,attch会又变为0,然后消失
在这里插入图片描述
在这里插入图片描述
processA.cc

#include "common.hpp"int main()
{Log log;int shmid = creatshm();char* addr = (char*)shmat(shmid, 0, 0);if (addr < 0){log.logmessage(fatal, "addr error: %s", strerror(errno));}sleep(5);int ret = shmdt(addr);if (addr < 0){log.logmessage(fatal, "unattach error: %s", strerror(errno));}shmctl(shmid, IPC_RMID, 0);log.logmessage(debug, "free result: %s", strerror(errno));sleep(20);return 0;
}

让两个进程往共享内存写入数据通信,a读,b写
在这里插入图片描述
processB.cc

#include "common.hpp"int main()
{int shmid = getshm();char* addr = (char*)shmat(shmid, 0, 0);while (true){cout << "please enter:";fgets(addr, 1024, stdin);}int ret = shmdt(addr);return 0;
}

共享内存的特性

1.共享内存没有同步互斥之类的保护机制
2.共享内存是所有进程通信中速度最快的,因为是直接往内存里写入内容
3.共享内存内部的数据,由用户自己维护

共享内存的结构
在这里插入图片描述
第一个属性是共享内存的许可结构,第二个是大小
共享内存perm结构
在这里插入图片描述
获取属性,打印,STAT可以从内核中拷贝数据结构

shmid_ds ds;shmctl(shmid, IPC_STAT, &ds);cout << "size: " << ds.shm_segsz << endl;cout << "attach: " << ds.shm_nattch << endl;cout << "key: 0x" << hex << ds.shm_perm.__key << endl;

在这里插入图片描述

共享内存加管道

先创建管道,两个进程打开管道,通过向管道输入内容来判断写入方是否写入。当写入了内容,就通过管道发送信号,读取方接收到文件尾就会退出,不然就会阻塞

common.hpp

#ifndef __COMMON__
#define __COMMON__#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <cstring>
#include <unistd.h>
#include <cstdlib>
#include <fcntl.h>
#include "log.hpp"#define FIFO_FILE "./fifo"
#define MODE 0664enum {FIFO_CREAT_ERR = 1,FIFO_DELET_ERR,FIFO_OPEN_ERR
};//using namespace std;
const char *pathname = "./dir";
const int proj = 123;Log log;
key_t getkey()
{key_t key = ftok(pathname, proj);if (key < 0){log.logmessage(fatal, "ftok error: %s", strerror(errno));}printf("key:%x\n", key);return key;
}int shm(int flag)
{key_t key = getkey();int shmid = shmget(key, 4096, flag);if (shmid < 0){log.logmessage(fatal, "shmget error: %s", strerror(errno));}printf("shmid:%x\n", shmid);return shmid;
}
//创建
int creatshm()
{return shm(IPC_CREAT | IPC_EXCL | 0666);
}//获取
int getshm()
{return shm(IPC_CREAT);
}class Init
{
public:Init(){// 创建管道int n = mkfifo(FIFO_FILE, MODE);if (n == -1){perror("server creat");exit(FIFO_CREAT_ERR);}}~Init(){// 删除管道int n = unlink(FIFO_FILE);if (n == -1){perror("server unlink");exit(FIFO_DELET_ERR);}}
};#endif

processA.cc

#include "common.hpp"int main()
{Log log;Init fifo;int shmid = creatshm();char *addr = (char *)shmat(shmid, 0, 0);//打开管道int fd = open(FIFO_FILE, O_RDONLY);shmid_ds ds;while (true){// cout << "processB: " << addr;// shmctl(shmid, IPC_STAT, &ds);// cout << "size: " << ds.shm_segsz << endl;// cout << "attach: " << ds.shm_nattch << endl;// cout << "key: 0x" << hex << ds.shm_perm.__key << endl;char c = 0;ssize_t x = read(fd, &c, 1);if (x == 0){break;}cout << "processB:" << addr;sleep(1);}int ret = shmdt(addr);shmctl(shmid, IPC_RMID, 0);  return 0;
}

processB.cc

#include "common.hpp"int main()
{int shmid = getshm();char* addr = (char*)shmat(shmid, 0, 0);int fd = open(FIFO_FILE, O_WRONLY);while (true){cout << "please enter:";fgets(addr, 1024, stdin);//通知对方write(fd, "c", 1);}int ret = shmdt(addr);return 0;
}

在这里插入图片描述

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

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

相关文章

Linux--链表 第二十五天

1. 链表 t1.next -> data t1.next->next->data .(点号)的优先级比->的大 所以 t1.next->data 就可以了 不用(t1.next)->data 2. 链表的静态增加和动态遍历 打印链表算法&#xff0c; void printLink(struct Test *head) { struct Te…

剑指Offer题目笔记32(拓扑排序)

面试题113&#xff1a; 解决方案&#xff1a; 将课程看成图中的节点&#xff0c;如果两门课程存在先修顺序那么它们在图中对应的节点之间存在一条从先修课程到后修课程的边&#xff0c;因此这是一个有向图。可行的修课序列实际上是图的拓扑排序序列。图中的每条边都是从先修课…

Web前端框架/库/工具

前言 前端从步枪&#xff08;原生js&#xff09;到了半自动武器&#xff08;jQuery&#xff09;并进化为全自动武器&#xff08;三大框架&#xff08;angular&#xff0c;react&#xff0c;vue及其生态链&#xff09;&#xff09;。 常说工欲善其事必先利其器。对于那些想要提…

【c++11】看完立马就懂--右值引用!!!

右值引用 一、什么是右值&#xff1f;什么是左值&#xff1f;二、右值引用三、右值引用的好处四、万能引用五、完美转发 一、什么是右值&#xff1f;什么是左值&#xff1f; 首先&#xff0c;当我们看到右值的时候&#xff0c;我们很自然的就会产生疑问&#xff1f; 什么的右边…

黑马鸿蒙学习5:LIST容器

LIST容器&#xff0c;其实就是如果FOREACH容器展示不全的话&#xff0c;会自动有滚动条了。要注意的是&#xff0c;LIST中必须有固定的listitem这个项&#xff0c;而且列表里面只能包含一个根组件。 必须把ROW容器放到listitem中&#xff0c;如下&#xff1a;

51、图论-岛屿数量

思路&#xff1a; 该问题要求在一个由 1&#xff08;表示陆地&#xff09;和 0&#xff08;表示水&#xff09;组成的二维网格中&#xff0c;计算岛屿的数量。岛屿被水包围&#xff0c;并且通过水平或垂直连接相邻的陆地可以形成。这个问题的核心是识别并计数网格中相连的陆地…

CSV解析

一直以为csv靠逗号&#xff08;,&#xff09;分割数据&#xff0c;那么只要用str.spilt(,,row)便可以将数据分割开。 事实证明想简单了&#xff0c;csv里还有这样的规定&#xff0c;如果数据内有双引号&#xff08;"&#xff09;和逗号&#xff08;,&#xff09;那么&…

车载电子电器架构 —— 售后诊断开发

车载电子电器架构 —— 售后诊断开发 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己…

在Postgres中,如何有效地管理大型数据库的大小和增长

文章目录 一、定期清理和维护1. VACUUM和ANALYZE2. 删除旧数据和归档 二、分区表三、压缩数据四、配置优化1. 调整维护工作负载2. 监控和日志 五、使用外部存储和扩展1. 外部表和FDW2. 扩展和插件 六、定期备份和恢复测试结论 管理大型数据库的大小和增长是数据库管理员&#x…

Java中的变量与常量

标识符 Java语言规定标识符由任意顺序的字母、下划线&#xff08;_&#xff09;、美元符号&#xff08;$&#xff09;和数字组成&#xff0c;并且第一个字符不能是数字。标识符也不能是Java中的关键字&#xff08;保留字&#xff09;。 在Java语言中&#xff0c;标识符的字母…

环境监测系统--------MQ系列气体检测模块驱动教程(保姆级教程)

⏩ 大家好哇&#xff01;我是小光&#xff0c;嵌入式爱好者&#xff0c;一个想要成为系统架构师的大三学生。 ⏩在环境检测中我们经常会用到检测气体的传感器&#xff0c;检测乙醇、甲烷、一氧化碳、氢气等等&#xff0c;博主呕心沥血对MQ系列传感器做一个史上最详细的使用教程…

网络安全产品---堡垒机

what 在网上搜索 运维审计与风险控制系统就是是堡垒机 我认为的堡垒机就是提供高效运维、认证管理、访问控制、安全审计和报表分析功能的云服务设备 实现高效运维的同时最大程度控制运维风险。 how 能够对运维人员维护过程进行全面跟踪、控制、记录、回放 支持细粒度配置…

政企版 WPS Pro 专业版注册安装教程

政企版 WPS Pro 专业版安装及激活步骤 第 1 步&#xff1a;下载压缩包&#xff08;内含注册码&#xff09;【无解压密码】。 第 2 步&#xff1a;解压缩后&#xff0c;运行 exe 文件&#xff0c;默认步骤安装即可。 第 3 步&#xff1a;安装完成后&#xff0c;新建一个 Word …

使用QQ邮箱进行登录验证

使用场景不多说&#xff0c;接下来直接看实现~ 登录到QQ邮箱&#xff0c;进入设置 打开IMAP/SMTP服务&#xff0c;记得把授权码记录下来&#xff0c;后面配置文件中需要用到 新建application的配置文件 spring:mail:# 指定邮件服务器地址host: smtp.qq.comusername: 你自己的q…

ROS 2边学边练(31)-- 管理大工程

前言 往往现实中的工程都是会包含很多节点很多参数很多主题的那种&#xff0c;如果单独通过各种ros2 run命令进行启动管理&#xff0c;恐怕难以招架&#xff0c;主要还是通过launch文件的方式进行管理&#xff0c;而launch文件也可以像节点那样按功能的不同模块化&#xff0c;最…

【学习】黑盒测试用例设计方法都有哪些

在软件测试中&#xff0c;黑盒测试是一种重要的测试方法&#xff0c;它专注于软件的外部行为&#xff0c;而不关心其内部结构和实现。黑盒测试的目标是确保软件的功能符合需求规格说明书中的要求。为了有效地进行黑盒测试&#xff0c;需要设计合理的测试用例。本文将详细介绍黑…

【Android】Activity task和Instrumentation杂谈

文章目录 activity taskInstrumentation机制参考 Android不仅可以装载众多的系统组件&#xff0c;还可以将它们跨进程组成ActivityTask&#xff0c;这个特性使得每个应用都不是孤立的。 activity task 从数据结构角度看&#xff0c;Task有先后之分&#xff0c;源码实现上采取了…

基于SpringBoot+Vue钢材销售管理系统的设计与实现

系统介绍 为了更好地发挥本系统的技术优势&#xff0c;根据钢材销售管理系统的需求&#xff0c;本文尝试以B/S经典设计模式中的Spring Boot框架&#xff0c;JAVA语言为基础&#xff0c;通过必要的编码处理、钢材销售管理系统整体框架、功能服务多样化和有效性的高级经验和技术…

高级IO简介

一、非阻塞IO 阻塞其实就是进入了休眠状态&#xff0c;交出了 CPU 控制权。 普通文件的读写操作是不会阻塞的&#xff0c;不管读写多少个字节数据&#xff0c; read() 或 write() 一定会在有限的时间内返回&#xff0c;所以普通文件一定是以非阻塞的方式进行 I/O 操作&…

【Visual Studio 2012中文版】下载安装以及使用方法

文章目录 前言一、下载安装包二、安装步骤1.双击VS2012_ULT_chs.iso文件打开2.双击vs_ultimate.exe打开安装程序3.选择要安装的功能4.软件正在安装&#xff0c;请耐心等待10分钟5.安装成功&#xff0c;点击“启动”6.激活码&#xff08;产品密钥&#xff09; 三、VS2012使用&am…