My Avatar

Shilong ZHAO

Monitor Process Event with Timeout

2017-01-03 00:00:00 +0100

In case you have any questions or suggestions, you can leave comments HERE . Thanks!

Generally speaking, the events in an operating system are either related to file, signal, or process. For a file descriptor, the possible events could be ready to read, write etc; for a signal, the events could be upon reception; for a process, the events could be upon exit, upon calling exec or fork etc.

To monitor file related events with a timeout, there are system calls like select, poll, etc. To monitor signal related events, there are two possible solutions: the easier but non-standard one, use sigtimedwait; or use sigalfd to create file descriptors related with a signal, and then apply select (this could be used to monitor a child process when the signal is set as SIGCHILD).

But to monitor a process with a timeout? There come kqueue and kevent. In fact, they are also able to do the file and signal monitor works, with proper parameters fed to EV_SET macro. For detailed references, click here.

In this post, a code snippet to monitor a process with time out is given (there is not much code examples about process monitoring but a lot about network file descriptors).

 

/***
 * monitor child process with a time out,
 * using EVFILT_PROC, NOTE_TRACK (deprecated)
 *
 * usage: ./kq2 timeout
 *
 * best ref: http://www.gsp.com/cgi-bin/man.cgi?section=2&topic=kevent
 * other refs: https://wiki.netbsd.org/tutorials/kqueue_tutorial/
 */
#include <sys/event.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void diep(const char *s)
{
    perror(s);
    exit(EXIT_FAILURE);
}

long get_time_ms() {
    struct timeval time;
    gettimeofday(&time, NULL);
    long millis = (time.tv_sec * 1000) + (time.tv_usec / 1000);
    return millis;
}

int main(int argc, char **argv) {
    struct kevent change;
    struct kevent event;
    struct timespec timeout;

    int kq, nev;
    pid_t  pid;

    if (argc != 2)
        return -1;

    int msec = atoi(argv[1]);
    timeout.tv_sec = msec / 1000;
    timeout.tv_nsec = (msec % 1000) * 1000000;

    if ((kq = kqueue()) == -1)
        diep("kqueue");

    if ((pid = fork()) < 0)
        diep("fork error");

    if (pid == 0) {
        usleep(7000); // sleep for 7 ms
        if (execlp("date", "date", (char *)0) < 0)
            diep("execlp date error");
    }
    else {
        long start_time = get_time_ms();
        // try with NOTE_EXIT, NOTE_TRACK, NOTE_EXEC, etc.
        EV_SET(&change, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0,0);
        nev = kevent(kq, &change, 1, &event, 1, &timeout);
        long end_time = get_time_ms();
        if (nev < 0) {
            diep("nev error");
        }
        if (nev == 0) {
            diep("no event");
        }
        if (nev > 0) {
            printf("nev = %d, time = %ld milliseconds\n", nev, end_time - start_time);
            return 0;
        }
    }
    close(kq);
    return EXIT_SUCCESS;
}