您的位置:首页 > 运维架构 > Linux

Memory Map(Linux 存储映射IO)

2016-07-19 14:40 585 查看
本文主要总结自UNIX环境高级编程以及RedHat6.5系统man函数

存储映射IO

函数说明 mmap

#include <sys/mman.h>
void *mmap(void *addr,//指定映射区的起始地址。通常设置为0,由系统选择该映射区起始地址
size_t len, //映射的字节数
int prot, //对映射存储区的保护要求
int flag, //
int filedes, //被映射文件的描述符
off_t off) //要映射字节在文件中的起始偏移量

//返回值:若成功则返回映射区的起始地址,若出错则返回MAP_FAILED


prot参数详细说明

prot说明
PROT_READ映射区可读
PROT_WRITE映射区可写
PROT_EXEC映射区可执行
PROT_NONE映射区不可访问
prot参数可以是上述4中权限的任意组合的按位或,并且对指定映射存储区的的保护要求不能超过文件open模式访问权限(如果文件是只读打开的,那么对映射存储区就不能指定PROT_WRITE)。

* flag参数详细说明

MAP_FIXED 返回值必须等于addr,。因为这不利于可移植性,所以不鼓励使用此标志。

MAP_SHARED 这一标志说明了本进程对映射区所进行的存储操作的配置。此标志指定存储操作修改映射文件,也就是说,存储操作相当于对该文件write。必须指定本标志或下一个标志(MAP_PRIVATE),但不能同时指定两者

*每种实现都可能还有另外一些MAP_XXX标志值,它们是这种实现所特有的。详细情况请参见使用系统的mmap(2)手册页。

MAP_32BIT (since Linux 2.4.20, 2.6)

把映射区放到运行地址空间的前2G空间内。这个flag职位64位系统的64位程序中使用。这个标志被用来允许程序栈在前2G内存中分配空间,同时在早期64位处理器上 提高上下文切换(context-switch)执行。现在的x84-64位处理器不再有这个执行问题,所以在目前系统中不再需要这个flag了。所以在使用MAP_FIXED标志是,此标志自动被忽略了。

MAP_PRIVATE

本标志说明,对映射区的存储操作导致创建该映射文件的一个私有副本。所有后来对该映射区的引用都是引用该副本,而不是原始文件。(此标志的一种用途是用于调试程序,它将依程序文件的正文部分映射至一存储区,但允许用户修改其中的指令。任何修改只影响程序文件的副本,但不影响原文件)

MAP_ANON

MAP_ANONYMOUS的同义词,不赞成使用。

MAP_ANONYMOUS

内存存储区并不依赖于任何文件;它的内容被初始化在地址0(?)。fileds和off变量是被忽略的,无论如何,如果要使用MAP_ANONYMOUS,一些具体实现要求fileds要置为-1,并且portable applications应该确认这些。共同使用MAP_ANONYMOUS和MAP_SHARED只在linux内核2.4以上支持。

off和addr补充说明

off和addr的值(如果指定了MAP_FIXED)通常应当是系统虚存页长度的倍数。虚存页长可用带参数_SCPAGESIZE或_SCPAGE_SIZE的sysconf函数得到。因为off和addr常常指定为0,所以这种要求一般并不重要。

映射区相关

因为映射文件的起始偏移量受系统虚存页长度的限制,那么如果映射区的长度不是页长的整数倍时,将如何呢?假定文件长12字节,系统页长512字节,则系统通常提供512字节的映射区,其中后500字节被设置为0.可以修改这500字节,但任何变动都不会再文件中稳赢出来,于是,我们不能用mmap将数据添加到文件中。为了做到这一点,我们必须首先加长该文件,将在之后的代码中展示。

