2

sleep()これは、サブルーチンと一緒にPOSIXプロセスごとのタイマーを使用するプログラムです。タイマーが期限切れになったときに送信される信号は、によって内部的に使用される可能性があるため、SIGUSR1ではなくに設定されていますが、それでも機能していないようです。SIGALRMSIGALRMsleep()

コマンドライン(1 cs間隔)を使用してプログラムを実行したtimer-overruns -d 1 -n 10000000ので、理論的には、への呼び出しの間に100回のオーバーランが予想されsigwaitinfo()ます。ただし、timer_getoverrun()0を返します。

forまた、時間のかかるループを使用して遅延を導入するバージョンも試しました。この場合、オーバーラン記録されます。

なぜこれが起こるのか誰かが知っていますか?3.4Linuxカーネルを実行しています。

プログラムソース

/*
 * 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 に答える