精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

深度解密Go語言之基于信號的搶占式調度

開發 后端
在主 goroutine 里,先用 GoMAXPROCS 函數拿到 CPU 的邏輯核心數 threads。這意味著 Go 進程會創建 threads 個數的 P。

[[398986]]

本文轉載自微信公眾號「碼農桃花源」,作者qcrao。轉載本文請聯系碼農桃花源公眾號。

不知道大家在實際工作中有沒有遇到過老版本 Go 調度器的坑:死循環導致程序“死機”。我去年就遇到過,并且搞出了一起 P0 事故,還寫了篇弱智的找 bug 文章。

識別事故的本質,并且用一個非常簡單的示例展示出來,是功力的一種體現。那次事故的原因可以簡化成如下的 demo:

demo-1

我來簡單解釋一下上面這個程序。在主 goroutine 里,先用 GoMAXPROCS 函數拿到 CPU 的邏輯核心數 threads。這意味著 Go 進程會創建 threads 個數的 P。接著,啟動了 threads 個數的 goroutine,每個 goroutine 都在執行一個無限循環,并且這個無限循環只是簡單地執行 x++。

接著,主 goroutine sleep 了 1 秒鐘;最后,打印 x 的值。

你可以自己思考一下,輸出會是什么?

如果你想出了答案,接著再看下面這個 demo:

demo-2

我也來解釋一下,在主 goroutine 里,只啟動了一個 goroutine(雖然程序里用了一個 for 循環,但其實只循環了一次,完全是為了和前面的 demo 看起來更協調一些),同樣執行了一個 x++ 的無限 for 循環。

和前一個 demo 的不同點在于,在主 goroutine 里,我們手動執行了一次 GC;最后,打印 x 的值。

如果你能答對第一題,大概率也能答對第二題。

下面我就來揭曉答案。

其實我留了一個坑,我沒說用哪個版本的 Go 來運行代碼。所以,正確的答案是:

Go 版本 demo-1 demo-2
1.13 卡死 卡死
1.14 0 0

這個其實就是 Go 調度器的坑了。

假設在 demo-1 中,共有 4 個 P,于是創建了 4 個 goroutine。當主 goroutine 執行 sleep 的時候,剛剛創建的 4 個 goroutine 馬上就把 4 個 P 霸占了,執行死循環,而且竟然沒有進行函數調用,就只有一個簡單的賦值語句。Go 1.13 對這種情況是無能為力的,沒有任何辦法讓這些 goroutine 停下來,進程對外表現出“死機”。

demo-1 示意圖

由于 Go 1.14 實現了基于信號的搶占式調度,這些執行無限循環的 goroutine 會被調度器“拿下”,P 就會空出來。所以當主 goroutine sleep 時間到了之后,馬上就能獲得 P,并得以打印出 x 的值。至于 x 為什么輸出的是 0,不太好解釋,因為這是一種未定義(有數據競爭,正常情況下要加鎖)的行為,可能的一個原因是 CPU 的 cache 沒有來得及更新,不過不太好驗證。

理解了這個 demo,第二個 demo 其實是類似的道理:

demo-2 示意圖

當主 goroutine 主動觸發 GC 時,需要把所有當前正在運行的 goroutine 停止下來,即 stw(stop the world),但是 goroutine 正在執行無限循環,沒法讓它停下來。當然,Go 1.14 還是可以搶占掉這個 goroutine,從而打印出 x 的值,也是 0。

Go 1.14 之前的版本,能否搶占一個正在執行死循環的 goroutine 其實是有講究的:

能否被搶占,不是看有沒有調用函數,而是看函數的序言部分有沒有插入擴棧檢測指令。

如果沒有調用函數,肯定不會被搶占。

有些雖然也調用了函數,但其實不會插入檢測指令,這個時候也不會被搶占。

像前面的兩個 demo,不可能有機會在函數擴棧檢測期間主動放棄 CPU 使用權,從而完成搶占,因為沒有函數調用。具體的過程后面有機會再寫一篇文章詳細講,本文主要看基于信號的搶占式調度如何實現。

preemptone