与映射存储区相关的有SIGSEGV和SIGBUS两个信号,信号SIGSEGV通常用于指示进程试图访问对它不可用的存储区。如果进程企图存数据到mmao指定为只读的映射存储区,那么也产生此信号。如果访问映射区的某个部分,而在访问时这一部分实际上已不存在,则产生SIGBUS信号。例如,用文件长度映射一个文件,蛋仔引用该映射区之前,另一个进程已将该文件截短。此时,如果进程启动访问对应于该文件已截去部分的映射区,则会接收到SIGBUS信号。

在调用fork之后,紫禁城继承存储映射区(因为子进程复制父进程的地址空间,而存储映射区是该地址空间中的一部分),但是由于同样的理由,调用exec后的新程序则不继承此存储映射区。

函数说明 mproject

#include <sys/mman.h>

int mprotect(void *addr, //必须是系统页长度的整数倍
size_t len,
int prot); //和mmap中的prot参数一样

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


函数说明 msync

#include <sys/mman.h>

int msync(void *addr, //必须与页边界对齐
size_t len,
int flags);
//返回值:若成功则返回0,若出错则返回-1


如果映射是私有的,那么不修改被映射的文件。

flags参数是我们对如何冲洗存储区有某种程度的控制。我们可以指定MS_ASYNC标志以简化被写页的调度。如果我们希望在返回之前等待写操作完成,则可指定MS_SYNV标志。一定要指定MS_ASYNC和MS_SYNC中的一个。MS_INVALIDATA是一个可选标志,使用它们以通知操作系统丢弃与底层存储器没有同步的任何页。若使用了此标志,某些实现将丢弃在指定范围中的所有页,但这并不是所期望的。

函数说明 munmap

#include <sys/mman.h>

int munmap(caddr_t addr, size_t len);
//返回值:若成功则返回0,若出错则返回-1


进程终止时,或调用了munmap之后,存储映射去就被自动解除映射。关闭文件描述符filedes并不解除映射区

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

实例

用存储映射IO复制一个文件

#include "apue.h"
#include <fcntl.h>
#include <sys/mman.h>

int main(int argc, char *argv[])
{
int  fdin, fout;
void *src, *dst;
struct stat statbuf;

if(argc != 3)
err_quit("usage: %s <fromfile> <tofile>", argv[0]);
if((fdin = open(argv[1], O_RDONLY)) < 0)
err_sys("can't open %s for reading", argv[1]);
if((fout = open(argv[2], ORDWR | O_CREAT | O_TRUNC)) < 0)
err_sys("can't create %s for reading", argv[2]);

if(fstat(fdin, &statbuf) < 0) //need size of input file
err_sys("fstat error");

//set size of output file
if(lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1)
err_sys("lseek error");
if(write(fout, "", 1) != 1)
err_sys("write error");

if((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdin, 0)) == MAP_FAILED)
err_sys("mmap error for input");

if((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fout, 0)) == MAP_FAILED)
err_sys("mmap error for output");

memcpy(dst, src, statbuf.st_size);// does the file copy

//msync(dst, statbuf.st_size, MS_SYNC);//ensure write to file
exit(0);
}


程序说明

1. 程序首先打开两个文件,然后调用fstat得到输入文件的长度,在为输入文件调用mmap的设置输出文件长度时都需要使用输入文件长度。调用lseek,然后写一个自己以设置输出文件的长度。如果不设置输出文件的长度,则对输出文件调用mmap也可以,但是对相关存储区的第一次引用会缠上SIGBUS。也可以使用ftruncate函数来设置输出文件的长度,但是并非所有操作系统都支持该函数扩充文件长度。

2. 然后对每个文件调用mmap将文件映射到存储区上,最后调用memcpy将输入缓冲区的内容复制到输出缓冲区。在从输入缓冲区(src)取数据字节时,内核自动读输入文件;在将数据存入输出缓冲区(dst)时,内核自动将数据写到输出文件中。

3. 特别的:数据被写被写入文件的确切时间依赖于系统的业管理算法。某些系统设置了守护进场,在系统运行期间,它”慢条斯理“地将脏页写到磁盘上。如果想要确保数据安全的写到文件中,则需要在进程终止前以MS_SYNC标志调用msync
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 存储