我需要在linux下以1ms的分辨率打开计时器。它用于递增计时器值,而计时器值又用于查看是否应触发各种事件。由于glibc要求,POSIX timerfd_create不是一个选项。我尝试过timer_create和timer_settimer,但我从它们得到的最好的是10ms的分辨率,较小的值似乎默认为10ms的分辨率。根据联机帮助页,Getittimer和setitimer的分辨率为10毫秒。
这个计时器的唯一方法我现在可以想到的是在我的主循环中使用带有CLOCK_MONOTONIC的clock_gettime,如果ms已经过了则测试,如果是,则增加计数器(然后检查各种事件是否应该触发) )。
有没有比在主循环中不断查询更好的方法呢?推荐的解决方案是什么?
我使用的语言很简单
更新
我使用的是2.6.26内核。我知道你可以在1kHz时中断它,然后POSIX timer_ *函数可以编程为1ms,但这似乎不可靠,我不想使用它,因为它可能需要一些新内核系统。一些库存内核似乎仍然配置了100Hz。我需要检测到这一点。该应用程序可以在我的系统以外的其他地方运行:)
我无法入睡1ms,因为可能会有网络事件我必须做出反应。
我是如何解决的 由于它并不重要,我只是声明全局定时器的分辨率为100ms。使用自己的计时器的所有事件必须为计时器到期设置至少100ms。我或多或少想知道是否会有更好的方法,因此问题。
为什么我接受了答案 我认为自由空间的答案最能说明为什么没有实时Linux系统就不可能实现。
答案 0 :(得分:18)
要获得1ms的分辨率计时器,请执行libevent所做的事情。
将你的计时器组织成min-heap,也就是说,堆的顶部是具有最早到期(绝对)时间的计时器(rb-tree也可以工作,但是有更多的开销)。在主事件循环中调用select()
或epoll()
之前,计算最早计时器到现在的到期时间之间的增量(以毫秒为单位)。将此增量用作select()
的超时。 select()
和epoll()
超时的分辨率为1毫秒。
我有一个定时器分辨率测试,它使用上面解释的机制(但不是libevent)。该测试测量所需的定时器到期时间与1ms,5ms和10ms定时器的实际到期时间之间的差异:
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。
答案 1 :(得分:15)
主循环中的轮询也不是一个答案 - 您的进程可能没有太多的CPU时间,因此在代码运行之前将超过10毫秒,这使得它没有实际意义。
10ms是大多数非realtime operating systems(RTOS)的标准定时器分辨率。但是在非RTOS中没有实际意义 - 调度程序和调度程序的行为将极大地影响您响应计时器到期的速度。例如,即使您有一个10ms以下的分辨率计时器,如果您的代码没有运行,也无法响应计时器到期。由于无法预测代码何时运行,因此无法准确响应计时器到期。
当然有实时Linux内核,请参阅http://www.linuxdevices.com/articles/AT8073314981.html获取列表。 RTOS提供了一些工具,您可以通过这些工具获得有关何时运行代码的软或硬保证。这是可靠和准确地响应定时器到期等的唯一方法。
答案 2 :(得分:6)
我不确定它是最好的解决方案,但您可能会考虑编写一个使用内核高分辨率计时器来执行计时的小型内核模块。基本上,您将创建一个设备文件,其读取时间间隔仅为1毫秒。
通过ztdummy模块在Asterisk PBX中使用了这种方法的一个示例。如果你谷歌搜索ztdummy,你可以找到执行此操作的代码。
答案 3 :(得分:5)
我认为即使在主循环中进行常量查询,您也无法使用标准Linux实现1 ms精度,因为内核无法确保您的应用程序始终获得CPU。例如,由于先发制人的多任务处理,您可以在几十毫秒内进入睡眠状态,而且您无能为力。
您可能需要查看Real-Time Linux。
答案 4 :(得分:3)
如果您的目标是x86平台,则应检查HPET计时器。这是具有高精度的硬件计时器。它必须得到你的母亲的支持(现在所有人都支持它),你的内核也应该包含它的驱动程序。我已经几次使用它没有任何问题,并且能够获得比1ms更好的分辨率。
以下是一些文档和示例:
答案 5 :(得分:2)
我似乎记得使用基于gettimeofday / usleep的轮询获得正常结果 - 我不需要一秒钟或任何时间1000个计时器,但我需要很好的准确性与我需要的刻度的时间 - 我的应用程序是一个MIDI鼓机控制器,我似乎记得得到亚毫秒精度,如果你不希望它听起来像一个非常糟糕的鼓手(特别是计算MIDI的内置延迟),你需要一个鼓机 - iirc (这是2005年所以我的记忆有点模糊)我在睡眠时间内达到200微秒的目标时间。
但是,我在系统上没有运行太多其他东西。如果你有一个受控制的环境,你可能会得到这样的解决方案。如果系统上有更多内容(观看cron激活更新等),那么事情可能会崩溃。
答案 6 :(得分:1)
您是否在Linux 2.4内核上运行?
来自VMware知识库文章#1420(http://kb.vmware.com/kb/1420)。
Linux客户操作系统保持不变 计时定时器中断的时间。 未修补的2.4及更早版本的内核 将虚拟系统计时器编程为 请求时钟中断为100Hz(100 每秒中断)。 2.6内核, 另一方面,请求中断 在1000Hz - 十倍的频率。一些 2.4分发供应商修改的内核也包含2.6特性 请求1000Hz中断,或某些 案件,其他费率的中断等 为512Hz。
答案 7 :(得分:1)
Linux内核有ktimer补丁:
http://lwn.net/Articles/167897/
http://www.kernel.org/pub/linux/kernel/projects/rt/
HTH
答案 8 :(得分:1)
首先,获取内核源代码并使用调整后的HZ参数进行编译。
HZ=1000
,计时器每秒中断1000次。可以将HZ=1000
用于i386计算机。为了良好的操作,应该打开PREEMPT_KERNEL选项。有 内核不能正确支持此选项。你可以通过检查出来 搜索。
最近的内核,即2.6.35.10,支持NO_HZ选项,后者转向 动态滴答。这意味着在空闲时没有计时器滴答, 但是会在指定时刻生成计时器滴答。
内核有一个RT补丁,但硬件支持非常有限。
一般来说,RTAI是解决您问题的最佳杀手解决方案 硬件支持非常有限。但是,好的CNC控制器就像 emc2,使用RTAI进行计时,可能是5000赫兹,但也可以 努力安装它。
如果可以,您可以添加硬件以生成脉冲。这会 一个可以适应任何操作系统版本的系统。
答案 9 :(得分:0)
你能在循环中至少使用nanosleep睡眠1ms吗?或者这是一个glibc的事情?
更新:没关系,我从手册页中看到“在进程再次运行之前,它可能需要比指定的时间长10毫秒”
答案 10 :(得分:0)
对于简单的实时应用,您不需要RTOS。所有现代处理器都有通用定时器。获取您正在处理的目标CPU的数据表。查看内核源代码,在arch目录下,您将找到处理器特定的源如何处理这些定时器。
您可以采取两种方法:
1)您的应用程序仅运行您的状态机,而不是其他任何东西。 Linux只是你的"引导加载程序。"创建一个安装字符设备的内核对象。插入内核后,将GP定时器设置为连续运行。你知道它运作的频率。现在,在内核中,明确禁用您的看门狗。现在禁用中断(硬件和软件)在单CPU Linux内核上,调用spin_lock()将完成此任务(永远不要放弃它。)CPU是你的。繁忙的循环,检查GPT的值,直到所需的滴答声已经过去,当它们有时,为下一个超时设置一个值并输入你的处理循环。只需确保代码的突发时间小于1毫秒
2)第二种选择。这假设您正在运行抢占式Linux内核。在正在运行的操作系统旁边设置未使用的GPT。现在,在1ms超时发生之前(例如50-75 uSec)设置一个中断来触发一些可配置的余量。当中断触发时,你将立即禁用中断并旋转等待1ms窗口发生,然后进入你的状态机,然后进入在等待OUT上启用中断。这说明您正在与内核中的其他东西合作以禁用中断。这假设没有其他内核活动可以长时间锁定中断(超过100us)。现在,您可以测量点火事件的准确性,并使窗口变大,直到满足您的需要。
如果您正试图了解RTOS的工作方式......或者您是否尝试解决具有多个实时责任的控制问题......那么请使用RTOS。
答案 11 :(得分:0)
使用" / dev / rtc0" (或" / dev / rtc")设备及其相关的ioctl()接口?我认为它提供了一个准确的计时器计数器不能将速率设置为1 ms,而是设置为接近值或1 / 1024sec(1024Hz),或设置为更高的频率,如8192Hz。