Go 程序启动流程你了解过吗?

【Golang】 项目启动方法 建议查看项目的 README.md 或文档,通常会有详细的启动说明。检查项目是否需要配置文件(如。等),并确保配置正确。 阅读详情

1:前言

通过之前《Go语言编译链接过程》我们知道Go程序需要经过编译链接成可执行程序才能到指定平台上运行,经过 ‘go build main.go’ 会在比如在windows下是.exe可执行程序,在 linux 平台上是 ELF 格式的可执行文件。

编译完后就行执行可执行程序,执行的时候可执行二进制文件会被操作系统加载起来运行,通常分为以下几个阶段:

  1. 从磁盘上把可执行程序读入内存;
  2. 创建进程和主线程;
  3. 为主线程分配栈空间;
  4. 把由用户在命令行输入的参数拷贝到主线程的栈;
  5. 把主线程放入操作系统的运行队列等待被调度执起来运行

那么可执行程序实际的启动流程是怎么样的呢?

📚 全文字数 : 3k

⏳ 阅读时长 : 5min

📢 关键词 : Go汇编、启动流程

2:Go程序启动流程

1:如何获得Go汇编

很多 Go 语言的开发者都知道我们可以使用下面的命令将 Go 语言的源代码编译成汇编语言,然后通过汇编语言分析程序具体的执行过程。

// 源代码
package main
import "fmt"
func main()  {
 go Nice()
}
func Nice() {
 fmt.Println("hello xiaoxu code")
}

通过命令 go build -gcflags -S main.go  或者  go tool compile -N -l -S main.go 获得go程序汇编

//得到如下汇编代码
...
"".Nice STEXT size=144 args=0x0 locals=0x58
        0x0000 00000 (D:\project\src\Arnold\src\demo\main.go:9) TEXT    "".Nice(SB), ABIInternal, $88-0
        0x0000 00000 (D:\project\src\Arnold\src\demo\main.go:9) MOVQ    TLS, CX
        0x0009 00009 (D:\project\src\Arnold\src\demo\main.go:9) PCDATA  $0, $-2
        0x0009 00009 (D:\project\src\Arnold\src\demo\main.go:9) MOVQ    (CX)(TLS*2), CX
        0x0010 00016 (D:\project\src\Arnold\src\demo\main.go:9) PCDATA  $0, $-1
        0x0010 00016 (D:\project\src\Arnold\src\demo\main.go:9) CMPQ    SP, 16(CX)
        0x0014 00020 (D:\project\src\Arnold\src\demo\main.go:9) PCDATA
...

2:程序入口

Go 程序启动后需要对自身运行时进行初始化,其真正的程序入口由 runtime 包控制,同时针对不同的系统平台,在src/runtime目录下游ret0开头的汇编文件,比如windows下的ret0*_windows_amd64.s,linux系统下是在src/runtime/rt0_linuxamd64.s。*他们都是指向ret_amd64(),程序编译为机器码之后, 依赖特定 CPU 架构的指令集,而操作系统的差异则是直接反应在运行时进行不同的系统级操作上。


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

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

而_rt0amd64()在src/runtime/asm_amd64.s,真实的入口就是runtime·rt0_go。

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

// go程序启动时进行初始化工作
TEXT runtime·rt0_go(SB),NOSPLIT,$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)
#ifdef GOOS_android
 MOVQ	$runtime·tls_g(SB), DX  // arg 3: &tls_g
 // arg 4: TLS base, stored in slot 0 (Android's TLS_SLOT_SELF).
 // Compensate for tls_g (+16).
 MOVQ -16(TLS), CX
#else
 MOVQ	$0, DX // arg 3, 4: not used when using platform's TLS
 MOVQ	$0, CX
#endif
#ifdef GOOS_windows
....
 CALL	runtime·args(SB)
 CALL	runtime·osinit(SB)
 CALL	runtime·schedinit(SB)  //调度器初始化

 // create a new goroutine to start program
 MOVQ	$runtime·mainPC(SB), AX  // entry
 PUSHQ	AX
 PUSHQ	$0   // arg size
 CALL	runtime·newproc(SB)  //创建第一个goroutine,习惯成为main goroutine
 
 // start this M
 CALL	runtime·mstart(SB)  //启动 M 主线程调度上面创建的main goroutine
