首页 > 代码库 > 第3章 文件I/O(7)_高级文件操作:存储映射

第3章 文件I/O(7)_高级文件操作:存储映射

8. 高级文件操作:存储映射

(1)概念:

  存储映射是一个磁盘文件存储空间的一个缓存映射,对缓存数据的读写就相应的完成了文件的读写。

技术分享 

(2)mmap和munmap函数

头文件

#include<sys/types.h>

#include<sys/mman.h>

函数

void* mmap(void* addr, size_t length, int prot, int flags,

                      int fd, off_t offset);

int munmap(void* addr, size_t length);

返回值

mmap成功时返回映射区的起始地址,munmap成功为0,两者出错均返回-1。

功能

mmap:I/O使一个磁盘文件与存储空间中的一个缓存相映射。

Munmap:解除映射。

参数

(1)addr:存储映射区的起始地址,通常设为0,让系统自动分配

(2)length:需要映射的字节数。

(3)prot:保护策略

  ①PROT_READ:映射区可读;②PROT_WRITE:映射区可写;

  ③PROT_EXEC:映射区可执行;④PROT_NONE:映射区不可访问。

(4)flags:

  ①MAP_FIXED:返回地址必须等于addr,不鼓励使用

  ②MAP_SHARED:存储操作立刻修改映射文件内容。

  ③MAP_PRIVATE:存储操作导致创建映射文件的副本,并对副本读写。

(5)offset:映射字节在文件中的偏移量

备注

(1)mmap函数从缓存中获取数据,就相当于读文件中相应的字节。与其类似,将数据存入缓存,则相应字节就自动地写入文件。这样,就可以在不使用read和write的情况下执行I/O

(2)子进程继承父进程的存储映射区

【编程实验】存储映射实现文件写入

//file_map.c

#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>

int main(int argc, char* argv[])
{
    if(argc < 2){
        printf("usage: %s file\n",argv[1]);
        exit(1);
    }

    int fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC);
    if(fd < 0){
        perror("open error");
        exit(1);
    }

    //定位到文件尾部第26个字节的位置,并创建一个空洞文件
    lseek(fd, 25, SEEK_END);//定位第26个字母Z,所在的位置
    write(fd, "0", 1);   //先写入一个"0",以后会被字母Z覆盖

    //对文件的“空洞”区进行存储映射
    char* addr = mmap(0,     //映射区起始地址由系统自动分配
                      26,    //映射的字节数
                      PROT_WRITE, 
                      MAP_SHARED,
                      fd,
                      0);    //从指定的文件偏移量处开始映射

    //修改存储映射区的内容(会同步到文件中去)
    int i = 0;
    for(i=0; i<26; i++)
    {
        *(addr + i) = A + i; //往空洞区写入26个大写字母
    }
    
    printf("write success\n");

    //解除映射
    munmap(addr, 0);
    close(fd);

    return 0;
}

【编程实验】存储映射实现文件的拷贝

//cp_map.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>

int main(int argc, char* argv[])
{
    if(argc < 3){
        printf("usage: %s srcfile, destfile\n", argv[0]);
        exit(1);
    }

    int srcfd = open(argv[1], O_RDONLY);
    if (srcfd < 0)
    {
        perror("open error");
        exit(1);
    }

    int dstfd = open(argv[2],
                     O_RDWR |  O_CREAT | O_TRUNC, 0777);
    if(dstfd < 0 )
    {
        perror("open error");
        exit(1);
    }

    off_t len = lseek(srcfd, 0, SEEK_END); //源文件的长度
    printf("len: %ld\n", len);
    
    //源文件映射到内存
    char* addr1 = mmap(0, len, PROT_READ, MAP_SHARED, srcfd, 0);
    if(addr1 < 0)
    {
        perror("mmap error");
        exit(1);
    }
    
    //在目标文件中,首先创建一个空洞文件
    lseek(dstfd, len-1, SEEK_SET);
    write(dstfd, "0", 1); //这个0会被后面复制而来的文件内容覆盖

    //目标文件映射到内存
    char* addr2 = mmap(0, len, PROT_WRITE, MAP_SHARED, dstfd, 0);
    if(addr2 < 0)
    {
        perror("mmap error");
        exit(1);
    }
     
    //存储映射区的复制,并同步到目标文件中
    memcpy(addr2, addr1, len); //拷addr1内容拷到addr2中
    
    printf("copy success!\n");

    //撤销映射
    munmap(addr1, 0);
    munmap(addr2, 0);
    
    close(srcfd);
    close(dstfd);

    return 0;
}

第3章 文件I/O(7)_高级文件操作:存储映射