现象
最近写了一个reactor模型代码,使用wrk来测试性能时,发现wrk结束运行后,reactor也会结束运行。
reactor应该在mainloop中循环,不应该直接退出才对。
原因
我在各个关键函数中加了printf,发现reactor退出时,send()后面的printf没有输出。
又用gdb运行一次,gdb返回的结果是:
的确是在send处出了问题,原因是:
Program received signal SIGPIPE, Broken pipe.
Broken Pipe错误通常是与一个已关闭的管道进行通信时出现的。
我的reactor代码运行时可能发生了这样的事:
1.wrk向reactor发送了数据,然后wrk关闭了fd。
2.reactor代码中,接受了数据后,自动把fd的event修改为EPOLLOUT,这样就一定会执行send()。
3.此时fd已经被关闭了,send()失败,函数向系统发出了一个SIGPIPE信号,系统接到信号后,默认结束了reactor进程。
//↑这是我猜的,等待后续验证
解决办法
解决办法就是不让send()发出SIGPIPE信号。
send()的第三个参数可以设置MSG_NOSIGNAL Flag,来禁止发送SIGPIPE信号。
MSG_NOSIGNAL (since Linux 2.2)
Don't generate a SIGPIPE signal if the peer on a stream-
oriented socket has closed the connection. The EPIPE
error is still returned. This provides similar behavior
to using sigaction(2) to ignore SIGPIPE, but, whereas
MSG_NOSIGNAL is a per-call feature, ignoring SIGPIPE sets
a process attribute that affects all threads in the
所以把send代码进行以下修改:
before:
int count = send(fd, buff, index, 0);
printf("send,to clientfd:%d,count=%d\n", clientfd, count);
after
int count = send(clientfd, buff, index, MSG_NOSIGNAL);
if(count == -1)
{
perror("send");
printf("send err,to clientfd:%d,count=%d\n", clientfd,count);
set_event(clientfd,-1,SET_EVENT_DEL);
}
else
printf("send,to clientfd:%d,count=%d\n", clientfd, count);
再用wrk测试一下:
可以看到,确实有send()报了“broken pipe”的error(上文提到的EPIPE),并且返回值是-1。
而这次没有结束进程。
此时再启动一个net assistant模拟client,还是可以继续跟reactor进程通信的。
所以目前解决了reactor进程结束的问题。