关于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 ...
随机推荐
- Android实现Button事件的处理
Android实现Button事件的处理 开发工具:Andorid Studio 1.3 运行环境:Android 4.4 KitKat 代码实现 首先是最基本的线性布局,给每个控件设立id值,以供代 ...
- Machine Learning 学习笔记 (2) —— 使用牛顿法寻找极值
本系列文章允许转载,转载请保留全文! [请先阅读][说明&总目录]http://www.cnblogs.com/tbcaaa8/p/4415055.html 1. 用牛顿法解方程 牛顿法是一种 ...
- 关于四则运算的代码debug测试
1.首先检测题目是否能为负数,0? 截图: 总结:如图所示出题数目为0的时候,并没提示重新输入,而是输出空白,而当输出题目为负数的时候系统提示错误,并且提示终止 2.检测操作值得范围: 总结:当操 ...
- Travis-CI的初步了解和测试程序的进一步编写
一. Travis-CI部分 最近基本都在研究Travis-CI的使用.CI是continue integration(持续集成)的缩写,Travis应该是给我们提供免费服务器的组织.下面介绍一下其使 ...
- mysql开机脚本
#!/bin/bash basedir=/home/app/db/mysql datadir=$basedir/data conf=$basedir/etc/my.cnf pid_file=$data ...
- 十个优秀的C语言学习资源推荐
学习C语言,需要一点一滴,沉下心来,找个安静的地方,泡上一杯咖啡,在浓郁的香味中一起品味她.-- Boatman Yang 人们通常认为计算机编程很烦,但是有些人却从中发现了乐趣.每一个程序员不得不跟 ...
- 学习JQuery的$.Ready()与OnLoad事件比较
$(document).Ready()方法 VS OnLoad事件 VS $(window).load()方法接触JQuery一般最先学到的是何时启动事件.在曾经很长一段时间里,在页面载入后引发的事件 ...
- android开发 wifi开发工具类
import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Iterator; import j ...
- HDU 3555 Bomb 数位dp
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3555 Bomb Time Limit: 2000/1000 MS (Java/Others) Mem ...
- BitMap排序
问题描述: BitMap排序思想: 用1bit位标记某个元素对应的值 优点: 效率高,不允许进行比较和移位 ...