关于SIGPIPE导致的程序退出
http://www.cppblog.com/elva/archive/2008/09/10/61544.html
收集一些网上的资料,以便参考:
http://blog.chinaunix.net/u2/69143/showart_1087349.html
当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。
根据信号的默认处理规则SIGPIPE信号的默认执行动作是terminate(终止、退出),所以client会退出。若不想客户端退出可以把SIGPIPE设为SIG_IGN
如: signal(SIGPIPE,SIG_IGN);
这时SIGPIPE交给了系统处理。
服务器采用了fork的话,要收集垃圾进程,防止僵尸进程的产生,可以这样处理:
signal(SIGCHLD,SIG_IGN); 交给系统init去回收。
这里子进程就不会产生僵尸进程了。
http://www.cublog.cn/u/31357/showart_242605.html
好久没做过C开发了,最近重操旧业。
#define MAXBUF 40960 while (1) { memset(sendline, 'a', sizeof(sendline)); } int signal(SIGPIPE, SIG_IGN); if (argc != 2) sockfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); Connect(sockfd, (SA *) &servaddr, sizeof(servaddr)); str_cli(stdin, sockfd); /* do it all */ exit(0); |
为了方便观察错误输出,lib/writen.c也做了修改,加了些日志:
ssize_t /* Write "n" bytes to a descriptor. */ ptr = vptr; nleft -= nwritten; void |
client.c放在tcpclieserv目录下,修改了Makefile,增加了client.c的编译目标
|
接着就可以开始测试了。
测试1 忽略SIGPIPE信号,writen之前,对方关闭接受进程
本机服务端:
Begin Writen 40960
Already write 40960, left 0, errno=0
Begin send 40960 data
Begin Writen 40960
Already write 40960, left 0, errno=0
执行到上步停止服务端,client会继续显示:
Begin Writen 40960
writen error: Broken pipe(32)
结论:可见write之前,对方socket中断,发送端write会返回-1,errno号为EPIPE(32)
测试2 catch SIGPIPE信号,writen之前,对方关闭接受进程
修改客户端代码,catch sigpipe信号
signal(SIGPIPE, processSignal); |
本机服务端:
Begin Writen 40960
Already write 40960, left 0, errno=0
Begin send 40960 data
Begin Writen 40960
Already write 40960, left 0, errno=0
执行到上步停止服务端,client会继续显示:
Begin Writen 40960
Signal is 13
writen error: Broken pipe(32)
结论:可见write之前,对方socket中断,发送端write时,会先调用SIGPIPE响应函数,然后write返回-1,errno号为EPIPE(32)
测试3 writen过程中,对方关闭接受进程
为了方便操作,加大1次write的数据量,修改MAXBUF为4096000
本机服务端:
Begin Writen 4096000
执行到上步停止服务端,client会继续显示:
Begin Writen 3506179
writen error: Connection reset by peer(104)
结论:可见socket write中,对方socket中断,发送端write会先返回已经发送的字节数,再次write时返回-1,errno号为ECONNRESET(104)
为什么以上测试,都是对方已经中断socket后,发送端再次write,结果会有所不同呢。从后来找到的UNP5.12,5.13能找到答案
The client's call |
以上解释了测试3的现象,write时,收到RST.
What happens if The rule that If the process |
以上解释了测试1,2的现象,write一个已经接受到RST的socket,系统内核会发送SIGPIPE给发送进程,如果进程catch/ignore这个信号,write都返回EPIPE错误.
因此,UNP建议应用根据需要处理SIGPIPE信号,至少不要用系统缺省的处理方式处理这个信号,系统缺省的处理方式是退出进程,这样你的应用就很难查处处理进程为什么退出。
http://blog.csdn.net/shcyd/archive/2006/10/28/1354577.aspx
在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
处理方法:
在初始化时调用signal(SIGPIPE,SIG_IGN)忽略该信号(只需一次)
其时send或recv函数将返回-1,errno为EPIPE,可视情况关闭socket或其他处理
gdb:
gdb默认收到sigpipe时中断程序,可调用handle SIGPIPE nostop print
相关
(1)SIG_DFL信号专用的默认动作:
(a)如果默认动作是暂停线程,则该线程的执行被暂时挂起。当线程暂停期间,发送给线程的任何附加信号都不交付,直到该线程开始执行,但是SIGKILL除外。
(b)把挂起信号的信号动作设置成SIG_DFL,且其默认动作是忽略信号 (SIGCHLD)。
(2)SIG_IGN忽略信号
(a)该信号的交付对线程没有影响
(b)系统不允许把SIGKILL或SIGTOP信号的动作设置为SIG_DFL
(3)指向函数的指针--捕获信号
(a)信号一经交付,接收线程就在指定地址上执行信号捕获程序。在信号捕 获函数返回后,接受线程必须在被中断点恢复执行。
(b)用C语言函数调用的方法进入信号捕捉程序:
void func (signo)
int signo;
func( )是指定的信号捕捉函数,signo是正被交付信号的编码
(c)如果SIGFPE,SIGILL或SIGSEGV信号不是由C标准定义的kill( )或raise( )函数所生成,则从信号SIGFPE,SIGILL,SIGSEGV的信号捕获函数正常返回后线程的行为是未定义的。
(d)系统不允许线程捕获SIGKILL和SIGSTOP信号。
(e)如果线程为SIGCHLD信号建立信号捕获函数,而该线程有未被等待的以终止的子线程时,没有规定是否要生成SIGCHLD信号来指明那个子线程。
每一种信号都被OSKit给予了一个符号名,对于32位的i386平台而言,一个字32位,因而信号有32种。下面的表给出了常用的符号名、描述和它们的信号值。
符号名 信号值 描述 是否符合POSIX
SIGHUP 1 在控制终端上检测到挂断或控制线程死亡 是
SIGINT 2 交互注意信号 是
SIGQUIT 3 交互中止信号 是
SIGILL 4 检测到非法硬件的指令 是
SIGTRAP 5 从陷阱中回朔 否
SIGABRT 6 异常终止信号 是
SIGEMT 7 EMT 指令 否
SIGFPE 8 不正确的算术操作信号 是
SIGKILL 9 终止信号 是
SIGBUS 10 总线错误 否
SIGSEGV 11 检测到非法的内存调用 是
SIGSYS 12 系统call的错误参数 否
SIGPIPE 13 在无读者的管道上写 是
SIGALRM 14 报时信号 是
SIGTERM 15 终止信号 是
SIGURG 16 IO信道紧急信号 否
SIGSTOP 17 暂停信号 是
SIGTSTP 18 交互暂停信号 是
SIGCONT 19 如果暂停则继续 是
SIGCHLD 20 子线程终止或暂停 是
SIGTTIN 21 后台线程组一成员试图从控制终端上读出 是
SIGTTOU 22 后台线程组的成员试图写到控制终端上 是
SIGIO 23 允许I/O信号 否
SIGXCPU 24 超出CPU时限 否
SIGXFSZ 25 超出文件大小限制 否
SIGVTALRM 26 虚时间警报器 否
SIGPROF 27 侧面时间警报器 否
SIGWINCH 28 窗口大小的更改 否
SIGINFO 29 消息请求 否
SIGUSR1 30 保留作为用户自定义的信号1 是
SIGUSR2 31 保留作为用户自定义的信号 是
注意:Linux
信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上
的信号叫做"不可靠信号",信号值小于SIGRTMIN(Red hat
7.2中,SIGRTMIN=32,SIGRTMAX=63)的信号都是不可靠信号。这就是"不可靠信号"的来源。它的主要问题是:进程每次处理信号后,
就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用
signal(),重新安装该信号。
另外,我再做一些补充,产生RST响应以至于系统发出SIGPIPE信号,应该分为两种情况:
1.
客户端到服务端之间网络断掉,或者服务端断电等,物理连接断掉了,这种情况下客户端不会退出,send函数正常执行,不会感觉到自己出错。因为由于物理网
络断开,服务端不会给客户端回应错误消息,没有RST响应,自然也不会产生SIGPIPE信号。但是当服务端再恢复正常的时候,对客户端send来的消息
会产生RST响应,客户端就收到SIGPIPE信号了,程序退出,但是这时send函数是能够返回 -1的。可以进行异常处理。
2.客户端到服务端的网络能通,服务程序挂掉,客户端程序会马上退出,因为服务端能正常返回错误消息,客户端收到,SIGPIPE信号就产生了。不过我不确定此时服务端返回是的RST响应,抓包来看没有RST标志。水平有限,只写到这了。
关于SIGPIPE导致的程序退出的更多相关文章
- [转] 关于SIGPIPE导致的程序退出
PS: 如果服务器程序不忽略SIGPIPE,在某些时候TCP writer收到这个信号,会导致进程退出 The rule that applies is: When a process writes ...
- Linux网络编程-SIGPIPE信号导致的程序退出问题
当客户端close关闭连接时,若server端接着发送数据,根据TCP协议的规定,server端会收到RST响应,当server端再次往客户端发送数据时,系统会发出一个SIGPIPE信号给server ...
- 【记录一个问题】cuda核函数可能存在栈溢出,导致main()函数退出后程序卡死30秒CUDA
调试一个CUDA核函数过程中发现一个奇怪的问题:调用某个核函数,程序耗时33秒,并且主要时间是main()函数结束后的33秒:而注释掉此核函数,程序执行不到1秒. 由此可见,可能是某种栈溢出,导致了程 ...
- linux c开发: 在程序退出时进行处理
有时候,希望程序退出时能进行一些处理,比如保存状态,释放一些资源.c语言开发的linux程序,有可能正常退出(exit),有可能异常crash,而异常crash可能是响应了某信号的默认处理.这里总结一 ...
- 【转】C#中WinForm程序退出方法技巧总结
C#中WinForm程序退出方法技巧总结 一.关闭窗体 在c#中退出WinForm程序包括有很多方法,如:this.Close(); Application.Exit();Application.Ex ...
- 程序退出异常_DebugHeapDelete和std::numpunct
前几天程序新加一个功能之后,其中用到了boost的lexical_cast<float>,发现在关闭命令行窗口的时候,程序报错,是程序退出清理时候报的错误. 一开始以为是程序新增的功能有问 ...
- Aliyun OSS SDK 异步分块上传导致应用异常退出
问题描述: 使用Aliyun OSS SDK的BeginUploadPart/EndUploadPart执行异步分块上传操作,程序出现错误并异常退出! 原因分析: Using .NET Framewo ...
- 如何在程序退出的时候清除activity栈
在公司里接手了一个后期的项目,由于项目前期对activity栈管理的不够谨慎,所以导致了在某些情况下程序退出的时候没有将activity栈清除掉.在网上找到的无非就是那几种例子,都不是最好的解决办法. ...
- C#中WinForm程序退出方法技巧总结
C#中WinForm程序退出方法技巧总结 一.关闭窗体 在c#中退出WinForm程序包括有很多方法,如:this.Close(); Application.Exit();Application.Ex ...
随机推荐
- homework-07 C++ 11 能好怎
大二时候学过c++,但是那只是为了考试在学习,大作业也就写了一个读写者线程同步的模拟,连一个完整的类都没有写过,所以我必须承认对c++了解的很少. 对于C++ 11这一新标准,我首先阅读了来自前C++ ...
- PB建数据窗口的时候会报内存错误
同事碰到了这个问题,百度了一下,按照下边的方法解决了 ------解决方案--------------------我遇到过,是powerbuilder的注册表出问题了,找到注册表中HKEY_USER ...
- cocos中使用VS自动创建工程的方法
为了省事,直接用VS编写了一小段代码,将cocos手动创建工程的命令改用system来执行,免去了手动输入命令的麻烦 其中: -d F:\\cocos2d-x-3.2-projects 是你要存放的工 ...
- 移植net-snmp到开发板(mini210)
1.安装交叉编译工具arm-linux-gcc 2.下载net-snmp源码安装包 3.解压安装包 4../configure --build=i686-linux --host=arm-linux ...
- EF之外键Include() left join
项目中用EF实现外键查询出的数据, 查询数量正确, 但实现返回数据集数量不对 //DbContext.cs HasRequired(s => s.ClassRoom) .WithMany() . ...
- 四则运算2--c++
1.设计思路: 上篇已写,不在解释..... 2.源代码: #include<iostream.h>#include<stdlib.h>#include "time. ...
- bzoj 1189 二分+最大流判定
首先我们可以二分一个答案时间T,这样就将最优性问题 转化为了判定性问题.下面我们考虑对于已知的T的判定 对于矩阵中所有的空点bfs一次,得出来每个点到门的距离, 然后连接空点和每个能在t时间内到达的门 ...
- poj 1986 Distance Queries LCA
题目链接:http://poj.org/problem?id=1986 Farmer John's cows refused to run in his marathon since he chose ...
- C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppCommon.targets(611,5): error MSB
project options, linker, manifest, Generate Manifest-> NO. 项目->属性->链接器->清单文件->生成清单 改 ...
- 用npm安装express后express命令找不到
Windows 平台加了 npm install -g express 也不行AppData\Roaming\npm 下面没有 express.bat 解决办法: sudo npm install - ...