Linux定时器

article/2025/10/10 19:38:17

原文:正点原子Linux驱动手册

Linux内核时间管理

系统时间管理需要一个硬件定时器提供时钟 ,通常这个定时器时通用的,有利于系统移植。
像UCOS 或 FreeRTOS 一般使用 Systick 作为系统时钟源(systick 是cortex-m 内核提供的定时器,芯片厂商在使用cortex-m内核制作芯片时为了通用性也会选择它作为芯片的硬件定时器)。

同样Linux 也需要一个定时器来驱动,Linux内核就是使用它来进行一系列的时间管理操作。(具体是什么不深究)

Linux 内核中有大量的函数需要时间管理,比如周期性的调度程序、延时程序 、对于驱动编写来说最常用的是定时器。

节拍率(系统频率)

节拍率:系统每秒计数的节拍数。

硬件定时器提供时钟源,时钟源的频率可以设置, 设置好以后就周期性的产生定时中断,系统使用定时中断来计时。
中断周期性产生的频率就是系统频率,也叫做节拍率(tick rate)(有的资料也叫系统频率),单位是 Hz。 比如 1000Hz, 100Hz 等等说的就是系统节拍率。
1000Hz 就是每秒钟产生1000次中断,100Hz就是每秒钟产生100次中断。
系统节拍率是可以设置的,我们在编译 Linux 内核的时候可以通过图形化界面设置系统节拍率,按照如下路径打开配置界面:
-> Kernel Features -> Timer frequency (<choice> [=y])
在这里插入图片描述
设置好以后打开 Linux 内核源码根目录下的.config 文件中也可以发现配置后定义的系统节拍率:
在这里插入图片描述
Linux 内核会使用 CONFIG_HZ 来设置自己的系统时钟。打开文件 include/asm-generic/param.h,有如下内容:

6 # undef HZ
7 # define HZ CONFIG_HZ
8 # define USER_HZ 100
9 # define CLOCKS_PER_SEC (USER_HZ)

节拍率大小的优劣

  1. 当节拍率越大的时候,发生中断的次数就越多,时间精度也就越高如果采用 100Hz 的节拍率,时间精度就是 10ms(1秒钟内发生100次中断,一次中断的计时长度就是1s/100 = 10ms),采用1000Hz 的话时间精度就是 1ms,精度提高了 10 倍。
  2. 高节拍率会导致中断的产生更加频繁,频繁的中断会加剧系统的负担, 1000Hz 和 100Hz的系统节拍率相比,系统要花费 10 倍的“精力”去处理中断。中断服务函数占用处理器的时间增加。

jiffies

Linux 内核使用全局变量 jiffies 来记录系统从启动以来的系统节拍数(jiffies/HZ 就代表系统启动时间),系统启动的时候会将 jiffies 初始化为 0。

jiffies 定义在文件 include/linux/jiffies.h 中,定义如下:

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

jiffies_64 和 jiffies 其实是同一个东西, jiffies_64 用于 64 位系统,而 jiffies 用于 32 位系统。为了兼容不同的硬件, jiffies 其实就是 jiffies_64 的低 32 位, jiffies_64 和 jiffies 的结构如图:
在这里插入图片描述
HZ 表示每秒的节拍数, jiffies 表示系统运行的 jiffies 节拍数,所以 jiffies/HZ 就是系统运行时间,单位为秒。

不管是 32 位还是 64 位的 jiffies,都有 溢出 的风险,溢出以后会重新从 0 开始计数,相当于绕回来了,因此有些资料也将这个现象也叫做绕回。

假如 HZ 为最大值 1000 的时候, 32 位的 jiffies 只需要 49.7 天就发生了绕回,对于 64 位的 jiffies 来说大概需要5.8 亿年才能绕回,因此 jiffies_64 的绕回忽略不计。

处理 32 位 jiffies 的绕回显得很重要,Linux 内核提供了如下 所示的几个 API 函数来处理绕回:

time_after(unkown, known)
time_before(unkown, known) 
time_after_eq(unkown, known)
time_before_eq(unkown, known)		//unkown 通常为 jiffies, known 通常是需要对比的值。

