存储映射IO使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取数据,就相当于读文件中的相应的字节;
将数据存入缓冲区,则相应字节就自动写入文件,这样就可以在不是用read和write的情况下执行IO。
这可以通过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,这表示由系统选择该映射区的起始地址。函数的返回地址是该
映射区的起始地址。
filedes指定要被映射文件的描述符。
len是映射的字节数。
off是要映射字节在文件中的起始偏移量。
prot参数说明对映射存储区的保护要求,见下表。
可以通过按位或指定任意组合,但是要求不能超过文件open模式访问权限。
flag参数影响存储区的多种属性。
MAP_FIXED :返回值必须等于addr。因为这不利于可移植性,所以不鼓励使用,如果未指定标志,而且addr非0,则内核
只把addr视为在何处设置映射区的一种建议,但是不保证会使用所要求的地址。
MAP_SHARED:这一标志说明了本进程对映射区所进行的存储操作的配置。此标志指定存储操作修改映射文件,这就是
说,存储操作相当于对该文件的write。必须指定本标志或下一个标志(MAP_PRIVATE),但不能同时指定。
MAP_PRIVATE:本标志说明,对映射区的存储操作导致创建该映射文件的一个私有副本。所有后来对该映射区的引用都
是引用该副本,而不是原始文件。
存储映射文件在进程内存空间中的安排如下:
off和addr的值通常应当是系统虚拟内存页长度的倍数。因为映射文件的起始偏移量受系统虚拟内存页长度的限制,那么如
果映射区的长度不是页长的整数倍时,将如何呢?假定文件长12字节,系统页长512字节,则系统通常提供512字节的映射
区,其中后500个字节被设置为0.
调用mprotect可以更改一个现存映射存储区的权限。
#include<sys/mman.h> int mprotect(void *addr, size_t len, int prot); //若成功返回0,出错则返回-1.prot的许可值与mmap中prot参数一样。
如果在共享存储映射区中的页已被修改,那么我们可以调用msync将该页冲洗到被映射的文件中,msync函数类似于
fsync,但作用于存储映射区。
#include<sys/mman.h> int msync(void *addr, size_t len, int flag); //若成功则返回0,出错则返回-1.flag参数是我们对如何冲洗存储区有某种程度的控制。
MS_ASYNC:简化被写页的调度。
MS_SYNC:在返回之前等待写操作完成。(一定要指定MS_ASYNC和MS_SYNC中的一个)
MS_INVALIDATE是一个可选标志,使用它们以通知操作系统丢弃与地层存储器没有同步的任何页。
进程终止时,或调用munmap之后,存储营社区就被自动解除映射。关闭文件描述符filedes并不解除映射区。
#include<sys/mman.h> int munmap(caddr_t addr, size_t len); //若成功则返回0,出错则返回-1.munmap不会影响被映射的对象,也就是说,调用munmap不会使营社区的内容写到磁盘文件上。对于MAP_SHARED区
磁盘文件的更新,在写到存储映射区时按内核虚拟内存算法自动进行,在解除了映射后,对于MAP_PRIVATE存储区的修改
被丢弃。
实践:
#include<stdio.h> #include<sys/mman.h> #include<fcntl.h> #include<string.h> int main(void){ int fdout; void *dst; char* src = "1234567890"; if((fdout = open("mtxt",O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR)) < 0){ perror("open"); return -1; } if(lseek(fdout,10,SEEK_SET) < -1){ perror("lseek"); return -1; } if(write(fdout,"",1) < 0){ perror("write"); return -1; } if((dst = mmap(0,10,PROT_READ|PROT_WRITE,MAP_SHARED, fdout, 0)) < 0){ perror("mmap"); return -1; } memcpy(dst,src,10); return 0; }运行结果:
root@gmdz-virtual-machine:~# ./a.out
root@gmdz-virtual-machine:~# cat mtxt
1234567890
操作dst地址就是操作mtxt文件。