首先看程式一,這個程式想要實作的功能是當使用者從控制台有任何輸入操作時,輸出”hello world!”。
l 程式一
點選(此處)折疊或打開
#include unistd.h>
#include iostream>
#include sys/epoll.h>
using namespace std;
int main(void)
{
int epfd,nfds;
struct epoll_event ev,events[5];//ev用于注冊事件,數組用于傳回要處理的事件
epfd=epoll_create(1);//隻需要監聽一個描述符——标準輸入
ev.data.fd=STDIN_FILENO;
ev.events=EPOLLIN|EPOLLET;//監聽讀狀态同時設定ET模式
epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&ev);//注冊epoll事件
for(;;)
{
nfds=epoll_wait(epfd,events,5,-1);
for(int i=0;infds;i++)
{
if(events[i].data.fd==STDIN_FILENO)
cout"hello world!"endl;
}
}
}
運作結果:

程式一中對标準輸入的監聽使用ET模式,結果實作了我們想要的功能。那麼實際原理是如何呢,我們将過程分析一下:
(1) 當使用者輸入一組字元,這組字元被送入buffer,字元停留在buffer中,又因為buffer由空變為不空,是以ET傳回讀就緒,輸出”hello world!”。
(2) 之後程式再次執行epoll_wait,此時雖然buffer中有内容可讀,但是根據我們上節的分析,ET并不傳回就緒,導緻epoll_wait阻塞。(底層原因是ET下就緒fd的epitem隻被放入rdlist一次)。
(3) 使用者再次輸入一組字元,導緻buffer中的内容增多,根據我們上節的分析這将導緻fd狀态的改變,是對應的epitem再次加入rdlist,進而使epoll_wait傳回讀就緒,再次輸出“hello world!”。
我們在看看LT的情況如何,将程式一以下修改:
ev.events=EPOLLIN;//預設使用LT模式
結果正如我們所料,程式出現死循環,因為使用者輸入任意資料後,資料被送入buffer且沒有被讀出,是以LT模式下每次epoll_wait都認為buffer可讀傳回讀就緒。導緻每次都會輸出”hello world!”。下面在看程式二。
l 程式二
char buf[256];
ev.events=EPOLLIN;//使用預設LT模式
if(events[i].data.fd==STDIN_FILENO)
{
read(STDIN_FILENO,buf,sizeof(buf));//将緩沖中的内容讀出
cout"hello world!"endl;
}
}
}
程式二依然使用LT模式,但是每次epoll_wait傳回讀就緒的時候我們都将buffer(緩沖)中的内容read出來,是以導緻buffer再次清空,下次調用epoll_wait就會阻塞。是以能夠實作我們所想要的功能——當使用者從控制台有任何輸入操作時,輸出”hello world!”。我們再來看看程式三。
l 程式三
ev.events=EPOLLIN|EPOLLET;//使用預設LT模式
{
ev.data.fd=STDIN_FILENO;
ev.events=EPOLLIN|EPOLLET;//使用預設LT模式
epoll_ctl(epfd,EPOLL_CTL_MOD,STDIN_FILENO,&ev);//重新MOD事件(ADD無效)
}
程式三依然使用ET,但是每次讀就緒後都主動的再次MOD IN事件,我們發現程式再次出現死循環,也就是每次傳回讀就緒。這就驗證了上一節讨論ET讀就緒的第三種情況。但是注意,如果我們将MOD改為ADD,将不會産生任何影響。别忘了每次ADD一個描述符都會在epitem組成的紅黑樹中添加一個項,我們之前已經ADD過一次,再次ADD将阻止添加,是以在次調用ADD IN事件不會有任何影響。