Linux驱动开发-内核定时器

【摘要】 内核定时器是内核用来控制在未来某个时间点(基于jiffies(节拍总数))调度执行某个函数的一种机制,相关函数位于 和 kernel/timer.c 文件中。 当内核定时器定时时间到达时,会进入用户指定的函数,相当于软中断。内核定时器注册开启后,运行一次就不会再运行(相当于自动注销),我们可以重新设置定时器的超时时间,让定时器重复运行。
1. 内核定时器介绍 内核定时器是内核用来控制在未来某个时间点(基于jiffies(节拍总数))调度执行某个函数的一种机制,相关函数位于 和 kernel/timer.c 文件中。
当内核定时器定时时间到达时,会进入用户指定的函数,相当于软中断。内核定时器注册开启后,运行一次就不会再运行(相当于自动注销),我们可以重新设置定时器的超时时间,让定时器重复运行。
每当时钟中断发生时,全局变量jiffies(一个32位的unsigned long 变量)就加1,因此jiffies记录了linux系统启动后时钟中断发生的次数,驱动程序常利用jiffies来计算不同事件间的时间间隔。内核每秒钟将jiffies变量增加hz次。因此,对于hz值为100的系统,jiffy+1等于隔了10ms,而对于hz为1000的系统,jiffy+1仅为1ms。
内核定时器结构体:
下面列出了需要关心的成员
struct timer_list { unsigned long expires; //设置超时时间,用jiffies作为基准值 void (*function)(unsigned long); //类似中断服务函数,设置定时器到时后处理的函数 unsigned long data; //中断服务函数的参数}expires设置:以当前时间为基准加上延时时间,时间基准用jiffies变量表示,延时时间可以使用以下两个宏转换成jiffies单位。 2. 内核定时器相关api函数 2.1 修改定时器超时时间 函数原型 *int mod_timer(struct timer_list timer, unsigned long expires)
函数功能 修改定时器超时时间
函数参数 timer:对应的定时器结构体 expires:超时时间
函数返回值 成功返回 :修改成功的时间值
函数定义文件 \linux-3.5\kernel\timer.c
2.2 初始化定时器 函数原型 #define init_timer(timer)\
函数功能 初始化定时器结构
函数参数 timer:对应的定时器结构体
函数定义文件 \linux-3.5\include\linux\timer.h
2.3 关闭定时器 函数原型 int del_timer(struct timer_list *timer)
函数功能 关闭定时器,停用一个定时器。
函数参数 timer:对应的定 时器结构体
函数返回值 返回0:成功
函数定义文件 \linux-3.5\include\linux\timer.h
2.4 关闭定时器 函数原型 int del_timer_sync(struct timer_list *timer)
函数功能 关闭定时器,停用一个定时器,多处理器使用。如果编内核时不支持 smp(多处理器), del_timer_sync()和 del_timer()等价
函数参数 timer:对应的定时器结构体
函数返回值 返回0:成功
函数定义文件 \linux-3.5\include\linux\timer.h
2.5 转换时间(微妙单位) 函数原型 unsigned long usecs_to_jiffies(const unsigned int m)
函数功能 转换时间(微妙单位),用于填充定时器结构体,设置超时时间
函数参数 m:要转换的时间值(微妙为单位)
函数返回值 成功返回转换成功的时间。用于填充定时器结构体,设置超时时间
函数定义文件 \linux-3.5\kernel\timer.c
2.6 转换时间(毫秒为单位) 函数原型 unsigned long msecs_to_jiffies(const unsigned int m)
函数功能 转换时间(毫秒为单位),用于填充定时器结构体,设置超时时间
函数参数 m:要转换的时间值(毫秒为单位)
函数返回值 成功返回转换成功的时间。用于填充定时器结构体,设置超时时间
函数定义文件 \linux-3.5\kernel\timer.c
将jiffies单位转为struct timespec结构体表示:
void jiffies_to_timespec(const unsigned long jiffies, struct timespec *value);示例:jiffies_to_timespec(jiffies,&value);printk(value.ts_sec=%d\n,value.tv_sec);printk(value.tv_nsec=%d\n,value.tv_nsec); 2.7 初始化定时器的结构体成员 timer_initializer( _function, _expires, _data) 宏用于赋值定时器结构体的function、 expires、 data 和 base 成员, 这个宏的定义如下所示:(被define_timer宏调用)
#define timer_initializer(_function, _expires, _data) { \ .entry = { .prev = timer_entry_static }, \ .function = (_function), \ .expires = (_expires), \ .data = (_data), \ .base = &boot_tvec_bases, \ .slack = -1, \ __timer_lockdep_map_initializer( \ __file__ : __stringify(__line__)) \ } 2.8 初始化定时器并且赋值 define_timer( _na me , _functi o n, _e x pires, _data) 宏是定义并初始化定时器成员的“快捷方式”, 这个宏定义如下所示:
/*初始化定时器,并进行赋值*/#define define_timer(_name, _function, _expires, _data) \ struct timer_list _name = \ timer_initializer(_function, _expires, _data) 2.9 定时器初始化赋值 setup_timer()也可用于初始化定时器并赋值其成员, 其源代码如下:
//初始化定时器并进行赋值#define setup_timer(timer, fn, data) \ do { \ static struct lock_class_key __key; \ setup_timer_key((timer), #timer, &__key, (fn), (data));\ } while (0)static inline void setup_timer_key(struct timer_list * timer, const char *name, struct lock_class_key *key, void (*function)(unsigned long), unsigned long data){ timer->function = function; timer->data = data; init_timer_key(timer, name, key);} 3. 使用定时器的步骤 (1) 定义定时器结构体timer_list。
/*定义一个内核定时器配置结构体*/static struct timer_list mytimer ; (2) 设置超时时间,定义定时器处理函数和传参。
mytimer.expires=jiffies+ msecs_to_jiffies(1000); /*设置定时器的超时时间,1000毫秒*///或者//mytimer.expires=jiffies+hz; /*设置定时器的超时时间,1000毫秒*/mytimer.function = time_fun; /*定时器超时的回调函数,类似中断服务函数*/mytimer.data = 12; /*传给定时器服务函数的参数*/ (3) 开启定时器。
init_timer(&mytimer); /*初始化定时器*/add_timer(&mytimer); /*启动定时器*/ 完整示例代码:
#include #include #include static struct timer_list timer;static void timer_function(unsigned long data){ printk(data=%ld\n,data); mod_timer(&timer,msecs_to_jiffies(3000)+jiffies);}static int __init tiny4412_linux_timer_init(void){ timer.expires=hz*3+jiffies; /*单位是节拍*/ timer.function=timer_function; timer.data=666; /*1. 初始化定时器*/ init_timer(&timer); /*2. 添加定时器到内核*/ add_timer(&timer); printk(驱动测试: 驱动安装成功\n); return 0;}static void __exit tiny4412_linux_timer_cleanup(void){ /*3. 删除定时器*/ del_timer_sync(&timer); printk(驱动测试: 驱动卸载成功\n);}module_init(tiny4412_linux_timer_init); /*驱动入口--安装驱动的时候执行*/module_exit(tiny4412_linux_timer_cleanup); /*驱动出口--卸载驱动的时候执行*/module_license(gpl); /*设置模块的许可证--gpl*/ 4. 内核提供的延时函数linux 内核中提供了进行纳秒、微秒和毫秒延迟。void ndelay(unsigned long nsecs) ;void udelay(unsigned long usecs) ;void mdelay(unsigned long msecs) ;上述延迟的实现原理本质上是忙等待,根据 cpu 频率进行一定次数的循环。在内核中,最好不要直接使用mdelay()函数, 这将无谓地耗费cpu资源。void msleep(unsigned int millisecs) ;unsigned long msleep_interruptible(unsigned int millisecs) ;void ssleep(unsigned int seconds) ;上述函数将使得调用它的进程睡眠参数指定的时间, msleep()、 ssleep()不能被打断,而 msleep_interruptible()则可以被打断。 5. 精度较高的时间获取方式 高精度定时器通常用ktime作为计时单位。
获取内核高精度时间单位: ktime_t ktime_get(void)
下面是一些时间辅助函数用于计算和转换:
ktime_t ktime_set(const long secs, const unsigned long nsecs); ktime_t ktime_sub(const ktime_t lhs, const ktime_t rhs); ktime_t ktime_add(const ktime_t add1, const ktime_t add2); ktime_t ktime_add_ns(const ktime_t kt, u64 nsec); ktime_t ktime_sub_ns(const ktime_t kt, u64 nsec); ktime_t timespec_to_ktime(const struct timespec ts); ktime_t timeval_to_ktime(const struct timeval tv); struct timespec ktime_to_timespec(const ktime_t kt); //转换的时间通过timespec结构体保存struct timeval ktime_to_timeval(const ktime_t kt); //转换的时间通过timeval结构体保存s64 ktime_to_ns(const ktime_t kt); //转换为ns单位int ktime_equal(const ktime_t cmp1, const ktime_t cmp2); s64 ktime_to_us(const ktime_t kt); //转换为us单位s64 ktime_to_ms(const ktime_t kt); //转换为ms单位ktime_t ns_to_ktime(u64 ns); 示例: 计算经过的一段时间
static int hello_init(void){ ktime_t my_time,my_time2; unsigned int i,j; unsigned int time_cnt=0; my_time=ktime_get(); //获取当前时间 i=ktime_to_us(my_time); //转us udelay(600); //延时一段时间 my_time2=ktime_get(); //获取当前时间 j=ktime_to_us(my_time2); //转us printk(time_cnt=%ld\n,j-i); //得出之间差值,正确值为: 600 return 0;}
多晶硅“双反”有利于产业健康发展
大众途观L怎么样?德系血统SUV,外观大气,乘坐空间大,底盘扎实,汉兰达都害怕它!售价仅22万
人工智能走进现实世界的切入点是
又一激光雷达公司破产,中科大校友创办,估值曾达20亿美元!
微软雇摇滚歌星为Windows Vista录音
Linux驱动开发-内核定时器
波峰焊机日常都要做哪些开启及注意事项
两天封停企业25家!深圳宝龙展开安全生产大搜查
!销售IFR2023B射频信号发生器IFR2023B 小兵/
天马自主研发柔性TFT光学指纹传感器,薄至低于0.1mm
DPU国产生态版图又双叒扩大了!
基于互联网的云安全服务需满足一定的基础条件
表面终端金刚石场效应晶体管的研究
先码再看,史上最全大屏电视选购指南,全是干货!家!
DRAM现货价格呈现出上涨趋势 预计DRAM合约价有望在2020年逐步回升
iPhone8什么时候上市:苹果给iPhone8配上史上最强处理器,只为实现这个功能!
灵动微MM32F0133C6P的规格配置是怎样的
简单实现完全集成的4线RTD温度测量系统,适用于高精度测量应用
雷军透露红米Redmi新机部分细节 保留3.5mm耳机孔而且是现货发售
可配置FFT IP核的实现及基础教程