什么是共享内存?在内存中的具体位置?shmget的具体使用原理以及其他关联函数(shmat ( ),shmdt ( ),shmctl ( ))、以及C++应用案例?mmap和shm的区别?
共享内存(shared memory)
- 共享内存
- 1、背景
- 2、定义
- 3、两种方式(mmap上一篇博客说明)
- 3.2、shmget
- 3.2.1、使用流程和基本原理
- 3.2.2、函数原型(创建共享内存)
- 3.2.3、其他相关AP函数
- 3.2.3.1、shmat ( ):挂接共享内存
- 3.2.3.2、shmdt ( ):去关联共享内存
- 3.2.3.3、shmctl ( ):控制共享内存
- 3.2.4、应用实例
- 4、mmap和shm的区别
- 参考
共享内存
1、背景
当存在客户-服务程序中复制文件时候,其数据流如下,要经历四次数据复制,开销很大。具体如下:
进程调用read或是write后会陷入内核,因为这两个函数都是系统调用,进入系统调用后,内核开始读写文件,
- 假设内核在读取文件,内核首先把文件读入自己的内核空间,
- 读完之后进程在内核回归用户态,内核把读入内核内存的数据再copy进入进程的用户态内存空间。
- 实际上我们同一份文件内容相当于读了两次,先读入内核空间,再从内核空间读入用户空间。
如果采用共享内存的方式,那么将大大优化IO操作,数据流变成了如下,数据只复制两次,[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。
- Linux提供了内存映射函数mmap, 它把文件内容映射到一段内存上(准确说是
虚拟内存
上), 通过对这段内存的读取和修改,实现对文件的读取和修改; - 普通文件映射到进程地址空间后,进程可以向访问内存的方式对文件进行访问,不需要其他系统调用(read,write)去操作。
2、定义
- 共享内存,顾名思义就是允许两个不相关的进程
访问同一个逻辑内存
- 不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。
- 如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
- 这是最快的进程间通信方式,但是
不提供同步功能
(需要我们信号量实现)。
3、两种方式(mmap上一篇博客说明)
mmap参考链接
这里简单对mmap() 和 shmget()基本原理简单说明
mmap() 基本原理
:可以把一个文件映射到进程的地址空间
(进程使用的虚拟内存),这样进程就可以通过读写这个进程地址空间来读写这个文件。
- mmap()通过映射一个
普通文件
实现共享内存
shmget() 基本原理
:内核里存在着一个特殊的文件系统(shm)
,这个文件系统的存储介质不是别的,正是 RAM。
- 在
shmget()
调用之后,系统会为你在这个文件系统上创建一个文件
,但是这个时候仅仅是创建了这个文件。 - 然后你就应该调用
shmat()
了,调用shmat()
之后,内核会这个文件映射到你的进程地址空间,这个时候你就能直接读写映射后的地址了。
总结
创建共享内存
。通过函数shmget()从内存中获取一块共享内存区域,该函数返回值为共享内存的ID。映射共享内存
。通过函数shmat()将上一步获取的共享内存映射到具体的内存空间。
3.2、shmget
3.2.1、使用流程和基本原理
使用流程
- 1、进程间需要
共享的数据
被放在一个叫做IPC共享内存区域
的地方,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。 - 2、系统V共享内存通过
shmget
获得或创建一个IPC共享内存区域
,并返回相应的标识符
。 - 3、内核在保证
shmget
获得或创建一个共享内存区,初始化该共享内存区相应的shmid_kernel
结构体 - 4、内核在
特殊文件系统shm
中,创建并打开一个同名文件,并在内存中建立起该文件的相应dentry
及inode结构
,新打开的文件不属于任何一个进程(任何进程都可以访问该共享内存区)。
所有这一切都是系统调用shmget
完成的。(下面的图结合上面的流程,理解起来可能稍微容易一些)
基本原理
每一个共享内存区都有一个控制结构struct shmid_kernel
,shmid_kernel
是共享内存区域中非常重要的一个数据结构,它是存储管理和文件系统结合起来的桥梁。
struct shmid_kernel /* private to the kernel */
{ struct kern_ipc_perm shm_perm;struct file * shm_file;int id;unsigned long shm_nattch;unsigned long shm_segsz;time_t shm_atim;time_t shm_dtim;time_t shm_ctim;pid_t shm_cprid;pid_t shm_lprid;
};
该结构中最重要的一个域应该是shm_file
,它存储了将被映射文件的地址。
内核通过数据结构struct ipc_ids shm_ids维护系统中的所有共享内存区域。
1、上图中的shm_ids.entries
变量指向一个ipc_id结构数组
,而每个ipc_id
结构数组中有个指向kern_ipc_perm结构的指针
。
2、对于系统V共享内存区来说,kern_ipc_perm的宿主是shmid_kernel结构
,shmid_kernel
是用来描述一个共享内存区域的,这样内核就能够控制系统中所有的共享区域。
3、在shmid_kernel
结构的file类型指针shm_file
指向文件系统shm中相应的文件
,这样,共享内存区域就与shm文件系统中的文件对应起来。
3.2.2、函数原型(创建共享内存)
#include<sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
返回值:
成功时返回一个新建或已经存在的的共享内存标识符,取决于shmflg的参数。失败返回-1并设置错误码。
参数说明:
key
:非0整数,它有效地为共享内存段命名,shmget()函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.size
:需要申请共享内存的大小。在操作系统中,申请内存的最小单位为页,一页是4k字节,为了避免内存碎片,我们一般申请的内存大小为页的整数倍。shmflg
:如果要创建新的共享内存,需要使用IPC_CREAT,IPC_EXCL,如果是已经存在的,可以使用IPC_CREAT或直接传0。- 举例来说,
0644
,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。
- 举例来说,
3.2.3、其他相关AP函数
3.2.3.1、shmat ( ):挂接共享内存
第一次创建完共享内存时,它还不能被任何进程访问,shmat()函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。它的原型如下:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
返回值:
调用成功时返回一个指向共享内存第一个字节的指针
,并且内核将使其与该共享存储段相关的shmid_ds结构中的shm_nattch计数器加1(类似于引用计数);出错返回-1。
参数说明:
- 1、
shm_id
:由shmget()函数返回的共享内存标识。 - 2、
shm_addr
:指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址 - 3、
shm_flg
:是一组标志位,通常为0
3.2.3.2、shmdt ( ):去关联共享内存
该函数用于将共享内存从当前进程中分离
。注意,将共享内存分离并不是删除它
,只是使该共享内存对当前进程不再可用。它的原型如下:
int shmdt(const void *shmaddr);
返回值:
调用成功时返回0,失败时返回-1.
参数说明:
shmaddr
:shmat()函数返回的地址指针
3.2.3.3、shmctl ( ):控制共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
返回值:
成功返回0,失败返回-1。
参数说明:
- 1、
shm_id
:shmget()函数返回的共享内存标识符。 - 2、
command
:要采取的操作,它可以取下面的三个值 :- IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
- IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID
:删除共享内存段
- 3、
buf
:一个结构指针,它指向共享内存模式和访问权限的结构,设置为NULL
即可。
3.2.4、应用实例
实例:进程间通信
shmread.c
:创建共享内存,并读取其中的信息,
shmwrite.c
:向共享内存中写入数据。
shmdata.h
:定义结构shared_use_st
中的written
作为一个可读或可写的标志,非0:表示可读,0:表示可写,text则是内存中的文件。
shmdata.h的源代码
#ifndef _SHMDATA_H_HEADER
#define _SHMDATA_H_HEADER#define TEXT_SZ 2048struct shared_use_st
{int written; // 作为一个标志,非0:表示可读,0:表示可写char text[TEXT_SZ]; // 记录写入 和 读取 的文本
};#endif
shmread.c的源代码
#include <stddef.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "shmdata.h"int main(int argc, char **argv)
{void *shm = NULL;struct shared_use_st *shared; // 指向shmint shmid; // 共享内存标识符// 创建共享内存shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);if (shmid == -1){fprintf(stderr, "shmat failed\n");exit(EXIT_FAILURE);}// 将共享内存连接到当前进程的地址空间shm = shmat(shmid, 0, 0);if (shm == (void *)-1){fprintf(stderr, "shmat failed\n");exit(EXIT_FAILURE);}printf("\nMemory attached at %X\n", (int)shm);// 设置共享内存shared = (struct shared_use_st*)shm; // 注意:shm有点类似通过 malloc() 获取到的内存,所以这里需要做个 类型强制转换shared->written = 0;while (1) // 读取共享内存中的数据{// 没有进程向内存写数据,有数据可读取if (shared->written == 1){printf("You wrote: %s", shared->text);sleep(1);// 读取完数据,设置written使共享内存段可写shared->written = 0;// 输入了 end,退出循环(程序)if (strncmp(shared->text, "end", 3) == 0){break;}}else // 有其他进程在写数据,不能读取数据{sleep(1);}}// 把共享内存从当前进程中分离if (shmdt(shm) == -1){fprintf(stderr, "shmdt failed\n");exit(EXIT_FAILURE);}// 删除共享内存if (shmctl(shmid, IPC_RMID, 0) == -1){fprintf(stderr, "shmctl(IPC_RMID) failed\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);
}
shmwrite.c的源代码
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include "shmdata.h"int main(int argc, char **argv)
{void *shm = NULL;struct shared_use_st *shared = NULL;char buffer[BUFSIZ + 1]; // 用于保存输入的文本int shmid;// 创建共享内存shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);if (shmid == -1){fprintf(stderr, "shmget failed\n");exit(EXIT_FAILURE);}// 将共享内存连接到当前的进程地址空间shm = shmat(shmid, (void *)0, 0);if (shm == (void *)-1){fprintf(stderr, "shmat failed\n");exit(EXIT_FAILURE);}printf("Memory attched at %X\n", (int)shm);// 设置共享内存shared = (struct shared_use_st *)shm;while (1) // 向共享内存中写数据{// 数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本while (shared->written == 1){sleep(1);printf("Waiting...\n");}// 向共享内存中写入数据printf("Enter some text: ");fgets(buffer, BUFSIZ, stdin);strncpy(shared->text, buffer, TEXT_SZ);// 写完数据,设置written使共享内存段可读shared->written = 1;// 输入了end,退出循环(程序)if (strncmp(buffer, "end", 3) == 0){break;}}// 把共享内存从当前进程中分离if (shmdt(shm) == -1){fprintf(stderr, "shmdt failed\n");exit(EXIT_FAILURE);}sleep(2);exit(EXIT_SUCCESS);
}
分析:
1、程序
shmread创建共享内存
,然后将它连接到自己的地址空间。- 在共享内存的开始处使用了一个结构
struct_use_st
。该结构中有个标志written
:- 当共享内存中有其他进程向它写入数据时,共享内存中的
written被设置为0
,程序等待。 - 当它不为0时,表示没有进程对共享内存写入数据,程序就从共享内存中读取数据并输出,然后重置设置共享内存中的written为0,即让其可被shmwrite进程写入数据。
- 当共享内存中有其他进程向它写入数据时,共享内存中的
- 在共享内存的开始处使用了一个结构
2、程序
shmwrite取得共享内存并连接到自己的地址空间
。- 检查共享内存中的
written,是否为0
不为0
,表示共享内存中的数据还没有被完,则等待其他进程读取完成,并提示用户等待。为0
,表示没有其他进程对共享内存进行读取,则提示用户输入文本,- 再次设置共享内存中的
written为1
,表示写完成,其他进程可对共享内存进行读操作
。
- 检查共享内存中的
4、mmap和shm的区别
linux中的两种共享内存。一种是我们的IPC通信System V版本的共享内存(shm),另外的一种就是内存映射I/O(mmap函数)
- 1、
mmap()
通过映射一个普通文件
实现共享内存,shmget()
:内核里存在着一个特殊的文件系统(shm)
,这个文件系统的存储介质不是别的,正是 RAM。- 所以,mmap可以看到文件的实体,而 shmget 对应的文件在交换分区上的 shm 文件系统内,无法直接 cat 查看
速度比较
:shm保存在RAM,这样读写的速度要比磁盘要快,但是存储量不是特别大。mmap是在磁盘上建立一个文件安全比较
: mmap把文件保存在磁盘上,这个文件还保存了操作系统同步的映像,所以mmap不会丢失,但是shmget就会丢失。
- 2、mmap是在磁盘上建立一个文件,每个进程地址空间中都会开辟出一块空间进行
文件-内存的映射
。
而对于shm而言,shm每个进程最终会映射到同一块物理内存。- mmap 方式下各进程映射文件的相同部分可以共享内存, shmget 时各个进程共享同一片物理内存区
参考
1、https://www.cnblogs.com/huxiao-tee/p/4660352.html
2、https://blog.csdn.net/mj813/article/details/52082499
3、https://www.cnblogs.com/52php/p/5861372.html
4、https://blog.csdn.net/woaiclh13/article/details/106409361
什么是共享内存?在内存中的具体位置?shmget的具体使用原理以及其他关联函数(shmat ( ),shmdt ( ),shmctl ( ))、以及C++应用案例?mmap和shm的区别?相关推荐
- 共享内存(shmget,shmat,shmdt,shmctl)
共享内存shmgetshmatshmdtshmctl shmget int shmget(key_t key, size_t size, int flag); key: 标识符的规则 size:共享存 ...
- Linux 3.进程间通信(shmget shmat shmdt shmctl 共享内存、signal signaction sigqueue 信号、semget semctl semop 信号量)
Linux 3.进程间通信(IPC) 共享内存 共享内存的接口指令 shmget 创建获取获取共享内存 shmat 映射:连接共享内存到当前进程的地址空间 shmdt 断开与共享内存的连接 shmct ...
- python共享内存mmap_python - IPC在单独的Docker容器中的Python脚本之间共享内存 - 堆栈内存溢出...
问题 我已经编写了一个神经网络分类器,该分类器可以获取海量图像(每张图像约1-3 GB),将其打补丁,然后分别通过网络传递这些补丁. 培训的进行过程非常缓慢,因此我对其进行了基准测试,发现用大约50秒 ...
- 什么是共享内存?在内存中的具体位置?共享内存相关API,mmap 的具体使用原理、以及C++应用案例?
共享内存(shared memory) 共享内存 1.背景 2.定义 3.两种方式(shmget下一篇博客说明) 3.1.mmap 3.1.1.调用过程 3.1.2.优点总结 3.1.3.API函数( ...
- mmap和shm共享内存的区别和联系
共享内存的创建 根据理论: 1. 共享内存允许两个或多个进程共享一给定的存储区,因为数据不需要来回复制,所以是最快的一种进程间通信机制.共享内存可以通过mmap()映射普通文件(特殊情况下还可以采用匿 ...
- Java 类中各成分加载顺序和内存中的存放位置
一.什么时候会加载类? 使用到类中的内容时加载:有三种情况 1.创建对象:new StaticCode(); 2.使用类中的静态成员:StaticCode.num=9; StaticCode.show ...
- 操作系统核心原理-5.内存管理(中):分页内存管理
在上一篇介绍的几种多道编程的内存管理模式中,以交换内存管理最为灵活和先进.但是这种策略也存在很多重大问题,而其中最重要的两个问题就是空间浪费和程序大小受限.那么有什么办法可以解决交换内存存在的这些问题 ...
- 共享内存---shmget shmat shmdt
From: http://fengxue103.blog.hexun.com/32303320_d.html 要使用共享内存,应该有如下步骤: 1.开辟一块共享内存 shmget() 2.允许本进程使 ...
- java线程工作内存在栈中吗_JVM常见面试题解析
前言 总结了JVM一些经典面试题,分享出我自己的解题思路,希望对大家有帮助,有哪里你觉得不正确的话,欢迎指出,后续有空会更新. 1.什么情况下会发生栈内存溢出. 思路: 描述栈定义,再描述为什么会溢出 ...
最新文章
- 依赖属性之“风云再起”三
- Apache配置静态缓存
- 递归——阶乘加斐波那契数列(简单掌握递归思想的敲门砖)
- Laravel服务提供者在平台短信服务中的应用
- 漫步最优化四十一——Powell法(下)
- 【NOIP2013模拟】七夕祭
- 【Computer Organization笔记11】多周期CPU
- TensorFlow入门篇(一):搭建简单的线性拟合例子
- 【渝粤教育】国家开放大学2018年春季 8618-22T燃气行业规范 参考试题
- Win8系统 界面大放送(Win8 抢先版)
- linux comd skill
- 。成功实现avd 模拟器 与pc 虚拟串口实现通信 通过多方文章综合
- 开发者必备的网站。javascript手册,css手册
- turtlebot运动控制问题(不用键盘控制,自己写控制节点控制地盘)
- 微信支付的appid,appsecret,商户号mchid,微信交易支付密钥在哪里
- 【Camera】通过查看位置方向的平面进行灵活的相机校准
- 做PPT只会用黑体和宋体?这些可商用字体瞬间提升你的PPT档次
- 方格取数(多线程dp,深搜)
- Swiper:无限循环滚动时出现空白页/页面内容不刷新
- 利用TUN创建虚拟网络