首页 > 代码库 > Linux下的hrtimer高精度定时器【转】

Linux下的hrtimer高精度定时器【转】

本文转载自:http://blog.csdn.net/waverider2012/article/details/38305785

hrtimer高精度定时器的interval由ktime_set(const long secs, const unsigned long nsecs)决定,可做到ns级。此处的例子为5ms interval:

[cpp] view plain copy
 
  1. #include <linux/kernel.h>  
  2. #include <linux/module.h>  
  3. #include <linux/hrtimer.h>  
  4. #include <linux/ktime.h>  
  5.   
  6. MODULE_LICENSE("GPL");  
  7.   
  8. static struct hrtimer hr_timer;  
  9. static struct work_struct wq_hrtimer;    
  10. static ktime_t ktime;  
  11. static unsigned int interval=5000; /* unit: us */  
  12. struct timespec uptimeLast;  
  13.   
  14. static unsigned int count=0;  
  15. #define COUNT_INTERVAL 4000  
  16. unsigned long long diff_tv(struct timespec start, struct timespec end) {  
  17.     return (end.tv_sec-start.tv_sec)*1000000000+(end.tv_nsec-start.tv_nsec);  
  18. }  
  19.   
  20. enum hrtimer_restart my_hrtimer_callback( struct hrtimer *timer )  
  21. {  
  22.     schedule_work(&wq_hrtimer);   
  23.     return HRTIMER_NORESTART;  
  24. }  
  25.   
  26. static void wq_func_hrtimer(struct work_struct *work)  
  27. {   
  28.     struct timespec uptime;  
  29.   
  30.     hr_timer.function = my_hrtimer_callback;   
  31.     ktime = ktime_set( interval/1000000, (interval%1000000)*1000 );  
  32.     hrtimer_start(&hr_timer, ktime, HRTIMER_MODE_REL );  
  33.   
  34.     /* print time every COUNT_INTERVAL*interval second*/  
  35.     if(count%COUNT_INTERVAL==0)   
  36.     {  
  37.         do_posix_clock_monotonic_gettime(&uptime);    
  38.         printk(KERN_INFO"hrtimer:%9lu sec, %9lu ns, interval_delay=%lu ns\n",   
  39.             (unsigned long) uptime.tv_sec, uptime.tv_nsec,  
  40.             (unsigned long)(diff_tv(uptimeLast, uptime)-interval*1000*COUNT_INTERVAL) \  
  41.                 /COUNT_INTERVAL);   
  42.         uptimeLast=uptime;  
  43.     }  
  44.     count++;  
  45. }   
  46.   
  47. static int __init module_hrtimer_init( void )  
  48. {  
  49.     struct timespec uptime;   
  50.       
  51.     printk(KERN_INFO"HR Timer module installing\n");  
  52.       
  53.     hrtimer_init( &hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL );  
  54.     
  55.     ktime = ktime_set( interval/1000000, (interval%1000000)*1000 );  
  56.     hr_timer.function = my_hrtimer_callback;  
  57.   
  58.     hrtimer_start( &hr_timer, ktime, HRTIMER_MODE_REL );  
  59.   
  60.     do_posix_clock_monotonic_gettime(&uptime);  
  61.     uptimeLast = uptime;  
  62.     printk(KERN_INFO"hrtimer:%9lu sec, %9lu ns\n", (unsigned long) uptime.tv_sec,    
  63.                                         uptime.tv_nsec );   
  64.   
  65.     INIT_WORK(&wq_hrtimer, wq_func_hrtimer);  
  66.     return 0;  
  67. }  
  68.   
  69. static void __exit module_hrtimer_exit( void )  
  70. {  
  71.     int ret;  
  72.   
  73.     ret = hrtimer_cancel( &hr_timer );  
  74.     if (ret)   
  75.         printk("The timer was still in use...\n");  
  76.   
  77.     printk("HR Timer module uninstalling\n");  
  78.     return;  
  79. }  
  80.   
  81. module_init(module_hrtimer_init);  
  82. module_exit(module_hrtimer_exit);  

 

如果在my_hrtimer_callback()里面直接返回HRTIMER_RESTART会导致立即重新进入my_hrtimer_callback()。这时shell对输入没有任何响应。

所以为了解决这个问题,创建了一个work queue,由my_hrtimer_callback() enqueue这个工作队列。在work queue的处理函数里面重启hrtimer。

但是这样带来的负面影响是进入hrtimer_callback和wq_func被调用之间有Linux系统调度引入的延迟,导致interval出现误差。经过实测,在ZC706缺省配置下,这个延迟大约是17.5us (hrtimer interval为5ms,每20秒计算一次interval误差)。

[plain] view plain copy
 
    1. root@zynq:~/nfs/hrtimer# insmod hrtimer.ko  
    2. HR Timer module installing  
    3. hrtimer:    2900 sec, 993366078 ns  
    4. hrtimer:    2900 sec, 998395278 ns, interval_delay=369966 ns  
    5. hrtimer:    2921 sec,  69525447 ns, interval_delay=17782 ns  
    6. hrtimer:    2941 sec, 139764655 ns, interval_delay=17559 ns  
    7. hrtimer:    2961 sec, 210029519 ns, interval_delay=17566 ns  
    8. hrtimer:    2981 sec, 280465631 ns, interval_delay=17609 ns  
    9. hrtimer:    3001 sec, 350677038 ns, interval_delay=17552 ns  
    10. hrtimer:    3021 sec, 420625114 ns, interval_delay=17487 ns  
    11. hrtimer:    3041 sec, 490744847 ns, interval_delay=17529 ns 

Linux下的hrtimer高精度定时器【转】