据我所知,在CPython的,open()和read()-的API来读取一个文件是用C代码。C代码可能会调用一些C库,该C库知道如何进行系统调用。
像Go这样的语言呢?现在Go本身不是用Go编写的吗?Go会在后台调用C库吗?
是的 go 现在写在 go 中。但是,您不需要 C 来进行系统调用。
需要指出的重要一点是系统调用不是“用 C 编写的”。由于<unistd.h>. 尤其是Linux如何定义这个头文件有点复杂,但是你可以从这个文件中看到大致的思路。系统调用是用名称和编号定义的。read例如,当您调用时,幕后真正发生的是参数设置在适当的寄存器/内存中(linux 需要系统调用号eax),然后是syscall触发中断的指令0x80. 操作系统已经设置了适当的中断处理程序来接收这个中断,操作系统会为那个系统调用做任何需要的事情。因此,您不需要用 C(或与此相关的标准库)编写的东西来进行系统调用。您只需要了解调用 ABI 并知道中断号。
然而,正如@retgits 指出的那样,golang 的方法是利用 libc 已经拥有处理系统调用的所有逻辑这一事实。mksyscall.go是一个 CLI 脚本,它解析这些 libc 文件以提取必要的信息。
如果您编译一个 go 脚本,您实际上可以跟踪系统调用的生命周期,例如:
package main
import (
"syscall"
)
func main() {
var buf []byte
syscall.Read(9, buf)
}
Run Code Online (Sandbox Code Playgroud)
objdump -D在生成的二进制文件上运行。go 运行时相当大,所以最好的办法是找到该main函数,查看它调用的位置syscall.Read,然后从那里搜索偏移量:syscall.Read调用syscall.syscall、syscall.syscall调用runtime.libcCall(从 go ABI 切换到 C ABI 兼容性,以便参数位于操作系统期望 - 您可以在runtime,例如 darwin 中看到这一点),runtime.libcCall调用runtime.asmcgocall等。
为了获得更多乐趣,请使用 gdb 运行该二进制文件并继续介入,直到您遇到系统调用。
简短的回答是“取决于”。
Go可以针对硬件和操作系统的多种组合进行编译,并且在使用它们时,它们都具有如何进行系统调用的不同方法。
例如,Solaris没有提供一组稳定的受支持的syscall,因此它们必须按照供应商的要求通过系统libc。
Windows确实支持一组相当稳定的系统调用,但是它被定义为一组标准DLL提供的C API。这些DLL公开的功能大多是垫片,它们使用单个“按数字进行系统调用”功能,但是这些数字没有记录,并且在内核版本和发行版之间可能有所不同(也许是有意的)。
Linux确实提供了一组稳定且有据可查的编号的系统调用集,因此Go只是直接调用内核。
现在请记住,对于Go来说,“直接调用内核”意味着遵循H / W和OS组合的所谓ABI。例如,在现代Linux上的amd64上,进行系统调用需要用一定的值填充一组CPU寄存器,然后进行一些其他安排,然后发出SYSENTERCPU指令。
在Windows上,您必须使用其本机调用约定(这是stdcall,而不是cdecl)。
sys包负责对底层操作系统的系统调用。根据您使用的操作系统,使用不同的包来生成适当的调用。以下是在 Unix 系统上运行的 Go README 的链接:https://github.com/golang/sys/blob/master/unix/README.md mksyscall.go上的部分,它们是手写的 Go 文件,实现需要特殊处理的系统调用,并输入文件,应该引导您了解它是如何工作的。