基於 epoll 設計相似 libevent 的異步 I/O 庫 - 接口

這篇文章能夠算是我在 GitHub 上一個工程的設計概要了。簡要說明了該工程的設計思路以及技術要點。git

本文章純原創,沒有參考資料。不過有設計過程當中記錄下的相關文章:程序員

使用 pipe 在程序正文中捕獲和處理信號github

本文地址:http://www.javashuo.com/article/p-zyoylygv-cx.htmlsegmentfault

工程簡介

基本原理

總所周知,在 Linux 中實現異步 I/O,適用的系統 API 就是 epoll。這裏主要包括 epoll_ctl()epoll_wait() 兩個函數。前者配置各個文件描述符在 epoll 裏的機制,然後者則是關鍵的阻塞調用。服務器

這個工程,就是基於 epoll,實現相似於 libevent 的異步 I/O 庫。架構

函數的具體用法能夠參照 man 頁,或者工程的 AMCEpoll.c 源文件的 _dispatch_main_loop() 函數。異步


設計原因

早期打算通用的寫一個進程間通訊庫。通訊庫想要使基於異步 I/O 來作,又不想只是使用裸的 epoll。想起本身對異步 I/O 的原理也算是大體瞭解了,就本身研究着寫一個。目前已經大體完成的 AMCEpoll 並無通過嚴格的測試驗證,更多地像是一個學習工程,或者說是課後做業同樣。就像 Andrew S. Tanenbaum 教授的 Minix 相似哈。函數

不過這個庫是瞄準了實用來設計的。若是要面向可靠的話,建議用開源的 libevent 來構建;若是以爲 libevent 太大了,則推薦 libev;若是以爲 libev 的開發者太少,不靠譜的話,那麼能夠用 libuv。若是須要有本身知識產權的產品的話,那麼,能夠本身設計一個——這也就是我開發這個庫的初衷。oop

API

公共的 API 都在 AMCEpoll.h 文件中。各函數的說明以下:學習

接口說明

struct AMCEpoll

至關於 libevent 的 「event base」。這是整個 AMCEpoll 對象,每個對象可執行一個事件循環(event loop)。

struct AMCEpollEvent

至關於 libevent 的 「event」。與 libevent 不一樣的是,每個 event 能夠從某個 base 中分離,再加入到另外一個 base 裏面去。不過實際上應該沒有這樣的需求,只是說個人程序架構容許這麼作。

AMCEpoll 支持三種事件:

  • 文件事件:或者稱 fd 事件、文件描述符事件。基於 Linux 的 file descriptor 的事件。同時支持事件超時
  • 信號事件:也就是 signal 事件。能夠捕獲信號,而後在事件循環中處理信號,而不用擔憂信號函數的上下文。信號時間也支持超時機制
  • 超時事件:純粹的超時事件,支持一次性的定時,也支持循環定時。

AMCEpoll_New()

建立、初始化並返回一個 AMCEpoll 對象。其中參數 pollSize 指的是 epoll_wait() 函數中的 maxevents 參數。

AMCEpoll_Free()

銷燬一個 AMCEpoll 對象。若是對象中還有事件存在,而且事件關注了 EP_EVENT_FREE 事件,則會收到對應的回調。

不能在 event loop 中調用。

AMCEpoll_NewEvent()

建立並返回一個事件(AMCEpollEvent)對象。各參數說明以下:

  1. fd:當建立文件事件時,fd 參數就是對應的文件描述符;當建立信號事件時,fd 則是信號碼。若是是純超時時間,fd 必須設置爲 -1。須要注意的是,全部的文件事件在 event loop 啓動以前,必須確保 fd 是非阻塞的。
  2. events:EP_EVENT_XXX 掩碼組成的時間列表集。具體各個掩碼的做用,後文再說。純粹的超時事件還沒集成上去(這有什麼難的呢,畢竟 fd 事件和 signal 事件的 timeout 都已經完成了。只是我最近沒時間……)。
  3. timeout:超時時間,單位是毫秒。固然,實際上超時調用的時候是大於這個數的,畢竟函數切換須要時間。在壓力大的時候,建議超時不要小於 1 秒,最好大於 10 秒。
  4. callback:接收事件的回調函數。
  5. userData:事件回調函數的自定義參數。

AMCEpoll_FreeEvent()

銷燬一個事件。若是事件關注了 EP_EVENT_FREE,那麼會在此時收到回調。程序員能夠在這個地方執行一些庫之外的清理工做,好比 close(fd) 之類的。

