• 【整理】--【KERNEL】内核定时器


    一、LINUX内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,其实现位于 <linux/timer.h> 和 kernel/timer.c 文件中。

    被调度的函数肯定是异步执行的,它类似于一种“软件中断”,而且是处于非进程的上下文中,所以调度函数必须遵守以下规则:

    1) 没有 current 指针、不允许访问用户空间。因为没有进程上下文,相关代码和被中断的进程没有任何联系。

    2) 不能执行休眠(或可能引起休眠的函数)和调度。

    3) 任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件。

    内核定时器的调度函数运行过一次后就不会再被运行了(相当于自动注销),但可以通过在被调度的函数中重新调度自己来周期运行。

    二、函数使用

    下面是关于timer的API函数:

    (0)初始化定时器:

     void init_timer(struct timer_list * timer);

    (1)增加定时器:

    void add_timer(struct timer_list * timer);

    注册定时器,将定时器放到内核定时器链表中

    (2)删除定时器:

    int del_timer(struct timer_list * timer);

    (3)修改定时器的expire:

    int mod_timer(struct timer_list *timer, unsigned long expires);

    三、使用流程

    使用定时器的一般流程为:

    (1)timer、编写function;

    (2)为timer的expires、data、function赋值;

    (3)调用add_timer将timer加入列表;

    (4)在定时器到期时,function被执行;

    (5)在程序中涉及timer控制的地方适当地调用del_timer、mod_timer删除timer或修改timer的expires。

    注意:

    mod_timer   内核通过函数mod_timer来实现已经激活的定时器超时时间。

              mod_timer函数也可以操作那些已经初始化,但还没有被激活的定时器,如果定时器没有激活,mod_timer会激活它。如果调用时定时器未被激活,该函数返回0,否则返回1。一旦从mod_timer函数返回,定时器都将被激活而且设置了新的定时值。如果需要在定时器超时前停止定时器,可以使用del_timer函数。

               被激活或未被激活的定时器都可以使用该函数,如果定时器还未被激活,该函数返回0;否则返回1。当删除定时器,必须小心一个潜在的竞争条件。当del_timer返回后,可以保证的只是:定时器不会被再激活,但是多处理器上定时器中断可能已经在其他处理上运行了,所以需要等待可能在其他处理器上运行的定时器处理程序都退出,这时需要使用del_timer_sync函数执行删除工。和del_timer函数不同,del_timer_sync数不能在中断上下文中使用。

    四、HZ和Jiffies

            系统定时器能以可编程的频率中断处理器。此频率即为每秒的定时器节拍数,对应着内核变量HZ。选择合适的HZ值需要权衡。HZ值大,定时器间隔时间 就小,因此进程调度的准确性会更高。但是,HZ值越大也会导致开销和电源消耗更多,因为更多的处理器周期将被耗费在定时器中断上下文中。

             HZ的值取决于体系架构。在x86系统上,在2.4内核中,该值默认设置为100;在2.6内核中,该值变为1000;而在2.6.13中,它又被 降低到了250。在基于ARM的平台上,2.6内核将HZ设置为100。在目前的内核中,可以在编译内核时通过配置菜单选择一个HZ值。该选项的默认值取 决于体系架构的版本。

             jiffies变量记录了系统启动以来,系统定时器已经触发的次数。内核每秒钟将jiffies变量增加HZ次。因此,对于HZ值为100的系统,1个jiffy等于10ms,而对于HZ为1000的系统,1个jiffy仅为1ms。

    六、实例

    #include <linux/timer.h>

    static struct timer_list key_autorepeat_timer =

    {

    function: key_callback

    };

    static void

    kbd_processkeycode(unsigned char keycode, char up_flag, int autorepeat)

    {

    char raw_mode = (kbd->kbdmode == VC_RAW);

    if (up_flag) {

    rep = 0;

    if(!test_and_clear_bit(keycode, key_down))

    up_flag = kbd_unexpected_up(keycode);

    } else {

    rep = test_and_set_bit(keycode, key_down);

    /* If the keyboard autorepeated for us, ignore it.

    * We do our own autorepeat processing.

    */

    if (rep && !autorepeat)

    return;

    }

    if (kbd_repeatkeycode == keycode || !up_flag || raw_mode) {

    kbd_repeatkeycode = -1;

    del_timer(&key_autorepeat_timer);

    }

    /*

    * Calculate the next time when we have to do some autorepeat

    * processing. Note that we do not do autorepeat processing

    * while in raw mode but we do do autorepeat processing in

    * medium raw mode.

    */

    if (!up_flag && !raw_mode) {

    kbd_repeatkeycode = keycode;

    if (vc_kbd_mode(kbd, VC_REPEAT)) {

    if (rep)

    key_autorepeat_timer.expires = jiffies + kbd_repeatinterval;

    else

    key_autorepeat_timer.expires = jiffies + kbd_repeattimeout;

    add_timer(&key_autorepeat_timer);

    }

    }

    }

  • 相关阅读:
    str_split 分隔中文出现乱码 替代函数
    PHP 浮点数 转化 整数方法对比 ceil,floor,round,intval,number_format
    php 判断字符串之间包含关系
    不解之谜
    正则匹配 特殊的 符号
    PHP 判断字符串 是否 包含另一个字符串
    PHP 删除 数组 指定成员
    HTML 权重标签的使用
    【PAT甲级】1094 The Largest Generation (25 分)(DFS)
    【PAT甲级】1093 Count PAT's (25 分)
  • 原文地址:https://www.cnblogs.com/apolloenterprise/p/4611045.html
Copyright © 2020-2023  润新知