天天看點

select、poll、epoll之間的差別總結

select、poll、epoll之間的差別總結[整理]

  select,poll,epoll都是IO多路複用的機制。I/O多路複用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而異步I/O則無需自己負責進行讀寫,異步I/O的實作會負責把資料從核心拷貝到使用者空間。關于這三種IO多路複用的用法,前面三篇總結寫的很清楚,并用伺服器回射echo程式進行了測試。連接配接如下所示:

select:http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html

poll:http://www.cnblogs.com/Anker/archive/2013/08/15/3261006.html

epoll:http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html

  今天對這三種IO多路複用進行對比,參考網上和書上面的資料,整理如下:

1、select實作

select的調用過程如下所示:

select、poll、epoll之間的差別總結

(1)使用copy_from_user從使用者空間拷貝fd_set到核心空間

(2)注冊回調函數__pollwait

(3)周遊所有fd,調用其對應的poll方法(對于socket,這個poll方法是sock_poll,sock_poll根據情況會調用到tcp_poll,udp_poll或者datagram_poll)

(4)以tcp_poll為例,其核心實作就是__pollwait,也就是上面注冊的回調函數。

(5)__pollwait的主要工作就是把current(目前程序)挂到裝置的等待隊列中,不同的裝置有不同的等待隊列,對于tcp_poll

來說,其等待隊列是sk->sk_sleep(注意把程序挂到等待隊列中并不代表程序已經睡眠了)。在裝置收到一條消息(網絡裝置)或填寫完檔案數

據(磁盤裝置)後,會喚醒裝置等待隊列上睡眠的程序,這時current便被喚醒了。

(6)poll方法傳回時會傳回一個描述讀寫操作是否就緒的mask掩碼,根據這個mask掩碼給fd_set指派。

(7)如果周遊完所有的fd,還沒有傳回一個可讀寫的mask掩碼,則會調用schedule_timeout是調用select的程序(也就是

current)進入睡眠。當裝置驅動發生自身資源可讀寫後,會喚醒其等待隊列上睡眠的程序。如果超過一定的逾時時間(schedule_timeout

指定),還是沒人喚醒,則調用select的程序會重新被喚醒獲得CPU,進而重新周遊fd,判斷有沒有就緒的fd。

(8)把fd_set從核心空間拷貝到使用者空間。

總結:

select的幾大缺點:

(1)每次調用select,都需要把fd集合從使用者态拷貝到核心态,這個開銷在fd很多時會很大

(2)同時每次調用select都需要在核心周遊傳遞進來的所有fd,這個開銷在fd很多時也很大

(3)select支援的檔案描述符數量太小了,預設是1024

2 poll實作

  poll的實作和select非常相似,隻是描述fd集合的方式不同,poll使用pollfd結構而不是select的fd_set結構,其他的都差不多。

關于select和poll的實作分析,可以參考下面幾篇博文:

http://blog.csdn.net/lizhiguo0532/article/details/6568964#comments

http://blog.csdn.net/lizhiguo0532/article/details/6568968

http://blog.csdn.net/lizhiguo0532/article/details/6568969

http://www.ibm.com/developerworks/cn/linux/l-cn-edntwk/index.html?ca=drs-

http://linux.chinaunix.net/techdoc/net/2009/05/03/1109887.shtml

3、epoll

  epoll既然是對select和poll的改進,就應該能避免上述的三個缺點。那epoll都是怎麼解決的呢?在此之前,我們先看一下

epoll和select和poll的調用接口上的不同,select和poll都隻提供了一個函數——select或者poll函數。而epoll提供

了三個函數,epoll_create,epoll_ctl和epoll_wait,epoll_create是建立一個epoll句

柄;epoll_ctl是注冊要監聽的事件類型;epoll_wait則是等待事件的産生。

  對于第一個缺點,epoll的解決方案在epoll_ctl函數中。每次注冊新的事件到epoll句柄中時(在epoll_ctl中指定

EPOLL_CTL_ADD),會把所有的fd拷貝進核心,而不是在epoll_wait的時候重複拷貝。epoll保證了每個fd在整個過程中隻會拷貝

一次。

  對于第二個缺點,epoll的解決方案不像select或poll一樣每次都把current輪流加入fd對應的裝置等待隊列中,而隻在

epoll_ctl時把current挂一遍(這一遍必不可少)并為每個fd指定一個回調函數,當裝置就緒,喚醒等待隊列上的等待者時,就會調用這個回調

函數,而這個回調函數會把就緒的fd加入一個就緒連結清單)。epoll_wait的工作實際上就是在這個就緒連結清單中檢視有沒有就緒的fd(利用

schedule_timeout()實作睡一會,判斷一會的效果,和select實作中的第7步是類似的)。

  對于第三個缺點,epoll沒有這個限制,它所支援的FD上限是最大可以打開檔案的數目,這個數字一般遠大于2048,舉個例子,

在1GB記憶體的機器上大約是10萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統記憶體關系很大。

(1)select,poll實作需要自己不斷輪詢所有fd集合,直到裝置就緒,期間可能要睡眠和喚醒多次交替。而epoll其實也需要調用

epoll_wait不斷輪詢就緒連結清單,期間也可能多次睡眠和喚醒交替,但是它是裝置就緒時,調用回調函數,把就緒fd放入就緒連結清單中,并喚醒在

epoll_wait中進入睡眠的程序。雖然都要睡眠和交替,但是select和poll在“醒着”的時候要周遊整個fd集合,而epoll在“醒着”的

時候隻要判斷一下就緒連結清單是否為空就行了,這節省了大量的CPU時間。這就是回調機制帶來的性能提升。

(2)select,poll每次調用都要把fd集合從使用者态往核心态拷貝一次,并且要把current往裝置等待隊列中挂一次,而epoll隻要

一次拷貝,而且把current往等待隊列上挂也隻挂一次(在epoll_wait的開始,注意這裡的等待隊列并不是裝置等待隊列,隻是一個epoll内

部定義的等待隊列)。這也能節省不少的開銷。

參考資料:

http://www.cnblogs.com/apprentice89/archive/2013/05/09/3070051.html

http://www.linuxidc.com/Linux/2012-05/59873p3.htm

http://xingyunbaijunwei.blog.163.com/blog/static/76538067201241685556302/

繼續閱讀