doxqszx09742 2018-08-29 16:06
浏览 219
已采纳

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

syscall write returns -1 and set errno is a trivial case. I am interested in the status of errno if C write call returning zero or positive. 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.

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.

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?
  2. When and how write call returning 0 and errno is not 0?
  3. What is the status of errno if write call returning positive? Will it be negative?
  4. Is there any other syscall may encounter the same situation?

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:

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.

Indeed Go directly wraps system call 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:

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?

  • 写回答

1条回答 默认 最新

  • dsajkdadsa14222 2018-08-29 16:53
    关注

    With write, there are only two cases to consider:

    1. If it fails, the result is -1 and errno is set.
    2. If it succeeds, the result is 0 or larger and errno is not set.

    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?).

    The reason that write may return 0 is because the input buffer may be empty.

    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.

    All this means is that it's possible for a 0-length write to fail. If it fails, it returns -1 and sets errno. If it succeeds, it returns 0 and does not set 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.

    What is the status of errno if write call returning 0 for a file, a non-blocking socket, or a blocking socket?

    In this case, errno is not set, because write did not fail. This will only happen if the input buffer is zero bytes.

    When and how write call returning 0 and errno is not 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.

    What is the status of errno if write call returning positive? Will it be negative?

    The errno value will not be set. It will have the same value as it did before the write call.

    Is there any other syscall may encounter the same situation?

    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.

    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

    Technically, write is both a system call and a C function (at least on many systems). However, the C function is just a stub which invokes the system call. 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).

    The man page shows the calling conventions and behavior of the C stub, write. Go chooses to copy that behavior in its own stub, syscall.Write. The actual system call itself only has an assembly language interface.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 乘性高斯噪声在深度学习网络中的应用
  • ¥15 运筹学排序问题中的在线排序
  • ¥15 关于docker部署flink集成hadoop的yarn,请教个问题 flink启动yarn-session.sh连不上hadoop,这个整了好几天一直不行,求帮忙看一下怎么解决
  • ¥30 求一段fortran代码用IVF编译运行的结果
  • ¥15 深度学习根据CNN网络模型,搭建BP模型并训练MNIST数据集
  • ¥15 C++ 头文件/宏冲突问题解决
  • ¥15 用comsol模拟大气湍流通过底部加热(温度不同)的腔体
  • ¥50 安卓adb backup备份子用户应用数据失败
  • ¥20 有人能用聚类分析帮我分析一下文本内容嘛
  • ¥30 python代码,帮调试,帮帮忙吧