...

这里只截取了一部分ret_go的代码,其中前面部分包括了系统相关检查,在 #ifdef 和 #endif 之间,后半部分才是核心启动部分,核心部分包括下面这些点:

  1. schedinit:进行各种运行时组件初始化工作,这包括我们的调度器与内存分配器、回收器的初始化
  2. newproc:负责根据主 goroutine(即 main)入口地址创建可被运行时调度的执行单元,这里的main还不是用户的main函数,是 runtime.main mstart
  3. 开始启动调度器的调度循环, 执行队列中 入口方法是 runtime.main 的 G

这些启动的顺序其实在schedinit()函数有注释的,这里就很清楚的说明了启动的调用序列。

// src/runtime/proc.go  schedinit函数所在文件
// The bootstrap sequence is:
//
//	call osinit
//	call schedinit
//	make & queue new G  (其实就是newproc,go关键字创建goroutine就是newproc函数)
//	call runtime·mstart

这些runtime的核心函数其实都是调用src/runtime下对应的函数,比如schedinit就是在 src/runtime/proc.go,接着就是各种初始化。

// 这里的注释就更加清楚的说明了启动的顺序
func schedinit() {
 _g_ := getg()
 (...)

 // 栈、内存分配器、调度器相关初始化
 sched.maxmcount = 10000 // 限制最大系统线程数量
 stackinit()   // 初始化执行栈
 mallocinit()  // 初始化内存分配器
 mcommoninit(_g_.m) // 初始化当前系统线程
 (...)

 gcinit() // 垃圾回收器初始化
 (...)

 // 创建 P
 // 通过 CPU 核心数和 GOMAXPROCS 环境变量确定 P 的数量
 procs := ncpu
 if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
  procs = n
 }
 procresize(procs)
 (...)
}

3:总结

这里从网上盗一张图吧,它把整个调用脉络讲的很清楚。

文末安利一波:

欢迎朋友们关注我的同名公众号📢📢:【小许code】,等你哦!🤣🤣

欢迎点赞 👍、收藏 💙、关注 💡 三连支持一下~🎈

🎈知道的越多,不知道的也越多,我是小许,下期见~🙇💻

linux宝塔面板部署go项目,从go环境安装到项目启动 1.从官网下载go包go1.21.5.linux-amd64.tar.gz,上传到宝塔/www/wwwroot/下,然后在当前路径下打开终端,执行解压命令。10.2 请勿一开始就先配置域名,免得出现启动报错问题,先按照教程配置项目,启动成功在最后添加域名即可,配置完成,就可以在外网直接访问域名,看到页面效果了。5.2 GOPATH:Golang项目的路径,可自定义,例如/golang/blogs-golang。5.1 GOROOT:Golang的安装目录,即/usr/local/go。 阅读详情

相关推荐

宝塔面板部署go项目

即和本地跑项目没太大区别;但是该方法不太适合大型的项目,因为build会占用较大的内存,而服务器内存通常都较小,会有概率失败,而且下载各种环境也是一个弊端,日常项目自然是没有问题的;为了能够更完整的演示docker部署,教程会先演示在本地生成docker镜像并提交到dockerhub,然后在服务器上拉取先前提交的docker镜像生成容器运行;3.事先生成项目的linux版本二进制文件为main,单独将main拉进服务器,并配置启动项,若没有问题可直接运行,直接少了build和下载环境的步骤;

qq_63728673的博客 6969

go项目运行方法

操作前提条件:系统已经安装go 方法1:直接终端运行.go文件 命令:go run hello.go 方法2:编译.go文件后,终端运行.exe文件 编译后会产生一个 .exe编译文件,可直接运行 方法3:golang编辑器中运行 ...

一越王超的博客 1万+

【Golang系列】Golang环境配置和第一个Go程序

