2012-07-26 56 views
1

当我在写一个使用定时器以固定的采样率(200Hz的)做一些数据采集和处理应用程序中处理“intterupted系统调用”的错误。 应用程序像服务器一样运行并在后台运行。它应该可以从其他进程或来自UDP的其他机器进行控制。使用定时器

要做到这一点,我用timer_create()API定期生成SIGUSR1和调用完成的采集和处理的处理程序。

配置定时器的代码是如下(为了清楚减去错误校验):

sa.sa_flags = SA_SIGINFO; 
sa.sa_sigaction = handler; 
sigemptyset(&sa.sa_mask); 
sigaction(SIGUSR1, &sa, NULL); 
sev.sigev_notify = SIGEV_SIGNAL; 
sev.sigev_signo = SIGUSR1; 
sev.sigev_value.sival_ptr = &timerid; 
timer_create(CLOCK_REALTIME, &sev, &timerid); 
timer_settime(...) 

当从UDP接收到“启动”命令上面的代码被调用。为了检查命令,我在调用recvfrom()系统调用的主程序中有一个无限循环。

问题是,当收到'start'命令,然后定时器正常启动并运行(使用上面的代码)时,由于SIGUSR1,我得到'中断系统调用'错误(EINTR)定时器发送的信号中断了recvfrom()调用。如果我检查这个特定的错误代码并忽略它,当调用recvfrom()时,我终于得到'连接被拒绝'错误。

所以在这里我的问题:

  1. 如何解决这个“中断的系统调用”的错误,因为它似乎 忽略它,重新做recvfrom的()不工作?
  2. 为什么在大约20次尝试后出现“连接被拒绝”错误?
  3. 我有一种感觉,使用SIGEV_THREAD可能是一个解决方案,我的理解是,创建一个新的线程(如phread_create)不产生信号。我对吗?
  4. 信号号码在这里很重要吗?使用实时信号还有什么优点?
  5. 是否有任何其他的方式做什么,我打算做的:具有从UDP和实时周期任务后台循环检查命令?

而且这里的奖金问题:

  • 它是安全的进行数据采集和处理的处理程序或我应该使用一个信号量机制来唤醒一个线程,这样做?

解决方案: 作为一个答案,并在评论中建议,使用SA_RESTART似乎解决的主要问题。

解决方案2: 通过SIGEV_SIGNAL使用SIGEV_THREAD也起作用。我读过使用SIGEV_THREAD可能需要比SIGEV_SIGNAL更多的资源的地方。但是,我没有看到有关任务时间的重大差异。

+0

我并不完全了解你的情况,但是避免在信号处理程序中做“真正的工作”的一种优雅方法是打开一个管道到自身,让信号处理程序写入管道,并让main选择循环(我猜你有一个)从管道读取并做真正的工作。我不知道这是否能满足您的时间需求。至少管道避免了并发问题,因为从管道读取和写入是原子的(并且在它们之间存在上下文切换),这也避免了需要连续切换sigmask。 – wildplasser 2012-07-26 20:04:43

+0

我不确定我是否理解了你的所有问题,但是你可以尝试将'SA_RESTART'添加到'sa_flags'吗? – ninjalj 2012-07-26 20:12:41

+0

wildplasser>我没有任何选择循环,因为我只有一件事要做:检查通过带有'recvfrom' – pahpaul 2012-07-26 22:38:52

回答

1

定时器倾向于使用SIGALARM来实现。

信号接收,包括SIGALARM,往往会导致长时间运行的系统调用在errno中以EINTR提前返回。

SA_RESTART是解决此问题的一种方法,因此系统调用会因接收到信号而中断,并且会自动重新启动。另一种方法是检查系统调用errno的EINTR,并在收到EINTR时重新启动它们。

使用read()和write()当然,你不能只是重新启动,你需要选择你离开的地方。这就是为什么这些会返回传输数据的长度。

+0

OP没有使用警报(2),他使用timer_create,因此他处理SIGUSR1。 – Giel 2012-07-26 20:42:22

+0

正如Giel所说,我正在使用timer_create而不是报警。无论如何,我会尝试使用SA_RESTART标志。 – pahpaul 2012-07-26 22:27:55

1

既然你使用Linux,那么我会选择使用timerfd_create代替。

这样,您可以只用select(2),poll(2)epoll(7)代替,并且在主循环中处理定时器事件时没有信号处理程序的困难。

至于EINTR(中断系统调用),只需重新启动中断的特定系统调用即可正确处理这些事件。

+0

不幸的是'timerfd_create'在我的平台上不可用。 – pahpaul 2012-07-26 22:21:47

0

重新启动中断的系统调用是对EINTR的正确响应。你“连接被拒绝”的问题是一个不相关的错误 - 在一个UDP套接字上,它指示在该套接字上发送的先前数据包被目的地拒绝(通过ICMP消息通知)。

0

问题5:您使用消息和实时定期线程是完全正确的。不过,我建议你完全避免使用定时器,正是因为它们使用了信号。我自己遇到了这个问题,并最终用一个简单的clock_nanosleep()替换了计时器,该计时器使用TIMER_ABSTIME,并更新了时间以保持所需的速率(即将周期添加到绝对时间)。结果是代码更简单,没有更多信号问题,以及比基于信号的定时器更准确的定时器。顺便说一句,你应该在处理程序中测量你的计时器的周期,以确保它足够准确。我对定时器的经验是8年前,所以准确性问题可能会得到解决。然而,信号的其他问题是信号本身固有的,因此不能被“解决” - 只能解决问题。

另外,我发现从处理程序获取数据没有问题,它肯定会减少检索数据的延迟。