天天看點

Libevent 源碼剖析一libevent概述libevent下載下傳和安裝libevent源碼組織架構Reactor模式EPOLL反應堆詳解(libevent核心)

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)。
Libevent 源碼剖析一libevent概述libevent下載下傳和安裝libevent源碼組織架構Reactor模式EPOLL反應堆詳解(libevent核心)

事件源: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;  
    // ...  
};  
           
Libevent 源碼剖析一libevent概述libevent下載下傳和安裝libevent源碼組織架構Reactor模式EPOLL反應堆詳解(libevent核心)

EPOLL反應堆詳解(libevent核心)

  1. 第一步,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) 這裡可以做很多很多其他的工作,例如定時清除沒讀完的不要的資料
           *     也可以做點和資料庫有關的設定
           *     玩大點你在這裡搞搞分布式的代碼也可以
           */
 }
           
Libevent 源碼剖析一libevent概述libevent下載下傳和安裝libevent源碼組織架構Reactor模式EPOLL反應堆詳解(libevent核心)

到了這裡,也将是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

繼續閱讀