假如计时 1s 时间,可以将1s 换算成对应的节拍数,那么就可以对比 known(jiffies) 和unkown (jiffies + 1s 的节拍数),jiffies 表示当前的节拍数,jiffies + 1s节拍数就代表1s后的节拍数。
通常情况下unkown 肯定是大于known,所以当unkown 小于known 时就是绕回了。

如果 unkown 超过 known 的话, time_after 函数返回真,否则返回假。如果 unkown 没有超过 known 的话 time_before 函数返回真,否则返回假。 time_after_eq 函数和 time_after 函数类似。

利用这个特点我们可以用作代码里经常使用的超时 timeout。

为了方便开发, Linux 内核提供了几个 jiffies 和 ms、 us、 ns 之间的转换函数(时间单位与节拍数之间的转换):

int jiffies_to_msecs(const unsigned long j)			//将 jiffies 类型的参数 j 分别转换为对应的毫秒、微秒、纳秒。
int jiffies_to_usecs(const unsigned long j) 
u64 jiffies_to_nsecs(const unsigned long j)long msecs_to_jiffies(const unsigned int m)			//将毫秒、微秒、纳秒转换为 jiffies 类型。
long usecs_to_jiffies(const unsigned int u) 
unsigned long nsecs_to_jiffies(u64 n)

Linux 定时器

  1. 这里所说的 Linux定时器是软定时器, 基于系统节拍率 (HZ)系统启动总节拍数 (jiffies) 实现。
  2. jiffies 加上需要计时的时间对应的节拍数,当计时时间到达后,会进入 定时处理函数(跟中断处理函数功能相似,但是不是中断,软件定时器也不依赖中断实现)。

jiffes +(time*HZ)

  1. Linux 软件定时器每次开启只能作一次计时,再次使用需要再次开启(可以在定时处理函数中开启)。

定时器API

既然是内核提供的定时器,自然由内核提供API。

Linux 内核使用 struct timer_list 描述一个定时器:

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

1、初始化定时器

初始化一个定时器:

void init_timer(struct timer_list *timer)

timer: 要初始化定时器。

上面这个函数类似于复位操作,我们还是需要对定时器做一些初始化,主要是对struct timer_list成员expires,function,data 的赋值,注意expires的单位是节拍数,可用上面的函数转化。

2、 add_timer 函数

add_timer 函数用于向 Linux 内核注册定时器,使用 add_timer 函数向内核注册定时器以后,定时器就会开始运行,函数原型如下:

void add_timer(struct timer_list *timer)

timer: 要注册的定时器。

3、del_timer

del_timer 函数用于删除一个定时器,不管定时器有没有被激活,都可以使用此函数删除。

在多处理器系统上,定时器可能会在其他的处理器上运行,因此在调用 del_timer 函数删除定时器之前要先等待其他处理器的定时处理器函数退出。

int del_timer(struct timer_list * timer)

返回值: 0,定时器还没被激活; 1,定时器已经激活。

del_timer_sync 函数

del_timer_sync 函数是 del_timer 函数的同步版,会等待其他处理器使用完定时器再删除,del_timer_sync 不能使用在中断上下文中。

int del_timer_sync(struct timer_list *timer)

返回值: 0,定时器还没被激活; 1,定时器已经激活。

4、mod_timer 函数

mod_timer 函数用于修改定时值,如果定时器还没有激活的话, mod_timer 函数会激活定时器! 可将此函数在定时处理中调用,就能循环定时。

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

timer: 要修改超时时间(定时值)的定时器。
expires: 修改后的超时时间。
返回值: 0,调用 mod_timer 函数前定时器未被激活; 1,调用 mod_timer 函数前定时器已被激活。

定时器示例程序

