https://www.cnblogs.com/skyfsm/p/7079458.html
1、select & poll
一、select API介紹:
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
maxfdp:被監聽的文件描述符的總數,它比全部文件描述符集合中的文件描述符的最大值大1,由於文件描述符是從0開始計數的;
readfds、writefds、exceptset:分別指向可讀、可寫和異常等事件對應的描述符集合。
timeout:用於設置select函數的超時時間,即告訴內核select等待多長時間以後就放棄等待。timeout == NULL 表示等待無限長的時間
返回值:超時返回0;失敗返回-1;成功返回大於0的整數,這個整數表示就緒描述符的數目。
如下介紹與select函數相關的常見的幾個宏:
#include <sys/select.h>
int FD_ZERO(int fd, fd_set *fdset); //一個 fd_set類型變量的全部位都設爲 0
int FD_CLR(int fd, fd_set *fdset); //清除某個位時可使用
int FD_SET(int fd, fd_set *fd_set); //設置變量的某個位置位
int FD_ISSET(int fd, fd_set *fdset); //測試某個位是否被置位
二、深刻理解select模型:
理解select模型的關鍵在於理解fd_set,爲說明方便,取fd_set長度爲1字節,fd_set中的每一bit能夠對應一個文件描述符fd。則1字節長的fd_set最大能夠對應8個fd。
(1)執行fd_set set; FD_ZERO(&set); 則set用位表示是0000,0000。
(2)若fd=5,執行FD_SET(fd,&set);後set變爲0001,0000(第5位置爲1)
(3)若再加入fd=2,fd=1,則set變爲0001,0011
(4)執行select(6,&set,0,0,0)阻塞等待
(5)若fd=1,fd=2上都發生可讀事件,則select返回,此時set變爲0000,0011。注意:沒有事件發生的fd=5被清空。select返回後會把之前加入的但並沒有事件發生的fd清空,則每次開始select前都要從新從array取得fd逐一加入(FD_ZERO最早),掃描array的同時取得fd最大值maxfd,用於select的第一個參數。html
select的缺點:
一、單個進程可以監視的文件描述符的數量存在最大限制,一般是1024,固然能夠更改數量,但因爲select採用輪詢的方式掃描文件描述符,文件描述符數量越多,性能越 差;(在linux內核頭文件中,有這樣的定義:#define __FD_SETSIZE 1024)
二、內核 / 用戶空間內存拷貝問題,select須要複製大量的句柄數據結構,產生巨大的開銷;
三、select返回的是含有整個句柄的數組,應用程序須要遍歷整個數組才能發現哪些句柄發生了事件;
四、select的觸發方式是水平觸發,應用程序若是沒有完成對一個已經就緒的文件描述符進行IO操做,那麼以後每次select調用仍是會將這些文件描述符通知進程
相比select模型,poll使用鏈表保存文件描述符,所以沒有了監視文件數量的限制,但其餘三個缺點依然存在。linux
select和poll的用武之地愈來愈有限,風頭已經被epoll佔盡
2、該epoll上場了
---------------------
原文:https://blog.csdn.net/davidsguo008/article/details/73556811
當某一進程調用epoll_create方法時,Linux內核會建立一個eventpoll結構體,用於存放經過epoll_ctl方法向epoll對象中添加進來的事件。這些事件都會掛載在紅黑樹中。
eventpoll結構體以下所示:數組
struct eventpoll{
....
/*紅黑樹的根節點,這顆樹中存儲着全部添加到epoll中的須要監控的事件*/
struct rb_root rbr;
/*雙鏈表中則存放着將要經過epoll_wait返回給用戶的知足條件的事件*/
struct list_head rdlist;
....
}
epoll_create 建立一個epoll對象,通常epollfd = epoll_create()
epoll_ctl (epoll_add/epoll_del的合體),往epoll對象中增長/刪除某一個流的某一個事件
好比
epoll_ctl(epollfd, EPOLL_CTL_ADD, socket, EPOLLIN);//有緩衝區內有數據時epoll_wait返回
epoll_ctl(epollfd, EPOLL_CTL_DEL, socket, EPOLLOUT);//緩衝區可寫入時epoll_wait返回
epoll_wait(epollfd,...)等待直到註冊的事件發生
每個epoll對象都有一個獨立的eventpoll結構體,用於存放經過epoll_ctl方法向epoll對象中添加進來的事件。這些事件都會掛載在紅黑樹中。經過紅黑樹和雙鏈表數據結構,並結合回調機制,造就了epoll的高效。
3、kqueue & kevent
linux有epoll,Free BSD都有它們本身的實現kqueue。有一種說法是kqueue技術上比epoll更優。
kqueue()函數行爲有點相似於epoll_create()。可是,kevent()卻集成了epoll_ctl()(用於調整興趣集)和epoll_wait()(獲取事件) 的角色。註冊一批socket描述符到 kqueue 之後,當其中的描述符狀態發生變化時,kqueue 將一次性通知應用程序哪些描述符可讀、可寫或出錯了。
int kqueue(void);數據結構
int kevent(int kq, const struct kevent *changelist, int nchanges,
struct kevent *eventlist, int nevents, const struct timespec *timeout);
struct kevent {
uintptr_t ident; /* 事件 ID */
short filter; /* 事件過濾器 */
u_short flags; /* 行爲標識 */
u_int fflags; /* 過濾器標識值 */
intptr_t data; /* 過濾器數據 */
void *udata; /* 應用透傳數據 */
};
kqueue 中,{ident, filter} 肯定一個惟一的事件。
ident:事件的 id,通常設置爲文件描述符。
filter:能夠將 kqueue filter 看做事件,例如:EVFILT_READ,EVFILT_WRIT。
行爲標誌flags:
EV_ADD:指示加入事件到 kqueue
EV_DELETE:指示將傳入的事件從 kqueue 中移除
過濾器標識值:
EV_ENABLE:過濾器事件可用,註冊一個事件時,默認是可用的。
EV_DISABLE:過濾器事件不可用,當內部描述可讀或可寫時,將不通知應用程序。socket