1. 程式人生 > >libgo 原始碼剖析(1. libgo簡介與排程淺談)

libgo 原始碼剖析(1. libgo簡介與排程淺談)

閒談

協程是一個很早的概念了,早些年的遊戲行業中已經大規模地在使用,像lua、go這些語言中的協程原語已經相對比較完善了,一般來說直接使用就好,但是在系統後臺開發上,出現的時間並不長。
我是做C++方向的後臺開發,目前國內一些公司也開源了一些C++協程庫,但目前來說,還是在逐步完善的階段。最早接觸的C++協程庫是騰訊微信的 libco,可以說是相當輕量級的協程,網上關於libco的實現的文章也是相對較多,這裡的話不會去過多地敘述。


在網上查詢關於 libgo 的資料,關於程式碼實現的文章並沒有多少,因此,打算自己看程式碼總結,之後的文章中,可能會針libgo和libco的部分功能進行簡單對比,不足之處,希望讀者指出。

背景

因為工作需要,以前系統的非同步框架已經顯得不再那麼地具有擴充套件性,非同步使得原本很簡單的邏輯(讀->處理->寫),要拆分開來成多個階段,通過回撥來響應各個事件,因此有計劃地使用協程來代替。

為什麼最後選擇了libgo,而沒有選擇更加輕量級的libco ?網上有人給出過兩者的效能對比,從自旋鎖、協程的數量以及棧空間、執行緒支援等各個角度考慮,貌似libgo完勝。
libgo 原始碼剖析(1. libgo簡介與排程淺談)

圖片來源於網路

效能對比資料來源於網路,並不是說libco不好,也許各自應用的場景略有不同,libco幾千行的程式碼可以實現一個相對較完備的協程,而且經得住微信後臺龐大的資料流量,自有它的優勢。由於對 libgo 的程式碼正在研究當中,因此,暫不對兩者評價。

協程

不管是什麼樣的協程,最核心的內容,都是在系統發生阻塞的時候上層主動讓出CPU,切換就緒協程的上下文,簡要總結,有如下幾個方面:

  1. 上下文切換的實現
  2. 系統函式的hook;
  3. 協程排程;
  4. 時間管理;

libgo 原始碼

https://github.com/yyzybb537/libgo

libgo 目錄結構說明

[email protected]:~/code/libgo/libgo-master$ ll
total 64
[email protected]  1 muhui  staff  5913 11  7 11:20 CMakeLists.txt
[email protected]
1 muhui staff 1084 11 7 11:20 LICENSE [email protected] 1 muhui staff 8758 11 7 11:20 README.md [email protected] 1 muhui staff 4230 11 7 11:20 TODO [email protected] 4 muhui staff 128 11 7 11:20 imgs [email protected] 15 muhui staff 480 11 7 11:20 libgo [email protected] 8 muhui staff 256 11 7 11:20 test [email protected] 6 muhui staff 192 11 7 11:20 third_party [email protected] 20 muhui staff 640 11 7 11:20 tutorial [email protected] 4 muhui staff 128 11 7 11:20 vs_proj [email protected]:~/code/libgo/libgo-master$ cd libgo/ [email protected]:~/code/libgo/libgo-master/libgo$ ll total 16 [email protected] 4 muhui staff 128 11 7 11:20 cls [email protected] 19 muhui staff 608 11 7 11:20 common [email protected] 6 muhui staff 192 11 7 11:20 context [email protected] 1 muhui staff 1848 11 7 11:20 coroutine.h [email protected] 5 muhui staff 160 11 7 11:20 debug [email protected] 4 muhui staff 128 11 7 11:20 defer [email protected] 1 muhui staff 36 11 7 11:20 libgo.h [email protected] 4 muhui staff 128 11 7 11:20 netio [email protected] 5 muhui staff 160 11 7 11:20 pool [email protected] 8 muhui staff 256 11 7 11:20 scheduler [email protected] 7 muhui staff 224 11 7 11:20 sync [email protected] 4 muhui staff 128 11 7 11:20 task [email protected] 4 muhui staff 128 11 7 11:20 timer

libgo 做的較好的一點是增加了對windows 環境的支援等,我們只針對 Linux 環境做研究。

  • TODO:libgo 後續會逐步完善或增加的功能;
  • libgo:原始碼實現的主目錄,關於協程和排程策略的實現都在該目錄下;
  • test:測試程式碼;
  • tutorial:libgo 使用教程程式碼;
  • vs_proj:VS 環境下如何使用libgo。

    在libgo目錄下

    • task:協程的相關實現;
    • scheduler:協程排程的實現;
    • debug:libgo 自帶的除錯功能(用於協程狀態的定位等);
    • coroutine.h:對一些常用對方法進行了重定義。
  • netio:hook的系統呼叫;
  • context:上下文的切換;
  • pool:libgo 實現的連線池



libgo排程原理概述

我們知道,協程是使用者態執行緒,因此libco的話是不支援執行緒的。但在libgo中,執行緒同樣是支援的,這於它的排程方式有關。

首先我們要說的一點是,在libgo中,每個協程是一個task,libgo 的排程並不是直接作用於協程,是通過間接排程實現的。

libgo中有排程器(scheduler)和執行器(processer)的概念:

  1. 直接負責協程排程的是執行器,它會在協程阻塞的時候切出上下文,並切入一個就緒協程的上下文去繼續處理,當沒有可執行的協程時,執行器就會阻塞等待,當有新到來的任務時,會繼續處理;

  2. 負責管理執行器的是排程器,對排程器而言,每個執行器是一個單獨的執行緒,排程器做的最主要的工作,就是平衡各個執行器中的協程數量,防止飢餓效應,部分執行器過忙,部分執行器卻沒有task可執行,另外,如果某個執行器卡住,排程器也會將執行器中的可執行協程取出,放到負載最低的執行器上。

  3. 當然,排程器的個數的話是支援動態擴充套件的。

如下圖:
libgo 原始碼剖析(1. libgo簡介與排程淺談)

關於程式碼實現會在下一篇中敘述。