1. 程式人生 > >Linux文件IO(四)IO多路復用

Linux文件IO(四)IO多路復用

lin 效率 結構體 其余 多文件 timeout wait 文件的 多個

當程序進行IO時,如果數據尚未準備好,那麽IO將處於阻塞狀態。當某個進程有多個打開的文件,比如socket,那麽其後的所有準備好讀寫的文件將受到阻塞的影響而不能操作。不借助線程,單一進程無法在同一時間服務多個文件描述符。非阻擋式IO可以作為一個解決方案,但是效率並不高。首先進程需要不斷發IO請求,其次,如果程序可以休眠,讓出CPU將提高效率。多任務式IO是在其中任何一個文件描述符就緒時收到通知,此時IO將不會受到阻擋,其余時間處於休眠狀態,將CPU資源讓給別的進程。
I/O多路復用的設計遵循以下原則:
1,I/O多路復用:當任何文件描述符準備好I/O時告訴我
2,在一個或更多文件描述符就緒前始終處於睡眠狀態。
3,喚醒:哪個準備好了?
4,在不阻塞的情況下處理所有I/O就緒的文件描述符。
5,返回第一步,重新開始。

多任務式IO主要有select、poll、epoll三種:
select監視一大片文件描述符,監視的範圍是最大的文件描述符maxfd加1,並將所監視的文件描述符分為三組,采用FD_ISSET宏查看哪些fd對應的bitmap變為準備狀態,就可以對其進行操作。由於select()在各種Unix系統中都很容易實現,相對於微秒級精度的睡眠機 制來講,經常將select()做為一種可移植的微秒級的睡眠機制:select (0, NULL, NULL, NULL, &tv)。此外BSD還是先了pselect()系統調用。Select可以用在文件描述符數量相對比較確定的場景。

poll是System V的IO多路復用方案,采用pollfd數組來設定需要監視的文件描述符,相比select需要為每個分組檢查maxfd+1個比特位,效率更高。

EPoll接口
epoll把監聽註冊從實際監聽中分離出來,從而解決了這個問題。一個系統調用初始化一個epoll上下文,另一個從上下文中加入或刪除需要監視的文件描述符,第三個執行真正的事件等待(eventwait)。如果epoll_ctl()的參數event中的events項設置為EPOLLE,fd上的監聽稱為邊沿觸發,相反的稱為水平觸發。對於水平觸發的監聽,在步驟2裏對epoll_wait()的調用將立即返回,以表明pipe可讀。對於邊沿觸發的監聽,這個調用直到步驟1發生後才會返回。也就是說,即使調用epoll_wait()時管道已經可讀,調用仍然會等待直到有數據寫入,之後返回。水平觸發是默認行為。也是poll()和select()的行為,也是大多數開發者所期望的。邊沿觸發需要一個不同的方式來寫程序,通常利用非阻塞I/O,並需要仔細檢查EAGAIN。

水平觸發在一個狀態發生時觸發。邊沿觸發只有在狀態改變的時候才會產生。水平觸發在只關心狀態時有用。邊沿觸發則在關心事件本身時有用。

epoll是Linux下多路復用IO接口select/poll的增強版本,它能顯著提高程序在大量並發連接中只有少量活躍的情況下的系統CPU利用率,因為它不會復用文件描述符集合來傳遞結果而迫使開發者每次等待事件之前都必須重新準備要被偵聽的文件描述符集合,另一點原因就是獲取事件的時候,它無須遍歷整個被偵聽的描述符集,只要遍歷那些被內核IO事件異步喚醒而加入Ready隊列的描述符集合就行了。epoll除了提供select/poll那種IO事件的電平觸發(Level Triggered)外,還提供了邊沿觸發(Edge Triggered),這就使得用戶空間程序有可能緩存IO狀態,減少epoll_wait/epoll_pwait的調用,提高應用程序效率。epoll的主要優點有:
1、支持一個進程打開大數目的socket描述符;
2、IO效率不隨FD數目增加而線性下降;
3、使用mmap加速內核與用戶空間的消息傳遞。
int epoll_create (int size)
epoll_create()創 建 一 個 epoll實例,返回與該實例關聯的文件描述符。這個文件描述符和真正的文件沒有關系,僅僅是為了後續調用使用epoll而創建的。

int epoll_ctl (int epfd, int op, int fd, struct epoll_event *event);
在epfd實例中加入一個fd指定的監聽文件參數op用來添加、刪除、修改fd指定文件的事件。events結構體中的events參數列出了在給定文件描述符上監聽的事件。多個事件可以使用位或運算同時指定。最常見的是讀寫事件的監聽: EPOLLIN | EPOLLOUT。

int epoll_wait (int epfd, struct epoll_event events, int maxevents, int timeout);
返回值是事件數,出錯返回-1。events要保證有足夠的buffer:
events = malloc (sizeof (struct epoll_event)
MAX_EVENTS);
nr_events = epoll_wait (epfd, events, MAX_EVENTS, -1);
如果timeout為 0, 即使沒有事件發生 , 調用也立即返回,此時調用返回0。如果timeout為 -1,調用將一直等待到有事件發生。

Linux文件IO(四)IO多路復用