共享内存简介和mmap 函数

一、共享内存简介

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


即每个进程地址空间都有一个共享存储器的映射区,当这块区域都映射到相同的真正的物理地址空间时,可以通过这块区域进行数据交换,例如共享库就是这么实现的,很多进程都会使用同一个函数如printf,也许在真正的物理地址空间中只存在一份printf.o ,然后所有进程都映射到这一份printf.o 就实现了共享。


用管道或者消息队列传递数据:


用共享内存传递数据:


即使用共享内存传递数据比用消息队列和管道来说,减少了进入内核的次数,提高了效率。


二、mmap 函数

#include <sys/mman.h>

功能:将文件或者设备空间映射到共享内存区。
原型 void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
参数
addr: 要映射的起始地址,通常指定为NULL,让内核自动选择
len:映射到进程地址空间的字节数
prot:映射区保护方式
flags:标志
fd:文件描述符
offset:从文件头开始的偏移量,必须是页大小的整数倍(在32位体系统结构上通常是4K)
返回值:成功返回映射到的内存区的起始地址;失败返回-1


prot 参数取值:

PROT_EXEC 表示映射的这一段可执行,例如映射共享库

PROT_READ 表示映射的这一段可读

PROT_WRITE 表示映射的这一段可写

PROT_NONE 表示映射的这一段不可访问


flag参数有很多种取值,这里只讲两种,其它取值可查看mmap(2)

MAP_SHARED 多个进程对同一个文件的映射是共享的,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。

MAP_PRIVATE 多个进程对同一个文件的映射不是共享的,一个进程对映射的内存做了修改,另一个进程并不会看到这种变化,也不会真的写到文件中去。


内存映射文件示意图:


如果mmap成功则返回映射首地址,如果出错则返回常数MAP_FAILED。当进程终止时,该进程的映射内存会自动解除,也可以调用munmap解除映射:

功能:取消mmap函数建立的映射
原型 int munmap(void *addr, size_t len);
参数
addr: 映射的内存起始地址
len:映射到进程地址空间的字节数
返回值:成功返回0;失败返回-1


下面写两个程序测试一下:

mmap_write.c

 

C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
 
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/mman.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

typedef struct stu
{
    char name[4];
    int age;
} STU;

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s <file>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int fd;
    fd = open(argv[1], O_CREAT | O_RDWR | O_TRUNC, 0666);
    if (fd == -1)
        ERR_EXIT("open");

    lseek(fd, sizeof(STU) * 5 - 1, SEEK_SET);
    write(fd, "", 1);

    STU *p;
    p = (STU *)mmap(NULL, sizeof(STU) * 5, PROT_READ | PROT_WRITE,
                    MAP_SHARED, fd, 0);

    if (p == -1)
        ERR_EXIT("mmap");

    char ch = 'a';
    int i;
    for (i = 0; i < 5; i++)
    {
        memcpy((p + i)->name, &ch, 1);
        (p + i)->age = 20 + i;
        ch++;
    }

    printf("initialize over\n");

    munmap(p, sizeof(STU) * 5);
    printf("exit...\n");
    return 0;
}

 

mmap_read.c

 

C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
 
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/mman.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

typedef struct stu
{
    char name[4];
    int age;
} STU;

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s <file>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int fd;
    fd = open(argv[1], O_RDWR);
    if (fd == -1)
        ERR_EXIT("open");


    STU *p;
    p = (STU *)mmap(NULL, sizeof(STU) * 5, PROT_READ | PROT_WRITE,
                    MAP_SHARED, fd, 0);

    if (p == -1)
        ERR_EXIT("mmap");

    int i;
    for (i = 0; i < 5; i++)
    {
        printf("name = %s age = %d\n", (p + i)->name, (p + i)->age);
    }
    munmap(p, sizeof(STU) * 5);
    printf("exit...\n");
    return 0;
}

 

先运行mmap_write ,然后用od -c 查看文件内容:

 

simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./mmap_write test 
initialize over
exit...
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ od -c test 
0000000   a  \0  \0  \0 024  \0  \0  \0   b  \0  \0  \0 025  \0  \0  \0
0000020   c  \0  \0  \0 026  \0  \0  \0   d  \0  \0  \0 027  \0  \0  \0
0000040   e  \0  \0  \0 030  \0  \0  \0
0000050
注意od -c 输出的是八进制,024即20,即对内存的操作写入了文件。

再尝试运行mmap_read,输出如下:
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./mmap_read test 
name = a age = 20
name = b age = 21
name = c age = 22
name = d age = 23
name = e age = 24
exit...
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ 

再次将文件test 映射到内存,然后从内存读取到了文件的内容。


mmap 编程注意点:

1、映射不能改变文件的大小;
2、可用于进程间通信的有效地址空间不完全受限于被映射文件的大小;
3、文件一旦被映射后,所有对映射区域的访问实际上是对内存区域的访问。映射区域内容写回文件时,所写内容不能超过文件的大小;


