概述
文章目录
- 1.内核定时器
- 2.内核定时器数据结构
- 3.内核定时器API
- 3.1 timer_setup()
- 3.2 add_timer()
- 3.3 mod_timer()
- 3.4 del_timer()
- 3.5 del_timer_sync
- 4.相关概念介绍
- 4.1 节拍率
- 4.2 节拍数
- 5. 实例
1.内核定时器
在Linux 内核中有大量的函数需要时间管理,比如周期性的调度程序、延时程序等。
Linux内核定时器就是timer_list。硬件定时器提供时钟源,时钟源的频率可以设置,设置好以后就周期性的产生定时中断,系统使用定时中断来计时。
2.内核定时器数据结构
struct timer_list {
struct list_head entry, /*定时器列表*/
unsigned long expires, /*定时器到期时间,单位为节拍数*/
void (*function) (unsigned long), /*定时器处理函数*/
unsigned long data,/*作为参数被传入定时器处理函数*/
struct timer_base_s *base,
...
};
expires 字段表示期望定时器执行的 jiffies 值,到达该 jiffies 值时,将调用 function 函数,并传递 data 作为参数
3.内核定时器API
作用 | API |
---|---|
初始化定时器 | void timer_setup(struct timer_list *timer, void (*func)(struct timer_list *), unsigned int flags) |
注册定时器 | void add_timer(struct timer_list *timer) |
修改或再次启动定时器 | int mod_timer(struct timer_list *timer, unsigned long expires) |
删除定时器 | int del_timer(struct timer_list * timer) |
等待删除定时器 | int del_timer_sync(struct timer_list *timer) |
3.1 timer_setup()
作用:
timer_setup函数负责初始化timer_list类型变量,当我们定义了一个timer_list变量以后一定要先用timer_setup初始化一下。
参数:
timer:要初始化定时器。
func:定时器的回调函数,此函数的形参是当前定时器的变量。
flags: 标志位,直接给0就行。
返回值:
无
3.2 add_timer()
作用:
add_timer函数用于向Linux内核注册定时器,使用add_timer函数向内核注册定时器以后,定时器就会开始运行。
参数:
timer:要注册的定时器。
返回值:
无
3.3 mod_timer()
作用:
mod_timer函数用于修改定时值,如果定时器还没有激活的话,mod_timer函数会激活定时器。
参数:
timer:要修改超时时间(定时值)的定时器。
expires:修改后的超时时间。
返回值:
0,调用mod_timer函数前定时器未被激活;
1,调用mod_timer函数前定时器已被激活。
3.4 del_timer()
作用:
del_timer函数用于删除一个定时器,不管定时器有没有被激活,都可以使用此函数删除。在多处理器系统上,定时器可能会在其他的处理器上运行,因此在调用del_timer函数删除定时器之前要先等待其他处理器的定时处理器函数退出。
参数:
timer:要删除的定时器。
返回值:
0,定时器还没被激活;
1,定时器已经激活。
3.5 del_timer_sync
作用:
del_timer_sync函数是del_timer函数的同步版,会等待其他处理器使用完定时器再删除,del_timer_sync不能使用在中断上下文中
参数:
timer:要删除的定时器。
返回值:
0,定时器还没被激活;
1,定时器已经激活。
4.相关概念介绍
4.1 节拍率
内核中有一个宏HZ,表示一秒所对应的节拍数,可以通过这个宏来把时间转换成节拍数,1秒的表示如下:
mytimer.expires = jiffies+HZ;
HZ的值也可以通过make menuconfig自己设置:
但是为什么系统节拍率有这么多呢,为什么默认的节拍率这么小?为什么不选择大一点的呢?这里就引出了一个问题:高节拍率和低节拍率的优缺点。
高节拍优点:
高节拍率会提高系统时间精度,如果采用100Hz的节拍率,时间精度就是10ms,采用1000Hz的话时间精度就是1ms,精度提高了10倍。
- 提高内核定时器定时的准确度;
- 系统时间测量更精细;
- 系统调用poll()和select()能更高的精度运行;
- 提高进程抢占的准确度。
高节拍缺点:
高节拍率会导致中断的产生更加频繁,频繁的中断会加剧系统的负担,1000Hz和100Hz的系统节拍率相比,系统要花费10倍的“精力”去处理中断。
- 中断频率增高,系统负担增加;
- 中断处理程序占用处理器时间增多;
- 频繁打断处理器高速缓存。
4.2 节拍数
jiffies:全局变量,用来记录自系统启动以来产生的节拍总数。启动时内核将该变量初始化为0,jiffies定义在文件include/linux/jiffies.h中。
前面说了HZ表示每秒的节拍数,jiffies表示系统运行的jiffies节拍数,所以jiffies/HZ就是系统运行时间,单位为秒。
不管是32位还是64位的jiffies,都有溢出的风险,溢出以后会重新从0开始计数,相当于绕回来了,因此有些资料也将这个现象也叫做绕回。假如HZ为最大值1000的时候,32位的jiffies只需要49.7天就发生了绕回,对于64位的jiffies来说大概需要5.8亿年才能绕回,因此jiffies_64的绕回忽略不计。(处理32位jiffies的绕回显得尤为重要,内核有专门的处理函数,此文不再深究。)
5. 实例
源文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
struct timer_list timer;
void timer_interrupt(struct timer_list* timer_01)
{
printk("%s: %lu, %sn", __func__, jiffies, "hello");
//到时后再次调用
mod_timer(&timer, jiffies+HZ);
}
static int __init mycdev_init(void)
{
printk(KERN_EMERG "%s : %s : %d - ok.n", __FILE__, __func__, __LINE__);
printk(KERN_EMERG "hello");
//初始化定时器
timer.expires = jiffies + HZ;//定时1s
timer_setup(&timer, timer_interrupt, 0);
//注册定时器
add_timer(&timer);
return 0;
}
static void __exit mycdev_exit(void)
{
//删除定时器
del_timer(&timer);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
Makefile
modname ?=
arch ?= x86
ifeq ($(arch),x86)
KERNELDIR := /lib/modules/$(shell uname -r)/build
CROSS_COMPILE :=
else
KERNELDIR := /home/linux/linux-stm32mp-5.10.61-stm32mp-r2-r0/linux-5.10.61/
CROSS_COMPILE := arm-linux-gnueabihf-
endif
CURRENTDIR := $(shell pwd)
CC := $(CROSS_COMPILE)gcc
all:
make -C $(KERNELDIR) M=$(CURRENTDIR) modules
#$(CC) test.c -o test
install:
@cp *.ko ~/nfs/rootfs/
#@cp test ~/nfs/rootfs
help:
@echo "make arch=arm|x86 modname=modules drivers source file name"
clean:
make -C $(KERNELDIR) M=$(CURRENTDIR) clean
#rm test
obj-m := $(modname).o
使用dmesg命令查看
最后
以上就是自由路灯为你收集整理的Linux内核定时器1.内核定时器2.内核定时器数据结构3.内核定时器API4.相关概念介绍5. 实例的全部内容,希望文章能够帮你解决Linux内核定时器1.内核定时器2.内核定时器数据结构3.内核定时器API4.相关概念介绍5. 实例所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复