libevent编程疑难解答
? ? ? ? 前段時(shí)間閱讀了libevent的源碼。讀畢,之前使用libevent時(shí)的一些疑問(wèn)都已經(jīng)豁然開(kāi)朗了。對(duì)于libevent源碼的分析,可以移步http://blog.csdn.net/luotuo44/article/category/2435521查看。如果是libevent的初學(xué)者,可以先閱讀《libevent使用例子,從簡(jiǎn)單到復(fù)雜》。
? ? ? ??本文通過(guò)自問(wèn)自答的形式,希望能幫助其他人解答在使用libevent時(shí)的一些疑惑。
? ? ??
一個(gè)文件描述符可以關(guān)聯(lián)多個(gè)event嗎?
? ? ? ??同一個(gè)文件描述符(fd)是可以多次調(diào)用event_new,產(chǎn)生不同的event的。這些具有相同fd的event,回調(diào)函數(shù)和回調(diào)參數(shù)是可以不同的。而且它們監(jiān)聽(tīng)的事件也是可以不同(這可能是能關(guān)聯(lián)多個(gè)event的原因吧)。
? ? ? ??如果多個(gè)event監(jiān)聽(tīng)同一個(gè)fd的同一個(gè)事件,比如可讀事件。那么當(dāng)這個(gè)fd變成可讀后,所有的監(jiān)聽(tīng)該事件的event的回調(diào)函數(shù)都會(huì)被調(diào)用(即觸發(fā)event)。沒(méi)有監(jiān)聽(tīng)該事件的event的回調(diào)函數(shù)不會(huì)被調(diào)用。如果兩個(gè)event監(jiān)聽(tīng)同一個(gè)fd的不同事件,那么它們的觸發(fā)相互獨(dú)立。
?
?
一個(gè)超時(shí)event可以多次調(diào)用event_add函數(shù)嗎?
? ? ? ??一個(gè)超時(shí)event是可以多次調(diào)用event_add函數(shù)的。其實(shí)所有的event都可以多次調(diào)用event_add函數(shù)。不過(guò)只有超時(shí)event多次調(diào)用才有實(shí)質(zhì)的意義,其他event多次調(diào)用會(huì)被發(fā)現(xiàn),然后被遣返(return)。
? ? ? ??如果每次調(diào)用event_add時(shí),超時(shí)值不同的話,那么以最后一次調(diào)用的為準(zhǔn)。如果想取消超時(shí),讓這個(gè)event變成普通的event,直接把event_add的第二個(gè)參數(shù)設(shè)為NULL即可。
?
怎么把一個(gè)超時(shí)event設(shè)置成永久觸發(fā)(EV_PERSIST)?
? ? ? ??通過(guò)libevent提供的evtimer_xxx宏函數(shù),是無(wú)法把一個(gè)超時(shí)event設(shè)置成永久的。于是有一些人就想在該超時(shí)event的超時(shí)回調(diào)函數(shù)中再次調(diào)用evtimer_add函數(shù)。這就有點(diǎn)像對(duì)不可靠的信號(hào)再次設(shè)置信號(hào)處理函數(shù)。
? ? ? ??其實(shí),不使用libevent提供的這些evtimer_xx宏函數(shù)即可。一個(gè)event之所以是超時(shí)event,不是因?yàn)檎{(diào)用了evtimer_xx這些宏函數(shù),而是因?yàn)樵谡{(diào)用event_add函數(shù)時(shí),第二個(gè)參數(shù)不為NULL。所以我們可以像下面代碼那樣設(shè)置一個(gè)永久的超時(shí)event
[cpp] view plaincopy print??
超時(shí)event的實(shí)現(xiàn)依賴于系統(tǒng)時(shí)間嗎?
? ? ? ??也可以這樣問(wèn):使用了超時(shí)event,如果用戶手動(dòng)修改了系統(tǒng)時(shí)間,會(huì)有影響嗎?
? ? ? ??如果所在的系統(tǒng)支持MONOTONIC時(shí)間的話,那么沒(méi)有影響。如果不支持那么會(huì)有一些影響,即超時(shí)不那么準(zhǔn)確。關(guān)于MONOTONIC時(shí)間,可以參考這里??梢杂孟旅娴拇a測(cè)試你的系統(tǒng)是否支持MONOTONIC時(shí)間。
[cpp] view plaincopy print?? ? ? ??如果不是頻繁修改,假如只修改一次,那么只會(huì)在修改后第一次的超時(shí)可能(僅僅是可能)會(huì)不那么準(zhǔn)確,之后的超時(shí)又準(zhǔn)確了(如果該event有EV_PERSIST選項(xiàng)的話)。為什么說(shuō)“僅僅”、“不那么”呢?因?yàn)檫@取決于你在Libevent處于什么狀態(tài)下? 修改的系統(tǒng)時(shí)間。細(xì)節(jié)可以參考http://blog.csdn.net/luotuo44/article/details/38661787#t2。
?
對(duì)某個(gè)信號(hào)進(jìn)行捕抓 和 對(duì)該信號(hào)使用信號(hào)event 能同時(shí)進(jìn)行嗎?
? ? ? ??不可以!!
? ? ? ??因?yàn)長(zhǎng)ibevent內(nèi)部實(shí)現(xiàn)信號(hào)event的原理就是對(duì)該信號(hào)設(shè)置一個(gè)信號(hào)捕抓函數(shù)(這也叫統(tǒng)一事件源)。 對(duì)信號(hào)捕抓熟悉的讀者應(yīng)該明白,信號(hào)捕抓函數(shù)只能有一個(gè)。你設(shè)置了另外一個(gè),那么將覆蓋之前設(shè)置的。
?
?
?
在Linux中,Libevent默認(rèn)使用epoll嗎?
? ? ? ??如果你的系統(tǒng)支持epoll,那么它將優(yōu)先使用epoll。事實(shí)上,Libevent總是優(yōu)先選擇高性能的多路IO復(fù)用函數(shù)(在Windows上卻是一個(gè)例外,它并不優(yōu)先使用IOCP)。
?
?
怎么知道libevent具體是使用了哪個(gè)多路IO復(fù)用函數(shù)?
? ? ? ??當(dāng)你調(diào)用event_base_new得到一個(gè)event_base后,就確定使用哪個(gè)多路IO復(fù)用函數(shù)了。此時(shí)調(diào)用event_base_get_method函數(shù)就能得到該event_base使用的是哪個(gè)多路IO復(fù)用函數(shù)。該函數(shù)返回一個(gè)字符串,字符串的內(nèi)容就是”select”、”poll”、”epoll” 這類(lèi)多路IO復(fù)用函數(shù)的名稱。不過(guò)在Windows平臺(tái)上,返回是的”win32”。一般情況下,在Windows平臺(tái)上是選用select的。至于什么時(shí)候選用IOCP,可以參考下一條。
?
在Windows中,怎么使用IOCP?
? ? ? ??Windows中,Libevent對(duì)IOCP的支持比較少。只在連接監(jiān)聽(tīng)器evconnlistener和bufferevent? socket中支持IOCP。此外,因?yàn)長(zhǎng)ibevent在Windows平臺(tái)默認(rèn)選擇select,要通過(guò)設(shè)置EVENT_BASE_FLAG_STARTUP_IOCP宏,Libevent才會(huì)使用IOCP。
? ? ? ??可以通過(guò)event_config_set_flag函數(shù)設(shè)置這個(gè)宏。具體的內(nèi)容可以參考設(shè)置,可以參考http://blog.csdn.net/luotuo44/article/details/38443569#t5
?
什么時(shí)候調(diào)用evthread_use_pthreads函數(shù)?
? ? ? ??如果要使用多線程,需要線程安全,那么在調(diào)用event_base_new函數(shù)之前一定要調(diào)用該函數(shù)(對(duì)應(yīng)的Windows版本為evthread_use_windows_threads)。如果在event_base_new之后才調(diào)用evthread_use_pthreads,那么該event_base就不會(huì)是線程安全的了。原理可以參考這里http://blog.csdn.net/luotuo44/article/details/38501341#t0。
? ? ? ??注意:該函數(shù)只是確保Libevent線程安全,多線程的使用還是要靠自己寫(xiě)代碼。Libevent里面的代碼也沒(méi)有使用多線程,它僅僅用到了鎖和條件變量。
?
?
Libevent允許定制內(nèi)存分配、日志、線程鎖,這些定制有順序要求嗎?
? ? ? ??有。首先,這三個(gè)東西的定制都應(yīng)該放到程序的前面,確保放到其他任何libevent API調(diào)用前。其次,這個(gè)三者也是有順序。它們的順序應(yīng)該為:內(nèi)存分配、日志記錄、線程鎖。
?
?
可以在次線程調(diào)用event_add添加一個(gè)event嗎?
? ? ? ??可以。但為了安全,必須確保你的程序在一開(kāi)始調(diào)用了evthread_use_pthreads函數(shù)。如果主線程不是在調(diào)用其他event的回調(diào)函數(shù),那么該event將馬上被主線程添加到監(jiān)聽(tīng)隊(duì)列中,和其他event一起等待事件的發(fā)生。至于主線程如何知曉次線程添加了event,可以參考《evthread_notify_base通知主線程》。
?
Libevent哪些函數(shù)是線程安全的?
? ? ? ??你認(rèn)為一個(gè)函數(shù)理應(yīng)線程安全,那么Libevent的作者也會(huì)認(rèn)為該函數(shù)得是線程安全的。
? ? ? ??調(diào)用evthread_use_pthreads函數(shù)后,就放心使用Libevent提供的函數(shù)吧。它總會(huì)在需要加鎖的時(shí)候加鎖,保證線程安全的。
?
?
bufferevent線程安全嗎?
? ? ? ??你在調(diào)用bufferevent_socket_new的時(shí)候加入了BEV_OPT_THREADSAFE選項(xiàng),那么就線程安全了。
?
?
bufferevent_write是非阻塞的嗎?
? ? ? ?是非阻塞的。調(diào)用后,不會(huì)被阻塞,能馬上返回。?
?
既然bufferevent_write馬上返回,那么返回后用戶的那份數(shù)據(jù)是否可以刪除了?
? ? ? ??如果讀者試過(guò)僅僅用OS提供的系統(tǒng)網(wǎng)絡(luò)API寫(xiě)非阻塞socket的發(fā)送代碼的話,那么一定被非阻塞氣死了。你得考慮要發(fā)送的數(shù)據(jù)并沒(méi)有在一次write調(diào)用中發(fā)送完。此時(shí),得找一個(gè)地方保存這些要發(fā)送的數(shù)據(jù),等到下次調(diào)用write時(shí)還要使用。但找一個(gè)地方是一個(gè)煩人的事情。
? ? ? ??雖然bufferevent_write是非阻塞的,但它很好人。當(dāng)它返回后,他已經(jīng)把用戶要發(fā)送的數(shù)據(jù)都copy了一份,保存在內(nèi)部的緩沖區(qū)中。所以從bufferevent_write返回后,就可以丟棄要發(fā)送的數(shù)據(jù)了,無(wú)需傷腦筋找地方保存這些數(shù)據(jù)。
?
?
bufferevent的可讀事件是水平觸發(fā)還是邊沿觸發(fā)?
? ? ? ??bufferevent的可讀事件是邊沿觸發(fā)的。也就是說(shuō),如果客戶端往服務(wù)器發(fā)了100字節(jié)的數(shù)據(jù),而且客戶端僅僅發(fā)送一次數(shù)據(jù)。那么服務(wù)器觸發(fā)可讀事件后,就應(yīng)該把這100字節(jié)都讀出來(lái)(假設(shè)這100字節(jié)是一起到達(dá)的)。不應(yīng)該想著,這次回調(diào)只讀4字節(jié)(比如是長(zhǎng)度信息),然后等到下次回調(diào)再讀取其他數(shù)據(jù)。因?yàn)榭蛻舳酥话l(fā)送一次數(shù)據(jù),所以不會(huì)再有下次回調(diào)了,即使bufferevent的緩沖區(qū)里面還有數(shù)據(jù)。
? ? ? ??當(dāng)然,如果客戶端再次發(fā)送數(shù)據(jù),那么bufferevent的可讀回調(diào)函數(shù)又會(huì)被調(diào)用。
? ? ? ??具體的原理可以參考http://blog.csdn.net/luotuo44/article/details/39344743#t6。
?
?
bufferevent讀事件的高水位是什么意思?
? ? ? ??讀事件的低水位比較容易理解,當(dāng)bufferevent讀緩沖區(qū)的數(shù)據(jù)到達(dá)這個(gè)低水位后,用戶設(shè)置的可讀回調(diào)函數(shù)才會(huì)被調(diào)用。比如說(shuō),用戶設(shè)置了4字節(jié)的低水位,因?yàn)橛脩粽J(rèn)為少于4字節(jié)都是不值得去處理的。當(dāng)bufferevent的讀緩沖區(qū)的數(shù)據(jù)量小于4字節(jié)時(shí),并不會(huì)調(diào)用用戶的可讀回調(diào)函數(shù)。當(dāng)數(shù)據(jù)量大于等于4字節(jié)時(shí),就會(huì)調(diào)用用戶的可讀回調(diào)函數(shù)。
? ? ? ??讀事件的高水位又是什么呢?在默認(rèn)情況下(即沒(méi)有設(shè)置高水位),一旦socket fd有數(shù)據(jù)可讀了,那么libevent就會(huì)把數(shù)據(jù)從該socket fd的內(nèi)核緩沖區(qū) 讀取到bufferevent的讀緩沖區(qū)中??蛻舳送?wù)器發(fā)送大量數(shù)據(jù),服務(wù)器會(huì)不斷地把數(shù)據(jù)copy到bufferevent緩沖區(qū)中。此時(shí)TCP的滑動(dòng)窗口協(xié)議就沒(méi)有用了。
? ? ? ??讀事件的高水位此時(shí)就應(yīng)運(yùn)而生了,當(dāng)bufferevent讀緩沖區(qū)的數(shù)據(jù)量達(dá)到這個(gè)高水位后,就不再?gòu)膕ocket fd中讀取數(shù)據(jù)了。此時(shí),socket fd的內(nèi)核緩沖區(qū)會(huì)堆積大量數(shù)據(jù),滑動(dòng)窗口協(xié)議就起作用了。當(dāng)bufferevent的讀緩沖區(qū)的數(shù)量少于高水位后,libevent又可以從socket fd的緩沖區(qū)讀取數(shù)據(jù)。停止讀取、恢復(fù)讀取這一系列操作都是由libevent負(fù)責(zé)完成,用戶完全不知情。有的讀者可能會(huì)想:既然已經(jīng)監(jiān)聽(tīng)了可讀事件,那怎么做到socket 內(nèi)核緩沖區(qū)既要保留數(shù)據(jù),又能避免無(wú)休止地觸發(fā)可讀事件。Libevent的解決方法可以參考http://blog.csdn.net/luotuo44/article/details/39344743#t5。
?
次線程調(diào)用bufferevent_write,為什么不能發(fā)送數(shù)據(jù)?
? ? ? ??此種情況,一般是主線程在event_base_dispatch中運(yùn)行。用戶想在次線程中調(diào)用bufferevent_write發(fā)送數(shù)據(jù)。
? ? ? ??首先,確保你已經(jīng)調(diào)用了evthread_use_pthreads函數(shù)(Windows平臺(tái)為evthread_use_windows_threads函數(shù))。
? ? ? ??其次,確保你在是event_base_new函數(shù)之前調(diào)用的。
?
使用bufferevent時(shí),為什么每次最多只能讀取4096字節(jié)?
? ? ? ??如果客戶端往服務(wù)器發(fā)送了大量的數(shù)據(jù),并且服務(wù)器是使用bufferevent的。那么在bufferevent的讀事件回調(diào)函數(shù)中,一般最多只能接收到4096個(gè)字節(jié)。這個(gè)是libevent這個(gè)庫(kù)本身代碼所限制的。
? ? ? ??libevent監(jiān)聽(tīng)到一個(gè)socket fd可讀后,就會(huì)去把數(shù)據(jù)從socket fd的內(nèi)核緩沖區(qū)的數(shù)據(jù)copy到bufferevent內(nèi)部的一個(gè)緩沖區(qū)里面。但libevent每次最多只copy 4096字節(jié),即使socket fd的緩沖區(qū)里面有再多的數(shù)據(jù)。用戶在讀事件回調(diào)函數(shù)中讀取數(shù)據(jù),是從bufferevent內(nèi)部的緩沖區(qū)讀取的。所以最多只能讀取4096字節(jié)。當(dāng)然如果用戶故意沒(méi)有把這個(gè)4096字節(jié)讀完,那么下次可以讀取超過(guò)4096字節(jié)。
? ? ? ??對(duì)于某些情景,只copy 4096字節(jié),性能是不夠的。此時(shí),只能修改libevent的源代碼,然后重新編譯。在后面的鏈接可以看到libevent為什么每次只從socket fd中讀取4096字節(jié),也在那里能修改之。http://blog.csdn.net/luotuo44/article/details/39325447#t11
在Linux中編譯Libevent生成的四個(gè)靜態(tài)庫(kù)各有什么區(qū)別?
? ? ? ??在Linux中,編譯Libevent,會(huì)產(chǎn)生下面這個(gè)靜態(tài)庫(kù)libevent.a、libevent_core.a、libevent_extra.a、libevent_pthreads.a? ? ? ??這四個(gè)靜態(tài)庫(kù)的區(qū)別是:
- event_core.a: 包含Libevent的核心內(nèi)容。比如event、buffer、bufferevent、log、epoll、evthread
- event_extra.a:?包含Libevent額外提供的四大功能,為:event_tagging、http、dns、rpc
- event_pthreads.a:?包含了pthreads線程的具體實(shí)現(xiàn)
- event.a:?event.a = event_core + event_extra
總結(jié)
以上是生活随笔為你收集整理的libevent编程疑难解答的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: MySQL表最大能达到多少?
- 下一篇: c++ winpcap开发(7)