linux系統中的異步IO模型select,poll,epoll

我們都知道linux的哲學是一切接文件,也就是說無論是我們的進程,線程,還是我們的設備,最後都對應的是文件,而文件操作就是linux的核心。那麼linux中有幾種操作輸入輸出的模型呢?答案是三種,就是select,poll,epoll。


linux系統中的異步IO模型select,poll,epoll


select


linux系統中的異步IO模型select,poll,epoll

這個模型是在1983年的unix系統中引入的,它通過fd_set變量來控制設備需要等待什麼。也就是說linux中有個select函數,我們是通過它來監控文件狀態的。

int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);

(1)intmaxfdp是一個整數值,是指集合中所有文件描述符的範圍,即所有文件描述符的最大值加1,不能錯。

說明:對於這個原理的解釋可以看上邊fd_set的詳細解釋,fd_set是以位圖的形式來存儲這些文件描述符。maxfdp也就是定義了位圖中有效的位的個數。

(2)fd_set*readfds是指向fd_set結構的指針,這個集合中應該包括文件描述符,我們是要監視這些文件描述符的讀變化的,即我們關心是否可以從這些文件中讀取數據了,如果這個集合中有一個文件可讀,select就會返回一個大於0的值,表示有文件可讀;如果沒有可讀的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入NULL值,表示不關心任何文件的讀變化。

(3)fd_set*writefds是指向fd_set結構的指針,這個集合中應該包括文件描述符,我們是要監視這些文件描述符的寫變化的,即我們關心是否可以向這些文件中寫入數據了,如果這個集合中有一個文件可寫,select就會返回一個大於0的值,表示有文件可寫,如果沒有可寫的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入NULL值,表示不關心任何文件的寫變化。

(4)fd_set*errorfds同上面兩個參數的意圖,用來監視文件錯誤異常文件。

(5)structtimeval* timeout是select的超時時間,這個參數至關重要,它可以使select處於三種狀態,第一,若將NULL以形參傳入,即不傳入時間結構,就是將select置於阻塞狀態,一定等到監視文件描述符集合中某個文件描述符發生變化為止;第三,若將時間值設為0秒0毫秒,就變成一個純粹的非阻塞函數,不管文件描述符是否有變化,都立刻返回繼續執行,文件無變化返回0,有變化返回一個正值;第三,timeout的值大於0,這就是等待的超時時間,即 select在timeout時間內阻塞,超時時間之內有事件到來就返回了,否則在超時後不管怎樣一定返回,返回值同上述。

poll

poll在1986年引入unix中,在linux中是在1997年引入的。它的函數定義為:

int poll ( struct pollfd * fds, unsigned int nfds, int timeout);

fds:是一個struct pollfd結構類型的數組,用於存放需要檢測其狀態的Socket描述符;每當調用這個函數之後,系統不會清空這個數組,操作起來比較方便;特別是對於socket連接比較多的情況下,在一定程度上可以提高處理的效率;這一點與select()函數不同,調用select()函數之後,select()函數會清空它所檢測的socket描述符集合,導致每次調用select()之前都必須把socket描述符重新加入到待檢測的集合中;因此,select()函數適合於只檢測一個socket描述符的情況,而poll()函數適合於大量socket描述符的情況;

linux系統中的異步IO模型select,poll,epoll

epoll

linux系統中的異步IO模型select,poll,epoll

epoll是在linux2.5內核中引入的,目前是比poll和select模型都要高效。epoll實現主要實現三個接口。

int epoll_create(int size);

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

首先,調用epoll_create建立一個epoll對象。參數size是內核保證能夠正確處理的最大句柄數。

然後,epoll_ctl可以操作上面建立的epoll,例如,將剛建立的socket加入到epoll中讓其監控,或者把 epoll正在監控的某個socket句並移出epoll,不再監控它等等。

最後,epoll_wait在調用時,在給定的timeout時間內,當在監控的所有句柄中有事件發生時,就返回用戶態的進程。

epoll提供兩種觸發模式,一種水平觸發,一種邊緣觸發。

水平觸發:就是隻有高電平(1)或低電平(0)時才觸發通知,只要在這兩種狀態就能得到通知。

邊緣觸發:只有電平發生變化(高電平到低電平,或者低電平到高電平)的時候才觸發通知。

簡單理解,在水平觸發的時候,可以時刻監測IO的狀態,而邊緣觸發,只有下次IO活動到來的時候,才進行通知。因此,當監視數量描述符多的時候,邊緣觸發的epoll效率更高。

<code># operations  |  poll  |  select   | epoll10            |   0.61 |    0.73   | 0.41100           |   2.9  |    3.0    | 0.421000          |  35    |   35      | 0.5310000         | 990    |  930      | 0.66/<code>

總結

linux系統中的異步IO模型select,poll,epoll

epoll採用基於事件的就緒通知方式。在select/poll中,進程只有在調用一定的方法後,內核才對所有監視的文件描述符進行掃描,而epoll事先通過epoll_ctl()來註冊一個文件描述符,一旦基於某個文件描述符就緒時,內核會採用類似callback的回調機制,迅速激活這個文件描述符,當進程調用epoll_wait()時便得到通知。


分享到:


相關文章: