您的位置:首页 > 编程语言

《unix高级环境编程》高级 I/O—— 存储映射 I/O

2014-11-25 16:39 232 查看
存储映射 I/O 使一个磁盘文件与存储空间中的一个缓冲区相映射。于是从缓冲区中取数据,就相当于读文件的相应字节,同理,将数据写入缓冲区,则相应字节就会自动写入文件。这样可以不使用 read 和 write 函数的情况下执行 I/O。

将一个给定的文件映射到缓冲区可以使用 mmap 函数;

[cpp] view
plaincopy





/* 存储映射IO */

/*

* 函数功能:将一个给定文件映射到存储区域中;

* 返回值:若成功则返回缓冲区的起始地址,若出错则返回MAP_FAILED;

* 函数原型:

*/

#include <sys/mman.h>

void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off);

/*

* 说明:

* addr参数用于指定映射存储区的起始地址,通常将其设置为0,这样由系统自动分配起始地址;

* filedes指定要被映射文件的描述符,在映射之前,先要打开该文件;

* len是映射的字节数;

* off是要映射字节在文件中的起始偏移量;

* prot是对映射存储区的保护要求,具体参数是以下的按位"或"组合:

* PROT_READ 映射区可读

* PROT_WRITE 映射区可写

* PROT_EXEC 映射区可执行

* PROT_NONE 映射区不可访问

*

* flag参数影响映射存储区的多重属性:

* MAP_FIXED 返回值必须等于addr;

* MAP_SHARED 说明本进程对映射区所进行的存储操作的配置;指定存储操作修改映射文件;

* MAP_PRIVATE 对映射区的存储操作导致创建该映射文件的一个私有副本,所有后来对映射区的引用都是该副本;

*/

存储映射文件的存储空间如下图所示:



调用 mprotect 可以更改一个现有映射存储区的权限:

[cpp] view
plaincopy





/*

* 函数功能:更改一个现有映射存储区的权限;

* 返回值:若成功则返回0,若出错则返回-1;

* 函数原型:

*/

#include <sys/mman.h>

int mprotect(void *addr, size_t len, int prot);

/*

* 说明:

* 参数prot和mmap函数的参数一样;

* 起始地址addr必须是系统页长的整数倍;

*/

如果在共享存储映射区中的页已被修改,那么我们可以调用 msync 将该页冲洗到被映射的文件中:

[cpp] view
plaincopy





/*

* 函数功能:将页冲洗到被映射的文件中;

* 返回值:若成功则返回0,若出错则返回-1;

* 函数原型:

*/

#include <sys/mman.h>

int msync(void *addr, size_t len, int flags);

/*

* 说明:

* 若映射是私有的,则不修改被映射的文件,地址必须与页边界对齐;

* 参数flags取值如下:

* MS_ASYNC 执行异步写

* MS_SYNC 执行同步写

* MS_INVALIDATE 使高速缓存的数据失效

*/

当我们使用 mmap 函数成功映射存储区之后,我们可以关闭文件描述符 filedes,此操作并不会解除映射区,想要解除映射区必须调用 munmap 函数:

[cpp] view
plaincopy





/*

* 函数功能:解除映射存储区;

* 返回值:若成功则返回0,若出错则返回-1;

* 函数原型:

*/

#include <sys/mman.h>

int munmap(void *addr, size_t len);

munmap 不会影响被映射的对象,也就是说,调用 munmap 不会使映射区的内容写到磁盘文件上。对于 MAP_SHARED 区磁盘文件的更新,在写到存储映射区时按内核虚拟内存算法自动进行,在解除了映射后,对于 MAP_PRIVATE 存储区的修改被丢弃。

测试程序:

[cpp] view
plaincopy





#include "apue.h"

#include <sys/mman.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

int main(int argc, char **argv)

{

int fd;

void *dst;

struct stat f_stat;

char buf[MAXLINE];

memset(buf, 0, MAXLINE);

char *src = "mmap munmap msync 12";

if(argc != 2)

err_quit("usage: a.out <pathname>");

if((fd = open(argv[1], O_RDWR)) < 0)

err_sys("open argv[1] file error");

if(fstat(fd, &f_stat) == -1 )

err_sys("fstat error");

if((dst = mmap(NULL, f_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)

err_sys("mmap error");

close(fd);

memcpy(dst, src, 20);

if((msync(dst, f_stat.st_size, MS_SYNC)) == -1)

err_sys("msync error");

if((munmap(dst, f_stat.st_size)) == -1)

err_sys("munmap error");

exit(0);

}

原始文件mmap.txt 里面的内容为:

[cpp] view
plaincopy





cat mmap.txt

cccccccccccccccccc

ffffffffffffffffff

eeeeeeeeeeeeeeeeee

yyyyyyyyyyyyyyyyyy

qqqqqqqqqqqqqqqqqq

aaaaaaaaaaaaaaaaaa

pppppppppppppppppp

vvvvvvvvvvvvvvvvvv

zzzzzzzzzzzzzzzzzz

xxxxxxxxxxxxxxxxxx

映射存储之后的内容为:

[cpp] view
plaincopy





mmap munmap msync 12

fffffffffffffffff

eeeeeeeeeeeeeeeeee

yyyyyyyyyyyyyyyyyy

qqqqqqqqqqqqqqqqqq

aaaaaaaaaaaaaaaaaa

pppppppppppppppppp

vvvvvvvvvvvvvvvvvv

zzzzzzzzzzzzzzzzzz

xxxxxxxxxxxxxxxxxx

输出结果:注意:以下是mmap以共享模式的即MAP_SHARED,若是以私有模式的并不会修改源文件

[cpp] view
plaincopy





$cat mmap.txt

cccccccccccccccccc

ffffffffffffffffff

eeeeeeeeeeeeeeeeee

yyyyyyyyyyyyyyyyyy

qqqqqqqqqqqqqqqqqq

aaaaaaaaaaaaaaaaaa

pppppppppppppppppp

vvvvvvvvvvvvvvvvvv

zzzzzzzzzzzzzzzzzz

xxxxxxxxxxxxxxxxxx

$ ./mmap mmap.txt

$ cat mmap.txt

mmap munmap msync 12

fffffffffffffffff

eeeeeeeeeeeeeeeeee

yyyyyyyyyyyyyyyyyy

qqqqqqqqqqqqqqqqqq

aaaaaaaaaaaaaaaaaa

pppppppppppppppppp

vvvvvvvvvvvvvvvvvv

zzzzzzzzzzzzzzzzzz

xxxxxxxxxxxxxxxxxx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: