IO多路轉接之select、poll、epoll

IO多路轉接之select
     select系統調用的用途:在一段指定的時間內,監聽用戶感興趣的文件描述符上的可讀,可寫,異常時間。
     select函數原型:
          int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* execptfds, struct timeval* timeout);
          第一個參數通常設置爲所有文件描述符中的最大值加1。
          第二,第三,第四個參數表示可讀,可寫,異常事件的文件描述符集。select調用返回的時候,內核將修改(如何修改:就緒的文件描述符相應的比特位設置爲1)他們來通知應用程序哪些文件描述符已經就緒。
          最後一個參數表示select函數的超時時間(timeout=NULL表示一直阻塞,直到某一個文件描述符就緒)。
          select成功的時候返回就緒文件描述符的總數,如果超時時間內沒有文件描述符就緒,那麼返回值爲0.

下面的宏提供了處理這三種描述詞組的方式:
     FD_CLR(inr fd,fd_set* set);用來清除描述詞組set中相關fd 的位 
     FD_ISSET(int fd,fd_set *set);用來測試描述詞組set中相關fd 的位是否爲真 
     FD_SET(int fd,fd_set*set);用來設置描述詞組set中相關fd的位 
     FD_ZERO(fd_set *set);用來清除描述詞組set的全部位

select模型的特點
     (1)可監控的文件描述符的個數取決於sizeof(fd_set),一般是512,每一個bit表示一個文件描述符,那麼可以描述8*512=4096那麼多個文件描述符
     (2)需要一個輔助空間的數據結構array。因爲select的讀/寫/異常文件描述符集都是輸入/輸出型參數。每次select返回的時候沒有就緒的文件描述符對應的比特位都會被清0,array需要將fd重新保存,並獲取最大的文件描述符的值。
     (3)select調用前循環array,添加文件描述符到對應的文件描述符集中(在此之前要FD_ZERO操作),並且獲取到最大的文件描述符的值。
             select返回後還是要循環,由FD_ISSET來判斷是否有事件發生。

select模型的缺點:
     (1)select能夠支持的文件描述符的數量少,是被fd_set所限制。
     (2)在調用select函數前,都要遍歷array,將fd集合從用戶態拷貝到內核態(FD_SET),這個開銷在fd很多的時候開銷是非常大的。
     (3)在select返回以後也要在內核中遍歷傳遞進來的所有的fd(FD_ISSET),查看文件描述符是否發生事件,這個開銷在fd很多的時候開銷也是非常大的。

IO多路轉接之poll
     poll系統調用的用途:指定時間內輪詢一定數量的文件描述符,以測試其中是否有就緒事件。不同於select使用三個位圖來表示三個fdset的方式,poll使用一個pollfd指針實現。
     poll函數原型:
          int poll(struct pollfd* fds, nfds_t nfds, int timeout);
          struct pollfd
          {
               int fd;               //文件描述符
               short events;     //註冊的事件
               short revents;    //實際發生的事件,由內核填充
          }
          events成員告訴poll監聽fd上的什麼時間,他是一系列的事件的按位或。
          revents成員則由內核進行修改,以通知應用程序fd上實際發生了哪些事件。

     poll事件類型
          POLLIN           事件可讀
          POLLOUT        事件可寫
          POLLERR         錯誤

poll模型的特點:
     (1)poll使用一個結構體將讀,寫,異常事件
     (2)poll並沒有最大的文件描述符數量的限制(但是數量過多以後性能也會下降)
     (3)poll返回以後和select一樣要輪詢pollfd來獲取就緒的文件描述符

poll模型的缺點:
     (1)fd過多會導致性能下降
     (2)還是需要輪詢pollfd裏查詢就緒的文件描述符

IO多路轉接之epoll
      epoll把用戶關心的文件描述符上的事件放在內核裏的一個事件表中,而無需像select,poll那樣每次調用都要重複傳入文件描述符集和事件集。但是epoll需要一個額外的文件描述符來唯一標識內核中的這個事件表。
     epoll使用一組函數來完成任務
          int epoll_create(int size);     //返回值表示指定要訪問的內核事件表
          int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);     //操作epoll的內核事件表
               參數op:EPOLL_CTL_ADD,往事件表中註冊fd上的事件
                             EPOLL_CTL_MOD,修改fd上的註冊事件
                             EPOLL_CTL_DEL,刪除fd上的註冊事件
          int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);//在一段超時時間內等待一組文件描述符上的事件
               epoll_wait函數如果檢測到事件,就將所有就緒的事件從內核事件表中(由epfd參數所指定)中複製到他的第二個參數events所指向的數組中。

