简体   繁体   English

C写调用和Go syscall.Write之间的区别

[英]Differences between C write call and Go syscall.Write

syscall write returns -1 and set errno is a trivial case. syscall write返回-1,而设置errno情况很简单。 I am interested in the status of errno if C write call returning zero or positive. 如果C write调用返回零或正值,我对errno的状态感兴趣。 The wrapper syscall.Write in Go simply returns err if errno is not zero for any case, which also includes the case of write call returns positive. 如果errno在任何情况下都不为零,则Go中的包装器syscall.Write仅返回err ,这还包括write调用返回正数的情况。

https://github.com/golang/go/blob/3cb64ea39e0d71fe2af554cbf4e99d14bc08d41b/src/syscall/zsyscall_linux_386.go#L1007 https://github.com/golang/go/blob/3cb64ea39e0d71fe2af554cbf4e99d14bc08d41b/src/syscall/zsyscall_linux_386.go#L1007

However, the man page of C write call roughly describes errno may also be set but unspecified if we write zero length buffer without explaining any detail. 但是,如果我们编写零长度缓冲区而没有说明任何细节,则可以粗略地描述errno 的C write调用手册页,也可以设置。

Thus, the following cases seem unclear: 因此,以下情况似乎不清楚:

  1. What is the status of errno if write call returning 0 for a file, a non-blocking socket, or a blocking socket? 如果对文件,非阻塞套接字或阻塞套接字的write调用返回0,则errno的状态是什么?
  2. When and how write call returning 0 and errno is not 0? 什么时候以及如何write调用返回0并且errno不为0?
  3. What is the status of errno if write call returning positive? 如果write调用返回正值, errno的状态如何? Will it be negative? 会否定的?
  4. Is there any other syscall may encounter the same situation? 是否有任何其他syscall可能遇到相同的情况?

I think the above description points to the difference between C write call and Go syscall.Write , which is unclear for developers, here are my thoughts: 我认为以上描述指出了C write调用和Go syscall.Write之间的区别,对于开发人员来说尚不清楚,这是我的想法:

According to the man page, returning zero is clearly defined in C write call for to files and for non-blocking sockets, but it's unclear whether there are non-error conditions for a blocking socket which would result in a write() not blocking, returning 0, and (presumably) possibly succeeding later if retried. 根据手册页,对于文件和非阻塞套接字,在C write调用中明确定义了返回零,但是不清楚阻塞套接字是否存在非错误条件,这会导致write()不阻塞,返回0,并且(大概)在以后重试时可能会成功。

Indeed Go directly wraps system call write . 实际上,Go直接包装了系统调用write However, the following code snippet seems not safe because written equals to zero is a case that may trigger err but we don't want to break the loop: 但是,以下代码段似乎并不安全,因为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
}

Is there any wrong in my suspicion? 我的怀疑有什么不对吗?

With write , there are only two cases to consider: 使用write ,只有两种情况需要考虑:

  1. If it fails, the result is -1 and errno is set. 如果失败,则结果为-1并errno
  2. If it succeeds, the result is 0 or larger and errno is not set. 如果成功,则结果为0或更大,并且未设置errno

There are no other cases to consider, unless you are interested in historical Unix implementations (see: Is a return value of 0 from write(2) in C an error? ). 除非您对历史的Unix实现感兴趣,否则没有其他情况可以考虑(请参阅: C中write(2)的返回值0是否是错误? )。

The reason that write may return 0 is because the input buffer may be empty. write可能返回0的原因是因为输入缓冲区可能为空。

However, the man page of C write call roughly describes errno may also be set but unspecified if we write zero length buffer without explaining any detail. 但是,如果我们编写零长度缓冲区而没有说明任何细节,则可以粗略地描述errno的C write调用的手册页,也可以设置。

All this means is that it's possible for a 0-length write to fail. 这一切都意味着0长度写入可能会失败。 If it fails, it returns -1 and sets errno . 如果失败,则返回-1并设置errno If it succeeds, it returns 0 and does not set errno . 如果成功,则返回0并且不设置errno This is the same behavior for any other write, it's just mentioned in the man page because people may find it surprising that a 0-length write could fail. 手册页中刚刚提到了这与其他任何写入操作相同的行为,因为人们可能会惊讶地发现0长度写入可能会失败。

What is the status of errno if write call returning 0 for a file, a non-blocking socket, or a blocking socket? 如果对文件,非阻塞套接字或阻塞套接字的write调用返回0,则errno的状态是什么?

In this case, errno is not set, because write did not fail. 在这种情况下,未设置errno ,因为write没有失败。 This will only happen if the input buffer is zero bytes. 仅当输入缓冲区为零字节时才会发生这种情况。

When and how write call returning 0 and errno is not 0? 什么时候以及如何write调用返回0并且errno不为0?

This does not happen. 这不会发生。 Either errno is set and the return is -1, or errno is not set and the return is 0 or larger. errno且返回值为-1,或者未设置errno且返回值为0或更大。

What is the status of errno if write call returning positive? 如果write调用返回正值, errno的状态如何? Will it be negative? 会否定的?

The errno value will not be set. errno值将不会设置。 It will have the same value as it did before the write call. 它的值将与write调用之前的值相同。

Is there any other syscall may encounter the same situation? 是否有任何其他syscall可能遇到相同的情况?

In general, system calls will either return an error or they will succeed. 通常,系统调用将返回错误成功。 They won't do some mixture of both. 他们不会将两者混合在一起。 Look at the Return Value section of other man pages and you will see that they are mostly the same as write . 查看其他手册页的“返回值”部分,您将发现它们与write大部分相同。

Code

This code is safe. 此代码是安全的。

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
}

Note that it's a bit redundant, we can just do this: 请注意,这有点多余,我们可以这样做:

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
}

A note about C 关于C的注释

Technically, write is both a system call and a C function (at least on many systems). 从技术上讲, write既是系统调用又是C函数(至少在许多系统上)。 However, the C function is just a stub which invokes the system call. 但是,C函数只是一个调用系统调用的存根。 Go does not call this stub, it invokes the system call directly, which means that C is not involved here (well, not until you get into the kernel). Go不会调用此存根,而是直接调用系统调用,这意味着此处不涉及C(嗯,直到您进入内核为止)。

The man page shows the calling conventions and behavior of the C stub, write . 手册页显示了C存根的调用约定和行为write Go chooses to copy that behavior in its own stub, syscall.Write . Go选择将其行为复制到自己的存根syscall.Write The actual system call itself only has an assembly language interface. 实际的系统调用本身仅具有汇编语言界面。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM