您的位置:首页 > 理论基础 > 计算机网络

网络编程(13)—— 利用信号处理函数signal和sigaction销毁僵尸进程

2016-10-09 23:10 639 查看

一、引言

        上一文中介绍了利用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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: