Linux内核 定时器 用法

56 篇文章 6 订阅
42 篇文章 2 订阅
【前言】
    最近在工程中接触到调用 Linux 内核函数配置定时器实现 LED 闪烁效果的代码。对定时器的使用方法写个简单小结。


【概述】
    定时器的用法不复杂。调用过程分为以下几步:
    1、声明一个 timer。
    2、初始化 timer。
    3、完善定时中断服务函数。
    4、注册 timer 到定时器链表。
    5、重新注册 timer。
    6、提前停止定时器 timer。(非必需步骤)


【函数调用】
    1、声明一个新的定时器可以使用语句 struct timer_list newTimer;

    2、初始化定时器就是要为 timer 设定中断服务函数及其参数、超时时间等值。
        代码示例:
        setup_timer(newTimer, fn, data);    // 实际上是一个宏。timer 是要设置的定时器,fn 是定时中断服务函数,data 是传递给 fn 的参数。
        newTimer.expires = jiffies + 10 * HZ;    // 设定 10 秒后发生定时中断
        工程代码示例:setup_timer(&trigger_data->timer, netdev_trig_timer, (unsigned long)trigger_data);

    3、 向中断服务函数中添加的代码应当位于上锁/解锁操作之间。
        代码示例:
        static void netdev_trig_timer(unsigned long arg)    // 我的工程中实际使用的回调函数,即 setup_timer 函数中的 fn 参数。根据需要不同,这个函数可以不同。中断服务在运行时需要上锁,运行结束时解锁。
        {
            struct led_netdev_data *trigger_data = (struct led_netdev_data *)arg;
            ...    // 变量声明
            write_lock(&trigger_data->lock);    // 写操作上锁
            ...    // 中断服务函数主体
            mod_timer(&trigger_data->timer, jiffies + trigger_data->interval);    // 为定时器 timer 重新注册。
            write_unlock(&trigger_data->lock);
        }

    4、 配置好定时器后,需要将定时器添加到定时器链表中才会生效。一旦将定时器添加进链表,定时器便开始工作了。
        函数原型:
        void add_timer(struct timer_list *timer);    // 一般只在初始化结束后调用,之后的周期性添加工作一般使用 mod_timer() 函数完成

    5、 定时器配在置好后只运行一次,执行完中断服务函数后定时器就会自动销毁。
        所以,如果想要实现周期性定时中断就必须在中断服务程序的结尾重新给定时器写入超时值。

        常用 mod_timer() 函数来完成这一步骤,其源码如下:
        int mod_timer(struct timer_list *timer, unsigned long expires)
        {
            int ret;
            unsigned long flags;
            spin_lock_irqsave(&timerlist_lock, flags);
            timer->expires = expires;    // 重新写入超时值
            ret = detach_timer(timer);    // 停止定时器运行,相当于 del_timer()
            internal_add_timer(timer);    // 对定时器进行注册,相当于 add_timer()
            spin_unlock_irqrestore(&timerlist_lock, flags);
            return ret;
        }
        需要注意,上面的源码意味着该函数也会将一个处于未激活状态的定时器激活。因此,使用了 mod_timer() 就无需再单独调用 add_timer() 了。
        代码示例:
        mod_timer(&trigger_data->timer, jiffies + trigger_data->interval);

    6、如果想要在定时器 中断发生前停止定时器,可以使用 del_timer() 函数。
        函数原型:
        int del_timer(struct timer_list *timer);


        在多 CPU 平台上可能发生要停止的定时器正在其它 CPU 上运行的情况,此时应该使用 del_timer_sync() 函数。
        函数原型:
        int del_timer_sync(struct timer_list *timer);

【相关概念】
    jiffies    是一个类型为 unsigned long 的全局变量,表示系统启动之后时钟芯片产生的节拍总数。
    Hz    每秒钟 jiffies 增加的数值。从 2.5版本内核开始这个参数的内核空间默认值修改为 1000,而以前的版本里该值是 100。可以在内核源码根目录下的 linux-4.3/.config 文件中查看 Hz 的内核默认值,命令 cat .config | grep -i config_hz。文件 /linux-4.3/include/asm-generic/param.h 中则有更详细的记录。

    所以,如果想要知道系统已经运行了多长时间,可以按照 jiffies/Hz 的方式进行计算,从而获得系统运行了多少秒。
    在 64位 系统中,直接在代码里使用 jeffies 变量只能访问到 低32位 的值。要获取完全 64位 数值需要调用 get_jiffies_64() 函数。

【延伸阅读】
    [1] 《linux 内核定时器 timer_list》
    [2] 《linux驱动之内核定时器驱动设计》
    [3] 《定时器使用和延迟执行》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值