Linux内核定时器使用

VERSION:5.4.24

参考:
include/linux/timer.h
kernel/time/timer.c
include/linux/jiffies.h

重要的结构体,函数

  • 定时器结构体

    struct timer_list {
    	/*
    	 * All fields that change during normal runtime grouped to the
    	 * same cacheline
    	 */
    	struct hlist_node	entry;
    	unsigned long		expires;
    	void			(*function)(struct timer_list *);
    	u32			flags;
    
    #ifdef CONFIG_LOCKDEP
    	struct lockdep_map	lockdep_map;
    #endif
    };
    /*
     * 只关注两个成员
     * expires : 定时器超时时间
     * function: 定时器超时的执行函数
     */
    
  • 定时器初始化

    • 方法一

      /*
       * @_name: 定义的timer_list名字
       * @_function: 定时器超时的执行函数
       */
      #define DEFINE_TIMER(_name, _function)				\
      	struct timer_list _name =				\
      		__TIMER_INITIALIZER(_function, 0)
      
    • 方法二

      //include/linux/timer.h
      /*
       * @timer: 传入的timer_list指针
       * @callback: 定时器超时的执行函数
       * @flags: 标志位,默认为0即可
       */
      #define timer_setup(timer, callback, flags)			\
      	__init_timer((timer), (callback), (flags))
      
  • 启动定时器

    /**
      * add_timer - 启动一个计时器
      * @timer:要添加的定时器
      *
      * 将来,内核将从-> expires点的计时器中断中执行-> function(@timer)回调。 当前时间是“jiffies”。
      *
      * 定时器的 ->expires, ->function 字段必须在调用此函数之前设置。
      *
      * 过去带有 ->expires 字段的计时器将在下一个计时器滴答中执行。
      */
    void add_timer(struct timer_list *timer)
    {
    	BUG_ON(timer_pending(timer));
    	mod_timer(timer, timer->expires);
    }
    EXPORT_SYMBOL(add_timer);
    
  • 修改定时器超时时间

    /**
      * mod_timer - 修改定时器的超时时间
      * @timer:要修改的定时器
      * @expires: jiffies 中的新超时
      *
      * mod_timer() 是更新活动计时器的过期字段的更有效方法(如果计时器处于非活动状态,它将被激活)
      *
      * mod_timer(timer, expires) 等价于:del_timer(定时器); 计时器->过期=过期; 添加计时器(计时器);
      *
      * 请注意,如果同一计时器有多个未序列化的并发用户,那么 mod_timer() 是修改超时的唯一安全方法,因为 add_timer() 无法修改已运行的计时器。
      *
      * 该函数返回是否修改了一个挂起的计时器。
      *(即,非活动定时器的 mod_timer() 返回 0,活动定时器的 mod_timer() 返回 1。)
      */
    int mod_timer(struct timer_list *timer, unsigned long expires)
    {
    	return __mod_timer(timer, expires, 0);
    }
    EXPORT_SYMBOL(mod_timer);
    
  • 删除定时器

    /**
      * del_timer - 停用计时器。
      * @timer:要停用的计时器
      *
      * del_timer() 停用计时器 - 这适用于活动和非活动计时器。
      *
      * 该函数返回是否取消了待处理的计时器。
      *(即,非活动定时器的 del_timer() 返回 0,活动定时器的 del_timer() 返回 1。)
      */
    int del_timer(struct timer_list *timer)
    {
    	struct timer_base *base;
    	unsigned long flags;
    	int ret = 0;
    
    	debug_assert_init(timer);
    
    	if (timer_pending(timer)) {
    		base = lock_timer_base(timer, &flags);
    		ret = detach_if_pending(timer, base, true);
    		raw_spin_unlock_irqrestore(&base->lock, flags);
    	}
    
    	return ret;
    }
    EXPORT_SYMBOL(del_timer);
    
  • 时间转换

    /**
      * msecs_to_jiffies: - 将毫秒转换为 jiffies
      * @m: 以毫秒为单位的时间
      *
      * 转换如下:
      *
      * 负值意味着“无限超时”(MAX_JIFFY_OFFSET)
      *
      * “太大”值[会导致大于 MAX_JIFFY_OFFSET 值]也意味着“无限超时”。
      *
      */
    static __always_inline unsigned long msecs_to_jiffies(const unsigned int m)
    {
    	if (__builtin_constant_p(m)) {
    		if ((int)m < 0)
    			return MAX_JIFFY_OFFSET;
    		return _msecs_to_jiffies(m);
    	} else {
    		return __msecs_to_jiffies(m);
    	}
    }
    

内核定时器使用示例一

  • 第一步:构造结构体

    /* 定义结构体 */
    static struct timer_list my_timer;
    
    /* 实现超时函数 */
    void timer_work(struct timer_list * ptimer)
    {
    	/* user code */
    }
    
  • 第二步:初始化定时器

    /* 
     * 在合适的位置添加这一句代码来初始化定时器 
     * 一般在驱动的 probe,open函数里使用  
     */
    timer_setup(&my_timer, timer_work, 0);
    
  • 第三步:启动定时器

    /* 在合适的位置添加这一句代码来启动定时器 */
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(500)); // 500ms 后执行超时函数
    

内核定时器使用示例二

  • 第一步:构造结构体

    /* 实现超时函数 */
    void timer_work(struct timer_list * ptimer)
    {
    	/* user code */
    }
    
  • 第二步:初始化定时器

    /* 
     * 在函数体外面使用,可以加static关键字
     */
     DEFINE_TIMER(my_timer, timer_work);
    
  • 第三步:启动定时器

    /* 在合适的位置添加这一句代码来启动定时器 */
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(500)); // 500ms 后执行超时函数
    

注意事项

  • timer_list 是中断上下文,要遵守中断处理原则:不能嵌套。
    所以 i2c_transfer 之类涉及中断的函数不能在超时函数里使用。否则会导致内核奔溃。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值