• 5-3 Linux内核计时、延时函数与内核定时器【转】


    转自:http://www.xuebuyuan.com/510594.html

    5-3 Linux内核计时、延时函数与内核定时器 

    计时

    1、 
    内核时钟

    1.1   
    内核通过定时器(timer)中断来跟踪时间流

    1.2   
    硬件定时器以周期性的间隔产生时间中断,这个间隔(即频率)由内核根据HZ来确定,HZ是一个与体系结构无关的常数。

    1.3   
    这个时间间隔通常取1ms到10ms.

    2、 
    jiffies计算器

    2.1每次当定时器中断发生时,内核内部通过一个64位的变量jiffies_64做加一计数。

    2.2驱动程序开发者通常访问的是jiffies变量,它是jiffes_64的低32位。

    2.3jiffies是unsigned
    long型的变量,该变量被声明为volatile,这样可避免编译器对访问该变量的语句的优化。

    2.4jiffies记录了自最近一次Linux启动后到当前的时间间隔(即时钟中断发生的次数)。驱动程序常利用jiffies来计算不同事件间的时间间隔。

    3、HZ

    3.1 HZ表每秒中产生的定时中断次数

    3.2 HZ就代表1秒,HZ/2就代表0.5秒

    4、使用jiffies计数器

      
    #include<linux/jiffies.h>

      
    Unsigned long  current_i,  stamp_1, 
    stamp_half,  stamp_n;

      
    Current_i = jiffies;  //读取当前的值

      
    Stamp_1 = current_i+HZ;  //未来的一秒

      
    Stamp_half = current_i +HZ/2 ; //未来的半秒

      
    Stamp_n = current_i + n*HZ/1000; //未来的n毫秒

    5、为了防止jiffies溢出导致问题,最好使用宏比较

      
    #include<linux/jiffies.h>

      
    Int time_after(unsigned long a, unsigned long b);//判断a代表的时间是否在b之后。

      
    Int time_before(unsigned long a,unsigned long b);

      
    Int time_after_eq(unsigned long a ,unsigned long b);

      
    Int time_before_eq(unsigned long a,unsigned long b);

    6、

    (1)用户空间的timeval

    struct timeval{

     
    time_t tv_sev;//秒、

     
    suseconds_t tv_usec;//毫秒

    }

    (2)用户空间的timespec

     
    struct timespec{

        
    time_t tv_sec;//秒

        
    long  tv_nsec;//纳秒

      
    }

    7、内核空间jiffies和用户空间的timeval、timevspec的转换。

     
    #include<linux/time.h>

      
    unsigned long timespec_to_jiffies(struct timespec* value);

    void jiffies_to_timespec(unsigned long jiffie,struct timespec* value);

    unsigned long timeval_to_jiffies(struct timeval* value);

    void jiffies_to_timeval(unsigned long jiffies, struct 
    timeval* value);

    8、获取当前时间

      
    #include<linux/time.h>
       void do_gettimeofday(struct timeval*  
    tv);

      
    struct timespec current_kernel_time(void);

    9、使用jiffies延时(如果对延时的精度要求不是很高时,用忙等待)

      
    unsigned long j = jiffies + delay*HZ

      
    while(jiffies<j){  //jiffies表当前的滴答值,是不停地走的。
      
    /*do nothing*/

      
    }

    10、长延迟。

    while(time_before(jiffies,end_time)){

      
    Schedule();//在end_time之前,则调度

    }

    #include<linux/sched.h>
        signed long schedule_timeout(signed long timeout);//使用jiffies表示的延迟。先做超时,

    典型应用:

     set_current_state(TASK_INTERRUPTIBLE);//设置状态值,设置为可中断的睡眠

     schedule_timeout(delay);//延时

    11、短延时(忙等待延时,不发生休眠)

    #include<linux/delay> 

    //以下三个延时函数均是忙等待函数,因而在延迟过程中午饭运行其他任务。不发生休眠的。

    void ndelay(unsigned long nsecs); //延时纳秒

    void udelay(unsigned long usecs);//延时微秒

    void mdelay(unsigned long msecs);//延时毫秒

    12、不用忙等待的延时方式(将调用进程休眠给定时间)

    #include<linux/delay.h>

    void msleep(unsigened int millisecs);//休眠millisecs毫秒
    不可中断的休眠millisecs毫秒

    unsigned long msleep_interruptible(unsigned int millisecs);//可中断的休眠

    void ssleep(unsigned int seconds);//休眠seconds秒。

    内核定时器:

    13、定时器用于控制某个函数(定时器处理函数)在未来的某个特定时间执行。内核定时器注册的处理函数只执行一次—不是循环执行的。

    14、内核定时器被组织成双向链表,并使用struct
    time_list 结构描述。

    #include<linux/timer.h>

    struct timer_list{

     
    unsigned long expires;  //超时的jiffies

     
    void (*function)(unsigned long);//超时处理函数

      
    unsigned long data; //超时处理函数参数。

    };

    15、内核定时器操作函数

    15.1初始化(初始化定时器队列结构)

       
    void init_timer(struct timer_list * timer);

       
    struct timer_list TIMER_INITIALIZER(_function,_expires,_data);

    15.2添加定时器(启动定时器,开始倒计时)

       
    void add_timer(struct time_list *tiemer0:

    15.3删除定时器(在定时器超时前将它删除。定时器超时后,系统会自动地将它删除)

    15.4内核定时器使用模板

       
    static struct timer_list  key_timer;//定义内核定时器对象

       
    static void key_timer_handle(unsigned ong data)//定时器处理函数

       
    {

         
    ……

         
    //定时器处理函数具体执行代码

         
    ……

         
    // 定时器参数的更新,重启定时器

         
    key_timer.expires = jiffies+ KEY_TIMER_DELAY;

       
      add_time(&key_time); 
     //赋新值,可以实现
    循环

         
    ……

    }

    //设备驱动模块加载函数

    static int__ init xxx_init(void)

    {

     
    ……

     
    //初始化内核定时器

      init_time(&key_timer);

     
    key_timer.function=&key_timer_handle;

     
    key_timer.data = (unsigned long)key_desc;

     
    key_timer.expires = jiffies+KEY_TIMER_DELAY;

     
    //添加内核定时器(这是第一次启动)

     
    add_time(&key_time);  

    ……

    }

    Static void__exit xxx_exit(void)

    {
                  ……

      
    //删除定时器

      
    del_timer(&key_timer);

      
    ……

    }

    16、内核定时器与tasklet比较

    16.1相同点:
    在中断期间运行,时钟会在调度他们的同一个cpu上运行,软件中断的上下文,原子模式运行。(软件中断时打开硬件中断的同时执行某些异步任务的一种内核机制) 

     16.2不同的:不能要求TASKLECT在给定的时间执行。

    声明:本文非原创,整理自申嵌 

  • 相关阅读:
    Linux命令——mkdir
    UNIX 高手的 10 个习惯
    Linux命令——pwd
    Linux命令——cd命令
    Linux命令——ls命令
    denyhost安装脚本
    三台服务器无需密码相互访问
    字符串方法
    nginx简易安装
    shell 条件判断语句整理
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/5314250.html
Copyright © 2020-2023  润新知