1. epoll概念
在Linux的Man文檔中狠角,我們可以看到如下定義
Epoll - I/O event notification facility
epoll是一種I/O事件通知機(jī)制
I/O事件
I/O
輸入輸出(input/output),輸入輸出的對(duì)象可以是 文件(file), 網(wǎng)絡(luò)(socket), 進(jìn)程之間的管道(pipe), 在linux系統(tǒng)中病涨,都用文件描述符(fd)來表示-
事件
- 可讀事件蚁滋, 當(dāng)文件描述符關(guān)聯(lián)的內(nèi)核讀緩沖區(qū)可讀,則觸發(fā)可讀事件
什么是可讀呢? 就是內(nèi)核緩沖區(qū)非空窿锉,有數(shù)據(jù)可以讀取 - 可寫事件, 當(dāng)文件描述符關(guān)聯(lián)的內(nèi)核寫緩沖區(qū)可寫,則觸發(fā)可寫事件
什么是可寫呢膝舅?就是內(nèi)核緩沖區(qū)不滿嗡载,有空閑空間可以寫入
- 可讀事件蚁滋, 當(dāng)文件描述符關(guān)聯(lián)的內(nèi)核讀緩沖區(qū)可讀,則觸發(fā)可讀事件
通知機(jī)制
- 通知機(jī)制,就是當(dāng)事件發(fā)生的時(shí)候仍稀,去通知他
- 通知機(jī)制的反面洼滚,就是輪詢機(jī)制
以上兩點(diǎn)結(jié)合起來理解
epoll是一種當(dāng)文件描述符的內(nèi)核緩沖區(qū)非空的時(shí)候,發(fā)出可讀信號(hào)進(jìn)行通知技潘,當(dāng)寫緩沖區(qū)不滿的時(shí)候遥巴,發(fā)出可寫信號(hào)通知的機(jī)制
2. 水平觸發(fā)與邊緣觸發(fā)
水平觸發(fā)(level-trggered)
- 只要文件描述符關(guān)聯(lián)的讀內(nèi)核緩沖區(qū)非空,有數(shù)據(jù)可以讀取享幽,就一直發(fā)出可讀信號(hào)進(jìn)行通知铲掐,
- 當(dāng)文件描述符關(guān)聯(lián)的內(nèi)核寫緩沖區(qū)不滿,有空間可以寫入值桩,就一直發(fā)出可寫信號(hào)進(jìn)行通知
邊緣觸發(fā)(edge-triggered)
- 當(dāng)文件描述符關(guān)聯(lián)的讀內(nèi)核緩沖區(qū)由空轉(zhuǎn)化為非空的時(shí)候摆霉,則發(fā)出可讀信號(hào)進(jìn)行通知,
- 當(dāng)文件描述符關(guān)聯(lián)的內(nèi)核寫緩沖區(qū)由滿轉(zhuǎn)化為不滿的時(shí)候奔坟,則發(fā)出可寫信號(hào)進(jìn)行通知
兩者的區(qū)別在哪里呢携栋?水平觸發(fā)是只要讀緩沖區(qū)有數(shù)據(jù),就會(huì)一直觸發(fā)可讀信號(hào)咳秉,而邊緣觸發(fā)僅僅在空變?yōu)榉强盏臅r(shí)候通知一次婉支,舉個(gè)例子:
- 讀緩沖區(qū)剛開始是空的
- 讀緩沖區(qū)寫入2KB數(shù)據(jù)
- 水平觸發(fā)和邊緣觸發(fā)模式此時(shí)都會(huì)發(fā)出可讀信號(hào)
- 收到信號(hào)通知后,讀取了1kb的數(shù)據(jù)澜建,讀緩沖區(qū)還剩余1KB數(shù)據(jù)
- 水平觸發(fā)會(huì)再次進(jìn)行通知向挖,而邊緣觸發(fā)不會(huì)再進(jìn)行通知
所以邊緣觸發(fā)需要一次性的把緩沖區(qū)的數(shù)據(jù)讀完為止,也就是一直讀炕舵,直到讀到EGAIN為止户誓,EGAIN說明緩沖區(qū)已經(jīng)空了,因?yàn)檫@一點(diǎn)幕侠,邊緣觸發(fā)需要設(shè)置文件句柄為非阻塞
//水平觸發(fā)
ret = read(fd, buf, sizeof(buf));
//邊緣觸發(fā)
while(true) {
ret = read(fd, buf, sizeof(buf);
if (ret == EAGAIN) break;
}
3. epoll接口介紹
-
epoll_create
- 創(chuàng)建epoll實(shí)例帝美,會(huì)創(chuàng)建所需要的紅黑樹,以及就緒鏈表,以及代表epoll實(shí)例的文件句柄
int epoll_create(int size);
Man文檔中說明了在老的內(nèi)核版本中悼潭,入?yún)ize用來指出創(chuàng)建的內(nèi)部數(shù)據(jù)結(jié)構(gòu)的大小庇忌,目前已經(jīng)可以動(dòng)態(tài)調(diào)整,但是為了兼容老的版本舰褪,所以仍然保留皆疹,這個(gè)size其實(shí)意義已經(jīng)不大
- 創(chuàng)建epoll實(shí)例帝美,會(huì)創(chuàng)建所需要的紅黑樹,以及就緒鏈表,以及代表epoll實(shí)例的文件句柄
-
epoll_ctl
-
添加,修改占拍,或者刪除 注冊(cè)到epoll實(shí)例中的文件描述符上的監(jiān)控事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
對(duì)于添加到epfd的文件描述符fd, 添加或者刪除或者修改略就, 對(duì)應(yīng)的event- epfd: 通過epoll_create創(chuàng)建的文件描述符
- op:操作類型
EPOLL_CTL_ADD, 為相應(yīng)fd添加事件
EPOLL_CTL_MOD, 修改fd的事件
EPOLL_CTL_DEL,刪除fd上的某些事件 - fd: 操作的目標(biāo)文件描述符
- event: 要操作的事件
typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; events可以是一組bit的組合 EPOLLIN:可讀 EPOLLOUT: 可寫 EPOLLET: 邊緣觸發(fā)晃酒,默認(rèn)是水平觸發(fā)
-
-
epoll_wait
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
等待注冊(cè)的事件發(fā)生表牢,返回事件的數(shù)目,并將觸發(fā)的事件寫入events數(shù)組中- epfd: epoll實(shí)例文件描述符
- events: 數(shù)組出參贝次,用來記錄被觸發(fā)的events崔兴,其大小應(yīng)該和maxevents一致
- maxevents: 返回的events的最大個(gè)數(shù),如果最大個(gè)數(shù)大于實(shí)際觸發(fā)的個(gè)數(shù)蛔翅,則下次epoll_wait的時(shí)候仍然可以返回
- timeout: 等待事件敲茄,毫秒為單位 -1:無限等待 0:立即返回
了解以上這些,面試是足夠了山析,但是對(duì)于工程中的實(shí)際使用堰燎,仍然不足,后續(xù)會(huì)以epoll在redis中的使用為例笋轨,為大家進(jìn)行剖析