首頁 > 軟體

epoll 事件之 EPOLLRDHUP

2020-06-16 17:44:11

在對系統問題進行排查時,我發現了一個奇怪的現象:明明是對方斷開請求,系統卻報告一個查詢失敗的錯誤,但從使用者角度來看請求的結果正常返回,沒有任何問題。

對這個現象深入分析後發現,這是一個基於 epoll 的連線池實現上的問題,或者說是特性 :)

首先解釋一下導致這個現象的原因。

在使用 epoll 時,對端正常斷開連線(呼叫 close()),在伺服器端會觸發一個 epoll 事件。在低於 2.6.17 版本的核心中,這個 epoll 事件一般是 EPOLLIN,即 0x1,代表連線可讀。

連線池檢測到某個連線發生 EPOLLIN 事件且沒有錯誤後,會認為有請求到來,將連線交給上層進行處理。這樣一來,上層嘗試在對端已經 close() 的連線上讀取請求,只能讀到 EOF,會認為發生異常,報告一個錯誤。

因此在使用 2.6.17 之前版本核心的系統中,我們無法依賴封裝 epoll 的底層連線庫來實現對對端關閉連線事件的檢測,只能通過上層讀取資料時進行區分處理。

不過,2.6.17 版本核心中增加了 EPOLLRDHUP 事件,代表對端斷開連線,關於新增這個事件的理由可以參見 “[Patch][RFC] epoll and half closed TCP connections”。

在使用 2.6.17 之後版本核心的伺服器系統中,對端連線斷開觸發的 epoll 事件會包含 EPOLLIN | EPOLLRDHUP,即 0x2001。有了這個事件,對端斷開連線的異常就可以在底層進行處理了,不用再移交到上層。

重現這個現象的方法很簡單,首先 telnet 到 server,然後什麼都不做直接退出,檢視在不同系統中觸發的事件碼。

注意,在使用 2.6.17 之前版本核心的系統中,sys/epoll.h 的 EPOLL_EVENTS 列舉型別中是沒有 EPOLLRDHUP 事件的,所以帶 EPOLLRDHUP 的程式無法編譯通過。

本文永久更新連結地址http://www.linuxidc.com/Linux/2016-04/129819.htm


IT145.com E-mail:sddin#qq.com