对于Go语言的安装包,有两种类型,一种是msi文件,这个文件直接双击打开,然后设置安装目录,一直next即可,还有一个zip等压缩文件格式的安装包,直接解压到你想安装的目录即可。 安装好go语言包后,在命令行窗口运行go version,如果出现具体的版本就表示安装好了,为了配合VsCode等IDE环境,还需要配置环境变量。

未见花闻的博客 3624

(Go基础)Go的运行流程步骤与包的概念

初次上手Go语言!先来了解它的运行流程吧! 在Go中对包的概念又有怎样不同的见解呢?

拔高、提升、创新! 2436

硬件之光实战篇--Boost

当开关管处于关闭状态时,由于电感中的电流在短时间内不能瞬间消失,因此它会产生一个电感电动势,在电容器上加上一个高电压,从而使输出电压变高。--------------------------------------------------------分割线----------------------------------------------------------------在应用中,升压芯片需要根据负载电路的需求,选择合适的电感和电容等元器件,以达到所需要的升压倍数和输出电压。

m0_58664890的博客 2101

智能驾驶感知模块评测实战:如何用回灌测试提升BEV模型准确率(附数据集处理技巧)

本文深入探讨了如何通过诊断型回灌测试系统性地提升智能驾驶BEV感知模型的准确率与鲁棒性。文章详细拆解了从场景切片、时空对齐到多维指标分析的完整评测流水线,并针对BEV模型特有的深度估计、特征融合等环节提供了优化思路,旨在帮助工程师将海量路采数据转化为精准的模型迭代燃料。

weixin_29045585的博客 395

Go中main.main的启动流程:一图流

Go中main.main的启动流程

Voryla的博客 306

Sentaurus TCAD 实战指南:几何、掺杂与物理模型对Ion/Ioff的协同调控

本文详细介绍了Sentaurus TCAD在器件仿真中的实战应用,重点解析了Ion(开态电流)与Ioff(关态电流)的协同优化方法。通过几何结构设计、掺杂工程和物理模型选择三大要素的精细调整,实现器件性能的显著提升。文章结合28nm和7nm工艺案例,展示了如何平衡Ion与Ioff的矛盾关系,为TCAD工程师提供实用指导。

weixin_29159711的博客 135

go runtime debug 小技巧

前言本意是打算研究一下go程序的启动流程,然后就去网上搜索了一下入门教程。结果令我有点沮丧,搜到的几乎所有文章开篇都是通过GDB调试, 然后就是不同平台下的汇编代码。。。这令我很不开心,...

Go中国 653

避坑指南:在Ubuntu 20.04上从零部署IS-Fusion环境(含CUDA 11.1和PyTorch 1.10.1配置)

本文提供了在Ubuntu 20.04系统上从零部署IS-Fusion 3D目标检测环境的详细避坑指南。内容涵盖CUDA 11.1、PyTorch 1.10.1、spconv等核心组件的精确版本安装与兼容性配置,并指导如何准备nuScenes数据集及启动模型训练,旨在帮助研究者和开发者高效搭建稳定的多模态融合算法开发环境。

gitlab7runner的博客 552

PCL:读写pcd点云的两种方式

本文介绍了PCL读写点云的两种方式:PCDReader,PCDWriter;loadPCDFile,savePCDFile.

孙悟空 1万+

golang 中 init func 和 var 谁先执行问题

