【问题标题】:pread for very large files准备非常大的文件
【发布时间】:2016-04-12 06:31:00
【问题描述】:

我正在使用pread 读取一个大文件,如下所示:

ssize_t s = pread(fd, buff, count, offset);
if (s != (ssize_t) count)
  fprintf(stderr, "s = %ld != count = %ld\n", s, count);
assert(s == (ssize_t ) count);

上面的代码对于小文件(最大 1.5GB)运行良好。但是,对于大文件大小,返回的字节数与预期计数不同。

特别是,对于 2.4GB 文件大小,我的 count 设置为 2520133890 并且断言失败,fprintf 说:

s = 2147479552 != count = 2520133890

令人费解的是,我正在使用 64 位系统,因此,sizeof(ssize_t) = 8

这个失败的原因是什么,我该如何解决这个问题,以便我可以一次读取整个文件?

【问题讨论】:

  • “偏移量”呢?你确定它包含零吗?
  • countoffset 是什么类型的?你是否使用libc中的pread的正确版本,例如在Linux上有preadpread64,你必须确保你使用pread64函数来自glibc
  • @GiuseppeGuerrini 偏移量始终设置为 0。
  • @user1034749 我明确尝试使用 pread64 并且行为仍然相同。
  • 那你需要打印sizeof(off_t), sizeof(size_t), sizeof(ssize_t) 确保它们等于8

标签: c unix file-io posix


【解决方案1】:

看起来你使用的是 linux,pread 返回的幻数是 2147479552 = 0x7ffff000,所以答案在man 2 read

在 Linux 上,read()(和类似的系统调用)将在 最多 0x7ffff000 (2,147,479,552) 个字节,返回字节数 实际转移。 (在 32 位和 64 位上都是如此 系统。)

所以您至少需要两次致电pread 来获取您的数据, 此限制与_FILE_OFFSET_BITS=64O_LARGEFILEsizeof(off_t) 等无关, 这个限制是由rw_verify_area在linux内核中创建的:

/*
 * rw_verify_area doesn't like huge counts. We limit
 * them to something that fits in "int" so that others
 * won't have to do range checks all the time.
 */
int rw_verify_area(int read_write, struct file *file, const loff_t *ppos, size_t count)
...
return count > MAX_RW_COUNT ? MAX_RW_COUNT : count;

#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)

【讨论】:

  • 哇,每天都能学到新东西。 FWIW,在我的系统上, read(2) 手册页不包含此文本,例如man7.org/linux/man-pages/man2/read.2.html 确实如此。
  • 手册页中的修复是在 2016 年 2 月推出的,最近足以传播到 linux 发行版 (bugs.debian.org/cgi-bin/bugreport.cgi?bug=630029)
  • 短读无论如何都会发生。另外,I/O 子系统会拆分大型请求。我最近听到的典型建议是每个 I/O 最多 8-16MB。
  • 有趣的是,当大规模执行测试时,各种实现决策是如何支持的。 @user1034749:是否有设计/逻辑上的原因为什么要实施这种通用检查(而不是专门基于 32 位/64 位平台)?
  • 这些代码行写了一个叫 Linus Torvalds 的人,他是这样解释的:permalink.gmane.org/gmane.linux.kernel.commits.head/67962
【解决方案2】:

根据您的描述,听起来您正在执行 32 位构建,并且您尚未启用大文件支持 (LFS)。为此,您需要将宏 _FILE_OFFSET_BITS 设置为值 64。

所以,请仔细检查您是否真的像您说的那样进行 64 位构建。编辑:好的,我相信您确实在使用 64 位系统。

我认为您的问题的正确原因,正如答案 https://stackoverflow.com/a/36568630/75652 中所指出的那样,在 read(2) 手册页中进行了解释:http://man7.org/linux/man-pages/man2/read.2.html。为了处理这个,你需要像

这样的代码

  bytes_left = count;
  while (bytes_left > 0)
    {
      trans = pread (fd, buff, bytes_left, offset);
      if (trans == -1)
        {
          if (errno == EINTR)
            continue;
          else
            return trans;
        }
      buff += trans;
      bytes_left -= trans;
      offset += trans;
    }

  return count - bytes_left;

【讨论】:

  • 在运行 ldd 时,它看起来像一个 64 位版本:$ ldd program linux-vdso.so.1 => (0x00007fff4e18f000) libboost_system.so.1.54.0 => /usr/lib/x86_64-linux-gnu/libboost_system.so.1.54.0 (0x00007fe57440b000) libboost_filesystem.so.1.54.0 => /usr/lib/x86_64-linux-gnu/libboost_filesystem.so.1.54.0 (0x00007fe5741f5000) libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fe573ef0000) libgomp.so.1 => /usr/lib/x86_64-linux-gnu/libgomp.so.1 (0x00007fe573ce1000)
  • libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fe573acb000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fe5738ac000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe5734e7000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe5731e1000) /lib64/ld-linux-x86-64.so.2 (0x00005562e9fca000)
猜你喜欢
  • 2021-06-15
  • 2013-05-07
  • 1970-01-01
  • 2017-11-12
  • 2015-03-24
  • 1970-01-01
  • 2017-04-29
  • 1970-01-01
  • 2011-12-14
相关资源
最近更新 更多