Linux信号处理2

引言

先看以下两个信号量:

13)SIGPIPE     当管道读端关闭,再往管道写东西,会发出SIGPIPE信号
17)SIGCHLD   子进程退出会向父进程发出SIGCHLD信号,系统默认处理是忽略掉该信号

代码

/*************************************************************************

    > File Name: my_fork.c

    > Author: KrisChou

    > Mail:[email protected] 

    > Created Time: Mon 25 Aug 2014 10:42:26 AM CST

 ************************************************************************/



#include<stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <sys/wait.h>

#include <signal.h>

void handler(int num)

{

    pid_t pid ;

    printf("sig_num: %d \n", num);

    pid = wait(NULL);

    printf("wait : %u \n", pid);

}

int main(int argc, char* argv[])

{

    int fds[2];// fds[0] r  fds[1] w

    pipe(fds);

    char buf[1024];

    signal(SIGPIPE, handler);

    signal(SIGCHLD, handler);

    if(fork() == 0)

    {

        close(fds[1]);

        while(memset(buf, 0, 1024), read(fds[0], buf, 1024))

        {

            write(1, buf, strlen(buf));

        }

        printf("child over ! \n");

        exit(0);

    }

    if(fork() == 0)

    {

        exit(0);

    }

    if(fork() == 0)

    {

        exit(0);

    }

    close(fds[0]) ;

    while(memset(buf, 0, 1024), read(0, buf, 1024))

    {

        write(fds[1], buf, strlen(buf));

    }

    //while(1) ;

    return 0 ;

}

运行结果如下:

[purple@localhost review]$ ./a.out

sig_num: 17

wait : 15693

sig_num: 17

wait : 15692

hello

hello

world!

world!

child over !

程序运行时,先是两个子进程先后退出,向父进程发送SIGCHLD 信号,接着执行handler函数,回收两个子进程资源。接着程序执行到父进程的while循环,由于read是阻塞函数,在我们没有按下enter或者ctrl+d之前,时间片会在第一个子进程和父进程之间来回切换。如果输入字符按enter,那么也就是父进程将其写入管道,子进程将其从管道中取出,并显示在屏幕上。

如果按ctrl+d,则父进程退出while循环,并且退出程序。此时第一个子进程成为孤儿进程,被init收养,子进程退出时,会向init发送SIGCHLD 信号,由init回收资源。(注意,init很猛的,它本身就已经将SIGCHLD 注册了,其信号处理函数实现了回收资源)

干货

我们之所以将回收子进程资源的wait函数写在信号处理函数中,是因为wait是阻塞函数。如果父进程阻塞了就不能处理自己的工作了。

之前我们已经提过,当子进程终止时,会给父进程发送SIGCHLD信号,虽然该信号的默认处理动作是忽略,但是父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程,当子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程资源即可。

通常情况下服务器是永远不会退出的,因此我们可以在之前的程序中 return 0 前加个while(1),以此来模拟服务器。

注意

如果父进程先于子进程退出,则子进程成为孤儿进程,此时将自动被PID为1的进程(即init)接管。孤儿进程退出后,它的清理工作有祖先进程init自动处理。但在init进程清理子进程之前,它一直消耗系统的资源,所以要尽量避免。

如果子进程先退出,系统不会自动清理掉子进程的环境,而必须由父进程调用wait或waitpid函数来完成清理工作,如果父进程不做清理工作,则已经退出的子进程将成为僵尸进程(defunct),在系统中如果存在的僵尸(zombie)进程过多,将会影响系统的性能,所以必须对僵尸进程进行处理。

你可能感兴趣的:(linux)