SIGCHLD 信号处理

linux下进程终止时,内核会向父进程发送一个SIGCHLD信号,其有几个特点:

1.在一个信号处理函数运行期间,正被递交的信号是阻塞的。

2.如果一个信号在被阻塞期间产生了一次或多次,那么该信号被解阻塞之后通常只递交一次,也就是说linux信号默认是不排队的。

举个栗子:

进程的第一个子进程终止产生信号(1),(1)信号处理过程中第二个子进程终止产生信号(2),此时(1)处于正在处理状态,(2)处于等待处理状态,接着又有第三、四、五子进程终止产生信号(3)(4)(5);(1)信号处理函数执行完后,只进行(2)信号的处理,其余信号(3)(4)(5)则丢失。

编写linux多进程的并发服务程序时,我们需要处理客户端退出,服务端fork出的子进程变为僵死(zombie)进程的情况,通常这样来做:

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

这里我们指定WNOHANG选项,它告知waitpid在有尚未终止的子进程在运行时不要阻塞。我们不能在循环中调用wait,因为没有办法防止wait在正在运行的子进程尚有未终止时阻塞。另外值得注意的是,在信号处理函数中使用printf等不可重入函数是不推荐的。

查看waitpid的man手册,有这样一段描述:

POSIX.1-2001  specifies that if the disposition of SIGCHLD is set to SIG_IGN or the SA_NOCLDWAIT flag is set for SIGCHLD (see sigaction(2)), then children that terminate do not become zombies and a call to wait() or waitpid() will block until all children have terminated, and then fail with errno set to ECHILD.

提供了另外两种僵死进程处理的办法:signal(SIGCHLD, SIG_IGN); 或为SIGCHLD设置SA_NOCLDWAIT标志。

上述方法,在高并发场景下仍存在缺陷,SIGCHLD信号在上面代码的作用,其实只是通知进行waitpid调用,waitpid循环处理所有已经终止的子进程。假设一种情况,服务端支持最大2k的并发,满负载时2k个客户端同时退出,按上面例子会有大量信号丢失,产生的僵死进程在下次父进程收到SIGCHLD信号时得到清理,此时服务端只能提供少量并发请求(最坏情况2个并发);

简短描述一下该问题:高并发下,上述代码的waitpid在清理僵死进程时不及时。

一种解决办法是我们自己来维护子进程的退出状态。创建一个队列,子进程退出时将pid入队列,在线程中阻塞读并处理队列。

时间: 2024-11-18 10:40:52

SIGCHLD 信号处理的相关文章

SIGCHLD waitpid, 在ndk开发简直就在踩屎坑

原本项目中依赖子进程执行的地方,都使用jni调用java层的ProcessManager,换了c++ACE框架后,发现这些任务都很慢,调试才发现所有子进程执行的任务都必须等待到reactor超时才返回控制权.一时慌了居然怀疑是不是app进程没有收到SIGCHLD信号,所以调试跟踪了一下内核,信号正常. 既然收到了信号,那么去了哪里呢? 这里要说明一下ACE框架, ACE_Process_Manager会在handle_signal处理SIGCHLD信号,然后向reactor发一个notify,从

nobup 与 后台运行命令

1. Linux进程状态:R (TASK_RUNNING),可执行状态&运行状态(在run_queue队列里的状态) 2. Linux进程状态:S (TASK_INTERRUPTIBLE),可中断的睡眠状态, 可处理signal 3. Linux进程状态:D (TASK_UNINTERRUPTIBLE),不可中断的睡眠状态, 可处理signal, 有延迟 4. Linux进程状态:T (TASK_STOPPED or TASK_TRACED),暂停状态或跟踪状态, 不可处理signal, 因为根

对于linux下system()函数的深度理解(整理)

对于linux下system()函数的深度理解(整理) (2013-02-07 08:58:54) 这几天调程序(嵌入式linux),发现程序有时就莫名其妙的死掉,每次都定位在程序中不同的system()函数,直接在shell下输入system()函数中调用的命令也都一切正常.就没理这个bug,以为是其他的代码影响到这个,或是内核驱动文件系统什么的异常导致,昨天有出现了这个问题,就随手百了一下度,问题出现了,很多人都说system()函数要慎用要少用要能不用则不用,system()函数不稳定?

一、进程与信号之僵尸进程

孤儿进程:父进程被终结,子进程成为孤儿进程,被init进程接管 僵尸进程:子进程被终结,内存未被释放,形成僵尸进程 #include <unistd.h> #include <stdlib.h> #include <stdio.h> int main(void) { pid_t pid; pid=fork(); if(pid<0) { printf("fork error"); exit(1); } else if(pid ==0) { //终

linux 如何清理僵尸进程

今天在维护服务器的时候,发现有5个nova-novncproxy的僵尸进程. 26327 ?        S      0:05  \_ /usr/bin/python /usr/bin/nova-novncproxy --config-file=/etc/nova/nova.conf 4765 ?        Z      0:00      \_ [nova-novncproxy] <defunct> 4766 ?        Z      0:00      \_ [nova-no

Linux的system()和popen()差异

Linux的system()和popen()差异 1. system()和popen()简介 在linux中我们可以通过system()来执行一个shell命令,popen()也是执行shell命令并且通过管道和shell命令进行通信. system().popen()给我们处理了fork.exec.waitpid等一系列的处理流程,让我们只需要关注最后的返回结果(函数的返回值)即可. 2. system().popen()源码 首先我们来看一下这两个函数在源码(伪代码)上面的差异. int s

僵尸进程

In UNIX System terminology, a process that has terminated,but whose parent has not yet waited for it, is called a zombie. 在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 那么他将变成一个僵尸进程. 但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程, 因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进

linux 中的进程wait()函数

转载自:http://blog.sina.com.cn/s/blog_7776b9d3010144f9.html 在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 那么他将变成一个僵尸进程. 但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程, 因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程, 看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init 来接管他,成为他的父进程-- 一个进程在调用exit

php多进程 防止出现僵尸进程

对于用PHP进行多进程并发编程,不可避免要遇到僵尸进程的问题. 僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程(zombie)进程.任何进程在退出前(使用exit退出) 都会变成僵尸进程(用于保存进程的状态等信息),然后由init进程接管.如果不及时回收僵尸进程,那么它在系统中就会占用一个进程表项,如果这种僵尸进程过多,最后系统就没有可以用的进程表项,于是也无法再运行其它的程序. 方法一: 父进程通过pcntl_wait和pcntl_waitpid等函数等待子进程