EPOLLET vs. EPOLLLT

人果然是很懶的,一轉眼,就有好長一段時間沒有發文了。這倒不是說沒有值得紀錄的事情,而是「懶」。其實本來這裡也是給自己看的而已,但還是希望能養成良好的紀錄習慣囉。

上一篇文章是關於 epoll 的範例。 epoll 在運作上有兩種模式,EPOLLET(Edge-Triggered)和 EPOLLLT(Level-Triggered)。其實我完全不知道這個名字是怎麼來的。但姑且不管名字的由來,在使用上還是要知道這兩者的差異。

先從簡單的說起,EPOLLLT 在運作上可以視做是一個比較快的POLL(這可是官方文件說的)。只要 FD 裏面還有資料可以讀取,早晚都會被 polling 到。

上面好像是廢話,那 EPOLLET 有什麼不一樣呢?當有 FD 有資料可以被存取的時候,EPOLLET 運作模式會發一個 event 出來,提醒使用者去存取資料。注意,只會發一次唷。意思是,如果你的資料沒有存取完畢,它也不會再發一次事件來通知。我們就參考下面來自 man 的情境會更容易了解:

1. The file descriptor that represents the read side of a pipe (rfd) is registered on the epoll instance.
2. A pipe writer writes 2 kB of data on the write side of the pipe.
3. A call to epoll_wait(2) is done that will return rfd as a ready file descriptor.

4. The pipe reader reads 1 kB of data from rfd.
5. A call to epoll_wait(2) is done.


重點在步驟 4,就算你已經讀了 1kB,意味著還有 1kB 的資料需要讀取,epoll_wait 並不會再次送出通知而會停在那邊等待。這是因為 EPOLLET 發出事件通知的條件是 FD 的狀態發生改變。如果沒有改變,就算仍有資料要讀取,也不會有任何通知送出來。

那要如何避免這個狀況?第一,使用 EPOLLLT,這是預設的作法,但老實說,因為底層還是使用 polling ,所以效能比不上 EPOLLET。第二,透過 EAGAIN。作法很簡單,可以參考下面的程式:


while(1)
{
    num = recvfrom( fd, buf, BUFFER_SIZE, 0 (struct sockaddr *)&srcAddr, $srcInfoLen );
    
    if( num < 0 )
    {
        break;
    }
}

if( errno != EAGAIN )
{
    return -1;
} 
 
 
在這裡 EAGAIN 代表的意思是這個 FD目前沒有資料可以讀取了,而且 FD 還在正常運作中的意思。

不曉得下一篇會多久以後才寫 ...

修正:

之前請同事用這個方法來撰寫,實驗結果卻不是這樣,因為還是會停在 recv 無窮回圈裏面,理由是,這個 socket 並沒有被設成 Non-Blocking!!要將 Socket 設成 Non-blocking 可以用下面的函式:


static int
make_socket_non_blocking (int sfd)
{
  int flags, s;

  flags = fcntl (sfd, F_GETFL, 0);
  if (flags == -1)
    {
      perror ("fcntl");
      return -1;
    }

  flags |= O_NONBLOCK;
  s = fcntl (sfd, F_SETFL, flags);
  if (s == -1)
    {
      perror ("fcntl");
      return -1;
    }

  return 0;
}

留言

這個網誌中的熱門文章

我弟家的新居感恩禮拜分享:善頌善禱

Openssl 範例程式:建立SSL連線

如何利用 Wireshark 來監聽 IEEE 802.11 的管理封包