深入分析go調(diào)度(一)

以下文章均為拜讀公眾號(hào) 源碼游記 的筆記 http://mp.weixin.qq.com/mp/homepage?__biz=MzU1OTg5NDkzOA==&hid=1&sn=8fc2b63f53559bc0cee292ce629c4788&scene=18#wechat_redirect

  1. 預(yù)備知識(shí)

1. 寄存器

我們一般用到的寄存器有三種

  1. 通用寄存器

    rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8, r9, r10, r11, r12, r13, r14, r15寄存器讨越。CPU對(duì)這16個(gè)通用寄存器的用途沒有做特殊規(guī)定。

    但是這些寄存器有一些默認(rèn)用途,

    1. 在傳參的時(shí)候,前6個(gè)參數(shù)分別為:rax, rbx, rcx, rdx, rsi, rdi畜晰。

    2. rsp 棧頂寄存器 & rbp 椚鹂穑基址寄存器

      這2個(gè)寄存器都跟函數(shù)調(diào)用棧相關(guān)。其中rsp一般存在棧頂?shù)牡刂罚鴕bp是棧幀起始地址扫步。編譯器一般用這2個(gè)寄存器來獲取函數(shù)局部變量或者參數(shù)

  2. 指令寄存器、程序計(jì)算寄存器

    rip寄存器河胎,它用來存放下一條即將執(zhí)行的指令的地址。

  3. 段寄存器

    fs和gs寄存器游岳,在go中使用fs寄存器實(shí)現(xiàn)線程本地存儲(chǔ)其徙。

2. 內(nèi)存

  1. 內(nèi)存的單元為字節(jié)胚迫,每一個(gè)字節(jié)都有一個(gè)地址
  2. 變量連續(xù)存儲(chǔ):何大于一個(gè)字節(jié)的變量在內(nèi)存中都存儲(chǔ)在相鄰連續(xù)的的幾個(gè)內(nèi)存單元之中;
  3. 大端&小端
    1. 大端:高字節(jié)-> 低地址
    2. 小端:高字節(jié)-> 高地址
image.png

說明:

rsp 指的是棧頂是已經(jīng)使用的地址唾那。非下一條地址访锻。

3. 函數(shù)調(diào)用棧

函數(shù)是以棧的方式調(diào)用的。

程序運(yùn)行時(shí)布局圖:

image.png

進(jìn)程在虛擬地址的布局如上闹获。一個(gè)進(jìn)程把內(nèi)存分為了4個(gè)部分:

  1. 代碼區(qū): 包括被CPU執(zhí)行的機(jī)器代碼和只讀數(shù)據(jù)比如字符串常量期犬。一旦加載完成就不會(huì)再變化
  2. 數(shù)據(jù)區(qū):包括程序的全局變量和靜態(tài)變量(c語言有靜態(tài)變量,而go沒有)避诽。一旦加載完成就不會(huì)再變化
  3. 堆:動(dòng)態(tài)分配的內(nèi)存在堆中龟虎。
  4. 棧:函數(shù)調(diào)用棧

下面重點(diǎn)說函數(shù)調(diào)用棧

它在函數(shù)中扮演著重要的角色

  1. 保存 函數(shù)中的局部變量
  2. 傳遞 在函數(shù)調(diào)用中傳遞參數(shù)
  3. 返回 把函數(shù)的返回值返回
  4. 保存 函數(shù)的返回地址

每個(gè)函數(shù)在執(zhí)行過程中都需要使用一塊內(nèi)存來保存上述的這些值,我們稱這塊棧內(nèi)存為棧幀(stack frame)沙庐。當(dāng)發(fā)生函數(shù)調(diào)用的時(shí)候鲤妥,被調(diào)用者不能覆蓋調(diào)用者的棧幀旗芬,所以需要把調(diào)用者的棧幀push到棧上王财,等調(diào)用完成再pop。

另外裁厅,在AMD64 Linux平臺(tái)古涧,棧是從高向低方向生成的垂券。其中就使用了上面提到的2個(gè)寄存器

  • rsp
  • rbp

舉例花盐,假設(shè)有如下調(diào)用關(guān)系A()->B()->C(),則有如下的調(diào)用關(guān)系

image.png

需要注意:

  1. 函數(shù)調(diào)用時(shí)羡滑,參數(shù)和返回值都是存放在調(diào)用者的棧幀中。(這個(gè)會(huì)影響到行程的匯編代碼)
  2. go語言把參數(shù)和返回值都是放在棧上算芯。(gcc 是吧參數(shù)和返回值放到寄存器中)

