我正在将我们的一个嵌入式微控制器库移植到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()调用提前退出并且我的计时器变得非常不准确。
感谢任何帮助。理想情况下,这适用于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);
}
}
感谢。
答案 0 :(得分:2)
每当出现非屏蔽信号时,它将中断任何正在运行的系统调用。有SA_RESTART
选项(有关详细信息,请参阅man -s7 signal
),但这只会重新启动某些系统调用(例如,sleep
)。无论您使用何种信号,如果系统调用中断,则受上述SA_RESTART
条件限制,行为将被更改。因此,您需要完成整个应用程序,确保正确处理EINTR
的返回代码。当然,如果您使用sleep
并期望可靠性,那就很难了。
我建议你最好不要使用信号,特别是如果你还在使用线程;两个人打得不好。你说你希望它是便携的:避免瘟疫等信号的另一个原因。
我要做的是设置另一个线程来执行您的计时器,并使用适当的互斥锁保护计时器值。该线程可以睡眠,直到它需要使用nanosleep
或pthread_cond_timedwait
来运行(如果你想要条件突破它)。在任何一种情况下,您都需要包装调用以避免导致错误退出的信号。在调用之前读取时钟的当前值,将调用包装在do/while()
循环中,并测试时间是否真的超过了测试条件中所需的时间。传统上一个人使用gettimeofday
,但这并不总是单调的,因此您可能需要clock_gettime
CLOCK_MONOTONIC
,具体取决于您对系统时间变化的反应。