zoukankan      html  css  js  c++  java
  • Linux驱动技术(七) _内核定时器与延迟工作

    内核定时器

    软件上的定时器最终要依靠硬件时钟来实现,简单的说,内核会在时钟中断发生后检测各个注册到内核的定时器是否到期,如果到期,就回调相应的注册函数,将其作为中断底半部来执行。实际上,时钟中断处理程序会触发TIMER_SOFTIRQ软中断,运行当前处理器上到期的所有定时器。
    设备驱动程序如要获得时间信息以及需要定时服务,都可以使用内核定时器。

    jiffies

    要说内核定时器,首先就得说说内核中关于时间的一个重要的概念:jiffies变量,作为内核时钟的基础,jiffies每隔一个固定的时间就会增加1,称为增加一个节拍,这个固定间隔由定时器中断来实现,每秒中产生多少个定时器中断,由在<linux/param.h>中定义的HZ宏来确定,如此,可以通过jiffies获取一段时间,比如jiffies/HZ表示自系统启动的秒数。下两秒就是(jiffies/HZ+2),内核中用jiffies来计时,秒转换成的jiffies:seconds*HZ,所以以jiffiy为单位,以当前时刻为基准计时2秒:(jiffies/HZ+2)*HZ=jiffies+2*HZ如果要获取当前时间,可以使用do_gettimeofday(),该函数填充一个struct timeval结构,有着接近微妙的分辨率。

    //kernel/time/timekeeping.c
     473 /**
     474  * do_gettimeofday - Returns the time of day in a timeval
     475  * @tv:         pointer to the timeval to be set
     476  *
     477  * NOTE: Users should be converted to using getnstimeofday()
     478  */
     479 void do_gettimeofday(struct timeval *tv)   
    

    驱动程序为了让硬件有足够的时间完成一些任务,常常需要将特定的代码延后一段时间来执行,根据延时的长短,内核开发中使用长延时短延时两个概念。长延时的定义为:延时时间>多个jiffies,实现长延时可以用查询jiffies的方法:

    time_before(jiffies, new_jiffies);
    time_after(new_jiffiesmjiffies);
    

    **短延时的定义为:延迟事件接近或短于一个jiffy,实现短延时可以调用

    udelay();
    mdelay();
    

    这两个函数都是忙等待函数,大量消耗CPU时间,前者使用软件循环来延迟指定数目的微妙数,后者使用前者的嵌套来实现毫秒级的延时。

    定时器

    驱动可以注册一个内核定时器,来指定一个函数在未来某个时间来执行。定时器从注册到内核开始计时,达到指定的时间后会执行注册的函数。即超时值是一个jiffies值,当jiffies值大于timer->expires时,timer->function就会被执行。API如下

    //定一个定时器
    struct timer_list my_timer;
    
    //初始化定时器
    void init_timer(struct timer_list *timer);
    mytimer.function = my_function;
    mytimer.expires = jiffies +HZ;
    
    //增加定时器
    void add_timer(struct timer_list *timer);
    
    //删除定时器
    int del_tiemr(struct timer_list *timer);
    
    

    实例

    static struct timer_list tm;
    struct timeval oldtv;
    
    void callback(unsigned long arg)
    {
    	struct timeval tv;
    	char *strp = (char*)arg;
    	do_gettimeofday(&tv);
    	printk("%s: %ld, %ld
    ", __func__,
    		tv.tv_sec - oldtv.tv_sec,
    		tv.tv_usec- oldtv.tv_usec);
    	oldtv = tv;
    	tm.expires = jiffies+1*HZ;
    	add_timer(&tm);
    }
    
    static int __init demo_init(void)
    {
    	init_timer(&tm);
    	do_gettimeofday(&oldtv);
    	tm.function= callback;
    	tm.data    = (unsigned long)"hello world";
    	tm.expires = jiffies+1*HZ;
    	add_timer(&tm);
    	return 0;
    }
    

    延迟工作

    除了使用内核定时器完成定时延迟工作,Linux内核还提供了一套封装好的"快捷方式"-delayed_work,和内核定时器类似,其本质也是利用工作队列和定时器实现,

    //include/linux/workqueue.h
    100 struct work_struct {           
    101         atomic_long_t data;
    102         struct list_head entry;
    103         work_func_t func;
    104 #ifdef CONFIG_LOCKDEP
    105         struct lockdep_map lockdep_map;
    106 #endif
    107 };
    113 struct delayed_work {              
    114         struct work_struct work;
    115         struct timer_list timer;
    116 
    117         /* target workqueue and CPU ->timer uses to queue ->work */
    118         struct workqueue_struct *wq;
    119         int cpu;
    120 };
    

    struct work_struct
    --103-->需要延迟执行的函数, typedef void (*work_func_t)(struct work_struct *work);

    至此,我们可以使用一个delayed_work对象以及相应的调度API实现对指定任务的延时执行

    //注册一个延迟执行
    591 static inline bool schedule_delayed_work(struct delayed_work *dwork,unsigned long delay)
    //注销一个延迟执行
    2975 bool cancel_delayed_work(struct delayed_work *dwork)    
    

    和内核定时器一样,延迟执行只会在超时的时候执行一次,如果要实现循环延迟,只需要在注册的函数中再次注册一个延迟执行函数。

    schedule_delayed_work(&work,msecs_to_jiffies(poll_interval));
    
  • 相关阅读:
    笨办法学Python——学习笔记4
    意识、语言、文字和程序感想
    笨办法学Python——学习笔记3
    把vim作为shell ide
    HDUYuna's confusion 树状数组 Or Multiset
    POJ3252 Round Numbers 组合数学
    HDU3874 Necklace 树状数组+离线处理
    UVA10212 The Last Nonzero Digit. 分解质因子+容斥定理
    HDU1041 Computer Transformation 大数
    HDUFish买电脑 二分查找
  • 原文地址:https://www.cnblogs.com/xiaojiang1025/p/6388604.html
Copyright © 2011-2022 走看看