轉載請註明出處:http://blog.csdn.net/luotuo44/article/details/39547391windows
Libevent源碼分析:http://blog.csdn.net/luotuo44/article/category/2435521安全
前段時間閱讀了libevent的源碼。讀畢,以前使用libevent時的一些疑問都已經豁然開朗了。對於libevent源碼的分析,能夠移步http://blog.csdn.net/luotuo44/article/category/2435521查看。若是是libevent的初學者,能夠先閱讀《libevent使用例子,從簡單到複雜》。
本文經過自問自答的形式,但願能幫助其餘人解答在使用libevent時的一些疑惑。
服務器
同一個文件描述符(fd)是能夠屢次調用event_new,產生不一樣的event的。這些具備相同fd的event,回調函數和回調參數是能夠不一樣的。並且它們監聽的事件也是能夠不一樣(這多是能關聯多個event的緣由吧)。
網絡
若是多個event監聽同一個fd的同一個事件,好比可讀事件。那麼當這個fd變成可讀後,全部的監聽該事件的event的回調函數都會被調用(即觸發event)。沒有監聽該事件的event的回調函數不會被調用。若是兩個event監聽同一個fd的不一樣事件,那麼它們的觸發相互獨立。多線程
一個超時event是能夠屢次調用event_add函數的。其實全部的event均可以屢次調用event_add函數。不過只有超時event屢次調用纔有實質的意義,其餘event屢次調用會被發現,而後被遣返(return)。
若是每次調用event_add時,超時值不一樣的話,那麼以最後一次調用的爲準。若是想取消超時,讓這個event變成普通的event,直接把event_add的第二個參數設爲NULL便可。socket
經過libevent提供的evtimer_xxx宏函數,是沒法把一個超時event設置成永久的。因而有一些人就想在該超時event的超時回調函數中再次調用evtimer_add函數。這就有點像對不可靠的信號再次設置信號處理函數。函數
其實,不使用libevent提供的這些evtimer_xx宏函數便可。一個event之因此是超時event,不是由於調用了evtimer_xx這些宏函數,而是由於在調用event_add函數時,第二個參數不爲NULL。因此咱們能夠像下面代碼那樣設置一個永久的超時event源碼分析
struct event *ev = event_new(base, -1, EV_PERSIST, cb, arg); struct timeval timout = {2, 0}; //兩秒的超時 event_add(ev, &timeout);
也能夠這樣問:使用了超時event,若是用戶手動修改了系統時間,會有影響嗎?若是所在的系統支持MONOTONIC時間的話,那麼沒有影響。若是不支持那麼會有一些影響,即超時不那麼準確。關於MONOTONIC時間,能夠參考這裏。能夠用下面的代碼測試你的系統是否支持MONOTONIC時間。性能
struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) printf("支持\n"); else printf("不支持\n");
若是不是頻繁修改,假如只修改一次,那麼只會在修改後第一次的超時可能(僅僅是可能)會不那麼準確,以後的超時又準確了(若是該event有EV_PERSIST選項的話)。爲何說「僅僅」、「不那麼」呢?由於這取決於你在Libevent處於什麼狀態下 修改的系統時間。細節能夠參考http://blog.csdn.net/luotuo44/article/details/38661787#t2。測試
不能夠!! 由於Libevent內部實現信號event的原理就是對該信號設置一個信號捕獲函數(這也叫統一事件源)。 對信號捕抓熟悉的讀者應該明白,信號捕獲函數只能有一個。你設置了另一個,那麼將覆蓋以前設置的。
若是你的系統支持epoll,那麼它將優先使用epoll。事實上,Libevent老是優先選擇高性能的多路IO複用函數(在Windows上倒是一個例外,它並不優先使用IOCP)。 怎麼知道libevent具體是使用了哪一個多路IO複用函數? 當你調用event_base_new獲得一個event_base後,就肯定使用哪一個多路IO複用函數了。此時調用event_base_get_method函數就能獲得該event_base使用的是哪一個多路IO複用函數。該函數返回一個字符串,字符串的內容就是」select」、」poll」、」epoll」 這類多路IO複用函數的名稱。不過在Windows平臺上,返回是的」win32」。通常狀況下,在Windows平臺上是選用select的。至於何時選用IOCP,能夠參考下一條。
Windows中,Libevent對IOCP的支持比較少。只在鏈接監聽器evconnlistener和bufferevent socket中支持IOCP。此外,由於Libevent在Windows平臺默認選擇select,要經過設置EVENT_BASE_FLAG_STARTUP_IOCP宏,Libevent纔會使用IOCP。
能夠經過event_config_set_flag函數設置這個宏。具體的內容能夠參考設置,能夠參考http://blog.csdn.net/luotuo44/article/details/38443569#t5
若是要使用多線程,須要線程安全,那麼在調用event_base_new函數以前必定要調用該函數(對應的Windows版本爲evthread_use_windows_threads)。若是在event_base_new以後才調用evthread_use_pthreads,那麼該event_base就不會是線程安全的了。原理能夠參考這裏http://blog.csdn.net/luotuo44/article/details/38501341#t0。
注意:該函數只是確保Libevent線程安全,多線程的使用仍是要靠本身寫代碼。Libevent裏面的代碼也沒有使用多線程,它僅僅用到了鎖和條件變量。
有。首先,這三個東西的定製都應該放到程序的前面,確保放到其餘任何libevent API調用前。其次,這個三者也是有順序。它們的順序應該爲:內存分配、日誌記錄、線程鎖。
能夠。但爲了安全,必須確保你的程序在一開始調用了evthread_use_pthreads函數。若是主線程不是在調用其餘event的回調函數,那麼該event將立刻被主線程添加到監聽隊列中,和其餘event一塊兒等待事件的發生。至於主線程如何知曉次線程添加了event,能夠參考《evthread_notify_base通知主線程》。
你認爲一個函數理應線程安全,那麼Libevent的做者也會認爲該函數得是線程安全的。調用evthread_use_pthreads函數後,就放心使用Libevent提供的函數吧。它總會在須要加鎖的時候加鎖,保證線程安全的。
你在調用bufferevent_socket_new的時候加入了BEV_OPT_THREADSAFE選項,那麼就線程安全了。
是非阻塞的。調用後,不會被阻塞,能立刻返回。
若是讀者試過僅僅用OS提供的系統網絡API寫非阻塞socket的發送代碼的話,那麼必定被非阻塞氣死了。你得考慮要發送的數據並無在一次write調用中發送完。此時,得找一個地方保存這些要發送的數據,等到下次調用write時還要使用。但找一個地方是一個煩人的事情。
雖然bufferevent_write是非阻塞的,但它很好人。當它返回後,他已經把用戶要發送的數據都copy了一份,保存在內部的緩衝區中。因此從bufferevent_write返回後,就能夠丟棄要發送的數據了,無需傷腦筋找地方保存這些數據。
bufferevent的可讀事件是邊沿觸發的。也就是說,若是客戶端往服務器發了100字節的數據,並且客戶端僅僅發送一次數據。那麼服務器觸發可讀事件後,就應該把這100字節都讀出來(假設這100字節是一塊兒到達的)。不該該想着,此次回調只讀4字節(好比是長度信息),而後等到下次回調再讀取其餘數據。由於客戶端只發送一次數據,因此不會再有下次回調了,即便bufferevent的緩衝區裏面還有數據。固然,若是客戶端再次發送數據,那麼bufferevent的可讀回調函數又會被調用。具體的原理能夠參考http://blog.csdn.net/luotuo44/article/details/39344743#t6。
讀事件的低水位比較容易理解,當bufferevent讀緩衝區的數據到達這個低水位後,用戶設置的可讀回調函數纔會被調用。好比說,用戶設置了4字節的低水位,由於用戶認爲少於4字節都是不值得去處理的。當bufferevent的讀緩衝區的數據量小於4字節時,並不會調用用戶的可讀回調函數。當數據量大於等於4字節時,就會調用用戶的可讀回調函數。
讀事件的高水位又是什麼呢?在默認狀況下(即沒有設置高水位),一旦socket fd有數據可讀了,那麼libevent就會把數據從該socket fd的內核緩衝區 讀取到bufferevent的讀緩衝區中。客戶端往服務器發送大量數據,服務器會不斷地把數據copy到bufferevent緩衝區中。此時TCP的滑動窗口協議就沒有用了。
讀事件的高水位此時就應運而生了,當bufferevent讀緩衝區的數據量達到這個高水位後,就再也不從socket fd中讀取數據了。此時,socket fd的內核緩衝區會堆積大量數據,滑動窗口協議就起做用了。當bufferevent的讀緩衝區的數量少於高水位後,libevent又能夠從socket fd的緩衝區讀取數據。中止讀取、恢復讀取這一系列操做都是由libevent負責完成,用戶徹底不知情。有的讀者可能會想:既然已經監聽了可讀事件,那怎麼作到socket 內核緩衝區既要保留數據,又能避免無休止地觸發可讀事件。Libevent的解決方法能夠參考考http://blog.csdn.net/luotuo44/article/details/39344743#t5。
此種狀況,通常是主線程在event_base_dispatch中運行。用戶想在次線程中調用bufferevent_write發送數據。首先,確保你已經調用了evthread_use_pthreads函數(Windows平臺爲evthread_use_windows_threads函數)。其次,確保你在是event_base_new函數以前調用的。
若是客戶端往服務器發送了大量的數據,而且服務器是使用bufferevent的。那麼在bufferevent的讀事件回調函數中,通常最多隻能接收到4096個字節。這個是libevent這個庫自己代碼所限制的。
libevent監聽到一個socket fd可讀後,就會去把數據從socket fd的內核緩衝區的數據copy到bufferevent內部的一個緩衝區裏面。但libevent每次最多隻copy 4096字節,即便socket fd的緩衝區裏面有再多的數據。用戶在讀事件回調函數中讀取數據,是從bufferevent內部的緩衝區讀取的。因此最多隻能讀取4096字節。固然若是用戶故意沒有把這個4096字節讀完,那麼下次能夠讀取超過4096字節。
對於某些情景,只copy 4096字節,性能是不夠的。此時,只能修改libevent的源代碼,而後從新編譯。在後面的連接能夠看到libevent爲何每次只從socket fd中讀取4096字節,也在那裏能修改之。http://blog.csdn.net/luotuo44/article/details/39325447#t11
在Linux中,編譯Libevent,會產生下面這個靜態庫libevent.a、libevent_core.a、libevent_extra.a、libevent_pthreads.a
這四個靜態庫的區別是:event_core.a: 包含Libevent的核心內容。好比event、buffer、bufferevent、log、epoll、evthreadevent_extra.a: 包含Libevent額外提供的四大功能,爲:event_tagging、http、dns、rpcevent_pthreads.a: 包含了pthreads線程的具體實現event.a: event.a = event_core + event_extra