原文地址:http://www.blogbus.com/public/tb.php/1376295/4862205/d489c231edb979476798ac5d29c8d449
一般来说,多进程环境下的Linux程序,子进程是继承父进程的信号处理方式的。也就是说,如果在父进程中为某一个信号指定了处理函数,那么子进程在收到这个信号时同样会调用这个处理函数。
举例如下:
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <time.h>
void handler(int signum,siginfo_t *info,void *myact)
{
fprintf(stderr,"receive signal %d/n", signum);
fprintf(stderr,"Signal is sended by %d as reason : %d on error: %d/n",info->si_pid,info->si_code,info->si_errno);
}
int main(int argc,char**argv)
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=handler;
if(sigaction(SIGUSR1,&act,NULL) < 0)
{
printf("install sigal error/n");
}
pid_t pid=fork();
if(pid == 0){
for(int i=0;i<100;i++){
if(i==5){
fprintf(stderr,"Send SIGUSR1 signal....../n");
kill(getpid(),SIGUSR1);
}
else{
fprintf(stderr,"Sleeping....../n");
sleep(1);
}
}
return 0;
}
fprintf(stderr,"Child Process id is : %d/n",pid);
wait(NULL);
return 0;
}
但很多情况下需要在fork之后在子进程中调用exec族函数来执行一个新的程序。那这种情况下,父子进程对于信号处理的继承关系又是怎样的呢。
将上面的程序改动一下。
main.cpp:
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <time.h>
void handler(int signum,siginfo_t *info,void *myact)
{
fprintf(stderr,"receive signal %d/n", signum);
fprintf(stderr,"Signal is sended by %d as reason : %d on error: %d/n",info->si_pid,info->si_code,info->si_errno);
}
int main(int argc,char**argv)
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=handler;
if(sigaction(SIGUSR1,&act,NULL) < 0)
{
printf("install sigal error/n");
}
pid_t pid=fork();
if(pid == 0){
execlp("/root/test/sigusr","./sigusr",NULL);
return 0;
}
fprintf(stderr,"Child Process id is : %d/n",pid);
wait(NULL);
return 0;
}
将原来子进程中的语句放到一个新的文件中,并将其编译成sigusr可执行文件 :
sigusr.cpp
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
int main(){
for(int i=0;i<100;i++){
if(i==5){
fprintf(stderr,"Send SIGUSR1 signal....../n");
kill(getpid(),SIGUSR1);
}
else{
fprintf(stderr,"Sleeping....../n");
sleep(1);
}
}
}
编译执行main.cpp可以看到,子进程打印了五条sleeping......语句后就退出了,并没有调用父进程指定的信号处理函数。
分析:
1、fork生成的子进程中继承了父进程所有的数据,当然包括信号处理函数的入口地址等信息,所以当子进程继承父进程的信号处理方式不会出现问题。
2、fork后调用exec后发生了什么呢?
open group上的exec的文档是这么说的 :
The exec functions replace the current process image with a new process image. The new image is constructed from a regular, executable file called the new process image file . There is no return from a successful exec , because the calling process image is overlaid by the new process image.
exec 后,系统用参数指定的可执行文件的映像将原由子进程完全替换掉,只留下进程号等一小部分信息不变,这样的话在exec后子进程中已经不存在父进程中的数 据,子进程也无法得知父进程信号处理函数的入口地址。在这种情况下,继承父进程的信号处理机制也就无从谈起了,因为这是不可能的。但有一个例外情况,那就 是如果在父进程中将对某个信号的处理方式设置为忽略(SIG_IGN),那么exec后的子进程会继承父进程的设置,在子进程中忽略这个信号。因为设置为 忽略的话就不涉及到寻找处理函数入口地址了,这是可行的。
Open Group对此的描述如下:
Signals set to the default action (SIG_DFL) in the calling process image are set to the default action in the new process image. Signals set to be ignored (SIG_IGN) by the calling process image are set to be ignored by the new process image. Signals set to be caught by the calling process image are set to the default action in the new process image. After a successful call to any of the exec functions, alternate signal stacks are not preserved and the SA_ONSTACK flag is cleared for all signals.
还有一种情况需要注意,如果在父进程中调用了atexit()函数,那么在exec后的子进程中同样会失效,原理和上面是一样的。
Open Group The Single UNIX ® Specification, Version 2,exec:
http://www.opengroup.org/onlinepubs/007908799/xsh/exec.html