阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Go实现启动参数加载

265次阅读
没有评论

共计 3177 个字符,预计需要花费 8 分钟才能阅读完成。

导读 刚学 Go 的同学一定思考过 Go 程序的启动过程,关于这个问题可以看饶大的文章 Go 程序是怎样跑起来的。今天我们将问题缩小,来学习 Go 程序是怎么加载启动参数,以及如何进行参数解析。

Go 实现启动参数加载

C 参数解析

学习过 C 语言的童鞋,一定对 argc 和 argv 不会陌生。

C 程序总是从主函数 main 开始执行的,而在带参数的主函数中,依照惯例,会使用 argc 和 argv 的命名作为主函数参数。

其中,argc (argument count)代表的是命令行参数个数,argv(argument value) 是用来存放指向参数的指针数组。

#include  
 
int main(int argc, char *argv[]) 
{printf("argc = %d\n",argc); 
 printf("argv[0] = %s, argv[1] = %s, argv[2] = %s \n", argv[0], argv[1], argv[2]); 
 return 0; 
} 

编译执行以上 C 代码,得到输出如下

$ gcc c_main.c -o main 
$ ./main foo bar sss ddd 
argc = 5 
argv[0] = ./main, argv[1] = foo, argv[2] = bar

那在 Go 语言中,又该如何获取命令行参数呢?

os.Args 加载

同 C 一样,Go 程序也是从 main 主函数开始 (用户层) 执行,但主函数中并没有定义 argc 和 argv。

我们可以通过 os.Args 函数,获取命令行参数。

package main 
 
import ( 
 "fmt" 
 "os" 
) 
 
func main() { 
 for i, v := range os.Args {fmt.Printf("arg[%d]: %v\n", i, v) 
 } 
}

编译执行 Go 函数

$ go build main.go 
 $ ./main foo bar sss ddd 
arg[0]: ./main 
arg[1]: foo 
arg[2]: bar 
arg[3]: sss 
arg[4]: ddd

同 C 一样,第一个参数也是代表可执行文件。

加载实现

下文我们需要展示一些 Go 汇编代码,为了方便读者理解,先通过两图了解 Go 汇编语言对 CPU 的重新抽象。

X86/AMD64 架构

Go 实现启动参数加载

Go 伪寄存器

Go 实现启动参数加载

Go 汇编为了简化汇编代码的编写,引入了 PC、FP、SP、SB 四个伪寄存器。

四个伪寄存器加上其它的通用寄存器就是 Go 汇编语言对 CPU 的重新抽象。当然,该抽象的结构也适用于其它非 X86 类型的体系结构。

回到正题,命令行参数的解析过程是程序启动中的一部分内容。

以 linux amd64 系统为例,Go 程序的执行入口位于 runtime/rt0_linux_amd64.s。

TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8 
    JMP _rt0_amd64(SB)

_rt0_amd64 函数实现于 runtime/asm_amd64.s

TEXT _rt0_amd64(SB),NOSPLIT,$-8 
    MOVQ    0(SP), DI   // argc 
    LEAQ    8(SP), SI   // argv 
    JMP runtime·rt0_go(SB)

看到 argc 和 argv 的身影了吗? 在这里,它们从栈内存分别被加载到了 DI、SI 寄存器。

rt0_go 函数完成了 runtime 的所有初始化工作,但我们这里仅关注 argc 和 argv 的处理过程。

TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0 
    // copy arguments forward on an even stack 
    MOVQ    DI, AX      // argc 
    MOVQ    SI, BX      // argv 
    SUBQ    $(4*8+7), SP        // 2args 2auto 
    ANDQ    $~15, SP 
    MOVQ    AX, 16(SP) 
    MOVQ    BX, 24(SP) 
    ... 
    MOVL    16(SP), AX      // copy argc 
    MOVL    AX, 0(SP) 
    MOVQ    24(SP), AX      // copy argv 
    MOVQ    AX, 8(SP) 
    CALL    runtime·args(SB) 
    CALL    runtime·osinit(SB) 
    CALL    runtime·schedinit(SB) 
    ...

经过一系列操作之后,argc 和 argv 又被折腾回了栈内存 0(SP)和 8(SP) 中。

args 函数位于 runtime/runtime1.go 中

var ( 
 argc int32 
 argv **byte 
) 
 
func args(c int32, v **byte) { 
 argc = c 
 argv = v 
 sysargs(c, v) 
}

在这里,argc 和 argv 分别被保存至变量 runtime.argc 和 runtime.argv。

在 rt0_go 函数中调用执行完 args 函数后,还会执行 schedinit。

func schedinit() { 
  ... 
 goargs() 
 ...

goargs 实现于 runtime/runtime1.go

var argslice []string 
 
func goargs() { 
 if GOOS == "windows" {return} 
 argslice = make([]string, argc) 
 for i := int32(0); i 

该函数的目的是,将指向栈内存的命令行参数字符串指针,封装成 Go 的 string 类型,最终保存于 runtime.argslice。

这里有个知识点,Go 是如何将 C 字符串封装成 Go string 类型的呢? 答案就在以下代码。

func gostringnocopy(str *byte) string {ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)} 
 s := *(*string)(unsafe.Pointer(&ss)) 
 return s 
} 
 
