写入(2)在C中的返回值是否为错误?

在系统调用的手册页写入(2) –

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

它说以下内容:

返回值

成功时,将返回写入的字节数(零表示什么都没有写入)。 出错时,返回-1,并适当地errno 。 如果计数为零且文件描述符引用常规文件,则可能返回0,或者可能检测到错误。 对于一个特殊的文件,结果是不可移植的。

我认为这意味着返回0意味着没有任何东西被写出来,无论出于任何原因。

然而,当处理一个TCP套接字的文件描述符时,UNP中的Stevens将返回值0视为一个致命错误(这是由另一个在短期内调用exit(1)函数封装的):

 ssize_t /* Write "n" bytes to a descriptor. */ writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nwritten = write(fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; /* and call write() again */ else return(-1); /* error */ } nleft -= nwritten; ptr += nwritten; } return(n); } 

如果errno表明写入调用被接收信号的进程中断,他只会将0作为合法返回值。

为什么?

史蒂文斯可能会这样做,以赶上旧的执行write()行为有所不同。 例如,Single Unix Spec说( http://www.opengroup.org/onlinepubs/000095399/functions/write.html

如果IEEE Std 1003.1-2001的这一卷需要返回-1,并且将errno设置为[EAGAIN],则大多数历史实现将返回零

另外,在这里只是有些迂腐,如果你不写入套接字,我会检查,以确保缓冲区的长度(第一个例子中的“计数”)实际上是正确计算。 在Stevens的例子中,如果缓冲区长度为0,你甚至不会执行write()调用。

这将确保代码不会无限旋转,即使文件描述符不是TCP套接字或意外的非阻塞标志有效。 在某些系统上,如果没有数据可以被阻塞地写入,某些传统的非阻塞模式(例如O_NDELAY )会导致write()返回0(没有设置errno ),至少对于某些类型的文件描述符。 (POSIX标准的O_NONBLOCK在这种情况下使用了一个错误返回。)而一些系统上的一些非阻塞模式适用于底层对象(比如socket,fifo)而不是文件描述符,所以甚至可以被启用另一个进程为同一个对象打开文件描述符。 在这种情况下,代码可以保护自己免受旋转的影响,因为它不能用于非阻塞模式。

如你的手册页所述,0的返回值对于特殊文件是“不可移植的”。 套接字是特殊文件,所以结果可能意味着不同的东西。

通常,对于套接字,read()或write()中的0字节值表示套接字已关闭,接收到0后,后续调用将返回-1,并带有错误代码。