【Linux编程】存储映射I/O

存储映射I/O使一个磁盘文件与存储空间中的一个缓冲区相映射,对缓冲区的读、写操作就是对文件的读、写操作,从而可以不再使用read、write系统调用。

将文件映射到存储区的函数由mmap完成,函数原型如下:
#include <sys/mman.h>
 
/* 成功返回映射区起始地址,出错返回MAP_FAILED */
void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off);


参数说明:
  • addr:指定映射存储区的起始地址,通常为0表示由系统选择起始地址。
  • len:需要映射的字节数。
  • prot:对映射存储区的保护要求,不能超过open文件时的权限。
    • PROT_READ:映射区可读
    • PROT_WRITE:映射区可写
    • PROT_EXEC:映射区可执行
    • PROT_NONE:映射区不可访问
  • flag:影响映射存储区的属性。
    • MAP_FIXED:返回值必须等于addr,不利于移植,不鼓励使用。
    • MAP_SHARED:表示存储操作相当于对该文件的write。
    • MAP_PRIVATE:对映射区的存储操作导致创建该映射文件的一个私有副本。
  • filedes:指定要被映射的文件描述符,映射之前需要先打开该文件。
  • off:要映射字节在文件中的起始偏移量,通常为0。

存储映射如下所示:


测试代码:
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
 
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
 
int main(int argc, char *argv[])
{
    int fdin, fdout;
    void *src, *dst;
    struct stat statbuf;
 
    if (argc != 3)
    {
        printf("usage: %s <fromfile> <tofile>\n", argv[0]);
        return -1;
    }
 
    fdin = open(argv[1], O_RDONLY);
    fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, FILE_MODE);
 
    fstat(fdin, &statbuf);
 
    lseek(fdout, statbuf.st_size - 1, SEEK_SET);
    write(fdout, " ", 1);   /* lseek偏移量大于文件长度时,写操作将加长文件 */
 
    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);
 
    memcpy(dst, src, statbuf.st_size);  /* 数据复制 */
 
    munmap(src, statbuf.st_size);
    munmap(dst, statbuf.st_size);
 
    return 0;
}


此函数实现了文件内容之间的拷贝。lseek + write的组合操作使得目标文件的大小增加到和源文件大小相同。因为当lseek设置的文件偏移量大于文件当前长度时,下一个写操作将会使文件增大。如果没有对目标文件扩大,那么进程会接收到SIGBUS信号,表示存储区中有地址无法映射到文件中。

mmap实际上是将包含文件内容的内核缓冲区映射到应用程序地址空间,然后用memcpy直接进行数据的拷贝。其优势在于避免了类似read、write系统调用,在内核空间和用户空间之间的数据传递。

参考:
《unix环境高级编程》 P390-P395.

【Linux编程】存储映射I/O,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。