1 struct timer_list timer; /* 定义定时器 */
2 
3/* 定时器回调函数 */
4 void function(unsigned long arg)
5 {
6 /*
7 * 定时器处理代码
8 */
9
10 /* 如果需要定时器周期性运行的话就使用 mod_timer
11 * 函数重新设置超时值并且启动定时器。
12 */
13 mod_timer(&dev->timertest, jiffies + msecs_to_jiffies(2000));
14 }
15
16 /* 初始化函数 */
17 void init(void)
18 {
19 init_timer(&timer); /* 初始化定时器 */
20
21 timer.function = function; /* 设置定时处理函数 */
22 timer.expires=jffies + msecs_to_jiffies(2000);/* 超时时间 2 秒 */
23 timer.data = (unsigned long)&dev; /* 将设备结构体作为参数 */
24
25 add_timer(&timer); /* 启动定时器 */
26 }

Linux内核短暂延时函数

有时候我们需要在内核中实现短延时,尤其是在 Linux 驱动中。 Linux 内核提供了毫秒、微
秒和纳秒延时函数,这三个函数如下所示:

void ndelay(unsigned long nsecs)	/*纳秒、微秒和毫秒延时函数。*/
void udelay(unsigned long usecs) 	
void mdelay(unsigned long mseces)

http://chatgpt.dhexx.cn/article/LoqrED0H.shtml

相关文章

单片机定时器

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 一、定时器简介 C51中的定时器和计数器是同一个硬件电路支持的&#xff0c;通过寄存器配置不同&#xff0c;就可以将他当做定时器 或者计数器使用。 确切的说&#xff0c;定…

定时器使用

目录 一、定时器简单介绍 二、定时器的使用 三、定时器的实现 一、定时器简单介绍 定时器就相当于是闹钟 在网络编程中定时器非常关键的 比如浏览器浏览某个网页网卡了&#xff0c;浏览器就会转圈圈&#xff08;阻塞等待&#xff09;&#xff0c;这个等待不是无限等待&…

51单片机定时器/计数器(定时器中断)

51单片机定时器/计数器&#xff08;中断系统&#xff1a;定时器中断&#xff09; 实现功能 通过定时器终端控制LED灯 D1 间隔一秒闪烁 单片机型号&#xff1a;STC89C52 定时器介绍 1、51单片机定时器原理 定时器实质上就是一个加1计数器。它随着计数器的输入脉冲进行自加1&a…

定时器用法

在JavaScr中&#xff0c;定时器可以让我们在设定的时间间隔之后来执行代码&#xff0c;而不是在函数被调用后立即执行。而定时器分别有两种类型&#xff1a; 一次性定时器&#xff1a;仅在指定的延迟时间后触发一次。间隔性定时器&#xff1a;每隔一定的时间间隔就触发一次。 …

STM32定时器

目录 一 定时器的基本介绍 二 定时器的原理框图 1、定时器时钟 2 时基单元 3 捕获输入 4 PWM输出。 三 定时器的应用 1 定时器的基础定时计数功能 2 PWM比较输出 3 外部脉冲计数 a、外部触发输入&#xff08;ETR--外部时钟模式2&am…

SpringBoot定时器

SpringBoot定时器 1 介绍2 注解3 代码4 Cron表达式1. 格式2. 取值3. 特殊字符4. 经典案例 5 Cron既然那么麻烦就生成吧 1 介绍 定时器是一种控制任务延时调用&#xff0c;或者周期调用的技术。 作用&#xff1a;定时邮件、短信发送、更新数据、同步数据、检查数据库和缓存数据…

555定时器

555定时器 美国signetics公司1972年研制&#xff0c;取代机械式定时器&#xff0c;因为输入端有3个5k欧的电阻得名 电路结构 输入引脚 输出引脚 1.因为要接电容&#xff0c;电流较大&#xff0c;需要用oc门进行输出 2.需要接上拉电阻接vcc功能引脚 5号引脚对参考电压的影…

SysTick 定时器

11.1关于 SysTick 定时器 SysTick定时器(又名系统滴答定时器)是存在于Cortex-M3的一个定时器&#xff0c;只要是ARM Cotex-M系列内核的MCU都包含这个定时器。使用内核的SysTick定时器来实现延时&#xff0c;可以不占用系统定时器&#xff0c;节约资源。由于SysTick是在CPU核内…

JavaScript之定时器

