2

这是一个在子例程旁边使用 POSIX 每进程计时器的sleep()程序。计时器到期时发送的信号已设置为SIGUSR1而不是SIGALRM,因为SIGALRM可能由 内部使用sleep(),但它似乎仍然不起作用。

我已经使用命令行timer-overruns -d 1 -n 10000000(1 个 cs 间隔)运行了程序,所以理论上,我们应该期望在调用sigwaitinfo(). 但是,timer_getoverrun()返回 0。

我还尝试了一个使用耗时for循环来引入延迟的版本。在这种情况下,会记录溢出。

有谁知道为什么会这样?我正在运行 3.4 Linux 内核。

程序源

/*
 * timer-overruns.c
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>

// Signal to be used for timer expirations
#define TIMER_SIGNAL SIGUSR1

int main(int argc, char **argv) {
    int opt;
    int d = 0;
    int r = 0; // Repeat indefinitely
    struct itimerspec its;
    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 0;

    // Parse arguments
    while ((opt = getopt(argc, argv, "d:r:s:n:")) != -1) {
        switch (opt) {
        case 'd': // Delay before calling sigwaitinfo()
            d = atoi(optarg);
            break;
        case 'r': // Number of times to call sigwaitinfo()
            r = atoi(optarg);
            break;
        case 's': // Timer interval (seconds)
            its.it_interval.tv_sec = its.it_value.tv_sec = atoi(optarg);
            break;
        case 'n': // Timer interval (nanoseconds)
            its.it_interval.tv_nsec = its.it_value.tv_nsec = atoi(optarg);
            break;
        default: /* '?' */
            fprintf(stderr,
                    "Usage: %s [-d signal_accept_delay] [-r repetitions] [-s interval_seconds] [-n interval_nanoseconds]\n",
                    argv[0]);
            exit(EXIT_FAILURE);
        }
    }

    // Check sanity of command line arguments
    short e = 0;

    if (d < 0) {
        fprintf(stderr, "Delay (-d) cannot be negative!\n");
        e++;
    }
    if (r < 0) {
        fprintf(stderr, "Number of repetitions (-r) cannot be negative!\n");
        e++;
    }
    if (its.it_interval.tv_sec < 0) {
        fprintf(stderr, "Interval seconds value (-s) cannot be negative!\n");
        e++;
    }
    if (its.it_interval.tv_nsec < 0) {
        fprintf(stderr, "Interval nanoseconds value (-n) cannot be negative!\n");
        e++;
    }
    if (its.it_interval.tv_nsec > 999999999) {
        fprintf(stderr, "Interval nanoseconds value (-n) must be < 1 second.\n");
        e++;
    }

    if (e > 0)
        exit(EXIT_FAILURE);

    // Set default values if not specified
    if (its.it_interval.tv_sec == 0 && its.it_interval.tv_nsec == 0) {
        its.it_interval.tv_sec = its.it_value.tv_sec = 1;
        its.it_value.tv_nsec = 0;
    }

    printf("Running with timer delay %d.%09d seconds\n",
            (int) its.it_interval.tv_sec, (int) its.it_interval.tv_nsec);

    // Will be waiting for signals synchronously, so block the one in use.  
    sigset_t sigset;

    sigemptyset(&sigset);
    sigaddset(&sigset, TIMER_SIGNAL);
    sigprocmask(SIG_BLOCK, &sigset, NULL );

    // Create and arm the timer
    struct sigevent sev;
    timer_t timer;

    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = TIMER_SIGNAL;
    sev.sigev_value.sival_ptr = timer;

    timer_create(CLOCK_REALTIME, &sev, &timer);
    timer_settime(timer, TIMER_ABSTIME, &its, NULL );

    // Signal handling loop
    int overruns;
    siginfo_t si;

    // Make the loop infinite if r = 0
    if (r == 0)
        r = -1;

    while (r != 0) {
        // Sleeping should cause overruns
        if (d > 0)
            sleep(d);

        sigwaitinfo(&sigset, &si);

        // Check that the signal is from the timer
        if (si.si_code != SI_TIMER)
            continue;

        overruns = timer_getoverrun(timer);
        if (overruns > 0) {
            printf("Timer overrun occurred for %d expirations.\n", overruns);
        }

        // Decrement r if not repeating indefinitely
        if (r > 0)
            r--;
    }

    return EXIT_SUCCESS;
}
4

1 回答 1

1

its.it_value.tv_sec当仅设置纳秒时,您无法设置,即-n 10000000没有设置-s 0。正确初始化its.it_value,或使用-s 0参数,你的代码应该可以工作——在某种程度上。

由于sigemptyset(&sigset); sigaddset(&sigset, TIMER_SIGNAL); sigprocmask(SIG_BLOCK, &sigset, NULL);我不认为存在竞态条件,sigwaitinfo()应该可靠地获得挂起的信号。查看sigwaitinfo()手册页了解详情;它确实在注释部分描述了这个非常相同的用例。

似乎timer_getoverrun()在计时器触发后第一次返回的值是假的,至少在 x86-64 上的 3.5.0 内核上是假的。换句话说,在循环之前添加这个,

    printf("Overruns: %d\n",
           timer_getoverrun(timer));
    sigwaitinfo(&sigset, &si);
    printf("Overruns: %d (%d)\n",
           timer_getoverrun(timer), si.si_overrun);
    sigwaitinfo(&sigset, &si);
    printf("Overruns: %d (%d)\n",
           timer_getoverrun(timer), si.si_overrun);

将输出零,一个看似随机的溢出计数,然后是预期的溢出计数。(计时器和 siginfo 溢出计数都匹配。)我怀疑kernel/posix-timers.c有问题,但我可能错了..问题似乎是第一个计时器事件将使用随机值以前的溢出计数。我想我会进一步调查。

如果您能告诉我您是否在内核上看到相同的内容,我将不胜感激。(还请说明确切的版本和架构。我在 amd64 (x86-64) 上运行 Ubuntu linux-image-3.5.0-3-generic,版本 3.5.0-3.3。

于 2012-07-08T13:02:01.357 回答