一方面,Go 進程在啟動的時候,會開啟一個后臺線程 sysmon,監控執行時間過長的 goroutine,進而發出搶占。另一方面,GC 執行 stw 時,會讓所有的 goroutine 都停止,其實就是搶占。這兩者都會調用 preemptone() 函數。

preemptone() 函數會沿著下面這條路徑:

  1. preemptone->preemptM->signalM->tgkill 

向正在運行的 goroutine 所綁定的的那個 M(也可以說是線程)發出 SIGURG 信號。

注冊 sighandler

每個 M 在初始化的時候都會設置信號處理函數:

  1. initsig->setsig->sighandler 

信號執行過程

我們從“宏觀”層面看一下信號的執行過程:

信號執行過程

主程序(線程)正在“勤勤懇懇”地執行指令:它已經執行完了指令 m,接著就要執行指令 m+1 了……不幸在這個時候發生了,線程收到了一個信號,對應圖中的 ①。

接著,內核會接管執行流,轉而去執行預先設置好的信號處理器程序,對應到 Go 里,就是執行 sighandler,對應圖中的 ② 和 ③。

最后,執行流又交到線程手上,繼續執行指令 m+1,對應圖中的 ④。

這里其實涉及到了一些現場的保護和恢復,內核都幫我們搞定了,我們不用操心。

dosigPreempt

