使用内存映射复制文件

时间:2014-12-17 21:18:59

标签: c linux mmap memcpy bsd

我想在C中为在BSD OS上运行的进程实现有效的文件复制技术。截至目前,该功能是使用读写技术实现的。我试图通过使用内存映射文件复制技术使其优化。

基本上我会分叉一个进程,该进程将src和dst文件同步到msc并执行从src到dst的指定字节的memcpy()。 memcpy()返回后进程退出。这里需要msync(),因为当我实际使用MS_SYNC标志调用msync时,该函数需要花费大量时间才能返回。使用MS_ASYNC标志也可以看到相同的行为?

i)总结一下,避免使用msync()是否安全?

ii)是否有其他更好的方法在BSD中复制文件。因为bsd似乎不支持sendfile()或splice()?还有其他等价物吗?

iii)是否有任何简单的方法可以为此要求实施我们自己的零拷贝技术?

我的代码

/* mmcopy.c

   Copy the contents of one file to another file, using memory mappings.

   Usage mmcopy source-file dest-file
*/
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "tlpi_hdr.h"

int
main(int argc, char *argv[])
{
    char *src, *dst;
    int fdSrc, fdDst;
    struct stat sb;

    if (argc != 3)
        usageErr("%s source-file dest-file\n", argv[0]);

    fdSrc = open(argv[1], O_RDONLY);
    if (fdSrc == -1)
        errExit("open");

    /* Use fstat() to obtain size of file: we use this to specify the
       size of the two mappings */

    if (fstat(fdSrc, &sb) == -1)
        errExit("fstat");

    /* Handle zero-length file specially, since specifying a size of
       zero to mmap() will fail with the error EINVAL */

    if (sb.st_size == 0)
        exit(EXIT_SUCCESS);

    src = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fdSrc, 0);
    if (src == MAP_FAILED)
        errExit("mmap");

    fdDst = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
    if (fdDst == -1)
        errExit("open");

    if (ftruncate(fdDst, sb.st_size) == -1)
        errExit("ftruncate");

    dst = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdDst, 0);
    if (dst == MAP_FAILED)
        errExit("mmap");

    memcpy(dst, src, sb.st_size);       /* Copy bytes between mappings */
    if (msync(dst, sb.st_size, MS_SYNC) == -1)
        errExit("msync");
    enter code here
    exit(EXIT_SUCCESS);
}

1 个答案:

答案 0 :(得分:2)

简短回答:msync()不是必需的。

如果未指定msync(),操作系统将在终止进程后在后台刷新内存映射页面。这在任何符合POSIX标准的操作系统上都是可靠的。

回答次要问题:

通常,在任何符合POSIX的操作系统(例如BSD)上复制文件的方法是使用open() / read() / write()和一些大小的缓冲区(16kb)例如,32kb或64kb)。从src将数据读入缓冲区,将数据从缓冲区写入dest。重复直到read(src_fd)返回0字节(EOF)。

但是,根据您的目标,使用mmap()以这种方式复制文件可能是一个非常可行的解决方案,只要被处理的文件相对较小(相对于目标硬件的预期内存限制和你的申请)。 mmap复制操作大约需要文件总物理内存的2倍。因此,如果您尝试复制8MB的文件,您的应用程序将使用16MB来执行复制。如果您希望使用更大的文件,那么复制可能会变得非常昂贵。

使用mmap()还有其他优势吗?实际上,没有。

  1. 与使用write()将数据直接写入文件相比,刷新mmap页面的操作系统通常会慢得多。这是因为操作系统会在页面刷新之前故意优先考虑其他事项,以保持系统的响应能力。用于前台任务/应用程序。

  2. 在将mmap页面刷新到磁盘(在后台)的过程中,系统突然断电的可能性将导致数据丢失。当然,使用write()时也会发生这种情况,但如果write()完成得更快,那么意外中断的可能性就会降低。

  3. 调用msync()时观察到的长时间延迟大致是操作系统将复制的文件刷新到磁盘所需的时间。当你不打电话给msync()时,它会在后台发生(而且由于这个原因也需要更长的时间)。

相关问题