EPOLL講解、注意點和使用建議

介紹:

  1. 網(wǎng)上已經(jīng)有很多關于Epoll講解的教程和文章了,這里我介紹下我感覺不錯的一篇:
    https://blog.csdn.net/libaineu2004/article/details/70197825
  2. 本篇文章我主要想講解一些網(wǎng)絡上比較難查找到的一些問題,并給出自己的理解和補充,另外文后會附上個人認為比較應該注意的注意點和使用習慣。

疑問相關:

一、關于EOPLLIN和EPOLLOUT設置問題:

相信很多朋友在最初接觸學習的時候,發(fā)現(xiàn)網(wǎng)上的很多示例代碼在處理事件時候是類似如下處理:


1.jpg

這時候會產(chǎn)生三個疑問:

  1. 為什么不設置成:EPOLLIN|EPOLLOUT呢???這樣豈是可以同時監(jiān)聽 “數(shù)據(jù)變?yōu)榭勺x” 和 “數(shù)據(jù)變?yōu)榭蓪憽?了?
  2. 這么操作當從EPOLLIN換成EPOLLOUT時豈不是檢測不到 “接收數(shù)據(jù)” 了?
  3. 什么時候設置成EPOLLOUT呢?

\color{red}{關于第1點:}
是不可以設置成EPOLLIN|EPOLLOUT的。有這樣一段答案:

老王:“The way epoll works, is by hooking into the existing kernel poll subsystem. It hooks into the poll wakeups, via callback, and it that way it knows that "something" is changed. Then it reads the status of a file via f_op->poll() to know the status. What happens is that, if you listen for EPOLLIN|EPOLLOUT, when a packet arrives the callback hook is hit, and the file is put into a maybe-ready list. Maybe-ready because at the time of the callback, epoll has no clue of what happened. After that, via epoll_wait(), f_op->poll() is called to get the status of the file, and since POLLIN|POLLOUT is returned (and since you're listening for EPOLLIN|EPOLLOUT), that gets reported back to you. The POLLOUT event, by meaning a buffer-full->buffer-avail transition, did not really happen, but since POLLOUT is true, that gets reported back too.”

張三:“Ok, so make sure I understand you correctly, you're saying that currently the kernel doesn't have
awareness of the difference between EPOLLIN and EPOLLOUT events because at the time of the event, both EPOLLIN/EPOLLOUT are returned from the kernel and that at least for the near term that's not going to change. At some point, we can expect the EPOLLOUT to give the correct event, but not till later than .28.”

老王:“The kernel knows the difference between EPOLLIN and EPOLLOUT, of course. At the moment though, such condition is not reported during wakeups, and this is what is going to be changing.”

最主要的是這句:at the time of the callback, epoll has no clue of what happened。大概就是指的回調(diào)的時候,epoll是不能區(qū)分出是EPOLLIN還是EPOLLOUT的。換句話說如果你同時監(jiān)聽了EPOLLIN和EPOLLOUT,當發(fā)生了EPOLLIN,由于epoll無法區(qū)分種類,那么將會同時監(jiān)聽到EPOLLIN和EPOLLOUT事件(即使當時沒有發(fā)生EPOLLOUT)。

\color{red}{關于第2點:}
快遞到樓下菜鳥驛站, 菜鳥驛站給你發(fā)短信讓你去取件,至于你手機開沒開機,是不影響快遞是否到達的。

\color{red}{關于第3點:}
什么時候設置為EPOLLOUT呢?
個人認為,當需要發(fā)送數(shù)據(jù)時,write的返回值。如果是-1錯誤是EAGAIN時候,這時候該記錄下已經(jīng)寫了多少,fd設置監(jiān)聽EPOLLOUT即可,當發(fā)生EPOLLOUT時候表示可以繼續(xù)寫,直到發(fā)送完畢,然后再設置為EPOLLIN。

二、epoll_wait最后一個參數(shù)設置為幾好些:

個人認為:
出現(xiàn)如下代碼時候設置為-1 (多線程)

while(1)
{
  n = epoll_wait(..., -1)
 if (n== -1)
  {
    if (errno == EINTR)
      continue;
  }
}

出現(xiàn)如下代碼時候設置為0:

void update()//多少毫秒一調(diào)用
{
  n = epoll_wait(..., 0)
}

出現(xiàn)如下代碼時候設置為某個數(shù):

while(1)//多少毫秒一調(diào)用
{
  time = 規(guī)定的刷新間隔時間 - 上次邏輯處理時間;//湊合看,不考慮小于0了
  n = epoll_wait(..., time);
  logic->update();
}

個人認為需要特殊的注意點:

  1. ET模式下,read和write一定要讀完或者寫完(網(wǎng)上很多示例)。
  2. 記得如果listen_fd設置的也是ET模式的話,記得全部accept完畢。
  3. 使用epoll_wait(..., -1)時候,記得如下處理
while(1)
{
  n = epoll_wait(..., -1)
 if (n== -1)
  {
    if (errno == EINTR)
      continue;
  }
}
  1. 當發(fā)生新用戶連接時候:
connfd = ::accept4(listenfd, (struct sockaddr*)&saddr,&slen, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (connfd == -1)
{
  if (errno == EMFILE)
  {
    close(idlefd);
    idlefd = accept(listenfd, NULL, NULL);
    close(idlefd);
    idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
    //todu
    continue;
  }
break;
}

當發(fā)生EMFILE時候,accept會返回-1,但是此時(經(jīng)測試)你雖然調(diào)用過了accept,這個連接過來的用戶是不會從accept列表中移除的。當你再次調(diào)用的時候,仍會常識為該用戶分配一個新的fd值。如果此時關閉某個fd再次調(diào)用accept時,是可以為該用戶分配到一個fd的(剛剛你關閉的那個)。這時候,你就可以很優(yōu)雅的給該用戶發(fā)送 “用戶數(shù)目達到上限” 等消息,然后再做關閉等處理。

其他想不到了,隨筆寫個,也是我簡書的第一篇文章。后期想到什么的話還會更新添加,先就這樣吧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容