分类:
2008-12-08 12:17:38
10.18 system function
Posix.1要求,调用system函数时要ignore SIGINT和SIGQUIT,并且要block
SIGCHLD。因为:
1.System其实是启动一个sh,然后用这个sh去exec
一个程序,并且system本身父进程会waitpid生成的子进程
2.SIGINT和SIGQUIT如果用terminal
device发送一般都是发送给一个process group的,所以调用system函数的caller进程和被执行的程序进程都回收到该信号。
2.SIGINT和SIGQUIT应该给被执行的程序,而不是调用system的caller进程,所以caller进程应该忽略他们,由被执行的程序去处理他们。
3.由于在system函数内部,caller进程会waitpid被执行的程序子进程,所以他通过waitpid得到了sh的执行结果,并返回。如果该程序结束后,向父亲发送的SIGCHLD被捕获,父亲当作自己一个普通的孩子结束了,就有可能去调用waitpid,从而提前将该子进程的返回status破坏,这样system里面的waitpid就不能有效得到他了,system就不能返回正确的直了。
下面我们列举出posix.1的system函数的实现代码:
#include
#include
#include
#include
int
system(const char *cmdstring) /*
with appropriate signal handling */
{
pid_t pid;
int status;
struct sigaction ignore, saveintr, savequit;
sigset_t chldmask, savemask;
if (cmdstring == NULL)
return(1); /* always a command processor with UNIX
*/
ignore.sa_handler =
SIG_IGN; /* ignore SIGINT and SIGQUIT
*/
sigemptyset(&ignore.sa_mask);
ignore.sa_flags = 0;
if (sigaction(SIGINT, &ignore,
&saveintr) < 0)
return(-1);
if (sigaction(SIGQUIT,
&ignore, &savequit) < 0)
return(-1);
sigemptyset(&chldmask); /* now block SIGCHLD */
sigaddset(&chldmask,
SIGCHLD);
if (sigprocmask(SIG_BLOCK,
&chldmask, &savemask) < 0)
return(-1);
if ((pid = fork()) < 0) {
status = -1; /* probably out of processes */
} else if (pid == 0) { /* child */
/* restore previous signal
actions & reset signal mask */
sigaction(SIGINT,
&saveintr, NULL);
sigaction(SIGQUIT,
&savequit, NULL);
sigprocmask(SIG_SETMASK,
&savemask, NULL);
execl("/bin/sh", "sh",
"-c", cmdstring, (char *)0);
_exit(127); /* exec error */
} else { /* parent */
while (waitpid(pid,
&status, 0) < 0)
if (errno != EINTR) {
status = -1; /* error
other than EINTR from waitpid() */
break;
}
}
/* restore previous signal
actions & reset signal mask */
if (sigaction(SIGINT,
&saveintr, NULL) < 0)
return(-1);
if (sigaction(SIGQUIT,
&savequit, NULL) < 0)
return(-1);
if (sigprocmask(SIG_SETMASK,
&savemask, NULL) < 0)
return(-1);
return(status);
}
我们分析一下有几个进程:
执行命令:./a.out “sleep
1.Shell会fork出一个进程,紧接着exec为a.out,此时一个进程
2.a.out执行到system调用,system里fork出一个进程,此时有2个a.out进程
3.孩子a.out调用execl
sh …变成一个sh进程,此时还是2个进程
4.Sh进程此时有参数-c “sleep
5.这个刚被fork出的进程会立马exec 成sleep
30,至此还是3个进程。
如果sleep异常中止,那么他的父亲sh会知道,sh会正常终止,system得到正常终止的结果。
如果sh异常中止,那么system 会得到异常中止的结果。
System的返回值是sh的返回值。
Note: 在linux suse上,我只看到了2个进程。一个是a.out,一个是sleep
30, 我并没有看到那个 中间的sh.