《UNIX环境高级编程》笔记--存储映射IO

来源:互联网 发布:使命召唤ol审判数据 编辑:程序博客网 时间:2024/06/05 20:54

存储映射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文件。

0 0