• 八、文件IO——存储映射


    8.1 存储映射介绍

    8.1.1 概念

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

      

      文件操作部分映射到虚拟内存的一块区域,我们对虚拟内存映射的那块区域进行读写操作,读写之后,那块区域自动同步到文件当中。

      4G空间分布:

      

      共享内存映射区就是文件映射到的内存区。

    8.1.2 mmap---建立内存映射

    1 #include <unistd.h>
    2 #include <sys/mman.h>
    3 //mmap(建立内存映射)
    4 void *mmap(void *start, size_t length, int prot, int flags, int fd,off_t offsize);
    • 函数说明
      • mmap()用来将某个文件内容映射到内存中,对该内存区域的存取即是直接对该文件内容的读写。
      • 作用就是 使一个磁盘文件与存储空间中的一个缓存相映射
      • mmap 函数从缓存中获取数据,就相当于读文件中的相应字节。与其类似,将数据存入缓存,则相应字节就自动地写入文件。这样,就可以再不使用 read 和 write 的情况下执行 I/O
      • 子进程继承父进程的映射
    • 参数说明:
      • @start:指向欲对应的内存起始地址,通常设为NULL或0,代表让系统自动选定地址,对应成功后该地址会返回。
      • @length:代表将文件中多大的部分对应到内存。需要映射的字节数
      • @ prot:代表映射区域的保护方式,有下列组合:
        • PROT_EXEC   映射区域可被执行
        • PROT_READ   映射区域可被读取
        • PROT_WRITE      映射区域可被写入
        • PROT_NONE       映射区域不能访问
      • @flags:会影响映射区域的各种特性
        • MAP_FIXED:如果参数 start 所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此标志。
        • MAP_SHARED:对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。存储操作立刻修改映射文件内容
        • MAP_PRIVATE:对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。存储操作导致创建映射文件的副本,并对副本读写
        • MAP_ANONYMOUS:建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
        • MAP_DENYWRITE:只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。
        • MAP_LOCKED:将映射区域锁定住,这表示该区域不会被置换(swap)。
        • 在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。
      • @fd:为 open() 返回的文件描述词,代表欲映射到内存的文件。
      • @offset:为文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset 必须是分页大小的整数倍。
    • 返回值
      • 若映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1),错误原因存于errno 中。
    • 错误代码
      • EBADF 参数fd 不是有效的文件描述词
      • EACCES 存取权限有误。如果是MAP_PRIVATE 情况下文件必须可读,使用 MAP_SHARED 则要有 PROT_WRITE 以及该文件要能写入。
      • EINVAL 参数start、length 或offset有一个不合法。
      • EAGAIN 文件被锁住,或是有太多内存被锁住。
      • ENOMEM 内存不足。

    8.1.3 munmap---解除内存映射

    1 #include <unistd.h>
    2 #include <sys/mman.h>
    3 //munmap(解除内存映射)    
    4 int munmap(void *start,size_t length);
    • 函数说明
      • munmap() 用来取消参数 start 所指的映射内存起始地址,参数length 则是欲取消的内存大小。
      • 当进程结束或利用 exec 相关函数来执行其他程序时,映射内存会自动解除,但关闭对应的文件描述符时不会解除映射。
    • 返回值
      • 如果解除映射成功则返回0,否则返回-1,错误原因存于 errno 中
    • 错误代码
      • EINVAL     参数 start或length 不合法。

    8.2 例子

    (1)写字符

      通过存储映射向文件中写入若干个字符。(文件中必须开辟空间供存储映射区同步,就相当于在写入的区域做一块空洞文件,然后将空洞文件映射到存储映射区)

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <fcntl.h>
     4 #include <unistd.h>
     5 #include <string.h>
     6 #include <errno.h>
     7 #include <stdlib.h>
     8 #include <stdio.h>
     9 #include <fcntl.h>
    10 #include <sys/mman.h>
    11 
    12 int main(int argc, const char *argv[])
    13 {
    14     if(argc < 2) {
    15         printf("usage: %s file
    ", argv[0]);
    16         exit(1);
    17     }
    18 
    19     int fd = open(argv[1], O_RDWR);
    20     if(fd < 0){
    21         perror("open error");
    22         exit(1);
    23     }
    24     
    25 
    26     //在19个字节处写入一个0
    27     lseek(fd, 19, SEEK_END);
    28     write(fd, "0", 1);
    29 
    30     char *addr;
    31     //进行存储映射
    32     addr = mmap(0, 20, PROT_WRITE, MAP_SHARED, fd, 0);
    33     if(addr < 0) {
    34         perror("mmap error");
    35         exit(1);
    36     }
    37 
    38     //修改存储映射区的内容
    39     int i;
    40     for(i = 0; i < 20; i++)
    41     {
    42         *(addr + i) = 'A' + i;
    43     }
    44     printf("write success
    ");
    45 
    46     //解除映射
    47     munmap(addr, 0);
    48 
    49     close(fd);
    50 
    51     return 0;
    52 }

      程序运行结果:

      

    (2)文件的拷贝

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <fcntl.h>
     4 #include <unistd.h>
     5 #include <string.h>
     6 #include <errno.h>
     7 #include <stdlib.h>
     8 #include <stdio.h>
     9 #include <fcntl.h>
    10 #include <sys/mman.h>
    11 
    12 int main(int argc, const char *argv[])
    13 {
    14     if(argc < 3) {
    15         printf("usage: %s srcfile destfile
    ", argv[0]);
    16         exit(1);
    17     }
    18 
    19     int src_fd;
    20     int dest_fd;
    21 
    22     if((src_fd = open(argv[1], O_RDONLY)) < 0) {
    23         perror("open error");
    24         exit(1);
    25     }
    26 
    27     if((dest_fd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0777)) < 0) {
    28         perror("open error");
    29         exit(1);
    30     }
    31 
    32     // 定位到文件尾部,返回一个偏移量,反应文件长度
    33     long len = lseek(src_fd, 0, SEEK_END);
    34     printf("len: %ld
    ", len);
    35 
    36     // 在文件开始处跳过 len-1 个字节
    37     lseek(dest_fd, len-1, SEEK_SET);
    38     write(dest_fd, "0", 1); //创建了目标文件的空洞文件
    39 
    40     char *src_map = mmap(0, len, PROT_READ, MAP_SHARED, src_fd, 0);
    41     if(src_map < 0) {
    42         perror("mmap error");
    43         exit(1);
    44     }
    45 
    46     char *dest_map = mmap(0, len, PROT_WRITE, MAP_SHARED, dest_fd, 0);
    47     if(dest_map < 0) {
    48         perror("mmap error");
    49         exit(1);
    50     }
    51 
    52     //存储映射区的复制并同步到文件中
    53     memcpy(dest_map, src_map, len);
    54     munmap(src_map, 0);
    55     munmap(dest_map, 0);
    56 
    57     close(src_fd);
    58     close(dest_fd);
    59 
    60     return 0;
    61 }

      运行结果:

      

      

  • 相关阅读:
    bzoj 1295 [SCOI2009]最长距离 最短路
    bzoj 3669 [Noi2014]魔法森林
    bzoj 1432 [ZJOI2009]Function 思想
    用JSP输出Hello World
    Web开发基础
    JSP相关背景
    JSP概述
    Java视频播放器的制作
    为JFileChooser设定扩展名过滤
    使用JFileChooser保存文件
  • 原文地址:https://www.cnblogs.com/kele-dad/p/9048712.html
Copyright © 2020-2023  润新知