频道栏目
IT货架 > > 正文
诡异的bug:tcsh陷入死循环
网友分享于:Jun 12, 2018 11:59:08 PM    来源: IT货架   

问题项目开发中,碰到一个很奇怪的问题:当tcsh启动的子程序退出之后,tcsh本身无法退出,并占用大量CPU资源

背景:应用程序在fork之后,用tcsh启动另一个子进程,执行特定任务。进程之间使用sockepair(pipe)进行进程间通讯;为简化编程,将子进程的socket fd重定向为stdin和stdout。

具体症状:

Strace tcsh程序:

tcsh不断打印上述系统调用,可判断tcsh陷入了死循环,无法退出。其中

write(17, "[1] 30274\n", 10)            = -1 EPIPE (Broken pipe)

--- SIGPIPE (Broken pipe) @ 0 (0) ---

lseek(16, 0, SEEK_END)                  = -1 ESPIPE (Illegal seek)

初步这两个系统调用是引发死循环的关键。为什么write到17文件描述符。(如果有经验,估计在这一步就可知道问题的根源。)

Lsof tcsh进程:

tcsh没有0,1,2的文件描述符,只有3个指向同一个unix地址的socket地址,基本断定: 16为stdin,17为stdout,18为stderr,19未知。

问题就变成:tcsh不断尝试往stdout输入特定信息![1] 30274这个信息,应该是tcsh所起子进程的PID。

这样就基本清楚了:tcsh收到子进程的状态信息([1] PID),并将收到的信息输出。由于stdout已被重定向成unix socket,在输出时,发生SIGPIPE (Broken pipe)事件。 SIGPIPE的默认行为是终止程序,显然tcsh没终止,并不断尝试输出。为什么tcsh不终止?难道tcsh内部实现屏蔽了该信号?为了解决这个问题,有两个办法:看tcsh的源码;看是否自己的程序改变了tcsh的信号处理。

最终,从自己的程序入手,发现果然是自己的程序改变了tcsh的信号配置。

A child created via fork(2) inherits a copy of its parent's signal dispositions.  During an execve(2), the dispositions of handled signals are reset to the default; the dispositions of ignored signals are left unchanged.

问题的结论:

1)      自己的程序忽略了SIGPIPE;

2)      fork之后,重定向了stdin和stdout;

3)      tcsh启动的子程序因socket对端被关闭,主动退出,tcsh捕获到子进程的状态,并尝试输出到stdout(socket),因socket已被关闭,写失败并导致SIGPIPE。因SIGPIPE已被忽略,导致tcsh不断重试,无法退出!

重现程序:

gcc -o pipe pipe_broke.c
./pipe /usr/bin/strace -e trace=write,lseek -o strace.log csh -c "sleep 10 & "

Note: bash has no this problem! csh -> tcsh

广告服务联系QQ:1134687142 | 网站地图

版权所有: IT货架- 内容来自互联网,仅供用于技术学习,请遵循相关法律法规. 京ICP备11030978号-1