您好,登錄后才能下訂單哦!
epoll是Linux特有的I/O復(fù)用函數(shù),它在實(shí)現(xiàn)和使用上與select、poll有很大差異。
epoll使用一組函數(shù)來(lái)完成任務(wù),而不是單個(gè)函數(shù)。
epoll把用戶關(guān)心的文件描述符上的事件放在內(nèi)核里的一個(gè)事件表中,從而無(wú)需像select、poll那樣每次調(diào)用都要重復(fù)傳入文件描述符集或事件集。
但epoll需要使用一個(gè)額外的文件描述符,來(lái)唯一標(biāo)識(shí)內(nèi)核中的這個(gè)事件表。
epoll API
epoll有epoll_create、epoll_ctl、epoll_wait三個(gè)系統(tǒng)調(diào)用。
1.epoll_create
epoll_create創(chuàng)建一個(gè)額外的文件描述符,來(lái)唯一標(biāo)識(shí)內(nèi)核中的這個(gè)事件表。
1)size參數(shù)現(xiàn)在不起作用,只是給內(nèi)核一個(gè)提示,告訴它事件表需要多大。
2)該函數(shù)返回的文件描述符將用作其他所有epoll系統(tǒng)調(diào)用的第一個(gè)參數(shù),以指定要訪問的內(nèi)核事件表。
2.epoll_ctl
epoll_ctl用來(lái)操作epoll的內(nèi)核事件表。
1)fd參數(shù)是要操作的文件描述符。
2)op參數(shù)指定操作類型,操作類型有如下3種:
EPOLL_CTL_ADD | 往事件表上注冊(cè)fd上的事件 |
EPOLL_CTL_MOD | 修改fd上的注冊(cè)事件 |
EPOLL_CTL_DEL | 刪除fd上注冊(cè)的事件 |
3)event參數(shù)指定事件,它是epoll_event結(jié)構(gòu)指針類型。
events成員描述事件類型。epoll支持的文件類型和poll基本相同。表示epoll事件類型的宏是在poll對(duì)應(yīng)的宏前加上“E”。但epoll有兩個(gè)額外的事件類型——EPOLLET和EPOLLONESHOT,它們對(duì)于epoll高效運(yùn)作非常關(guān)鍵。 data成員用于存儲(chǔ)用戶數(shù)據(jù),其類型epoll_data_t定義如下: epoll_data是一個(gè)聯(lián)合體,其四個(gè)成員中使用最多的是fd,它指定事件所從屬的目標(biāo)文件描述符。 ptr成員可用來(lái)指定與fd相關(guān)的用戶數(shù)據(jù)。但由于epoll_data_t是一個(gè)聯(lián)合體,我們不能同時(shí)使用其ptr成員和fd成員,因此,如果要將文件描述符和用戶數(shù)據(jù)關(guān)聯(lián)起來(lái),以實(shí)現(xiàn)快速的數(shù)據(jù)訪問,只能使用其他手段,比如放棄使用epoll_data_t的fd成員,而在ptr指向的用戶數(shù)據(jù)中包含fd。 |
epoll_ctl 成功時(shí)返回0,失敗返回-1并設(shè)置errno。
3.epoll_wait
它在一段超時(shí)時(shí)間內(nèi)等待一組文件描述符上的事件。
參數(shù)從后往前
1)timeout參數(shù)的含義與poll接口的timeout參數(shù)相同。
2)maxevents參數(shù)指定最多監(jiān)聽多少個(gè)事件。
3)epoll_wait函數(shù)如果檢測(cè)到事件,就將所有就緒的事件從內(nèi)核事件表(由epfd參數(shù)指定)中拷貝到它的第二個(gè)參數(shù)events指向的數(shù)組中。這個(gè)數(shù)組只用于輸出epoll檢測(cè)到的就緒事件,而不像select和poll的數(shù)組參數(shù)那樣既用于傳入用戶注冊(cè)的事件,又用于輸出內(nèi)核檢測(cè)到的就緒事件。這就極大提高了應(yīng)用程序索引就緒文件描述符的效率。
epoll_wait成功時(shí)返回就緒的文件描述符個(gè)數(shù),失敗時(shí)返回-1并設(shè)置errno。
LT和ET模式
epoll對(duì)文件描述符的操作有兩種模式:LT(Level Trigger,水平觸發(fā))模式和ET(Edge Trigger,邊緣觸發(fā))模式。
LT工作模式是默認(rèn)的工作模式,這種模式下epoll相當(dāng)于一個(gè)效率較高的poll。當(dāng)epoll_wait檢測(cè)到其上有時(shí)間發(fā)生并將此事件通知應(yīng)用程序后,應(yīng)用程序可以不立即處理該事件。這樣,當(dāng)應(yīng)用程序下一次調(diào)用eoll_wait時(shí),epoll_wait還會(huì)再次向應(yīng)用程序通告該事件,直到該事件被處理。
當(dāng)往epoll內(nèi)核事件表中注冊(cè)一個(gè)文件描述符上的EPOLLET事件時(shí),epoll將以ET模式來(lái)操作該文件描述符。ET模式是epoll的高效工作模式。當(dāng)epoll_wait檢測(cè)到其上有事件發(fā)生并將此通知應(yīng)用程序后,應(yīng)用程序必須立即處理該事件,因?yàn)楹罄m(xù)的epoll_wait調(diào)用將不再向應(yīng)用程序通知這一事件??梢?,ET模式在很大程度上降低了同一個(gè)epoll事件被重復(fù)觸發(fā)的次數(shù),因此效率要比LT模式高。
——《Linux高性能服務(wù)器編程》
LT同時(shí)支持block和non-block socket。這種模式中,內(nèi)核告訴我們一個(gè)文件描述符是否就緒了,然后我們可以對(duì)這個(gè)就緒的fd進(jìn)行I/O操作。如果我們不做任何操作,內(nèi)核還是會(huì)繼續(xù)通知我們 。所以,這種模式編程出錯(cuò)的可能性要小一點(diǎn),傳統(tǒng)的select/poll都是這種模式的代表。
ET是高速的工作方式,只支持non-block socket,它的效率要比LT更高。ET與LT的區(qū)別在于,當(dāng)一個(gè)新的事件到來(lái)時(shí),ET模式下當(dāng)然可以從epoll_wait調(diào)用中獲取到這個(gè)事件,可是如果這次沒有把這個(gè)事件對(duì)應(yīng)的套接字緩沖區(qū)處理完,在這個(gè)套接字中沒有新的事件再次到來(lái)時(shí),在ET模式下是無(wú)法再次從epoll_wait調(diào)用中獲取這個(gè)事件的。而LT模式正好相反,只要一個(gè)事件對(duì)應(yīng)的套接字緩沖區(qū)還有數(shù)據(jù),就總能從epoll_wait中獲取這個(gè)事件。 因此,LT模式下開發(fā)基于epoll的應(yīng)要簡(jiǎn)單些,不太容易出錯(cuò)。而在ET模式下事件發(fā)生時(shí),如果沒有徹底地將緩沖區(qū)數(shù)據(jù)處理完,則會(huì)導(dǎo)致緩沖區(qū)中的用戶請(qǐng)求得不到響應(yīng)。
Nginx默認(rèn)使用ET模式來(lái)使用epoll。
epoll ET模式為何fd必須要設(shè)置為非阻塞
ET(邊緣觸發(fā))數(shù)據(jù)就緒只會(huì)通知一次,也就是說,如果要使用ET模式,當(dāng)數(shù)據(jù)就緒時(shí),需要一直read,直到出錯(cuò)或完成為止。但倘若當(dāng)前fd為阻塞(默認(rèn)),那么在當(dāng)讀完緩沖區(qū)的數(shù)據(jù)時(shí),如果對(duì)端并沒有關(guān)閉寫端,那么該read函數(shù)會(huì)一直阻塞,影響其他fd以及后續(xù)邏
輯。所以把fd設(shè)置為非阻塞,當(dāng)沒有數(shù)據(jù)的時(shí)候,read雖然讀取不到任何內(nèi)容,但是肯定不會(huì)被阻塞,那么此時(shí),說明緩沖區(qū)數(shù)據(jù)已經(jīng)讀取完畢,需要繼續(xù)處理后續(xù)邏輯(讀取其他fd或者進(jìn)行wait)。
epoll的優(yōu)點(diǎn):
1.支持一個(gè)進(jìn)程打開大數(shù)目的socket描述符(fd)。
select的缺點(diǎn)是一個(gè)進(jìn)程打開的fd是有限制的,由FD_SETSIZE指定,默認(rèn)值是2048。對(duì)于那些需要支持的上萬(wàn)連接數(shù)目的IM服務(wù)器來(lái)說顯然太少了。要解決這個(gè)問題,我們一是選擇修改這個(gè)宏然后重新編譯內(nèi)核,不過資料也同時(shí)指出這樣會(huì)帶來(lái)網(wǎng)絡(luò)效率的下降;二是可以選擇多進(jìn)程的解決方案(傳統(tǒng)的Apache方案),不過雖然Linux上面創(chuàng)建進(jìn)程的代價(jià)比較小,但仍是不可忽略的 ,加上進(jìn)程間數(shù)據(jù)同步遠(yuǎn)不不上線程間同步高效,所以也不是一種完美的方案。不過epoll則沒有這個(gè)限制,它所支持的fd上限是最大可以打開文件的數(shù)目,這個(gè)數(shù)字一般遠(yuǎn)大于2048,這個(gè)數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大。例如在1GB內(nèi)存的機(jī)器上大約是10w左右,具體數(shù)目可以cat /proc/sys/fs/file-max查看。
2.I/O效率不隨fd數(shù)目增加而下降。
傳統(tǒng)的select/poll另一個(gè)缺點(diǎn)是當(dāng)我們擁有一個(gè)很大的socket集合,不過由于網(wǎng)絡(luò)延時(shí),任何時(shí)間只有部分的socket是活躍的,但是select/poll每次調(diào)用都會(huì)線性掃描全部的集合,導(dǎo)致效率呈現(xiàn)線性下降。但是epoll不存在這個(gè)問題,它只會(huì)對(duì)"活躍"的socket進(jìn)行操作---這是因?yàn)樵趦?nèi)核實(shí)現(xiàn)中epoll是根據(jù)每個(gè)fd上面的回調(diào)函數(shù)實(shí)現(xiàn)的。
3.使用mmap加速內(nèi)核與用戶空間的消息傳遞。
無(wú)論是select、poll還是epoll都要通過內(nèi)核把fd消息通知給用戶空間,epoll是通過內(nèi)核與用戶空間mmap同一塊內(nèi)存實(shí)現(xiàn)的。
使用epoll的tcp服務(wù)器,完成簡(jiǎn)單的HTTP消息回顯
用瀏覽器測(cè)試:
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。