Есть ли способ получить статус возврата процесса, не дожидаясь его? - PullRequest
2 голосов
/ 05 февраля 2012

То, что я пытаюсь сделать, - это запустить дочерний процесс в фоновом режиме, пока родительский процесс идет и делает что-то еще.Когда ребенок вернется, я бы хотел, чтобы родитель получил этот статус и что-то с ним сделал.Однако я не хочу явно ждать ребенка в любой момент.

Я посмотрел на WNOHANG параметр waitpid, но, похоже, он немного отличается от того, что я ищу.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?

Ответы [ 4 ]

4 голосов
/ 05 февраля 2012

Вы можете установить обработчик сигнала для SIGCHLD и wait для процесса, когда этот триггер запускается (он будет срабатывать, когда дочерний процесс завершается или уничтожается).

Однако следует помнить, что очень мало полезныхвсе может быть сделано в обработчике сигнала.Все должно быть безопасно для асинхронных сигналов. Стандарт специально упоминает wait и waitpid как безопасный.

1 голос
/ 05 февраля 2012

Вот правильный способ (или, по крайней мере, один правильный способ) создать асинхронное ожидание из синхронного:

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_trywait или sem_timedwait с таймаутом, чтобы проверить, доступно ли еще состояниепрежде чем получить к нему доступ.

0 голосов
/ 05 февраля 2012

Мне нравится решение Виктора - в родительском процессе запустите поток для блокировки дочернего процесса и установите переменную 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); */
  }
}
0 голосов
/ 05 февраля 2012

Вы можете использовать функции IPC с общей памятью, такие как shmget и shmat, для связи между двумя процессами. Это может быть неблокирующим и может хорошо работать для моделей производителей / потребителей, таких как модель, которую вы описываете. Тем не менее, вам все равно придется опросить.

shmget должен быть вызван перед форком для создания блока общей памяти. Затем вы можете использовать shmat после разветвления, чтобы получить указатель на разделяемую память и просто использовать его в качестве буфера оттуда. По окончании вызовите shmdt на дочернем и родительском элементах, чтобы отключить, и shmctl, чтобы удалить общую память.

В сети есть пример здесь .

...