首页 > 编程语言 >go程序启动过程

go程序启动过程

时间:2023-11-28 13:14:08浏览次数:30  
标签:MOVL runtime 启动 SP 程序 go SB main

go的启动入口函数

对go有开发经验的朋友都知道,main函数不是真正的启动入口,只是go暴露给用户编写的业务的接口。
这点上基本所有的语言都是类似,在main函数调用前,go需要做一系列的准备工作。

go的启动在 runtime/rto XXX.s, xxx是因为平台的差异。不同系统不同芯片都有自己的启动方法。

go runtime包中,给不同平台的定义的启动函数。.s 是汇编的文件与.asm一样。

以Linux为例,探究启动阶段都做了什么

#include "textflag.h"

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

TEXT _rt0_amd64_linux_lib(SB),NOSPLIT,$0
	JMP	_rt0_amd64_lib(SB)

// 上边入口函数 调用的函数定义
TEXT _rt0_amd64(SB),NOSPLIT,$-8
// 两个参数存入了寄存器
MOVQ	0(SP), DI	// argc  
LEAQ	8(SP), SI	// argv
// 跳转到了新的方法
JMP	runtime·rt0_go(SB)

关键代码:

  TEXT runtime·rt0_go(SB),NOSPLIT|NOFRAME|TOPFRAME,$0
	// Copy arguments forward on an even stack.
	// Users of this function jump to it, they don't call it.
	MOVL	0(SP), AX
	MOVL	4(SP), BX
	SUBL	$128, SP		// plenty of scratch
	ANDL	$~15, SP
	MOVL	AX, 120(SP)		// save argc, argv away
	MOVL	BX, 124(SP)
      
	// set default stack bounds.
	// _cgo_init may update stackguard.
    // 初始化 g0协程
	MOVL	$runtime·g0(SB), BP
	LEAL	(-64*1024+104)(SP), BX
	MOVL	BX, g_stackguard0(BP)
	MOVL	BX, g_stackguard1(BP)
	MOVL	BX, (g_stack+stack_lo)(BP)
	MOVL	SP, (g_stack+stack_hi)(BP)
  
    // 中间删除了一些源码 太多判断

	// save m->g0 = g0
	MOVL	DX, m_g0(AX)
	// save g0->m = m0
	MOVL	AX, g_m(DX)

	CALL	runtime·emptyfunc(SB)	// fault if stack check is wrong

	// convention is D is always cleared
	CLD
  
   // 运行时检测
	CALL	runtime·check(SB)

	// saved argc, argv
	MOVL	120(SP), AX
	MOVL	AX, 0(SP)
	MOVL	124(SP), AX
	MOVL	AX, 4(SP)
    // 拷贝参数到 go程序中
	CALL	runtime·args(SB)
   // 初始化系统相关参数,如cpu是几核
	CALL	runtime·osinit(SB)
   // 初始化调度器 
	CALL	runtime·schedinit(SB)
     
    // 新建一个 goroutine,该 goroutine 绑定 runtime.main,放在 P 的本地队列,等待调度
	// create a new goroutine to start program
	PUSHL	$runtime·mainPC(SB)	// entry 
    // go 创建协程底层都是采用newproc 函数 
	CALL	runtime·newproc(SB)
	POPL	AX
    
     // 创建一个 M 这里开始真正调用 runtime·main
	// start this M
	CALL	runtime·mstart(SB)

	CALL	runtime·abort(SB)
	RET

  DATA	runtime·mainPC+0(SB)/8,$runtime·main<ABIInternal>(SB)

到此,系统中有了两个协程goroutine ,一个g0和一个主协程。

往下走看 runtime.main

跳到 proc.go 目录
// 删除了一些代码
// The main goroutine.
func main() {
	mp := getg().m

	// Lock the main goroutine onto this, the main OS thread,
	// during initialization. Most programs won't care, but a few
	// do require certain calls to be made by the main thread.
	// Those can arrange for main.main to run in the main thread
	// by calling runtime.LockOSThread during initialization
	// to preserve the lock.
	lockOSThread()

	// 一些初始化的工作
	doInit(runtime_inittasks) // Must be before defer.

     // 启动垃圾回收器
	gcenable()
    // 删除了一些代码,有判断是否为 cgo
    
  / / Run the initializing tasks. Depending on build mode this
	// list can arrive a few different ways, but it will always
	// contain the init tasks computed by the linker for all the
	// packages in the program (excluding those added at runtime
	// by package plugin).
   // 用户依赖包的init方法,在这个时候执行了。
	for _, m := range activeModules() {
		doInit(m.inittasks)
	}
     
    // 链接到 main包的main函数,开始调用。
	fn := main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
	fn()
    
	if raceenabled {
		runExitHooks(0) // run hooks now, since racefini does not return
		racefini()
	}

	// Make racy client program work: if panicking on
	// another goroutine at the same time as main returns,
	// let the other goroutine finish printing the panic trace.
	// Once it does, it will exit. See issues 3934 and 20018.
   // 进行panic trace的配置
	if runningPanicDefers.Load() != 0 {
		// Running deferred functions should not take long.
		for c := 0; c < 1000; c++ {
			if runningPanicDefers.Load() == 0 {
				break
			}
			Gosched()
		}
	}
	if panicking.Load() != 0 {
		gopark(nil, nil, waitReasonPanicWait, traceBlockForever, 1)
	}
	runExitHooks(0)
}

