共享内存:mmap函数实现

内存映射的应用:

  • 以页面为单位,将一个普通文件映射到内存中,通常在需要对文件进行频繁读写时使用,这样用内存读写取代I/O读写,以获得较高的性能;
  • 将特殊文件进行匿名内存映射,可以为关联进程提供共享内存空间;
  • 为无关联的进程提供共享内存空间,一般也是将一个普通文件映射到内存中。

相关API

#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
void *mmap64(void *addr, size_t length, int prot, int flags,int fd, off64_t offset);
int munmap(void *addr, size_t length);int msync(void *addr, size_t length, int flags);

mmap函数说明:

  1. 参数 addr 指明文件描述字fd指定的文件在进程地址空间内的映射区的开始地址,必须是页面对齐的地址,通常设为 NULL,让内核去选择开始地址。任何情况下,mmap 的返回值为内存映射区的开始地址。

  2. 参数 length 指明文件需要被映射的字节长度。off 指明文件的偏移量。通常 off 设为 0 。

    • 如果 len 不是页面的倍数,它将被扩大为页面的倍数。扩充的部分通常被系统置为 0 ,而且对其修改并不影响到文件。
    • off 同样必须是页面的倍数。通过 sysconf(_SC_PAGE_SIZE) 可以获得页面的大小。
  3. 参数 prot 指明映射区的保护权限。通常有以下 4 种。通常是 PROT_READ | PROT_WRITE 。

    • PROT_READ 可读
    • PROT_WRITE 可写
    • PROT_EXEC 可执行
    • PROT_NONE 不能被访问
  4. 参数 flag 指明映射区的属性。取值有以下几种。MAP_PRIVATE 与 MAP_SHARED 必选其一,MAP_FIXED 为可选项。

    • MAP_PRIVATE 指明对映射区数据的修改不会影响到真正的文件。
    • MAP_SHARED 指明对映射区数据的修改,多个共享该映射区的进程都可以看见,而且会反映到实际的文件。
    • MAP_FIXED 要求 mmap 的返回值必须等于 addr 。如果不指定 MAP_FIXED 并且 addr 不为 NULL ,则对 addr 的处理取决于具体实现。考虑到可移植性,addr 通常设为 NULL ,不指定 MAP_FIXED。
  5. 当 mmap 成功返回时,fd 就可以关闭,这并不影响创建的映射区。

munmap函数说明:

进程退出的时候,映射区会自动删除。不过当不再需要映射区时,可以调用 munmap 显式删除。当映射区删除后,后续对映射区的引用会生成 SIGSEGV 信号。

msync函数说明:

文件一旦被映射后,调用mmap()的进程对返回地址的访问是对某一内存区域的访问,暂时脱离了磁盘上文件的影响。所有对mmap()返回地址空间的操作只在内存中有意义,只有在调用了munmap()后或者msync()时,才把内存中的相应内容写回磁盘文件。

代码实例:

两个进程通过映射普通文件实现共享内存通信

map_normalfile1.c及map_normalfile2.c。编译两个程序,可执行文件分别为map_normalfile1及map_normalfile2。两个程序通过命令行参数指定同一个文件来实现共享内存方式的进程间通信。map_normalfile2试图打开命令行参数指定的一个普通文件,把该文件映射到进程的地址空间,并对映射后的地址空间进行写操作。map_normalfile1把命令行参数指定的文件映射到进程地址空间,然后对映射后的地址空间执行读操作。这样,两个进程通过命令行参数指定同一个文件来实现共享内存方式的进程间通信。

