徹底學會使用epoll(四)——ET的寫操作例項分析

gettogetto發表於2017-03-26

首先,看程式四的例子。

程式四


點選(此處)摺疊或開啟

  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. };

這個程式的功能是隻要標準輸出寫就緒,就輸出“hello world”。

執行結果:

我們發現這將是一個死迴圈。下面具體分析一下這個程式的執行過程:

(1) 首先初始buffer為空,buffer中有空間可寫,這時無論是ET還是LT都會將對應的epitem加入rdlist(對應第一節圖中的紅線),導致epoll_wait就返回寫就緒。

(2) 程式想標準輸出輸出hello world和換行符,因為標準輸出為控制檯的時候緩衝是“行緩衝”,所以換行符導致buffer中的內容清空,這就對應第二節中ET模式下寫就緒的第二種情況——當有舊資料被髮送走時,即buffer中待寫的內容變少得時候會觸發fd狀態的改變。所以下次epoll_wait會返回寫就緒。之後重複這個過程一直迴圈下去。

我們再看程式五。

程式五

相對程式四這裡僅僅去掉了輸出的換行操作。即:

 cout<<"hello world!";

執行結果如下:

我們看到程式成掛起狀態。因為第一次epoll_wait返回寫就緒後,程式向標準輸出的buffer中寫入“hello world!”,但是因為沒有輸出換行,所以buffer中的內容一直存在,下次epoll_wait的時候,雖然有寫空間但是ET模式下不再返回寫就緒。回憶第一節關於ET的實現,這種情況原因就是第一次buffer為空,導致epitem加入rdlist,返回一次就緒後移除此epitem,之後雖然buffer仍然可寫,但是由於對應epitem已經不再rdlist中,就不會對其就緒fdevents的在檢測了。

程式六


點選(此處)摺疊或開啟

  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返回寫就緒。

程式

點選(此處)摺疊或開啟

  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事件。所以相當於每次重新進行第一節中紅線描述的途徑返回就緒,導致程式迴圈輸出。

相關文章