代码托管在 gitos 上,请使用下面的命令获取:
如果你已经 clone 过这个代码了,请使用 git pull 更新一下。
本次实验是接着上一篇文章的实验继续的,所以大家最好两篇连着看。上一篇文章的地址:《连接断开异常(服务器进程终止)》。
1. 相关程序
本次实验所使用的程序路径是 unp/program/echo/exception_sigpipe
。这个程序与之前的 processzombie 并没有什么本质上的不同,几乎完全一样,只不过,我们做了两个修改
- 捕捉了 SIGPIPE 信号
- 客户端部分稍稍做了修改,如下:
这样修改的目的是什么呢?我们的实验步骤其实上上一篇文章完全一样,即让服务器进程终止,客户端再不知道服务器终止的情况下,又向服务器发送数据。
客户端分成 2 次 writen,第一次发送 1 个字节,第二个发送 n - 1 个字节,两次 writen 之间休眠 1 秒。这样做的目的是希望在第二次 writen 前收到对端发来的 RST 段。
2. 实验结果
图1 服务器端,杀死子进程
图2 客户端,第二次发送 hehe 收到 SIGPIPE 信号
3. 结果分析
3.1 为什么服务器收到两次 SIGCHLD
很奇怪的是,服务器端在杀死子进程时,我们收到了两个 SIGCHLD 信号,不知道大家有没有注意,在上一篇文章里实际上也是这种情况。因为当你按下 CTRL Z 的时候,会导致父进程和子进程都停止。
而 SIGCHLD 信号产生需要满足以下任意一个条件之一:
- 子进程结束
- 子进程停止
- 子进程恢复执行(man 手册中并没有写这种情况,不知道是不是和内核有关,希望大家进行测试并进行留言)
因此,一次收到 SIGPIPE 是因为子进程停止,另一次是因为子进程终止。
3.2 为什么收到 SIGPIPE
接下来,我们分析为什么客户端会接收到 SIGPIPE。当 flower 服务器结束的时候,sun 客户端第一次 writen 后,服务器会回送 RST 端,接着 sun 等待了 1 秒,这一秒时间,足以让 sun 接收到 RST 段,此时 sun 又执行了一次 writen,于是引发了 SIGPIPE,同时 writen 错误,errno = EPIPE.
《Unix 网络编程》 是这样解释的:
当一个进程向某个已收到 RST 段的套接字执行写操作时,内核向该进程发送一个 SIGPIPE 信号。
4. 总结
- 向收到 RST 套接字执行写,会引发 SIGPIPE