第 64 期深入淺出 Golang Runtime

mai_yang發表於2020-02-13

文章來自於:https://reading.developerlearning.cn/reading/64-2019-10-24-go-runtime/

分享者:郝以奮 @ 騰訊 NOW 直播

觀看視訊

Go 夜讀第 64 期深入淺出 Golang Runtime

內容簡介

本次分享將會對 go runtime 的排程,記憶體分配,gc 做一些細節上的講解,同時也需要參與者對 runtime 有一些初步瞭解。

內容大綱

  • Golang Runtime 是什麼,其發展歷程;
  • 排程的實質和關鍵資料結構,函式;
  • 記憶體分配中 mspan, mheap, mcentral, mcache 等資料結構
  • Golang GC 發展,Golang 三色標記實現的一些細節,元資訊,寫屏障,1.5 與 1.12 GC 的區別;
  • 一點優化思路與問題排查思路;
  • 總結及 question;
  • 平時我看 runtime 程式碼的一些方式;

分享嘉賓

郝以奮,yifhao, 騰訊 NOW 直播後臺開發,負責 NOW 直播 CPP+JAVA 雙棧 -> Golang 轉型:框架協同建設,業務功能定製,Go Mod 引入,服務模板,RPC 協議 Go Mod 化,服務模板,Golang 培訓,文件等。

目前 NOW 直播後臺有 300 多個 Go 服務。

分享資訊

時間:2019-10-17 21:00:00 ~ 23:10:00, UTC+8 分享 Slides:https://github.com/Frank-Hust/share

回看視訊

-


Q&A 總結

Q: 騰訊現在用 go 的多嗎?

多, 至少 2000 人的級別了,對 go 的接受度挺高的,使用人數在迅速增加,當然大部分團隊還是 cpp。

Q: 騰訊 NOW 直播 go 開發佔比多少?

我們都是從其他語言轉的,cpp,java->golang,一開始就寫 go 的比較少。基本上學習一下,一個星期就可以開始寫線上 go 服務了。目前新服務都是 go。

Q: 執行緒切換的開銷

執行緒切換大概在幾微秒級別,協程切換大概在百 ns 級別。

執行緒切換過程:

  1. 進入系統呼叫
  2. 排程器本身程式碼執行
  3. 執行緒上下文切換: PC, SP 等暫存器,棧,執行緒相關的一些區域性變數,還涉及一些 cache miss 的情況;
  4. 退出系統呼叫

協程切換不需要進入和退出系統呼叫, 在進行上下文切換時也更輕量, 只需要切換幾個暫存器, 協程 runtime.g 結構只有 40 多個欄位, 而執行緒的 task struct 有大概 300 個欄位.

可參考程式/執行緒上下文切換會用掉你多少 CPU? https://zhuanlan.zhihu.com/p/79772089

協程究竟比執行緒能省多少開銷? https://zhuanlan.zhihu.com/p/80037638

Q: 為啥是邊緣觸發, 而不是水平觸發的方式?

因為網路操作 ready 和未 ready 對於協程來說就是狀態的切換。 socket fd ready 了, 阻塞之上的協程就從 waiting 變成 runnable。 操作時 socket fd 未 ready,那協程就從 running 變成 waiting。 假如採取水平觸發,如果一個協程因為某個連線讀而變成 waiting 狀態,這個連線有資料後,與之關聯的協程就變成 ready,這個協程一直沒去讀資料,那水平觸發一直就會 poll 出來該 fd,沒必要。

Q: 記憶體什麼時候釋放?

記憶體釋放分兩步 沒有存活物件的 span 被 GC 回收, 歸還到 mheap 結構中,變成 free 的 page。 sysmon 協程會掃描,超過一段時間沒有再被使用的 page(1.12 機制有改變), 通過 madvise 系統呼叫告訴作業系統,這些 page 對應的實體記憶體不再需要了,可以與虛擬記憶體解綁,給其他分配使用。

Q: 0.1+13+0.3ms 三個時間的意思?

GCDEBUG=gctrace=1 會列印出 gc 相關的時間,這三個分別代表,gc 開始時第一個 stw 的 wall time, 併發標記的 wall time 以及 GC 標記結束階段 stw 的 wall time。

Q: [] byte 於 string 的黑魔法

底層資料共享,減少資料拷貝。 https://jaycechant.info/2019/golang-unsafe-cast-between-string-and-bytes/

Q: 之前說的 netpoll,被 gopark 掛起的 G 扔哪了,怎麼找到對應的 G,然後又怎麼扔給對應的 M 的 runQ 的?

並沒有扔哪裡去,也沒放在哪個佇列。 一個協程因為某個網路 fd 的操作阻塞時,會把該 fd 新增到 epoll 中,使用以下系統呼叫。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
typedef union epoll_data {
    void *ptr;
    int fd;
    __uint32_t u32;
    __uint64_t u64;
} epoll_data_t;

struct epoll_event {
    __uint32_t events; /* Epoll events */
    epoll_data_t data; /* User data variable */
};

go 在 epoll_event 中的 epoll_data_t 放了一個指標值,該指標指向一個包含 runtime.g 的結構體。 下次 epoll_wait 時,便可把該 epoll_data_t 也 poll 出來,相當於與該 fd 關聯的上下文,也就可以找到阻塞其上的協程。

不需要再放回對應的 M 的 runq 中,目前是通過 injectglist 放在全域性的 runq 中。


更多原創文章乾貨分享,請關注公眾號

更多原創文章乾貨分享,請關注公眾號
  • 第 64 期深入淺出 Golang Runtime
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章