【问题标题】:Copying files using memory map使用内存映射复制文件
【发布时间】:2015-02-16 13:24:36
【问题描述】:

我想在 C 语言中为我在 BSD 操作系统上运行的进程实现一种有效的文件复制技术。到目前为止,该功能是使用读写技术实现的。我试图通过使用内存映射文件复制技术对其进行优化。

基本上,我将分叉一个进程,它同时映射 src 和 dst 文件,并对从 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);
}

【问题讨论】:

  • 需要。如果您不执行msync(2)。您无法保证在您 munmap(2) 文件时您的更改已刷新回 fs。
  • 谢谢,但 msync() 似乎需要相当长的时间,有没有有效的方法来做到这一点?
  • 我对 BSD 不太熟悉,无法提供权威的答案,抱歉。
  • msync 只是等待数据提交到磁盘(如fsync)。不介意的可以跳过。
  • 查看 /bin/cp (svnweb.freebsd.org/base/head/bin/cp) 的 freebsd 源代码表明,它们仅对源文件执行 mmap(2),并且仅当源大小为 8M 或更小时,然后只需使用 write(2) 来发出目标数据。你确定你能做得更好?你量过吗?有时,完善的系统实用程序的执行人员可以很好地融合可移植性和性能。

标签: c linux mmap memcpy bsd


【解决方案1】:

简答: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() 时,它会在后台发生(并且因此需要更长的时间)。

【讨论】:

  • 感谢 jstine 的信息
猜你喜欢
  • 1970-01-01
  • 2013-02-17
  • 2016-02-23
  • 1970-01-01
  • 1970-01-01
  • 2011-12-31
  • 2010-11-01
  • 2011-05-14
  • 2016-12-08
相关资源
最近更新 更多