编译阶段回去link到main函数。
//go:linkname main_main main.main
func main_main()

总结:

前面为了不影响阅读,有些分支的代码,未贴出来,这里来统一总结下:

1. g0是每个go程序的第一个协程,目的是为了调度其他协程

2. 运行时的检测 主要检测内容

   // 运行时检测
	CALL	runtime·check(SB)
  
•检查各种类型的长度
•检查结构体字段的偏移量
•检查 CAS 操作
•检查指针操作
•检查 atomic 原子操作
•检查栈大小是否是 2的幂次

3.调度器的初始化

     // 初始化调度器 
	CALL	runtime·schedinit(SB)

全局栈空间内存分配
堆内存空间的初始化
初始化当前系统线程
加载命令行参数到 os.Args
加载操作系统环境变量
垃圾回收器的参数初始化
算法初始化 (map、hash等)
设置 process 数量

4. 创建了一个主协程

  用于执行 `runtime.main`

5. 初始化了一个 M,用来调度主协程

6.主协程在执行主函数包含了这几个工作

  执行runtime 包中的init 方法
  启动GC 垃圾收集器
  执行用户包依赖的 init 方法
  执行用户主函数 main.main0
  配置了panic trace的收集

标签:MOVL,runtime,启动,SP,程序,go,SB,main
From: https://www.cnblogs.com/studyios/p/17861570.html

相关文章

  • 基于WSAAsyncSelect模型的通信程序设计
    基于WSAAsyncSelect模型的通信程序设计一、问题描述编写Win32程序模拟实现基于WSAAsyncSelect模型的两台计算机之间的通信,要求编程实现服务器端与客户端之间双向数据传递。客户端向服务器端发送“请输出从1到1000内所有的质数”,服务器回应客户端给出结果。二、代码实现①CInit......
  • Java程序员必备技能:Collections工具类深度解析!
    在之前的文章中,我们学习了单列集合的两大接口及其常用的实现类;在这些接口或实现类中,为我们提供了不少的实用的方法。本篇文章我们来介绍一种java开发者为我们提供了一个工具类,让我们更好的来使用集合Collections工具类Collections是一个操作Set,List,Map等的集合工具类它提......
  • odigos 基于ebpf 以及OpenTelemetry 的分布式tracing 解决方案
    按照odigos官方的介绍是不需要进行代码的修改就可以实现方便的跨应用的分布式trace,目前支持java,python,net,go,js等语言目前看官方的介绍,安装是比较简单的(核心基于了k8s),目前官方文档比较清晰可以试用下说明目前开源分布式trace的工具是越来越多了,同时基于ebpf以及OpenTelemetry标......
  • C++获取机器启动至今的时长和机器启动的时间戳
    根据当前时间戳与机器启动至今的时间长度相减,可以精确计算出机器启动时刻的时间戳epochtime代码#include<iostream>#include<stdio.h>#include<time.h>#include<chrono>intmain(){#ifdef__linux //linuxonly std::cout<<"===linuxonlytimeanalysis==......
  • 用java写一个抽奖程序
    需求分析1)实现三个基本功能:登录、注册、抽奖。2)登录:用户输入账号密码进行登录,输入账号后会匹配已注册的用户,若输入用户不存在则退出,密码有三次输入机会,登录成功后主界面会显示已登录用户的账号信息。3)注册:用户首先输入账号名称,系统查询此名称是否存在,如存在则请求用户换一个名称,......
  • QT第2课-GUI程序实例分析
    GUI程序开发概述不同的操作系统GUI开发原理相同不同的操作系统GUISDK不同GUI程序开发原理GUI程序在运行时会创建一个消息队列系统内核将用户的键盘鼠标操作翻译成对应的程序消息程序在运行过程中需要实时处理队列中的消息当队列中没有消息时,程序将处于停滞状态,等待用户操作经典......
  • kore可扩展安全的Web 应用程序框架
    kore是基于c开发的web框架,可以让我们使用c以及python开发webapi,主要的特点是安全以及可扩展主要特性SNI支持http1.1支持websocket支持默认TLS支持可选后台任务内置参数校验基于acme的自动https权限分离设计可选异步pg访问模块热加载worker进程沙箱支持(基......
  • 从前端的角度来梳理微信支付(小程序、H5、JSAPI)的流程
    因业务需要,开发微信支付功能,涉及三种支付方式:JSAPI支付:微信内网页支付,需要开通微信服务号小程序支付:在小程序中支付,需要开通小程序H5支付:在手机浏览器(出微信内网爷)中网页支付使用微信支付的前提必开通微信商户号,要使用到那种的支付方式要前需在商户平台开通(要审核)。支付......
  • django 创建model 并迁移生成表 在创建记录的写法流程
    django创建model并迁移生成表在创建记录的写法流程在Django中,创建一个新的模型并迁移生成表的步骤如下:在你的应用的models.py文件中定义模型。例如,我们创建一个名为Person的模型,它有name和age两个字段:fromdjango.dbimportmodelsclassPerson(models.Model):name=m......
  • 线上微信小程序无法登录问题
    反馈客户反馈无法线上小程序无法登录复现因为本地有为微信开发者工具,所以本地很快复现,找到traceid,查询sls日志,发现有问题日志:###Errorupdatingdatabase.Cause:com.mysql.cj.jdbc.exceptions.CommunicationsException:Communications linkfailure日志指向数据库通讯有......