系统调用write
返回-1并设置errno
是一个简单的案例。如果C errno
称为返回零或正数,我对write
的状态感兴趣。如果syscall.Write
对于任何情况都不为零,那么Go中的包装器err
只返回errno
,其中还包括write
调用返回正数的情况。
然而,man page of C write
call粗略地描述errno
也可以设置但是未指定如果我们写零长度缓冲区而不解释任何细节。
因此,以下案例似乎不清楚:
errno
为文件,非阻塞套接字或阻塞套接字调用返回0,write
的状态是什么?write
调用返回0和errno
不是0?errno
称回归正面,write
的状态是什么?它会消极吗?我认为上面的描述指出了C write
调用和Go syscall.Write
之间的区别,这对于开发人员来说还不清楚,以下是我的想法:
根据手册页,返回零在C write
调用文件和非阻塞套接字中明确定义,但是不清楚是否存在阻塞套接字的非错误条件,这将导致write()
不阻塞,返回0 ,并且(可能)如果重试可能会在以后成功。
确实Go直接包装系统调用write
。但是,下面的代码片段似乎不安全,因为written
等于零是一个可能触发err
的情况,但我们不想打破循环:
func writeAll(fd int, buffer []byte) bool {
length := len(buffer)
for length > 0 {
written, err := syscall.Write(fd, buffer)
if err != nil { // here
return false
}
length -= written
buffer = buffer[written:]
}
return true
}
我怀疑有什么不对吗?
使用write
,只有两种情况需要考虑:
errno
。errno
。除非您对历史Unix实现感兴趣,否则没有其他情况需要考虑(请参阅:Is a return value of 0 from write(2) in C an error?)。
write
可能返回0的原因是因为输入缓冲区可能为空。
然而,C
write
调用的手册大致描述errno
也可以设置但是未指定如果我们写零长度缓冲区而不解释任何细节。
所有这些意味着0长度写入可能会失败。如果失败,则返回-1并设置errno
。如果成功,则返回0并且不设置errno
。这与任何其他写操作的行为相同,它只是在手册页中提到的,因为人们可能会发现0长度写入可能会失败令人惊讶。
如果
errno
为文件,非阻塞套接字或阻塞套接字调用返回0,write
的状态是什么?
在这种情况下,errno
没有设置,因为write
没有失败。仅当输入缓冲区为零字节时才会发生这种情况。
何时以及如何
write
调用返回0和errno
不是0?
这不会发生。设置errno
并且返回值为-1,或者未设置errno
且返回值为0或更大。
如果
errno
称回归正面,write
的状态是什么?它会消极吗?
errno
值不会被设置。它将具有与write
调用之前相同的值。
是否有其他系统调用可能会遇到相同的情况?
通常,系统调用将返回错误,否则它们将成功。他们不会做两者的混合。查看其他手册页的返回值部分,您将看到它们与write
大致相同。
这段代码很安全。
func writeAll(fd int, buffer []byte) bool {
length := len(buffer)
for length > 0 {
written, err := syscall.Write(fd, buffer)
if err != nil { // here
return false
}
length -= written
buffer = buffer[written:]
}
return true
}
请注意,它有点多余,我们可以这样做:
func writeAll(fd int, buf []byte) bool {
for len(buf) > 0 {
n, err := syscall.Write(fd, buf)
if err != nil {
return false
}
buf = buf[n:]
}
return true
}
从技术上讲,write
既是系统调用又是C函数(至少在许多系统上)。但是,C函数只是一个调用系统调用的存根。 Go不会调用此存根,它会直接调用系统调用,这意味着此处不涉及C(嗯,直到进入内核)。
手册页显示了C stub,write
的调用约定和行为。 Go选择在自己的存根中复制该行为,syscall.Write
。实际的系统调用本身只有一个汇编语言接口。