#include <sys/wait.h>
#include <erron.h>
#include <signal.h>
#include <unistd.h>
int system(const char* cmdstring)
{
pid_t pid;
int status;
struct sigaction ignore,saveintr,savequit;
sigset_t chldmask,savemask;
if(cmdstring==NULL)
return 1;
ignore.sa_handler=SIG_IGN;
if(sigaction(SIGINT,&ignore,&saveintr)<0)
return -1;
if(sigaction(SIGQUIT,&ignore,&savequit)<0)
return -1;
sigemptyset(&chldmask);
sigaddset(&chldmask,SIGCHLD);
if(sigpromask(SIG_BOLCK,&chllmask,&savemask)
return -1;
if((pid=fork())<0)
status=-1;
else if(pid==0)
{
sigaction(SIGINT,&saveintr,NULL);
sigaction(SIGQUIT,&savequit,NULL);
sigpromask(SIG_SETMASK,&savemask,NULL);
execl("/bin/sh","sh","-c",cmdstring,(char*)0);
_exit(127);
}
else {
while(waitpid(pid,&status,0)<0)
{
if(errno!=EINTR)
{
status=-1;
break;
}
}
if(sigaction(SIGINT,&saveintr,NULL)<0)
return -1;
if(sigaction(SIGQUIT,&savequit,NULL)<0)
return -1;
if(sigpromask(SIG_SETMASK,&savemask,NULL)<0)
return -1;
return status;
}
上面的是apue中对system函数加信号处理函数的实现,其中调用进程忽略了SIGINT和SIGQUIT信号,以及阻塞了SIGCHLD信号,并且书上举了一个ed编辑器的例子来说明system函数的用法,这里需要注意的第一点就是调用进程通过fork创建的进程的函数是/bin/sh这个子shell,然后又因为ed不是shell的内建命令,从而又是fork调用创建了一个新的进程,这个进程的函数才是ed,这里所说的就是system设置的是调用进程和/bin/sh这两个进程对信号的不同处理方式。但是我在这里一直存在着一个疑问,那就是在if(pid==0) 之后的那两行,我记得apuep242上面说到当exec函数时,exec函数会将原先设置为要捕捉的信号更改为它们的默认动作,其他信号的状态不变。 如果是这样的话,那么这两行有什么用呢?
下面说说为什么要对SIGCHLD加上阻塞:
apue的p277上面这么说:如果父进程正在捕捉SIGCHLD信号,那么正在执行system函数的时候,应当阻塞对父进程递送SIGCHLD信号.否则,当system创建的子进程结束的时候,system的调用者可能错误的认为,它自己的一个子进程结束了.于是,调用者将会调用一种wait函数以获得子进程的终止状态,这样就阻止了system函数获得子进程的终止状态,并将其作为返回值。
上面的话我是有些听得不是很懂,所以自己就往里面加了点东西,首先是父进程设置了捕捉SIGCHLD信号的处理函数,并且处理函数中调用waipid函数来获得子进程的结束状态。如果调用 者进程没有将SIGCHLD信号设置为阻塞位,也就是上面的system函数中没有设置(或者是采用类似于第八章的未加信号处理的system函数),则当system创建的子进程结束的时候,也就是ed编译器进程结束的时候,调用者进程的SIGCHLD信号处理函数会捕捉到这个信号,然后waitpid获得子进程的终止状态,然后再为子进程收尸。。当执行完这个信号处理程序之后,会继续执行调用者进程的其余部分,显然这个时候调用者进程被停止在waitpid的地方,但是这个时候pid对应的子进程的终止状态在信号处理函数中已经被处理了,这里再等下去也没什么意思,就会返回错误了,所以我们需要阻塞在调用者进程中阻塞对SIGCHLD信号的处理,只有当调用者进程waitpid获得子进程的状态之后,再接触对该信号的阻塞,这个时候信号处理函数才能起作用,但是这个时候信号处理函数中的waitpid也会出错。注意的是ed编译器进程结束的时候,会向所以的前台进程组成员发信号(当然同属于唯一的一个前台进程组),这个时候调用者进程是阻塞,shell子进程是忽略,因为有execl的复位操作。
apue上面的对于SIGINT和SIGSTOP信号的处理为什么是忽略我不是很了解,p278上面的解释也没明白,希望大神求踩。。