Code Bye

请教关于wait和waitpid

 

在看unix网络编程的时候,在第五章书中提到在服务端处理僵死的子进程可以通过信号处理函数,但是调用wait并不足以防范僵死进程,原因是unix信号不排队。然后书中又说waitpid可以解决这个问题,因为有WNOHANG选项。
我就很奇怪,难道处理waitpid时候,信号就会排队了吗?不是照样会有信号丢失的问题出现?

程序被杀死之后,为了在垂死之际将最后的状态信息留给父进程,才留下了僵尸进程,等待父进程使用waitpid来“收尸”,wait内部也是调用了waitpid。至于子进程退出的信号,通常被忽略掉了。
void sig_chld(int signo)
{
        pid_t         pid;
        int         stat;
        
        while((pid = waitpid(-1, &stat, WNOHANG))  > 0) {
                printf("child %d terminated\n", pid);
        }
        
        return;
}

从网上copy的一段代码. wnohang的选项意味着不会阻塞一直等到某个子进程结束。那当一个子进程结束的时候进入到这个信号处理函数进行处理的时候这个时候另外一个子进程也结束了。那么意味着要重入这个处理函数了。只要这个处理函数式可重入的。那么无论有多少个子进程结束了,都会发SIGCHLD信号,都会被waitpid所处理。

waitpid是非阻塞的。每当有子进程退出的信号都能接收到。而wait阻塞,如果不是最后一个进程退出,而是其他子进程退出,那么就接收不到退出信号,然后就成了僵尸进程了
10分
信号不排队的意思是:可能有2个子进程都退出了,向父进程发来了总共2个SIGCHLD信号,由于信号不排队,所以会合并成1个事件,调用信号处理函数一次。 如果你在信号处理函数里只wait一次,那么就会漏掉1个子进程的资源没有回收。所以要在信号处理函数里循环调用不阻塞的waitpid函数,直到其返回值表明没有更多子进程可以回收了,这里有一个前提就是信号处理函数被调用期间,对应的信号会被线程阻塞,这样在信号处理函数期间到达的SIGCHLD信号就不会丢失,可以保障信号函数处理退出后马上被再次唤起。
引用 4 楼 qq120848369 的回复:

信号不排队的意思是:可能有2个子进程都退出了,向父进程发来了总共2个SIGCHLD信号,由于信号不排队,所以会合并成1个事件,调用信号处理函数一次。 如果你在信号处理函数里只wait一次,那么就会漏掉1个子进程的资源没有回收。所以要在信号处理函数里循环调用不阻塞的waitpid函数,直到其返回值表明没有更多子进程可以回收了,这里有一个前提就是信号处理函数被调用期间,对应的信号会被线程阻塞,这样在信号处理函数期间到达的SIGCHLD信号就不会丢失,可以保障信号函数处理退出后马上被再次唤起。

是不是指在调用waitpid的时候,也有可能只有一个SIGCHLD信号,但是父进程调waitpid会查询还有没有子进程是僵死状态,如果有的话那就回收?

引用 2 楼 pengzhixi 的回复:
void sig_chld(int signo)
{
        pid_t         pid;
        int         stat;
        
        while((pid = waitpid(-1, &stat, WNOHANG))  > 0) {
                printf("child %d terminated\n", pid);
        }
        
        return;
}

从网上copy的一段代码. wnohang的选项意味着不会阻塞一直等到某个子进程结束。那当一个子进程结束的时候进入到这个信号处理函数进行处理的时候这个时候另外一个子进程也结束了。那么意味着要重入这个处理函数了。只要这个处理函数式可重入的。那么无论有多少个子进程结束了,都会发SIGCHLD信号,都会被waitpid所处理。

你的意思是SIGCHLD信号会排队?我觉得不是这样子的吧。比如以下这个代码,如果有n个信号同时产生,你觉得会打印几遍hello:

void sig_chld(int signo)
{
        pid_t         pid;
        int         stat;
        printf("hello\n");
        while((pid = waitpid(-1, &stat, WNOHANG))  > 0) {
                printf("child %d terminated\n", pid);
        }
        
        return;
}

20分
引用 6 楼 u011761982 的回复:
Quote: 引用 2 楼 pengzhixi 的回复:
void sig_chld(int signo)
{
        pid_t         pid;
        int         stat;
        
        while((pid = waitpid(-1, &stat, WNOHANG))  > 0) {
                printf("child %d terminated\n", pid);
        }
        
        return;
}

从网上copy的一段代码. wnohang的选项意味着不会阻塞一直等到某个子进程结束。那当一个子进程结束的时候进入到这个信号处理函数进行处理的时候这个时候另外一个子进程也结束了。那么意味着要重入这个处理函数了。只要这个处理函数式可重入的。那么无论有多少个子进程结束了,都会发SIGCHLD信号,都会被waitpid所处理。

你的意思是SIGCHLD信号会排队?我觉得不是这样子的吧。比如以下这个代码,如果有n个信号同时产生,你觉得会打印几遍hello:

void sig_chld(int signo)
{
        pid_t         pid;
        int         stat;
        printf("hello\n");
        while((pid = waitpid(-1, &stat, WNOHANG))  > 0) {
                printf("child %d terminated\n", pid);
        }
        
        return;
}

这有什么关系呢?因为你已经进入了这个循环执行waitpid了。waitpid可不需要SIGCHLD信号,只要有子进程挂了就会返回。只是说waitpid借助第一个SIGCHLD信号发挥作用。他本身处理是与SIGCHLD信号没关系。简单的说waitpid等待的不是SIGCHLD,而是子进程的死亡状态,仅此而已。

感谢大家的回复!

CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明请教关于wait和waitpid