54 votes

Équivalent Waitpid avec timeout?

Imaginez que j'ai un processus qui commence plusieurs processus enfant. Le parent a besoin de savoir quand un enfant quitte.

Je peux utiliser waitpid, mais alors, si/lorsque le parent doit quitter je n'ai aucun moyen de dire le thread est bloqué en waitpid à la sortie gracieusement et se joindre à elle. C'est agréable d'avoir des choses à nettoyer eux-mêmes, mais peut-être pas que les grandes d'un accord.

Je peux utiliser waitpid avec WNOHANG, puis le sommeil pour quelques temps arbitraire pour éviter une longue attente. Toutefois, alors je ne peux savoir si un enfant est sorti de chaque tellement souvent. Dans mon cas, il peut ne pas être super critique que je sais que quand un enfant quitte tout de suite, mais j'aimerais savoir le plus tôt possible...

Je peux utiliser un gestionnaire de signal pour SIGCHLD, et dans le gestionnaire de signal faire ce que j'allais faire quand un enfant quitte, ou envoyer un message à un autre thread pour faire une action. Mais à l'aide d'un gestionnaire de signal code le flux de un peu le code.

Ce que j'aimerais vraiment faire est d'utiliser waitpid sur certains délai, disons 5 secondes. Depuis la sortie du processus n'est pas une critique de l'opération, je peux paresseusement signal le fil à la sortie, tout en ayant encore il s'est bloqué en waitpid le reste du temps, toujours prêt à réagir. Il y a un appel dans linux? Des solutions de rechange, lequel est le meilleur?


EDIT:

Une autre méthode basée sur les réponses serait de bloquer SIGCHLD dans toutes les discussions avec pthread \ _sigmask(). Puis dans un thread, l'appel, sigtimedwait() alors que la recherche d' SIGCHLD. Cela signifie que je peux de temps sur cet appel, et de vérifier si le fil de sortie, et si non, rester bloqué dans l'attente du signal. Une fois un SIGCHLD est livré à ce fil, nous pouvons réagir immédiatement, et dans la ligne de l'attente d'un thread, sans l'aide d'un gestionnaire de signal.

42voto

geocar Points 5915

Ne mélangez pas alarm() avec wait() . Vous pouvez ainsi perdre des informations sur les erreurs.

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

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

    //fixed the misspelling of fcntl in the 3rd argument
    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 E / S basées sur select() .

33voto

mpartel Points 2365

Fork un enfant intermédiaire, qui bifurque le vrai enfant et un processus de temporisation et attend tous (les deux) de ses enfants. Quand l'un sort, il tuera l'autre et sortira.

 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 n'effectue ses propres processus enfants.

19voto

osexp2003 Points 51

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

4voto

Steve Baker Points 2220

La fonction peut être interrompue par un signal, vous pouvez donc définir une minuterie avant d'appeler waitpid () et elle se terminera avec un EINTR lorsque le signal de la minuterie sera augmenté. Edit: Cela devrait être aussi simple que d'appeler alarm (5) avant d'appeler waitpid ().

2voto

Chris Dodd Points 1990

Si vous souhaitez utiliser des signaux de toute façon (selon la suggestion de Steve), vous pouvez simplement envoyer le signal manuellement lorsque vous souhaitez quitter. Cela entraînera waitpid pour retourner EINTR et le thread peut alors quitter. Pas besoin d'une alarme / redémarrage périodique.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X