后台进程退出的速度比我添加它的 pid 进行管理的速度要快
Posted
技术标签:
【中文标题】后台进程退出的速度比我添加它的 pid 进行管理的速度要快【英文标题】:Background process is exiting faster than I can add its pid for management 【发布时间】:2010-10-03 00:53:25 【问题描述】:我正在使用 fork() 在 C 中创建后台进程。
当我创建其中一个进程时,我将它的 pid 添加到一个数组中,这样我就可以跟踪后台进程。
pid = fork();
if(pid == -1)
printf("error: fork()\n");
else if(pid == 0)
execvp(*args, args);
exit(0);
else
// add process to tracking array
addBGroundProcess(pid, args[0]);
我有一个收割僵尸的处理程序
void childHandler(int signum)
pid_t pid;
int status;
/* loop as long as there are children to process */
while (1)
/* get zombie pids */
pid = waitpid(-1, &status, WNOHANG);
if (pid == -1)
if (errno == EINTR)
continue;
break;
else if (pid == 0)
break;
/* Remove this child from tracking array */
if (pid != mainPid)
cleanUpChild(pid);
当我创建后台进程时,处理程序正在执行并试图在我调用 addBGroundProcess 之前清理子进程。
我正在使用像 emacs& 这样不应该立即退出的命令。
我错过了什么?
谢谢。
【问题讨论】:
【参考方案1】:你是对的,那里有一个竞争条件。我建议您使用sigprocmask
函数阻止SIGCHLD
的传递。将新的 PID 添加到数据结构后,再次解除对信号的阻塞。当信号被阻塞时,如果接收到该信号,内核会记住它需要传递该信号,当信号被解除阻塞时,它就会被传递。
这就是我的意思,具体来说:
sigset_t mask, prevmask;
//Initialize mask with just the SIGCHLD signal
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, &prevmask); /*block SIGCHLD, get previous mask*/
pid = fork();
if(pid == -1)
printf("error: fork()\n");
else if(pid == 0)
execvp(*args, args);
exit(0);
else
// add process to tracking array
addBGroundProcess(pid, args[0]);
// Unblock SIGCHLD again
sigprocmask(SIG_SETMASK, &prevmask, NULL);
另外,我认为execvp
可能会失败。 (一般来说处理这个问题很好,即使在这种情况下没有发生。)这完全取决于它是如何实现的,但我不认为你可以在命令末尾放置一个&
让它在后台运行。在这种情况下,运行emacs
本身可能是您想要的,而将&
放在命令行的末尾是shell 提供的一项功能。
编辑:我看到您的 cmets 关于您不希望 emacs 在当前终端会话中运行的方式。您希望它如何运行,确切地说——也许是在单独的 X11 窗口中?如果是这样,还有其他方法可以实现。
处理execvp
失败的一个相当简单的方法是这样做:
execvp(*args, args);
perror("execvp failed");
_exit(127);
【讨论】:
【参考方案2】:您的代码只是捕获了它派生的子进程的退出,这并不是说该子进程没有首先派生另一个进程。我猜在你的情况下,emacs 出于某种原因正在对自己进行另一个 fork(),然后允许初始进程退出(这是守护进程会做的一个技巧)。
setsid() 函数可能也值得一看,虽然我自己没有编写一些代码来检查它,但我不确定这是否相关。
【讨论】:
说清楚,你是不是说自从我调用 emacs& (带有 &)后,Unix 将产生另一个子进程来执行后台进程 emacs,从而导致我创建的子进程死亡并引发SIGCHLD 标志? 啊……我错过了“&”。这似乎是第二次分叉的可能原因。虽然更具体地说,我认为“&”可能会导致生成一个 shell,它执行 emacs 并立即退出。请记住,这不是我的想法,所以我还没有做太多的测试。 如果我不运行 emacs&,而是运行 emacs,emacs 会在当前终端会话中打开。这是我不想要的行为。我实际上想在后台执行命令(emacs、ls 等)并跟踪这些进程并在僵尸出现时清理它们。【参考方案3】:您不应该使用带有&
的shell 来运行后台进程。如果你这样做,他们就会成为你无法追踪和等待的孙辈。相反,您需要模仿 shell 在您自己的代码中运行后台进程的操作,或者关闭终端(或者更确切地说是 stdin/out/err)并在其位置打开 /dev/null
子进程,因此它们不会尝试写入终端或控制它。
【讨论】:
您能否进一步解释或提供示例?我同意使用 & 执行是不正确的,但我不希望命令使用终端并且我不想关闭终端。 如果你不想让命令使用终端,为什么不想关闭终端呢?您将在 child 进程中关闭它,而不是在父进程中。 只是沟通不畅...我确实想关闭子进程的终端,以便只有前台进程(我的 shell)可以使用它。以上是关于后台进程退出的速度比我添加它的 pid 进行管理的速度要快的主要内容,如果未能解决你的问题,请参考以下文章