GNU/Linux >> Tutoriales Linux >  >> Linux

Waitpid equivalente con tiempo de espera?

Fork un hijo intermedio, que bifurca al hijo real y un proceso de tiempo de espera y espera a todos (ambos) de sus hijos. Cuando uno sale, matará al otro y saldrá.

pid_t intermediate_pid = fork();
if (intermediate_pid == 0) {
    pid_t worker_pid = fork();
    if (worker_pid == 0) {
        do_work();
        _exit(0);
    }

    pid_t timeout_pid = fork();
    if (timeout_pid == 0) {
        sleep(timeout_time);
        _exit(0);
    }

    pid_t exited_pid = wait(NULL);
    if (exited_pid == worker_pid) {
        kill(timeout_pid, SIGKILL);
    } else {
        kill(worker_pid, SIGKILL); // Or something less violent if you prefer
    }
    wait(NULL); // Collect the other process
    _exit(0); // Or some more informative status
}
waitpid(intermediate_pid, 0, 0);

Sorprendentemente simple :)

Incluso puede omitir el elemento secundario intermedio si está seguro de que ningún otro módulo del programa genera procesos secundarios propios.


No mezcles alarm() con wait() . Puede perder información de error de esa manera.

Usa el truco de la auto-tubería. Esto convierte cualquier señal en un select() evento capaz:

int selfpipe[2];
void selfpipe_sigh(int n)
{
    int save_errno = errno;
    (void)write(selfpipe[1], "",1);
    errno = save_errno;
}
void selfpipe_setup(void)
{
    static struct sigaction act;
    if (pipe(selfpipe) == -1) { abort(); }

    fcntl(selfpipe[0],F_SETFL,fcntl(selfpipe[0],F_GETFL)|O_NONBLOCK);
    fcntl(selfpipe[1],F_SETFL,fcntl(selfpipe[1],F_GETFL)|O_NONBLOCK);
    memset(&act, 0, sizeof(act));
    act.sa_handler = selfpipe_sigh;
    sigaction(SIGCHLD, &act, NULL);
}

Entonces, su función similar a waitpid se ve así:

int selfpipe_waitpid(void)
{
    static char dummy[4096];
    fd_set rfds;
    struct timeval tv;
    int died = 0, st;

    tv.tv_sec = 5;
    tv.tv_usec = 0;
    FD_ZERO(&rfds);
    FD_SET(selfpipe[0], &rfds);
    if (select(selfpipe[0]+1, &rfds, NULL, NULL, &tv) > 0) {
       while (read(selfpipe[0],dummy,sizeof(dummy)) > 0);
       while (waitpid(-1, &st, WNOHANG) != -1) died++;
    }
    return died;
}

Puedes ver en selfpipe_waitpid() cómo puedes controlar el tiempo de espera e incluso mezclar con otros select() -IO basado.


Esta es una pregunta interesante. Descubrí que sigtimedwait puede hacerlo.

EDIT 2016/08/29:Gracias por la sugerencia de Mark Edington. Probé su ejemplo en Ubuntu 16.04, funciona como se esperaba.

Nota:esto solo funciona para procesos secundarios. Es una lástima que no parezca una forma equivalente de WaitForSingleObject(unrelated_process_handle, timeout) de Windows en Linux/Unix para recibir notificaciones de la terminación de un proceso no relacionado dentro del tiempo de espera.

Bien, el código de muestra de Mark Edington está aquí:

/* The program creates a child process and waits for it to finish. If a timeout
 * elapses the child is killed. Waiting is done using sigtimedwait(). Race
 * condition is avoided by blocking the SIGCHLD signal before fork().
 */
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

static pid_t fork_child (void)
{
    int p = fork ();

    if (p == -1) {
        perror ("fork");
        exit (1);
    }

    if (p == 0) {
        puts ("child: sleeping...");
        sleep (10);
        puts ("child: exiting");
        exit (0);
    }

    return p;
}

int main (int argc, char *argv[])
{
    sigset_t mask;
    sigset_t orig_mask;
    struct timespec timeout;
    pid_t pid;

    sigemptyset (&mask);
    sigaddset (&mask, SIGCHLD);

    if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
        perror ("sigprocmask");
        return 1;
    }

    pid = fork_child ();

    timeout.tv_sec = 5;
    timeout.tv_nsec = 0;

    do {
        if (sigtimedwait(&mask, NULL, &timeout) < 0) {
            if (errno == EINTR) {
                /* Interrupted by a signal other than SIGCHLD. */
                continue;
            }
            else if (errno == EAGAIN) {
                printf ("Timeout, killing child\n");
                kill (pid, SIGKILL);
            }
            else {
                perror ("sigtimedwait");
                return 1;
            }
        }

        break;
    } while (1);

    if (waitpid(pid, NULL, 0) < 0) {
        perror ("waitpid");
        return 1;
    }

    return 0;
}

Linux
  1. Parchear un binario con Dd?

  2. ¿Qué hace poll() con un tiempo de espera de 0?

  3. equivalente de Windows de inet_aton

  4. Linux:¿hay una lectura o recepción desde el socket con tiempo de espera?

  5. fallocate() comando equivalente en OS X?

Comando ip de Linux con ejemplos

Comando Netcat (nc) con ejemplos

Comando de tiempo de espera en Linux

Comando de tiempo de espera de Linux explicado para principiantes (con ejemplos)

15 comandos ps de Linux con ejemplos

Ponga un temporizador en sus comandos en ejecución con el comando de tiempo de espera en Linux