日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

POLLERR的故事

發(fā)布時間:2023/12/10 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 POLLERR的故事 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

今天code review時,同事B對我代碼中的poll()的處理做法提出了異議。于是做了些研究,還發(fā)現(xiàn)了一些好玩的故事。

異議的代碼

我的代碼是參考manpage寫的,類似下面的做法。同事B說沒有處理POLLERR、而且應(yīng)當(dāng)使用else if。

OK。我贊同補(bǔ)充POLLERR的處理,但不贊同使用else if。原因:

  • fd的讀事件、寫事件可能會同時到達(dá),因此我想同時處理這兩個事件;
  • Linux Manpage里面的示例,就是三個if語句獨(dú)立的。
ret = poll(fds, 2, timeout_msecs); if (ret > 0) {/* An event on one of the fds has occurred. */for (i=0; i<2; i++) {if (fds[i].revents & POLLIN ) {/* Priority data may be written on device number i. */...}if (fds[i].revents & POLLOUT ) {/* Data may be written on device number i. */...}} }

詭異的經(jīng)歷

但是同事B舉出了他偶然體驗到的詭異經(jīng)歷:

POLLIN, POLLOUT, POLLERR同時出現(xiàn)。

在這種異常下,我的代碼處理邏輯就會坑爹了。

于是問題變成了,什么情況下會出現(xiàn)這種詭異場景、三個事件同時出現(xiàn)究竟是什么含義?

翻閱《UNIX環(huán)境高級編程》、《UNIX網(wǎng)絡(luò)編程》里面對poll()的講解,均沒有提到信號是否會同時出現(xiàn)的問題(所以也沒提到該不該用else if的事情了)。

在Github上查找POLLERR相關(guān)的代碼,發(fā)現(xiàn)大多數(shù)人都是用3個if語句處理這三個事件。那真相究竟是啥?

牛人的解答

百般搜索,終于在StackOverflow.com上看到有人提到了一個相似的問題:

Sometimes epoll_wait returns with both POLLOUT & POLLERR events set for the same socket descriptor.

終于下面有大神做了解答:

Here is some good information on?non-blocking tcp connect().

When a socket error is detected (i.e. connection closed/refused/timedout), epoll will return the registered interest events POLLIN/POLLOUT with POLLERR. So epoll_wait() will return POLLOUT|POLLERR if you registered POLLOUT, or POLLIN|POLLOUT|POLLERR if POLLIN|POLLOUT was registered.

Just because epoll returns POLLIN doesn't mean there will be data available to read, since recv() may just return the error from the non-blocking connect() call. I think epoll returns all the registered events with POLLERR to make sure the program calls send()/recv()/etc.. and gets the socket error. Some programs never check for POLLERR/POLLHUP and only catch socket errors on the next send()/recv() call.

翻譯一下:

這兒有些很贊的關(guān)于非阻塞TCP connect()的信息。

當(dāng)一個socket出現(xiàn)錯誤時(例如 連接斷開/拒絕/超時),epoll()會返回POLLERR加上注冊時的POLLIN/POLLOUT事件。所以,如果監(jiān)聽的是POLLOUT,那epoll_wait()會返回POLLOUT|POLLERR;如果監(jiān)聽的是POLLIN,那epoll_wait()會返回POLLIN|POLLERR。

注意epoll()返回POLLIN并不表示會有數(shù)據(jù)可讀,因為recv()會立刻返回前一個錯誤碼(即非阻塞的connect()調(diào)用)。我個人認(rèn)為epoll()返回所有的注冊事件加POLLERR,是為了確保程序會調(diào)用send()/recv()等等,進(jìn)而發(fā)現(xiàn)socket出錯了。畢竟有些代碼從來不檢測POLLERR/POLLHUP,只折騰send()/recv()等函數(shù)的錯誤碼。

呵呵,Github上翻看了這么多代碼,的確是大神說的樣子。

驗證

所以同事B的經(jīng)歷是常見的場景。而且很容易就能夠觸發(fā)。只要在連接上鬧些問題,就能達(dá)到目的了。例如下面這段代碼演示了連接失敗時,POLLERR/POLLIN/POLLOUT事件都同時觸發(fā)了。

示例中使用了getsockopt()來獲取錯誤碼;也可以直接使用read()/write()也是能夠獲取相同的錯誤碼。

深入探究

StackOverflow的大神只做了簡要的解答。真正的原因只能自己去翻看代碼了。

翻閱內(nèi)核代碼(我的系統(tǒng)版本是Linux-2.6.32.57-x86 ),可以看到在tcp_poll()里(net/ipv4/tcp.c的389行,我的場景是TCP),對于所有sock錯誤都置了POLLERR。而異常情況下,POLLIN/POLLOUT則分別與RCV_SHUTDOWN/SEND_SHUTDOWN有關(guān)。換個視角,和連接斷開有關(guān)的代碼在tcp_reset()中(net/ipv4/tcp_input.c的3957行)的處理,里面的tcp_done()(代碼)則明確設(shè)置了sk->sk_shutdown = SHUTDOWN_MASK——所以,對于關(guān)閉的連接,總是會有POLLIN/POLLOUT事件!

研究到此解決。真相大白。

所以啊,我還是聽取同事B的建議,加個else if優(yōu)化一下處理邏輯吧。

總結(jié)

以上是生活随笔為你收集整理的POLLERR的故事的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。