几个关于 Go Runtime 的问题

2021-02-04 22:59:30 +08:00
 Dongxiem

问题:go 的 runtime 如何实现?(这个实在不理解)

追问:runtime 这个进程是运行在哪儿的?

追问:每个 go 进程都会有一个吗还是共用一个?(应该是共用一个)

追问:如果你运行了 go 进程,在 linux 系统里查看进程能看到 runtime 进程吗?

4476 次点击
所在节点    Go 编程语言
60 条回复
Dongxiem
2021-02-05 13:54:29 +08:00
@lewis89 是的,关于 runtime 包含的东西很多,比如你说的 channel 、调度模型、垃圾回收等,但是我还是觉得 Runtime 最开始的几个内容还是不太清晰,如 runtime 究竟运行在哪里呢?如果按照 @henglinli 的说法,是不是在 GMP 模型中 runtime 运行在每个 M 系统线程上?每个 M 都承载着一个 runtime 进行并发调度?还是说 runtime 只是运行在一个初始的系统线程 M0 上,然后再进行指挥调度所有的线程、协程等;

而且关于 Runtime 的创建及运行过程,Google 上面几乎也搜索不到,都是在将 GMP 的调度模型等等的内容,并没有涉及到最开始的部分内容。如果你觉得可以了解到的一些博客,请推荐一下啊,谢谢大佬了。
lewis89
2021-02-05 14:04:23 +08:00
@Dongxiem #21 这个你要了解 runtime 每个细节的话,只能去看源代码了, 我只是提供一些大致上的思路,因为很多东西道理是一样的,例如垃圾回收 肯定要暂停 goroutine 维持 变量之间的引用关系的一致性,不然你一边在修改变量引用关系 又进行垃圾回收 是不现实的..
weiwenhao
2021-02-05 14:07:34 +08:00
runtime.collector()
xbh1794970183564
2021-02-05 14:09:14 +08:00
老哥头像能发一张吗。。
weiwenhao
2021-02-05 14:12:53 +08:00
没看过源码,但是看过一个垃圾回收的教程,我觉得应该会有类似这样的实现(上面还没打完就发出去了也不能删除)

movl $4096, %rdi
call runtime.alloc # 调用 runtime 中的垃圾回收函数
movl $1, -4($rax)
movl $2, -8($ax)
...
Lemeng
2021-02-05 14:13:36 +08:00
@lewis89 期待老哥补充
Dongxiem
2021-02-05 14:15:30 +08:00
@lewis89 是的,这个问题的本意就是了解 runtime 的初始化、运行等底层原理,你回答的 GMP 、CSP 、GC 等内容也是 runtime 的原理,但是还是没能解决到上面一开始的几个问题啊。
Dongxiem
2021-02-05 14:17:34 +08:00
@xbh1794970183564 这不知道要咋发啊。
weiwenhao
2021-02-05 14:22:51 +08:00
@weiwenhao

var a []int // 申请一个动态数组
a = append(a, 22222222)
a = append(a, 33333333)

类似这样的 golang 代码会转成上面你的汇编,另外汇编是可以调用 c 函数的,只要按照 c 调用约定就可以了。runtime.alloc 无非就是调用 c 的 malloc 函数申请一个 4096 字节的空间,然后返回指针地址给 %rax(调用约定)。


-----go tool compile -S main.go 虽然没看太懂,但是大概是这样

