06、定时器实验 *
系统节拍率(tick rate)是操作系统管理时间的核心指标,它指每秒系统会触发多少次时钟中断。这个参数决定了系统对时间的最小分辨能力,直接影响任务调度的频率、延迟响应速度以及整体运行效率。
一、定时器
Linux内核的时间管理主要依靠系统定时器实现。这个定时器的工作原理是:设定一个未来时间点,到点后触发操作。内核需要依靠它来计算时间,但这种定时器精度不高(无法精确到很小的时间单位),所以不能用于需要高精度的场景。
更关键的是,这种定时器没有自动循环功能。当设定的目标时间到达后,定时器会自动关闭。如果需要重复定时(比如每秒触发一次),必须在定时器触发时的处理程序里,手动重新启动这个定时器。这样循环往复才能实现周期性计时功能。
1.1、timer_list 结构体
Linux 内核中使用 timer_list 结构体表示内核定时器, 该结构体定义在“内核源码/include/linux/timer.h”文件中, 具体内容如下所示:
struct timer_list {
struct hlist_node entry;
unsigned long expires;/* 定时器超时时间, 单位是节拍数 */
void (*function)(struct timer_list *);/* 定时处理函数 */
u32 flags;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
};
2
3
4
5
6
7
8
9
10
11
使用以下宏对 timer_list 结构体进行定义, _name 为定义的结构体名称, _function 为定时处理函数, 该宏同样定义在文件 “内核源码/include/linux/timer.h” 文件中, 如下所示:
#define DEFINE_TIMER(_name, _function) \
struct timer_list _name = \
__TIMER_INITIALIZER(_function, 0)
2
3
例如可以使用以下代码对定时器和相应的定时处理函数进行定义
DEFINE_TIMER(timer_test,function_test);//定义一个定时器
1.2、API接口
使用Linux定时器需要通过以下步骤操作:
初始化定时器时间
在调用add_timer()前,必须先设置定时时间
时间值由timer_list结构体的expires参数指定
单位是系统的时钟节拍数(可通过系统设置调整节拍频率)
添加定时器 void add_timer(struct timer_list *timer);
将定时器注册到内核
注册后定时器立即开始计时
删除定时器 int del_timer(struct timer_list *timer);
终止并移除指定定时器
修改定时器 int mod_timer(struct timer_list *timer, unsigned long expires);
更新定时器的到期时间值
如果定时器未运行,会自动重新激活
操作流程:先设置好expires参数的值,再通过add_timer启动定时器。若需调整时间,使用mod_timer更新;若要停止,用del_timer删除。所有操作都通过操作timer_list结构体完成。
-> Kernel Features
A-> Timer frequency (<choice> [=y])
2
1.3、节拍率
在Linux系统中,系统节拍率(比如默认的300Hz)决定了CPU每秒处理系统任务的次数。选择300Hz作为默认值主要有两个原因:
- 平衡精度和省电 300Hz意味着CPU每秒检查300次任务,这个频率既能让系统及时响应任务(比如程序运行、硬件操作),又不会因为检查太频繁(比如1000Hz)而白白消耗过多电量和计算资源。如果频率太低(比如100Hz),可能无法及时处理需要快速响应的任务。
- 适用大多数场景 这个数值对大多数设备都够用——无论是普通电脑、服务器还是嵌入式设备(比如路由器)。它不会因为追求极端性能而牺牲通用性,比如游戏电脑可能需要更高的频率,但普通用户用不着这么极致的配置。
简单来说,300Hz就像「 Goldilocks原则」(刚刚好):既不会太慢卡顿,也不会太激进浪费资源,所以成了默认选择。
1.4、jiffies节拍总数
Linux内核用一个叫jiffies的计数器来记录系统启动后经过的时钟节拍总数。这个计数器在开机后初始化为0,每当系统定时中断触发时,它的数值就加1。例如,如果系统设置的时钟频率是每秒100次(即每秒产生100个节拍),那么jiffies每秒就会增加100。
具体来说:
- 在64位系统中,这个计数器叫jiffies_64,用长整型存储
- 在32位系统中,直接叫jiffies,用普通整型存储
为了方便时间单位转换,系统提供了以下辅助工具:
jiffies转常见时间单位:
- jiffies_to_msecs:把节拍数转成毫秒
- jiffies_to_usecs:转成微秒
- jiffies_to_nsecs:转成纳秒
常见时间单位转jiffies:
- msecs_to_jiffies:把毫秒转成节拍数
- nsecs_to_jiffies:把纳秒转成节拍数
这些工具让开发者在编写涉及时间管理和定时任务的代码时,能方便地在系统时钟节拍数和我们熟悉的毫秒、微秒、纳秒等单位之间进行转换,避免了手动计算的麻烦。
二、实验程序
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
static void function_test(struct timer_list *t);//定义function_test定时功能函数
DEFINE_TIMER(timer_test,function_test);//定义一个定时器
static void function_test(struct timer_list *t)
{
printk("this is function test \n");
mod_timer(&timer_test,jiffies_64 + msecs_to_jiffies(5000));//使用mod_timer函数将定时时间设置为五秒后
}
static int __init timer_mod_init(void) //驱动入口函数
{
timer_test.expires = jiffies_64 + msecs_to_jiffies(5000);//将定时时间设置为五秒后
add_timer(&timer_test);//添加一个定时器
return 0;
}
static void __exit timer_mod_exit(void) //驱动出口函数
{
del_timer(&timer_test);//删除一个定时器
printk("module exit \n");
}
module_init(timer_mod_init);
module_exit(timer_mod_exit);
MODULE_LICENSE("GPL v2");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26