【问题标题】:Will write(2) always write less than or equal to SSIZE_MAX?write(2) 是否总是写入小于或等于 SSIZE_MAX?
【发布时间】:2015-04-18 21:20:41
【问题描述】:

write(2) 的函数签名是 ssize_t write(int fd, const void *buf, size_t count)。一般size_t的最大值大于ssize_t的最大值。这是否意味着write实际上可以写入的数据量实际上是SSIZE_MAX而不是SIZE_MAX?如果不是这样,当写入的字节数大于SSIZE_MAX 时会发生什么情况?

我基本上想知道write 写入的数据量是否受SSIZE_MAXSIZE_MAX 的限制。

【问题讨论】:

  • 进程也有限制。超过process's file size limit 的进程将失败(错误EFBIG),因此该限制可能小于SSIZE_T_MAX,尽管我对此不确定。
  • SSIZE_T 代表有符号大小,因此意味着它是一个有符号数。这就是在写系统调用期间发生一些错误的情况。所以最大写入次数不能大于signed Size_t
  • pendrive,我知道很多。这根本没有解决我的问题。返回值是ssize_t,在大多数系统上它的范围比size_t 更有限,所以我本质上是在询问写入是否受SSIZE_MAXSIZE_MAX 的限制。
  • 请注意,大于PTRDIFF_MAX(通常等于SIZE_MAX/2SSIZE_MAX)的对象不应该出现在高质量的实现中,如果出现,指针减法是不安全的(可以溢出并产生UB)。因此,您传递给write 的有效大小永远不应该大于SSIZE_MAX

标签: c posix


【解决方案1】:

类型 ssize_t 由 POSIX 定义为能够存储至少 32767 (_POSIX_SSIZE_MAX) 的签名类型,没有其他保证。所以它的最大值可以小于size_t的最大值。

ssize_t 的 POSIX 定义:

ssize_t

用于字节计数或错误指示。

因此,您请求写入的字节数可能大于ssize_t 可以容纳的字节数。在这种情况下,POSIX 将其留给实现。

来自write() 的 POSIX 规范:

ssize_t write(int fildes, const void *buf, size_t nbyte);

如果 nbyte 的值大于 {SSIZE_MAX}, 结果是实现定义的。

【讨论】:

  • 我知道它有点飘,但如果使用-ansi,那么SSIZE_MAX_POSIX_SSIZE_MAX 可能不会在GNU 系统上定义。另见SSIZE_MAX on ia64。讨论迅速扩大到包括所有 glibc 系统。
  • 如果nbyte > SSIZE_MAX+1,Linux 似乎返回EINVAL。 (它确实接受SSIZE_MAX+1,返回0)不幸的是,标准中并没有通过强制最多写入SSIZE_MAX字节的部分写入来简单地处理这个问题。
【解决方案2】:

write() 的 POSIX 规范说:

如果nbyte 的值大于{SSIZE_MAX},则结果是实现定义的。

因此,任何写入超过 SSIZE_MAX 字节的尝试都会导致 POSIX 未强制执行但必须由系统记录的行为(它是实现定义的行为,而不是未定义的行为)。但是,不同的系统可能会以不同的方式处理它,没有什么可以阻止一个系统报告错误(可能errno 设置为EINVAL),另一个系统写入SSIZE_MAX 字节并报告该错误,让应用程序重试在其余部分,其他系统可能具有创造性并且仍然以不同的方式做事。

如果您有一个 64 位系统,SSIZE_MAX 可能比世界上最大的单个数据中心的磁盘空间量还要大(可能是一个数量级或更多,即使考虑到 NSA和谷歌),所以你不太可能遇到真正的问题,但在 32 位系统上,你可以轻松拥有超过 2 GiB 的空间,如果 ssize_t 是 32 位的,你必须处理这一切。 (在 Mac OS X 10.10.3 上,32 位构建有一个 4 字节的 size_tssize_t,至少默认情况下是这样。)

【讨论】:

  • 比世界上的磁盘空间量大(几个数量级)。一点也不!反之亦然:只需要 2000 万块 500 GB 硬盘就可以超过 2^63 字节。
  • @chqrlie 9223372036854775808 吉比字节中的字节 = 8589934592。将该数量除以 500(500 个硬盘驱动器)。 17 179 869.184 500 GiB 硬盘。虽然你是对的,但我的世界有更多的磁盘空间。
  • @chqrlie:嗯..你已经足够接近我已经减少了索赔。任何试图复制这么多数据的人都会遇到“它在哪里被读取和写入”和“你是否允许一次读取和写入这么多数据”的问题——答案是“不,你是不允许一次写入那么多数据'。
  • 查看这篇文章:它试图回答这个问题,但它已经过时了:paulwallbank.com/2012/08/23/…。 9 EB 对 google、dropbox 或 NSA 来说并非遥不可及,但建议他们将其传播到多个物理数据中心。然而,它仍可能看起来像一个巨大的虚拟数据中心。
  • 2^63 是 8 EiB,或大约 9E18 字节;这是触手可及的,但它将是一个巨大的数据中心,SO 上的人不太可能访问一个,即使他们确实可以访问一个,他们也有可能被允许进行一次写入传输这么多数据可以忽略不计,如果只是因为获得足够的 RAM 来保存要写入的图像也是一个问题。但是 10^50 对应于大约 2^166,这要大得多,然后你就会遇到原子计数问题。
【解决方案3】:

是的,在一次 write 调用中可以写入的数据量仅限于 ssize_t 中可以保存的数据量。有关说明,请参阅the relevant glibc documentation page。引用该页面,“你的程序应该总是在循环中调用 write,迭代直到所有数据都被写入。” (强调补充)该页面还阐明了 ssize_t 用于表示可以在单个操作中读取或写入的块的大小。

【讨论】:

  • 您知道是否在 POSIX 规范中的某处提到 write 写入的数据量以 ssize_t 而不是 size_t 为界?
  • 我不确定,我不熟悉 POSIX 规范。
  • 其实,我认为答案是行为是实现定义的:the page on write 表示“如果 nbyte 的值大于 {SSIZE_MAX},则结果是实现定义的。”这对我来说似乎很愚蠢:为什么不将 write 定义为做一些合理的事情:即只写可以安全完成的事情?
猜你喜欢
  • 2016-04-06
  • 2015-07-08
  • 1970-01-01
  • 2010-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多