libevent不只支持io事件,同時還支持timeout事件與signal事件,這篇文件將分析libevent是如何組織timeout事件以及如何響應timeout事件。後端
首先,event_base中有一個成員struct min_heap timeheap,這是一個最小堆,用來存儲timeout事件的結構之一。先來看一下它的定義:數組
typedef struct min_heap { struct event** p; size_t n, a; } min_heap_t;
p是一個能夠動態擴展的指針數組,數組長度爲a,n表示堆成員數量,堆成員是指向event結構的指針。libevent中最小堆相關的操做定義在minheap-internal.h源碼文件中,使用這些操做能夠維持數組中的成員保持最小堆性質。函數
堆排序的依據是event結構中的成員ev_timeout,這個成員記錄了事件超時的絕對時間(即超時時間點)。根據最小堆性質,堆頂元素老是最先超時的事件。oop
使用min_heap的堆頂元素,libevent老是能夠知道從如今起到有事件超時所需的時間tv,這個操做在event_base_loop中調用timeout_next函數完成。libevent處理io事件的後端接口,如select,都有一個時間參數做爲阻塞的最長時間,將tv做爲後端接口的入參,那麼event_base_loop在循環中老是會在有事件超時以前被喚醒。喚醒後再循環檢查min_heap的堆頂元素是否超時,超時則將它加入到event_base的待執行回調函數鏈表,這步操做在event_base_loop中調用timeout_process函數實現。如此便可實現超時事件的響應。this
僅使用min_heap成員,event_base已經足夠管理timeout事件了,可是當有太多的timeou事件時,維持最小堆性質會成爲一個耗時的操做。爲解決這個問題,libevent使用了一個叫作common_timeout_list的結構,將超時時間相近的事件放到一塊兒,這些事件在min_heap中僅留一個表明,減小min_heap的成員數量。common_timeout_list結構的定義以下(源碼在event-internal.h中):spa
struct common_timeout_list { /* List of events currently waiting in the queue. */ struct event_list events; /* 'magic' timeval used to indicate the duration of events in this * queue. */ struct timeval duration; /* Event that triggers whenever one of the events in the queue is * ready to activate */ struct event timeout_event; /* The event_base that this timeout list is part of */ struct event_base *base; };
events是一個事件的雙向鏈表,鏈接了全部屬於這個common_timeout_list的超時事件,struct event結構中的成員ev_timeout_pos.ev_next_with_common_timeout正是用來構造這個鏈表;duration是一個帶有特殊標記的時間戳(後面解釋如何標記);timeout_event便是加入min_heap中的表明事件;base記錄了歸屬的event_base結構。設計
event_base中能夠存在多個common_timeout_lsit結構,它使用common_timeout_queues進行管理,這是一個動態擴展的指針數組,每個成員都指向一個common_timeout_list結構,n_common_timeouts_allocated表示動態數組的長度,n_common_timeouts表明有效的成員數目,common_timeout_queues的結構能夠用圖3-1表示:指針
圖3-1 common_timeout_queues結構code
相似timeout_event這種特殊事件被稱爲內部事件,相似的設計在libevent處理signal事件中也有用到。blog
common_timeout_list事件中timeval的特殊標記:
libevent設計了以下標記:
/** Mask used to get the real tv_usec value from a common timeout. */ #define COMMON_TIMEOUT_MICROSECONDS_MASK 0x000fffff #define MICROSECONDS_MASK COMMON_TIMEOUT_MICROSECONDS_MASK #define COMMON_TIMEOUT_IDX_MASK 0x0ff00000 #define COMMON_TIMEOUT_IDX_SHIFT 20 #define COMMON_TIMEOUT_MASK 0xf0000000 #define COMMON_TIMEOUT_MAGIC 0x50000000
全部common_timeout_list鏈表中的event的ev_timeout.tv_usec都會異或上值COMMON_TIMEOUT_MAGIC做爲標識;並將這個common_timeout_lsit在common_timeout_queues數組中的索引記錄在第21bit至28bit中;真正的tv_usec值只使用了COMMON_TIMEOUT_MICROSECONDS_MASK標記的比特位。