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

时间:2020-02-02 16:53:16

标签: go

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

我想使用相同的程序包进行本机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.

现在,阅读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)
}

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

...
r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
...

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

1 个答案:

答案 0 :(得分:4)

免责声明:我是Go语言的新手(尽管不是低级OS编程的人)。不过,这里的路径似乎很清楚。

作为一个生态系统,不仅是语言本身,而且还包括所有各种库,请尝试 1 进行移植。但是直接系统调用几乎是可移植的。因此,这里会自动出现张力。

为了做任何有用的事情,Go运行时需要操作系统提供的 各种服务,例如创建OS级线程,发送和接收信号,打开文件和网络连接等等。 。从如何在操作系统A,B和C上完成操作,到现在,这些操作中的许多操作已被抽象化,成为大多数或所有OS支持的通用概念 。这些抽象基于各种操作系统中的实际机制。

他们甚至可以在内部分层执行此操作。例如,查看Go source for the os package会显示file.gofile_plan9.gofile_posix.gofile_unix.gofile_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

很明显,此代码本身不是 完全可移植的,但是它为os实现的例程由os.File抽象包装,对于所有符合POSIX的系统都足够。例如,这减少了必须在特定于Unix / Linux的files_unix.go文件中进行处理的代码量。

在某种程度上,可以将操作系统级别的操作包装为更抽象,更可移植的操作,然后,各种内置的Go软件包即可执行此操作。您不需要知道是否存在用于打开设备文件,文本文件,二进制文件的不同系统调用,或者长路径名与短路径名的打开:您只需调用os.Createos.Open,它会在幕后进行任何必要的工作。

这整个想法与系统调用无关。创建新的UID名称空间的Linux系统调用没有Windows等效项。 2 Windows WaitForMultipleObjects系统调用在Linux上没有等效项。 stat / lstat调用的低级详细信息因一个系统而异,依此类推。

在Go的早期版本中,曾尝试使用syscall软件包来对此进行书写。但是您引用的链接https://golang.org/s/go1.4-syscall将这种尝试描述为(如果没有失败的话)至少是过度拉伸的话。 “问题”部分的最后一个单词是“问题”。

同一链接上的建议说syscall软件包从Go 1.4开始将被冻结(或大部分被冻结):不要在其中添加新功能。但是中的功能足以实现新的,实验性的golang.org/x/sys/*包或至少其中一些功能。如果这样做可以满足实验性新程序包的需要,那么借用现有的,已正式弃用的syscall程序包对实验程序包没有任何危害。

golang.org/x/中的内容是实验性的:可以随意使用它们,但是请注意,与标准软件包中的内容不同,版本更新中没有兼容性保证。因此,要回答您问题的最后一行:

为什么golang/x/sys依靠[鼓励]并鼓励使用要替换的软件包?

依赖 syscall,因为这很好。不过,它根本不“鼓励使用” syscall。只要足够,它就会使用。如果由于任何原因而变得不够用,它将停止依赖它。

回答一个您没有问过的问题(但是我回答了):假设您想要有关文件的特定于Unix的stat信息,例如其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

或:

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

第一段代码的优点是,您可以获得有关文件的所有 other (便携式)信息,例如,文件的模式,大小和时间戳。您也许知道,也许没有得到索引节点号。 !ok案例告诉您是否这样做。这里的主要缺点是,这样做需要更多的代码。

第二个代码块的优点是它可以说出您的意思。您可以从stat调用中获取所有信息,也可以不获取任何信息。缺点很明显:

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

因此,这取决于您,其中哪个更重要。


1 或者这是一个隐喻,或者我只是将其拟人化了。有一个古老的规则:不要对计算机进行拟人化,他们讨厌那样!

2 Linux UID名称空间从容器内的UID映射到容器外的UID。也就是说,在容器内部,文件可能归UID 1234拥有。如果该文件位于也安装在容器外部的文件系统中,则该文件可以由另一个所有者(也许是5678)拥有。容器的“一侧”在该侧的名称空间中进行了更改;更改是通过名称空间映射对ID进行映射或反向映射的结果。

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