我正在為套接字撰寫一個協程包裝器作為協程用例的演示,并且我在如何安全地使用 epoll(不引入競爭條件)方面有點掙扎。
我已經發現我必須將邊緣模式EPOLLET與EPOLLONESHOT. 但現在我不確定什么時候應該重新裝上插座。
我應該在呼叫非阻塞操作之前還是之后重新武裝?我想確保我既不會錯過活動,也不會收到幻影。
// epoll & socket setup
int ret;
ret = epoll_ctl(epoll_, EPOLL_CTL_MOD, ...); // rearm here
//...
ret = read(...);
//...
ret = epoll_ctl(epoll_, EPOL_CTL_MOD, ...); // or here?
int ret = epoll_wait(...);
uj5u.com熱心網友回復:
我應該在呼叫非阻塞操作之前還是之后重新武裝?
從技術上講,之后,但并沒有那么簡單。
無論如何EPOLLONESHOT,一旦您收到一個邊緣觸發的事件,表明給定檔案描述符的讀取就緒,您必須考慮該 FD 繼續準備就緒,直到read()它失敗并errno設定為EAGAIN(因此該檔案必須處于非阻塞狀態)模式)。在這些讀取程序中,可能會出現這樣的情況:您用一個 讀取了所有剩余的位元組read(),但更多的位元組在下一個之前到達。在這種情況下,如果 FD 仍處于布防狀態,那么它的新事件將排隊(或與該 FD 的另一個事件合并,視情況而定)。在這種情況下,您可能會在實際上 FD 不再準備好時收到事件。
您應該考慮只接受那些“幻影”事件。由于您的檔案將處于非阻塞模式,因此它們不會導致不必要的停頓,只需做一些額外的作業。你的代碼會更簡單。但是,如果您確實使用EPOLLONESHOT來避免接收幻象事件,那么在您確定它read未準備好之前(通過失敗EAGAIN),您不得重新武裝 FD ,否則您將無法達到目的。
因此,完整的答案是在 FD 確定為 unready 之后。這至少需要read()兩秒,甚至可能更多。如果檔案在最后一次讀取之后和重新準備之前準備好,那么重新準備應該會導致一個適當的事件排隊。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/353294.html
