4

我正在使用 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& 这样不应该立即退出的命令。

我错过了什么?

谢谢。

4

3 回答 3

4

你是对的,那里有一个竞争条件。我建议您阻止SIGCHLD使用该sigprocmask功能的交付。将新的 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可能会失败。(一般来说处理这个很好,即使在这种情况下没有发生。)这完全取决于它是如何实现的,但我不认为你可以&在命令的末尾加上 a 来得到它在后台运行。无论如何,在这种情况下,自行运行emacs可能是您想要的,并且放在&命令行末尾是 shell 提供的功能。

编辑:我看到您关于您不希望 emacs 在当前终端会话中运行的评论。您希望它如何运行,确切地说——也许是在一个单独的 X11 窗口中?如果是这样,还有其他方法可以实现这一目标。

处理 's 失败的一个相当简单的方法execvp是这样做:

    execvp(*args, args);
    perror("execvp failed");
    _exit(127);
于 2010-10-03T01:55:49.450 回答
0

您不应该使用 shell&来运行后台进程。如果你这样做,他们就会成为你无法追踪和等待的孙辈。相反,您需要模仿 shell 在您自己的代码中运行后台进程所做的事情,或者关闭终端(或者更确切地说是 stdin/out/err)并/dev/null在子进程中打开它的位置可能同样有效,所以他们不会尝试写入终端或控制它。

于 2010-10-03T01:59:38.713 回答
0

您的代码只是捕获了它派生的子进程的退出,这并不是说该子进程没有首先派生另一个进程。我猜在你的情况下,emacs 出于某种原因正在对自己进行另一个 fork() ,然后允许初始进程退出(这是守护进程会做的一个技巧)。

setsid() 函数也可能值得一看,尽管我自己没有编写一些代码来检查它,但我不确定这是否与此处相关。

于 2010-10-03T01:13:34.760 回答