APUE_存储映射

1.引言   

       存储映射I/OMemory-mapped I/O)使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当在缓冲区中存取数据,就相当于读写文件中的相应字节。这样就可以在不使用readwrite的情况下执行I/O

2.mmap函数

       首先,需要告诉内核将一个给定的文件映射到一个存储区域中。这由mmap函数实现。

#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off );
	//返回值:若成功则返回映射区的地址,若出错则返回MAP_FAILED

 

addr参数指定映射存储区的起始地址。通常将其设置为0,表示由系统选择映射区的起始地址(有利于可移植性)mmap的返回地址是该映射区的起始地址。

filedes指定要被映射文件的描述符。在映射该文件到一个地址空间之前,先要打开该文件。

len是映射的字节数。

off是要映射字节在文件中的起始(SEEK_SET)偏移量。

prot参数说明对映射区域的保护要求,可以是PROT_READ(映射区可读)PROT_WRITE(映射区可写)PROT_EXEC(映射区可执行)PROT_NONE(映射区不可访问)。注意:对指定映射存储区的保护要求不能超过文件open模式访问权限。

flag参数影响映射存储区的多种属性,取值可以是:1MAP_FIXED,表示返回值必须等于addr,这不利于可移植性。如果未设置MAP_FIXED,且addr不为0,那么内核只是将addr作为映射地址的一种建议,并不保证实际映射地址就是addr2MAP_SHARED,指示存储操作修改映射文件。也就是说直接对原文件进行操作,必须指定该标志或下一个标志中的一个;3MAP_PRIVATE,说明对映射区的存储操作导致创建该映射文件的一个私有副本。

此外,需要记住以下几点:

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

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

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

3.其他接口

       调用mprotect可以更改一个现存映射存储区的权限。prot的许可值与mmapprot参数一样,地址参数addr的值必须是系统页长的整数倍。

#include <sys/mman.h>
int mprotect(void *addr, size_t len, int prot);
		//返回值:成功返回0,错误返回-1


       如果在共享存储映射区中的页已经修改,那么可以调用msync将该页冲洗到被映射的文件中,其作用类似于fsync。如果映射是私有的,那么不修改被映射的文件。addr同样必须与页边界对齐。flags参数一定要指定MS_ASYNCMS_SYNC中的一个。如果希望在函数返回之前等待写操作完成,则可指定MS_SYNC标志。MS_INVALIDATE是一个可选标志,使用它可以通知操作系统丢弃与底层存储器没有同步的任何页。

#include <sys/mman.h>
int msync(void *addr, size_t len, int flags);
		//返回值:成功返回0,错误返回-1


 

       进程终止时,或调用了munmap后,存储映射区就被自动解除映射。关闭文件描述符filedes并不解除映射区。调用munmap不会使映射区的内容写到磁盘文件上。对于MAP_SHARED区磁盘文件的更新,在写到存储映射区时按内核虚存算法自动进行。在解除了映射后,对于MAP_PRIVATE存储区的修改被丢弃。

#include <sys/mman.h>
int munmap(caddr_t addr, size_t len);
		//返回值:成功返回0,错误返回-1

 

4.范例

//存储映射复制一个文件(类似于cp(1)命令)
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>//为了取得文件信息,主要需要文件长度,才能知道映射多长
#include <stdio.h>
#include <stdlib.h>
int main(int argc, int **argv)
{
	int fdin,fdout;
	void *src,*dst;
	struct stat statbuf;
	fdin=open(argv[1],O_RDONLY);
	fdout=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0x777);
	fstat(fdin,&statbuf);
	lseek(fdout,statbuf.st_size-1,SEEK_SET);//mmap并不加长文件,需要自行加长文件
	write(fdout,"",1);//在这里写一下文件就加长了。好比创建了一个有空洞的文件
	src=mmap(0,statbuf.st_size,PROT_READ,MAP_SHARED,fdin,0);
	dst=mmap(0,statbuf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fdout,0);
	printf("source address: %lxndestination address: %lxn",(long unsigned int)src,(long unsigned int)dst);
	memcpy(dst,src,statbuf.st_size);
	munmap(src,statbuf.st_size); //解除映射
	munmap(dst,statbuf.st_size);
	return 0;
}


 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值