我在我的BeagleBoard上安裝了Angstrom Linux。如何在Linux用戶空間中實現高度準確的計時器?
我想實現非常準確的計時器,它可以在每500us發射一次。我閱讀了hrtimers
,但我發現的所有實現都是針對內核空間的。我想在用戶空間中實現它。
是否有任何API可以調用這些hrtimers
,我可以在用戶空間或任何其他方式在linux中實現精確定時器?
我已將jiffy
設置爲幾納秒。
我在我的BeagleBoard上安裝了Angstrom Linux。如何在Linux用戶空間中實現高度準確的計時器?
我想實現非常準確的計時器,它可以在每500us發射一次。我閱讀了hrtimers
,但我發現的所有實現都是針對內核空間的。我想在用戶空間中實現它。
是否有任何API可以調用這些hrtimers
,我可以在用戶空間或任何其他方式在linux中實現精確定時器?
我已將jiffy
設置爲幾納秒。
最後,經過一番更多的努力,我發現這表明與信號處理(處理SIGALRM),類似於巴西萊Starynkevitch在他的評論暗示一起使用timer_create()
,clock_gettime()
代碼。
我在我的1 GHz Beaglebone上嘗試了它,時鐘類型爲CLOCK_MONOTONIC
,間隔爲500us。
在定時器過期的2%
中,出現2%
,這正好是500us(我忽略了納秒差異)。和96.6%
它是在500 +/- 10us
的範圍內。而其餘時間,平均誤差不超過+/- 50us。
我已經張貼在這裏的代碼稍加修改。我以下修改的代碼:
對於小的時間間隔爲10μs〜所述count
漸漸無限遞減,因此我添加上的信號處理程序本身內測試(count
)數的控制。
在運行計時器中添加printf
需要很多時間。因此,我將時差存儲在一個數組中,然後在最後一次測試結束後,我將所有內容都打印出來。
我認爲在unsigned long
(即以納秒爲單位)中計算時間差要好於在double
(以秒爲單位)中的計算,因爲它更準確並且速度可能更快。因此我修改了timerdiff
宏以輸出差值(以納秒爲單位)。由於我使用500us或更少的間隔,所以差異將永遠不會溢出unsigned long
的範圍。
正如你所看到的,即使修改之後,只有2%的結果精確到< 1us。所以我現在想喜歡不運行Linux的不必要的流程,簡化了,我的計劃更等
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#define NSEC_PER_SEC 1000000000L
#define MAX_TESTS 10000
#define timerdiff(a,b) (((a)->tv_sec - (b)->tv_sec) * NSEC_PER_SEC + \
(((a)->tv_nsec - (b)->tv_nsec)))
static struct timespec prev = {.tv_sec=0,.tv_nsec=0};
static int count = MAX_TESTS;
unsigned long diff_times[MAX_TESTS];
void handler(int signo)
{
struct timespec now;
register int i, correct=0;
if(count >= 0)
{
clock_gettime(CLOCK_MONOTONIC, &now);
diff_times[count]=timerdiff(&now, &prev);
prev = now;
count --;
}
else
{
for(i=0; i<MAX_TESTS; ++i)
{
if(diff_times[i]/1000 < 510 && diff_times[i]/1000 > 490)
{
printf("%d->\t", i);
correct++;
}
printf("%lu\n", diff_times[i]);
}
printf("-> %d\n", correct);
exit(0);
}
}
int main(int argc, char *argv[])
{
int i = 0;
timer_t t_id;
struct itimerspec tim_spec = {.it_interval= {.tv_sec=0,.tv_nsec=500000},
.it_value = {.tv_sec=1,.tv_nsec=0}};
struct sigaction act;
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
act.sa_flags = 0;
act.sa_mask = set;
act.sa_handler = &handler;
sigaction(SIGALRM, &act, NULL);
if (timer_create(CLOCK_MONOTONIC, NULL, &t_id))
perror("timer_create");
clock_gettime(CLOCK_MONOTONIC, &prev);
if (timer_settime(t_id, 0, &tim_spec, NULL))
perror("timer_settime");
while(1);
return 0;
}
您是否正在使用帶有「實時」內核搶佔補丁的內核?如果可以的話,你應該。你看到的不穩定性很可能是由於在定時器中斷觸發時內核中發生了不可搶佔的東西;內核搶佔計時器應該能夠在時間上更接近完美。 – steveha
謝謝@steveha,我會盡力... –
你知道你的解決方案有多少開銷嗎?是否有明顯的CPU開銷來生成和處理每隔500us觸發一次的信號? –
閱讀[時間(7)(http://man7.org/linux/man-pages/一些修改man7/time.7.html),然後考慮[clock_gettime(2)](http://man7.org/linux/man-pages/man2/clock_gettime.2.html)和'CLOCK_REALTIME'和/或[timer_create (2)](http://man7.org/linux/man-pages/man2/timer_create.2.html)和/或[timerfd_create(2)](http://man7.org/linux/man-pages /man2/timerfd_create.2.html);它可能適合您的需求。但是,準確度取決於硬件!我不確定你能得到一個2000Hz的週期性定時器。 –
Thanks @BasileStarynkevitch:我嘗試了一個類似的代碼,它的平均工作能力很強。我已在我的答案中發佈了詳細信息。 –
我已添加修改的代碼。 –