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