-10 +

Golang 源码分析 - 调度器的实现分析

为什么要要读 golang 的源码

大家都知道 golang 1.5 是一个里程碑版本,因为这个版本实现了 golang 的自举,也就是说 golang 整个语言的运行时用自己 golang 来实现了,除了少量的汇编语言,实现自举充分说明了 golang 的稳定性。所以我读 golang 的源码的源码不仅能够学习 golang 语言,还能学习到 google 的对 golang 的设计,学习 golang 能够将汇编,编译原理,链接,操作系统 这四个方面的知识连成一个整体,对自己本身系统知识的了解和熟悉也能有较大的提升。

golang 的精髓包含两个方面

golang 在语言层面就实现了协程的支持,这样能大大减轻程序员在并发编程上的心智负担。很好的驾驭了多核云时代。使用 golang 实现的明星项目就不少了,

更加详细的信息可以戳这里 基本上当前热门和有趣的项目大部分都是 golang 开发的,可以说我们正在经历一个基础设施有 c 到 golang 重写的过程中。

Golang 语言运行时有两个皇冠上的明珠,其一为内存管理和垃圾收集,内存管理是基于 google tcmalloc 算法实现的,其二就是 golang 的 goroutine 设计和调度。我们这次先分析 golang 的 goroutine 和调度器。

现代操作系统的调度

cpu 中一些基本的概念

我们先来看看现代计算机的一般结构

注意上图中的一些重要的组件

kernel 是如何调度线程的

Goroutine 介绍

使用过 go 语言的同学都知道在 go 中实现一个 goroutine 是非常简单的,只需要使用关键字 go 就能运行一个 goroutine,那到底 goroutine 是什么?又是如何是实现的呢?这篇文章先试图讲清楚这两个问题,至于之后的具体实现和源码分析,我们随后写 blog 来阐述。

goroutine 翻译为 协程 字面意思是协同的程序?确实如此,就是协同的工作的程序。

咱们先看看协同的是啥,就是说有多个 goroutine 时,可以大家一起协同工作,那程序指啥呢?指的就是协同工作的内容了,咱们现在程序是运行在 cpu 上,所以就是一段 cpu 指令,对应就是咱们程序中的代码,因为咱们程序的代码分为 code data,就是 code 也就是咱们具体写的各个函数,因为咱们的 function 进过编译器编译后生成的咱可执行文件的 text segment,即 cpu 可执行的执行。

为什么需要协程

调度器和 kernel 直接的矛盾

实现 Goroutine 需要解决的问题

在上文中我讲了程序是啥,那么咱们这节就来看看要让 goroutine run 起来需要解决的问题有哪些?

第一问题就得我们了解电脑的结构和运行是的原理,现代电脑都是 冯诺曼 体系的,而今天要回答这个问题,我们先得了解两个重要的组件,cpu 和 内存。有这些基础的同学都知道,cpu 和内存有几个重要的概念,寄存器 和 stack。 寄存器是 cpu 运行的重要组件可以和 cpu 来交换数据,而寄存是有限的,但是内存就要大的多,所以又变化而来了 stack 来和 cpu 交换数据。

第二个问题:

Metadata 组织

既然是协作式的,那我们就得知道怎么样来协作,协作的隐含的一个意思就是可调度,既然要可调度,就得又调度的依据即数据,应为没有数据就没有调度的依据,而一般调度数据是通过埋点收集起来,goroutine 也不例外,通过自身 runtime 的数据来组织这些需要的数据。

通过上一节的说明我们大概知道了一个程序要运行起来几个重要的状态信息

这两个数据是 goroutine 能调度运行的关键

syscall 和 network 的问题

goroutine 程序也是运行在操作系统系统之上,那么操作系统上的进程或者线程都是受操作系统管控的,我们知道和操作系统通讯只有两个方式一个中断,如 io,设备的中断,一个系统调用,其实这也是利用的中断。当操作系统陷入中断时就陷入内核这时,不是在用户态了,也就不受用户的管控,如果这是因为一个长时间的 io,操作系统会将这个线程或者进程调度让出,这是 golang 用户台的调度也就没有机会运行了。对于 network stack 上问题就更加突出,应为使用 golang 大都是网络程序,有着成千上万的网络链接。解决上面的两个 golang 主要使用了两个方式

goroutine 之间的数据同步问题

引入 goroutine 时,当两个或者多个 goroutine 需要通讯时,如生产者和消费这样的场景是很常见,当消费者向生产者要数据时,生产者的数据还没有准备好,这是就应该让消费者挂起来,反正亦然。对于者问题,golang 使用 channel 来解决

参考

关于我

85 后程序员, 比较熟悉 Java,JVM,Golang 相关技术栈, 关注 Liunx kernel,目前痴迷于分布式系统的设计和实践。 研究包括但不限于 Docker Kubernetes eBPF 等相关技术。

Blog

Code

Life

Archive