I/O多路複用——epoll

epoll是Linux特有的I/O複用函數,它在實現和使用上與select、poll有很大差別。編程

epoll使用一組函數來完成任務,而不是單個函數。數組

epoll把用戶關心的文件描述符上的事件放在內核裏的一個事件表中,從而無需像select、poll那樣每次調用都要重複傳入文件描述符集或事件集。瀏覽器

但epoll須要使用一個額外的文件描述符,來惟一標識內核中的這個事件表。
服務器


epoll API網絡

epoll有epoll_create、epoll_ctl、epoll_wait三個系統調用。socket


1.epoll_createtcp

epoll_create建立一個額外的文件描述符,來惟一標識內核中的這個事件表。ide

wKiom1fqRAmCaci1AAAcZINGAzs701.png

1)size參數如今不起做用,只是給內核一個提示,告訴它事件表須要多大。函數

2)該函數返回的文件描述符將用做其餘全部epoll系統調用的第一個參數,以指定要訪問的內核事件表。性能


2.epoll_ctl

epoll_ctl用來操做epoll的內核事件表。

wKioL1fqRRXhY1yXAAAZa4V4vFo672.png

1)fd參數是要操做的文件描述符。

2)op參數指定操做類型,操做類型有以下3種:


EPOLL_CTL_ADD 往事件表上註冊fd上的事件
EPOLL_CTL_MOD 修改fd上的註冊事件
EPOLL_CTL_DEL 刪除fd上註冊的事件


3)event參數指定事件,它是epoll_event結構指針類型。

wKioL1fqRp2h0qLfAAAZRSQiC3U785.png

events成員描述事件類型。epoll支持的文件類型和poll基本相同。表示epoll事件類型的宏是在poll對應的宏前加上「E」。但epoll有兩個額外的事件類型——EPOLLET和EPOLLONESHOT,它們對於epoll高效運做很是關鍵。

wKiom1fqR6ChbWxFAADmIiTR4xM471.png


data成員用於存儲用戶數據,其類型epoll_data_t定義以下:

wKioL1fqSDOh2iZ3AAASuARPxjA491.png

epoll_data是一個聯合體,其四個成員中使用最多的是fd,它指定事件所從屬的目標文件描述符。

ptr成員可用來指定與fd相關的用戶數據。但因爲epoll_data_t是一個聯合體,咱們不能同時使用其ptr成員和fd成員,所以,若是要將文件描述符和用戶數據關聯起來,以實現快速的數據訪問,只能使用其餘手段,好比放棄使用epoll_data_t的fd成員,而在ptr指向的用戶數據中包含fd。


epoll_ctl 成功時返回0,失敗返回-1並設置errno。


3.epoll_wait

它在一段超時時間內等待一組文件描述符上的事件。

wKiom1fqSpCBCD2uAAAfgh08Xs4197.png


參數從後往前

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上面的回調函數實現的。

        wKioL1fqWg2DlG5BAAB2DfNg0Ew235.png


3.使用mmap加速內核與用戶空間的消息傳遞。

        不管是select、poll仍是epoll都要經過內核把fd消息通知給用戶空間,epoll是經過內核與用戶空間mmap同一塊內存實現的。


使用epoll的tcp服務器,完成簡單的HTTP消息回顯

wKioL1fqXUySIQ6QAA82ofbjdW4391.jpg


用瀏覽器測試:

wKioL1fqXvaTzAZvAAFyo_-C75Q277.png

相關文章
相關標籤/搜索