syscall write
返回 -1 并设置 errno
是一个微不足道的案例。如果 C write
调用返回零或正数,我对 errno
的状态感兴趣。如果 errno
在任何情况下都不为零,Go 中的包装器 syscall.Write
只返回 err
,这也包括 write 的情况
调用返回正值。
然而,man page of C write
call粗略地描述了errno
可能如果我们写入零长度缓冲区而不解释任何细节,也可以设置但未指定。
因此,以下情况似乎不清楚:
- 如果
write
调用为文件、非阻塞套接字或阻塞套接字返回 0,errno
的状态是什么? - 何时以及如何
编写
调用返回 0 而errno
不为 0? - 如果
write
调用返回正值,errno
的状态是什么?会是负面的吗? - 是否有其他系统调用可能遇到同样的情况?
我认为上面的描述指出了 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
,只有两种情况需要考虑:
- 如果失败,结果为-1 并设置
errno
。 - 如果成功,则结果为 0 或更大,并且
errno
未设置。
没有其他情况需要考虑,除非您对历史 Unix 实现感兴趣(参见:Is a return value of 0 from write(2) in C an error?)。
write
可能返回 0 的原因是输入缓冲区可能为空。
However, the man page of C
write
call roughly describeserrno
may also be set but unspecified if we write zero length buffer without explaining any detail.
这意味着 0 长度写入有可能失败。如果失败,则返回 -1 并设置 errno
。如果成功,则返回 0 并且不设置 errno
。这与任何其他写入的行为相同,它只是在手册页中提到,因为人们可能会对 0 长度写入可能失败感到惊讶。
What is the status of
errno
ifwrite
call returning 0 for a file, a non-blocking socket, or a blocking socket?
在这种情况下,没有设置errno
,因为write
没有失败。只有当输入缓冲区为零字节时才会发生这种情况。
When and how
write
call returning 0 anderrno
is not 0?
这不会发生。 errno
已设置且返回值为 -1,或者 errno
未设置且返回值为 0 或更大。
What is the status of
errno
ifwrite
call returning positive? Will it be negative?
errno
值不会被设置。它将具有与 write
调用之前相同的值。
Is there any other syscall may encounter the same situation?
一般来说,系统调用要么返回错误,要么成功。他们不会做两者的混合。查看其他手册页的返回值部分,您会发现它们与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
}
关于C的说明
从技术上讲,write
既是系统调用又是 C 函数(至少在许多系统上是这样)。但是,C 函数只是调用系统调用的 stub 。 Go 不调用这个 stub ,它直接调用系统调用,这意味着这里不涉及 C(好吧,直到你进入内核)。
手册页显示了 C stub 的调用约定和行为,write
。 Go 选择在它自己的 stub syscall.Write
中复制该行为。实际的系统调用本身只有一个汇编语言接口(interface)。
关于c - C write call 和 Go syscall.Write 的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52081841/