对于1,3点,将mmap_write.c 中40行以后的代码中的5改成10,即映射的内存大于文件的大小,这样写入是不会出错的,因为是向内存写入,但用od 查看时发现文件还是40 个字节,即只有前5个STU才被真正写入到了文件。

对于第2点,将mmap_write.c 和 mmap_read.c 都按上面说的更改成10,然后在mmap_write.c 中munmap 函数之前sleep(10); 先运行mmap_write,再在另一终端运行mmap_read,观察结果:

simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./mmap_read test 
name = a age = 20
name = b age = 21
name = c age = 22
name = d age = 23
name = e age = 24
name = f age = 25
name = g age = 26
name = h age = 27
name = i age = 28
name = j age = 29
exit...

即在mmap_write 对映射内存区域写入之后尚未取消映射时,mmap_read 也映射了test 文件,两个虚拟进程地址空间的映射区域都指向了同一块物理内存,所以也能读到write 进程对内存的修改,但进程结束后查看test 文件,还是40个字节而已。内存的映射是以页面为单位的,一般为4k,所以才有第2条的说法,其实这才是真正体现共享内存可以进程间通信的所在。


最后一点,与write 类似,将文件映射到内存后对内存进行写入,不一定会马上写回文件,有可能内核也会产生一个缓冲区,找个适当的时间内核再写回设备文件,write 之后可以调用fsync 进行同步,同样地,mmap 可以调用msync 进行同步。


 

参考:

《linux c 编程一站式学习》

《UNP》

转载于:https://www.cnblogs.com/alantu2018/p/8473111.html

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

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

相关文章

【pyqt5学习】——QToolTip,QLabel控件,伙伴关系

目录 0、学习资源 1、给控件设置提示信息——QToolTip 2、QLabel控件&#xff08;显示图像、设置超链接、信号绑定&#xff09; 3、QLabel伙伴关系 1&#xff09;代码 2&#xff09;效果 3&#xff09;知识点addWidget 0、学习资源 PyQt5教程&#xff0c;来自网易云课堂…

SylixOS中select原理及使用分析

2019独角兽企业重金招聘Python工程师标准>>> 1. select接口简介 1.1 select接口使用用例 select是操作系统多路I/O复用技术实现的方式之一。 多路I/O复用技术大致使用场景为&#xff1a;构造一张感兴趣的文件描述符列表&#xff0c;然后调用多路复用的IO接口&#x…

【pyqt5学习】——QLineEdit学习(回显模式)

目录 1、回显模式 2、成果显示 3、知识点 1&#xff09;FormLayout布局添加addRow方法 2&#xff09;在输入框显示灰色提示字体&#xff0c;输入内容时消失setPlaceholderText 3&#xff09;设置回显模式setEchoMode 4、完整代码 1、回显模式 QLineEdit控件的主要功能是输…

【pyqt5学习】——QLineEdit控件输入校验器Validator、掩码setInputMask限制输入、textChanged信号

目录 1、输入校验器——限制输入框输入的内容 1&#xff09;校验器类型——整数、浮点数、数字字母结合&#xff08;正则&#xff09; 2&#xff09;步骤 3&#xff09;结果 ​编辑 ​编辑 4&#xff09;完整代码 2、利用掩码进行输入的限制 0&#xff09;掩码对照表 1…

PyOpenCL图像处理:Box模糊

为什么80%的码农都做不了架构师&#xff1f;>>> # -*- coding: utf-8 -*-from __future__ import absolute_import, print_function import numpy as np import pyopencl as cl import cv2 from PIL import Imagedef RoundUp(groupSize, globalSize): r globalSi…

MySQL日志分类及性能分析你应该知道的知识

为什么80%的码农都做不了架构师&#xff1f;>>> MySQL日志记录了MySQL数据库日常操作和错误信息&#xff0c;MySQL总共有四种类型的日志&#xff0c;通过分析这些日志可以查询到MySQL的运行情况、用户操作、错误信息等&#xff0c;可以为MySQL的管理和优化提供必要…

android基础组件----Button的使用

按钮由文本或图标&#xff08;或文本和一个图标&#xff09;组成&#xff0c;当用户触摸到它时&#xff0c;会发生一些动作。今天我们开始Button的学习。少年的爱情永远不够用&#xff0c;一杯酒足以了却一件心事。 Button的简要说明 根据你是否想要一个带有文本的按钮&#xf…

【pyqt5学习】——QTextEdit控件学习:获取文本、添加文本

目录 1、QTextEdit控件介绍 2、QTextEdit控件添加文本、添加HTML格式 3、QTextEdit控件获取文本、获取HTML格式文本 4、案例 1&#xff09;完整代码 2&#xff09;效果 1、QTextEdit控件介绍 QTextEdit控件是一个支持多行输入的输入框&#xff0c;支持HTML进行格式的设置 2…

空间变化

