1. epoll概念
在Linux的Man文檔中,我們可以看到如下定義
Epoll - I/O event notification facility
epoll是一種I/O事件通知機(jī)制
I/O事件
I/O
輸入輸出(input/output),輸入輸出的對象可以是 文件(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ā)生的時候,去通知他
- 通知機(jī)制的反面,就是輪詢機(jī)制
以上兩點結(jié)合起來理解
epoll是一種當(dāng)文件描述符的內(nèi)核緩沖區(qū)非空的時候,發(fā)出可讀信號進(jìn)行通知,當(dāng)寫緩沖區(qū)不滿的時候,發(fā)出可寫信號通知的機(jī)制
2. 水平觸發(fā)與邊緣觸發(fā)
水平觸發(fā)(level-trggered)
- 只要文件描述符關(guān)聯(lián)的讀內(nèi)核緩沖區(qū)非空,有數(shù)據(jù)可以讀取,就一直發(fā)出可讀信號進(jìn)行通知,
- 當(dāng)文件描述符關(guān)聯(lián)的內(nèi)核寫緩沖區(qū)不滿,有空間可以寫入,就一直發(fā)出可寫信號進(jìn)行通知
邊緣觸發(fā)(edge-triggered)
- 當(dāng)文件描述符關(guān)聯(lián)的讀內(nèi)核緩沖區(qū)由空轉(zhuǎn)化為非空的時候,則發(fā)出可讀信號進(jìn)行通知,
- 當(dāng)文件描述符關(guān)聯(lián)的內(nèi)核寫緩沖區(qū)由滿轉(zhuǎn)化為不滿的時候,則發(fā)出可寫信號進(jìn)行通知
兩者的區(qū)別在哪里呢?水平觸發(fā)是只要讀緩沖區(qū)有數(shù)據(jù),就會一直觸發(fā)可讀信號,而邊緣觸發(fā)僅僅在空變?yōu)榉强盏臅r候通知一次,舉個例子:
- 讀緩沖區(qū)剛開始是空的
- 讀緩沖區(qū)寫入2KB數(shù)據(jù)
- 水平觸發(fā)和邊緣觸發(fā)模式此時都會發(fā)出可讀信號
- 收到信號通知后,讀取了1kb的數(shù)據(jù),讀緩沖區(qū)還剩余1KB數(shù)據(jù)
- 水平觸發(fā)會再次進(jìn)行通知,而邊緣觸發(fā)不會再進(jìn)行通知
所以邊緣觸發(fā)需要一次性的把緩沖區(qū)的數(shù)據(jù)讀完為止,也就是一直讀,直到讀到EGAIN為止,EGAIN說明緩沖區(qū)已經(jīng)空了,因為這一點,邊緣觸發(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實例,會創(chuàng)建所需要的紅黑樹,以及就緒鏈表,以及代表epoll實例的文件句柄
int epoll_create(int size);
Man文檔中說明了在老的內(nèi)核版本中,入?yún)ize用來指出創(chuàng)建的內(nèi)部數(shù)據(jù)結(jié)構(gòu)的大小,目前已經(jīng)可以動態(tài)調(diào)整,但是為了兼容老的版本,所以仍然保留,這個size其實意義已經(jīng)不大
- 創(chuàng)建epoll實例,會創(chuàng)建所需要的紅黑樹,以及就緒鏈表,以及代表epoll實例的文件句柄
-
epoll_ctl
-
添加,修改,或者刪除 注冊到epoll實例中的文件描述符上的監(jiān)控事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
對于添加到epfd的文件描述符fd, 添加或者刪除或者修改, 對應(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)
等待注冊的事件發(fā)生,返回事件的數(shù)目,并將觸發(fā)的事件寫入events數(shù)組中- epfd: epoll實例文件描述符
- events: 數(shù)組出參,用來記錄被觸發(fā)的events,其大小應(yīng)該和maxevents一致
- maxevents: 返回的events的最大個數(shù),如果最大個數(shù)大于實際觸發(fā)的個數(shù),則下次epoll_wait的時候仍然可以返回
- timeout: 等待事件,毫秒為單位 -1:無限等待 0:立即返回
了解以上這些,面試是足夠了,但是對于工程中的實際使用,仍然不足,后續(xù)會以epoll在redis中的使用為例,為大家進(jìn)行剖析