linux编程之内存映射
一.概述
内存映射是在调用进程的虚拟地址空间创建一个新的内存映射。
内存映射分为2种:
1.文件映射:将一个普通文件的全部或者一部分映射到进程的虚拟内存中。映射后,进程就可以直接在对应的内存区域操作文件内容!
2.匿名映射:匿名映射没有对应的文件或者对应的文件是虚拟文件(如:/dev/zero),映射后会把内存分页全部初始化为0。
当多个进程映射了同一个内存区域时,它们会共享物理内存的相同分页。通过fork()创建的子进程也会继承父进程的映射副本!!!
如果多个进程都会同一个内存区域操作时,会根据映射的特性,会有不同的行为。映射特征可分为私有映射和共享映射:
1.私有映射:映射的内容对其他进程不可见。对于文件映射来说,某一个进程在映射内存中改变文件的内容不会反映到被映射的底层文件中。内核会使用copy-on-write(写时复制)技术来解决这个问题:只要有一个进程修改了分页中的内容,内核会为该进程重新创建一个新的分页,并将需要修改的内容复制到新分页中。
2.共享映射:某一个进程对共享的内存区域操作都对其他进程可见!!!对于文件映射,操作的内容会反映到底层文件中。
注意:进程执行exec()调用后,先前的内存映射会丢失,而fork()创建的子进程会继承父进程的,映射的特征(私有和共享)也会被继承。
异常信号:
1.当映射内存的属性设置只读时,如果进行写操作会产生SIGSEGV信号。
2.当映射内存的字节数大于被映射文件的大小,且大于该文件当前的内存分页大小时。如果访问的区域超过了该文件分页大小,会产生SIGBUS信号。
有点绕口,举个简单的例子:假设内核维护的内存分页是4k(一般都是4k,4096字节),一个普通文件a.txt的大小是10字节。如果创建一个映射内存为4097字节,并映射该文件。此时,因为a.txt的大小用一个分页就可以完全映射,10字节远小于一个分页的4096字节,所以内核只会给它一个分页。内存地址是从0开始,0-9区间的内容对应a.txt文件的数据,我们也是可以访问10-4095的区间。但如果访问4096区间时,已经超过一个分页的大小了,此时会产生SIGBUS信号!!!
等会我们用个简单的例子演示下这2个异常。
二.函数接口
1.创建映射
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
addr:映射后要存放的虚拟内存地址。如果是NULL,内核会自动帮你选择。
length:映射内存的字节数。
prot:权限保护:PROT_NONE(无法访问),PROT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可执行)。
flags:映射特征:MAP_PRIVATE(私有),MAP_SHARED(共享),MAP_ANONYMOUS。还有一些其他的可查询man手册。
fd:要映射的文件描述符。
offset:文件的偏移量,如果为0,且length为文件长度,代表映射整个文件。
2.解除映射
#include <sys/mman.h> int munmap(void *addr, size_t length);
addr:要解除内存的起始地址。如果addr不在刚刚映射区域的开始位置,解除一部分后内存区域可能会分成两半!!!
length:要解除的字节数。
3.同步映射区
#include <sys/mman.h> int msync(void *addr, size_t length, int flags);
addr:要同步的内存起始地址。
length:要同步的字节长度。
flags:MS_SYNC(执行同步文件写入),此操作内核会把内容直接写到磁盘。MS_ASYNC(执行异步文件写入),此操作内核会先把内容写到内核的缓冲区,某个合适的时候再写到磁盘。
三.文件映射实例
/** * @file mmap_file.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <signal.h> #include <unistd.h> #include <sys/mman.h> #define MMAP_FILE_NAME "a.txt" #define MMAP_FILE_SIZE 10 void err_exit(const char *err_msg) { printf("error:%s\n", err_msg); exit(); } /* 信号处理器 */ void signal_handler(int signum) { if (signum == SIGSEGV) printf("\nSIGSEGV handler!!!\n"); else if (signum == SIGBUS) printf("\nSIGBUS handler!!!\n"); exit(); } int main(int argc, const char *argv[]) { ) { printf(]); exit(); } char *addr; int file_fd, text_len; long int sys_pagesize; /* 设置信号处理器 */ if (signal(SIGSEGV, signal_handler) == SIG_ERR) err_exit("signal()"); if (signal(SIGBUS, signal_handler) == SIG_ERR) err_exit("signal()"); ) err_exit("open()"); /* 系统分页大小 */ sys_pagesize = sysconf(_SC_PAGESIZE); printf("sys_pagesize:%ld\n", sys_pagesize); /* 内存只读 */ //addr = (char *)mmap(NULL, MMAP_FILE_SIZE, PROT_READ, MAP_SHARED, file_fd, 0); /* 映射大于文件长度,且大于该文件分页大小 */ //addr = (char *)mmap(NULL, sys_pagesize + 1, PROT_READ | PROT_WRITE, MAP_SHARED, file_fd, 0); /* 正常分配 */ addr = (); if (addr == MAP_FAILED) err_exit("mmap()"); /* 原始数据 */ printf("old text:%s\n", addr); /* 越界访问 */ //addr += sys_pagesize + 1; //printf("out of range:%s\n", addr); /* 拷贝新数据 */ text_len = strlen(argv[]); memcpy(addr, argv[], text_len); /* 同步映射区数据 */ //if (msync(addr, text_len, MS_SYNC) == -1) // err_exit("msync()"); /* 打印新数据 */ printf("new text:%s\n", addr); /* 解除映射区域 */ ) err_exit("munmap()"); ; }
1.首先创建一个10字节的文件:
$: count=
2.把程序编译运行后,依次执行2次写入:
可以看到本机的分页大小是4096字节。第一次写入9个字节,原来用dd命令创建的文件为空,old text为空。第二次写入4个字节,只覆盖了最前面的1234。
3.验证可访问现有分页的内存。写入超过10字节的数据:
上面我们写入了17个字节,虽然64行的mmap()映射了MMAP_FILE_SIZE=10字节。但从输入new text可以看出,我们依然可以访问10字节后面的内存,因为该数据都在一个分页(4096)里面。cat查看a.txt后,只有前10个字节写入了a.txt。
4.验证SIGSEGV信号。把64行注释调,58行打开,设置映射属性为只读,编译后访问:
设置只读属性后,第77行有写操作。我们自定义的信号处理器就捕捉到了该信号。如果没有自定义信号处理器,终端就会输出Segmentation fault
5.验证SIGBUS信号。用61行的方法来映射内存。映射了一个分页大小再加1字节的内存,并放开72,73行的代码,让指针指向一个分页后的区域。编译后运行:
SIGBUS信号被自定义处理器捕捉到了。如果没有自定义信号处理器,终端就会输出Bus error
四.匿名映射
匿名映射有2种方式:
1.指定mmap()的flags参数为MAP_ANONYMOUS,在linux上当指定这个值后会忽略fd参数的值。不过在有的UNIX上还需要把fd指定为-1。
2.把/dev/zero当做文件描述符打开,从/dev/zero读取数据时它会给你提供无穷无尽的0,向它写数据,它会丢弃。丢弃这点跟/dev/null一样,只是/dev/null不跟你提供数据。
3.匿名映射的使用跟上面的文件映射差不多。这里不再给例子。
linux编程之内存映射的更多相关文章
- Linux驱动之内存映射
本文参考了http://www.cnblogs.com/geneil/archive/2011/12/08/2281222.html.本文作为学习总结,将主要过程简要描述. 很多驱动实现某些功能都要通 ...
- [转载]Linux驱动mmap内存映射
原文地址:https://www.cnblogs.com/wanghuaijun/p/7624564.html mmap在linux哪里? 什么是mmap? 上图说了,mmap是操作这些设备的一种方法 ...
- Linux驱动mmap内存映射
mmap在linux哪里? 什么是mmap? 上图说了,mmap是操作这些设备的一种方法,所谓操作设备,比如IO端口(点亮一个LED).LCD控制器.磁盘控制器,实际上就是往设备的物理地址读写数据. ...
- Linux高端内存映射(上)【转】
转自:http://blog.csdn.net/vanbreaker/article/details/7579941 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 高端内 ...
- Linux编程之内存池的设计与实现(C++98)
假设服务器的硬件资源"充裕",那么提高服务器性能的一个很直接的方法就是空间换时间,即"浪费"服务器的硬件资源,以换取其运行效率.提升服务器性能的一个重要方法就是 ...
- 【Linux编程】存储映射I/O
存储映射I/O使一个磁盘文件与存储空间中的一个缓冲区相映射,对缓冲区的读.写操作就是对文件的读.写操作,从而能够不再使用read.write系统调用. 将文件映射到存储区的函数由mmap完毕,函数原型 ...
- Linux就这个范儿 第15章 七种武器 linux 同步IO: sync、fsync与fdatasync Linux中的内存大页面huge page/large page David Cutler Linux读写内存数据的三种方式
Linux就这个范儿 第15章 七种武器 linux 同步IO: sync.fsync与fdatasync Linux中的内存大页面huge page/large page David Cut ...
- linux mmap 内存映射【转】
转自:http://blog.csdn.net/xyyangkun/article/details/7830313 [-] mmap vs readwritelseek mmap vs malloc ...
- linux中的 IO端口映射和IO内存映射
参考自:http://blog.csdn.net/zyhorse2010/article/details/6590488 CPU地址空间 (一)地址的概念 1)物理地址:CPU地址总线传来的地址,由硬 ...
随机推荐
- linux下MySQL表名忽略大小写设置
最近公司项目的MySQL数据库要迁移到linux下,部署时日志总是显示报找不到一个表,用MYSQL查看明明有这个表.后来经百度,原来LINUX下的MYSQL默认是区分表名大小写的. 用命令查看当前是否 ...
- GJM : 【C# 高性能服务器】完成端口、心跳的高性能Socket服务器 [转载]
感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...
- vim自动补全插件YouCompleteMe
前言 Valloric/YouCompleteMe可以说是vim安装最复杂的插件之一,但是一旦装好,却又是非常好用的.YouCompleteMe简称ycm 在安装折腾的过程中,我再一次的体会到,除了官 ...
- Redis-分片
分片(partitioning)就是将你的数据拆分到多个 Redis 实例的过程,这样每个实例将只包含所有键的子集.本文第一部分将向你介绍分片的概念,第二部分将向你展示 Redis 分片的可选方案. ...
- vue安装
条件:已安装 node&npm 1.安装 cnpm : $ npm install -g cnpm --registry=https://regis ...
- js 中{},[]中括号,大括号使用详解
一.{ } 大括号,表示定义一个对象,大部分情况下要有成对的属性和值,或是函数. 如:var LangShen = {"Name":"Langshen",&qu ...
- 如何保护在Autodesk应用程序商店的应用不被盗版 - 1
Autodesk应用程序商店如火如荼,但来自中国的应用却还是寥寥无几.大家在担心什么呢?可能其中一个因素就是担心自己的应用上线后被盗版的问题.对应用的版权保护和授权管理是每个应用开发者都应该认真考虑的 ...
- ios7 tableview被navigationbar挡住
用ego下拉刷新的时候,每次在ios7时,tableview都会上移...导致被navagationbar挡住.ios6是正常的,于是在init的时候添加如下代码... NSComparisonRes ...
- iOS xcode使用断点追踪后,无法nslog,无法po对应的值 方法小结
今天使用断点追踪后,发现无法正常nslog,使用po也无法打印出对应的值,进入断点显示的值都为nil,网上查了一下,我总结出了以下几个可行方法: 法一:项目根目录->PROGECT->Bu ...
- iOS 学习 - 15.添加水印
绘制到位图 下面利用位图图形上下文给一个图片添加水印,在下面的程序中我们首先创建上下文,然后在上下文中绘制图片.直线和文本,最后从当前位图上下文中取得最终形成的新图片显示到界面 - (void)vie ...