【Golang源码分析】golang的启动原理

简介: 【Golang源码分析】golang的启动原理

  随着技术的发展,大多的PHPer都开始转型golang。这个也是golang因为go的一些特别深受大家喜爱。我们团队也在试水,在内部的一些小项目上做了试探。在此同时我作为试探的一员身先士卒,沙场练兵。从中也琢磨到一些技巧希望能和大家一起学习共勉。


一、前期准备:

     go版本:go1.13.4

      由于是做源码分析调试,我们必须找到适合自己的工具。咱们调试的话可以考虑几种工具

       1)gdb    linux使用比较合适

       2)lldb   mac自带,不过建议dlv

       3)dlv    go开发的调试工具


       本文中主要以dlv为主,接下来我们一起来安装一下dlv。  

   第一步下载

#go get -u github.com/derekparker/delve/cmd/dlv

   

第二步编译:

#git clone https://github.com/go-delve/delve.git $GOPATH/src/github.com/go-delve/delve

#cd $GOPATH/src/github.com/go-delve/

#make install

$GOPATH 是golang的环境变量,我使用的是Linux,建议大家在/etc/profile设置,mac相同。

windows设置gopath

https://jingyan.baidu.com/article/5d368d1eb616133f60c057bf.html

   第三步设置环境变量:

#vim /etc/profile

export PATH=$PATH:/data/gopath/bin

/data/gopath/bin 为你自己的go的bin目录

初始化环境变量

#source /etc/profile


dlv参数介绍(常用)


命令(全) 命令(简写) 备注
restart r 重新启动程序
continue c 运行到断点或者程序终止
break b 设置断点
breakpoints bp 打印设置断点
next n 执行下一行
list l 查看当前代码
step s 进入下一层
stack bt 当前调用栈
print p 打印变量


二.调试:

    1.编译调试文件  

   代码内容

package main

import (

   "fmt"

)

 

func main() {

    var p **int    

    var i int = 10    

    var p1 *int = &i    

    fmt.Println("p1=", p1)    

   

    p = &p1    

    fmt.Println("p=", p)

}

   编译

#go build -gcflags=all="-N -l" pointer.go

必须这样编译,待能用dlv打印导出变量信息;


   2.载入文件

#dlv exec ./pointer


设置rt0_go断点,程序入口

(dlv) break runtime.rt0_go

是不是很好奇为什么入口在runtime.rt0_go

可以打开程序后输入r,在输入list,然后在输入si;si是单步cpu指令;

其实go在运行时会根据系统和CPU的不同,找到底层代码下的,rt0_**.s的汇编代码,汇编代码中再去调转到runtime.rt0_go 。

当然由于系统的不同可执行程序的形式不同。

   常见的可执行程序可以分为三大类:

   1)PE文件    

        PE文件主要是Windows系列系统,可执行文件索引;

   2)ELF文件    

       ELF文件是linux系列系统,可执行文件索引;

   3)mach-o文件    

       mach-o是mac系列系统的可执行文件格式,苹果系统是基于FreeBSD的,属于unix-like操作系统;

   有一篇比较不错的文章可以推荐给大家:

   https://blog.csdn.net/abc_12366/article/details/88205670


好的我们继续往下说,我们进入


我们按住n执行停留到212行

我们可以看到runtime.args 、runtime.osinit和runtime.schedinit三个函数调用。我们可以依次输入si进入函数体内查看

(div)si

args函数主要用途整理命令行参数;


osint函数是确定CPU Core数量


schedinit源码如下:

schedinit主要作用是所有运行时环境初始化;我们继续下一个断点,然后往下执行:

(dlv)b runtime.main  

(dlv)n



runtime/proc.go部分源码

func main() {  

 g := getg()        

 g.m.g0.racectx = 0  

 

 // 执行栈最大限制 64位系统1GB,32位系统250MB    

 if sys.PtrSize == 8 {      

     maxstacksize = 1000000000    

 } else {      

     maxstacksize = 250000000

 }    

 

 // 允许newproc启动新Ms。    

 mainStarted = true    

 

 if GOARCH != "wasm" { // wasm上还没有线程,所以没有sysmon      

     // 启动系统后监控(并发任务调度相关)      

     systemstack(func() {          

         newm(sysmon, nil)      

     })    

 }    

 ...      

 //启动垃圾回收器后台操作        

 gcenable()    

 ...        

 //进行间接调用,因为链接器在放置运行时不知道主包的地址。这个就是我们程序文件入口    

 //其实就是用户main.main函数    

 fn := main_main    

 fn()      

 

 //执行结束    

 exit(0)    

}

我们执行到fn()出可以按s

(dlv)s

这样就来到了我们程序的代码;这时我们可以看一下我们的调度栈

执行完成程序完成之后又回到runtime/proc.go中if atomic.Load(&runningPanicDefers) 处

继续往下执行,一直到exit(0)处,按si执行。

程序回到sys_linux_amd64.s汇编代码中的runtime.exit

最终程序结束;


三.调用流程:

非goroutine退出情况


相关文章
|
6月前
|
测试技术 Go 开发工具
100天精通Golang(基础入门篇)——第3天:Go语言的执行原理及常用命令、编码规范和常用工具
100天精通Golang(基础入门篇)——第3天:Go语言的执行原理及常用命令、编码规范和常用工具
149 1
|
8月前
|
Go 调度 数据安全/隐私保护
Golang 并发&同步的详细原理和使用技巧
Golang 并发&同步的详细原理和使用技巧
|
8月前
|
缓存 Go
Golang Channel 详细原理和使用技巧
Golang Channel 详细原理和使用技巧
|
8月前
|
存储 安全 测试技术
Golang Context 详细原理和使用技巧
Golang Context 详细原理和使用技巧
|
8月前
|
存储 设计模式 Go
Golang interface 接口详细原理和使用技巧
Golang interface 接口详细原理和使用技巧
|
8月前
|
安全 测试技术 程序员
Go 单测高级篇:Golang 单测原理深入理解
Go 单测高级篇:Golang 单测原理深入理解
|
9月前
|
Go 调度
Golang的goroutine原理介绍
Golang的goroutine原理介绍
|
9月前
|
存储 缓存 算法
golang本地缓存选型及原理总结
golang本地缓存选型及原理总结
|
9月前
|
存储 Go 调度
golang 锁原理剖析,你值得收藏
golang 锁原理剖析,你值得收藏
|
9月前
|
存储 缓存 Java
golang channel的创建、接受和发送原理讲透
golang channel的创建、接受和发送原理讲透