定时器 一、 setTimeout() 定时器二、停止 setTimeout() 定时器三、setInterval() 定时器四、清除setInterval() 定时器五、电子时钟案例 在很多页面中&#xff0c;我们都可以看到一些倒计时或者和时间相关的效果&#xff0c;今天小熊将就JavaScript里面的倒计时做一概述。 首先…

STM32-通用定时器-定时器中断

1 STM32的定时器 STM32F103ZET6一共有8个定时器&#xff0c;其中分别为&#xff1a;高级定时器&#xff08;TIM1、TIM8&#xff09;&#xff1b;通用定时器&#xff08;TIM2、TIM3、TIM4、TIM5&#xff09;&#xff1b;基本定时器&#xff08;TIM6、TIM7&#xff09;。 …

Python——定时器

1.定时器 Timer定时器源码实现&#xff0c;和自定义一个线程方式一样&#xff0c;都是继承Thread类&#xff0c;重写了run()方法&#xff0c;只是实现的功能是延时执行一个函数或方法。 &#xff08;1&#xff09;线程定时器(Timer)解释&#xff1a; Timer类是Thread的子类&a…

[JavaEE]定时器

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录: 1.定时器的概念 2.标准库中的定时器 3.实现定时…

定时器的作用

一、简介。 在很多时候&#xff0c;我们设计网页时&#xff0c;为了某种表现形式&#xff0c;会使用到定时器这一功能&#xff0c;如&#xff1a;为了保证用户有仔细阅读我们的用户条款&#xff0c;我们会给确认按钮设置只有条款被打开&#xff0c;并超过5秒才允许点击。 二、…

定时器简介

文章目录 一.定时器基本介绍A.CPU时序B.定时器的原理 二.定时/计数器的相关寄存器A.定时器工作方式寄存器&#xff08;TMOD&#xff09;B.控制寄存器&#xff08;TCON&#xff09; 三.定时器的四种工作方式图解 一.定时器基本介绍 A.CPU时序 振荡周期&#xff1a;CPU外部晶振…

定时器基本常识

1.概念解读 1.1定时器和计数器&#xff0c;电路一样 1.2定时或者计数的本质就是让单片机某个部件数数 1.3当定时器用的时候&#xff0c;靠内部震荡电路数数 1.4当计数器用的时候&#xff0c;书外面的信号&#xff0c;读取针脚的数据 2.定时器怎么定时 定时器的本质原理&a…

定时器详解

1. 什么是定时器&#xff08;timer&#xff09; 定时器实际上就是Soc当中的一个内部外设。 &#xff08;1&#xff09;定时器与计数器 定时器常与计数器扯到一起&#xff0c;计数器也是soc当中的一个内部外设&#xff0c;计数器顾名思义是用来计数的&#xff0c;就和我们的秒…

定时器(Timer)

一、定时器是什么&#xff1f; 定时器类似于我们生活中的闹钟&#xff0c;可以设定一个时间来提醒我们。 而定时器是指定一个时间去执行一个任务&#xff0c;让程序去代替人工准时操作。 标准库中的定时器: Timer 方法作用void schedule(TimerTask task, long delay)指定dela…

STM32-定时器详解

目录 前言 一、定时器基本介绍 1. STM32定时器 2. 通用定时器功能和特点 3. 计数器模式 4. 定时器工作原理 a.定时器框图 b.时钟产生器部分 c.时基单元 d.输入捕获通道 e.输出比较通道&#xff08;PWM&#xff09; 二、定时器中断应用 1.内部时钟选择 2.计数器模式 …

typedef和#define

typedef是c语言中一个重要的关键字其作用是为一种数据类型定义了一个新的名字这里的类型包括&#xff08;int&#xff0c;char,double 等)和自定义数据类型&#xff0c;通俗一点来说就是为一种数据类型起一个别名 举个例子&#xff1a; 定义一个整型变量a并将其初始化为666&a…

typedef和define的区别、typedef的具体用法

typedef最核心的用法&#xff1a;给数据类型取别名&#xff0c;这个别名既可以是此数据类型的替换&#xff0c;也是指向此数据类型的指针。 具体用法&#xff08;对普通数据类型取别名&#xff09;&#xff1a; 对结构体数据类型取别名&#xff1a; typedef与define的区别&…