进程状态-Z:僵尸进程产生的原因
Z (zombie)
:僵死状态是一个比较特殊的状态。进程在退出的过程中,处于TASK_DEAD
状态。
在这个退出过程中,进程占有的所有资源将被回收,除了task_struct结构(以及少数资源)以外。于是进程就只剩下task_struct这么个空壳,故称为僵尸。
1 | ps |
- 僵尸进程产生的原因???
- 如何确定根本原因???
- 如何避免???
僵尸进程产生的原因
僵尸进程: 是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源(也就是子进程先于父进程结束),此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源
原因:
- 子进程被直接杀死
- 子进程无法正常关闭
场景:主进程创建的线程在进行数据搬运时,搬运数据的大小超过了放置数据buffer的大小,导致部分数据被污染,最终导致子线程在运行过程中出现段错误,将其直接杀死,没有等到父进程回收,而产生了僵尸进程.
为什么子进程结束时,不直接退出
因为父进程有时候需要获取到子进程的退出状态,如果是正常退出,可以直接将其释放,如果是异常退出,又可以根据异常信息进行进一步的相关操作。
调试&&Debug
- 查找子进程被杀死的原因,如,段错误可以重新定义段错误信号的处理打印部分信息.
- 排查显示fork和隐式fork,对子进程的操作.
- 子进程退出信号
signal(SIGCHLD,sig_child)
的自定义处理
设计时的预防
父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。
- 执行wait()或waitpid()系统调用,则子进程在终止后会立即把它在进程表中的数据返回给父进程,此时系统会立即删除该进入点。在这种情形下就不会产生defunct进程。
如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。
如果父进程不关心子进程什么时候结束,那么可以用
signal(SIGCLD, SIG_IGN)
或signal(SIGCHLD, SIG_IGN)
通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号- signal(SIGCHLD, SIG_IGN): 通过信号回收子进程的SIGCHLD. 子进程要终止了,发个SIGCHLD信号告诉父进程, 设置
SIG_IGN
忽略信号,则表示父进程不关心子进程退出,子进程退出后内核把资源回收即可。 - signal(SIGCLD, SIG_IGN): 子进程状态改变后产生此信号,该信号的配置为SIG_IGN, 子进程状态信息会被丢弃,也就是自动回收了,则调用进程的子进程将不产生僵死进程
- signal(SIGCHLD, SIG_IGN): 通过信号回收子进程的SIGCHLD. 子进程要终止了,发个SIGCHLD信号告诉父进程, 设置
fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做
父进程退出时通知子进程退出
- 方法1:在子进程和父进程之间建立通信管道(
socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)
),一旦通信异常,则认为父进程退出,子进程自己也回收资源退出。 - 方法2:借助
prctl
函数中的PR_GET_PDEATHSIG
参数, 在子进程中添加prctl(PR_SET_PDEATHSIG,SIGKILL);
,这样父进程退出时,子进程将会收到SIGKILL信号,而进程收到该信号的默认动作则是退出。
1 | PR_SET_PDEATHSIG (since Linux 2.1.57) |
子进程状态变为Zl
1 | ps aux | grep enc-test |
当前系统无法使用gdb和strace,但是系统配置均正常,是否为僵尸进程就无法被attach。
出现错误信息:
1 | strace: Could not attach to process. If your uid matches the uid of the target process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try again as the root user. For more details, see /etc/sysctl.d/10-ptrace.conf: Operation not permitted |
注:系统已经正确配置将
/etc/sysctl.d/10-ptrace.conf
文件中修改kernel.yama.ptrace_scope = 0
并且系统进行了重启,重启后/proc/sys/kernel/yama/ptrace_scope
为0
,但是还是无法使用。
(strace系统中的其他正常进程是正常的)
栈信息
通过proc文件系统查看进程当前栈信息
1 | $ sudo cat /proc/132525/stack |
没有输出如何栈信息,也就是说当前出问题的进程没有运行,正是僵尸进程的表现。
该僵尸进程如何产生
一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程
。
其实就是子进程退出的时候父进程不知道,因此解决方法就是在子进程退出时,告诉父进程一下(参考:设计时的预防)。
而该测试中的僵尸进程是由于测试进程在循环测试中,Ctrl+C终止测试时,偶尔会产生僵尸进程,应该是在测试程序运行过程中,子进程和父进程先后收到了Ctrl+c信号(可能信号处理的时序问题)导致部分子进程退出时,父进程已经退出没有来的即调用wait接口,无人回收资源变为僵尸进程。
解决方法
在进程中忽略INT信号(也就是Ctrl+c),如果实际业务流程需要,可以自己实现INT信号的处理函数。