天天看點

C1000K之Libevent源碼分析



說到異步IO,高并發之類的名詞, 可能很多人第一反應就是 select, poll, epoll, kqueue 之類的底層代碼庫。 但是其實除非你要寫一個 Nginx 性能級别的伺服器, 否則直接使用 epoll 之類的還是太過底層, 諸多不便,要榨幹整個異步程式設計的高并發性能還需要開發很多相關元件, 而

運作事件循環的函數肯定是阻塞函數, 拿 linux 平台來說, libevent 的事件循環其實就是循環調用 epoll_wait 函數,

如果隻有一個單線程的話,一旦調用了 event_base_dispatch 之後,這個線程就會被事件完完全全的霸占, 無法進行任何其他的操作。

如果我們需要臨時手動激活任何其他事件的話, 則需要借助另一個線程來操作(因為主線程仍然在阻塞等待中)。

在 event_active 函數的解釋裡面就有一句話說的就是這個事情:

這裡的事件指的就是 <code>struct event</code> 資料結構。

事件可以注冊的各種信号如下:

幾乎所有其它更上層的資料結構都是基于 struct event 的包裝來完成的。

evhttp_request

evhttp_connection

bufferevent

evbuffer

從上到下是從高層到底層的關系, 下文的順序也是從高層往底層分析。

evhttp_request 和 evhttp_connection 的關系很簡單,拿協定棧來對比的話, 前者代表的是 HTTP 協定,即應用層協定, 後者代表的是 TCP 協定,即傳輸層協定。 前者需要管理所有和 HTTP 相關的資料内容,比如 HTTP header 資料和 body 資料。

evhttp_connection 結構裡面含有 <code>enum evhttp_connection_state state</code> 變量, 這個和

bufferevent 就是包裝了 EV_READ event 和 EV_WRITE event , 并且帶有讀寫緩沖區(evbuffer)的更高層的機關。

我覺得上面代碼就非常簡潔易懂了, 兩個讀寫時間沒什麼好說的, 兩個讀寫緩沖區也是必須的(evbuffer的實作在後面會談到), 三個回調函數就是核心。 讀和寫的回調函數沒什麼好說的, 唯一需要注意的是

<code>bufferevent_event_cb errorcb</code> 這個回調函數是必須注冊的, 它關系到當該 bufferevent 對應的時間發生讀寫外的任何行為(比如socket關閉)時, 都會觸發。

整理一遍 bufferevent 的事件處理過程就是:

當可讀事件發生時,調用 readcb 将 socket 的資料 通過 recv 讀出來存入 input 緩沖區;

當可寫事件發生時,調用 writecb 将 output 緩沖區裡面的資料通過 socket send 發送出去;

當其他事件發生時,比如 socket close 發生,進行相應的資料清理退出工作。

基本上的異步IO服務裡的buffer都是一個德行(包括Nginx也是這樣), 都是即是數組又是連結清單(類似C++ STL裡面的deque)。 對于 libevent 來說, evbuffer 是一個連結清單,管理整個緩沖區的頭指針和尾指針,

對于 evbuffer 這個連結清單的每個單元,也就是 evbuffer_chain 來說, 則是數組(連續記憶體空間),

對于我個人而言,讀源碼的時候主要是從核心資料結構入手, 如果了解了這幾個核心資料結構, 一般就能猜到這些資料結構的相關函數都有哪些。 可以圍繞着這些結構去找相關的函數為己所用。

<a href="http://yanyiwu.com/work/2014/12/10/asyncronous-io-libevent.html">http://yanyiwu.com/work/2014/12/10/asyncronous-io-libevent.html</a>