func argv_index(argv **byte, i int32) *byte {return *(**byte)(add(unsafe.Pointer(argv), uintptr(i)*sys.PtrSize)) 
} 
 
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {return unsafe.Pointer(uintptr(p) + x) 
}

此时,Go 已经将 argc 和 argv 的信息保存至 runtime.argslice 中,那聪明的你一定能猜到 os.Args 方法就是读取的该 slice。

在 os/proc.go 中,是它的实现

var Args []string 
 
func init() { 
 if runtime.GOOS == "windows" { 
  // Initialized in exec_windows.go. 
  return 
 } 
 Args = runtime_args()} 
 
func runtime_args() []string // in package runtime

而 runtime_args 方法的实现是位于 runtime/runtime.go 中的 os_runtime_args 函数

//go:linkname os_runtime_args os.runtime_args 
func os_runtime_args() []string {return append([]string{}, argslice...) }

在这里实现了 runtime.argslice 的拷贝。至此,os.Args 方法最终成功加载了命令行参数 argv 信息。

总结

本文我们介绍了 Go 可以利用 os.Args 解析程序启动时的命令行参数,并学习了它的实现过程。

在加载实现的源码学习中,我们发现如果从一个点出发,去追溯它的实现原理,这个过程并不复杂,希望童鞋们不要惧怕研究源码。

os.Args 方法将命令行参数存储在字符串切片中,通过遍历即可提取它们。但在实际开发中我们一般不会直接使用 os.Args 方法,因为 Go 为我们提供了一个更好用的 flag 包。但鉴于篇幅原因,该部分的内容以后再写了。

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-25发表,共计3177字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19351
评论数
4
阅读量
7979230
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
你的云服务器到底有多强?宝塔跑分告诉你

你的云服务器到底有多强?宝塔跑分告诉你

你的云服务器到底有多强?宝塔跑分告诉你 为什么要用宝塔跑分? 宝塔跑分其实就是对 CPU、内存、磁盘、IO 做...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
星哥带你玩飞牛NAS-5:飞牛NAS中的Docker功能介绍

星哥带你玩飞牛NAS-5:飞牛NAS中的Docker功能介绍

星哥带你玩飞牛 NAS-5:飞牛 NAS 中的 Docker 功能介绍 大家好,我是星哥,今天给大家带来如何在...
仅2MB大小!开源硬件监控工具:Win11 无缝适配,CPU、GPU、网速全维度掌控

仅2MB大小!开源硬件监控工具:Win11 无缝适配,CPU、GPU、网速全维度掌控

还在忍受动辄数百兆的“全家桶”监控软件?后台偷占资源、界面杂乱冗余,想查个 CPU 温度都要层层点选? 今天给...
告别Notion焦虑!这款全平台开源加密笔记神器,让你的隐私真正“上锁”

告别Notion焦虑!这款全平台开源加密笔记神器,让你的隐私真正“上锁”

  告别 Notion 焦虑!这款全平台开源加密笔记神器,让你的隐私真正“上锁” 引言 在数字笔记工...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
多服务器管理神器 Nexterm 横空出世!NAS/Win/Linux 通吃,SSH/VNC/RDP 一站式搞定

多服务器管理神器 Nexterm 横空出世!NAS/Win/Linux 通吃,SSH/VNC/RDP 一站式搞定

多服务器管理神器 Nexterm 横空出世!NAS/Win/Linux 通吃,SSH/VNC/RDP 一站式搞...
150元打造低成本NAS小钢炮,捡一块3865U工控板

150元打造低成本NAS小钢炮,捡一块3865U工控板

150 元打造低成本 NAS 小钢炮,捡一块 3865U 工控板 一块二手的熊猫 B3 工控板 3865U,搭...
把小米云笔记搬回家:飞牛 NAS 一键部署,小米云笔记自动同步到本地

把小米云笔记搬回家:飞牛 NAS 一键部署,小米云笔记自动同步到本地

把小米云笔记搬回家:飞牛 NAS 一键部署,小米云笔记自动同步到本地 大家好,我是星哥,今天教大家在飞牛 NA...
星哥带你玩飞牛NAS-14:解锁公网自由!Lucky功能工具安装使用保姆级教程

星哥带你玩飞牛NAS-14:解锁公网自由!Lucky功能工具安装使用保姆级教程

星哥带你玩飞牛 NAS-14:解锁公网自由!Lucky 功能工具安装使用保姆级教程 作为 NAS 玩家,咱们最...
开发者福利:免费 .frii.site 子域名,一分钟申请即用

开发者福利:免费 .frii.site 子域名,一分钟申请即用

  开发者福利:免费 .frii.site 子域名,一分钟申请即用 前言 在学习 Web 开发、部署...