[譯]Go 調度器: M, P 和 G

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

這是另一篇關於Go調度器(scheduler)的文章。 原文: GO SCHEDULER: MS, PS & GS by Uber工程師 Povilas。

網上已經有很多關於Go調度器的文章了, 比如Golang調度器源碼分析,多看一些,可以加深記憶,也可以對比查看文章中是否有不準確的地方,更全面的瞭解Go的調度器。

我決定深入瞭解Go的內部機制, 因為很長時間沒人寫關於Go scheduler的文章了, 我覺得這是一個很有趣的知識點,所以讓我們開始吧。

基礎知識

Go的運行時管理著調度、記憶體回收以及goroutine的運行環境。本文只關注於調度器。

運行時負責運行goroutine並把它們影射到作業系統的線程上。goroutine比線程還輕量, 啟動的時候花費很少。每個goroutine都是由一個G結構來表示,
這個結構體的欄位用來跟蹤此goroutine的棧(stack)和狀態,所以你可以認為G = goroutine

運行時管理著G並把它們映射到Logical Processor(稱之為P). P可以看作是一個抽象的資源或者一個上下文,它需要擷取以便作業系統線程(稱之為M)可以運行G

通過runtime.GOMAXPROCS (numLogicalProcessors)可以控制多少P可以擷取。如果你需要調整這個參數(大部分情況下你無需調整), 只設定一次, 因為它需要 STW gc pause。

本質上,作業系統運行線程,線程運行你的代碼。Go的技巧是編譯器會在Go運行時的一些地方插入系統調用, (比如通過channel發送值,調用runtime包等),所以Go可以通知調度器執行特定的操作。

的理解來自 Analysis of the Go runtime scheduler

M、P 和 G 之間的互動

MPG之間的互動有點複雜。看看下面這張來自 Gao Chao的go runtime scheduler 投影片中的一張圖:

可以看到,Go運行時存在兩種類型的queue: 一種是一個全域的queue(在schedt結構體中,很少用到), 一種是每個P都維護自己的G的queue。

為了運行goroutine, M需要持有上下文PM會從P的queue彈出一個goroutine並執行。

當你建立一個新的goroutine的時候(go func()方法),它會被放入P的queue。當然還有一個 work-stealing調度演算法,當M執行了一些G後,如果它的queue為空白,它會隨機的選擇另外一個P,從它的queue中取走一半的G到自己的queue中執行。(偷!)

當你的goroutine執行阻塞的系統調用的時候(syscall),阻塞的系統調用會中斷(intercepted),如果當前有一些G在執行,運行時會把這個線程從P中摘除(detach),然後再建立一個新的作業系統的線程(如果沒有閒置線程可用的話)來服務於這個P

當系統調用繼續的時候,這個goroutine被放入到本地運行queue,線程會park它自己(休眠), 加入到空閑線程中。

如果一個goroutine執行網路調用,運行時會做類似的動作。調用會被中斷,但是由於Go使用整合的network poller,它有自己的線程,所以還給它。

Go運行時會在下面的goroutine被阻塞的情況下運行另外一個goroutine:

- blocking syscall (for example opening a file),
- network input,
- channel operations,
- primitives in the sync package.

調度器跟蹤調試

Go可以跟蹤運行時的調度器,這是通過 GODEBUG 環境變數實現的:

1
$ GODEBUG=scheddetail=1,schedtrace=1000 ./program

下面是輸出的例子:

123456789101112
SCHED 0ms: gomaxprocs=8 idleprocs=7 threads=2 spinningthreads=0 idlethreads=0 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0  P0: status=1 schedtick=0 syscalltick=0 m=0 runqsize=0 gfreecnt=0  P1: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  P2: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  P3: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  P4: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  P5: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  P6: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  P7: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 helpgc=0 spinning=false blocked=false lockedg=-1  M0: p=0 curg=1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 helpgc=0 spinning=false blocked=false lockedg=1  G1: status=8() m=0 lockedm=0

注意輸出使用了 GMP的概念以及她們的狀態, 比如P的queue的大小。 如果你不想關心這些細節,你可以使用:

1
$ GODEBUG=schedtrace=1000 ./program

William Kennedy寫了一篇很好的文章, 解釋了這些細節。

當然,還有一個go自己的工具 go tool trace, 它有一個UI, 允許你查看你的程式和運行時的狀況。你可以閱讀這篇文章: Pusher。

參考文檔

  1. Slides by Matthew Dale
  2. Columbia University paper: Analysis of the Go runtime scheduler
  3. Scalable Go Scheduler Design Doc
  4. Hacker news chat which explains a lot
  5. go runtime scheduler slides by Gao Chao
  6. Morsmachine article

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.