c - 处理不完整的 write() 调用

标签 c linux unix io

在 Linux/Unix 中,write() 调用最终写入的字节数可能少于请求的字节数:

The number of bytes written may be less than count if, for example, there is insufficient space on the underlying physical medium, or the RLIMIT_FSIZE resource limit is encountered (see setrlimit(2)), or the call was interrupted by a signal handler after having written less than count bytes. (See also pipe(7).)

C 标准库的 fwrite() 具有相同的行为。我见过的大多数代码都忽略了这种可能性,选择以下列方式处理错误:

int ret = write(fd, buf, size);
if (ret < 0) {
    printf("Couldn't write %s: %s\n", path, strerror(errno));
    exit(1);
}

我个人养成了修改条件的习惯,以便我们进行检查

if (ret != size) {
    printf("Couldn't write %s: %s\n", path, strerror(errno));
    exit(1);
}

它注意到了这种情况。但是,我也注意到我的程序偶尔会退出:

Couldn't write /some/file: Success

我想这并不奇怪。但是,处理这种情况的标准、健壮、干净的方法是什么?显然,“静默数据损坏”——这似乎是每个教程的行为——是不对的。我可以修改我的代码,使其专门检测这种情况并退出。

但是man 2 write中提供的例子只是一个例子。还有其他例子可以重试吗(EINTR 就是一个例子......)?我如何检测这些,更重要的是,确保我处理了每一个案例?难道没有一种标准的简洁方法来制作这些错误处理程序吗?

最佳答案

如果在两种情况下什么都不写,Write 将返回一个负数:

  • 临时错误(例如EINTREAGAINEWOULDBLOCK);第一个可以发生在任何写入中,后两个(广泛地)只发生在非阻塞 I/O 上。

  • 永久性错误。

通常你会想重试第一个,所以例程是在返回 EINTREAGAINEWOULDBLOCK 时重复写入(尽管我见过反对后者的论点)。

例如:

ssize_t
write_with_retry (int fd, const void* buf, size_t size)
{
    ssize_t ret;
    do
    {
         ret = write(fd, buf, size);
    } while ((ret<0) && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
    return ret;
}

另请注意(来自手册页)在非阻塞 I/O 或阻塞 I/O 的情况下,write 可以返回少于您请求的字节数(正如 linux 手册页所述) .

OS-X 手册页摘录:

When using non-blocking I/O on objects, such as sockets, that are subject to flow control, write() and writev() may write fewer bytes than requested; the return value must be noted, and the remainder of the operation should be retried when possible.

Linux 手册页摘录(我的重点):

The number of bytes written may be less than count if, for example, there is insufficient space on the underlying physical medium, or the RLIMIT_FSIZE resource limit is encountered (see setrlimit(2)), or the call was interrupted by a signal handler after having written less than count bytes.

您通常会使用 select() 处理这些情况,但要手动处理这种情况:

ssize_t
write_with_retry (int fd, const void* buf, size_t size)
{
    ssize_t ret;
    while (size > 0) {
        do
        {
             ret = write(fd, buf, size);
        } while ((ret < 0) && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
        if (ret < 0)
            return ret;
        size -= ret;
        buf += ret;
    }
    return 0;
}

关于c - 处理不完整的 write() 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32683086/

相关文章:

C - 使用 fork()、pipe()、select()、execl() 和 write() 未达到正确的结尾

C++ 套接字 - 从结构数组返回单个地址信息

linux - 以独立于分发的方式定位配置文件?

unix - 在 Mac OS X 终端中通过别名进入目录

c++ - GlGenTextures 不断返回 0

C 简单程序不起作用

linux - 将 yubikey 用于 sudo

linux - echo 命令中的命令或别名 (Linux Ubuntu Bash)

linux - 无法检查 Fedora core 2 中的磁盘输入输出

c - 如何将字符串数组的元素连接到字符串数组?