當(dāng)C柒昏、B函數(shù)運(yùn)行完,A調(diào)用D得到如圖

image-20200512010034024

如上圖熙揍,D覆蓋了之前B职祷、C的內(nèi)存。

正因?yàn)闂5膬?nèi)存會(huì)被覆蓋,所以在C語言中才不能返回局部變量的指針有梆。但是Go因?yàn)橛袃?nèi)存逃逸是尖,則不會(huì)有這樣的問題。

key note

  1. 每個(gè)進(jìn)程地址一致是靠虛擬內(nèi)存機(jī)制保證的泥耀。
  2. caller save,callee save

4. 匯編指令

https://mp.weixin.qq.com/s?__biz=MzU1OTg5NDkzOA==&mid=2247483693&idx=1&sn=e5398ae82e2f3484bea5e8858b1a9cd7&scene=19#wechat_redirect

5. Go 匯編語言

go中的runtime有部分代碼是用匯編寫的饺汹。但是它的匯編語言并非針對(duì)特定體系結(jié)構(gòu)的匯編代碼,而是go語言引入的的偽匯編plan9.

TODO: go blog about plan9

go匯編和AT&T 差不多痰催,但是也有區(qū)別,下面主要對(duì)其做說明逸吵。(因?yàn)楹罄m(xù)調(diào)度分析的時(shí)候需要看匯編)

寄存器映射

image.png

除此go還引入幾個(gè)虛擬寄存器:(所謂虛擬寄存器扫皱,就是沒有任何硬件寄存器與之對(duì)應(yīng))啸罢。這些寄存器一般用來存放內(nèi)存地址胎食,引入他們的目的是為了方便程序員和編譯器 用來定位內(nèi)存中的代碼和數(shù)據(jù)厕怜。

FP虛擬寄存器

主要用來引用函數(shù)參數(shù)。前面提到過go的參數(shù)都在棧上琅捏。所以引入FP可以方便我們獲取到參數(shù)地址柄延。

