GNU/Linux >> Tutoriels Linux >  >> Linux

Waitpid équivalent avec timeout?

Fork un enfant intermédiaire, qui fork le véritable enfant et un processus de temporisation et attend tous (les deux) de ses enfants. Quand l'un sort, il tue l'autre et sort.

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

Étonnamment simple :)

Vous pouvez même laisser de côté l'enfant intermédiaire si vous êtes sûr qu'aucun autre module du programme ne génère ses propres processus enfants.


Ne mélangez pas alarm() avec wait() . Vous pouvez perdre des informations d'erreur de cette façon.

Utilisez l'astuce de l'auto-pipe. Cela transforme n'importe quel signal en un select() événement possible :

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

Ensuite, votre fonction de type waitpid ressemble à ceci :

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

Vous pouvez voir en selfpipe_waitpid() comment vous pouvez contrôler le délai d'attente et même mélanger avec d'autres select() -basé sur les E/S.


C'est une question intéressante. J'ai trouvé que sigtimedwait peut le faire.

EDIT 2016/08/29 :Merci pour la suggestion de Mark Edington. J'ai testé votre exemple sur Ubuntu 16.04, cela fonctionne comme prévu.

Remarque :cela ne fonctionne que pour les processus enfants. C'est dommage qu'il n'y ait pas de moyen équivalent à WaitForSingleObject(unrelated_process_handle, timeout) de Windows sous Linux/Unix pour être averti de la fin d'un processus non lié dans le délai imparti.

OK, l'exemple de code de Mark Edington est ici :

/* 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. Traçage du noyau avec trace-cmd

  2. Patcher un binaire avec Dd ?

  3. UNIX / Linux :Comment supprimer un utilisateur équivalent root (utilisateur non root avec UID 0)

  4. Que fait poll() avec un timeout de 0 ?

  5. Windows équivalent d'inet_aton

Commande Linux ip avec exemples

Commande Netcat (nc) avec exemples

Commande de temporisation sous Linux

Explication de la commande Linux timeout pour les débutants (avec exemples)

15 Commande Linux ps avec exemples

Mettez une minuterie sur vos commandes en cours d'exécution avec la commande Timeout sous Linux