0x0046 00070 (main.go:5) CALL runtime.growslice(SB) #调用 runtime 函数得到一段堆内存空间
0x004b 00075 (main.go:5) MOVQ 40(SP), AX
0x0050 00080 (main.go:5) MOVQ 48(SP), CX
0x0055 00085 (main.go:5) MOVQ 56(SP), DX
0x005a 00090 (main.go:5) MOVQ $22222222, (AX) # 根据指针偏移写入到堆内存
0x0061 00097 (main.go:6) LEAQ 2(CX), BX
0x0065 00101 (main.go:6) CMPQ DX, BX
0x0068 00104 (main.go:6) JCS 125
0x006a 00106 (main.go:6) MOVQ $33333333, 8(AX)(CX*8) # 根据指针偏移写入到堆内存
lewis89
2021-02-05 14:24:48 +08:00
@Dongxiem #27 运行时的原理?我不知道你提出的问题 具体是什么,代码无非是运行在 ring3 用户态上,基本上所有操作都是要用操作系统 提供的 API,初始化? 初始化就更简单了 malloc 一个连续内存地址 就能当做协程的栈帧使用,栈幁只是一个逻辑的概念。
xbh1794970183564
2021-02-05 14:26:38 +08:00
@Dongxiem 有原链接吗。。
lewis89
2021-02-05 14:27:13 +08:00
@Dongxiem #27 然后调度代码的话 每一个 M 都是有自己的 线程栈幁的,调度的时候 有调度队列,不同的 M 可以去其它 M 的队列里面去偷 goroutine 执行,这可以理解吗?因为暂停 goroutine 的话 只要保存 goroutine 的协程栈幁就好了..
lewis89
2021-02-05 14:33:06 +08:00
@Dongxiem #27 我觉得你可能对 M 这个抽象的概念没理解清楚,每个 M 实际上就是一个操作系统线程,它自己有自己的线程栈幁,然后它拿到 goroutine 的时候 可以把 寄存器保存起来,然后把 goroutine 的协程上下文等寄存器变量换上去,然后把对 CPU 的控制权 交给 goroutine (在汇编层面上 可能就是 JMP 到 goroutine 的代码 恢复 goroutine 之前被暂停时的上下文状态) .. 然后 goroutine 运行到一定时候 例如调用方法 或者 使用网络 IO 它会自动把对 CPU (线程)的控制权 让给 M 的调度代码.. 或者 goroutine 长期占用线程不放,这个时候会有操作系统提供的信号中断机制 强行迫使 goroutine 放弃对 CPU 的控制权,然后代码会跳回到调度器 重新进行调度..
lewis89
2021-02-05 14:39:02 +08:00
@Dongxiem #21 你如果要完全理解 goroutine 代码 调度器代码 谁控制了线程( CPU )控制权的话,最好去读 X86 汇编跟抢占式调度原理,因为无论协程 线程都是抽象的概念,只要能保存其上下文 例如线程只要操作系统保存 RSP RBP IP 寄存器 CPU 就能在线程之间 不断来回切换,另外操作系统在进程之间切换 可能还要修改 CR3 寄存器 因为每个进程的内存地址空间不一样,但是对于协程来讲,我不清楚协程内部是如何维护栈幁的,但是原理肯定是相同的,无非是保存当前运行状态的寄存器跟各种上下文的东西 以便切换回来的时候 使用..
Dongxiem
2021-02-05 14:39:20 +08:00
@xbh1794970183564 去这里找: https://medium.com/a-journey-with-go,里面很多 go 的配图,包你喜欢。
xbh1794970183564
2021-02-05 14:42:00 +08:00
@Dongxiem 感谢
lewis89
2021-02-05 14:46:07 +08:00
@weiwenhao #29 这个要看是用 引用计数 还是 可达性分析,如果是引用计数的话 那就是跟 python 一样,每个对象有一个引用计数器,当它变成 0 的时候 这样就能回收,如果是可达性分析,那就要判断当前程序运行时这个状态哪些对象是不能被回收的,例如我 A->B->C 三个方法调用 C 方法在执行磁盘 IO,此时 A 跟 B 的临时变量引用或者间接引用的堆上面的内存空间 你是没法标记回收的,如果标记回收了 C 方法从内核返回回来 A 跟 B 发现自己的临时变量或者对象都不见了..那就出问题了,还有一些全局变量也是不能被回收的,因为全局变量的生命周期可能跨越整个程序的运行周期。
kksco
2021-02-05 15:06:30 +08:00
@lewis89 老哥建个微信群呗,拉点 v 友跟你学习学习
lewis89
2021-02-05 15:12:43 +08:00
@kksco #38 我有吹水群的 你发下 id 我拉你进去..
weiwenhao
2021-02-05 15:19:07 +08:00
对于静态语言而言 runtime 其实就是一个帮助函数库( runtime.go )。
golang 用户代码编译成汇编,然后链接这个帮助函数库(runtime.go)。 最后得到一整个完整的二进制可执行文件。

所以 runtime 和你自己写的 golang 代码并没啥太大的区别。用户代码调用 runtime 中的帮助函数,函数执行完了返回。 用户代码调用 import 中的函数,函数执行完了就返回了。都一样的呗。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/751398

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX