C SIGALRM、间隔计时器和睡眠问题()

C SIGALRM、间隔计时器和睡眠问题(),c,linux,signals,sleep,gevent,C,Linux,Signals,Sleep,Gevent,我正在将我们的一个嵌入式微控制器库移植到linux,并围绕它编写python包装器 我的一个低级模块依赖于每10毫秒调用一次的回调。此回调对每个软件计时器都有自己的回调进行计数。这些计时器在我们的图书馆里随处可见,虽然它们不一定要100%准确,但预计它们会继续滴答作响,并有望达到+/-10%的准确度。该模块模拟微控制器内的标准定时器中断 我有一个使用setitimer和SIGALRM处理程序的现有实现,它工作得非常好。以下是我的一些代码: void halCounterInit(void) {

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

我的一个低级模块依赖于每10毫秒调用一次的回调。此回调对每个软件计时器都有自己的回调进行计数。这些计时器在我们的图书馆里随处可见,虽然它们不一定要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()的调用会提前终止。我正在使用ctypes与python中的gevent(基于libevent)进行接口,以便在我的C库之上创建一个新层。我担心这个SIGALRM问题会导致难以在python中找到bug

我试图使用pthread和阻塞select()调用重写我的简单计数器,但是select()调用似乎很早就退出了,我的计时器变得非常不准确

  • 有没有办法让setitimer生成一个我可以捕捉到的不同信号,而不是sigalrm
  • 有没有其他不使用信号的方法?我尝试过在另一个线程中使用nanosleep、select和regular sleep,但效果不太好
  • 感谢您的帮助。理想情况下,这将适用于OSX和linux,但linux是唯一的硬需求

    编辑:

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

    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);
        }
    }
    

    谢谢。

    每当出现非屏蔽信号时,它将中断任何正在运行的系统调用。有一个
    SA_RESTART
    选项(有关详细信息,请参阅
    man-s7 signal
    ),但该选项只会重新启动某些系统调用(例如,
    sleep
    )。无论您使用什么信号,如果系统调用被中断,则根据上面的
    SA_RESTART
    但书,行为将被改变。因此,您需要检查整个应用程序,确保正确处理返回代码
    EINTR
    。当然,如果你使用
    睡眠
    并且期望可靠性,那么这很难做到

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


    我要做的是设置另一个线程来执行计时器,并使用适当的互斥来保护计时器值。该线程可以使用
    nanosleep
    pthread\u cond\u timedwait
    (如果您希望某个条件打破它),一直休眠到下一步需要运行为止。在这两种情况下,您都需要包装调用以避免导致错误退出的信号。在调用之前读取时钟的当前值,将调用包装在
    do/while()
    循环中,并测试时间是否确实超过了测试条件中所需的时间。传统上,人们使用
    gettimeofday
    进行此操作,但这并不总是单调的,因此您可能希望
    clock\u gettime
    具有
    clock\u单调的
    ,这取决于您对系统时间变化的反应。

    感谢您的支持。我确实尝试使用单独的线程以及select()和nanosleep,但无法获得正确的行为。我想我可以尝试对代码进行适当的包装,以解释传入并中断它的信号。我理解你关于EINTR的观点,但我担心的不一定是我的代码,而是我可能与之交互的各种python库。Re#1,是的,但正如@abligh在下面建议的那样,这是很难正确完成的(tm),尤其是库代码(不能对信号进行自由控制)。Re#2,是的,但我们无法帮助您调试看不到的代码。(顺便说一下,SIGALRM还是SIGVTALRM?你的叙述和你的代码说明了不同的事情。)@pilcrow,SIGALRM,我刚刚意识到我复制了一些我正在玩的调试代码。将编辑。