Golang如何进行系统调用?

时间:2019-04-17 20:58:38

标签: go

据我所知,在CPython中,open()read()-读取文件的API用C代码编写。 C代码可能会调用一些C库,该C库知道如何进行系统调用。

那像Golang这样的语言呢? Golang本身现在不是用Golang编写的吗? Golang会在后台调用C库吗?

4 个答案:

答案 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支持代码。