POLLERR的故事
2015-09-08 10:06
169 查看
今天code review时,同事B对我代码中的
我的代码是参考manpage写的,类似下面的做法。同事B说没有处理
OK。我赞同补充
fd的读事件、写事件可能会同时到达,因此我想同时处理这两个事件;
Linux Manpage里面的示例,就是三个if语句独立的。
但是同事B举出了他偶然体验到的诡异经历:
在这种异常下,我的代码处理逻辑就会坑爹了。
于是问题变成了,什么情况下会出现这种诡异场景、三个事件同时出现究竟是什么含义?
翻阅《UNIX环境高级编程》、《UNIX网络编程》里面对poll()的讲解,均没有提到信号是否会同时出现的问题(所以也没提到该不该用
在Github上查找
百般搜索,终于在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.
翻译一下:
这儿有些很赞的关于非阻塞TCP connect()的信息。
当一个socket出现错误时(例如 连接断开/拒绝/超时),
注意
呵呵,Github上翻看了这么多代码,的确是大神说的样子。
所以同事B的经历是常见的场景。而且很容易就能够触发。只要在连接上闹些问题,就能达到目的了。例如下面这段代码演示了连接失败时,POLLERR/POLLIN/POLLOUT事件都同时触发了。
示例中使用了
StackOverflow的大神只做了简要的解答。真正的原因只能自己去翻看代码了。
翻阅内核代码(我的系统版本是Linux-2.6.32.57-x86 ),可以看到在
研究到此解决。真相大白。
poll()的处理做法提出了异议。于是做了些研究,还发现了一些好玩的故事。
异议的代码
我的代码是参考manpage写的,类似下面的做法。同事B说没有处理POLLERR、而且应当使用
else if。
OK。我赞同补充
POLLERR的处理,但不赞同使用
else if。原因:
fd的读事件、写事件可能会同时到达,因此我想同时处理这两个事件;
Linux Manpage里面的示例,就是三个if语句独立的。
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. */ ... } } }
诡异的经历
但是同事B举出了他偶然体验到的诡异经历:POLLIN, POLLOUT, POLLERR同时出现。
在这种异常下,我的代码处理逻辑就会坑爹了。
于是问题变成了,什么情况下会出现这种诡异场景、三个事件同时出现究竟是什么含义?
翻阅《UNIX环境高级编程》、《UNIX网络编程》里面对poll()的讲解,均没有提到信号是否会同时出现的问题(所以也没提到该不该用
else if的事情了)。
在Github上查找
POLLERR相关的代码,发现大多数人都是用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.
翻译一下:
这儿有些很赞的关于非阻塞TCP connect()的信息。
当一个socket出现错误时(例如 连接断开/拒绝/超时),
epoll()会返回POLLERR加上注册时的POLLIN/POLLOUT事件。所以,如果监听的是POLLOUT,那
epoll_wait()会返回POLLOUT|POLLERR;如果监听的是POLLIN,那
epoll_wait()会返回POLLIN|POLLERR。
注意
epoll()返回POLLIN并不表示会有数据可读,因为
recv()会立刻返回前一个错误码(即非阻塞的
connect()调用)。我个人认为
epoll()返回所有的注册事件加POLLERR,是为了确保程序会调用
send()/recv()等等,进而发现socket出错了。毕竟有些代码从来不检测POLLERR/POLLHUP,只折腾
send()/recv()等函数的错误码。
呵呵,Github上翻看了这么多代码,的确是大神说的样子。
验证
所以同事B的经历是常见的场景。而且很容易就能够触发。只要在连接上闹些问题,就能达到目的了。例如下面这段代码演示了连接失败时,POLLERR/POLLIN/POLLOUT事件都同时触发了。示例中使用了
getsockopt()来获取错误码;也可以直接使用
read()/write()也是能够获取相同的错误码。
深入探究
StackOverflow的大神只做了简要的解答。真正的原因只能自己去翻看代码了。翻阅内核代码(我的系统版本是Linux-2.6.32.57-x86 ),可以看到在
tcp_poll()里(net/ipv4/tcp.c的389行,我的场景是TCP),对于所有sock错误都置了
POLLERR。而异常情况下,
POLLIN/POLLOUT则分别与
RCV_SHUTDOWN/SEND_SHUTDOWN有关。换个视角,和连接断开有关的代码在
tcp_reset()中(net/ipv4/tcp_input.c的3957行)的处理,里面的
tcp_done()(代码)则明确设置了
sk->sk_shutdown = SHUTDOWN_MASK——所以,对于关闭的连接,总是会有POLLIN/POLLOUT事件!
研究到此解决。真相大白。
所以啊,我还是听取同事B的建议,加个else if
优化一下处理逻辑吧。
相关文章推荐
- 快开学了,继续码一码iOS开发~
- 《高性能MySQL》第一章
- 原理图,PCB设计软件
- shell 获得本机IP和free端口
- 教你怎样在ppt2010抠图的小技巧|用ppt2010抠图的方法
- <context:annotation-config/>、<context:component-scan/>
- java基础—9.GUI图形化界面
- sql中top使用方法
- 表单美化——下拉列表
- JS设计模式——适配器模式
- Ember.js 入门指南——计算属性(compute properties)
- hadoop hbase metric
- ios-xcode真机测试时报错001 process launch failed
- 深入理解javascript原型和闭包(完结
- Study 《如何批量修改文件名》
- Ember.js 入门指南——计算属性(compute properties)
- 在DOS窗口下不能正常使用net命令
- Linux命令ps,netstat,top,time,cat,uname
- *** Can't find default configuration "arch/x86/configs/xxx"!
- Myeclipse8.5 打开后一直显示未响应的解决方法