/*-------------map_normalfile1.c-----------*/
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>typedef struct{char name[4];int  age;
}people;int main(int argc, char** argv) // map a normal file as shared mem:
{int fd,i;people *p_map;char temp[2] = {'\0'};fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);lseek(fd,sizeof(people)*5-1,SEEK_SET);write(fd,"",1);p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );close( fd );temp[0] = 'a';for(i=0; i<15; i++){temp[0] += 1;memcpy( ( *(p_map+i) ).name, &temp[0],2 );( *(p_map+i) ).age = 20+i;}printf("initialize over\n");sleep(10);munmap( p_map, sizeof(people)*10 );printf( "umap ok \n" );return 0;
}
/*-------------map_normalfile2.c-----------*/
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>typedef struct{char name[4];int  age;
}people;int main(int argc, char** argv) // map a normal file as shared mem:
{int fd,i;people *p_map;char temp[2] = {'\0'};fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);lseek(fd,sizeof(people)*5-1,SEEK_SET);write(fd,"",1);p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );close( fd );temp[0] = 'a';for(i=0; i<15; i++){temp[0] += 1;memcpy( ( *(p_map+i) ).name, &temp[0],2 );( *(p_map+i) ).age = 20+i;}printf("initialize over\n");sleep(10);munmap( p_map, sizeof(people)*10 );printf( "umap ok \n" );return 0;
}

map_normalfile1首先打开或创建一个文件,并把文件的长度设置为5个people结构大小.mmap映射10个people结构大小的内存,利用返回的地址开始设置15个people结构。然后睡眠10S,等待其他进程映射同一个文件,然后解除映射。

通过实验,在map_normalfile1输出initialize over 之后,输出umap ok之前,运行map_normalfile2 file,可以输出设置好的15个people结构

在map_normalfile1 输出umap ok后,运行map_normalfile2则输出结构,前5个people是已设置的,后10结构为0。

1) 最终被映射文件的内容的长度不会超过文件本身的初始大小,即映射不能改变文件的大小.
2) 可以用于进程通信的有效地址空间大小大体上受限于被映射文件的大小,但不完全受限于文件大小.打开文件的大小为5个people结构,映射长度为10个people结构长度,共享内存通信用15个people结构大小。
在linux中,内存的保护是以页为基本单位的,即使被映射文件只有一个字节大小,内核也会为映射分配一个页面大小的内存。当被映射文件小于一个页面大小时,进程可以对从mmap()返回地址开始的一个页面大小进行访问,而不会出错;但是,如果对一个页面以外的地址空间进行访问,则导致错误发生,后面将进一步描述。因此,可用于进程间通信的有效地址空间大小不会超过文件大小及一个页面大小的和。
3) 文件一旦被映射后,调用mmap()的进程对返回地址的访问是对某一内存区域的访问,暂时脱离了磁盘上文件的影响。所有对mmap()返回地址空间的操作只在内存中有意义,只有在调用了munmap()后或者msync()时,才把内存中的相应内容写回磁盘文件,所写内容仍然不能超过文件的大小

技巧:
生成固定大小的文件的两种方式:

/*第一种方法*/
fd = open(PATHNAME, O_RDWR | O_CREAT | O_TRUNC, FILE_MODE);
lseek(fd, filesize-1, SEEK_SET);
write(fd, "", 1);/*第二种方法*/
ftruncate(fd, filesize);

父子进程通过匿名映射实现共享内存

  1. 匿名内存映射 与 使用 /dev/zero 类型,都不需要真实的文件。要使用匿名映射之需要向 mmap 传入 MAP_ANON 标志,并且 fd 参数 置为 -1 。
  2. 所谓匿名,指的是映射区并没有通过 fd 与 文件路径名相关联。匿名内存映射用在有血缘关系的进程间。
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{char name[4];int  age;
}people;
main(int argc, char** argv)
{int i;people *p_map;char temp;p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);if(fork() == 0){sleep(2);for(i = 0;i<5;i++)printf("child read: the %d people's age is %d\n",i+1,(*(p_map+i)).age);(*p_map).age = 100;munmap(p_map,sizeof(people)*10); //实际上,进程终止时,会自动解除映射。exit();}temp = 'a';for(i = 0;i<5;i++){temp += 1;memcpy((*(p_map+i)).name, &temp,2);(*(p_map+i)).age=20+i;}sleep(5);printf( "parent read: the first people,s age is %d\n",(*p_map).age );printf("umap\n");munmap( p_map,sizeof(people)*10 );printf( "umap ok\n" );
}

