poll函数_I/O--多路复用的三种机制Select,Poll和Epoll对比
I/O多路復用(multiplexing)的本質是通過一種機制(系統內核緩沖I/O數據),讓單個進程可以監視多個文件描述符,一旦某個描述符就緒(一般是讀就緒或寫就緒),能夠通知程序進行相應的讀寫操作
特點
select、poll 和 epoll 都是 Linux API 提供的 IO 復用方式。
多進程和多線程技術相比,I/O多路復用技術的最大優勢是系統開銷小,系統不必創建進程/線程,也不必維護這些進程/線程,從而大大減小了系統的開銷。
Select
我們先分析一下select函數
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);
【參數說明】
int maxfdp1 指定待測試的文件描述字個數,它的值是待測試的最大描述字加1。
fd_set *readset , fd_set *writeset , fd_set *exceptset
fd_set可以理解為一個集合,這個集合中存放的是文件描述符(file descriptor),即文件句柄。中間的三個參數指定我們要讓內核測試讀、寫和異常條件的文件描述符集合。如果對某一個的條件不感興趣,就可以把它設為空指針。
const struct timeval *timeout timeout告知內核等待所指定文件描述符集合中的任何一個就緒可花多少時間。其timeval結構用于指定這段時間的秒數和微秒數。
【返回值】
int 若有就緒描述符返回其數目,若超時則為0,若出錯則為-1
select運行機制
select()的機制中提供一種fd_set的數據結構,實際上是一個long類型的數組,每一個數組元素都能與一打開的文件句柄(不管是Socket句柄,還是其他文件或命名管道或設備句柄)建立聯系,建立聯系的工作由程序員完成,當調用select()時,由內核根據IO狀態修改fd_set的內容,由此來通知執行了select()的進程哪一Socket或文件可讀。
從流程上來看,使用select函數進行IO請求和同步阻塞模型沒有太大的區別,甚至還多了添加監視socket,以及調用select函數的額外操作,效率更差。但是,使用select以后最大的優勢是用戶可以在一個線程內同時處理多個socket的IO請求。用戶可以注冊多個socket,然后不斷地調用select讀取被激活的socket,即可達到在同一個線程內同時處理多個IO請求的目的。而在同步阻塞模型中,必須通過多線程的方式才能達到這個目的。
select機制的問題
Poll
poll的機制與select類似,與select在本質上沒有多大差別,管理多個描述符也是進行輪詢,根據描述符的狀態進行處理,但是poll沒有最大文件描述符數量的限制。也就是說,poll只解決了上面的問題3,并沒有解決問題1,2的性能開銷問題。
下面是pll的函數原型:
poll改變了文件描述符集合的描述方式,使用了pollfd結構而不是select的fd_set結構,使得poll支持的文件描述符集合限制遠大于select的1024
【參數說明】
struct pollfd *fds fds是一個struct pollfd類型的數組,用于存放需要檢測其狀態的socket描述符,并且調用poll函數之后fds數組不會被清空;一個pollfd結構體表示一個被監視的文件描述符,通過傳遞fds指示 poll() 監視多個文件描述符。其中,結構體的events域是監視該文件描述符的事件掩碼,由用戶來設置這個域,結構體的revents域是文件描述符的操作結果事件掩碼,內核在調用返回時設置這個域
nfds_t nfds 記錄數組fds中描述符的總數量
【返回值】
int 函數返回fds集合中就緒的讀、寫,或出錯的描述符數量,返回0表示超時,返回-1表示出錯;
Epoll
epoll在Linux2.6內核正式提出,是基于事件驅動的I/O方式,相對于select來說,epoll沒有描述符個數限制,使用一個文件描述符管理多個描述符,將用戶關心的文件描述符的事件存放到內核的一個事件表中,這樣在用戶空間和內核空間的copy只需一次。
Linux中提供的epoll相關函數如下:
1. epoll_create 函數創建一個epoll句柄,參數size表明內核要監聽的描述符數量。調用成功時返回一個epoll句柄描述符,失敗時返回-1。
2. epoll_ctl 函數注冊要監聽的事件類型。四個參數解釋如下:
- epfd 表示epoll句柄
- op 表示fd操作類型,有如下3種
- EPOLL_CTL_ADD 注冊新的fd到epfd中
- EPOLL_CTL_MOD 修改已注冊的fd的監聽事件
- EPOLL_CTL_DEL 從epfd中刪除一個fd
- fd 是要監聽的描述符
- event 表示要監聽的事件
epoll_event 結構體定義如下:
3. epoll_wait 函數等待事件的就緒,成功時返回就緒的事件數目,調用失敗時返回 -1,等待超時返回 0。
- epfd 是epoll句柄
- events 表示從內核得到的就緒事件集合
- maxevents 告訴內核events的大小
- timeout 表示等待的超時事件
epoll是Linux內核為處理大批量文件描述符而作了改進的poll,是Linux下多路復用IO接口select/poll的增強版本,它能顯著提高程序在大量并發連接中只有少量活躍的情況下的系統CPU利用率。原因就是獲取事件的時候,它無須遍歷整個被偵聽的描述符集,只要遍歷那些被內核IO事件異步喚醒而加入Ready隊列的描述符集合就行了。
epoll除了提供select/poll那種IO事件的水平觸發(Level Triggered)外,還提供了邊緣觸發(Edge Triggered),這就使得用戶空間程序有可能緩存IO狀態,減少epoll_wait/epoll_pwait的調用,提高應用程序效率。
- 水平觸發(LT):默認工作模式,即當epoll_wait檢測到某描述符事件就緒并通知應用程序時,應用程序可以不立即處理該事件;下次調用epoll_wait時,會再次通知此事件
- 邊緣觸發(ET): 當epoll_wait檢測到某描述符事件就緒并通知應用程序時,應用程序必須立即處理該事件。如果不處理,下次調用epoll_wait時,不會再次通知此事件。(直到你做了某些操作導致該描述符變成未就緒狀態了,也就是說邊緣觸發只在狀態由未就緒變為就緒時只通知一次)。
LT和ET原本應該是用于脈沖信號的,可能用它來解釋更加形象。Level和Edge指的就是觸發點,Level為只要處于水平,那么就一直觸發,而Edge則為上升沿和下降沿的時候觸發。比如:0->1 就是Edge,1->1 就是Level。
ET模式很大程度上減少了epoll事件的觸發次數,因此效率比LT模式下高。
總結
一張圖總結一下select,poll,epoll的區別:
epoll是Linux目前大規模網絡并發程序開發的首選模型。在絕大多數情況下性能遠超select和poll。目前流行的高性能web服務器Nginx正式依賴于epoll提供的高效網絡套接字輪詢服務。但是,在并發連接不高的情況下,多線程+阻塞I/O方式可能性能更好。
備注
既然select,poll,epoll都是I/O多路復用的具體的實現,之所以現在同時存在,其實他們也是不同歷史時期的產物
- select出現是1984年在BSD里面實現的
- 14年之后也就是1997年才實現了poll,其實拖那么久也不是效率問題, 而是那個時代的硬件實在太弱,一臺服務器處理1千多個鏈接簡直就是神一樣的存在了,select很長段時間已經滿足需求
- 2002, 大神 Davide Libenzi 實現了epoll
總結
以上是生活随笔為你收集整理的poll函数_I/O--多路复用的三种机制Select,Poll和Epoll对比的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sklearn pipeline_Skl
- 下一篇: rest 接口怎么传list_如何设计一