Bee*_*ope 19 c linux file-io posix
我想实现一个适当的write(2)
循环,它接受一个缓冲区并一直调用,write
直到写入整个缓冲区.
我想基本的方法是这样的:
/** write len bytes of buf to fd, returns 0 on success */
int write_fully(int fd, char *buf, size_t len) {
while (len > 0) {
ssize_t written = write(fd, buf, len);
if (written < 0) {
// some kind of error, probably should try again if its EINTR?
return written;
}
buf += written;
len -= written;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
...但是这引发了一个问题:是否write()
可以有效地返回写入的0字节以及在这种情况下该怎么做.如果情况仍然存在,上面的代码只会热情地转动,write
这似乎是一个坏主意.只要返回零以外的其他内容,您就可以取得进展.
手册页write
有点含糊不清.它说,例如:
成功时,返回写入的字节数(零表示没有写入).
这似乎表明在某些情况下这是可能的.只显示了一个这样的场景:
如果count为零且fd引用常规文件,则write()可能会在检测到以下错误之一时返回失败状态.如果未检测到错误或未执行错误检测,则将返回0而不会导致任何其他影响.如果count为零且fd指的是常规文件以外的文件,则不指定结果.
这种情况下,避免了上述,因为我从来没有叫write
用len == 0
.还有许多其他情况下无法编写任何内容,但一般来说它们都有与之关联的特定错误代码.
文件本身将open
来自命令行中给出的路径/名称.因此它通常是一个常规文件,但用户当然可以传递管道,输入重定向,传递特殊设备等/dev/stdout
内容.我至少控制了open
调用,并且 O_NONBLOCK
标志没有传递给open.我无法合理地检查所有文件系统的行为,所有特殊设备(即使我可以,还会添加更多),所以我想知道如何以合理和一般的方式处理这个问题.
*...表示非零缓冲区大小.
除非你不顾一切地调用未指定的行为,否则你不会得到零结果,write()
除非你尝试写零字节(问题中的代码避免这样做).
write()
我相信,POSIX规范涵盖了这个问题.
在写()函数将尝试写nbyte个字节的缓冲区指出,由BUF与打开的文件描述符,相关的文件菲尔德斯.
在执行下面描述的任何操作之前,如果nbyte为零且文件是常规文件,则write()函数可以检测并返回错误,如下所述.在没有错误的情况下,或者如果没有执行错误检测,write()函数将返回零并且没有其他结果.如果nbyte为零且文件不是常规文件,则结果未指定.
这说明如果你请求写入零字节,你可能会得到一个零返回值,但是有一堆警告 - 它必须是一个常规文件,如果EBADF
检测到错误,你可能会收到错误,并且它未指定如果文件描述符不引用常规文件会发生什么.
如果write()请求写入更多字节而不是有空间(例如,[XSI] [Option Start]进程的文件大小限制或[Option End]介质的物理端),只有那么多有空间的字节应写入.例如,假设在达到限制之前文件中有20个字节的空间.写入512字节将返回20.下一次写入非零字节数将导致失败返回(除非如下所述).
[XSI]⌦如果请求会导致文件大小超过进程的软文件大小限制,并且没有空间可以写入任何字节,则请求将失败,并且实现将为线程生成SIGXFSZ信号.⌫
如果write()在写入任何数据之前被信号中断,它将返回-1并将errno设置为[EINTR].
如果write()在成功写入一些数据后被信号中断,它将返回写入的字节数.
如果nbyte的值大于{SSIZE_MAX},则结果是实现定义的.
这些规则并没有真正给予返回0的权限(虽然一个学者可能会说这个nbyte
太大的值可能被定义为返回0).
尝试写入支持非阻塞写入的文件描述符(管道或FIFO除外)并且无法立即接受数据时:
如果O_NONBLOCK标志是清除的,则write()将阻塞调用线程,直到可以接受数据.
如果设置了O_NONBLOCK标志,则write()不会阻塞该线程.如果可以在不阻塞线程的情况下写入某些数据,则write()应该写入它可以写入并返回写入的字节数.否则,它将返回-1并将errno设置为[EAGAIN].
...隐藏文件类型的详细信息 - 其中一些具有未指定的行为......
返回值
成功完成后,这些函数将返回实际写入与fildes关联的文件的字节数.这个数字永远不会大于字节.否则,返回-1并设置errno以指示错误.
因此,由于您的代码避免尝试写入零字节,只要len
不大于{SSIZE_MAX},并且只要您不写入模糊文件类型(如共享内存对象或类型化内存对象),您应该看不到归零write()
.
稍后在POSIX页面中write()
,在Rationale部分中,有以下信息:
如果需要
-1
返回此POSIX.1-2008卷并将其errno
设置为[EAGAIN],则大多数历史实现将返回零(O_NDELAY
设置标志,这是历史的前身O_NONBLOCK
,但本身不是POSIX.1- 2008).选择此卷POSIX.1-2008中的错误指示,以便应用程序可以将这些情况与文件结尾区分开来.虽然write()
无法接收文件结束的指示,read()
但是,这两个函数具有相似的返回值.此外,一些现有系统(例如,第八版)允许写入零字节以表示读者应获得文件结束指示; 对于那些系统,返回值为0write()
表示成功写入文件结束指示.
因此,虽然POSIX(在很大程度上如果不是全部)排除了从零返回的可能性write()
,但是相关系统上的现有技术确实已经write()
返回零.
它取决于文件描述符引用的内容.当您调用write
文件描述符时,内核最终会在关联的文件操作向量中调用写例程,该向量对应于文件描述符引用的基础文件系统或设备.
大多数普通文件系统永远不会返回0,但设备可能会做任何事情.您需要查看相关设备的文档,了解它可能会执行的操作.设备驱动程序返回0字节是合法的(内核不会将其标记为错误或任何内容),如果是,则写入系统调用将返回0.
归档时间: |
|
查看次数: |
2616 次 |
最近记录: |