参考:

  • man pthread_mutexattr_init 查看信号量进程间同步实现实例
  • http://blog.csdn.net/nancygreen/article/details/6558039
  • http://blog.chinaunix.net/uid-20564848-id-74123.html
  • Linux环境进程间通信(五): 共享内存(上)

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

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

相关文章

MSYS2开发环境搭建

MSYS2开发环境搭建 软件安装 下载msys2-x86_64软件包 https://www.msys2.org/&#xff0c;双击安装到某根目录下&#xff0c;比如D:\msys64。 pacman是MSYS2自带的软件管理工具&#xff1a; 可通过修改msys64\etc\pacman.d下的三个文件修改软件源&#xff0c;可供选择的源有…

设置python路径

在python开发应用&#xff0c;我们多数是通过pip、easy_install等工具将需要的python安装到自己机子上就可以应用了&#xff0c;但是我们完成开发给用户使用时&#xff0c;程序运行环境就是一个问题。当然&#xff0c;你可以要求客户按照你的方法安装依赖的库&#xff0c;这种方…

linux动态库查找路径以及依赖关系梳理

编译时与运行时库的路径 linux下&#xff0c;编译时与运行时库的搜索路径是不同的 运行时动态库的路径搜索顺序 LD_PRELOAD环境变量&#xff0c;一般用于hack 编译目标代码时指定的动态库搜索路径(指的是用 -wl,rpath 或-R选项而不是-L)&#xff0c;readelf -d命令可以查看编…

eclipse--android开发环境搭建教程

引言 在windows安装Android的开发环境不简单也说不上算复杂&#xff0c;但由于国内无法正常访问google给android开发环境搭建带来不小的麻烦。现将本人搭建过程记录如下&#xff0c;希望会对投身android开发的小伙伴有所帮助。 android开发环境部署过程 安装JDK环境 下载安装…

pip工具使用总结以及常用库PIL、freetype的安装

pip工具安装使用 pip为python库软件管理工具pip docs 安装 wget https://bootstrap.pypa.io/ez_setup.py -O - | python 安装setuptools https://pypi.python.org/pypi/setuptoolswget https://bootstrap.pypa.io/get-pip.py -O - | python 安装pip工具 ttps://pypi.python.…

【技巧】Chrome应用技巧

把Chrome浏览器变成文本编辑器 在浏览器地址栏中输入一行代码&#xff1a;data:text/html, <html contenteditable>&#xff0c;回车即可把浏览器变临时编辑器。【CtrlShiftJ】调出JavaScript控制台&#xff0c;在控制台输入&#xff1a; document.body.contentEditable…

eclipse--python开发环境搭建

pydev插件介绍 PyDev is a Python IDE for Eclipse pydev官方网站&#xff1a;http://www.pydev.org/ 在Eclipse中安装pydev插件 启动Eclipse, 点击Help->Install New Software… 在弹出的对话框中&#xff0c;点Add 按钮。 Name中填:Pydev, Location中填http://pydev.or…

Win7虚拟无线AP以及Android手机抓包

设备要求 Windows7操作系统装有无线网卡的笔记本或台式机无线网卡必须支持“承载网络” 查看无线网卡是否支持“承载” 方法一: 开始菜单→所有程序→附件→命令提示符→右键“以管理员权限运行”; 键入命令“netsh wlan show drivers”,查看“支持承载网络”这一项,如果是…

CMD命令之BAT脚本路径信息

CD命令解疑 cd是chdir的缩写&#xff0c;命令详解参见cd /? 可以看到/d参数的解释如下&#xff1a; 使用 /D命令行开关&#xff0c;除了改变驱动器的当前目录之外&#xff0c;还可改变当前驱动器。 通常我们在xp系统中打开cmd窗口时&#xff0c;会显示 C:\Documents and Se…

Ubuntu开发环境搭建

