syscall 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,并且(大概)在重试后可能会成功。
Indeed 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
}
我的怀疑有什么不对吗?
答案 0 :(得分:6)
对于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存根write
的调用约定和行为。 Go选择将其行为复制到自己的存根syscall.Write
中。实际的系统调用本身仅具有汇编语言界面。