【问题标题】:POSIX timer fail after many repeats多次重复后 POSIX 计时器失败
【发布时间】:2016-06-16 16:45:18
【问题描述】:

有人可以解释为什么 SIGSEGV 在 5-7 次迭代后计时器会失败?

这两种情况都会发生:同步和不同步。 操作系统为 Ubuntu 15.04,Ubuntu GLIBC 2.21-0ubuntu4。

void timer_thread (sigval signal_value) {
    printf ("Timer callback!\n");
}

int main(int argc, char* argv[]) {

    const int TIMER_COUNT = 300;

    for (int i = 0; i < 10000; i++) {
        int status = 0;

        timer_t timer_id[TIMER_COUNT] = {};
        memset(&timer_id[0], 0, sizeof(timer_t)*TIMER_COUNT);

        for (int j = 0; j < TIMER_COUNT; j++) {

            struct itimerspec ts = {};
            struct sigevent se = {};

            memset(&ts, 0, sizeof(itimerspec));
            memset(&se, 0, sizeof(sigevent));

            se.sigev_notify = SIGEV_THREAD;
            se.sigev_value.sival_int = j;
            se.sigev_notify_function = timer_thread;

            // Specify a repeating timer that fires each 100000 nanosec.
            memset(&ts, 0, sizeof(ts));
            ts.it_value.tv_nsec = 100000;
            ts.it_interval.tv_nsec = 100000;

            status = timer_create(CLOCK_REALTIME, &se, &timer_id[j]);
            assert(!status && "Create timer");

            status = timer_settime(timer_id[j], 0, &ts, 0);
            assert(!status && "Set timer");
        }

        for (int j = 0; j < TIMER_COUNT; j++) {
            usleep(100);
            //stop and delete

            status = timer_delete(timer_id[j]);
            assert(!status && "Fail delete timer");
        }
    }
    printf("Success!\n");
    return 0;
}

GDB 回溯:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  __pthread_create_2_1 (newthread=newthread@entry=0x7f00e9817e28, attr=attr@entry=0x11c47e8, start_routine=start_routine@entry=0x7f00e93f6eb0 <timer_sigev_thread>, arg=<optimized out>)
    at pthread_create.c:711
711 pthread_create.c: No such file or directory.
(gdb) bt
#0  __pthread_create_2_1 (newthread=newthread@entry=0x7f00e9817e28, attr=attr@entry=0x11c47e8, start_routine=start_routine@entry=0x7f00e93f6eb0 <timer_sigev_thread>, arg=<optimized out>)
    at pthread_create.c:711
#1  0x00007f00e93f6e7a in timer_helper_thread (arg=<optimized out>) at ../sysdeps/unix/sysv/linux/timer_routines.c:125
#2  0x00007f00e91db6aa in start_thread (arg=0x7f00e9818740) at pthread_create.c:333
#3  0x00007f00e866feed in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109

构建命令行:/usr/bin/c++ -lrt -lpthread -g ./main.cc

Full code posix timer with synchronization

Full code posix timer with sleep

【问题讨论】:

  • 请确认您在不断地创建、超时然后删除 300 个计时器。一遍又一遍,一遍又一遍。
  • 对。我创建了大约 300 个计时器,然后是睡眠或等待条件变量,并且在所有计时器多次调用之后,删除所有计时器。大约 5 次是工作,然后在 6 日或以后它会失败。
  • 而且不需要 300 个计时器,它会在 50 个或更少的计时器时失败。
  • 这似乎是一个运行时问题,但发布的代码缺少#include 语句。您希望我们猜测包含哪些头文件吗?
  • 我不会重现这个问题。我在 VM 中运行 Mint 17.2 x86_64 内核 v3.16.0(我猜这会使问题变得更糟)。尝试在另一个终端窗口中运行它以查看您获得的“信号队列限制”值:cat /proc/[programs-PID]/status | grep SigQ。我得到如下结果:SigQ: 271/22308。第一个值是排队的信号数,第二个是最大值。这里的第一个永远不会超过 300(正如您所期望的,因为一次最多有 300 个计时器处于活动状态)。看看您是否在第一个条目中获得更高的值或在第二个条目中获得非常低的值。

标签: c timer posix glibc ubuntu-15.04


【解决方案1】:

以下代码实际运行,不崩溃,干净编译

