为什么 golang.org/x/sys 包鼓励使用它要替换的 syscall 包?

Shu*_*eng 2 go

我已经阅读了一些syscall用于与底层操作系统(例如 Linux 或 Windows)进行低级交互的Go 代码。

我想使用相同的包进行本机 Windows 开发,但阅读其文档说它已被弃用,有利于golang/x/sys

$ go doc syscall
package syscall // import "syscall"

Package syscall contains an interface to the low-level operating system
primitives.

...

Deprecated: this package is locked down. Callers should use the
corresponding package in the golang.org/x/sys repository instead. That is
also where updates required by new systems or versions should be applied.
See https://golang.org/s/go1.4-syscall for more information.
Run Code Online (Sandbox Code Playgroud)

现在,阅读文档golang/x/sys并检查其代码,它严重依赖并鼓励使用该syscall包:

https://github.com/golang/sys/blob/master/windows/svc/example/beep.go

package main

import (
    "syscall"
)

var (
    beepFunc = syscall.MustLoadDLL("user32.dll").MustFindProc("MessageBeep")
)

func beep() {
    beepFunc.Call(0xffffffff)
}
Run Code Online (Sandbox Code Playgroud)

https://godoc.org/golang.org/x/sys/windows#example-LoadLibrary

...
r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
...
Run Code Online (Sandbox Code Playgroud)

为什么golang/x/sys依赖并鼓励使用它要替换的包?

tor*_*rek 6

免责声明:我对 Go 特别陌生(虽然不是低级操作系统编程)。不过,这里的路径似乎很清楚。

Go,作为一个生态系统——不仅仅是语言本身,还有所有不同的库——试图1是可移植的。但是直接系统调用几乎是不可移植的。所以这里会自动产生一些张力。

为了做任何有用的事情,Go 运行时需要来自操作系统的各种服务,例如创建操作系统级线程、发送和接收信号、打开文件和网络连接等。许多这样的操作就可以了,并且已经从抽象出来它是如何在操作系统上做了A,B,和C大多数或所有操作系统支持的通用概念。这些抽象建立在各种操作系统的实际机制之上。

他们甚至可以在内部分层执行此操作。一看围棋源的os,例如,表演file.gofile_plan9.gofile_posix.gofile_unix.go,和file_windows.go源文件。顶部file_posix.go显示一个+build指令:

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows
Run Code Online (Sandbox Code Playgroud)

显然,这段代码本身并不是完全可移植的,但它为 实现的例程(osos.File抽象包装)足以满足所有符合 POSIX 的系统的要求。例如,这减少了必须进入 Unix/Linux 特定files_unix.go文件的代码量。

如果操作系统级别的操作可以包装成更抽象、更便携的操作,那么各种内置的 Go 包就可以做到这一点。例如,您不需要知道是否有不同的系统调用来打开设备文件与文本文件与二进制文件,或者长路径名与短路径名:您只需调用os.Createoros.Open并且它会执行任何操作幕后必要的工作。

这整个想法并不适用于系统调用。用于创建新 UID 命名空间的 Linux 系统调用没有 Windows 等效项。2 WindowsWaitForMultipleObjects系统调用在 Linux 上没有真正的等价物。stat/lstat 调用的底层细节因系统而异,依此类推。

在 Go 的早期版本中,有人试图用syscall包来掩盖这一点。但是你引用的链接——https : //golang.org/s/go1.4-syscall——将这种尝试描述为,如果没有失败,至少是过度拉伸。“问题”部分的最后一个词是“问题”。

同一个链接上的提议说,从syscallGo 1.4 开始,该包将被冻结(或大部分被冻结):不要将新功能放入其中。但是,该功能它足以实现新的,实验性golang.org/x/sys/*的软件包,或至少其中的一部分。syscall如果实验性的新软件包满足了实验性新软件包的需要,那么借用现有的、正式弃用的软件包并没有什么坏处。

里面的东西golang.org/x/是实验性的:随意使用它们,但请注意,与标准包中的东西不同,版本更新之间没有兼容性承诺。因此,要回答您问题的最后一行:

为什么golang/x/sys依赖 [on] 并鼓励使用它要替换的包?

依赖, syscall因为那很好。但它根本不“鼓励使用” syscall。它只是在足够的时候使用它。如果这变得不够,无论出于何种原因,它将停止依赖它。

回答一个你没有问过的问题(但我问过):假设你想要stat一个文件的Unix 特定信息,比如它的 inode 号。你有一个选择:

info, err := os.Stat(path) // or os.Lstat(path), etc
if err != nil { ... handle error ... }
raw, ok := info.Sys().(*syscall.Stat_t)
if !ok { ... do whatever is appropriate ... }
inodeNumber := raw.Ino
Run Code Online (Sandbox Code Playgroud)

或者:

var info unix.Stat
err := unix.Stat(path, &info) // or unix.Lstat, etc
if err != nil { ... handle error ... }
inodeNumber := unix.Ino
Run Code Online (Sandbox Code Playgroud)

第一个代码块的优点是您可以获得有关文件的所有其他(可移植)信息——例如,它的模式、大小和时间戳。你可能会,也可能没有得到 inode 编号;该!ok案例告诉你,你是否做到了。这里的主要缺点是执行此操作需要更多代码。

第二个代码块的优点是它说明了你的意思。您要么从stat通话中获得所有信息,要么一无所有。缺点很明显:

  • 它只适用于 Unix-ish 系统,并且
  • 它使用一个实验包,其行为可能会改变。

所以这取决于你,其中哪一个对你更重要。


1要么这是一个比喻,要么我只是将其拟人化了。有一条古老的规则:不要将计算机拟人化,他们讨厌那样!

2 Linux UID 命名空间从容器内的 UID 映射到容器外的 UID。也就是说,在容器内部,一个文件可能归 UID 1234 所有。如果该文件位于同样挂载在容器外部的文件系统中,则该文件可能归不同的所有者所有,可能是 5678。更改任一所有者的所有权容器的“侧”在该侧的命名空间中进行更改;作为通过命名空间映射映射或反向映射 ID 的结果,更改显示在另一端。

(例如,同样的技巧也适用于 NFS UID 映射。上面的 Docker 容器示例只是一种用途,但可能是当今最引人注目的一种。)