处理不完整的write()调用

Jef*_*lor 7 c unix linux io

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

如果,例如,底层物理介质上没有足够的空间,或者遇到RLIMIT_FSIZE资源限制(请参阅setrlimit(2)),或者调用被信号处理程序中断后,写入的字节数可能小于count写得少于计数字节.(另见管道(7).)

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

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

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

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

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

Couldn't write /some/file: Success
Run Code Online (Sandbox Code Playgroud)

我想这并不太令人惊讶.但那么处理这种情况的标准,强大,干净的方法是什么?显然,"无声数据损坏" - 似乎是每个教程的行为 - 都不行.我可以修改我的代码,以便它专门检测到这种情况并退出.

但是man 2中提供的示例只是一个例子.还有其他的例子,重试将是要走的路(EINTR就是一个例子......)?我如何检测这些,更重要的是,确保我已经处理了每一个案例?是否有一个标准的清洁方法来制作这些错误处理程序?

abl*_*igh 9

如果在两种情况下没有写入,则写入将返回负数:

  • 暂时性错误(例如EINTR,EAGAINEWOULDBLOCK); 第一个可以在任何写入时发生,第二个(广泛地)仅在非阻塞I/O上发生.

  • 永久性的错误.

通常你会想要重试第一个,所以例程是重复写if EINTR,EAGAIN或者EWOULDBLOCK返回(尽管我已经看到了针对后者的论点).

例如:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

OS-X手册页提取:

在对象(如套接字)上使用非阻塞I/O时,它们受到流量控制,write()并且writev()可能写入的字节数少于请求的字节数; 必须注意返回值,并且应尽可能重试操作的其余部分.

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

写入的字节数可能小于count,例如,如果底层物理介质上没有足够的空间,或者遇到RLIMIT_FSIZE资源限制(请参阅参考资料setrlimit(2)),或者在写入小于的信号处理程序后中断了调用计数字节.

你通常会处理那些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;
}
Run Code Online (Sandbox Code Playgroud)