关于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 ...
随机推荐
- [DHCP服务]——一个验证DHCP原理实验(VMware)
大致实验拓扑图 DHCP Server端的配置 1. 安装DHCP # yum -y install dhcp 2. 拷贝配置文件 # /dhcpd.conf.sample /etc/dhcp/dhc ...
- java笔试题(1)
char型变量中能不能存贮一个中文汉字? char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字啦.不过,如果某个特殊的汉字没 ...
- 在安卓3.0以下版本使用Fragment的注意事项
1. 按照网上的Fragment官网资料翻译来做一直有错: 10-03 02:43:13.971: E/AndroidRuntime(1921): java.lang.RuntimeException ...
- Jquery $.getJSON()设置同步
如下: $.ajaxSettings.async = false; $.getJSON('/AjaxSwitchDynamicInfo/GetPortUsedCount.cspx', { switch ...
- 【BZOJ】【3676】【APIO2014】回文串
回文自动机/Manacher+SA 这道题可以用Manacher找出本质不同的回文串(令max增大的所有回文串),然后再用SA跑出来有多少相同. 还有一种做法就是回文自动机(Orz Hzwer)的裸题 ...
- 剑指offer-17题
题目要求:输入一个表示整数的字符串,把该字符串转换成整数并输出.例如输入字符串"345",则输出整数345. 分析:这道题能够很好地反应出程序员的思维和编程习惯. 的确,自己编写的 ...
- Leetcode#140 Word Break II
原题地址 动态规划题 令s[i..j]表示下标从i到j的子串,它的所有分割情况用words[i]表示 假设s[0..i]的所有分割情况words[i]已知.则s[0..i+1]的分割情况words[i ...
- Leetcode#97 Interleaving String
原题地址 转化为二维地图游走问题. 比如s1="abab",s2="aab",s3="aabaabb",则有如下地图,其中"^&q ...
- 获取iframe中的元素
父窗口中获取iframe中的元素 var ifr = document.getElementById('suggustion').contentWindow.document.body; 在ifram ...
- .NET设计模式(13):享元模式(Flyweight Pattern)(转)
摘要:面向对象的思想很好地解决了抽象性的问题,一般也不会出现性能上的问题.但是在某些情况下,对象的数量可能会太多,从而导致了运行时的代价.那么我们如何去避免大量细粒度的对象,同时又不影响客户程序使用面 ...