Linux下内核定时器

前言

本文的主要内容是Linux下的内核定时器介绍,包括基本概念和其相关的一些函数介绍,还有一个内核定时器的应用例子。


一、内核定时器

不同于单片机定时器,Linux内核定时器是一种基于未来时间点的计时方式,它以当前时刻为启动的时间点,以未来的某一时刻为终止点,类似于我们的闹钟。
内核定时器的精度不高,不能作为高精度定时器使用,其内核定时器不是周期性运行的,超时以后就会自动关闭,因此要想实现周期性的定时,就需要在定时处理函数中重新开启定时器。


二、相关的函数

1.timer_list结构体

timer_list结构体定义在/linux-4.1.15/include/linux/timer.h文件中。

struct timer_list {
    struct list_head entry;
    unsigned long expires;  //定时器的超时时间,不是时长,单位是节拍数
    struct tvec_base *base;
    void (*function)(unsigned long); //定时处理函数
    unsigned long data;  //传递给function函数的参数
    int slack;
};

expires为到期时间,单位是节拍数,其值等于定时的当前时钟节拍计数(存储在系统全局变量jiffies中)+ 定时时长对应的时钟节拍数量。

2.宏HZ

内核中有一个宏HZ,表示一秒所对应的节拍数量,可以通过这个宏来把时间转换成节拍数,1秒的表示如下:

expires = jiffies + 1*HZ;

在Linux根目录下打开.config文件,找到CONFIG_HZ所在的位置,如下图所示。
在这里插入图片描述
HZ的值可以自己设置,其定义在/linux-4.1.15/include/asm-generic/param.h文件中。

#ifndef __ASM_GENERIC_PARAM_H
#define __ASM_GENERIC_PARAM_H
#include <uapi/asm-generic/param.h>
# undef HZ
# define HZ     CONFIG_HZ   /* Internal kernel timer frequency */
# define USER_HZ    100     /* some user interfaces are */
# define CLOCKS_PER_SEC (USER_HZ)       /* in "ticks" like times() */
#endif /* __ASM_GENERIC_PARAM_H */

也可以通过make menuconfig命令打开内核进行配置,依次进到Kernel Features——>Timer frequency下进行设置。
在这里插入图片描述
在这里插入图片描述
这里有几个频率供自己选择。
在这里插入图片描述
HZ表示1秒的节拍数,这里HZ如果是100的话,时间精度就是10ms。
高的节拍率会提高系统时间精度,与此同时,高节拍率会导致中断的产生更加频繁,频繁的中断会加剧系统的负担,中断服务函数占用处理器的时间会增加。
Linux内核使用全局变量jiffies来记录系统从启动以来的系统节拍数,系统启动的时候会将jiffies初始化为0,jiffies定义在文件/include/linux/jiffies.h中。

extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;

jiffies_64和jiffies分别用于64位系统和32位系统,为了兼容不同的硬件,jiffies其实就是jiffies_64的低32位。
定时10ms:

jiffies + msecs_to_ jiffies(10);

定时10us:

jiffies + usecs_to_ jiffies(10);

定时10ns:

jiffies + nsecs_to_ jiffies(10);

3.宏DEFINE_TIMER

宏DEFINE_TIMER的原型如下:

DEFINE_TIMER(_name,_function,_expires,_data);

作用:静态定义结构体变量并且初始化function,expires,data成员。
参数介绍:
name:变量名
function:超时处理函数
expires:到期时间,一般在启动定时前需要重新初始化。
data:传递给超时处理函数的参数。

4.函数add_timer

add_timer函数原型如下:

void add_timer(struct timer_list *timer);

作用:向Linux内核注册定时器,注册之后,定时器才会开始运行。
参数介绍:
timer:要注册的定时器。

5.函数del_timer

del_timer函数原型如下:

void del_timer(struct timer_list *timer);

作用:删除一个定时器,不管定时器有没有被激活,都可以使用此函数删除。在多处理器系统上,定时器可能会在其他的处理器上运行,因此在调用该函数删除定时器之前要先等待其他处理器的定时处理器函数退出。
参数介绍:
timer:要删除的定时器。
返回值:返回0表明定时器没有被激活,返回1表示定时器已经激活。

6.函数mod_timer

mod_timer函数原型如下:

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

作用:用于修改定时值,如果定时器还没有被激活,该函数可以激活定时器。
参数介绍:
timer:要修改超时时间的定时器。
expires:修改后的超时时间。
返回值:返回0表明调用mod_timer函数前定时器没有被激活,返回1表明调用mod_timer函数前定时器已经激活。


三、代码文件

1.timer.c文件

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

static void timer_function(unsigned long data);
DEFINE_TIMER(test_timer,timer_function,0,0); //静态定义结构体变量并且初始化function,expires,data成员

static void timer_function(unsigned long data)
{
	printk("This is timer function!\n");
	mod_timer(&test_timer,jiffies + 1*HZ);   //1秒打印一次
	//mod_timer(&test_timer,jiffies + 2*HZ);  //2秒打印一次
	//mod_timer为超时修改函数,jiffies相当于当前时间,HZ表示一秒对应的时钟节拍数,这里如果不使用该函数,则只打印一次
}

static int hello_init(void)
{
	printk("hello world!\n");
	test_timer.expires = jiffies + 1*HZ; //定义好未来时刻的时间点
	add_timer(&test_timer);  //向Linux内核注册定时器
	return 0;
}

static void hello_exit(void)
{
	printk("bye!\n");
	del_timer(&test_timer); //删除定时器
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

2.Makefile文件

obj-m += timer.o
KDIR:=/linux/linux-4.1.15
PWD?=$(shell pwd)
all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	make -C $(KDIR) M=$(PWD) clean

四、运行结果

将上面的代码写好之后编译,然后将驱动发送至开发板,开发板加载驱动后的打印内容如下图所示。
在这里插入图片描述
由上图可以看到,打印语句每隔一秒执行一次。
修改代码,使其两秒打印一次,结果如下图所示。
在这里插入图片描述


总结

以上就是Linux下内核定时器的所有内容了,重点理解节拍数和时间之间的转换关系。
本文参考视频:https://www.bilibili.com/video/BV1Vy4y1B7ta?p=40

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux内核定时器内核用于在未来某个时间点或者特定时间段内调度执行某个函数的一种机制。它是一个软定时器,最终依赖于CPU的硬件定时器实现。对于Linux内核来说,它依赖于系统时钟节拍。内核定时器的处理函数在软中断中执行。它有几个特点:依赖于系统时钟节拍、只执行一次,超时后即退出。如果需要周期性的定时器,需要在超时处理函数中重新开启定时器。在Linux内核编程中常常会使用定时器,例如在驱动程序中使用定时器解决按键消抖、延时等待硬件就绪等问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【Linux驱动编程】如何使用内核定时器](https://blog.csdn.net/qq_20553613/article/details/106028620)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [【嵌入式Linux驱动开发】十四、了解Linux内核定时器使用流程,实现LED闪烁](https://download.csdn.net/download/weixin_38664427/14883898)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西岸贤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值