目錄
- 基本使用方法
- step1:創建epollfd
- step2:將fd綁定到epollfd
- step3:調用epoll_wait檢測事件
- epoll_wait與poll、select區別所在
- 水平觸發與邊緣觸發
基本使用方法
step1:創建epollfd
- 創建一個epollfd,若epoll_create調用成功,則返回一個非負值的epollfd,否則返回-1
/* Creates an epoll instance. Returns an fd for the new instance.The "size" parameter is a hint specifying the number of filedescriptors to be associated with the new instance. The fdreturned by epoll_create() should be closed with close(). */
extern int epoll_create (int __size) __THROW;
step2:將fd綁定到epollfd
有了epollfd之后,我們有三種需求:
1、將需要檢測事件的其他fd綁定到這個epollfd上
2、修改一個已經綁定到epollfd的fd的事件類型
3、在不需要的時候將fd從epollfd上解綁
都需要依托函數epoll_ctl
完成:
/* Manipulate an epoll instance "epfd". Returns 0 in case of success,-1 in case of error ( the "errno" variable will contain thespecific error code ) The "op" parameter is one of the EPOLL_CTL_*constants defined above. The "fd" parameter is the target of theoperation. The "event" parameter describes which events the calleris interested in and any associated user data. */
extern int epoll_ctl (int __epfd, int __op, int __fd,struct epoll_event *__event) __THROW;
__epfd:即epollfd
__op:操作類型,有三種EPOLL_CTL_ADD
、EPOLL_CTL_MOD
、EPOLL_CTL_DEL
。分別對應著在epollfd上添加、修改、移除fd,當為EPOLL_CTL_DEL
時,__event
參數忽略,置NULL
__fd:需要備操作的fd
__event:一個epoll_event
結構體的地址
具體結構如下:
// 在64位操作系統下,大小為8 byte
typedef union epoll_data
{void *ptr;int fd;uint32_t u32;uint64_t u64;
} epoll_data_t;struct epoll_event
{uint32_t events; /* 需要檢測的fd事件標志 */epoll_data_t data; /* 用戶自定義的數據*/
} __EPOLL_PACKED;
返回值:
調用成功,返回0;
調用失敗,返回-1,通過errno錯誤碼可以獲取具體的錯誤原因。
step3:調用epoll_wait檢測事件
/* Wait for events on an epoll instance "epfd". Returns the number oftriggered events returned in "events" buffer. Or -1 in case oferror with the "errno" variable set to the specific error code. The"events" parameter is a buffer that will contain triggeredevents. The "maxevents" is the maximum number of events to bereturned ( usually size of "events" ). The "timeout" parameterspecifies the maximum wait time in milliseconds (-1 == infinite).This function is a cancellation point and therefore not marked with__THROW. */
extern int epoll_wait (int __epfd, struct epoll_event *__events,int __maxevents, int __timeout);
__events:一個epoll_event結構數組的首地址,是一個輸出參數,在函數調用成功后,在events中存放的是與就緒事件相關的epoll_event
結構體數組。
__maxevents:數組元素個數
__timeout:超時時間,單位為ms
返回值:調用成功返回有事件的fd數量,若返回0,表示超時。若返回-1,表示調用失敗。
使用示例如下:
while (true) {epoll_event epollEvents[1024];int n = epoll_wait(epollfd, epollEvents, 1024, 1000);if (n < 0) {if (errno == EINTR) {// 被信號中斷 重試continue;} else {// 出錯 退出break;}} else if (n == 0) {// 超時,繼續重試continue;} else {// 處理事件for (size_t i = 0; i < n; i++) {if (epollEvents[i].events & EPOLLIN) {// 處理可讀事件} else if (epollEvents[i].events & EPOLLOUT) {// 處理可寫事件} else if (epollEvents[i].events & EPOLLERR) {// 處理出錯事件}}}}
epoll_wait與poll、select區別所在
在第二講中演示了select的基本使用方式:C++網絡編程快速入門(二):Linux下使用select演示簡單服務端程序
select和epoll底層機制一樣,所以這里只看select。
可以發現調用完select之后,需要在原來的clientfds數組中遍歷,然后加條件判斷是否是有事件的。
而epoll_wait調用完之后是直接返回一個篩選過后的有事件的events數組。
所以:
在fd數量比較多但是某段時間內的就緒事件fd數量較少時,epoll_wait函數更加高效。
也就是epoll模型更適合用在socket連接數量較大而活躍的連接較少的情景下
水平觸發與邊緣觸發
epoll具有兩種模式:邊緣觸發模式(Edge Trigger,ET)和水平觸發模式(Level Trigger,LT)。
區別在于:
1、LT:一個事件只要有,就會一直觸發
2、ET:一個事件從無到有,才會觸發
以socket讀事件為例:
水平模式下,只要socket上有未讀完的數據,就會一直產生EPOLLIN事件。
邊緣模式下,socket上每新來一次數據就會觸發一次,如果上一次觸發后未將socket上的數據讀完,也不會再觸發,除非再新來一次數據。
以socket寫事件為例:
水平模式下,只要socket上TCP窗口一直不飽和,就會一直觸發EPOLLOUT事件。
邊緣模式下,只有TCP窗口由不飽和變成飽和 或者 再一次變成不飽和,才會觸發EPOLLOUT事件。
這對于編程的啟示是:
1、對于非阻塞socket,如果epoll使用邊緣模式檢測事件可讀,那么一旦觸發,一定要一次性把socket上數據收取干凈,即循環調用recv函數直到recv出錯
bool recvEtMode()
{// 每次只收取256個字節char buf[256];while (true) {int nRecv = ::recv(clientfd, buf, 256, 0);if (nRecv == -1) {if (errno == EWOULDBLOCK) {return true;} else if (errno == EINTR) {continue;} else {return false;}}else if (nRecv == 0) {// 對端關閉了socketreturn false;} else {inputBuffer.add(buf, (size_t)nRecv);}}return true;
}
2、如果是水平模式,可以根據業務一次性收取固定字節數
下面總結一下兩者在編碼上需要注意的地方:
1、LT模式下,讀事件觸發后可以按需收取想要的字節數,不用把本次數據收取干凈;
ET模式下,讀事件必須把數據收取干凈,因為我們不一定再有機會收取數據了。
2、LT模式下,不需要寫事件時一定要及時移除,避免不必要地觸發且浪費CPU資源。
ET模式下,寫事件觸發后,如果還需要下一次的寫事件觸發來驅動任務(例如發送上次剩余的數據),則我們需要繼續注冊一次檢測可寫事件
3、LT會導致多次觸發,ET優點是觸發次數少