我写了一个小的C程序,它创建一个子进程,然后使用popen运行shell命令。我创建了一个信号处理程序,以等待子进程结束,而父进程只是在无限循环中运行。当我在popen系统调用之后调用“ sleep(5)”时,sleep调用不起作用。
相反,如果我将睡眠呼叫放在popen之前,那么它将起作用。另外,如果我删除信号处理程序并只等待父进程中的子进程,那么就没有问题(即,睡眠在任何地方都可以正常运行)。
//Assume all required header files and function prototypes added
int main()
{
int pid,status;
signal(SIGCHLD,reaper);
pid = fork();
if(pid == 0)
runCommand();
while(1);
}
void runCommand()
{
FILE *pipen;
char *buffer;
buffer = (char*) malloc(100);
/*sleep #1*/ sleep(5);
if ((pipen = popen("uname -a", "r")) == NULL) {
perror("\nError during shell command execution: ");
exit(0);
}
/*sleep #2*/ sleep(5);
fgets(buffer, 100, pipen);
/*sleep #3*/ sleep(5);
printf("\n%s\n",buffer);
exit(0);
}
void reaper(int signal)
{
int status;
waitpid(-1,&status,WNOHANG);
}
当我运行上述程序时,睡眠#1起作用,并且该进程在给定时间内睡眠。我通过观察打印命令结果(通过printf语句)所花费的时间来了解这一点。 但是对于#2睡眠和#3睡眠,我希望该过程能够进入睡眠状态,但这似乎并没有发生,因为命令的结果会立即打印在控制台上。
可以通过仅保留#2睡眠或#3睡眠呼叫并删除其他两个睡眠呼叫来观察该行为。
有人知道为什么会这样吗?
答案 0 :(得分:3)
popen()
通过分叉孩子执行/bin/sh -c "uname -a"
来工作。当那个孩子退出时,该过程接收到SIGCHLD
信号。这会中断sleep()
调用并运行reaper
函数。
我在任何Linux或POSIX文档中都找不到提到的内容,但是它在SunOS documentation中:
使用
SIGCHLD
时,popen()
的信号处理程序应设置为默认值。如果进程已为SIGCHLD
建立了信号处理程序,则在命令终止时将调用该处理程序。如果信号处理程序或同一进程中的另一个线程发出wait(2)
调用,它将干扰pclose()
的返回值。如果已将SIGCHLD
的进程信号处理程序设置为忽略信号,则pclose()
将失败,并且errno将被设置为ECHILD
。
您应将SIGCHLD
处理程序设置为子进程的默认值。
void runCommand()
{
FILE *pipen;
char *buffer;
signal(SIGCHLD, SIG_DFL);
buffer = (char*) malloc(100);
/*sleep #1*/ sleep(5);
if ((pipen = popen("uname -a", "r")) == NULL) {
perror("\nError during shell command execution: ");
exit(0);
}
/*sleep #2*/ sleep(5);
fgets(buffer, 100, pipen);
/*sleep #3*/ sleep(5);
printf("\n%s\n",buffer);
exit(0);
}