簡體   English   中英

最后一次read(2)調用的Epoll TCP邊緣觸發的必要性

[英]Epoll TCP edge-triggered necessity of last read(2) call

給定非阻塞TCP套接字,如果調用

read(sock, buf, bufLen)

返回一個值< bufLen ,然后等待邊緣觸發的EPOLLIN事件是否安全? 還是我必須再次調用read以確保它為零或EAGAIN?

在我的測試中,刪除最后一個調用時,一切都保持正常,我只想知道是否可以在任何地方或Linux源代碼保證它,以及是否可以擺脫多余的調用。

man 7 epoll回答了您的問題。 如您所見,它取決於套接字類型(數據包/流):

Q9使用EPOLLET標志(邊沿觸發的行為)時,是否需要連續讀寫文件描述符直到EAGAIN?

A9從epoll_wait(2)接收事件應向您建議該文件描述符已准備就緒,可以執行請求的I / O操作。 您必須考慮到它准備就緒,直到下一次(非阻塞)讀/寫產生EAGAIN為止。 何時以及如何使用文件描述符完全取決於您。

對於面向數據包/令牌的文件(例如,數據報套接字,規范模式下的終端),檢測讀/寫I / O空間結束的唯一方法是繼續讀/寫直到EAGAIN。

對於面向流的文件(例如,管道,FIFO,流套接字),還可以通過檢查從目標文件描述符讀取/寫入到目標文件描述符的數據量來檢測讀取/寫入I / O空間已用完的情況。 例如,如果通過要求讀取一定數量的數據來調用read(2),而read(2)返回的字節數較少,則可以確定已經用完了文件描述符的讀取I / O空間。 使用write(2)進行寫入時也是如此 (如果不能保證受監視的文件描述符始終引用面向流的文件,請避免使用后一種技術。)

在不會崩潰的范圍內 ,它是“安全的”,但是除非您繼續調用read直到獲得EAGAIN (或為零,這意味着另一端已關閉連接),否則您有時會對數據的可用性做出錯誤的假設。 最糟糕的是,它在大多數情況下看起來也可以正常工作。

與從水平觸發的通知相反,邊緣觸發的通知僅保證自從上次調用epoll_wait以來的就緒狀態發生變化時,即使存在仍可讀取的數據,您也會收到一個通知。
邊緣觸發的事件通知有時在Linux下的行為確實有點怪異或不直觀,因此它可能會執行與您期望的不同的操作,例如,當有更多數據到達時會給您另一個通知(因此您的代碼似乎“仍然可以工作”),但這是不能保證什么。
epolleventfd使用時,我也有類似的“意外”。 您希望在邊緣觸發模式下發生的事情是所有已被阻塞喚醒的線程(全部同時(並且恰好一次)喚醒),並且每個在事件被通知阻塞后調用epoll_wait的信號都被通知阻塞,直到事件被消耗和再次發出信號。 它的真正作用是喚醒名為epoll_wait第一個線程。 再次令人驚訝的是,電平觸發模式的工作原理與您期望的完全一樣,除了必須消耗事件才能再次准備好該事件之外,這沒有適當的處理方法(因為您必須完全執行一次或ll阻塞read )。

因此,如果您不使用所有數據並稍后等待再次收到通知,那么您可能會很幸運,它將“仍然有效”,或者您可能等待很長時間,甚至可能永遠。 因此,我的建議是一定要繼續閱讀,直到獲得EAGAIN為止,這是避免意外的唯一真正可靠的事情。

請注意,如果您天真地閱讀,您可能會餓死發送緩慢的郵件。 如果您有一個非常快的發件人,並且繼續閱讀該快速發件人,那么您將永遠不會看到EAGAIN (至少直到另一端繼續發送EAGAIN !),並且您將完全餓死其他發件人。
因此,將所有就緒的描述符放入列表中並循環讀取它們是有意義的,並在它們返回EAGAIN時將其從列表中刪除。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM