i/o多路復用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。select,poll,epoll都是io多路復用的機制。
相關函式定義如下
/* according to posix.1-2001, posix.1-2008 */
#include /* according to earlier standards */
#include #include #include int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
//返回值:就緒描述符的數目,超時返回0,出錯返回-1
//第乙個引數nfds指定待測試的描述字個數,它的值是待測試的最大描述字加1,描述字0、1、2...nfds-1均將被測試。
void fd_clr(int fd, fd_set *set); //將乙個給定的檔案描述符從集合中刪除
int fd_isset(int fd, fd_set *set); //檢查集合中指定的檔案描述符是否可以讀寫
void fd_set(int fd, fd_set *set); //將乙個給定的檔案描述符加入集合之中
void fd_zero(fd_set *set); //清空集合
select將監聽的檔案描述符分為三組,每一組監聽不同的需要進行的io操作。readfds是需要進行讀操作的檔案描述符,writefds是需要進行寫操作的檔案描述符,exceptfds是需要進行異常處理處理的檔案描述符。這三個引數可以用null來表示對應的事件不需要監聽。
fd_xx系列的函式是用來操作檔案描述符組和檔案描述符的關係。
timeout告知核心等待所指定描述字中的任何乙個就緒可花多少時間。其timeval結構用於指定這段時間的秒數和微秒數。
struct timeval;
//events引數是我們需要關心的事件,revents是所有核心監測到的事件。每乙個pollfd結構體指定了乙個被監視的檔案描述符,可以傳遞多個結構體,events域中請求的任何事件都可能在revents域中返回。
合法的事件如下:
pollin 有資料可讀。
pollrdnorm 有普通資料可讀。
pollrdband 有優先資料可讀。
pollpri 有緊迫資料可讀。
pollout 寫資料不會導致阻塞。
pollwrnorm 寫普通資料不會導致阻塞。
pollwrband 寫優先資料不會導致阻塞。
pollmsgsigpoll 訊息可用。
此外,revents域中還可能返回下列事件:
poller 指定的檔案描述符發生錯誤。
pollhup 指定的檔案描述符掛起事件。
pollnval 指定的檔案描述符非法。
epoll是在2.6核心中提出的,是之前的select和poll的增強版本。相對於select和poll來說,epoll更加靈活,沒有描述符限制。epoll使用乙個檔案描述符管理多個描述符,將使用者關係的檔案描述符的事件存放到核心的乙個事件表中,這樣在使用者空間和核心空間的copy只需一次。
#include 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);
int epoll_create(int size);
建立乙個epoll的控制代碼,size用來告訴核心這個監聽的數目一共有多大。這個引數不同於select()中的第乙個引數,給出最大監聽的fd+1的值。需要注意的是,當建立好epoll控制代碼後,它就是會占用乙個fd值,在linux下如果檢視/proc/程序id/fd/,是能夠看到這個fd的,所以在使用完epoll後,必須呼叫close()關閉,否則可能導致fd被耗盡。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件註冊函式,它不同與select()是在監聽事件時告訴核心要監聽什麼型別的事件,而是在這裡先註冊要監聽的事件型別。
第乙個引數是epoll_create()的返回值;
第二個引數表示動作,用三個巨集來表示:
epoll_ctl_add:註冊新的fd到epfd中;
epoll_ctl_mod:修改已經註冊的fd的監聽事件;
epoll_ctl_del:從epfd中刪除乙個fd;
第三個引數是需要監聽的fd,
第四個引數是告訴核心需要監聽什麼事,struct epoll_event結構如下:
struct epoll_event ;
events可以是以下幾個巨集的集合:
epollin :表示對應的檔案描述符可以讀;
epollout:表示對應的檔案描述符可以寫;
epollpri:表示對應的檔案描述符有緊急的資料可讀;
epollerr:表示對應的檔案描述符發生錯誤;
epollhup:表示對應的檔案描述符被結束通話;
epollet: 將epoll設為邊緣觸發(edge triggered)模式,這是相對於水平觸發(level triggered)來說的。
epolloneshot:只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到epoll佇列裡
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待事件的產生,類似於select()呼叫。引數events用來從核心得到事件的集合,maxevents告之核心這個events有多大,這個maxevents的值不能大於建立epoll_create()時的size,引數timeout是超時時間
(毫秒,0會立即返回,-1將不確定,也有說法說是永久阻塞)。該函式返回需要處理的事件數目,如返回0表示已超時。
epoll對檔案描述符的操作有兩種模式:lt(level trigger)和et(edge trigger)。lt模式是預設模式,lt模式與et模式的區別如下:
lt模式:當epoll_wait檢測到描述符事件發生並將此事件通知應用程式,應用程式可以不立即處理該事件。下次呼叫epoll_wait時,會再次響應應用程式並通知此事件。
et模式:當epoll_wait檢測到描述符事件發生並將此事件通知應用程式,應用程式必須立即處理該事件。如果不處理,下次呼叫epoll_wait時,不會再次響應應用程式並通知此事件。
et模式在很大程度上減少了epoll事件被重複觸發的次數,因此效率要比lt模式高。epoll工作在et模式的時候,必須使用非阻塞套介面,以避免由於乙個檔案控制代碼的阻塞讀/阻塞寫操作把處理多個檔案描述符的任務餓死。
poll和select基本上是一樣的,poll相比select好在如下幾點:
poll傳參對使用者更友好。比如不需要和select一樣計算很多奇怪的引數比如nfds(值最大的檔案描述符+1),再比如不需要分開三組傳入引數。
poll會比select效能稍好些,因為select是每個bit位都檢測,假設有個值為1000的檔案描述符,select會從第一位開始檢測一直到第1000個bit位。但poll檢測的是乙個陣列。
select的時間引數在返回的時候各個系統的處理方式不統一,如果希望程式可移植性更好,需要每次呼叫select都初始化時間引數。
而select比poll好在下面幾點
支援select的系統更多,相容更強大,有一些unix系統不支援poll
select提供精度更高(到microsecond)的超時時間,而poll只提供到毫秒的精度。
但總體而言 select和poll基本一致。
epoll優於select&poll在下面幾點:
在需要同時監聽的檔案描述符數量增加時,select&poll是o(n)的複雜度,epoll是o(1),在n很小的情況下,差距不會特別大,但如果n很大的前提下,一次o(n)的迴圈可要比o(1)慢很多,所以高效能的網路伺服器都會選擇epoll進行io多路復用。
epoll內部用乙個檔案描述符掛載需要監聽的檔案描述符,這個epoll的檔案描述符可以在多個執行緒/程序共享,所以epoll的使用場景要比select&poll要多。
參考:
I O多路復用
一 五種i o模型 1 阻塞i o模型 最流行的i o模型是阻塞i o模型,預設情形下,所有套介面都是阻塞的。我們以資料報套介面為例來講解此模型 我們使用udp而不是tcp作為例子的原因在於就udp而言,資料準備好讀取的概念比較簡單 要麼整個資料報已經收到,要麼還沒有。然而對於tcp來說,諸如套介面...
i o多路復用
最常見的i o多路復用就是 select poll epoll了,下面說說他們的一些特點和區別吧。select 可讀 可寫 異常三種檔案描述符集的申明和初始化。fd set readfds,writefds,exceptionfds fd zero readfds fd zero writefds ...
I O多路復用
我們都知道unix like 世界裡,一切皆檔案,而檔案是什麼呢?檔案就是一串二進位製流而已,不管socket,還是fifo 管道 終端,對我們來說,一切都是檔案,一切都是流。在資訊 交換的過程中,我們都是對這些流進行資料的收發操作,簡稱為i o操作 input and output 往流中讀出資料...