GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Waitpid entspricht Timeout?

Verzweigen Sie ein Zwischenkind, das das echte Kind und einen Timeout-Prozess verzweigt und auf alle (beide) seiner Kinder wartet. Wenn einer austritt, wird der andere getötet und ausgetreten.

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

Überraschend einfach :)

Sie können sogar das untergeordnete Kind weglassen, wenn Sie sicher sind, dass kein anderes Modul im Programm eigene untergeordnete Prozesse hervorbringt.


alarm() nicht mischen mit wait() . Auf diese Weise können Sie Fehlerinformationen verlieren.

Verwenden Sie den Self-Pipe-Trick. Dadurch wird jedes Signal zu einem select() fähiges Ereignis:

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

Dann sieht Ihre Waitpid-ähnliche Funktion so aus:

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

Sie können in selfpipe_waitpid() sehen wie Sie das Timeout steuern und sogar mit anderen select() mischen können -basiertes IO.


Dies ist eine interessante Frage. Ich habe festgestellt, dass sigtimedwait dies tun kann.

BEARBEITEN 29.08.2016:Danke für Mark Edingtons Vorschlag. Ich habe Ihr Beispiel auf Ubuntu 16.04 getestet, es funktioniert wie erwartet.

Hinweis:Dies funktioniert nur für untergeordnete Prozesse. Schade, dass es unter Linux/Unix keine gleichwertige Möglichkeit zu Windows WaitForSingleObject(unrelated_process_handle, timeout) zu geben scheint, um über die Beendigung eines nicht verwandten Prozesses innerhalb der Zeitüberschreitung benachrichtigt zu werden.

OK, Mark Edingtons Beispielcode ist hier:

/* 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. UNIX / Linux:So löschen Sie den root-äquivalenten Benutzer (Nicht-Root-Benutzer mit UID 0)

  2. Was macht poll() mit einem Timeout von 0?

  3. Windows-Äquivalent von inet_aton

  4. Linux:Gibt es ein Lesen oder Empfangen vom Socket mit Timeout?

  5. Bash:Warte mit Timeout

Linux-IP-Befehl mit Beispielen

Netcat (nc) Befehl mit Beispielen

Timeout-Befehl in Linux

Linux-Timeout-Befehl für Anfänger erklärt (mit Beispielen)

15 Linux-PS-Befehl mit Beispielen

Setzen Sie einen Timer auf Ihre laufenden Befehle mit dem Timeout-Befehl in Linux