Libevent 學習及源碼剖析(一)
- libevent概述
- libevent下載下傳和安裝
- libevent源碼組織架構
- Reactor模式
- EPOLL反應堆詳解(libevent核心)
libevent概述
Libevent 是一個用C語言編寫的、輕量級的開源高性能事件通知庫,主要有以下幾個亮點:事件驅動( event-driven),高性能;輕量級,專注于網絡,不如 ACE 那麼臃腫龐大;源代碼相當精煉、易讀;跨平台,支援 Windows、 Linux、 *BSD 和 Mac Os;支援多種 I/O 多路複用技術, epoll、 poll、 dev/poll、 select 和 kqueue 等;支援 I/O,定時器和信号等事件;注冊事件優先級。
Libevent 已經被廣泛的應用,作為底層的網絡庫;比如 memcached、 Vomit、 Nylon、 Netchat等等。
連結-來自百度百科: https://baike.baidu.com/item/libevent/.
libevent下載下傳和安裝
github:https://github.com/libevent/libevent
官網:https://libevent.org/
安裝:tar -zxvf libevent-2.1.11-stable.tar.gz cd
libevent-2.1.11-stable ./configure --prefix=/usr/local/ (你需要的指定路徑)
sudo make && make install
注:運作是出現找不到libevent.so庫的情況,這是連結時沒有将你的連結庫添加進去
可以參考:https://blog.csdn.net/mybelief321/article/details/9099659
libevent源碼組織架構
- 頭檔案 :主要就是event.h :事件宏定義,接口函數聲明,主體結構體event的聲明
- 内部頭檔案:xxx-internal.h,内部的資料結構,對外界不可見,以達到資訊隐藏的目的
- libevent架構:event.c 對整體架構的實作
- 對IO複用的封裝:epoll.c/select.c/devpoll/kequeue.c
- 定時事件管理:min-heap.h 一個以時間作為key的小根堆的結構
- 信号管理:signal.c:對信号的處理
- 輔助功能函數:evutil.h/evutil.c:包括建立socket pair和一些時間操作函數:加減和比較等等
- 日志:log.h/log.c : log日志函數
- 緩沖區管理:evbuffer.c / buffer.c : libevent對緩沖區的封裝
- 基本資料結構:compat/sys : queue.h是libevent基本資料結構的實作,包括連結清單,雙向連結清單,隊列等等;_libevent_time.h : 用于時間操作的結構體定義函數和宏定義
- 實用網絡庫:http/evdns : 基于libevent實作的http伺服器和異步dns查詢庫
Reactor模式
組成:事件源、架構部分(Reactor)、事件多路分發機制(event demultiplexing)、事件處理程式(event handler)。
事件源:Linux 上的檔案描述符,程式指定的句柄上注冊關心的事件,比如IO事件
event demultiplexer——事件多路分發機制
由作業系統提供的I/O多路複用機制,比如select和epoll。
程式首先将其關心的句柄(事件源)及其事件注冊到event demultiplexer上;
當有事件到達時,event demultiplexer會發出通知“在已經注冊的句柄集中,一個或多個句柄的事件已經就緒”;
程式收到通知後,就可以在非阻塞的情況下對事件進行處理了。
對應到libevent中,依然是select、poll、epoll等,但是libevent使用結構體eventop進行了封裝,以統一的接口來支援這些I/O多路複用機制,達到了對外隐藏底層系統機制的目的。
Reactor——反應器
Reactor,是事件管理的接口,内部使用event demultiplexer注冊、登出事件;并運作事件循環,當有事件進入“就緒”狀态時,調用注冊事件的回調函數處理事件。
對應到libevent中,就是event_base結構體。
一個典型的Reactor聲明方式
class Reactor
{
public:
int register_handler(Event_Handler *pHandler, int event);
int remove_handler(Event_Handler *pHandler, int event);
void handle_events(timeval *ptv);
// ...
};
Event Handler——事件處理程式
事件處理程式提供了一組接口,每個接口對應了一種類型的事件,供Reactor在相應的事件發生時調用,執行相應的事件處理。通常它會綁定一個有效的句柄。
對應到libevent中,就是event結構體。
下面是兩種典型的Event Handler類聲明方式,二者互有優缺點。
class Event_Handler
{
public:
virtual void handle_read() = 0;
virtual void handle_write() = 0;
virtual void handle_timeout() = 0;
virtual void handle_close() = 0;
virtual HANDLE get_handle() = 0;
// ...
};
class Event_Handler
{
public:
// events maybe read/write/timeout/close .etc
virtual void handle_events(int events) = 0;
virtual HANDLE get_handle() = 0;
// ...
};
EPOLL反應堆詳解(libevent核心)
-
第一步,epoll反應堆模型雛型 ----- epoll模型
epoll模型和epoll接口本質差別在于epoll模型傳入聯合體的是一個自定義結構體指針,該結構體的基本結構包括
struct my_events {
int m_fd; //監聽的檔案描述符
void *m_arg; //泛型參數
void (*call_back)(void *arg); //回調函數
/*
* 你可以在此處封裝更多的資料内容
* 例如使用者緩沖區、節點狀态、節點上樹時間等等
*/
int Status; //1 代表被監聽(添加到RBTree), 0 代表沒有被監聽
char buf[BUFLEN];
long last_active;//記錄最後一次響應時間,做逾時處理
};
/*
* 注意:使用者需要自行開辟空間存放my_events類型的數組,并在每次上樹前用epoll_data_t裡的
* ptr指向一個my_events元素。
*/
2.epoll_wait()傳回直接調用事件中對應的回調函數,就像這樣
/*
* -[ epoll模型使用描述01 ]-
*/
while(1) {
/* 監聽紅黑樹, 1秒沒事件滿足則傳回0 */
int n_ready = epoll_wait(ep_fd, events, MAX_EVENTS, 1000);
if (n_ready > 0) {
for (i=0; i<n_ready; i++)
events[i].data.ptr->call_back(/* void *arg */);
}
else
/*
* (3) 這裡可以做很多很多其他的工作,例如定時清除沒讀完的不要的資料
* 也可以做點和資料庫有關的設定
* 玩大點你在這裡搞搞分布式的代碼也可以
*/
}
到了這裡,也将是epoll的最終成型,如果從前面到這裡你都明白了,epoll的知識你已經十之七八了
讓我們先回想以下epoll模型的那張圖,我們來理一理思路。
(1) 程式設定邊沿觸發以及每一個上樹的檔案描述符設定非阻塞
(2) 調用epoll_create()建立一個epoll對象
(3) 調用epoll_ctl()向epoll對象中進行增加、删除等操作
上樹的檔案描述符與之對應的結構體,該結構體應該滿足填充事件與自定義結構體ptr,此時,監聽的事件與回調函數已經确定了對吧?
(4) 調用epoll_wait()(定時檢測) 傳回待處理的事件集合。
(5) 依次調用事件集合中的每一個元素中的ptr所指向那個結構體中的回調函數。
以上為雛形版本,那麼epoll反應堆模型還要比這個雛形版本多了什麼呢?
請看第三步的粗體字,當我們把描述符和自定義結構體上樹以後,如果放的是監聽可讀事件并做其對應的回調操作。也就是說,它将一直作為監聽可讀事件而存在。
其流程是:
監聽可讀事件(ET) ⇒ 資料到來 ⇒ 觸發事件 ⇒ epoll_wait()傳回 ⇒ 處理回調 ⇒ 繼續epoll_wait() ⇒ 直到程式停止前都是這麼循環
那麼接下來更新為成型版epoll反應堆模型
其流程是:
監聽可讀事件(ET) ⇒ 資料到來 ⇒ 觸發事件 ⇒ epoll_wait()傳回 ⇒
讀取完資料(可讀事件回調函數内) ⇒ 将該節點從紅黑樹上摘下(可讀事件回調函數内) ⇒ 設定可寫事件和對應可寫回調函數(可讀事件回調函數内) ⇒ 挂上樹(可讀事件回調函數内) ⇒ 處理資料(可讀事件回調函數内)
⇒ 監聽可寫事件(ET) ⇒ 對方可讀 ⇒ 觸發事件 ⇒ epoll_wait()傳回 ⇒
寫完資料(可寫事件回調函數内) ⇒ 将該節點從紅黑樹上摘下(可寫事件回調函數内) ⇒ 設定可讀事件和對應可讀回調函數(可寫讀事件回調函數内) ⇒ 挂上樹(可寫事件回調函數内) ⇒ 處理收尾工作(可寫事件回調函數内) ⇒ 直到程式停止前一直這麼交替循環
————————————————
https://blog.csdn.net/qq_36359022/article/details/81355897