本書(shū)主要講解了Go語(yǔ)言一些關(guān)鍵特性的實(shí)現(xiàn)原理,Nicklaus Wirth大師曾經(jīng)說(shuō)過(guò):算法 數(shù)據(jù)結(jié)構(gòu)=程序,語(yǔ)言特性的實(shí)現(xiàn)也不外乎就是數(shù)據(jù)結(jié)構(gòu) 代碼邏輯。 全書(shū)共分為4個(gè)部分:第一部分是基礎(chǔ)特性(第1章~第3章),第二部分是對(duì)象模型(第4章~第5章),第三部分是調(diào)度系統(tǒng)(第6章~第7章),第四部分是內(nèi)存管理(第8章~第9章)。書(shū)中主要內(nèi)容包括:指針、函數(shù)棧幀、調(diào)用約定、變量逃逸、Function Value、閉包、defer、panic、方法、Method Value、組合式繼承、接口、類(lèi)型斷言、反射、goroutine、搶占式調(diào)度、同步、堆和棧的管理,以及GC等。 書(shū)中包含大量的探索示例和源碼分析,在學(xué)會(huì)應(yīng)用的同時(shí)還能了解實(shí)現(xiàn)原理。書(shū)中絕大部分代碼都是用Go語(yǔ)言實(shí)現(xiàn),還有少部分使用匯編語(yǔ)言,都可以使用Go官方SDK直接編譯。探索過(guò)程循序漸進(jìn)條理清晰,用到的工具也都是SDK自帶,方便讀者親自上手實(shí)踐。 本書(shū)適合Go語(yǔ)言的初學(xué)者,在學(xué)習(xí)語(yǔ)言特性的同時(shí)了解其實(shí)現(xiàn)原理。更適合有一定的Go語(yǔ)言應(yīng)用基礎(chǔ),想要深入研究底層原理的技術(shù)人員。同樣適合有一些其他編程語(yǔ)言基礎(chǔ),想要轉(zhuǎn)學(xué)Go語(yǔ)言的開(kāi)發(fā)者。
近幾年來(lái),Go語(yǔ)言作為一門(mén)服務(wù)器端開(kāi)發(fā)語(yǔ)言越來(lái)越受歡迎,簡(jiǎn)潔易學(xué)的語(yǔ)法加上天生的高并發(fā)支持,還有日益完善的社區(qū),讓很多互聯(lián)網(wǎng)公司開(kāi)始轉(zhuǎn)向Go語(yǔ)言。隨著Go語(yǔ)言生態(tài)日趨成熟,各種組件框架如雨后春筍般涌現(xiàn),市面上相關(guān)的書(shū)籍也多了起來(lái),但是其中大部分是以應(yīng)用為主,對(duì)于語(yǔ)言特性本身探索一般不太深入。筆者希望能夠有一本講解語(yǔ)言特性及實(shí)現(xiàn)原理的書(shū),這也是寫(xiě)作本書(shū)的動(dòng)機(jī)。
筆者當(dāng)年剛參加工作的時(shí)候,使用的第一門(mén)開(kāi)發(fā)語(yǔ)言是C 。雖然之前在學(xué)校用過(guò)C語(yǔ)言和匯編語(yǔ)言,但在接觸到C 的一些面向?qū)ο筇匦詴r(shí)還是困惑了很久。直到有一天發(fā)現(xiàn)了《深度探索C 對(duì)象模型》,作者Stanley Lippman當(dāng)年在貝爾實(shí)驗(yàn)室工作,是世界上第1個(gè)C 編譯器cfront的實(shí)現(xiàn)者,他從一個(gè)語(yǔ)言實(shí)現(xiàn)者的高度,對(duì)一些關(guān)鍵特性的實(shí)現(xiàn)原理及其背后的思考進(jìn)行了詳細(xì)闡述,使筆者受益匪淺。后來(lái)因?yàn)楣ぷ鞯脑颍P者開(kāi)始使用Go語(yǔ)言,因?yàn)橛辛薈/C 相關(guān)的基礎(chǔ),所以學(xué)習(xí)起來(lái)更加高效。尤其是當(dāng)年學(xué)習(xí)C 對(duì)象模型,讓筆者認(rèn)識(shí)到語(yǔ)言特性也是通過(guò)數(shù)據(jù)結(jié)構(gòu)和代碼實(shí)現(xiàn)的,所以就按照自己的方式一邊學(xué)習(xí)一邊探索。第一次萌生要寫(xiě)點(diǎn)東西的念頭是在給從PHP轉(zhuǎn)Go語(yǔ)言的妻子講完接口動(dòng)態(tài)派發(fā)的實(shí)現(xiàn)原理后,用她的話來(lái)講就是有種豁然開(kāi)朗的感覺(jué),并鼓勵(lì)筆者把這些東西整理一下。后來(lái)我們就在微信公眾號(hào)上以幼麟實(shí)驗(yàn)室的名義發(fā)布了一系列視頻和文章,主要分析語(yǔ)言特性的底層實(shí)現(xiàn)。在一年多的時(shí)間里,幼麟實(shí)驗(yàn)室受到了廣大網(wǎng)友的好評(píng)與支持,清華大學(xué)出版社的趙佳霓編輯也是在此期間聯(lián)系了筆者,希望筆者能夠把自己的探索研究整理成書(shū)。因?yàn)閷?xiě)作本書(shū)的關(guān)系,讓筆者能夠更系統(tǒng)地思考,收獲頗多。希望本書(shū)能夠幫助各位讀者,解決大家學(xué)習(xí)Go語(yǔ)言中遇到的一些困惑。
本書(shū)主要內(nèi)容
第1章介紹x86匯編的一些基礎(chǔ)知識(shí),包括通用寄存器、幾條常用的指令,以及內(nèi)存分頁(yè)的實(shí)現(xiàn)原理等。
第2章介紹指針的實(shí)現(xiàn)原理,包括指針構(gòu)成、相關(guān)操作,以及Go語(yǔ)言的unsafe包等。
第3章圍繞函數(shù)進(jìn)行一系列探索,包括棧幀布局、調(diào)用約定、變量逃逸、Function Value、閉包、defer和panic等。
第4章介紹方法的實(shí)現(xiàn)原理,包括接收者類(lèi)型、Method Value和組合式繼承等。
第5章圍繞接口對(duì)Go語(yǔ)言的動(dòng)態(tài)特性展開(kāi)探索,包括裝箱、方法集、動(dòng)態(tài)派發(fā)、類(lèi)型斷言、類(lèi)型系統(tǒng)和反射等。
第6章介紹goroutine的實(shí)現(xiàn),包括GMP模型、goroutine的創(chuàng)建與退出、調(diào)度循環(huán)、搶占式調(diào)度、timer、netpoller和監(jiān)控線程等。
第7章介紹同步的原理及其相關(guān)的組件,包括內(nèi)存亂序、原子指令、自旋鎖、Go語(yǔ)言runtime中的互斥鎖和信號(hào)量,以及sync.Mutex和channel等。
第8章介紹堆內(nèi)存管理,包括heapArena、mspan等幾種主要的數(shù)據(jù)結(jié)構(gòu),mallocgc函數(shù)的主要邏輯,以及GC的三色抽象、寫(xiě)屏障等。
第9章介紹棧內(nèi)存管理,包括goroutine棧的分配、增長(zhǎng)、收縮和釋放等。
閱讀建議
本書(shū)寫(xiě)作過(guò)程主要使用了Go 1.16及之前的幾個(gè)版本,為了避免后續(xù)版本可能發(fā)生的不兼容問(wèn)題,相關(guān)示例建議使用Go 1.13~Go 1.16編譯運(yùn)行。
閱讀本書(shū)不需要精通匯編語(yǔ)言、操作系統(tǒng),但是需要對(duì)進(jìn)程、線程這類(lèi)基本概念有所了解。畢竟Go語(yǔ)言可直接構(gòu)建生成系統(tǒng)原生的可執(zhí)行文件,如果想要深入理解一些語(yǔ)言特性的實(shí)現(xiàn)原理,還是建議學(xué)習(xí)并實(shí)踐一下多線程編程、IO多路復(fù)用這類(lèi)關(guān)鍵技術(shù)。
第一部分主要包括指針和函數(shù),筆者希望大家能夠通過(guò)這部分內(nèi)容,對(duì)運(yùn)行時(shí)棧及函數(shù)棧幀的相對(duì)尋址方式有深入的理解,為后續(xù)探索打下堅(jiān)實(shí)的基礎(chǔ)。
第二部分想要表達(dá)對(duì)Lippman大師的崇高敬意,至今難忘初次閱讀《深度探索C 對(duì)象模型》時(shí)那種初聞大道,喜不自勝的心情。按照Lippman大師的解釋?zhuān)瑢?duì)象模型應(yīng)該是編譯器對(duì)自定義數(shù)據(jù)類(lèi)型的建模,指導(dǎo)了對(duì)象內(nèi)存布局及其他一些數(shù)據(jù)結(jié)構(gòu)和代碼的生成。只有理解了語(yǔ)言特性的實(shí)現(xiàn)原理,才真正是磨刀不誤砍柴工。
第三部分從服務(wù)器端程序開(kāi)發(fā)的角度,梳理了如何從最初的多進(jìn)程、多線程,逐漸發(fā)展到現(xiàn)在的協(xié)程。runtime的調(diào)度邏輯還是比較復(fù)雜的,但是最核心的思想就是IO多路復(fù)用與協(xié)程的結(jié)合,讓每個(gè)任務(wù)有自己獨(dú)立的棧,而同步的核心就是確立Happens Before條件。
第四部分從堆和棧兩方面,梳理了內(nèi)存管理的實(shí)現(xiàn)。內(nèi)存分配方面應(yīng)重點(diǎn)關(guān)注主要的數(shù)據(jù)結(jié)構(gòu)。至于GC方面,應(yīng)先理解宏觀層面的整體思想和流程,然后去研究一些細(xì)節(jié)會(huì)更加容易。整個(gè)runtime實(shí)際上是個(gè)不可分割的整體,在這里會(huì)看到內(nèi)存管理對(duì)類(lèi)型系統(tǒng)的依賴(lài)。
本書(shū)源代碼
掃描下方二維碼,可獲取本書(shū)源代碼:
本書(shū)源代碼
致謝
感謝那些喜愛(ài)Go語(yǔ)言的網(wǎng)友對(duì)筆者的支持; 感謝清華大學(xué)出版社的趙佳霓編輯; 感謝我的家人,尤其是和我一起討論技術(shù)問(wèn)題并幫忙整理書(shū)稿的妻子,給予我莫大的支持。
由于時(shí)間倉(cāng)促,并且受限于筆者水平,書(shū)中難免有不妥之處,請(qǐng)讀者見(jiàn)諒,并提寶貴意見(jiàn)。
封幼林2022年5月
封幼林,資深軟件工程師,十多年IT從業(yè)經(jīng)驗(yàn),曾涉足Win32桌面程序開(kāi)發(fā)、Android移動(dòng)端開(kāi)發(fā),以及互聯(lián)網(wǎng)服務(wù)器端開(kāi)發(fā)等多個(gè)領(lǐng)域。喜歡研究底層技術(shù),用自己的方法探究背后的實(shí)現(xiàn)原理。熱愛(ài)技術(shù)交流與分享,創(chuàng)建了微信公眾號(hào)幼麟實(shí)驗(yàn)室,致力做一些形象、通透的計(jì)算機(jī)教程,讓開(kāi)發(fā)者知其然亦知其所以然。
第1章匯編基礎(chǔ)
1.1x86通用寄存器
1.1.132位架構(gòu)
1.1.264位架構(gòu)
1.2常用匯編指令
1.2.1整數(shù)加減指令
1.2.2數(shù)據(jù)傳輸指令
1.2.3入棧和出棧指令
1.2.4分支跳轉(zhuǎn)指令
1.2.5過(guò)程調(diào)用指令
1.3內(nèi)存分頁(yè)機(jī)制
1.3.1線性地址
1.3.280386兩級(jí)頁(yè)表
1.3.3PAE三級(jí)頁(yè)表
1.3.4x64四級(jí)頁(yè)表
1.3.5虛擬內(nèi)存
1.4匯編代碼風(fēng)格
1.5本章小結(jié)
第2章指針(7min)
2.1指針構(gòu)成
2.1.1地址
2.1.2元素類(lèi)型
2.2相關(guān)操作
2.2.1取地址
2.2.2解引用
2.2.3強(qiáng)制類(lèi)型轉(zhuǎn)換
2.2.4指針運(yùn)算
2.3unsafe包
2.3.1標(biāo)準(zhǔn)庫(kù)與keyword
2.3.2關(guān)于uintptr
2.3.3內(nèi)存對(duì)齊
2.4本章小結(jié)
第3章函數(shù)(44min)
3.1棧幀
3.1.1棧幀布局
3.1.2尋址方式
3.1.3又見(jiàn)內(nèi)存對(duì)齊
3.1.4調(diào)用約定
3.1.5Go 1.17的變化
3.2逃逸分析
3.2.1什么是逃逸分析
3.2.2不逃逸分析
3.2.3不逃逸判斷
3.3Function Value
3.3.1函數(shù)指針
3.3.2Function Value分析
3.3.3閉包
3.4defer
3.4.1最初的鏈表
3.4.2棧上分配
3.4.3高效的open coded defer
3.5panic
3.5.1gopanic()函數(shù)
3.5.2gorecover()函數(shù)
3.5.3嵌套的panic
3.5.4支持open coded defer
3.6本章小結(jié)
第4章方法(6min)
4.1接收者類(lèi)型
4.1.1值類(lèi)型
4.1.2指針類(lèi)型
4.1.3包裝方法
4.2Method Value
4.2.1基于類(lèi)型
4.2.2基于對(duì)象
4.3組合式繼承
4.3.1嵌入值
4.3.2嵌入指針
4.3.3多重繼承
4.4本章小結(jié)
第5章接口(27min)
5.1空接口
5.1.1一個(gè)更好的void*
5.1.2類(lèi)型元數(shù)據(jù)
5.1.3逃逸與裝箱
5.2非空接口
5.2.1動(dòng)態(tài)派發(fā)
5.2.2具體實(shí)現(xiàn)
5.2.3接收者類(lèi)型
5.2.4組合式繼承
5.3類(lèi)型斷言
5.3.1E To 具體類(lèi)型
5.3.2E To I
5.3.3I To 具體類(lèi)型
5.3.4I To I
5.4反射
5.4.1類(lèi)型系統(tǒng)
5.4.2類(lèi)型元數(shù)據(jù)詳細(xì)講解
5.4.3對(duì)數(shù)據(jù)的操作
5.4.4對(duì)鏈接器裁剪的影響
5.5本章小結(jié)
第6章goroutine(48min)
6.1進(jìn)程、線程與協(xié)程
6.1.1進(jìn)程
6.1.2線程
6.1.3協(xié)程
6.2IO多路復(fù)用
6.2.13種網(wǎng)絡(luò)IO模型
6.2.2示例對(duì)比
6.3巧妙結(jié)合
6.4GMP模型
6.4.1基本概念
6.4.2從GM到GMP
6.5GMP主要數(shù)據(jù)結(jié)構(gòu)
6.5.1runtime.g
6.5.2runtime.m
6.5.3runtime.p
6.5.4schedt
6.6調(diào)度器初始化
6.6.1調(diào)度器初始化過(guò)程
6.6.2runtime.schedinit()函數(shù)
6.7G的創(chuàng)建與退出
6.7.1相關(guān)匯編函數(shù)
6.7.2runtime.newproc()函數(shù)
6.8調(diào)度循環(huán)
6.8.1runtime.schedule()函數(shù)
6.8.2runtime.findrunnable()函數(shù)
6.9搶占式調(diào)度
6.9.1Go 1.13的搶占式調(diào)度
6.9.2Go 1.14的搶占式調(diào)度
6.10timer
6.10.1一個(gè)示例
6.10.2數(shù)據(jù)結(jié)構(gòu)
6.10.3操作函數(shù)
6.11netpoller
6.11.1跨平臺(tái)的netpoller
6.11.2TCP連接的Read()方法
6.12監(jiān)控線程
6.12.1按需執(zhí)行timer和netpoll
6.12.2搶占G和P
6.12.3強(qiáng)制執(zhí)行GC
6.13本章小結(jié)
第7章同步(32min)
7.1Happens Before
7.1.1并發(fā)
7.1.2并行
7.2內(nèi)存亂序
7.2.1編譯期亂序
7.2.2執(zhí)行期亂序
7.2.3內(nèi)存排序指令
7.3常見(jiàn)的鎖
7.3.1原子指令
7.3.2自旋鎖
7.3.3調(diào)度器對(duì)象
7.3.4優(yōu)化的鎖
7.4Go語(yǔ)言的同步
7.4.1runtime.mutex
7.4.2semaphore
7.4.3sync.Mutex
7.4.4channel
7.5本章小結(jié)
第8章堆(17min)
8.1內(nèi)存分配
8.1.1sizeclasses
8.1.2heapArena
8.1.3mspan
8.1.4mcentral
8.1.5mcache
8.1.6mallocgc
8.2垃圾回收
8.2.1GC root
8.2.2三色抽象
8.2.3寫(xiě)屏障
8.2.4觸發(fā)方式
8.2.5GC Worker
8.2.6gctrace
8.3本章小結(jié)
第9章棧(8min)
9.1棧分配
9.1.1棧分配初始化
9.1.2棧分配邏輯
9.2棧增長(zhǎng)
9.2.1棧增長(zhǎng)檢測(cè)代碼
9.2.2棧增長(zhǎng)函數(shù)
9.3棧收縮
9.4棧釋放
9.4.1小于或等于16KB的棧空間
9.4.2大于或等于32KB的?臻g
9.4.3棧釋放時(shí)機(jī)
9.5本章小結(jié)