在虚拟中试玩Ubuntu1604版本&#xff0c;有关安装后一些配置记录如下&#xff0c;以备后用。 简单设置 root密码设置 虚拟机安装完成后&#xff0c;默认不弃用root用户&#xff0c;需要给root设置密码后使用 sudo passwd root终端加入右键中 将终端加入右键后&#xff0c;在某…

Python GUI Programming (Tkinter)

Tkinter编程实例 #!/usr/bin/python #coding:utf-8from Tkinter import * import sysreload(sys) sys.setdefaultencoding(utf-8)class GUI_WINDOWS:def __init__(self, root):self.root Frame(root)self.driver Noneself.friendEdit Noneself.bStopQuery Falseself.loadF…

【ubuntu 22.04】安装vscode并配置正常访问应用商店

注意&#xff1a;要去vscode官网下载deb安装包&#xff0c;在软件商店下载的版本不支持输入中文 在ubuntu下用火狐浏览器无法访问vscode官网&#xff0c;此时可以手动进行DNS解析&#xff0c;打开DNS在线查询工具&#xff0c;解析以下主机地址&#xff08;复制最后一个IP地址&a…

Appium安装使用总结

开发环境搭建 搭建java JDK与Android SDK环境下载安装nodejs下载安装Appium 问题答疑 在测试中adb devices查询无缘无故多出陌生的虚拟设备 同事的经验是重启电脑&#xff0c;莫名其妙的设备消失&#xff1b;自我总结就是重启adb服务&#xff0c;adb kill-server&#xff0…

Python使用笔记总结目录

从2012年接触python&#xff0c;陆陆续续使用python将近3年。从最开始的使用python进行xml解析&#xff0c;到使用python做爬虫开发&#xff0c;再到最近半年的使用深度学习进行图像识别&#xff0c;python简洁易用&#xff0c;丰富的资源库&#xff0c;活跃的社区&#xff0c;…

原始套接字编程(1)

Linux下原始套接字的原理 创建原始套接字&#xff1a; socket(AF_NET, SOCK_RAW, protocol);1. 参数protocol用来致命所接收的协议包&#xff0c;如果是像IPPROTO_TCP(6)这种非0、非255的协议&#xff0c;能接收ip头为protocol域的数据包&#xff0c;包括IP头&#xff0c;协议…

VS 使用技能总结

常用快捷键 复制/剪切/删除整行代码 1&#xff09;如果你想复制一整行代码&#xff0c;只需将光标移至该行&#xff0c;再使用组合键“CtrlC”来完成复制操作&#xff0c;而无需选择整行。 2&#xff09;如果你想剪切一整行代码&#xff0c;只需将光标移至该行&#xff0c;再使…

vim使用总结

vim使用命令 【自动补全操作】 Ctrl n / Ctrl p 自动补全字符串Ctrl x Ctrl f 自动补全文件名 【多文件操作】 打开多个文件&#xff1a; vim file1 file2 … filen 在同一个窗口中打开所有想要打开的文件vim -o file1 file2 … filen 打开一个编辑会话&#xff0c;水平分…

内核编程之Hello_kernel

前言 通过hello_kernel编程达到以下目的: 内核编程框架了解模块参数调用模块间函数调用内核模块相关关的命令使用 环境准备 内核源码树准备,有两种方法: 方法1&#xff1a;从kernel.org下载内核源码&#xff0c;手动编译 方法2&#xff1a;通过yum安装与系统版本一致的内…

机器学习基本库学习

前言 之前做过将近一年的python爬虫&#xff0c;python语言基础还是有的。眼下机器学习如日中天&#xff0c;项目中有用到机器学习对采集的内容进行分类的功能&#xff0c;闲暇之际学习一下相关的库&#xff0c;以期待money 简介 numpy python科学计算基础库matplotlib Ma…

unixODBC的使用

unixODBC介绍 unixODBC is a complete, free/open, ODBC solution for UNIX/Linux. 官网 http://www.unixodbc.org/源码下载url ftp://ftp.unixodbc.org/pub/unixODBC/ unixODBC版本目前有两套&#xff0c;soname为libodbc.so.1与soname为libodbc.so.2 对于使用libodbc.so.…