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