柚子快報(bào)激活碼778899分享:java NIO原理淺析(三)
柚子快報(bào)激活碼778899分享:java NIO原理淺析(三)
epoll
首先認(rèn)識(shí)一下epoll的幾個(gè)基礎(chǔ)函數(shù)
int s = socket(AF_INET, SOCK_STREAM, 0);
bind(s, ...);
listen(s, ...);
int epfd = epoll_create(...)
epoll_ctl(epfd, ...); //將所有需要監(jiān)聽的socket添加到epfd中
while(1) {
int n = epoll_wait(...);
for(接受到數(shù)據(jù)的socket) {
//處理數(shù)據(jù)
}
}
這段代碼涉及到幾個(gè)與epoll相關(guān)的函數(shù)方法:
epoll_create: 創(chuàng)建一個(gè)文件句柄 epoll_ctl: 向epoll對象添加/修改/刪除要管理的連接 epoll_wait: 等待其管理的連接上的IO事件
1、epoll_create
int epoll_create(int size);
功能描述:用于生成一個(gè)epoll專用的文件描述符
參數(shù)size:因?yàn)閑poll底層使用紅黑樹來保存文件描述符,紅黑樹的查找時(shí)間復(fù)雜度為O(logN),這個(gè)size并沒有必要限制大小。size可以設(shè)置為大于0的任何數(shù)。
返回值:如果成功,返回epoll專用的文件描述符,如果失敗,則返回-1。
2、epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event);
epoll中的事件注冊函數(shù),用于注冊要監(jiān)聽的事件類型
參數(shù):
epfd: epoll專用的文件描述符,epoll_create()的返回值 op: 表示動(dòng)作,用三個(gè)宏來表示:
EPOLL_CTL_ADD: 注冊新的fd到epfd中 EPOLL_CTL_MOD: 修改已經(jīng)注冊的fd的監(jiān)聽事件 EPOLL_CTL_DEL: 從epfd中刪除一個(gè)fd fd: 需要被監(jiān)聽的文件描述符 event: 告訴內(nèi)核需要監(jiān)聽什么事件 返回值:0表示成功,-1表示失敗
epoll_event結(jié)構(gòu)體如文檔描述:
#include
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
};
typedef union epoll_data epoll_data_t;
events可以是以下幾個(gè)宏的集合:
EPOLLIN: 表示對應(yīng)的文件描述符可以讀 EPOLLOUT:表示對應(yīng)的文件描述符可以寫 EPOLLPRI:表示對應(yīng)的文件描述符有緊急的數(shù)據(jù)可以讀 EPOLLERR: 表示對應(yīng)的文件描述符發(fā)生錯(cuò)誤 EPOLLHUP:表示對應(yīng)的文件描述符被掛斷 EPOLLET: 將EPOLL設(shè)為邊緣觸發(fā)模式,不設(shè)置為水平觸發(fā)模式 EPOLLONESHOT: 只監(jiān)聽一次事件,當(dāng)監(jiān)聽完這次事件之后,如果還需要監(jiān)聽這次事件,則需要把socket重新加入到EPOLL中。
3、epoll_wait
int epoll_wait(int epfd, struct epoll_event * event,
int maxevents, int timeout);
功能:本函數(shù)用于監(jiān)控事件的發(fā)生,當(dāng)epoll監(jiān)控的事件中存在已經(jīng)發(fā)送的事件,那么就可以將此事件收集起來。
參數(shù):
epfd:epoll自身產(chǎn)生的文件描述符,調(diào)用epoll_create的返回值 event:事先必須將空間分配好的結(jié)構(gòu)體數(shù)組,epoll將即將發(fā)生的事件賦值到event數(shù)組中。 maxevents:告訴內(nèi)核共有多少個(gè)event數(shù)組的大小 timeout:超時(shí)時(shí)間,單位為毫秒,設(shè)為-1時(shí),該函數(shù)狀態(tài)為阻塞。 返回值:如果為-1,表示失?。蝗绻麨?,表示已經(jīng)超時(shí);如果成功,返回需要處理的事件數(shù)目。
epoll整體流程圖如下所示:
我們在之前文章中討論過,select和poll存在三個(gè)缺陷,epoll方式很好的解決了這些問題:
epoll在內(nèi)核中使用紅黑樹這個(gè)數(shù)據(jù)結(jié)構(gòu)來保存文件描述符,紅黑樹這個(gè)數(shù)據(jù)結(jié)構(gòu)的增刪改的時(shí)間復(fù)雜度為O(logn),select/poll每次監(jiān)聽套接字,都需要將套接字列表整個(gè)復(fù)制到內(nèi)核態(tài),而epoll使用epoll_ctl,每次將一個(gè)監(jiān)聽套接字復(fù)制到內(nèi)核態(tài),這明顯減少了用戶空間到內(nèi)核態(tài)的大量數(shù)據(jù)拷貝和內(nèi)存分配。
epoll使用異步回調(diào)的機(jī)制,內(nèi)核態(tài)維護(hù)一個(gè)就緒隊(duì)列,如果某個(gè)socket已經(jīng)準(zhǔn)備好,那么就會(huì)通過回調(diào)函數(shù)觸發(fā)內(nèi)核將socket事件加入到就緒事件列表。用戶調(diào)用epoll_wait函數(shù),就會(huì)返回有事件發(fā)生的文件描述符的個(gè)數(shù)。這個(gè)過程中,沒有像select/poll一樣對socket集合進(jìn)行了O(N)時(shí)間復(fù)雜度的輪詢。提高了檢測的效率。
邊緣觸發(fā)和水平觸發(fā)
epoll支持兩種事件觸發(fā)模式:
邊緣觸發(fā):當(dāng)被監(jiān)控的Socket描述符有可讀事件發(fā)生時(shí),服務(wù)器端在調(diào)用epoll_wait這個(gè)函數(shù)時(shí),只會(huì)蘇醒一次,然后從內(nèi)核緩沖區(qū)中讀取所有的數(shù)據(jù) 水平觸發(fā):當(dāng)被監(jiān)控的Socket描述符有可讀事件發(fā)生時(shí),服務(wù)器端在調(diào)用epoll_wait這個(gè)函數(shù)時(shí),會(huì)不斷的蘇醒,直到內(nèi)核中沒有數(shù)據(jù)可讀才停止蘇醒。
柚子快報(bào)激活碼778899分享:java NIO原理淺析(三)
參考鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。