信号的基本操作

ctrl + c  --> 2)SIGINT 
ctrl +   --> 3)SIGQUIT

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

#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 
", num);
    pid = wait(NULL);
    printf("wait : %u 
", 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 ! 
");
        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 ;
}

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

如果按ctrl+d,则父进程退出while循环,并且退出程序。此时第一个子进程成为孤儿进程,被init收养,子进程退出时,会向init发送SIGCHLD 信号,由init回收资源。

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

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

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

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

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

原文地址:https://www.cnblogs.com/hxjbc/p/3962165.html