Golang中谁负责准备argc和argv参数?
Golang中谁负责准备argc和argv参数? 亲爱的朋友们大家好!
我对Go程序的启动过程非常感兴趣。我刚刚复制了适用于386系列处理器和Windows操作系统的Go程序启动函数 _rt0_386 的代码:
// _rt0_386 是使用内部链接时,大多数386系统的通用启动代码。
// 这是普通 -buildmode=exe 程序从内核进入的入口点。栈上保存着参数数量和C风格的argv。
TEXT _rt0_386(SB),NOSPLIT,$8
MOVL 8(SP), AX // argc
LEAL 12(SP), BX // argv
MOVL AX, 0(SP)
MOVL BX, 4(SP)
JMP runtime·rt0_go(SB)
但我就是无法理解为什么此时 8(SP) 包含了一个有效的 argc。在C语言中,我知道 argc 和 argv 是如何被准备的。是 mainCRTStartup 函数准备了 argc 和 argv,然后调用 main(argc, argv)。但对于Go语言,既然 _rt0_386 是入口函数,这意味着没有其他函数调用 _rt0_386,因此此时 argc 和 argv 应该还没有被准备好。
有人能解答我的疑惑吗?谢谢!
更多关于Golang中谁负责准备argc和argv参数?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang中谁负责准备argc和argv参数?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go语言中,argc和argv参数是由操作系统内核准备的,而不是由Go运行时或任何C运行时库准备的。当操作系统加载并执行一个可执行文件时,它会按照特定的调用约定将命令行参数传递给程序的入口点。
对于Windows上的386架构,当内核调用程序入口点时,栈的布局遵循stdcall调用约定。此时栈上已经包含了argc和argv参数,具体布局如下:
SP -> 返回地址(由内核压入)
8(SP) -> argc
12(SP) -> argv
_rt0_386函数作为入口点,直接从栈上获取这些参数。这是因为操作系统内核在加载可执行文件后,会解析命令行,计算参数数量,并将参数指针数组的地址准备好,然后按照调用约定传递给入口点函数。
以下是一个简化的示例,说明内核如何准备参数并调用入口点:
// 内核视角的伪代码
func kernel_start_program() {
// 解析命令行,得到参数列表
args := parse_command_line()
argc := len(args)
argv := prepare_argv_array(args)
// 按照调用约定压栈
push(argv) // 先压入argv
push(argc) // 再压入argc
push(return_address) // 压入返回地址(通常为0或退出例程)
// 跳转到程序入口点
jump_to(_rt0_386)
}
在Go运行时中,_rt0_386只是将参数从栈上加载到寄存器,然后传递给runtime·rt0_go函数进行进一步的初始化。runtime·rt0_go会处理这些参数,初始化Go运行时,并最终调用用户的main.main函数。
// runtime/rt0_386.s中的实际代码
TEXT runtime·rt0_go(SB),NOSPLIT,$0
// 此时栈上布局:
// 0(SP): argc
// 4(SP): argv
// ... 后续初始化代码
因此,argc和argv的准备是由操作系统内核完成的,Go运行时只需在入口点函数中接收这些参数即可。这与C语言程序类似,不同之处在于C语言程序通常通过C运行时库(如mainCRTStartup)进行包装,而Go程序直接使用内核传递的参数。

