底层逻辑-理解Go语言的本质
创始人
2024-03-20 02:59:18

1.Java VS Go语言

Java,从源代码到编译成可运行的代码

java组图1

上图已经展示了这个过程:从Java的源代码编译成jar包或war包(字节码),最终运行在JVM中。

java组图2

我们把Java源代码编译后的jar包或war包看成是工程师生产出来的产品,操作系统是一个平台,JVM就是中间商,那程序的整体性能也要受到中间商JVM的因素影响了。

  • 优点:一次编译,到处运行(windows、linux、macos)
  • 缺点:JVM性能损失大。

Go语言,从源代码到编译成可运行的代码

golang组图1

我们把Go语言的源代码编译后,生成二进制文件,直接就可以在操作系统上运行,没有中间商

优点:

  • 直接编译成二进制
  • 无需进行虚拟机环境,自动执行
  • 一次编写代码,跨平台执行
  • 高性能并发能力

2.为什么Go语言运行-“没有中间商”

每种编程语言都有自己的Runtime, 把这个单词拆开来看,Run=运行,Time=时间,简称:运行时

Go语言的Runtime作用:

  • 内存管理
  • 协程调度
  • 垃圾回收

Go语言的运行时,是和源代码最终编译生成到二进制文件中的。当我们启动二进制文件的时候,运行时也就是一并启动了。

Go语言是如何编译成二进制文件的

package mainimport "fmt"func main() {fmt.Println("面向加薪学习-从0到Go语言微服务架构师")
}

在命令行执行 go build -n(-n含义代表:打印编译时会用到的所有命令,但不真正执行)

编译过程1

编译过程1

从上图可以看到:

  1. import config 导入配置
  2. fmt.a—>对应fmt包
  3. runtime.a—>对应runtime包
  4. compile -o 编译输出到 pkg.a

编译过程2

编译过程2

  1. 创建exe目录
  2. link链接到a.out
  3. 把a.out该名成menu1

总结:看到上面的过程已经把runtime包放到我们的二进制文件中了。

3.编译过程

在编译原理中,有一个名词:AST(抽象语法树) = Abstract Syntax Tree
1. 把源代码变成文本,然后把每个单词拆分出来
2. 把每个单词变成语法树
3. 类型检查、类型推断、类型匹配、函数调用、逃逸分析

分析阶段

  1. 词法检查分析、语法检查分析、语义检查分析)
  2. 生成中间码生成(SSA代码,类似汇编)。
    执行export GOSSAFUNC=main,代表你要看main函数的ssa代码,然后执行go build,会生成ssa.html
    图1. ssa1
    图2. ssa2
  3. 代码优化
  4. 生成机器码(支持生成.a的文件)
  5. go build -gcflags -S main.go(生成和平台相关的plan9汇编代码)
  6. 链接(生成可执行二进制文件)

4.Go语言是如何启动的

Go语言启动的时候,Runtime到底发生了什么?

可以到runtime目录中找到rt0_darwin_amd64.s找到这个文件(由于我的电脑是mac,所以找到了这个,其他平台可以找各自的),这是一个汇编文件。

rt0_darwin_amd64.s

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

asm_amd64.s

TEXT _rt0_amd64(SB),NOSPLIT,$-8MOVQ	0(SP), DI	// argcLEAQ	8(SP), SI	// argvJMP	runtime·rt0_go(SB)

接下来在同名文件中找到

TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0

它执行

  • 在堆栈上复制参数。
  • 从给定的(操作系统)堆栈中创建 iStack。
  • _cgo_init(可能会更新堆栈保护)
  • 收集用到的处理器信息

上面信息就是初始化一个协程G0(这是一个根协程,此时还没有调度器,也就是说不受调度器控制)

接下来是各种平台的检测和判断

CALL	runtime·check(SB)

查找代码 在runtime1.go,很亲切的Go语言函数了吧。里面是各种检查。看看都干了啥。

func check() {unsafe.Sizeof(...)unsafe.Offsetof(...)atomic.Cas(...)atomic.Or8()unsafe.Pointer()if _FixedStack != round2(_FixedStack){...}   ...
}

上面代码执行了:

  • 检查类型长度是否合法
  • 检查偏移量是否合法
  • 检查CAS执行是否合法
  • 检查原子执行是否合法
  • 检查指针执行是否合法
  • 判断栈大小是否是2的幂次方

接下来

CALL	runtime·args(SB)
func args(c int32, v **byte) {argc = cargv = vsysargs(c, v)
}

下面看一下启动顺序:
osinit(操作系统的初始化) -> schedinit(调度器的初始化) -> make & queue new G(新建一个队列G) -> mstart(启动)

CALL    runtime·osinit(SB)
func osinit() {ncpu = getncpu()physPageSize = getPageSize()
}

runtime/proc.go

CALL	runtime·schedinit(SB)
func schedinit() {...stackinit()     //栈空间内存分配mallocinit()    //堆内存空间初始化cpuinit()      // must run before alginitalginit()      // maps, hash, fastrand must not be used before this callfastrandinit() // must run before mcommoninitmcommoninit(_g_.m, -1)modulesinit()   // provides activeModulestypelinksinit() // uses maps, activeModulesitabsinit()     // uses activeModulesstkobjinit()    // must run before GC starts...goargs()goenvs()parsedebugvars()gcinit()
}

可以看到上面的代码的操作:

  1. CPU初始化
  2. 栈空间初始化
  3. 堆空间初始化
  4. 命令行参数初始化
  5. 环境变量初始化
  6. GC初始化
    //拿到主函数的地址	,是$runtime·main的地址,这里还没到我们写的main函数呢MOVQ	$runtime·mainPC(SB), AX	PUSHQ	AX//启动一个新协程CALL	runtime·newproc(SB) POPQ	AX//启动一个M(可以把M看成是一个中间人,它联系Goroutine和Processor)CALL	runtime·mstart(SB) 

从上面看到,此时系统里拥有:

  1. G0-根协程
  2. runtime.main的主协程
  3. 启动了M等待调度

runtime.main在runtime/proc.go中(这个是runtime中的main方法,还没到我们自己写的main函数)

// The main goroutine.
func main() {g := getg()...doInit(&runtime_inittask)gcenable()fn := main_main fn()
}

从上面看到:

  1. getg() 获取当前的goroutine
  2. 对g做判断和设置操作
  3. 初始化runtime doInit(…)
  4. 启用GC
  5. fn := main_main 这是隐式的调用,因为linker运行时不知道主包的地址。在之前的学习,我们知道编译过程有链接的时候,就会从main_main去找main.main。这个时候,才真正执行到我们程序员写的代码中。 go:linkname main_main main.main

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...