后台进程退出的速度快于我添加其 pid 进行管理的速度

发布于 2024-09-26 12:27:53 字数 1111 浏览 7 评论 0原文

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

我缺少什么?

谢谢。

I'm creating background processes in C using fork().

When I created one of these processes, I add its pid to an array so I can keep track of background processes.

    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]);
    }

I have a handler for reaping zombies

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);
    }    
}

When I create a background process, the handler is executing and attempting to clean up the child before I can even make the call to addBGroundProcess.

I'm using commands like emacs& which should not be exiting immediately.

What am I missing?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

野侃 2024-10-03 12:27:53

你是对的,那里存在竞争条件。我建议您使用 阻止发送 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 可能会失败。 (一般情况下处理这个问题是很好的,即使在这种情况下没有发生。)这完全取决于它的实现方式,但我不认为你可以在上面添加 &命令的结尾以使其在后台运行。无论如何,在这种情况下,单独运行 emacs 可能就是您想要的,而将 & 放在命令行末尾是 shell 提供的一项功能。

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

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

    execvp(*args, args);
    perror("execvp failed");
    _exit(127);

You're right, there is a race condition there. I suggest that you block the delivery of SIGCHLD using the sigprocmask function. When you have added the new PID to your data structure, unblock the signal again. When a signal is blocked, if that signal is received, the kernel remembers that it needs to deliver that signal, and when the signal is unblocked, it's delivered.

Here's what I mean, specifically:

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);
}

Also, I think there's a possibility that execvp could be failing. (It's good to handle this in general, even if it's not happening in this case.) It depends exactly how it's implemented, but I don't think that you're allowed to put a & on the end of a command to get it to run in the background. Running emacs by itself is probably what you want in this case anyway, and putting & on the end of a command line is a feature provided by the shell.

Edit: I saw your comments about how you don't want emacs to run in the current terminal session. How do you want it to run, exactly - in a separate X11 window, perhaps? If so, there are other ways of achieving that.

A fairly easy way of handling execvp's failure is to do this:

    execvp(*args, args);
    perror("execvp failed");
    _exit(127);
昵称有卵用 2024-10-03 12:27:53

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

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

Your code just catches the exit of the child process it fork'ed, which is not to say that another process wasn't fork'ed by that child first. I'm guessing that emacs in your case is doing another fork() on itself for some reason, and then allowing the initial process to exit (that's a trick daemons will do).

The setsid() function might also be worth looking at, although without writing up some code myself to check it I'm not sure if that's relevant here.

陈甜 2024-10-03 12:27:53

您不应将 shell 与 & 一起使用来运行后台进程。如果你这样做,他们就会成为你无法追踪和等待的孙子。相反,您需要模仿 shell 在您自己的代码中运行后台进程的操作,或者关闭终端(或者更确切地说 stdin/out/err)并打开 /dev/null< /code> 放在子进程中的位置,这样它们就不会尝试写入终端或控制它。

You should not be using the shell with & to run background processes. If you do that, they come out as grandchildren which you cannot track and wait on. Instead you need to either mimic what the shell does to run background processes in your own code, or it would probably work just as well to close the terminal (or rather stdin/out/err) and open /dev/null in its place in the child processes so they don't try to write to the terminal or take control of it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文