select()
或 epoll()
前,计算最早定时器到期时间与当前时间之间的毫秒差。使用此差作为 select()
的超时时间。select()
和 epoll()
的超时精度为 1 毫秒。1000 deviation samples of 1msec timer: min= -246115nsec max= 1143471nsec median= -70775nsec avg= 901nsec stddev= 45570nsec
1000 deviation samples of 5msec timer: min= -265280nsec max= 256260nsec median= -252363nsec avg= -195nsec stddev= 30933nsec
1000 deviation samples of 10msec timer: min= -273119nsec max= 274045nsec median= 103471nsec avg= -179nsec stddev= 31228nsec
1000 deviation samples of 1msec timer: min= -144930nsec max= 1052379nsec median= -109322nsec avg= 1000nsec stddev= 43545nsec
1000 deviation samples of 5msec timer: min= -1229446nsec max= 1230399nsec median= 1222761nsec avg= 724nsec stddev= 254466nsec
1000 deviation samples of 10msec timer: min= -1227580nsec max= 1227734nsec median= 47328nsec avg= 745nsec stddev= 173834nsec
1000 deviation samples of 1msec timer: min= -222672nsec max= 228907nsec median= 63635nsec avg= 22nsec stddev= 29410nsec
1000 deviation samples of 5msec timer: min= -1302808nsec max= 1270006nsec median= 1251949nsec avg= -222nsec stddev= 345944nsec
1000 deviation samples of 10msec timer: min= -1297724nsec max= 1298269nsec median= 1254351nsec avg= -225nsec stddev= 374717nsec
测试在Fedora 13内核2.6.34上作为实时进程运行,最佳实现的1ms定时器精度为avg=22nsec stddev=29410nsec。
clock_gettime()
吗?外部硬件计时器?或者是什么?)。 - xakepp35rdtsc
转换为纳秒。 - Maxim Egorushkin我认为即使在主循环中不断查询,使用标准的Linux也很难实现1毫秒的精度,因为内核不能保证您的应用程序一直获得CPU。例如,由于抢占式多任务处理,您可能会被休眠数十毫秒,而您无能为力。
您可能需要了解实时Linux。
我记得使用gettimeofday/usleep进行轮询时,结果还不错。我不需要每秒1000个定时器之类的东西,但我需要在需要的节拍的时间上有很好的准确性。我的应用程序是一个MIDI鼓机控制器,我记得获得了亚毫秒级别的准确性,如果你不想让它听起来像一个非常糟糕的鼓手(特别是考虑到MIDI内置延迟),那么你就需要这样的准确性。根据我的回忆(它是2005年,所以我的记忆有点模糊),我使用usleep可以接近目标时间200微秒。
然而,我没有在系统上运行太多其他东西。如果您有一个受控环境,您可能能够使用这样的解决方案。如果系统上有更多的事情发生(例如cron启动updatedb等),那么事情可能会崩溃。
首先,获取内核源代码并使用调整后的HZ参数进行编译。
HZ=1000
,则定时器每秒中断1000次。对于i386机器,使用HZ=1000
是可以的。为了良好的操作,PREEMPT_KERNEL选项应该打开。有些内核不支持这个选项。您可以通过搜索来查找它们。
最近的内核,例如2.6.35.10,支持NO_HZ选项,它打开了动态滴答声。这意味着在空闲时不会有计时器滴答声,但在指定的时刻将生成计时器滴答声。
有一个RT补丁可用于内核,但硬件支持非常有限。
通常,RTAI是解决问题的全杀手解决方案,但其硬件支持非常有限。然而,像emc2这样的良好CNC控制器使用RTAI进行时钟同步,可能是5000 Hz,但安装它可能需要一些努力。
如果可以的话,您可以添加硬件来生成脉冲。这将使系统适应任何操作系统版本。
您正在运行Linux 2.4内核吗?
来自VMware KB文章#1420(http://kb.vmware.com/kb/1420)。
Linux客户操作系统通过计算定时器中断来保持时间。未打补丁的2.4及更早版本的内核将虚拟系统定时器编程为请求100Hz(每秒100个中断)的时钟中断。另一方面,2.6内核请求以1000Hz的频率进行中断-十倍于此。一些由发行商修改以包含2.6功能的2.4内核也会请求1000Hz中断,或者在某些情况下,请求其他速率的中断,例如512Hz。
Linux内核有ktimer补丁:
http://lwn.net/Articles/167897/
http://www.kernel.org/pub/linux/kernel/projects/rt/
HTH
对于简单的实时应用程序,您不需要RTOS。所有现代处理器都有通用定时器。获取您正在使用的目标CPU的数据表。在内核源代码中,在arch目录下,您将找到特定于处理器的源代码,以了解如何处理这些定时器。
您可以采取两种方法:
1)您的应用程序仅运行您的状态机,没有其他任何操作系统。Linux只是您的“引导加载程序”。创建一个内核对象,安装一个字符设备。插入内核时,设置您的GP计时器连续运行。您知道它的工作频率。现在,在内核中显式禁用您的看门狗。现在禁用中断(硬件和软件)。在单CPU Linux内核上,调用spin_lock()将实现此目的(永远不要释放它)。 CPU是您的。忙碌循环,检查GPT的值,直到所需的滴答数已经过去,当它们过去时,为下一个超时设置一个值并进入处理循环。只需确保您的代码的突发时间小于1ms即可。
2) 第二个选项。这假设您正在运行一个抢占式的Linux内核。在运行中的操作系统旁边设置一个未使用的GPT。现在,设置一个中断,在您的1ms超时发生之前触发一些可配置的余量(例如50-75微秒)。当中断触发时,您将立即禁用中断并旋转等待1ms窗口出现,然后进入您的状态机,并随后在等待OUT上启用中断。这考虑到您与内核中的其他事物合作,这些事物会禁用中断。这假定没有其他内核活动会长时间锁定中断(超过100us)。现在,您可以测量您的触发事件的准确性,并使窗口变大,直到满足您的需求。
如果您想了解RTOS的工作原理...或者如果您正在尝试解决具有多个实时责任的控制问题...那么请使用RTOS。