【问题标题】:C low level read write functions 64-bit equivalent?C低级读写功能64位等效?
【发布时间】:2016-08-05 01:54:36
【问题描述】:

我有一些代码使用低级 i/o readwrite 系统调用,如 C 编程语言书籍 Kernighan 和 Ritchie 的第 170 页所述。 函数原型是这样的

int n_read = read  ( int fd, char *buf, int n )
int n_read = write ( int fd, char *buf, int n )

现在,使用这些 readwrite 的两个 .c 文件被更大的基于 fortran 的程序调用以读取和写入大量数据。

C 代码就是这样,没有任何类型的#include,在函数名后面有下划线并通过引用传递:

int read_ ( int *descriptor, char *buffer, int *nbyte )
{
   return ( read( *descriptor, buffer, *nbyte ) );
}

int write_ ( int *descriptor, char *buffer, int *nbyte )
{
   return ( write( *descriptor, buffer, *nbyte ) );
}

更大的基于 fortran 的程序会做这样的事情

INTEGER nbyte
COMPLEX*16 matrix(*)
INTEGER READ, WRITE
EXTERNAL READ, WRITE

status = READ( fd, matrix, nbyte )
if ( status .eq. -1 ) then
   CALL ERROR('C call read failure')
   stop
endif

正如您可能已经猜到的那样,这适用于小于 2^31 的 nbyte 值。我需要读取超过 2 GB 的数据,所以我需要 nbyte 在 fortran 中成为 long integerINTEGER*8

是否有等价的 read64 和 write64,就像 unistd.h 和 features.h 提供的 lseek64 一样?

重新编码的最佳方法是什么? 我应该使用 fread 和 fwrite 吗? 来自低级writeint fd 是否与来自fread()FILE *stream 相同?

我的要求是能够传递一个 8 字节的长整数,以允许最大 100 到 500 GB 的值或具有 12 位的整数,这都是 nbyte 的值p>

当前使用readwrite 被标识为“系统调用”,我得到了什么还是失去了什么?这是什么意思?

【问题讨论】:

  • 小块连续读取直到全部读取怎么办?
  • K&R C 不是你现在应该学习的东西,除非你出于历史原因好奇。现在有很多东西是不受欢迎的、过时的或完全错误的。例如,如果您对 read() 函数只有 RTFM,您就会找到 Leandros 给您的声明,并且可能自己解决了您的问题。
  • 这充其量只是一个丑陋的解决方法,@WeatherVane。如果 Fortran 端无法与 C 代码接口,仍然需要牢记这一点。
  • 有关库函数的最新信息,请打开终端窗口,然后使用man 命令。例如,man 2 read 告诉您有关 read 函数的所有信息。数字 2 表示手册中的部分。第 1 节主要是 shell 命令。第 2 节和第 3 节有 C 库函数。

标签: c


【解决方案1】:

编辑:你不能,至少在 Linux 上不能。 read 永远不会传输超过 32 位整数所能容纳的容量。

来自read 上的 Linux 手册页:

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

这不是 POSIX 的约束,它是 POSIX 允许的,但最终它的实现定义了 read 的行为方式。正如Andrew Hanle 报告的那样,读取 32GB 文件在 Solaris 上运行良好。在这种情况下,我的旧答案仍然有效。

旧答案:

read 可以很好地处理 64 位文件。它在<unistd.h> 中定义如下:-

ssize_t read(int fd, void *buf, size_t count);

您必须调整例程以使用 size_t 而不是 int,才能正确支持大文件。

在将read 用于大文件之前,您应该检查SSIZE_MAXcount 支持的最大值),如果文件太小(或分成更小的块)则中止。 SSIZE_MAX 是实现定义的值。

【讨论】:

  • 你怎么知道size_t 大到足以处理 64 位数字?它的最大可表示值可以小到 65534。
  • @JohnBollinger 他使用的是 POSIX API,而不是 stdio。 POSIX 是否不要求 size_t 足够大以容纳最大文件大小?
  • @Barmar、POSIX 和 C 都允许 SIZE_MAX 小至 65534。参见 pubs.opengroup.org/onlinepubs/009695399/basedefs/stdint.h.html
  • 啊,我明白了。 POSIX 有off_t 用于文件偏移,但单个read() 仍可能限制为size_t
  • @JohnBollinger 如果你不能假设size_t 在 64 位系统上是 64 位,那么size_t 是相当没用的,不是吗? (也许这就是你的意思。)在任何情况下,OP 都可以在运行时检查 sizeof(size_t),如果不是 8,则中止。
【解决方案2】:

正如@Leandros 所观察到的,read()write() 的符合 POSIX 的实现接受 size_t 类型的字节计数,并返回 ssize_t 类型的字节计数。这些可能是实际适用于您的定义,因为 C 标准未指定 read()write() 函数。然而,这是一个没有太大区别的区别,因为size_t 不需要比int 更宽——事实上,它可以更窄。

反正你有一个更大的问题。 Fortran 代码似乎假设它正在调用的 C 函数将读取/写入完整的指定字节数,否则会失败,但 POSIX read()write() 不能保证在成功时这样做。事实上,前几天这里有一个问题,这取决于这些函数一次传输的字节数不能超过有符号的 32 位整数所能表示的字节数,即使在具有 64 位的 64 位系统上也是如此。位[s]size_t

您可以通过将read_()write_() 函数实现到循环,然后连续调用底层的read()write() 函数,从而用一块石头杀死这两只鸟,直到传输指定字节的全部数量或发生错误。

【讨论】:

  • 你必须这样做。 read 不支持读取超过 0x7ffff000 个字节。
  • @Leandros,虽然我的回答中已经涵盖了某些实现,但 POSIX 并不要求所有实现都是如此。但是,是的,这使得循环成为一个非常好的主意。
  • 正确。您通常无法以非实现定义的方式回答它。虽然这在 Linux 上是不可能的,但其他一些 POSIX 兼容的平台可能支持它。 (这里想到达尔文)
  • @Leandros 你必须这样做。 read 不支持读取超过 0x7ffff000 字节。 Not technically true如果nbyte 的值大于{SSIZE_MAX},则结果是实现定义的。 SSIZE_MAX on 64 位系统比0x7ffff000 大很多。我刚刚在 Solaris 11 上进行了快速测试,并在一次读取操作中成功读取了 32GB,但由于 64GB 分配失败而无法读取更大的数据。不过,无论请求的字节数有多大,好的代码都会处理短读取。
  • @Leandros 好吧,我回复的你的评论不符合你原来的条件 "read 不支持读取超过 0x7ffff000 字节"。不过这无关紧要,因为代码must handle short reads anyway如果read() 在成功读取一些数据后被信号中断,它应该返回读取的字节数。
猜你喜欢
  • 2016-10-06
  • 2012-07-17
  • 2013-11-09
  • 1970-01-01
  • 1970-01-01
  • 2014-03-31
  • 2010-11-19
  • 2011-06-09
相关资源
最近更新 更多