空间变化 第一种&#xff1a;圆 第二种: 矩形 第三种&#xff1a;正五边形 第四种:正六边形 第五种&#xff1a;腰圆 算法&#xff1a; 在规则图形&#xff08;不包括圆心-直径法&#xff09;中&#xff0c;等于或者超过三个点。一般的&#xff0c;利用p1,p2,p3来建立一个用户…

【pyqt5学习】——QAbstractButton学习(普通按钮QPushButton、工具按钮QToolButton、单选按钮QRadioButton、复选框按钮QCheckBox)

目录 1、按钮介绍 2、普通按钮QPushButton学习 1&#xff09;特殊模式——开关按钮&#xff08;toggle&#xff09; ①将按钮设置为可选择的 ②将按钮按下 判断按钮是否被按下 isChecked() 2&#xff09;给按钮设置前置图标setIcon() ​编辑 3&#xff09;按钮信号 4)完…

挖财后端架构简介

挖财后端技术用的主要是比较大众的东西&#xff0c;Web容器用Tomcat&#xff0c;框架主要是Spring MVC&#xff0c;也有少量的Play&#xff0c;中间服务层是Dubbo&#xff0c;微容器用Spring Boot&#xff0c;服务注册这一块是用ZooKeeper&#xff0c;核心业务开发方式还是围绕…

【pyqt5学习】——QcomboBox学习

目录 1、添加条目&#xff1a; 1)逐一添加 2&#xff09;批量添加 3&#xff09;获取当前选中的索引 4&#xff09;获取当前选中文本 5&#xff09;根据索引获取文本 6&#xff09;统计条目总数 7&#xff09;信号——当前选中的索引发生改变currentIndexChanged() 2、…

vSphere Client 编辑虚拟机属性的问题

vSphere Client 编辑虚拟机属性的问题 编辑虚拟机属性的时候&#xff0c; 出现&#xff1a; vpxclient.vmconfig.cpuid 初始值设置异常之类的&#xff0c;重置了&#xff0c; 并将注册表中的所有vmvare 相关键值删除了&#xff0c; 还是一样的。。 后面参照https://kb.vmware.c…

【pyqt5学习】—— 滑动条Qslider、计数器QSpinBox学习

目录 1、滑动条QSlider 1)常用属性 2&#xff09;实例——利用滑动条来实现字体大小的修改 ​编辑 2、计数器QSpinBox 1&#xff09;属性方法 2&#xff09;实例 1、滑动条QSlider 1)常用属性 self.slider QSlider(Qt.Horizontal)# 设置最小值self.slider.setMinimum(2)…

【pyqt5学习】——对话框QDialog学习(QMessageBox、QColorDialog、QFIleDialog、QFontDialog、QInputDialog)

目录 1、对话框QDialog类别 2、通用对话框 ​编辑 3、消息对话框QMessageBox() 1&#xff09;消息对话框QMessageBox类型 2&#xff09;案例 ​编辑 4、输入对话框QInputDialog 1) 类型 2&#xff09;案例 5、字体格式对话框QFontDialog 6、颜色对话框QColorDialog 1&…

使用 Mesos 管理虚拟机

摘要 为了满足渲染、基因测序等计算密集型服务的需求&#xff0c;UCloud 推出了“计算工厂”产品&#xff0c;让用户可以快速创建大量的计算资源&#xff08;虚拟机&#xff09;。该产品的背后&#xff0c;是一套基于 Mesos 的计算资源管理系统。本文简要介绍该系统的结构、Mes…

Val编程-套接字

套接字的介绍&#xff1a;http://zh.wikipedia.org/wiki/Berkeley%E5%A5%97%E6%8E%A5%E5%AD%97 主要分为客户端和服务器。客户端一般是需要主动去链接&#xff0c;需要配置服务器的IP和端口。服务器是被动响应&#xff0c;需要打开相应的端口。端口一般不推荐使用系统端口和常用…

【pyqt5学习】——Qpainter控件学习(文本、图像、各种图形)

目录 1、作用及应用步骤 2、绘制文本drawText() 3、像素级别绘制点&#xff08;正弦曲线为例&#xff09; 4、绘制不同样式的直线 5、绘制弧形、圆形、扇形、图像等 6、画刷填充区域QBrush 1、作用及应用步骤 QPainter是一个用于绘制的类&#xff0c;该类可以用于绘制&…

反向代理与Real-IP和X-Forwarded-For(转)

如下图所示&#xff0c;客户端通过Nginx Proxy1 和 Nginx Proxy2 两层反向代理才访问到具体服务Nginx Backend&#xff08;或如Tomcat服务&#xff09;。那Nginx Backend如何才能拿到真实客户端IP呢&#xff1f; 接下来我们来看看如何才能获取到客户端真实IP。 场景1  场景1是…

Android studio打开之后 cannot load project: java.lang.NUllpointerException

参考来源&#xff1a;http://bbs.csdn.net/topics/391014393 关闭网络&#xff0c;重新打开Android studio就好了。&#xff08;但是原因不清楚是为什么&#xff1f;&#xff09; Internal error. Please report to http://code.google.com/p/android/issuescom.intellij.ide.p…