天天看點

linux下epoll模型并發問題

最近用c++實作了貝葉斯分類算法,做了個自動識别垃圾資訊的小工具。工具中有個功能,通過綁定指定端口,和用戶端通信。服務端使用的是epoll網絡模型。在測試的時候發現,單使用者的情況下用戶端和伺服器通信正常。但是在多使用者并發的情況下,用戶端和服務端通信不正常。此時,用戶端能正常的連結,發送資料,但是一直卡在接收資料部分。如下圖:

linux下epoll模型并發問題

<img class="alignnone size-full wp-image-16" alt="t_1146193487" src="http://www.bo56.com/wp-content/uploads/2013/07/t_1146193487.jpg" width="500" height="76" /></a></p>

出現這種問題,是因為不正确的使用了epoll中的et(edge-trigger)模式。代碼如下:

注意倒數第二行:event.events = epollin|epollet; 采用的是et模式。下面我們來具體說下,問題出在那裡。

在epoll中有兩種模式:level-trigger模式,簡稱lt模式,和edge-trigger模式,簡稱et模式。其中,lt是預設的工作模式。

這兩種模式的工作方式有些不同。在level-trigger模式下隻要某個socket處于readable/writable狀态,無論什麼時候進行epoll_wait都會傳回該socket;而edge-trigger模式下隻有某個socket從unreadable變為readable或從unwritable變為writable時,epoll_wait才會傳回該socket。

在et模式socket非阻塞的情況下(上面代碼中就是這種情況),多個連接配接同時到達,伺服器的tcp就緒隊列瞬間積累多個就緒連接配接,由于是邊緣觸發模式,epoll隻會通知一次,accept隻處理一個連接配接,導緻tcp就緒隊列中剩下的連接配接都得不到處理。是以,就出現了上面所提及的問題。

解決辦法是用while循環抱住accept調用,處理完tcp就緒隊列中的所有連接配接後再退出循環。如何知道是否處理完就緒隊列中的所有連接配接呢?accept傳回-1并且errno設定為eagain就表示所有連接配接都處理完。

修改後的代碼如下:

同理,接收資料和發送資料時如果是et模式,且非阻塞,也得用循環。

讀:隻要可讀,就一直讀,直到傳回0,或者 errno = eagain

寫:隻要可寫,就一直寫,直到資料發送完,或者 errno = eagain

正确的讀:

正确的寫:

繼續閱讀