簡介 IO復用技術,簡單來說就是同時監聽多個描述符。在沒有用到IO復用以前,只能是一個線程或一個 線程去監聽,服務端同時有多個連接的時候,需要創建多個線程或者進程。而且,並不是所有的連 接是一直在傳輸這數據,可能只是連接後啥都沒乾,如果這樣,進程就啥都沒乾。 現在有了IO復用技術,只有描述符就緒的時 ...
簡介
IO復用技術,簡單來說就是同時監聽多個描述符。在沒有用到IO復用以前,只能是一個線程或一個
線程去監聽,服務端同時有多個連接的時候,需要創建多個線程或者進程。而且,並不是所有的連
接是一直在傳輸這數據,可能只是連接後啥都沒乾,如果這樣,進程就啥都沒乾。
現在有了IO復用技術,只有描述符就緒的時候才去處理,這樣就很方便了。
IO復用的使用大概是這樣:
設置要監聽的描述符以及需要監聽的事件
↓
監聽事件,一直阻塞直到有描述符就緒
↓
遍歷所有就緒的描述符,併進行相應處理
IO復用有三種方式,分別是select, poll, epoll
select
select函數原型
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
參數詳解:
nfds:nfds參數指定的是被監聽的文件描述符的總數,一般設置成最大的描述符加上1,因為描述
符是從0開始的。
readfds:需要監聽讀事件的描述符集
writefds:需要監聽寫事件的描述符集
exceptfds:需要監聽異常事件的描述符集
timeout:設置超時時間,如果傳遞的是NULL,則select會一直阻塞
一般的使用步驟:
1 設置FD_SET集合,如果要同時監聽多種事件,則需要使用多個描述符集合
2 調用select,select會通過更改描述集,只留下準備好的描述符,所以需要在調用前保留一份
3 同時描述符需要在數組中存儲一份,這個通過遍曆數組和FD_ISSET來判斷是否準備好,若準備好則去執行相關任務
缺點:
select 最大支持的描述符有限一般是1024。
同時select內部的操作是等待描述符集合,必然需要進入到內核態,所以,每次需要把描述符集合
從用戶空間拷貝到內核空間,這樣是特別消耗資源的,同時select這種方式,只保留準備好的描述符
這樣,使用特別不方便,每次還需要對數組進行遍歷來判斷描述符是否在準備好的集合當中。
poll
poll相對於select的優點就是解決了描述符的限制問題,但是性能並不好。poll也是需要通過遍歷
的方式來判斷描述符的準備狀態,當描述符較多時,則就尷尬了。
函數原型:
int poll(struct pollfd *fds, nfd_t nfds, int timeout);
struct pollfd
{
int fd; //文件描述符
short events; //註冊的事件
short revents; //實際發生的事件
}
參數詳解:
fds:需要監聽的數組,數組裡的每個結構體都設置好描述符和需要註冊的事件,如果有多個,使用或('|')操作符
nfds:數組的長度
timeout:指定超時值,單位是毫秒
常用的巨集:
POLLIN:數據(包括普通數據和優先數據)可讀
POLLOUT:數據(包括普通數據和優先數據)可寫
epoll
epoll 相比select和poll不同,epoll將監聽的描述符集存放在內核區,免去了select和poll每次調用
都需要將描述符集從用戶空間拷貝到內核空間的消耗。同時epoll的對於已經準備好的描述符處理比較方便。
還增加了一些新的特性,比如ET和LT兩種觸發模式,
epoll提供了三個函數:
int epoll_create(int size);
epoll_create用來創建一個描述符,指向內核創建的描述符集。之後的操作都通過描述符來操作。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epfd:傳入的是指向描述符集的描述符
op:有三個巨集:
EPOLL_CTL_ADD: 添加fd上的註冊事件
EPOLL_CTL_MOD: 修改fd上的註冊事件
EPOLL_CTL_DEL: 刪除fd上的註冊事件
fd:需要操作的描述符
event:
struct epoll_event
{
__uint32_t events; //epoll事件
epoll_data_t data; //用戶數據
}
typedef union epoll_data_t
{
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
函數詳解:
返回值:返回準備好的描述符的個數
epfd:指向描述符集的描述符
events:這裡是作為函數傳出使用,epoll_wait之後會設置這個數組,之後再遍歷這個數組即可
maxevents:指定最多監聽的描述符的個數
timeout:超時時間,和poll相同。
epoll有兩種觸發模式,一種是LT(條件觸發)一種ET(邊沿觸發)
LT條件觸發:
當滿足某個條件,就會觸發,如果沒有去處理,會一直去觸發
LT是預設的觸發方式
ET邊沿觸發:
當滿足某個條件之後觸發一次,如果未處理,就不管了。
在設置觸發事件的時候或上 EPOLLET
那麼問題來了?
如果之後還有數據到來,還會觸發嗎?
如果會,那麼之前未處理的數據還在嗎,會丟棄嗎?
如果之後還有新的數據來,依然會觸發,同時之前緩衝的數據可以一塊讀出來。