golang 中有的 init 函数会先 main 函数执行,var 会先 init 函数执行。 /** * @Author: v_bivwei * @Description: * @Date: 2020/7/4 16:44 */ package main import "fmt" func init() { fmt.Println("init func") } func main() { fmt.Println("main func") } /** * @Author: v_bi

卩杉的博客笔记 1734

励磁电感 Lm 选对了,LLC 才真“高效”:半桥 LLC 效率优化

你有没有遇到过这种 LLC:参数一套下来,ZVS 也有了,波形看着也“挺谐振”,但一上功率——,效率就是死活上不去。我说句直白的:LLC 的“高效”,很大一部分其实是在背后控场。Lm 选小了,励磁电流蹭蹭涨,谐振电流 RMS 变大,导通损耗、开关损耗、整流损耗一起上天;Lm 选太大,又可能把 ZVS 的门槛抬高,轻载掉 ZVS,EMI 也开始作妖。

weixin_43199439的博客 435

GoLang之init函数

GoLang之init函数

weixin_52690231的博客 7824

2020.12.25丨CRISPResso2安装教程

一个月前接到一个项目,说是做关于CRISPR的编辑效率个性化分析。本来这个分析以前是没有做过的,不过客户发了参考文献。里面流程和命令行都非常清楚,再加上读研期间CRISPR的项目“中道崩殂”,让我对CRiSPR的技术有些执念,便下决心接下来试试。 整个测序分析使用的工具就是CRISPResso,,下面介绍最快捷的安装方式。 首先,CRISPResso只支持python2.X,不支持3.X你需要下载Anaconda或者miniconda,以Miniconda为例: 安装环境: Linux 64-bi

4634

爆破shadow文件密码脚本(完成版)

由于添加了一个for循环,再稍作修改,让password not found!print(f"Trying password:")显示爆破的密码和过程。将代码中的password="123456"替换成下面代码。line.strip()函数首尾去空格。

m0_56578216的博客 771

Go中init()执行顺序分析

在运行启动时,最后导入的包会最先初始化并调用其init()函数,

FeelTouch Labs 775

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

go 包init初始化顺序

文件结构如下 假设各个go文件都有init函数 执行顺序是 a的init、b的init、c的init ,按文件名排序 单文件内init执行顺序是书写顺序 多个包引用同一个包的话,包init执行一次,即每个包init都执行一次 ...

takujo的博客 214

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

深入剖析 Golang 程序启动流程

理解 Golang 程序启动流程对于深入掌握 Golang 编程至关重要。通过对面试题目的分析,我们明确了面试的考察点,详细阐述了应用程序代码执行顺序和程序启动的底层流程,并展示了如何通过源码来佐证这些流程。希望本文能够帮助读者更好地理解 Golang 程序的启动机制,在学习和面试中取得更好的成绩。同时,鼓励读者深入研究源码,进一步提升自己对 Golang 的理解和应用能力。

m0_57836225的博客 851

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

golang内幕之程序启动流程

文件:startup.go ,代码如下: package main import "fmt" func main() { fmt.Println("startup") } 上面是golang程序的入口函数,即main包中的main函数。但main.main并发golang真正的程序入口,golang存在一个运行时(runtime),与我们的编写的golang代码一起编译、链接成可执行...

QQ1130141391的专栏 2130

Go 程序的启动流程【1/2】

相对而言 ARM 初始化 TLS 的过程是比较简单的,总体就做了一件事:为。

面向对象的笔记 2012

Golang 程序启动原理详解

Golang 程序启动原理步骤以及详解

资深系统架构,深耕高并发研发与 AI 大模型工程化落地。技术栈以 Python (AI/NLP/RAG/Agent) 与 Golang (高并发架构) 为双核心,兼具 PHP 大型应用经验。专注算法落地、复杂系统设计及团队技术赋能 1969

golang程序启动流程详解

golang程序启动流程详解 环境 go1.16.5 linux/amd64 用例 package main import "fmt" func main() { fmt.Println(42) } 编译 -gcflags “-N -l”: 关闭优化和内联,方便调试跟踪 $ go build -gcflags "-N -l" -o hello hello.go gdb跟踪执行流程 $ gdb hello $ source /usr/lib/go/src/runtime/runti

happyAnger6的专栏 2118

GoLang之Go程序启动过程是怎样的(5)

GoLang之Go程序启动过程是怎样的(5)

weixin_52690231的博客 946

详解 Go 程序的启动流程,你知道 g0,m0 是什么吗?

大家好,我是煎鱼。自古应用程序均从 Hello World 开始,你我所写的 Go 语言亦然:import"fmt" funcmain(){ fmt.Printl...

EDDYCJY的博客 5760

golang学习之go程序docker部署

windows 安装wsl2 docker 及 go 程序 docker 部署

weixin_56349119的博客 2842
上一篇: 结合图文一起搞懂MySQL事务、MVCC、ReadView!
下一篇: Redis执行用户命令的过程,你了解过吗!
小许code
博客等级 码龄3年 286粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值