當線程收到 SIGURG 信號的時候,就會去執行 sighandler 函數,核心是 doSigPreempt 函數。

  1. func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { 
  2.     ... 
  3.      
  4.     if sig == sigPreempt && debug.asyncpreemptoff == 0 { 
  5.   doSigPreempt(gp, c) 
  6.  } 
  7.   
  8.  ... 

doSigPreempt 這個函數其實很短,一會兒就執行完了。

  1. func doSigPreempt(gp *g, ctxt *sigctxt) { 
  2.  ... 
  3.  if ok, newpc := isAsyncSafePoint(gp, ctxt.sigpc(), ctxt.sigsp(), ctxt.siglr()); ok { 
  4.   // Adjust the PC and inject a call to asyncPreempt. 
  5.   ctxt.pushCall(funcPC(asyncPreempt), newpc) 
  6.  } 
  7.  ... 

isAsyncSafePoint 函數會返回當前 goroutine 能否被搶占,以及從哪條指令開始搶占,返回的 newpc 表示安全的搶占地址。

接著,pushCall 調整了一下 SP,設置了幾個寄存器的值就返回了。按理說,返回之后,就會接著執行指令 m+1 了,但那還怎么實現搶占呢?其實魔法都在 pushCall 這個函數里。

pushCall

在分析這個函數之前,我們需要先復習一下 Go 函數的調用規約,重點回顧一下 CALL 和 RET 指令就行了。

call 和 ret 指令

call 指令可以簡單地理解為 push ip + JMP。這個 ip 其實就是返回地址,也就是調用完子函數接下來該執行啥指令的地址。所以 push ip 就是在 call 一個子函數之前,將返回地址壓入棧中,然后 JMP 到子函數的地址執行。

ret 指令和 call 指令剛好相反,它將返回地址從棧上 pop 到 IP 寄存器,使得 CPU 從這個地址繼續執行。

理解了 call 和 ret,我們再來分析 pushCall 函數:

  1. func (c *sigctxt) pushCall(targetPC, resumePC uintptr) { 
  2.  // Make it look like we called target at resumePC. 
  3.  sp := uintptr(c.rsp()) 
  4.  sp -= sys.PtrSize 
  5.  *(*uintptr)(unsafe.Pointer(sp)) = resumePC 
  6.  c.set_rsp(uint64(sp)) 
  7.  c.set_rip(uint64(targetPC)) 

注意看這行注釋:

  1. // Make it look like we called target at resumePC. 

它清晰地說明了這個函數的作用:讓 CPU 誤以為是 resumePC 調用了 targetPC。而這個 resumePC 就是上一步調用 isAsyncSafePoint 函數返回的 newpc,它代表我們搶占 goroutine 的指令地址。

前兩行代碼將 SP 下移了 8 個字節,并且把 resumePC 入棧(注意,它其實是一個返回地址),接著把 targetPC 設置到 ip 寄存器,sp 設置到 SP 寄存器。這使得從內核返回到用戶態執行時,不是從指令 m+1,而是直接從 targetPC 開始執行,等到 targetPC 執行完,才會返回到 resumePC 繼續執行。整個過程就像是 resumePC 調用了 targetPC 一樣。而 targetPC 其實就是 funcPC(asyncPreempt),也就是搶占函數。

于是我們可以看到,信號處理器程序 sighandler 只是將一個異步搶占函數給“安插”進來了,而真正的搶占過程則是在 asyncPreempt 函數中完成。

異步搶占

當執行完 sighandler,執行流再次回到線程。由于 sighandler 插入了一個 asyncPreempt 的函數調用,所以 goroutine 原本的任務就得不到推進,轉而執行 asyncPreempt 去了:

asyncPreempt 調用鏈路

mcall(fn) 的作用是切到 g0 棧去執行函數 fn, fn 永不返回。在 mcall(gopreempt_m) 這里,fn 就是 gopreempt_m。

gopreempt_m 直接調用 goschedImpl:

goschedImpl

dropg

最精彩的部分就在 goschedImpl 函數。它首先將 goroutine 的狀態從 running 改成 runnable;接著調 dropg 將 g 和 m 解綁;然后調用 globrunqput 將 goroutine 丟到全局可運行隊列,由于是全局可運行隊列,所以需要加鎖。最后,調用 schedule() 函數進入調度循環。關于調度循環,可以看這篇文章。

運行 schedule 函數用的是 g0 棧,它會去尋找其他可運行的 goroutine,包括從當前 P 本地可運行隊列獲取、從全局可運行隊列獲取、從其他 P 偷等方式找到下一個可運行的 goroutine 并執行。

至此,這個線程就轉而去執行其他的 goroutine,當前的 goroutine 也就被搶占了。

那被搶占的這個 goroutine 什么時候會再次得到執行呢?

因為它已經被丟到全局可運行隊列了,所以它的優先級就會降低,得到調度的機會也就降低,但總還是有機會再次執行的,并且它會從調用 mcall 的下一條指令接著執行。

還記得 mcall 函數的作用嗎?它會切到 g0 棧執行 gopreempt_m,自然它也會保存 goroutine 的執行進度,其實就是 SP、BP、PC 寄存器的值,當 goroutine 再次被調度執行時,就會從原來的執行流斷點處繼續執行下去。

總結

本文講述了 Go 語言基于信號的異步搶占的全過程,一起來回顧下:

M 注冊一個 SIGURG 信號的處理函數:sighandler。

sysmon 線程檢測到執行時間過長的 goroutine、GC stw 時,會向相應的 M(或者說線程,每個線程對應一個 M)發送 SIGURG 信號。

收到信號后,內核執行 sighandler 函數,通過 pushCall 插入 asyncPreempt 函數調用。

回到當前 goroutine 執行 asyncPreempt 函數,通過 mcall 切到 g0 棧執行 gopreempt_m。

將當前 goroutine 插入到全局可運行隊列,M 則繼續尋找其他 goroutine 來運行。

 

被搶占的 goroutine 再次調度過來執行時,會繼續原來的執行流。

 

責任編輯:武曉燕 來源: 碼農桃花源
相關推薦

2020-12-31 09:06:44

Go語言Reflect

2021-10-03 22:18:14

Go語言整數

2021-10-23 06:42:14

Go語言接口

2025-01-15 09:13:53

2022-03-28 13:34:26

Go泛型部署泛型

2021-10-09 07:52:01

Go程序重命名

2021-10-16 17:53:35

Go函數編程

2010-01-14 10:34:02

C++語言

2021-10-18 10:53:26

Go 代碼技術

2022-09-04 23:24:45

Go語言監控

2024-01-05 20:46:14

2013-08-20 10:11:20

Go系統管理員

2022-03-13 23:51:39

Web項目Go

2024-01-08 08:23:07

Go語言代碼

2012-02-13 10:03:31

編程開發

2012-08-13 14:13:46

2024-10-29 08:52:01

Go協作式調度

2017-05-11 14:05:25

Consul分布式信號量

2023-12-15 14:38:00

GoRust編程語言

2021-08-02 22:31:24

Go語言Append
點贊
收藏

51CTO技術棧公眾號

欧美激情三级免费| 日本精品一区二区三区高清| 北条麻妃高清一区| 六月丁香婷婷综合| 日韩欧美伦理| 欧美一级理论性理论a| 精品中文字幕av| 在线观看免费高清完整| 国产999精品久久久久久绿帽| 欧美一级视频在线观看| 国产黄色录像片| 老司机aⅴ在线精品导航| 精品婷婷伊人一区三区三| 国产一级不卡视频| 69av亚洲| 99久免费精品视频在线观看 | 91免费公开视频| 黑人久久a级毛片免费观看| 色94色欧美sute亚洲线路二| 国产av熟女一区二区三区| 国产三级在线免费观看| 成人免费看的视频| 国产免费一区二区三区在线观看 | 在线欧美日韩国产| 国产一区二区三区小说| 日韩免费啪啪| 国产色综合久久| 精品999在线观看| 国产精品特级毛片一区二区三区| 久久这里只有| 久久99亚洲精品| 亚洲怡红院在线观看| 性欧美lx╳lx╳| 亚洲第一精品电影| av在线网站免费观看| 久久91视频| 在线免费一区三区| 91久久国产婷婷一区二区| 亚洲国产精品一区二区第四页av| 99久久婷婷国产一区二区三区| 爽好久久久欧美精品| 91精品国产91| 日韩精品一区二区在线播放| 欧美激情无毛| 欧美日韩成人网| 曰本女人与公拘交酡| 99精品全国免费观看视频软件| 国产亚洲人成网站在线观看| 大又大又粗又硬又爽少妇毛片| 日本中文字幕在线一区| 亚洲黄色成人网| 一级特级黄色片| 亚洲春色h网| 亚洲女人被黑人巨大进入al| 成年人网站免费看| 亚洲区小说区图片区qvod按摩| 亚洲国产日韩欧美在线图片| 国产xxxx视频| 一区二区美女| 伊人一区二区三区久久精品| 99电影在线观看| 香蕉污视频在线观看| 9国产精品视频| 77777亚洲午夜久久多人| 91视频免费网址| 久久久久国产一区二区| 国产激情久久久久| 亚洲字幕av一区二区三区四区| 精品一区二区成人精品| 99在线观看视频| 熟妇人妻系列aⅴ无码专区友真希| 99精品视频在线播放观看| 久久久久资源| 懂色一区二区三区| 亚洲另类在线制服丝袜| 97碰在线视频| 欧美aa视频| 777亚洲妇女| 年下总裁被打光屁股sp| 日韩大片在线免费观看| 中文字幕欧美日韩| 九九热精品在线观看| 午夜一区在线| 国产主播精品在线| 日本成人动漫在线观看| 欧美国产在线观看| 成人一区二区av| 亚洲天堂av影院| 欧美日韩高清在线播放| 美女扒开腿免费视频| 香蕉视频一区| 欧美成年人在线观看| 欧美精品亚洲精品日韩精品| 老妇喷水一区二区三区| 91在线观看免费观看 | 亚洲精品在线电影| 91网站免费入口| 亚洲五月综合| 人九九综合九九宗合| 国产精品亚洲欧美在线播放| 91麻豆福利精品推荐| 亚洲亚洲精品三区日韩精品在线视频 | 久草资源在线视频| 日韩黄色小视频| 风间由美久久久| 成人在线观看网站| 亚洲成av人在线观看| 天天干天天综合| 国产美女撒尿一区二区| www.日韩不卡电影av| 日本熟女一区二区| 九九视频精品免费| 欧美中文娱乐网| www.51av欧美视频| 欧美一卡二卡在线| 久久精品国产亚洲av麻豆| 欧美另类女人| 成人疯狂猛交xxx| 国内三级在线观看| 亚洲成在人线在线播放| 一级做a爱视频| 欧美日韩亚洲在线观看| 97精品一区二区三区| www.日日夜夜| 亚洲欧洲国产日本综合| 簧片在线免费看| 九九久久精品| 538国产精品一区二区在线| 午夜精品久久久久久久91蜜桃| 国产精品私人自拍| 密臀av一区二区三区| 任你躁在线精品免费| 欧美福利视频网站| 国产wwwxxx| 亚洲欧美日韩久久精品| 中文字幕第一页在线视频| 欧美码中文字幕在线| 日本一区二区三区四区视频| 色网站免费观看| 午夜精品一区二区三区免费视频| 97免费公开视频| 五月激情久久久| 91精品视频观看| 免费黄网站在线| 3d动漫精品啪啪1区2区免费| www.xx日本| 久久99久久99小草精品免视看| 亚洲人成77777| 日韩一级特黄| 久久影院免费观看| av高清一区二区| 亚洲精品成人天堂一二三| 欧美性受xxxx黒人xyx性爽| 这里只有精品在线| 国产精品国产三级欧美二区| 超黄网站在线观看| 亚洲大胆人体av| 欧美日韩一二三四区| 久久久天堂av| 依人在线免费视频| 久久久久久免费视频| 99久久精品久久久久久ai换脸| 怡红院在线观看| 亚洲国产小视频在线观看| 日韩欧美激情视频| 久久久久国产精品人| 日本久久久久久久久久久久| 99久久夜色精品国产亚洲狼| 91一区二区三区| heyzo高清在线| 亚洲人成人99网站| 亚洲中文字幕在线观看| 一区二区三区四区亚洲| 最近中文字幕无免费| 久久综合影音| 无码人妻精品一区二区三区99v| 秋霞影院一区| 91av在线免费观看| 免费成人黄色| 亚洲第一精品福利| wwwwww在线观看| 夜夜爽夜夜爽精品视频| 国精产品一区一区三区免费视频 | 天天色天天操综合| 国产真实乱人偷精品人妻| 精品一区二区三区免费毛片爱| 成人免费a级片| 成人高清电影网站| 91久久爱成人| 欧美xxx视频| 不卡毛片在线看| 看电影就来5566av视频在线播放| 欧美日韩国产电影| 精品人妻一区二区三区免费看 | 亚洲欧美国产va在线影院| 国产精品露脸视频| 亚洲五码中文字幕| 91视频免费在观看| 岛国精品在线观看| 五月婷婷六月合| 亚洲三级网站| 91社在线播放| 蜜乳av综合| 国产精品日韩一区二区| 先锋影音网一区二区| 55夜色66夜色国产精品视频| 麻豆免费在线观看| 亚洲新声在线观看| 成人无码一区二区三区| 9191久久久久久久久久久| 精品国产乱子伦| 亚洲国产一区二区三区| 激情高潮到大叫狂喷水| 91麻豆国产在线观看| 中国特级黄色片| 精品一区二区三区在线观看国产| 蜜臀av午夜一区二区三区| 欧美黄污视频| 亚洲在线不卡| 激情国产在线| 91丨porny丨蝌蚪视频| 亚洲欧洲日本精品| 免费国产自线拍一欧美视频| 中文字幕日韩精品无码内射| 97精品视频| 日韩中文字幕av在线| 日韩美脚连裤袜丝袜在线| 国产精品久久亚洲7777| 日韩三级久久| 亚洲一区二区三区乱码aⅴ蜜桃女| 久草视频国产在线| 在线观看免费黄色片| 在线观看的网站你懂的| 日韩在线www| 高清美女视频一区| 亚洲香蕉在线观看| 韩国中文免费在线视频| 亚洲色图18p| 国产精品久久久久久在线观看| 久久成人精品| 日韩精品视频一区二区在线观看| 在线成人www免费观看视频| www.男人天堂网| 欧美激情偷拍| 日本人妻伦在线中文字幕| 欧美一区二区三区久久精品茉莉花| 综合一区中文字幕| 婷婷综合网站| 成人免费看片视频在线观看| 你懂的亚洲视频| 免费日韩在线观看| 国产一区二区三区四区老人| 青青在线视频免费观看| 一区在线视频观看| 精品国产一二三四区| 国产精品一二| 日韩免费高清在线| 麻豆精品国产91久久久久久| 999在线观看| 国产成人综合在线| 插我舔内射18免费视频| 久久夜色精品国产噜噜av| 日本少妇高潮喷水xxxxxxx| 国产欧美日韩在线| 91大神福利视频| 亚洲图片欧美激情| 免费麻豆国产一区二区三区四区| 亚洲国产中文字幕在线视频综合| www.国产色| 欧美日韩成人在线| 亚洲av永久纯肉无码精品动漫| 亚洲第一男人天堂| av在线中文| 久久99亚洲热视| 美女扒开腿让男人桶爽久久软| 国产精品99一区| 国产一区二区av在线| 精品视频导航| 日本一区二区在线看| 久久久久久av无码免费网站下载| 国产精品婷婷| 樱花草www在线| 99久久久精品免费观看国产蜜| 成人无码av片在线观看| 亚洲精品日韩一| 手机看片久久久| 在线不卡a资源高清| 日韩专区第一页| 中文字幕日韩综合av| 国产丝袜在线观看视频| 日韩av毛片网| 日本一区二区三区电影免费观看| 你懂的视频在线一区二区| 99国产**精品****| 成人免费在线小视频| 国内久久精品视频| 欧美成人午夜精品免费| 亚洲色欲色欲www在线观看| 圆产精品久久久久久久久久久 | 一级片在线免费播放| 日韩久久久精品| av片在线免费观看| 7m第一福利500精品视频| 在线观看欧美| 秋霞久久久久久一区二区| 国产综合精品| 日韩av片免费观看| 国产日韩欧美不卡在线| 国产亚洲欧美久久久久| 8v天堂国产在线一区二区| 国产日本在线| 97视频在线观看免费| 日韩视频一二区| 亚洲一区二区三区在线观看视频| 性高湖久久久久久久久| 最新日本中文字幕| 亚洲天堂成人网| 亚洲精品无码久久久久| 精品亚洲一区二区三区在线播放 | 亚洲一区二区av在线| 97成人在线观看| 在线电影av不卡网址| 三上悠亚激情av一区二区三区| 国产精品一区二区欧美黑人喷潮水 | 精品福利av导航| 免费啪视频在线观看| 中文无码久久精品| 五月天激情视频在线观看| 久久久久久亚洲综合影院红桃| 国产午夜福利精品| 日韩一卡二卡三卡四卡| 欧美高清视频| 国产一区在线播放| 久久在线免费| 激情视频免费网站| 日本一区二区三级电影在线观看 | 日韩高清一级片| 亚洲第一成人网站| 欧美午夜美女看片| 水莓100在线视频| 2019日本中文字幕| 日日狠狠久久偷偷综合色| 我的公把我弄高潮了视频| 成人小视频免费观看| 国产网友自拍视频| 亚洲激情久久久| 国产在线精彩视频| 国产专区一区二区| 亚洲专区免费| 高潮毛片无遮挡| 欧美综合一区二区三区| www.黄在线观看| 国产精品第一视频| 成人在线电影在线观看视频| 欧美成年人视频在线观看| 亚洲欧美一区二区在线观看| 国产精品无码天天爽视频| 九九精品视频在线观看| 91精品短视频| 三上悠亚久久精品| 久久亚洲一级片| 中文字幕观看在线| 操91在线视频| 国产欧美三级电影| 精品国产成人av在线免| 久久久久久久久久久电影| 好吊色欧美一区二区三区四区| 国产毛片aaa| 久久中文欧美| 91免费精品国偷自产在线| 一级淫片免费看| 国产福利一区二区三区视频| 精品国产第一页| 波多野结衣在线网站| 亚洲精品日韩专区silk | 老熟妇一区二区三区啪啪| 韩国欧美一区| 久久久91精品国产| 欧美国产日韩在线观看成人| 91黄视频在线| 久做在线视频免费观看| 国产 高清 精品 在线 a| 国产女优一区| 久久噜噜色综合一区二区| 精品日韩av一区二区| 亚洲人免费短视频| 大陆极品少妇内射aaaaaa| 久久久精品影视| 99产精品成人啪免费网站| 992tv在线成人免费观看| 欧洲美女日日| 美女搡bbb又爽又猛又黄www| 日本二三区不卡| 日韩伦理电影网站| 日韩久久久久久久| 成人sese在线| 国产精品久久久久久免费免熟 | 波多野结依一区| 亚洲不卡1区| 国产风韵犹存在线视精品|