首先看程序一,這個程序想要實現的功能是當用戶從控制檯有任何輸入操做時,輸出」hello world!」。ios
l 程序一數組
1 #include <unistd.h> 2 #include <iostream> 3 #include <sys/epoll.h> 4 using namespace std; 5 int main(void) 6 { 7 int epfd,nfds; 8 struct epoll_event ev,events[5];//ev用於註冊事件,數組用於返回要處理的事件 9 epfd=epoll_create(1);//只須要監聽一個描述符——標準輸入 10 ev.data.fd=STDIN_FILENO; 11 ev.events=EPOLLIN|EPOLLET;//監聽讀狀態同時設置ET模式 12 epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&ev);//註冊epoll事件 13 for(;;) 14 { 15 nfds=epoll_wait(epfd,events,5,-1); 16 for(int i=0;i<nfds;i++) 17 { 18 if(events[i].data.fd==STDIN_FILENO) 19 cout<<"hello world!"<<endl; 20 } 21 } 22 }
(1) 當用戶輸入一組字符,這組字符被送入buffer,字符停留在buffer中,又由於buffer由空變爲不空,因此ET返回讀就緒,輸出」hello world!」。spa
(2) 以後程序再次執行epoll_wait,此時雖然buffer中有內容可讀,可是根據咱們上節的分析,ET並不返回就緒,致使epoll_wait阻塞。(底層緣由是ET下就緒fd的epitem只被放入rdlist一次)。code
(3) 用戶再次輸入一組字符,致使buffer中的內容增多,根據咱們上節的分析這將致使fd狀態的改變,是對應的epitem再次加入rdlist,從而使epoll_wait返回讀就緒,再次輸出「hello world!」。blog
l 程序二事件
1 #include <unistd.h> 2 #include <iostream> 3 #include <sys/epoll.h> 4 using namespace std; 5 int main(void) 6 { 7 int epfd,nfds; 8 char buf[256]; 9 struct epoll_event ev,events[5];//ev用於註冊事件,數組用於返回要處理的事件 10 epfd=epoll_create(1);//只須要監聽一個描述符——標準輸入 11 ev.data.fd=STDIN_FILENO; 12 ev.events=EPOLLIN;//使用默認LT模式 13 epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&ev);//註冊epoll事件 14 for(;;) 15 { 16 nfds=epoll_wait(epfd,events,5,-1); 17 for(int i=0;i<nfds;i++) 18 { 19 if(events[i].data.fd==STDIN_FILENO) 20 { 21 read(STDIN_FILENO,buf,sizeof(buf));//將緩衝中的內容讀出 22 cout<<"hello world!"<<endl; 23 } 24 } 25 } 26 }
程序二依然使用LT模式,可是每次epoll_wait返回讀就緒的時候咱們都將buffer(緩衝)中的內容read出來,因此致使buffer再次清空,下次調用epoll_wait就會阻塞。因此可以實現咱們所想要的功能——當用戶從控制檯有任何輸入操做時,輸出」hello world!」。it
l 程序三io
1 int main(void) 2 { 3 int epfd,nfds; 4 struct epoll_event ev,events[5];//ev用於註冊事件,數組用於返回要處理的事件 5 epfd=epoll_create(1);//只須要監聽一個描述符——標準輸入 6 ev.data.fd=STDIN_FILENO; 7 ev.events=EPOLLIN|EPOLLET;//使用默認LT模式 8 epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&ev);//註冊epoll事件 9 for(;;) 10 { 11 nfds=epoll_wait(epfd,events,5,-1); 12 for(int i=0;i<nfds;i++) 13 { 14 if(events[i].data.fd==STDIN_FILENO) 15 { 16 cout<<"hello world!"<<endl; 17 ev.data.fd=STDIN_FILENO; 18 ev.events=EPOLLIN|EPOLLET;//使用默認LT模式 19 epoll_ctl(epfd,EPOLL_CTL_MOD,STDIN_FILENO,&ev);//從新MOD事件(ADD無效) 20 } 21 } 22 } 23 }
程序三依然使用ET,可是每次讀就緒後都主動的再次MOD IN事件,咱們發現程序再次出現死循環,也就是每次返回讀就緒。這就驗證了上一節討論ET讀就緒的第三種狀況。可是注意,若是咱們將MOD改成ADD,將不會產生任何影響。別忘了每次ADD一個描述符都會在epitem組成的紅黑樹中添加一個項,咱們以前已經ADD過一次,再次ADD將阻止添加,因此在次調用ADD IN事件不會有任何影響。event
l 程序四class
1 #include <unistd.h> 2 #include <iostream> 3 #include <sys/epoll.h> 4 using namespace std; 5 int main(void) 6 { 7 int epfd,nfds; 8 struct epoll_event ev,events[5];//ev用於註冊事件,數組用於返回要處理的事件 9 epfd=epoll_create(1);//只須要監聽一個描述符——標準輸出 10 ev.data.fd=STDOUT_FILENO; 11 ev.events=EPOLLOUT|EPOLLET;//監聽讀狀態同時設置ET模式 12 epoll_ctl(epfd,EPOLL_CTL_ADD,STDOUT_FILENO,&ev);//註冊epoll事件 13 for(;;) 14 { 15 nfds=epoll_wait(epfd,events,5,-1); 16 for(int i=0;i<nfds;i++) 17 { 18 if(events[i].data.fd==STDOUT_FILENO) 19 cout<<"hello world!"<<endl; 20 } 21 } 22 }
咱們發現這將是一個死循環。下面具體分析一下這個程序的執行過程:
(1) 首先初始buffer爲空,buffer中有空間可寫,這時不管是ET仍是LT都會將對應的epitem加入rdlist(對應第一節圖中的紅線),致使epoll_wait就返回寫就緒。
(2) 程序想標準輸出輸出」hello world!」和換行符,由於標準輸出爲控制檯的時候緩衝是「行緩衝」,因此換行符致使buffer中的內容清空,這就對應第二節中ET模式下寫就緒的第二種狀況——當有舊數據被髮送走時,即buffer中待寫的內容變少得時候會觸發fd狀態的改變。因此下次epoll_wait會返回寫就緒。以後重複這個過程一直循環下去。
l 程序五
相對程序四這裏僅僅去掉了輸出的換行操做。即:
cout<<"hello world!";
咱們看到程序成掛起狀態。由於第一次epoll_wait返回寫就緒後,程序向標準輸出的buffer中寫入「hello world!」,可是由於沒有輸出換行,因此buffer中的內容一直存在,下次epoll_wait的時候,雖然有寫空間可是ET模式下再也不返回寫就緒。回憶第一節關於ET的實現,這種狀況緣由就是第一次buffer爲空,致使epitem加入rdlist,返回一次就緒後移除此epitem,以後雖然buffer仍然可寫,可是因爲對應epitem已經再也不rdlist中,就不會對其就緒fd的events的在檢測了。
l 程序六
1 int main(void) 2 { 3 int epfd,nfds; 4 struct epoll_event ev,events[5];//ev用於註冊事件,數組用於返回要處理的事件 5 epfd=epoll_create(1);//只須要監聽一個描述符——標準輸出 6 ev.data.fd=STDOUT_FILENO; 7 ev.events=EPOLLOUT;//使用默認LT模式 8 epoll_ctl(epfd,EPOLL_CTL_ADD,STDOUT_FILENO,&ev);//註冊epoll事件 9 for(;;) 10 { 11 nfds=epoll_wait(epfd,events,5,-1); 12 for(int i=0;i<nfds;i++) 13 { 14 if(events[i].data.fd==STDOUT_FILENO) 15 cout<<"hello world!"; 16 } 17 } 18 };
程序六相對程序五僅僅是修改ET模式爲默認的LT模式,咱們發現程序再次死循環。這時候緣由已經很清楚了,由於當向buffer寫入」hello world!」後,雖然buffer沒有輸出清空,可是LT模式下只有buffer有寫空間就返回寫就緒,因此會一直輸出」hello world!」,當buffer滿的時候,buffer會自動刷清輸出,一樣會形成epoll_wait返回寫就緒。
l 程序七
1 int main(void) 2 { 3 int epfd,nfds; 4 struct epoll_event ev,events[5];//ev用於註冊事件,數組用於返回要處理的事件 5 epfd=epoll_create(1);//只須要監聽一個描述符——標準輸出 6 ev.data.fd=STDOUT_FILENO; 7 ev.events=EPOLLOUT|EPOLLET;//監聽讀狀態同時設置ET模式 8 epoll_ctl(epfd,EPOLL_CTL_ADD,STDOUT_FILENO,&ev);//註冊epoll事件 9 for(;;) 10 { 11 nfds=epoll_wait(epfd,events,5,-1); 12 for(int i=0;i<nfds;i++) 13 { 14 if(events[i].data.fd==STDOUT_FILENO) 15 cout<<"hello world!"; 16 ev.data.fd=STDOUT_FILENO; 17 ev.events=EPOLLOUT|EPOLLET; 18 epoll_ctl(epfd,EPOLL_CTL_MOD,STDOUT_FILENO,&ev); //從新MOD事件(ADD無效) 19 } 20 } 21 };
程序七相對於程序五在每次向標準輸出的buffer輸出」hello world!」後,從新MOD OUT事件。因此至關於每次從新進行第一節中紅線描述的途徑返回就緒,致使程序循環輸出。