IO多路復用 select poll和epoll

2021-08-20 10:37:33 字數 3443 閱讀 9306

select

#include 

int select(int maxfdp1, fd_set *restrict readfds,

fd_set *restrict writefds, fd_set *restrict exceptfds,

struct timeval *restrict tvptr);

引數描述

select的第乙個引數maxfdp1的意思是「最大檔案描述符編號值加 1」。考慮所有 3 個描述符集合,在這 3 個描述符集合中找到最大描述符編號值,然後加 1,就是第乙個引數。

第二個到第四個引數readfdswritefdsexceptfds是指向描述符集合的指標。這三個描述符集說明了我們關心的可讀、可寫或處於異常條件的描述符集合。每個描述符集合儲存在乙個fd_set資料型別中。這個資料型別是由實現選擇的,它可以為每乙個可能的描述符保持一位。我們可以認為它只是乙個很大的位元組陣列。

這個fd_set型別的描述符集合可以用以下的函式來操作,向集合中增加或者刪除乙個關心的描述符。

#include 

int fd_isset(int fd, fd_set *fdset);

void fd_clr(int fd, fd_set *fdset);

void fd_set(int fd, fd_set *fdset);

void fd_zero(fd_set *fdset);

最後乙個引數tvptr表示願意等待的時間長度。如果超過這個時間,則不再等待。

返回值select有三個可能的返回值:

(1)返回 -1 表示出錯。例如,在指定的描述符乙個都沒準備好的時候收到了乙個訊號。此情況下,乙個描述符集都不修改。

(2)返回 0 表示沒有描述符準備好。超時發生時會產生這樣的狀況。此時,所有描述符集都會置 0。

(3)乙個正的返回值說明了已經準備好的描述符數。

poll

#include 

int poll(struct pollfd fdarray, nfds_t nfds, int timeout);

struct pollfd ;

引數描述

poll並未分別為每乙個條件(可讀、可寫和異常)構造乙個描述符集,而是構造乙個pollfd結構的陣列,每個陣列元素指定乙個描述符編號以及我們對該描述符感興趣的條件。

其中前兩個引數就是指定了我們所感興趣的描述符和其對應的事件以及陣列的長度。第三個引數依舊是超時時間。

詳細來說,poolfd的第乙個成員表示了要監聽的描述符,第二個成員為我們關心的是每個描述符的哪些事件。返回時,revents成員由核心設定,用於說明每個描述符發生了哪些事件。poll並沒有更改events成員。這與select不同,不通過修改其引數指示哪個描述符已經準備好了。

epoll

建立 epoll 例項

int epoll_create1(int flags);
flags:當前版本只支援 epoll_cloexec 標誌

我們也可以通過epoll_create(int size)這個函式來建立 epoll 例項,只不過這個函式中的 size 在 2.6.277 核心開始就不必要了:

syscall_define1(epoll_create, int, size)
管理 epoll 事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
op 可以有三個值:

typedef

union epoll_data epoll_data_t;

struct epoll_event ;

其中,data是乙個聯合體,能夠儲存fd或其它資料,我們需要根據自己的需求定製。events表示監控的事件的集合,是乙個狀態值,通過狀態位來表示,可以設定如下事件:

等待 epoll 事件的發生

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
lt和et原本應該是用於脈衝訊號的,可能用它來解釋更加形象。level和edge指的就是觸發點,level為只要處於水平,那麼就一直觸發,而edge則為上公升沿和下降沿的時候觸發。聽起來到時挺玄乎的,那麼怎麼區分這個level和edge呢?很簡單,0->1這種型別的事件就是edge,而level則正好相反,1->1這種型別就是,由此可見,當緩衝區有資料可取的時候,et會觸發一次事件,之後就不會再觸發,而lt只要我們沒有取完緩衝區的資料,就會一直觸發。

附上demo:

#define max_events 10

struct epoll_event ev, events[max_events];

int listen_sock, conn_sock, nfds, epollfd;

/* code to set up listening socket, 'listen_sock',

* (socket(), bind(), listen()) omitted */

epollfd = epoll_create1( 0 );

if ( epollfd == -1 )

ev.events = epollin;

ev.data.fd = listen_sock;

if ( epoll_ctl( epollfd, epoll_ctl_add, listen_sock, &ev ) == -1 )

for (;; )

for ( n = 0; n < nfds; ++n )

setnonblocking( conn_sock );

ev.events = epollin | epollet;

ev.data.fd = conn_sock;

if ( epoll_ctl( epollfd, epoll_ctl_add, conn_sock,

&ev ) == -1 )

} else

}}

參考

[1] 大話 select、poll、epoll

[2] linux epoll 詳解

[3] epoll 的使用詳解

[4] epoll核心原始碼詳解+自己總結的流程

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 往流中讀出資料...