Linux驱动---内核定时器

1、Linux内核定时器timer_list

内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制

2、timer_list 4.15之前的版本

void callback(unsigned long arg)

struct timer_list {
	/*
	* A1l fields that change during normal runtime grouped to the
	* same cacheline
	*/
	struct list_head entry;/*定时器列表*/
	unsigned long expires;/* 定时器到期时间*/
	struct tvec_base *base;
	void (*function) (unsigned long);/*定时器处理函数*/
	unsigned 1ong data;
};

3、定时器操作 4.15之前

●初始化
struct timer_list mytimer ;
void init_timer(struct timer_list *timer);|
●增加定时器
void
add_timer(struct timer_list *timer)
●修改定时器的expire
int mod_timer(struct timer_list *timer, unsigned 1ong expires)
●删除定时器
int del_timer(struct timer_list *timer)

4、内核定时器执行过程

内核定时器的调度函数运行过一次后就不会再被运行了(相当于自动注销) , 但可以通过在被调度的函数中重新调度自己来周期运行。
在SMP系统中,调度函数总是在注册它的同一CPU上运行,以尽可能获得缓存的局域性。

5、使用实例 4.15版本之前

void callback(unsigned long arg)
{
	add_timer(&tm) ; //重新开始计时
}
init_timer(&tm) ; //初始化内核定时器
tm.function  = callback; //指定定时时间到后的回调函数
tm.data  = (unsigned 1ong)"hello world"  ;//回调函数的参数
tm.expires = jiffies+1*HZ; //定时时间
add_timer(&tm); //注册定时器

6、Jiffies

●jiffies是记录着从电脑开机到现在总共的时钟中断次数
●5s后jiffies + 5*Hz
●可以计算一下:
32位: 497天后溢出
64位:天文数字

7、其他函数do_gettimeofday

●在Linux中可以使用函数do_ gettimeofday()函数来得到精确时间。它的精度可以达到微妙,是与C标准库中
gettimeofday()用法相同的函数。
●do_ gettimeofday)会把目前的时间用tv结构体返回,当地时区的信息则放到tV所指的结构中。

struct timeval {
time_t tV_sec;   /* seconds */
suseconds_t tV_usec; /* microseconds */
};

8、实例代码 4.15版本之前

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/timer.h>

static struct timer_list tm;
struct timeval oldtv;

void callback(unsigned long arg)
{
    struct timeval tv;
    char *strp = (char*)arg;
    
    printk("%s: %lu, %s\n", __func__, jiffies, strp);

    do_gettimeofday(&tv);
    printk("%s: %ld, %ld\n", __func__,
        tv.tv_sec - oldtv.tv_sec,        //与上次中断间隔 s
        tv.tv_usec- oldtv.tv_usec);        //与上次中断间隔 ms
    

    oldtv = tv;
    tm.expires = jiffies+1*HZ;    
    add_timer(&tm);        //重新开始计时
}

static int __init demo_init(void)
{
    printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);

    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;
}

static void __exit demo_exit(void)
{
    printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
    del_timer(&tm);        //注销定时器

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("yikoupeng");
MODULE_DESCRIPTION("timerlist");

9、效果 4.15版本之前

在这里插入图片描述


10 、timer_list 4.15之后的版本

在这里插入图片描述
初始化:
初始化从init_timer()变成了timer_setup()
在这里插入图片描述
在这里插入图片描述

11、示例代码 4.15版本之后

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/timer.h>

static struct timer_list tm;
struct timeval oldtv;

void callback(struct timer_list* timer)
{
    struct timeval tv;
    
    do_gettimeofday(&tv);
    printk("%s: %ld, %ld\n", __func__,
        tv.tv_sec - oldtv.tv_sec,        //与上次中断间隔 s
        tv.tv_usec- oldtv.tv_usec);        //与上次中断间隔 ms
    

    oldtv = tv;
    tm.expires = jiffies + 2*HZ;    
    add_timer(&tm);        //重新开始计时
}

static int __init demo_init(void)
{
    printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);

    timer_setup(&tm, callback, 0);
    do_gettimeofday(&oldtv);        //获取当前时间
    tm.function= callback;            //指定定时时间到后的回调函数
   
    add_timer(&tm);        //注册定时器
    return 0;
}

static void __exit demo_exit(void)
{
    printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
    del_timer(&tm);        //注销定时器

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("yikoupeng");
MODULE_DESCRIPTION("timerlist");

12、效果

在这里插入图片描述


13、内核定时器使用注意事项

●被调度的函数肯定是异步执行的,它类似于一种“软件中断”, 而且是处于非进程的上下文中,所以调度函数必须遵守以下规则:
●没有current指针、不允许访问用户空间【因为没有进程上下文,相关代码和被中断的进程没有任何联系】
●不能执行休眠(或可能弓|起休眠的函数)和调度。
●任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件。

14、其他时间函数

在这里插入图片描述

举例

time_before:

0.5秒后超时
●unsigned long timeout = jiffies + HZ/2;//注意jiffies值溢出回绕用宏time_ before 而非直timeout > jffries 

if(time_before(jiffies ,timeout)){
			//没有超时
}else{
			//超时
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值