存储映射I/O
一个进程拥有独立并且连续虚拟地址空间,在32位体系结构中进程的地址空间是4G。不过,内核在管理进程的地址空间时是以内存区域为单位。内存区域是进程整个地址空间中一个独立的内存范围,它在内核中使用vm_area_struct数据结构来描述。每个内存区域都有自己访问权限以及操作函数,因此进程只能对有效范围的内存地址进行访问。
存储映射I/O是一种基于内存区域的高级I/O操作,它将磁盘文件与进程地址空间中的一个内存区域相映射。当从这段内存中读数据时,就相当于读磁盘文件中的数据,将数据写入这段内存时,则相当于将数据直接写入磁盘文件。这样就可以在不使用基本I/O操作函数read和write的情况下执行I/O操作。
1.基本实现方法
实现存储映射I/O的核心操作是通过mmap系统调用将一个给定的磁盘文件映射到一个存储区域中。
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
关于该函数定义中各个参数说明Linux上的man手册已经解释的很清楚,在此不再赘述。这里需要特别说明的是prot和flags参数。prot用来指定对映射区域的保护要求,但是它的保护范围不能超过文件open时指定的打开权限。比如以只读(PROT_READ)方式打开一个文件,那么以读写(PROT_READ|PROT_WRITE)方式保护内存区域是不合法的。flags用来指定内存区域的多种属性,两个典型的取值是MAP_SHARED和MAP_PRIVATE。MAP_SHARED标志指定了进程对内存区域的修改会影响到映射文件。而当对flags指定MAP_PRIVATE时,进程会为该映射内存区域创建一个私有副本,对该内存区的所有操作都是在这个副本上进行的,此时对内存区域的修改并不会影响到映射文件。
下面列出一个简单的示例程序,它将磁盘文件映射到一个内存区域中,通过mmap返回的指针先读文件,再写文件。可以看到对文件的读和写操作都是通过内存映射I/O的方式完成的。
int main()
{
int fd;
char *buf = NULL;
int i;
//打开一个文件
if (-1 == (fd = open("./mapping_file.txt", O_RDWR))) {
printf("open file error!\n");
exit(1);
}
//将文件映射到进程的一个内存区域
buf = mmap(NULL, 100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (!buf) {
printf("mmap error!\n");
exit(1);
}
//对映射内存读数据
for (i = 0; i < 100; i++)
printf("%c", buf[i]);
//对映射内存写数据
if (buf[0] == 'H')
buf[0] = 'h';
else
buf[0] = 'H';
system("cat ./mapping_file.txt");
return 0;
}
2.使用内存映射I/O进行文件拷贝
使用基本I/O操作函数如何实现一个类似cp命令的程序?比如我们要将A文件复制到B文件,那么程序的基本框架是这样的:
1.open()文件A和文件B
2.将A文件的内容read()到buffer
3.将buffer中的数据write()到文件B
4.close()文件A和文件B
如果使用内存映射I/O来实现cp命令,那么它的基本框架是这样的:
1.open()文件A和文件B
2.mmap()文件A和文件B,其中src和dest分别为两个文件映射到内存的地址
3.将以src为起始的len长字节数据memcpy()到dest
4.close()文件A和文件B
示例程序如下:
int main()
{
int srcfd, destfd;
struct stat statbuf;
char *src = NULL, *dest = NULL;
//打开两个文件
if (-1 == (srcfd = open("./src.txt", O_RDONLY))) {
printf("open src file error!\n");
exit(1);
}
if (-1 == (destfd = open("./dest.txt", O_RDWR | O_CREAT | O_TRUNC))) {
printf("open dest file error!\n");
exit(1);
}
//获取原始文件的长度
if (-1 == fstat(srcfd, &statbuf)) {
printf("fstat src file error!\n");
exit(1);
}
//设置输出文件的大小
if (-1 == lseek(destfd, statbuf.st_size - 1, SEEK_SET)) {
printf("lseek error!\n");
exit(1);
}
if (-1 == write(destfd, "", 1)) {
printf("write error!\n");
exit(1);
}
if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, srcfd, 0)) == MAP_FAILED) {
printf("mmaping src file error!\n");
exit(1);
}
if ((dest = mmap(0, statbuf.st_size + 2, PROT_READ | PROT_WRITE, MAP_SHARED, destfd, 0)) == MAP_FAILED) {
printf("mmaping dest file error!\n");
exit(1);
}
memcpy(dest, src, statbuf.st_size);
printf("src file:\n");
system("cat ./src.txt");
printf("dest file:\n");
system("cat ./dest.txt");
close(srcfd);
close(destfd);
return 0;
}
按照上述列出的基本框架,该程序首先打开两个文件,通过fstat()获得源文件的长度。因为在mmap两个文件以及设置目的文件长度时都需要源文件的长度。设置目的文件通过lseek()即可完成,如果没有设置目的文件的长度,那么将会产生总线错误(引发信号SIGBUS)。然后分别mmap()两个文件到进程的地址空间,最后调用memcpy()将源文件内存区的数据拷贝到目的文件内存区。
通过基本I/O和内存映射I/O均可以进行文件拷贝,那么两者的效率谁更高一些?这其实是个很难回答的问题。不管是使用基本I/O操作函数还是mmap方式,操作系统都会在内存中进行缓存(cache),而且在不同的应用场景、不同的平台下结果都会收到影响。但是抛开这些因素单从对文件操作这个方面来说,内存映射方式比read和write方式要快。
如果使用read/write方式进行文件拷贝,首先将数据从用内核缓冲区复制到用户空间缓冲区,这是read的过程;再将数据从用户空间缓冲区复制到内核缓冲区,这是write过程。如果是内存映射方式,则直接是用户空间中数据的拷贝,也就是将源文件所映射内存中的数据拷贝到目的文件所映射的内存中。这样就避免了用户空间和内核空间之间数据的来回拷贝。
但是内存映射方式并不是完美的,它所映射的文件只能是固定大小,因为文件所映射的内存区域大小在mmap时通过len已经指定。另外,文件映射的内存区域的大小必须以页大小为单位。比如系统页大小为4096字节,假定映射文件的大小为20字节,那么该页剩余的4076字节全部被填充为0。虽然通过映射地址可以访问并修改剩余字节,但是任何变动都不会在映射文件中反应出来。由此可见,使用内存映射进行大数据量的拷贝比较有效。
存储映射I/O的更多相关文章
- 高级I/O之存储映射I/O
存储映射I/O(Memory-mapped I/O)使一个磁盘文件与存储空间中的一个缓冲区相映射.于是当从缓冲区中取数据,就相当于读文件中的相应字节.与此类似,将数据存入缓冲区,则相应字节就自动地写入 ...
- 【Linux编程】存储映射I/O
存储映射I/O使一个磁盘文件与存储空间中的一个缓冲区相映射,对缓冲区的读.写操作就是对文件的读.写操作,从而能够不再使用read.write系统调用. 将文件映射到存储区的函数由mmap完毕,函数原型 ...
- UNIX环境高级编程——存储映射I/O(mmap函数)
共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式,因为进程可以直接读写内存,而不需要任何数据的拷贝.对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共 ...
- 八、文件IO——存储映射
8.1 存储映射介绍 8.1.1 概念 存储映射是一个磁盘文件与存储空间的一个缓存相映射,对缓存数据的读写就相应的完成了文件的读写. 文件操作部分映射到虚拟内存的一块区域,我们对虚拟内存映射的那块区域 ...
- 第3章 文件I/O(7)_高级文件操作:存储映射
8. 高级文件操作:存储映射 (1)概念: 存储映射是一个磁盘文件与存储空间的一个缓存相映射,对缓存数据的读写就相应的完成了文件的读写. (2)mmap和munmap函数 头文件 #include&l ...
- 存储映射--mmap
存储映射 使一个磁盘文件与存储空间中的一个缓冲区相映射. 当从缓冲区中取数据,就相当于读文件中的相应字节. 将数据存入缓冲区,则相应的字节就自动写入文件. 使用这种方法,首先应通知内核,将一个指定文件 ...
- 共享内存与存储映射(mmap)
[前言]对这两个理解还是不够深刻,写一篇博客来记录一下. 首先关于共享内存的链接:共享内存.里面包含了创建共享内存区域的函数,以及两个进程怎么挂载共享内存通信,分离.释放共享内存. 共享内存的好处就是 ...
- 存储映射I/O函数
1.void * mmap((void *addr, size_t length, int prot, int flags, int fd, off_t offset) 参数: addr:用于指定映 ...
- 存储映射IO
mmap 将文件映射到内存, 对这块内存的修改会自动同步到相应的文件中 void *mmap(void *addr, size_t len, int prot, int flag, int fd, o ...
随机推荐
- IDA远程调试so库JNI_Onload函数
JNI_OnLoad函数大概功能就是在程序加载so的时候,会执行JNI_OnLoad函数,做一系列的准备工作.很多时候,程序猿们会将一些重要信息放在此函数中,而不是通过某种事件来重复触发.包括说将反调 ...
- 我所认识的PCA算法的princomp函数与经历 (基于matlab)
我接触princomp函数,主要是因为实验室的项目需要,所以我一接触的时候就希望快点学会怎么用. 项目中需要利用PCA算法对大量数据进行降维. 简介:主成分分析 ( Principal Compone ...
- Thrift 文件的格式及可用的数据类型
编写thrift文件是,须要知道thrift文件支持的数据类型有哪些.假设定义Service等.以下是官方文档的说明: # # Thrift Tutorial # Mark Slee (mcslee@ ...
- MFC画图总结-DIB图形绘制
參考文档: http://blog.csdn.net/hnust_xiehonghao/article/details/37652927 http://blog.sina.com.cn/s/blog_ ...
- css中url的路径含义及使用
http://www.jb51.net/css/37554.html 在CSS中有用url语法来指定background-image或是其他引用文件中,如: 复制代码 代码如下: .mainheade ...
- json的工具以及浏览器排序问题
浏览器中,所有涉及json的工具会按照键进行排序,这个与实际的查询的数组的顺序有出入,见下图:
- wepy - 使用vsCode编辑器安装插件
完成后,高亮以及智能补全代码. 1. 首先ctrl+shift+p打开扩展,安装以下三个插件 2. 复制以下文件内容到你的vsCode配置 files.associations { "fil ...
- JSP实现数据保存(web基础学习笔记四)
session对象: //服务端设置Session属性 session.setAttribute("user", username); //客户端接收Session Object ...
- 算法笔记_209:第六届蓝桥杯软件类决赛部分真题(Java语言B组)
目录 1 分机号 2 五星填数 3 表格计算 前言:以下代码仅供参考,若有错误欢迎指正哦~ 1 分机号 标题:分机号 X老板脾气古怪,他们公司的电话分机号都是3位数,老板规定,所有号码必须是降序排列, ...
- 传统数据库没落,OLTP新型数据库发展火热
參考资料: (1) <OLTP Through the Looking Glass, and What We Found There> (2) <The End of an Arch ...