注意每个计时器的扩展时间间隔,这样大约 300 个计时器有时间调用 printf() 并返回。

顺便说一句:在信号处理程序中调用 printf() 是一个非常糟糕的主意

#include <stdio.h>  // printf()
#include <stdlib.h> // exit(), EXIT_FAILURE
#include <signal.h>
#include <time.h>
#include <unistd.h> // sleep()
#include <string.h> // memset()

#define TIMER_COUNT (300)

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                           } while (0)

void timer_thread (union sigval sigev_value)
{
    (void)sigev_value;
    static int count = 0;
    count++;
    fprintf ( stdout, "Timer callback count: %d\n", count);
}

int main( void )
{
    timer_t timer_id[TIMER_COUNT];
    memset(timer_id, '\0', sizeof(timer_t)*TIMER_COUNT);

    struct itimerspec ts;
    struct sigevent se;

    for (size_t j = 0; j < TIMER_COUNT; j++)
    {
        memset(&ts, '\0', sizeof(struct itimerspec));
        memset(&se, '\0', sizeof(struct sigevent));

        se.sigev_notify = SIGEV_THREAD;
        se.sigev_value.sival_int = (int)j;
        se.sigev_notify_function = timer_thread;

        // Specify a repeating timer that fires each 2 second.
        ts.it_interval.tv_sec = 2;
        ts.it_interval.tv_nsec = 0;
        ts.it_value.tv_sec = ts.it_interval.tv_sec;
        ts.it_value.tv_nsec = ts.it_interval.tv_nsec;

        if( -1 == timer_create(CLOCK_REALTIME, &se, &timer_id[j]) )
            errExit( "timer_create failed" );

        if( -1 == timer_settime(timer_id[j], 0, &ts, NULL) )
            errExit("timer_settime failed");
    }

    sleep(10);

    for (int j = 0; j < TIMER_COUNT; j++)
    {
        //stop and delete
        fprintf( stdout, "stopping timer: %d, with ID:  %lu\n", j, (size_t)timer_id[j]);
        ts.it_value.tv_sec = 0;
        ts.it_value.tv_nsec = 0;
        ts.it_interval.tv_sec = 0;
        ts.it_interval.tv_nsec = 0;
        if( -1 == timer_settime( timer_id[j], 0, &ts, NULL) )
            errExit("timer_settime (to disable timer) failed" );

        fprintf( stdout, "deleting timer: %d, with ID:  %lu\n", j, (size_t)timer_id[j]);
        if( -1 == timer_delete(timer_id[j]) )
            errExit("timer_delete failed" );
    }

    printf("Success!\n");
    return 0;
} // end function: main

【讨论】:

  • 我同意在数百个 100us 定时器的回调中调用 printf() 会带来一些麻烦,但是在 se.sigev_notify 项中指定的 SIGEV_THREAD 配置意味着定时器回调不是在信号处理程序中完成 - 它在线程中完成。该线程可以专门为回调创建,也可以是线程池中的工作线程。
  • 我运行了上面的代码,并进行了修改,因此它会像 OPs 发布的代码一样迭代 10000 次。它运行(似乎永远)但它没有崩溃。 (ubuntu linux 14.04)
  • 注意我减慢了操作速度,(2秒定时器间隔和10秒开始删除定时器之前)我没有用10000usec定时器运行它,并且在杀死定时器之前没有等待。您可以测试该场景。但是,我怀疑某些操作系统队列在尝试运行太快时会溢出(主要是由于回调函数中对 printf() 的调用。
  • 我还怀疑某些队列因执行printf() 的回调而溢出。但是当我尝试在我的系统上重现 OP 的问题(使用 100 微秒计时器)时,我没有看到我的系统上的应用程序崩溃 - 这一切似乎都有效。我不知道从中得出什么结论。
  • 我修改了代码,使其具有与 OP 发布的代码相同的时序和循环计数。配备 AMD 4 核 CPU 和 8gigs 内存。所有 4 个内核的运行率都在 60% 左右,而内存(虽然分配了 8 gig,但没有使用交换空间)以接近 80% 的利用率运行。如果 OP 的计算机是一台性能较差的机器,那么它可能以 100% 的速度运行 CPU 和/或 100% 的内存。这将导致用户界面无响应。导致看起来应用程序崩溃了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-18
  • 2019-01-31
相关资源
最近更新 更多