4.15 版本内核调用 init_timer()函数出错

在学习init_timer()过程中,编写了一个字符设备的内核模块,实现每隔1s,调用回调函数,回调函数中记录秒数。

然后编译过程中发现编译出错,报错找不到init_timer()函数,在linux kernel的/include目录中使用grep init_timer ./ -r -n命令寻找无果,发现内核中定时器提供的api从4.15版本linux kernel以后发生了一些变化,在此做一些记录。

4.15版本之前:

4.15版本之前的定时器结构体定义(/include/linux/timer.h)

其中,结构体成员void  (*function)(unsigned long);

                             unsigned long   data;

上述两个结构体成员在4.15版本之后发生了变化

定时器结构体成员讲解:

unsigned long expires成员,一般设置成expires = jiffies + HZ*x

定时器的实现原理是通过时钟芯片,每隔一段时间进入一次中断,进入中断的次数叫做滴答数,在内核中使用jiffies表示。

对于每个定时器,都有一个定时器结构体表示(上述结构体)。结构体中都有一个jiffies的比较值,每隔一段时间,时钟芯片进入一次中断,即表示一次滴答,就对jiffies++。并且比较当前定时器结构体中的expires成员,滴答数超过了expires,那么就调用回调函数。

expires = jiffies + HZ*x中,HZ代表1s进入中断的次数,也就是滴答数。1/HZ表示多长时间进入一次中断。那么HZ*x就表示x秒的滴答数,即进入中断的次数,也就是表示x秒后,调用回调函数。

void (*function)(unsigned long)成员,函数指针,表示回调函数

unsigned long data成员,给回调函数传递的参数。

一般传递数据结构体的首地址。

定时器的函数讲解:

(1)初始化定时器——init_timer(struct timer_list*)

        在模块入口函数初始化定时器

(2)增加定时器,定时器开始记时——void add_timer(struct timer_list *timer)

        模块入口函数 或 open 或 希望定时器开始工作的地方。定时器在内核中是通过链表管理的,          可以设置多个定时器,每个定时器都是定时器链表的节点,只有使用add_timer函数将定时器           节点加到定时器链表中,定时器才开始运行。

        同时要先对定时器结构体成员进行初始化

(3)删除定时器,定时器停止工作——int del_timer(struct timer_list *timer)

(4)修改定时器——int mod_timer(struct timer_list *timer,unsigned long expires)

        

        假如我们设置的定时器节点,是3s后开始调用回调函数,回调函数开始执行。那么我们想              再过3s,也就是6s后,回调函数还会再次被调用吗?

        答案是不会,只会调用一次。那么如果我们想每隔3s就调用一次回调函数,那么我们在调用             回调函数的最后,再修改一次定时器,那么每隔3s,就会实现回调函数调用一次。

定时器步骤:

定时器代码示例:

#include <linux/module.h>
#include<linux/timer.h>
MODULE_LICENSE("GPL");
struct timer_list my_timer;  //声明定时器全局变量

// 自定义定时器到期执行的函数,在此只有显示的功能,不做任何处理
void my_timer_function(unsigned long data)
{
    printk("In the my_timer_function\n");
    printk("the jiffies is :%ld\n", jiffies);     //显示当前的节拍数
    struct timer_list *mytimer = (struct timer_list *)data;
    printk("the expries of my_timer1 is :%ld\n", mytimer->expires);                                              
}

int __init setup_timer_init(void)
{
    printk("my_timer will be created.\n");
    printk("the jiffies is :%ld\n", jiffies);      //显示当前的节拍数
    // 初始化定时器变量的function和data字段
    init_timer(&my_timer);//设置定时器
    my_timer.expires = jiffies + 1*HZ;            //HZ=250,初始化字段expires的值
    my_timer.function = my_timer_function;
    my_timer.data = (unsigned long)my_timer;
    add_timer(&my_timer);                          //将定时器变量加入到合适的链表,激活定时器
    printk("my_timer init.\n");
    return 0;
}

void __exit setup_timer_exit(void)
{
    printk("Goodbye setup_timer\n");
    del_timer(&my_timer);  //删除定时器变量
}

module_init(setup_timer_init);
module_exit(setup_timer_exit);

4.15版本之后:

4.15版本之后的定时器结构体定义(/include/linux/timer.h)

可以发现,相对于4.15版本之前的定时器结构体,定时器结构体成员不需要给回调函数传递参数data,而且  void   (*function)(struct timer_list *);回调函数的形参类型直接变成了定时器结构体。

定时器函数讲解(只讲解跟4.1版本之前不同的函数)

(1)初始化定时器函数——timer_setup()

第一个参数是定时器结构体变量地址,第二个参数是回调函数名称

定时器代码示例:

#include <linux/module.h>
#include<linux/timer.h>
MODULE_LICENSE("GPL");
struct tests{
	int my_number;
	struct timer_list my_timer;
} my_data;  //声明定时器全局变量

// 自定义定时器到期执行的函数,在此只有显示的功能,不做任何处理
void my_timer_function(struct timer_list t)	//这里参数有变化!
{
    printk("In the my_timer_function\n");
    printk("the jiffies is :%ld\n", jiffies);     //显示当前的节拍数
    
    struct tests *datas = from_timer(datas,t,my_timer);//在这里从timer找到包含其的结构体首地址!
    printk("my number is :%d\n", tests->my_number);                                              
}

int __init setup_timer_init(void)
{
    printk("my_timer will be created.\n");
    printk("the jiffies is :%ld\n", jiffies);      //显示当前的节拍数
    my_data.my_timer.expires = jiffies + 1*HZ;            //HZ=250,初始化字段expires的值

    // 初始化定时器变量的function和data字段
    timer_setup(&my_data.my_timer, my_timer_function, 0);//设置定时器
    add_timer(&my_data.my_timer);     //将定时器变量加入到合适的链表,激活定时器
    printk("my_timer init.\n");
    return 0;
}

void __exit setup_timer_exit(void)
{
    printk("Goodbye setup_timer\n");
    del_timer(&my_data.my_timer);  //删除定时器变量
}

module_init(setup_timer_init);
module_exit(setup_timer_exit);

因为4.15linux内核版本之后的定时器结构体struct timer_list中,因为回调函数的形参变成了struct timer_list*定时器结构体指针,而定时器结构体指针在我的公共数据集中,此时我知道定时器结构体的地址 ,知道定时器结构体的名称,知道定时器结构体名称所在结构体的数据类型名称,那么我就可以使用container_of的子函数from_timer知道定时器结构体所在公用数据结构体的首地址。

并且我们还可以使用mod_timer,当执行完回调函数之后,在回调函数最后,我们再重新设置秒,还是1s一次,执行这个回调函数。这样每隔1s就可以执行一次回调函数

注意:回调函数是在中断中执行的,属于异常,不可使用阻塞函数,也不可被打断。在执行回调函数期间,定时器不运行。

 

 

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值