DPDK(二):准備知識10 --- epoll


https://www.cnblogs.com/skyfsm/p/7079458.html
一、select & poll
1、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); //測試某個位是否被置位
2、深入理解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的第一個參數。

select的缺點:
1、單個進程能夠監視的文件描述符的數量存在最大限制,通常是1024,當然可以更改數量,但由於select采用輪詢的方式掃描文件描述符,文件描述符數量越多,性能越 差;(在linux內核頭文件中,有這樣的定義:#define __FD_SETSIZE    1024)
2、內核 / 用戶空間內存拷貝問題,select需要復制大量的句柄數據結構,產生巨大的開銷;
3、select返回的是含有整個句柄的數組,應用程序需要遍歷整個數組才能發現哪些句柄發生了事件;
4、select的觸發方式是水平觸發,應用程序如果沒有完成對一個已經就緒的文件描述符進行IO操作,那么之后每次select調用還是會將這些文件描述符通知進程
相比select模型,poll使用鏈表保存文件描述符,因此沒有了監視文件數量的限制,但其他三個缺點依然存在。

select和poll的用武之地越來越有限,風頭已經被epoll占盡
二、該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的高效。
三、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:過濾器事件不可用,當內部描述可讀或可寫時,將不通知應用程序。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM