【问题标题】:Is there a way to capture the return status of a process without waiting for it?有没有办法在不等待的情况下捕获进程的返回状态?
【发布时间】:2012-02-04 21:10:57
【问题描述】:

我想要做的是让一个子进程在后台运行,而父进程去做其他事情。当孩子回来时,我想让父母得到那个状态并用它做点什么。但是,我不想在任何时候明确地等待孩子。

我查看了 waitpid 的 WNOHANG 选项,但这似乎与我正在寻找的有点不同。 WNOHANG 仅在完成后才获得状态,否则继续。理想情况下,我想要一些选项,它不会等待该过程,而是会在完成后跳回并获取返回状态。

有什么办法吗?

代码示例:

pid_t p = fork();
if (p == 0){
    value = do_child_stuff();
    return(value);
}

if (p > 0){
    captureStatus(p, &status); //NOT A REAL FUNCTION
                               // captureStatus will put p's exit status in status
                               // whenever p returns, without waiting or pausing for p 
    //do other stuff.....
}

有什么方法可以模拟 captureStatus 的行为吗?

【问题讨论】:

  • 您可以在单独的线程中启动,然后在需要时使用某种并行编程魔法来获得结果:)
  • 如果你不知道status 什么时候会有一个有效值,那么captureStatus(p, &status) 有什么用?
  • 是的,我现在转向 Dmitri 的观点 - 如果您要经常检查 status,为什么不在那些时候使用非阻塞 waitpid?跨度>
  • @dmitri 我意识到这是一件很奇怪的事情。它涉及一个程序,其中多个命令同时运行并且其他命令相互依赖。在依赖项之一完成执行时,依赖于它的命令将开始执行。基本上,我一直在轮询,当该状态具有有效值时,我运行另一个命令。
  • 问题是,在您知道进程已退出并且status 已设置之前,您将无法依赖status。因此,如果您正在轮询它,您也可以使用非阻塞 waitpid() ——否则您仍然需要一些 other 方法来指示 status 何时具有有效值。你怎么知道status中的垃圾值和子进程的返回值之间的区别?

标签: c process fork status


【解决方案1】:

您可以为SIGCHLDwait 的进程建立一个信号处理程序,一旦触发(它将在子进程终止或被杀死时触发)。

但是,请注意,在信号处理程序中可以完成的有用事情很少。一切都必须是异步信号安全的。 The standard 特别提到 waitwaitpid 是安全的。

【讨论】:

    【解决方案2】:

    这是从同步等待中创建异步等待的正确方法(或至少一种正确方法):

    struct waitpid_async_args {
        pid_t pid;
        int *status;
        int flags;
        sem_t sem, *done;
        int *err;
    };
    
    static void *waitpid_async_start(void *arg)
    {
        struct waitpid_async_args *a = arg;
        pid_t pid = a->pid;
        int *status = a->status, flags = a->flags, *err = a->err;
        sem_post(&a->sem);
        if (waitpid(pid, status, flags) < 0) *err = errno;
        else *err = 0;
        sem_post(a->done);
        pthread_detach(pthread_self());
        return 0;
    }
    
    int waitpid_async(pid_t pid, int *status, int flags, sem_t *done, int *err)
    {
        struct waitpid_async_args a = { .pid = pid, .status = status,
            .flags = flags, .done = done, .err = err };
        sigset_t set;
        pthread_t th;
        int ret;
        sem_init(&a.sem, 0, 0);
        sigfillset(&set);
        pthread_sigmask(SIG_BLOCK, &set, &set);
        ret = pthread_create(&th, 0, waidpid_async_start, &a);
        if (!ret) sem_wait(&a.sem);
        pthread_sigmask(SIG_SETMASK, &set, 0);
        return ret;
    }
    

    请注意,异步函数将一个信号量作为一个额外的参数,它将发布以标记它已完成。您可以只检查status,但如果没有同步对象,就无法正式保证内存排序,因此最好使用这样的方法并调用sem_trywaitsem_timedwait 并超时以检查状态是否可用在访问它之前。

    【讨论】:

      【解决方案3】:

      您可以使用shmgetshmat 等共享内存IPC 函数在两个进程之间进行通信。这可以是非阻塞的,并且可能适用于您所描述的生产者/消费者模型。不过,您仍然需要进行投票。

      shmget 应该在 fork 之前调用以创建共享内存块。 然后你可以在 fork 之后使用shmat 来获取指向共享内存的指针,然后将其用作缓冲区。 完成后在子级和父级上调用 shmdt 以分离,并在 shmctl 上删除共享内存。

      网上有一个例子here

      【讨论】:

        【解决方案4】:

        我喜欢 Vyktor 的解决方案——在父进程中,启动一个线程来阻塞子进程,并在完成后设置 status 变量。这是一个实现:

        #include <stdio.h>
        #include <unistd.h>
        #include <sys/types.h>
        #include <sys/wait.h>
        #include <pthread.h>
        
        struct return_monitor {
          pid_t pid;
          int *return_status;
        };
        
        static void
        captureStatus(struct return_monitor *monitor)
        {
          printf("Monitor thread started...\n");
          int return_status;
          waitpid(monitor->pid, &return_status, 0);
          *(monitor->return_status) = WEXITSTATUS(return_status);
        }
        
        int
        main()
        {
          printf("Parent process started...\n");
          pid_t p = fork();
          if (p == 0){
            /* child */
            printf("Child process started...\n");
            int i;
            for (i = 0; i < 10; ++i) {
              sleep(1);
              printf("Child iteration %d...\n", i);
            }
            /* arbitrary return value that will be recognizable in parent */
            return(3);
          }
        
          if (p > 0){
            int child_return_status = -1;
        
            struct return_monitor monitor = {p, &child_return_status};
            pthread_t monitor_thread;
            pthread_create(&monitor_thread, NULL, captureStatus, &monitor);
        
            int i;
            for (i = 0; i < 10; ++i) {
              sleep(2);
              printf("Parent process iteration %d (Child return status %d)...\n",
                 i, child_return_status);
            }
            /*     captureStatus(p, &status); */
          }
        }
        

        【讨论】:

        • 这是最好的方法,但是你的实现有很多问题。看我的回答。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-06-22
        • 2010-12-12
        • 2010-11-04
        • 2015-11-02
        • 1970-01-01
        相关资源
        最近更新 更多