一、引言

        上一文中介绍了利用wait函数和waitpid函数来销毁僵尸进程,本文主要介绍利用Linux中的信号处理机制来销毁僵尸进程。linux中的信号处理类似于windows中的消息处理,基本的编程步骤就是先在系统中注册信号和对应的信号处理函数,我们用代码或者 系统自动产生注册的信号时会调用该信号处理函数。我们下面要介绍的signal函数和sigaction函数实际上就是我们用来向操作系统注册信号和信号处理函数的api。

二、signal函数

       signal函数的原型如下:

#include <signal.h>
typedef void (*sighandler_t)(int);
void signal(int signum, sighandler_t handler);

signum,要处理的信号,一般用宏来表示,如:
    SIGALRM,通过alarm函数触发的闹钟信号; 
    SIGINT,输入Ctrl+C时系统自动产生的信号;
    SIGCHIL,子进程终止 时产生的信号;
handler,信号处理器,就是我们自己定义的信号处理函数的函数指指针。 
 
    这里简单介绍下alarm函数:

#include <unistd.h>
 unsigned int alarm(unsigned int seconds);

       该函数接收一个int类型的参数,表示过了参数传递的秒之后,就会产生一个SIGALRM信号,当传给其0时,表示之前预约的SIGALRM将取消。特别指出的是如果调用该函数时,未指定对应的SIGALRM信号处理器,该函数将会迫使进程强制退出,此时就类似于exit()的作用了。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
void timeout(int sig)
{
    if(sig==SIGALRM)
            puts("Time out");
    alarm(2);
}
void keycontrol(int sig)
{
    if(sig==SIGINT)
            puts("CTRL + C pressed");
}
int main()
{
    int i;
    signal(SIGALRM,timeout);
    signal(SIGINT,keycontrol);
    alarm(2);
    for(i=0;i<3;i++)
    {
        puts("wait...");
        sleep(100);
    }
    return 0;
}

       上述代码实现的功能是分别用signal注册SIGALRM和SIGINT的信号处理函数timeout和keycontrol。用alarm函数注册一个闹钟,2秒之后发送一个SIGALRM信号,然后到达2秒后,timeout被调用,打印出字符串“Time out”。如此反复3次。如果用户按下了Ctrl+C,会产生一个SIGINT信号,该信号的处理函数keycontrol会打印一个字符串“CTRL + C pressed”。我们先来看下结果:

[Hyman@Hyman-PC csdn]$ ./a.out 
wait...
Time out
wait...
^CCTRL + C pressed
wait...
Time out

另外需要注意的是:
1、按下Ctrl + C时,信号会被signal捕获,而不会再终止进程。
2、调用信号处理器时将会唤醒正在sleep中的程序,也就是说上述程序不会再sleep 100秒
 

二、sigaction函数

        sigaction函数完全可以替代signal函数,而且使用sigaction函数还有一个好处就是:signal函数会随着Unix版本的不同而不同,但是每个版本的sigaction的都是一样的。sigacton的原型如下:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

signum,和signal的参数一样,传递的都是信号信息。 
act,sigaction结构体,里面包含信号处理函数等信息。 
oldact,通过此参数获取之前注册的sigaction结构体指针,如果不需要,直接设置0
 

先看一下sigaction结构体:

struct sigaction
{
    void (*sa_handler)(int);
    sigset_t sa_mask;
    int sa_flags;
}
sa_handler就是我们需要处理信号的信号处理函数指针,其他两个参数用来指定信号相关的选项和特性,一般情况下指定为0即可,指定的方法将下面的代码。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>

void timeout(int sig)
{
    if(sig==SIGALRM)
            puts("Time out");
    alarm(2);
}

int main()
{
    int i=0;
    struct sigaction act;
    act.sa_handler=timeout;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    sigaction(SIGALRM,&act,0);
    alarm(2);
    for(i=0;i<3;i++)
    {
        puts("wait ...");
        sleep(100);
    }
    return 0;
}

上述代码实现了和上面signal的代码同样的功能,只不过没有处理SIGINT信号,结果我们不再展示。下面主要介绍下利用sigaction处理子进程的结束:

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>

void childproHandler(int sig)
{
    int status;
    pid_t pid;
    if(sig==SIGCHLD)
    {
        pid = waitpid(-1,&status,WNOHANG);
        if(WIFEXITED(status))
        {
            printf("removed prc id:%d \n",pid);
            printf("child send:%d \n",WEXITSTATUS(status));
        }
    }
}

int main()
{
    pid_t pid;
    struct sigaction act;
    act.sa_handler=childproHandler;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    sigaction(SIGCHLD,&act,0);
    pid=fork();
    if(pid==0)
    {
        puts("I'm child process");
        sleep(10);
        return 12;
    }
    else
    {
        printf("child process id %id\n",pid);
        pid=fork();
        if(pid==0)
        {
            puts("I'm child process");
            sleep(10);
            exit(24);
        }
        else
        {
            int i=0;
            printf("child process id:%d\n",pid);
            for(i=0;i<5;i++)
            {
                puts("wait ...");
                sleep(5);
            }
        }
    }
    return 0;
}

第6行我们定义了一个SIGCHIL的信号处理函数,在这个函数中我们用waitpid来回收子进程资源。请注意signal和sigaction只是信号处理函数的注册api,其本身没有销毁僵尸进程的作用,我们还需要借助wait或者waitpid来销毁僵尸进程。

第24到28行,分别定义sigaction结构体变量以及利用sigaction注册信号已经对应的信号处理函数。

第29和39行,分别fork一个子进程,等到这两个子进程结束,操作系统就会产生一个SIGCHIL信号,就会调用我们定义的信号处理函数,输出我们定义的打印信息。

        结果如下:

[Hyman@Hyman-PC csdn]$ ./a.out 
child process id 2472d
child process id:2473
wait ...
I'm child process
I'm child process
wait ...
wait ...
removed prc id:2472 
child send:12 
wait ...
wait ...
[Hyman@Hyman-PC csdn]$ 

Github位置:

https://github.com/HymanLiuTS/NetDevelopment

克隆本项目:

git clone git@github.com:HymanLiuTS/NetDevelopment.git

获取本文源代码:

git checkout NL13


Logo

更多推荐