epoll工作原理
     epoll同樣只告知那些就緒的文件描述符,而且當我們調用epoll_wait()獲得就緒文件描述符時, 返回的不是實際的描述符,而是一個代表就緒描述符數量的值,你只需要去epoll指定的一個數組(第二個參數)中依次取得相應數量的文件描述符即可,這裏也使用了內存映射(mmap)技術,這樣便徹底省掉了這些文件描述符在系統調用時複製的開銷。
     另一個本質的改進在於epoll採用基於事件的就緒通知方式。在select/poll中,進程只有在調用一定的方法後,內核纔對所有監視的文件描述符進行掃描,而epoll事先通過epoll_ctl()來 註冊一個文件描述符,一旦基於某個文件描述符就緒時,內核會採用類似callback的回調機制,迅速激活這個文件描述符,當進程調用epoll_wait()時便得到通知。

ET和LT的工作模式
      ET 是高速工作方式,只支持no-block socket(非阻塞套接口),它效率要比LT更高效。ET與LT的區別在於,當一個新的事件到來時,ET模式下當然可以從epoll_wait調用中獲取到這個事件, 可是如果這次沒有把這個事件對應的套接字緩衝區處理完,在這個套接字中沒有新的事件再次到來時,在ET模式下是無法再次從epoll_wait調用中獲取這個事件的。而LT模式正好相反,只要這個事件對應的套接字緩衝區還有數據,就總能從epoll_wait中獲取這個事件。因此,LT模式下開發基於epoll的應用要簡單些,不太容易出錯。而在ET模式下事件發生時, 如果沒有徹底地將緩衝區數據處理完,則會導致緩衝區中的用戶請求得不到響應。

epoll ET模式爲何fd必須要設置爲非阻塞 
     ET(邊緣觸發)數據就緒只會通知一次,也就是說,如果要使用ET模式,當數據就緒時, 需要一直read,直到出錯或完成爲止。但倘若當前fd爲阻塞(默認爲阻塞),那麼在當讀完緩衝區 的數據時,如果對端並沒有關閉寫端,那麼該read函數會一直阻塞,影響其他fd以及後續邏輯!
     所以此時將該fd設置爲非阻塞,當沒有數據的時候,read雖然讀取不到任何內容,但是肯定不會被阻塞等待,那麼此時,說明緩衝區數據已經讀取完畢,需要繼續處理後續邏輯(讀取其他fd或者進入wait)。

epoll的優點:
     (1)允許一個進程打開大數目的socket描述符。一般允許打開socket描述符的數量是由數組大小來決定的,內核中能開闢多大的數組,socket描述符的數量就有多少
     (2)IO效率不會隨着fd數目的增加而下降。由於select/poll每次調用都會線性的掃描全部的集合,導致效率呈現線性下降。epoll不存在這個問題,他用一個內核中的事件表去記錄那些就緒的文件描述符,每次只關注這些就緒的文件描述符。
     (3)使用內存映射(mmap)技術加速了內核與用戶空間的消息傳遞。無論是select/poll/epoll都需要內核把fd消息通知給用戶空間,如何避免不必要的內存拷貝這很重要。epoll_wait函數如果檢測到事件,就將所有就緒的事件從內核事件表中(由epfd參數所指定)中複製到他的第二個參數events所指向的數組中。
    
select、poll、epoll的優缺點比較
     (1)select允許監聽的最大的文件描述符數量通常是有限制的(取決於sizeof(fd_set)的大小),poll和epoll允許監聽的最大文件描述符的數量都能達到系統中允許打開的最大文件描述符的數目。
     (2)select沒有將文件描述符和事件綁定,它僅僅只是一個文件描述符集合(可讀,可寫和異常),一方面select不能處理更多類型的時間,另一方面由於這些文件描述符集合都是輸入輸出型參數,內核對fd_set集合在線修改,應用程序每次在調用select前都必須重置這三個文件描述符集合。
     (3)poll和epoll使用一個結構體將文件描述符和事件綁定,將事件統一處理,接口也變得簡潔的多,內核每次修改結構體中的revents成員,不會對events成員進行修改,所以poll不會向select一樣每次都要重置事件。
     (4)select和poll調用每次返回的都是整個用戶註冊的事件集合(其中有就緒的還有未就緒的),每次都遍歷整個事件集合,這樣當fd數量增多的時候,會導致系統性能的下降。
     (5)epoll調用的返回全都是就緒的文件描述符的數量,只需要在就緒數組中(通過mmap內存映射到epoll的第二個參數數組中)遍歷就能獲取就緒事件
     (6)select和poll在管理用戶註冊事件時,都需要去遍歷用戶空間。(時間複雜度爲O(n))
     (7)epoll在內核中維護了一個事件表,提供了一個獨立的系統調用epoll_ctl來控制向裏面添加,刪除,修改事件,這樣每次在epoll_wait調用都直接從該內核事件表中取得用戶註冊的事件,無需從用戶空間讀入這些事件。(時間複雜度爲O(1))
     (8)select和poll只支持LT的工作模式,epoll支持LT工作模式和高效的ET工作模式
     


發佈了186 篇原創文章 · 獲贊 27 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章