iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Go程序的启动流程是什么
  • 559
分享到

Go程序的启动流程是什么

2023-06-15 14:06:29 559人浏览 泡泡鱼
摘要

这篇文章主要讲解了“Go程序的启动流程是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Go程序的启动流程是什么”吧!Go 引导阶段查找入口首先编译上文提到的示例程序:$ GOF

这篇文章主要讲解了“Go程序的启动流程是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Go程序的启动流程是什么”吧!

Go 引导阶段

查找入口

首先编译上文提到的示例程序:

$ GOFLAGS="-ldflags=-compressdwarf=false" go build

在命令中指定了 GOFLAGS 参数,这是因为在 Go1.11 起,为了减少二进制文件大小,调试信息会被压缩。导致在 MacOS 上使用 gdb  时无法理解压缩的 DWARF 的含义是什么(而我恰恰就是用的 MacOS)。

因此需要在本次调试中将其关闭,再使用 gdb 进行调试,以此达到观察的目的:

$ gdb awesomeProject  (gdb) info files Symbols from "/Users/eddycjy/go-application/awesomeProject/awesomeProject". Local exec file:  `/Users/eddycjy/go-application/awesomeProject/awesomeProject', file type mach-o-x86-64.  Entry point: 0x1063c80  0x0000000001001000 - 0x00000000010a6aca is .text  ... (gdb) b *0x1063c80 Breakpoint 1 at 0x1063c80: file /usr/local/Cellar/go/1.15/libexec/src/runtime/rt0_darwin_amd64.s, line 8.

通过 Entry point 的调试,可看到真正的程序入口在 runtime 包中,不同的计算机架构指向不同。例如:

  • MacOS 在 src/runtime/rt0_darwin_amd64.s。

  • linux 在 src/runtime/rt0_linux_amd64.s。

其最终指向了 rt0_darwin_amd64.s 文件,这个文件名称非常的直观:

Breakpoint 1 at 0x1063c80: file  /usr/local/Cellar/go/1.15/libexec/src/runtime/rt0_darwin_amd64.s, line 8.

rt0 代表 runtime0 的缩写,指代运行时的创世,超级奶爸:

  • darwin 代表目标操作系统(GOOS)。

  • amd64 代表目标操作系统架构(GOHOSTARCH)。

同时 Go 语言还支持更多的目标系统架构,例如:AMD64、AMR、MIPS、WASM 等:

Go程序的启动流程是什么

源码目录

若有兴趣可到 src/runtime 目录下进一步查看,这里就不一一介绍了。

入口方法

在 rt0_linux_amd64.s 文件中,可发现 _rt0_amd64_darwin JMP 跳转到了 _rt0_amd64 方法:

TEXT _rt0_amd64_darwin(SB),NOSPLIT,$-8  JMP _rt0_amd64(SB) ...

紧接着又跳转到 runtime·rt0_go 方法:

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

该方法将程序输入的 argc 和 argv 从内存移动到寄存器中。

栈指针(SP)的前两个值分别是 argc 和 argv,其对应参数的数量和具体各参数的值。

开启主线

程序参数准备就绪后,正式初始化的方法落在 runtime·rt0_go 方法中:

TEXT runtime·rt0_go(SB),NOSPLIT,$0  ...  CALL runtime·check(SB)  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)   // create a new goroutine to start program  MOVQ $runtime·mainPC(SB), AX  // entry  PUSHQ AX  PUSHQ $0   // arg size  CALL runtime·newproc(SB)  POPQ AX  POPQ AX   // start this M  CALL runtime·mstart(SB)  ...
  • runtime.check:运行时类型检查,主要是校验编译器的翻译工作是否正确,是否有 “坑”。基本代码均为检查 int8 在 unsafe.Sizeof  方法下是否等于 1 这类动作。

  • runtime.args:系统参数传递,主要是将系统参数转换传递给程序使用。

  • runtime.osinit:系统基本参数设置,主要是获取 CPU 核心数和内存物理页大小。

  • runtime.schedinit:进行各种运行时组件的初始化,包含调度器、内存分配器、堆、栈、GC 等一大堆初始化工作。会进行 p 的初始化,并将 m0  和某一个 p 进行绑定。

  • runtime.main:主要工作是运行 main goroutine,虽然在runtime·rt0_go  中指向的是$runtime·mainPC,但实质指向的是 runtime.main。

  • runtime.newproc:创建一个新的 goroutine,且绑定 runtime.main 方法(也就是应用程序中的入口 main  方法)。并将其放入 m0 绑定的p的本地队列中去,以便后续调度。

  • runtime.mstart:启动 m,调度器开始进行循环调度。

  • 在 runtime·rt0_go 方法中,其主要是完成各类运行时的检查,系统参数设置和获取,并进行大量的 Go 基础组件初始化。

初始化完毕后进行主协程(main goroutine)的运行,并放入等待队列(GMP 模型),最后调度器开始进行循环调度。

小结

根据上述源码剖析,可以得出如下 Go 应用程序引导的流程图:

Go程序的启动流程是什么

Go 程序引导过程

在 Go 语言中,实际的运行入口并不是用户日常所写的 main func,更不是 runtime.main 方法,而是从 rt0_*_amd64.s  开始,最终再一路 JMP 到 runtime·rt0_go 里去,再在该方法里完成一系列 Go 自身所需要完成的绝大部分初始化动作。

其中整体包括:

  • 运行时类型检查、系统参数传递、CPU 核数获取及设置、运行时组件的初始化(调度器、内存分配器、堆、栈、GC 等)。

  • 运行 main goroutine。

  • 运行相应的 GMP 等大量缺省行为。

  • 涉及到调度器相关的大量知识。

后续将会继续剖析将进一步剖析 runtime·rt0_go 里的爱与恨,尤其像是 runtime.main、runtime.schedinit  等调度方法,都有非常大的学习价值,有兴趣的小伙伴可以持续关注。

Go 调度器初始化

知道了 Go 程序是怎么引导起来的之后,我们需要了解 Go Runtime 中调度器是怎么流转的。

runtime.mstart

这里主要关注 runtime.mstart 方法:

func mstart() {  // 获取 g0  _g_ := getg()   // 确定栈边界  osStack := _g_.stack.lo == 0  if osStack {   size := _g_.stack.hi   if size == 0 {    size = 8192 * sys.StackGuardMultiplier   }   _g_.stack.hi = uintptr(noescape(unsafe.Pointer(&size)))   _g_.stack.lo = _g_.stack.hi - size + 1024  }  _g_.stackguard0 = _g_.stack.lo + _StackGuard  _g_.stackguard1 = _g_.stackguard0      // 启动 m,进行调度器循环调度  mstart1()   // 退出线程  if mStackIsSystemAllocated() {   osStack = true  }  mexit(osStack) }
  • 调用 getg 方法获取 GMP 模型中的 g,此处获取的是 g0。

  • 通过检查 g 的执行栈 _g_.stack 的边界(堆栈的边界正好是 lo, hi)来确定是否为系统栈。若是,则根据系统栈初始化 g  执行栈的边界。

  • 调用 mstart1 方法启动系统线程 m,进行调度器循环调度。

  • 调用 mexit 方法退出系统线程 m。

runtime.mstart1

这么看来其实质逻辑在 mstart1 方法,我们继续往下剖析:

func mstart1() {  // 获取 g,并判断是否为 g0  _g_ := getg()  if _g_ != _g_.m.g0 {   throw("bad runtime·mstart")  }   // 初始化 m 并记录调用方 pc、sp  save(getcallerpc(), getcallersp())  asminit()  minit()   // 设置信号 handler  if _g_.m == &m0 {   mstartm0()  }  // 运行启动函数  if fn := _g_.m.mstartfn; fn != nil {   fn()  }   if _g_.m != &m0 {   acquirep(_g_.m.nextp.ptr())   _g_.m.nextp = 0  }  schedule() }
  • 调用 getg 方法获取 g。并且通过前面绑定的 _g_.m.g0 判断所获取的 g 是否 g0。若不是,则直接抛出致命错误。因为调度器仅在 g0  上运行。

  • 调用 minit 方法初始化 m,并记录调用方的 PC、SP,便于后续 schedule 阶段时的复用。

  • 若确定当前的 g 所绑定的 m 是 m0,则调用 mstartm0 方法,设置信号 handler。该动作必须在 minit 方法之后,这样 minit  方法可以提前准备好线程,以便能够处理信号。

  • 若当前 g 所绑定的 m 有启动函数,则运行。否则跳过。

  • 若当前 g 所绑定的 m 不是 m0,则需要调用 acquirep 方法获取并绑定 p,也就是 m 与 p 绑定。

  • 调用 schedule 方法进行正式调度。

忙活了一大圈,终于进入到开题的主菜了,原来潜伏的很深的 schedule 方法才是真正做调度的方法,其他都是前置处理和准备数据。

由于篇幅问题,schedule 方法会放到下篇再继续剖析,我们先聚焦本篇的一些细节点。

问题深剖

不过到这里篇幅也已经比较长了,积累了不少问题。我们针对在 Runtime 中出镜率最高的两个元素进行剖析:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. m0 是什么,作用是?

  3. g0 是什么,作用是?

m0

m0 是 Go Runtime 所创建的第一个系统线程,一个 Go 进程只有一个 m0,也叫主线程。

从多个方面来看:

  • 数据结构:m0 和其他创建的 m 没有任何区别。

  • 创建过程:m0 是进程在启动时应该汇编直接复制给 m0 的,其他后续的 m 则都是 Go Runtime 内自行创建的。

  • 变量声明:m0 和常规 m 一样,m0 的定义就是 var m0 m,没什么特别之处。

g0

  • g 一般分为三种,分别是:

  • 执行用户任务的叫做 g。

  • 执行 runtime.main 的 main goroutine。

执行调度任务的叫 g0。。

g0 比较特殊,每一个 m 都只有一个 g0(仅此只有一个 g0),且每个 m 都只会绑定一个 g0。在 g0  的赋值上也是通过汇编赋值的,其余后续所创建的都是常规的 g。

从多个方面来看:

数据结构:g0 和其他创建的 g 在数据结构上是一样的,但是存在栈的差别。在 g0 上的栈分配的是系统栈,在 Linux 上栈大小默认固定  8MB,不能扩缩容。而常规的 g 起始只有 2KB,可扩容。

运行状态:g0 和常规的 g 不一样,没有那么多种运行状态,也不会被调度程序抢占,调度本身就是在 g0 上运行的。

变量声明:g0 和常规 g,g0 的定义就是 var g0 g,没什么特别之处。

感谢各位的阅读,以上就是“Go程序的启动流程是什么”的内容了,经过本文的学习后,相信大家对Go程序的启动流程是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: Go程序的启动流程是什么

本文链接: https://www.lsjlt.com/news/280599.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Go程序的启动流程是什么
    这篇文章主要讲解了“Go程序的启动流程是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Go程序的启动流程是什么”吧!Go 引导阶段查找入口首先编译上文提到的示例程序:$ GOF...
    99+
    2023-06-15
  • Android应用程序的启动流程是什么
    本篇内容介绍了“Android应用程序的启动流程是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!应用进程的启动流程本文基于Android...
    99+
    2023-07-05
  • android启动流程是什么
    Android启动流程是指从手机开机到系统完全启动的过程。具体的流程如下:1. 电源按下:当用户按下电源键时,电源管理芯片会向处理器...
    99+
    2023-10-11
    android
  • springboot启动流程是什么
    Spring Boot 启动流程如下:1. 初始化应用程序上下文:Spring Boot 应用程序启动时,首先会创建一个 Sprin...
    99+
    2023-05-17
    springboot启动流程 springboot
  • activity启动流程是什么
    Activity启动流程是指在Android应用中启动Activity的一系列操作。它包括以下步骤:1. 调用startActivi...
    99+
    2023-09-11
    activity
  • go微服务PolarisMesh服务端启动流程是什么
    这篇文章主要介绍“go微服务PolarisMesh服务端启动流程是什么”,在日常操作中,相信很多人在go微服务PolarisMesh服务端启动流程是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”go微服务...
    99+
    2023-07-05
  • android launcher启动流程是什么
    Android Launcher的启动流程如下:1. 用户点击设备上的Home按钮或者通过其他方式启动Launcher应用。2. 系...
    99+
    2023-10-20
    android
  • android activity启动流程是什么
    Android Activity的启动流程如下:1. 调用`startActivity()`方法或者`startActivityFo...
    99+
    2023-08-08
    android activity
  • RocketMQ broker启动流程是什么
    这篇文章主要介绍“RocketMQ broker启动流程是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“RocketMQ broker启动流程是什么”文章能帮助大家解决问题。1. 启动入口本系列...
    99+
    2023-07-05
  • 启动CentOS系统的流程是什么
    本篇内容主要讲解“启动CentOS系统的流程是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“启动CentOS系统的流程是什么”吧!当我们按下开机键后,系统背后的秘密我们是否了解呢?这里,我带...
    99+
    2023-06-10
  • Linux开机启动的流程是什么
    这篇文章主要讲解了“Linux开机启动的流程是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Linux开机启动的流程是什么”吧!Linux开机分为以下6个步骤,分别是BIOS, MBR,...
    99+
    2023-06-27
  • SpringBoot中WEB的启动流程是什么
    这篇文章主要介绍“SpringBoot中WEB的启动流程是什么”,在日常操作中,相信很多人在SpringBoot中WEB的启动流程是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”SpringBoot中WE...
    99+
    2023-06-29
  • Spring容器启动流程是什么
    本篇内容介绍了“Spring容器启动流程是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!源码解析考虑到直接看源码是一个非常枯燥无味的过程...
    99+
    2023-06-15
  • Android framework ATMS启动流程是什么
    这篇文章主要介绍“Android framework ATMS启动流程是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Android framework ...
    99+
    2023-07-05
  • Android广播Broadcast的启动流程是什么
    这篇“Android广播Broadcast的启动流程是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Android广播B...
    99+
    2023-07-05
  • Android Service启动绑定流程是什么
    这篇文章主要介绍了Android Service启动绑定流程是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Android Service启动绑定流程是什么文章都会有所收获,下面我们一起...
    99+
    2023-07-05
  • Linux系统启动的引导流程是什么
    本篇内容介绍了“Linux系统启动的引导流程是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!  LINUX是自由开源软件,在L...
    99+
    2023-06-12
  • Android系统启动流程的重要性是什么
    Android系统启动流程的重要性在于确保系统能够正常启动并运行。启动流程包括各个组件的初始化、资源加载、服务启动等步骤,这些步骤的...
    99+
    2023-10-11
    Android
  • OS的项目结构与启动流程是什么
    这篇文章主要讲解了“OS的项目结构与启动流程是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“OS的项目结构与启动流程是什么”吧!一、整体情况首先,咱们H...
    99+
    2022-10-19
  • CentOS6 启动流程是怎样的
    CentOS6 启动流程是怎样的,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。我们在使用Linux操作系统的时候,我们只需按下电源键,等待,然后输入账户和密码就可以使用Linu...
    99+
    2023-06-16
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作