poll
poll 對比select 沒有監控的文件描述符上限
採用事件結構信息進行描述符監控,簡化了三種描述符的操作流程
但是poll依然需要將事件結構信息拷貝到內核進行監控
在內核中需要輪詢遍歷的方式進行描述符事件監控(隨着描述符增多而性能降低)
poll也不會告訴用戶具體哪個描述符就緒,需要遍歷判斷revents來決定描述符應該進行何種操作
epoll
int epoll_create(int size) //在內核中創建eventpoll結構體
// size 決定epoll監控多少描述符
//返回值:返回描述符,決定epoll的操作句柄
//struct eventpoll { 紅黑樹,雙向鏈表}
int epoll_ctl (int epfd,int op,int fd,struct epoll_event * event)
對內核eventpoll結構體操作,採用時間結構方式對描述符進行事件監控 用戶定義struct
epoll_event描述符事件結構信息,將事件信息可以拷貝到北河,添加到eventpoll的紅黑樹結點
epfd
:epoll 操作句柄
op
:對內核eventpoll進行的操作
EPOLL_CTL_ADD 向紅黑樹中添加描述符fd的監控事件信息event
EPOLL_CTL_DEL 從紅黑樹中移除fd的監控事件信息event
EPOLL_CTL_MOD 修改描述符fd在紅黑樹的監控事件信息event
fd
: 用戶需要監控的描述符
event
: 描述符對應的事件結構信息
struct epoll_event{
uint32_t events;//用戶對描述符進行監控的事件(EPOLLIN/EPOLLOUT 讀寫)
union{
int fd;
void* ptr;
}data
}
int epoll_wait (int epfd,struct epoll_event* events,int maxevents, int timeout)
epfd
:epoll 操作句柄
events
:epoll _event事件結構信息數組
maxevents
:epoll_events
epoll監控流程:
epoll 對描述符的事件監控是一個異步操作;epoll_wait 發起調用,讓操作系統對描述符進行相應事件監控,操作系統對每個要監控的描述符都定義了就緒事件回調函數,當描述符相應事件就緒的時候,觸發事件,調用回調函數(將描述符事件結構信息添加到eventpoll的雙向鏈表中)
epoll_wait沒有返回(異步阻塞操作),每隔一會查看eventpoll中雙向鏈表是否爲空(爲空:沒有描述符就緒,則等待一會,鏈表不爲空表示有描述符就緒;將這個描述符對應事件結構信息,拷貝到epoll_wait傳入的事件結構數組中後調用返回,事件回調的方式)
epoll_wait 將就緒的描述符對應事件結構信息拷貝到events結構數組;相當於直接告訴用戶哪個描述符就緒;
//epoll大致邏輯代碼
bool Init()
bool add(sock)
bool del(sock)
bool wait(list ,int timeout)
class Epoll
{
private:
int _epfd;
public:
Init()
{
_epfd=epoll_create(1);
}
Add(sock)
//int epoll_ctl (int epfd,int op,int fd,struct epoll_event * event)
{
fd=sock.getfd();
struct epoll_event ev;
ev.events=EPOLLIN;//讀
//=EPOLLIN|EPOLLET j就是邊緣觸發了
int ret=epoll_ctl(_epfd,EPOLL_CTL_ADD,fd,&ev);//ret<0==>return false
}
Del(sock)
{
//同add
//epoll_ctl(_epfd,EPOLL_CTL_DEL,fd,NULL)
}
Wait(vector<tcosocket> & list,int timeout_msec=3000)//3000毫秒,3秒
{
// int epoll_wait (int epfd,struct epoll_event* events,int maxevents, int timeout)
struct epoll_event evs[MAX_EVENTS];
int nfds=epoll_wait(_epfd,evs,MAX_EVENTS,timeout_msec);//nfds<0,=0 出錯和超時
for(0-nfds)
{
tcpsocket sock;
sock(evs[i].data.fd)
list.push_back(sock);//list裏面的都是就緒的
}
}
}
邊緣觸發,水平觸發
水平觸發
默認
可讀事件就緒:接受緩衝區中數據大小,大於低水位標記
可寫事件就緒:發送緩衝區中空閒空間大小,大於低水位標記
只要接受/發送緩衝區數據大於低水位標記
就會一直觸發事件
邊緣觸發
可讀事件就緒:接受緩衝區,只要新數據到來才觸發
可寫事件就緒 :發送緩衝區中,只有剩餘空間大小從0變成>0時纔會觸發一次
邊緣觸發中,只有新數據到來的時候,可讀事件纔會被觸發一次
需要用戶在這一次事件中把緩衝區中數據全部讀取完畢爲止(循環讀到不能讀爲止)
但是recv中緩衝區沒有數據時,recv會阻塞,爲了避免循環讀取數據導致進程阻塞,把描述符設置爲非阻塞
//fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK)
優缺點
事件結構方式對描述符進行監控,簡化了select集合操作的流程
epoll描述符監控無上限
每個epoll 監控的描述符事件信息,只需要向內核拷貝一次
epoll_wait使用異步阻塞操作在內核完成事件監控
- 事件監控是操作系統通過事件回調的方式將就緒描述符信息添加到就緒的事件信息
- epoll_wait 只是間隔一段時間去查看雙向鏈表是否爲空判斷描述符就緒,性能不隨描述符增加而降低
epoll 直接通過epoll_wait 傳入的事件結構數組 向用戶返回就緒的事件信息
直接告訴用戶哪些描述符就緒,不需要用戶進行空遍歷
場景
大量描述符監控,同一時間部分描述符活躍的場景 事件監控