c - 在 popen 函数调用后睡眠在子进程中不起作用
问题描述
我编写了一个小的 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);
}
当我运行上述程序时,sleep #1 工作并且进程在给定的时间内睡眠。我通过观察打印命令结果(通过 printf 语句)所需的时间来了解这一点。但是对于睡眠#2 和睡眠#3,我希望进程进入睡眠状态,但这似乎并没有发生,因为命令的结果会立即打印在控制台上。
可以通过仅保留 sleep #2 或 sleep #3 调用并删除其他两个 sleep 调用来观察该行为。
有谁知道为什么会这样?
解决方案
popen()
通过分叉一个孩子来执行/bin/sh -c "uname -a"
。当那个子进程退出时,进程会收到一个SIGCHLD
信号。这会中断sleep()
调用并运行您的reaper
函数。
我在任何 Linux 或 POSIX 文档中都找不到提到的这一点,但它在SunOS 文档中:
的信号处理程序
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);
}
推荐阅读
- sparql - 绑定到由 VALUES 和大括号提供的内联数据
- c# - 需要帮助关闭控制面板
- macos - 如何使 NSToolbar 项目切换状态
- spring-cloud-feign - 如何将阻塞的 Feign 客户端转换为反应性的 Feign 客户端
- haskell - 为什么递归比使用此随机数计数器的过滤器慢
- google-maps - 谷歌距离矩阵 API 替换甚至不接近的地址
- pandas - Pandas 根据条件列对行子集进行排名
- python - 在小部件中放大和缩小
- c - 如何在共享内存中保存结构 C - Windows
- twitter-bootstrap - Bootstrap:在图片上添加按钮