前言
PREFACE
近几年来,Go语言作为一门服务器端开发语言越来越受欢迎,简洁易学的语法加上天生的高并发支持,还有日益完善的社区,让很多互联网公司开始转向Go语言。随着Go语言生态日趋成熟,各种组件框架如雨后春笋般涌现,市面上相关的书籍也多了起来,但是其中大部分是以应用为主,对于语言特性本身探索一般不太深入。笔者希望能够有一本讲解语言特性及实现原理的书,这也是写作本书的动机。
笔者当年刚参加工作的时候,使用的第一门开发语言是C++。虽然之前在学校用过C语言和汇编语言,但在接触到C++的一些面向对象特性时还是困惑了很久。直到有一天发现了《深度探索C++对象模型》,作者Stanley Lippman当年在贝尔实验室工作,是世界上第1个C++编译器——cfront的实现者,他从一个语言实现者的高度,对一些关键特性的实现原理及其背后的思考进行了详细阐述,使笔者受益匪浅。后来因为工作的原因,笔者开始使用Go语言,因为有了C/C++相关的基础,所以学习起来更加高效。尤其是当年学习C++对象模型,让笔者认识到语言特性也是通过数据结构和代码实现的,所以就按照自己的方式一边学习一边探索。第一次萌生要写点东西的念头是在给从PHP转Go语言的妻子讲完接口动态派发的实现原理后,用她的话来讲就是有种豁然开朗的感觉,并鼓励笔者把这些东西整理一下。后来我们就在微信公众号上以幼麟实验室的名义发布了一系列视频和文章,主要分析语言特性的底层实现。在一年多的时间里,幼麟实验室受到了广大网友的好评与支持,清华大学出版社的赵佳霓编辑也是在此期间联系了笔者,希望笔者能够把自己的探索研究整理成书。因为写作本书的关系,让笔者能够更系统地思考,收获颇多。希望本书能够帮助各位读者,解决大家学习Go语言中遇到的一些困惑。
本书主要内容
第1章介绍x86汇编的一些基础知识,包括通用寄存器、几条常用的指令,以及内存分页的实现原理等。
第2章介绍指针的实现原理,包括指针构成、相关操作,以及Go语言的unsafe包等。
第3章围绕函数进行一系列探索,包括栈帧布局、调用约定、变量逃逸、Function Value、闭包、defer和panic等。
第4章介绍方法的实现原理,包括接收者类型、Method Value和组合式继承等。
第5章围绕接口对Go语言的动态特性展开探索,包括装箱、方法集、动态派发、类型断言、类型系统和反射等。
第6章介绍goroutine的实现,包括GMP模型、goroutine的创建与退出、调度循环、抢占式调度、timer、netpoller和监控线程等。
第7章介绍同步的原理及其相关的组件,包括内存乱序、原子指令、自旋锁、Go语言runtime中的互斥锁和信号量,以及sync.Mutex和channel等。
第8章介绍堆内存管理,包括heapArena、mspan等几种主要的数据结构,mallocgc函数的主要逻辑,以及GC的三色抽象、写屏障等。
第9章介绍栈内存管理,包括goroutine栈的分配、增长、收缩和释放等。
阅读建议
本书写作过程主要使用了Go 1.16及之前的几个版本,为了避免后续版本可能发生的不兼容问题,相关示例建议使用Go 1.13~Go 1.16编译运行。
阅读本书不需要精通汇编语言、操作系统,但是需要对进程、线程这类基本概念有所了解。毕竟Go语言可直接构建生成系统原生的可执行文件,如果想要深入理解一些语言特性的实现原理,还是建议学习并实践一下多线程编程、IO多路复用这类关键技术。
第一部分主要包括指针和函数,笔者希望大家能够通过这部分内容,对运行时栈及函数栈帧的相对寻址方式有深入的理解,为后续探索打下坚实的基础。
第二部分想要表达对Lippman大师的崇高敬意,至今难忘初次阅读《深度探索C++对象模型》时那种“初闻大道,喜不自胜”的心情。按照Lippman大师的解释,对象模型应该是编译器对自定义数据类型的建模,指导了对象内存布局及其他一些数据结构和代码的生成。只有理解了语言特性的实现原理,才真正是磨刀不误砍柴工。
第三部分从服务器端程序开发的角度,梳理了如何从最初的多进程、多线程,逐渐发展到现在的协程。runtime的调度逻辑还是比较复杂的,但是最核心的思想就是IO多路复用与协程的结合,让每个任务有自己独立的栈,而同步的核心就是确立Happens Before条件。
第四部分从堆和栈两方面,梳理了内存管理的实现。内存分配方面应重点关注主要的数据结构。至于GC方面,应先理解宏观层面的整体思想和流程,然后去研究一些细节会更加容易。整个runtime实际上是个不可分割的整体,在这里会看到内存管理对类型系统的依赖。
本书源代码
扫描下方二维码,可获取本书源代码:
本书源代码