SIGALRM,间隔定时器和睡眠问题()

时间:2014-11-13 19:07:58

标签: c linux signals sleep gevent

我正在将我们的一个嵌入式微控制器库移植到linux并在其周围编写一个python包装器。

我的一个低级模块依赖于每10ms调用一次的回调。此回调计算软件计时器,每个计时器都有自己的回调。这些计时器在我们的图书馆中使用,虽然它们不必100%准确,但预计它们会继续滴答作响并且有望达到+/- 10%的准确度。该模块模拟微控制器内的标准定时器中断。

我有一个使用setitimer和SIGALRM处理程序的现有实现,它可以很好地工作。以下是我的一些代码:

void halCounterInit(void)
{
    struct sigaction alrm_action;
    alrm_action.sa_handler = timerInterrupt;
    alrm_action.sa_flags = SA_RESTART;
    sigaction(SIGALRM, &alrm_action, NULL);
}

void halCounterEnable(void)
{
    mytime.it_interval.tv_sec = 0;
    mytime.it_interval.tv_usec = 10000; //10ms
    mytime.it_value.tv_sec = 0;
    mytime.it_value.tv_usec = 10000; //10ms
    setitimer(ITIMER_REAL, &mytime, NULL);
}

void halCounterDisable(void)
{
    mytime.it_interval.tv_sec = 0;
    mytime.it_interval.tv_usec = 0; 
    mytime.it_value.tv_sec = 0;
    mytime.it_value.tv_usec = 0; 
    setitimer(ITIMER_REAL, &mytime, NULL);
}

void halCounterRegisterInterruptHandler(InterruptHandlerCallback cb, void *params)
{
    myInterruptHandler = cb;
    myParams = params;
}

void timerInterrupt(int signum)
{
    if (myInterruptHandler != NULL)
    {
        myInterruptHandler(myParams);
    }
}

在与其他代码一起测试这些代码时,我注意到在生成SIGALRM信号时,对sleep()的调用会提前开始。我在python中使用ctypes与gevent(基于libevent)连接,以便在我的C库之上创建一个新层。我担心这个SIGALRM问题会导致很难在python中找到bug。

我尝试使用pthread和阻塞的select()调用重写我的简单计数器,但似乎select()调用提前退出并且我的计时器变得非常不准确。

  1. 无论如何都要让setitimer生成一个我能捕捉的不同信号,而不是sigalrm?
  2. 有没有其他方法可以在不使用信号的情况下执行此操作?我已经尝试过纳米睡眠,在另一个线程中选择和定期睡眠,但这并不能很好地工作。
  3. 感谢任何帮助。理想情况下,这适用于OSX和Linux,但Linux是唯一的硬性要求。

    编辑:

    我使用select()或nanosleep()的单独线程代码。两者都不准确。注释掉了select实现。在init函数中正确配置了MyTime。这是线程函数:

    while (TRUE)
    {
        pthread_mutex_lock(&Lock);
        //we are paused
        while(!Running)
        {
            //we are waiting for a signal to tell us when to start again
            pthread_cond_wait(&Cond, &Lock);
        }
        pthread_mutex_unlock(&Lock);
        RemTime.tv_nsec = 0;
        do
        {
            nanosleep(&MyTime, &RemTime);
        }
        while(RemTime.tv_nsec != 0);
        //select(0, NULL, NULL, NULL, &MyTime);
        if (myInterruptHandler != NULL)
        {
            myInterruptHandler(myParams);
        }
    }
    

    感谢。

1 个答案:

答案 0 :(得分:2)

每当出现非屏蔽信号时,它将中断任何正在运行的系统调用。有SA_RESTART选项(有关详细信息,请参阅man -s7 signal),但这只会重新启动某些系统调用(例如,sleep)。无论您使用何种信号,如果系统调用中断,则受上述SA_RESTART条件限制,行为将被更改。因此,您需要完成整个应用程序,确保正确处理EINTR的返回代码。当然,如果您使用sleep并期望可靠性,那就很难了。

我建议你最好不要使用信号,特别是如果你还在使用线程;两个人打得不好。你说你希望它是便携的:避免瘟疫等信号的另一个原因。

我要做的是设置另一个线程来执行您的计时器,并使用适当的互斥锁保护计时器值。该线程可以睡眠,直到它需要使用nanosleeppthread_cond_timedwait来运行(如果你想要条件突破它)。在任何一种情况下,您都需要包装调用以避免导致错误退出的信号。在调用之前读取时钟的当前值,将调用包装在do/while()循环中,并测试时间是否真的超过了测试条件中所需的时间。传统上一个人使用gettimeofday,但这并不总是单调的,因此您可能需要clock_gettime CLOCK_MONOTONIC,具体取决于您对系统时间变化的反应。