若是該事件已經加入到某個 AMCEpoll 對象中,那麼這個函數會出錯。若是要避免這個問題,建議使用 AMCEpoll_DelAndFreeEvent() 函數。

AMCEpoll_AddEvent()

將某個 AMCEpollEvent 對象加入到 AMCEpoll 中,這樣才能在事件循環中關注這個對象上的事件。

AMCEpoll_DelEvent()

將已經加入到 AMCEpoll 的時間分離開來,成爲 「孤魂野鬼」 狀態。

AMCEpoll_DelAndFreeEvent()

Del 和 Free 兩個函數的結合。

AMCEpoll_SetEventTimeout()AMCEpoll_GetEventTimeout()

從新設置或者獲取一個事件的超時時間。這隻有在建立時,添加了 EP_EVENT_TIMEOUT 事件,纔會有效。

AMCEpoll_Dispatch()

在當前線程啓動事件循環。當沒有事件註冊在案的時候,event loop 會退出,也就是這個函數會返回。

AMCEpoll_LoopExit()

退出事件循環。若是這個函數在 event loop 外調用,那麼 AMCEpoll 會在走完下一次 event chain 以後退出;若是在 event loop 中間調用,則 AMCEpoll 會走完當前 event chain 以後退出。Event chain 的原理,在後文說明。


事件類型

事件類型就是 event_t 數據類型,聲明以下:

enum {
    EP_EVENT_READ    = (1 << 0),
    EP_EVENT_WRITE   = (1 << 1),
    EP_EVENT_ERROR   = (1 << 2),
    EP_EVENT_FREE    = (1 << 3),
    EP_EVENT_TIMEOUT = (1 << 4),
    EP_EVENT_SIGNAL  = (1 << 5),

    EP_MODE_PERSIST  = (1 << 8),    /* only used when adding events */
    EP_MODE_EDGE     = (1 << 9),    /* only used when adding events */
};

當建立事件的時候,上述全部的類型均可以使用;而當回調的時候,回調函數只可能接收到 EV_EVENT_XXX 類型的值。三種 AMCEpollEvent 所支持的具體類型以下:

———— _READ _WRITE _ERROR _FREE _TIMEOUT _SIGNAL _PERSIST _EDGE
文件事件 O O O O O . O O
信號事件 . . O O O O O O
超時事件 . . . O O . . .

具體的參數說明以下:

  • EP_EVENT_READ:讀事件。若是出現這個事件類型,則視爲文件事件。
  • EP_EVENT_WRITE:讀事件。若是出現這個事件類型,則視爲文件事件。
  • EP_EVENT_ERROR:通常是系統調用出錯。保留。
  • EP_EVENT_FREE:「銷燬」 事件。當調用 AMCEpoll_FreeEvent() 的時候觸發。
  • EP_EVENT_TIMEOUT:超時事件
  • EP_EVENT_SIGNAL:信號事件。若是出現這個事件類型,則視爲信號事件。不得與文件事件同時存在。
  • EP_MODE_PERSIST:持續事件。當一次回調(除了 free)完成後,程序會自動將事件及其超時配置加入到 event loop 中。固然,若是程序手動 add 的話,也是能夠的,只是不必。
  • EP_MODE_EDGE:邊緣觸發模式。若是使用這個模式的話,只在出現事件纔會觸發回調。

典型調用過程

基本的使用流程和 libevent 和 libev 很是相似,能夠參照個人這篇文章:《使用 libev 構建 TCP 響應服務器的簡單流程》

好比一個 UDP 事件基本流程:

  1. 建立 AMCEpoll 對象
  2. 建立 DGRAM fd,必要的話 bind
  3. 基於 fd 建立一個 AMCEpollEvent 對象
  4. 將 AMCEpollEvent 對象 add 到 AMCEpoll 對象中
  5. 寫一個 callback,遇到 read 事件的時候,調用 recvfrom()
  6. dispatch

其餘事件的用法,則能夠參照工程內的 test_server.c 文件。這個文件建立了三種事件:

  1. 一個 DNS 請求和解析,是一個 UDP read 事件。獲取到 DNS 響應後,將自身事件清除掉。
  2. 一個 HTTP server(其實 HTTP 響應不規範,請別在乎),而且 echo 回數據。是一個邊沿觸發的 TCP read 事件。
  3. 兩個信號事件,分別監聽 SIGINTSIGQUIT。監聽到前者時,退出 AMCEpoll 事件循環。監聽到後者時,打印調試信息。

下一篇

下一篇會說明一下工程的實現篇。文章還沒有完成,敬請期待。

相關文章
相關標籤/搜索