前一段时间写代码用到了子进程,设置了SIGCHLD的信号处理函数,并且父进程使用wait等待子进程结束,运行的结果和我想象的不大一样。
原型抽取如下:
/*
* SigChld.cpp
*
* Created on: 2012-11-16
* Author: hf
*/
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
using namespace std;
void fun()
{
cout << "fun begin" << endl;
int n = execl("/home/hf/workspace/Hello/src/tellPid", "ls", NULL);
if (-1 == n)
{
cout << "fun error" << endl;
}
cout << "fun end" << endl;
}
void sig_chld(int signo)
{
pid_t pid;
int stat;
cout << "in sig_chld" << endl;
if (signo == SIGCHLD)
{
cout << "in sig_chld sigchld" << endl;
}
while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
{
cout << "进程:" << pid << "结束" << endl;
}
}
void sig_chld1(int signo)
{
pid_t pid;
int stat;
cout << "in sig_chld1" << endl;
if (signo == SIGCHLD)
{
cout << "in sig_chld1 sigchld" << endl;
}
while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
{
cout << "进程1:" << pid << "结束" << endl;
}
}
int main()
{
cout << "main begin" << endl;
signal(SIGCHLD, sig_chld);
signal(SIGCHLD, sig_chld1);
int n = fork();
switch (n)
{
case 0:
cout << "child:" << getpid() << endl;
sleep(7);
fun();
cout << "child end" << endl;
exit(0);
break;
case -1:
cout << "fork error" << endl;
break;
default:
cout << "parent,child pid:" << n << endl;
break;
}
int status = 0;
int tt = wait(&status);
cout << "wait:" << tt << endl;
cout << "main end" << endl;
}
在父进程中使用wait等待子进程结束,同时设置了SIGCHLD的信号处理函数,此处理函数中使用waitpid来回收资源。
执行的时候发现,wait函数正常执行,并且返回了子进程的pid,信号处理函数也能够进入,但是waitpid函数不在能够收集到子进程的信息。
在网上搜索了一下,在http://www.cnblogs.com/taobataoma/archive/2007/08/30/875743.html发现一些信息:
内核对子进程终止(SIGCLD)信号的处理方法与其他信号有所区别。当进程检查出收到了一个子进程终止的信号时,缺省情况下,该进程 就象没有收到该信号似的,如果父进程执行了系统调用wait,进程将从系统调用wait中醒来并返回wait调用,执行一系列wait调用的后续操作(找 出僵死的子进程,释放子进程的进程表项),然后从wait中返回。SIGCLD信号的作用是唤醒一个睡眠在可被中断优先级上的进程。如果该进程捕捉了这个 信号,就象普通信号处理一样转到处理例程。如果进程忽略该信号,那么系统调用wait的动作就有所不同,因为SIGCLD的作用仅仅是唤醒一个睡眠在可被 中断优先级上的进程,那么执行wait调用的父进程被唤醒继续执行wait调用的后续操作,然后等待其他的子进程。
如果一个进程调用signal系统调用,并设置了SIGCLD的处理方法,并且该进程有子进程处于僵死状态,则内核将向该进程发一个SIGCLD信号。
总结如下:
当父进程阻塞在wait系统调用,子进程结束的时候,内核发送SIGCHLD到进程,父进程从wait调用中醒来,继续执行wait的后续操作(释放僵尸子进程),切换到用户态后,进程执行信号处理函数,此时waitpid已经取不到僵死子进程的信息,所以while里面的语句没有执行。
如果在调试的时候,把断点设置SIGCHLD信号处理函数的while之前,会发现,执行到断点的时候,ps一下,发现根本就没了僵尸进程了。