文章目录
作者简介:C/C++ 、Golang 领域耕耘者,创作者
个人主页:作者主页
如果感觉博主的文章还不错的话,还请关注➕ 、点赞👍 、收藏🧡三连支持一下博主哦~
1. 比较各种语言
1.1 c/c++
- c语言不是面向对象
- 直接编译为机器码,不需要执行环境
- 一次编码只能使用一种平台 (涉及到系统函数相关时)
- 自己处理GC问题, 容易内存泄漏
1.2 java
- 编译为中间码(字节码)
- 需要特定执行环境(JVM)
- 一次编译多出执行
- 有虚拟化损失(因为不是直接编译成的机器码)
1.3 js
- 不需要编译,直接解释执行
- 需要执行环境(浏览器)
- 有虚拟化损失
1.4 go
- 直接编译为二进制,没有虚拟化损失
- 自带运行环境,无需处理GC处理(后面有讲)
- 一次编码可以使用多种平台(后面讲)
- 超强的并发支持能力与并发易用性(容易上手,一般的程序员就可胜任)
2. go 中的runtime
2.1 位置
在官方包中(external libraries),里面有一个runtime文件夹
2.2 区别
runtime就是支撑程序运行时的组件,也称作程序运行环境,很多的语言都有runtime
- java : java 虚拟机
- js : 浏览器内核
2.3 runtime 特点
- go 没有虚拟机的概念
- runtime 作为程序的一部分打包进二进制产物 (是谷歌程序员开发go 的写的支撑包,是代码,与用户程序一起编译)
- runtime 随用户程序一起运行
- runtime 与用户程序没有明显界限,直接通过函数调用 (跟java不同,java 是通过字节码支持的)
2.4 runtime 能力
- 内存管理能力
- 垃圾回收能力(GC)(java 就是jvm, c++就是手动)
- 超强的并发能力(协程调度)
2.4 runtime other能力
- 有一定的屏蔽系统调用能力 (c/c++不跨平台就是因为不同系统的api 不同,在不同平台就需要处理不同的系统调用,无法屏蔽系统调用能力)
- 一些go 的关键字其实是runtime 下的函数
3. hello world 编译过程
3.1 编译code步骤分析
package main
import "fmt"
func main() {
fmt.Println("Hello world!")
}
这里用go build -n
一下,代表不实际编译它,只是输出编译过程
看下面一张图可以发现runtime 永远跟着用户程序一起编译的,所以有这个runtime.a
最后通过这个link 成一个exe 执行文件
3.2 编译code过程原理
前面都是编译成一堆.a 文件,最后链接成可执行文件
3.3 词法分析
将源代码翻译成token,token 是代码中最小语义结构
3.4 句法分析
token 序列经过处理,变成语法树
简单可以这样理解
3.5 语义分析
- 类型检查
- 类型推断
- 查看类型是否匹配
- 函数调用内联(优化部分代码)
- 逃逸分析(后续深入)
3.6 中间码(SSA)
go语言内部为了处理不同平台的差异,先生成中间代码(SSA)
查看从代码到SSA中间码的整个过程
$env:GOSSAFUNC=“main”
export GOSSAFUNC="main"
添加上去就行
输出某个函数的SSA代码
接下来可以专门看SSA 中间码(这个和汇编还是有点区别的)
通过看SSA 可以很清楚的知道调用了什么函数
3.7 机器码生成
- 先生成Plan9汇编代码(windows 平台和linux 平台的Plan9汇编是不一样的)
- 最后编译为机器码
- 输出的机器码为.a 文件
查看Plan9 汇编代码方法, 输入go build -gcflags -S main.go
, 之后可以看到这么一串汇编代码
这样可分分析机器在那几行代码中做了什么
3.8 链接
将各个包进行链接,包括runtime
3.9 编译过程总结
我们也将词法分析—句法分析—语义分析 叫做编译前端;中间码生成—代码优化—机器码生成 叫做 编译后端
当然咯,了解此过程并不是我们需要去更改go 的编译过程(大佬清随意),而是有时在遇到问题后,可以通过命令看中间码或者Plan9汇编代码看看go语言的底层是如何做的。在分析源码过程中也是可以应用这个原理的。
如果觉得对你有帮助的话:
👍 点赞,你的认可是我创作的动力!
🧡 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富!