避免僵死进程

http://blog.csdn.net/divlee130/article/details/49048881中 如果服务器主机子进程终止会给父进程发送一个sigchld 信号,但是父进程并没有处理该信号,所以子进程进入僵死状态,

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

一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被
销毁, 而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是 使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)

#include <time.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <signal.h>
void str_echo(int sockfd)
{

    ssize_t n;
    char buf[1024];

    while((n = read(sockfd, buf, 1024)) > 0 )
    {
        write(sockfd, buf, n);
    }

    if(n < 0)
    {
        printf("read error\r\n");
    }
}

void sig_chld(int signo) 
{ 
       pid_t   pid; 
       int     stat; 

       //没有已终止进程时不阻塞
       while((pid = waitpid(-1, &stat, WNOHANG)) > 0){ 
               printf("child %d terminated\n", pid); 
       } 
        return; 
} 

int main(int argc, char** argv)
{
    int listenfd, connectfd;
    socklen_t client_len;
    struct sockaddr_in servaddr, client_addr;
    pid_t childpid;

    if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("sock error\r\n");
        return 0;
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(13000);
    servaddr.sin_addr.s_addr = inet_addr("0.0.0.0");

    if( bind(listenfd, (struct sockaddr* )&servaddr, sizeof(servaddr)) < 0)
    {
        printf("bind error\r\n");
        return 0;
    }

    if(listen(listenfd, 0) < 0)
    {
        printf("listen error\r\n");
        return 0;
    }

    signal(SIGCHLD, sig_chld);  
    for( ; ;)
    {
        client_len = sizeof(client_addr);
        connectfd = accept(listenfd, (struct sockaddr*)&client_addr, &client_len);

        if( (childpid = fork())== 0)
        {
            close(listenfd);
            str_echo(connectfd);
            exit(0);
        }

        close(connectfd);
    }
}

提问者:
看unix网络编程第一卷的时候,碰到书上这样一个例子:
一个并发服务器, 每一个客户端连接服务器就fork一个子进程.书上讲到当同时有n多个客户端断开连接时,
服务器端同时有n多个子进程终止, 这时候内核同时向父进程发送n多个sigchld信号.它的sigchld信号处理
函数如下:
void sig_chld(int signo)
{
pid_t pid;
int stat;
while((pid = waitpid(-1, &stat, WNOHANG)) > 0){
printf(“child %d terminated\n”, pid);
}
return;
}

我的问题是:既然sigchld是不可靠的信号,进程就不可能对sigchld进行排队, 直接丢弃了sigchld信号(当进程注册信号的时候,发现已有sigchld注册进未决信号, 因为内核同时发送多个sigchld).请问大家上面的代码是如何保证不产生僵尸进程的.谢谢!

牛人正解:

根本就不需要找回来!
好比有五个进程,
不妨分别称为 p1 p2 p3 p4 p5,
一开始 p1 结束了,发了一个 SIGCHLD(s1),
这时父进程可能空闲了,于是开始处理这个信号,假设处理的过程中 p2 又结束了,又发了一个 SIGCHLD(s2),
这时候已经有两个信号了(一个正在处理,一个待处理),这时如果 p3 又结束了,那么它发的那个 SIGCHLD(s3) 势必会丢失,
丢失了怎么办?
没关系,因为那个信号处理函数是个循环嘛,
所以 while(waitpid()) 的时候,会把 p1 p2 p3 都处理的。
即使是很不幸,因为十分凑巧的原因,p3 没有被回收,导致变成僵尸进程了,也没关系,
因为还有 p4 p5 嘛,等到 p4 或者 p5 结束的时候,
又会再一次调用 while(waitpid()),到时候虽说这个 while(waitpid()) 是由 p4/p5 引起的,但是它也会一并把 p3 也处理的,因为它是个循环嘛!

如果还搞不懂,你就再看看 waitpid 的 man。

记住一点:
waitpid 和 SIGCHLD 没关系,即使是某个子进程对应的 SIGCHLD 丢失了,只要父进程在任何一个时刻调用了 waitpid,那么这个进程还是可以被回收的。

哎呀呀,简直费劲死了,其实说白了,就是一个“生产者-消费者”问题。
子进程结束的时候,系统“生产”出一个僵尸进程,
同时用 SIGCHLD 通知父进程来“消费”这个僵尸进程,
即使是 SIGCHLD 丢失了,没有来得及消费,
但是只要有一次消费,就会把所有的僵尸进程都处理光光!
(我再说一遍:因为,while(waitpid()) 是个循环嘛!)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值