比如可以使用firstarg+0(FP)來引用調(diào)用者傳遞來的第一個(gè)參數(shù)缀程,用secondarg+8(FP)來引用第二個(gè)參數(shù)杨凑。這里``firstargsecondarg`都是無意義的符號(hào),編譯器不關(guān)心也不解讀蜒程。舉例:

go 中有個(gè)gogo函數(shù),接受一個(gè)gobuf指針

// src/runtime/stubs.go:129
func gogo(buf *gobuf)

對(duì)應(yīng)的匯編部分如下

// func gogo(buf *gobuf)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $16-8
    MOVQ    buf+0(FP), BX       // gobuf -> BX
    MOVQ    gobuf_g(BX), DX // gp.sched.g = dx
    ...

由上忌锯,可以看到通過FP獲取到參數(shù)汉规≌胧罚回想之前的棧碟狞,F(xiàn)P并不在當(dāng)前的函數(shù)棧幀上,其關(guān)系如圖频祝。

image-20200512162439879

SB虛擬寄存器:

上面的示例代碼中有TEXT runtime·gogo(SB), NOSPLIT, $16-8常空。其中SB保存的是程序地址空間的的起始地址漓糙。之前棧中代碼區(qū)的地方昆禽。這個(gè)SB寄存器保存的值就是代碼區(qū)的起始地址蝇庭,它主要用來定位全局符號(hào)哮内。Go匯編中的函數(shù)定義、函數(shù)調(diào)用纹因、全局變量以及對(duì)其引用都會(huì)用到這個(gè)SB寄存器辐怕。

函數(shù)的其他定義:

  1. TEXT runtime·gogo(SB):指明在代碼區(qū)定義了一個(gè)名字叫 gogo的全局函數(shù)从绘,該函數(shù)屬于runtime包
  2. NOSPLIT:指示編譯器不要再這個(gè)函數(shù)中插入檢查棧是否溢出的代碼僵井。(TODO)
  3. $16-8:16代表函數(shù)棧幀為16字節(jié),8代碼函數(shù)的參數(shù)和返回值一共8個(gè)字節(jié)农曲。

6. 函數(shù)調(diào)用過程

https://mp.weixin.qq.com/s?__biz=MzU1OTg5NDkzOA==&mid=2247483723&idx=1&sn=772960aa0d5ae4aa6921e9ff43fcb99f&scene=19#wechat_redirect

7. 系統(tǒng)調(diào)用

操作內(nèi)核

https://mp.weixin.qq.com/s/YPiYNPa3xVD9Il1HeB5pTw

8. 操作系統(tǒng)的線程和線程調(diào)度

要深入理解goroutine的調(diào)度器乳规,就需要對(duì)操作系統(tǒng)線程有個(gè)大致的了解暮的,因?yàn)間o的調(diào)度系統(tǒng)是建立在操作系統(tǒng)線程之上的淌实,所以接下來我們對(duì)其做一個(gè)簡單的介紹拆祈。

很難對(duì)線程下一個(gè)準(zhǔn)確且易于理解的定義,特別是對(duì)于從未接觸過多線程編程的讀者來說咙咽,要搞懂什么是線程可能并不是很容易犁珠,所以下面我們拋開定義直接從一個(gè)C語言的程序開始來直觀的看一下什么是線程互亮。之所以使用C語言豹休,是因?yàn)镃語言中我們一般使用pthread線程庫,而使用該線程庫創(chuàng)建的用戶態(tài)線程其實(shí)就是Linux操作系統(tǒng)內(nèi)核所支持的線程凤巨,它與go語言中的工作線程是一樣的敢茁,這些線程都由Linux內(nèi)核負(fù)責(zé)管理和調(diào)度留美,然后go語言在操作系統(tǒng)線程之上又做了goroutine伸刃,實(shí)現(xiàn)了一個(gè)二級(jí)線程模型捧颅。

什么時(shí)候發(fā)生調(diào)度

  1. 用戶使用系統(tǒng)調(diào)用
  2. 硬件中斷尤其是時(shí)鐘中斷

線程保存著程序運(yùn)行的上下文

-  寄存器中的值
-  下一條指令
-  棧

所以當(dāng)進(jìn)行線程切換的時(shí)候需要把這些都保存下

參考:

  1. 進(jìn)程/線程上下文切換會(huì)用掉你多少CPU
  2. 協(xié)程究竟比線程能省多少開銷碉哑?

9. 線程本地存儲(chǔ)

線程本地存儲(chǔ)又叫線程局部存儲(chǔ)扣典,其英文為Thread Local Storage慎玖,簡稱TLS

TLS 在用戶側(cè)代碼是一個(gè)變量凄吏,但是在編譯層次確實(shí)2個(gè)地址。所以用戶使用的時(shí)候就可以使用一個(gè)變量訪問2個(gè)地址图柏。

我們需要知道fs段基址是多少蚤吹,雖然我們可以用gdb命令查看fs寄存器的值随抠,但fs寄存器里面存放的是段選擇子(segment selector)而不是該段的起始地址


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拱她,一起剝皮案震驚了整個(gè)濱河市秉沼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌矗积,老刑警劉巖棘捣,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乍恐,死亡現(xiàn)場離奇詭異,居然都是意外死亡禁熏,警方通過查閱死者的電腦和手機(jī)瞧毙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門宙彪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來有巧,“玉大人,你說我怎么就攤上這事男图⊙钒剩” “怎么了岂傲?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵镊掖,是天一觀的道長。 經(jīng)常有香客問我症虑,道長侦讨,這世上最難降的妖魔是什么苟翻? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任崇猫,我火速辦了婚禮,結(jié)果婚禮上蜡歹,老公的妹妹穿的比我還像新娘。我一直安慰自己汗洒,他們只是感情好父款,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布世杀。 她就那樣靜靜地躺著肝集,像睡著了一般。 火紅的嫁衣襯著肌膚如雪所刀。 梳的紋絲不亂的頭發(fā)上捞挥,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天树肃,我揣著相機(jī)與錄音,去河邊找鬼雏掠。 笑死乡话,一個(gè)胖子當(dāng)著我的面吹牛耳奕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播闸婴,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼邪乍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了榜配?” 一聲冷哼從身側(cè)響起蛋褥,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤烙心,失蹤者是張志新(化名)和其女友劉穎柏靶,沒想到半個(gè)月后溃论,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡炬转,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菲驴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡先煎,死狀恐怖薯蝎,靈堂內(nèi)的尸體忽然破棺而出谤绳,到底是詐尸還是另有隱情,我是刑警寧澤消略,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布艺演,位于F島的核電站,受9級(jí)特大地震影響啄寡,放射性物質(zhì)發(fā)生泄漏哩照。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一识藤、第九天 我趴在偏房一處隱蔽的房頂上張望痴昧。 院中可真熱鬧冠王,春花似錦、人聲如沸豪娜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至挎狸,卻和暖如春琳猫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背统刮。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國打工侥蒙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匀奏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓瑞佩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親炬丸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蜒蕾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容