IO復用的三種模型

2021-08-25 11:37:40 字數 4328 閱讀 3539

阻塞式i/o:所有套接字預設

非阻塞i/o

i/o復用(select,poll,epoll)

訊號驅動式(sigio):核心在描述符就緒時傳送sigio通知程序

非同步i/o(posix的aio_系列函式):不會阻塞。核心完成後整個操作,通知程序。

同步i/o:真正的io操作程序會阻塞,直到i/o程式結束

非同步i/o:不導致請求程序阻塞

對於乙個套接字的輸入操作分兩步:

第一步從網路端的資料複製到核心接收緩衝區

第二步從核心緩衝區複製到使用者緩衝區

引數:nfds:所監聽的所以檔案描述符中,最大的檔案描述符+1

參2/3/4:所監聽的檔案描述符可讀事件

所監聽的檔案描述符可寫事件

所監聽的檔案描述符異常事件

timeout: 定時阻塞監控時間,3種情況

1.null,永遠等下去

2.設定timeval,等待固定時間

3.設定timeval裡時間均為0,檢查描述字後立即返回,輪詢

struct timeval ;

返回值:所監聽的所以監聽集合中(三種)就緒的總數,若超時則為0,出錯則為-1。事件滿足的個數。

void fd_clr(int fd, fd_set *set);//將fd從set清除出去

int fd_isset(int fd, fd_set *set);//判斷fd是否在滿足集合(set)裡面,滿足返回1

void fd_set(int fd, fd_set *set);//把fd加入set集合中

void fd_zero(fd_set *set);//將set清空 置為0

fd_set是乙個集合可以指定三種事件的集合:可讀(建立連線也算。lfd),可寫,異常.

注:裡面是 0和1的集合 如果fd加入這個集合fd對應的位為1,select返回後,如果滿足就緒則繼續為1,否者為0。這三個引數是值-結果引數,也叫傳入傳出引數。

對於accept:之前需要伺服器阻塞,現在用核心中select去監聽,如果有lfd滿足條件。核心會通知伺服器,伺服器此時呼叫accept且不阻塞立即返回。

步驟:1,fd_set readfds

2,fd_zero

3,fd_set

4,select

如果是連線事件,把accept返回值也加入集合中。

5,判斷是否就緒

可以做乙個優化,不必遍歷所有的檔案描述符。可以自己建立資料結構,只遍歷被監聽的檔案描述符。

select不足:

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

(2)同時每次呼叫select都需要在核心遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大。但是可以用陣列儲存監聽的描述符,使遍歷的範圍變小

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

#include #include #include #include #include #include #include "wrap.h"

#define serv_port 6666

int main(int argc, char *argv)

if (i == fd_setsize)

fd_set(connfd, &allset); /* 加入監聽集合 */

if (connfd > maxfd)

maxfd = connfd; /* 得出最大的描述符*/

if (i > maxi)

maxi = i; /* maxi指向最近新增的檔案描述符位置*/

if (--nready == 0)

continue;

} for (i = 0; i <= maxi; i++) else if (n > 0)

if (--nready == 0)

break; /* 處理事件減一 如果不為0 繼續輪詢*/}}

}close(listenfd);

return 0;

}

#include

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

nfds:監控陣列中有多少檔案描述符需要被監控

timeout:timeout 毫秒級等待

-1:阻塞等,#define inftim -1 linux中沒有定義此巨集

0:立即返回,不阻塞程序

>0:等待指定毫秒數,如當前系統時間精度不夠毫秒,向上取值

struct pollfd

if (i == open_max)

perr_exit("too many clients");

client[i].events = pollin; /* 指定該檔案描述符是什麼事件*/

if (i > maxi)

maxi = i; /* 使maxi一直是有效元素的下標*/

if (--nready <= 0)

continue; /* 進行輪詢 */

}for (i = 1; i <= maxi; i++) else

perr_exit("read error");

} else if (n == 0) else

if (--nready <= 0)

break;}}

}return 0;

}1,檔案描述符的數量大於1024,且可以修改

2,監聽和返回的集合分離,不再是傳入傳出引數

3,有監聽陣列,可以縮小收縮範圍。(select的自定義的陣列就是仿照這個)

#include

建立乙個epoll控制代碼,引數size用來告訴核心監聽的檔案描述符的個數,跟記憶體大小有關

size:紅黑樹上節點個數的建議值(監聽的個數)

返回值:epld,紅黑樹節點的檔案描述符。也叫乙個控制代碼

#include

控制某個epoll監控的檔案描述符上的事件:註冊、修改、刪除。

epfd: 為epoll_creat的控制代碼

op: 表示動作,用3個巨集來表示:

epoll_ctl_add (註冊新的fd到epfd),

epoll_ctl_mod (修改已經註冊的fd的監聽事件),

epoll_ctl_del (從epfd刪除乙個fd);

event: 告訴核心需要監聽的事件

typedef union epoll_data epoll_data_t;

struct epoll_event else else if (n < 0) else }}

}close(listenfd);

close(efd);

return 0;

}檔案描述符數量

讀取滿足條件的fd

是否重複拷貝fd進核心

傳入fd與傳出fd是否分離

select

最大1024

使用者自己建立陣列,陣列中儲存所有監聽的fd,遍歷陣列判斷哪個fd滿足,

(用fd_isset判斷是何種事件),一般不把lfd加入進去

每次呼叫select都會重複把fd複製到核心

select中間三個引數都是傳入傳出引數,沒有分離

poll

可突破1024

核心提供了上述陣列,(然後用revents判斷是何種事件),但是lfd在也在裡面

每次呼叫poll都會重複把fd複製到核心

結構體中的傳入事件和傳出事件實現分離

epoll

可突破1024

核心提供乙個陣列,元素就是全部滿足的fd,不需要判斷是否滿足,只需判斷是何種事件

只會複製到核心一次

傳入是乙個結構體,傳出是乙個滿足陣列元素,所以實現分離

Linux的三種I O復用方式 select

select poll epollselect的原型是 int select int maxfd,fd set readfds,fd set,fd set writefds,fd set exceptfds,struct timeval timeout maxfd 監聽的最大檔案描述符的值 1 輪詢...

Linux的三種I O復用方式 poll

select poll epollpoll的原型是 int poll struct pollfd fds,int nfds,int timeout struct pollfd ndfs 傳入的結構體陣列長度,因為此時的 fds會退化為指標,更準確的來說,ndfs的型別是ndfs t是無型別的long...

IO模型 io多路復用(三)

兩者相互比較 1 如果只有乙個使用者連線server端,多路復用io還不如阻塞io效率高 2 相比阻塞io,多路復用io中間多了個反饋機制 3 多路復用io的 可以同時監控socket 多個socket物件coon1 coon2.recv 4 多路復用io可以識別有人連線某個coon3,然後告知有c...