Linux驱动技术(七) _内核定时器与延迟工作

简介:

内核定时器

软件上的定时器最终要依靠硬件时钟来实现,简单的说,内核会在时钟中断发生后检测各个注册到内核的定时器是否到期,如果到期,就回调相应的注册函数,将其作为中断底半部来执行。实际上,时钟中断处理程序会触发TIMER_SOFTIRQ软中断,运行当前处理器上到期的所有定时器。

设备驱动程序如要获得时间信息以及需要定时服务,都可以使用内核定时器。

jiffies

要说内核定时器,首先就得说说内核中关于时间的一个重要的概念:jiffies变量,作为内核时钟的基础,jiffies每隔一个固定的时间就会增加1,称为增加一个节拍,这个固定间隔由定时器中断来实现,每秒中产生多少个定时器中断,由在<linux/param.h>中定义的HZ宏来确定,如此,可以通过jiffies获取一段时间,比如jiffies/HZ表示自系统启动的秒数。下两秒就是(jiffies/HZ+2),内核中用jiffies来计时,秒转换成的jiffies:seconds*HZ,所以以jiffiy为单位,以当前时刻为基准计时2秒:(jiffies/HZ+2)*HZ=jiffies+2*HZ如果要获取当前时间,可以使用do_gettimeofday(),该函数填充一个struct timeval结构,有着接近微妙的分辨率。

 
 
  1. //kernel/time/timekeeping.c 
  2. /** 
  3. * do_gettimeofday - Returns the time of day in a timeval 
  4. * @tv:         pointer to the timeval to be set 
  5. * NOTE: Users should be converted to using getnstimeofday() 
  6. */ 
  7. void do_gettimeofday(struct timeval *tv)  

驱动程序为了让硬件有足够的时间完成一些任务,常常需要将特定的代码延后一段时间来执行,根据延时的长短,内核开发中使用长延时和短延时两个概念。长延时的定义为:延时时间>多个jiffies,实现长延时可以用查询jiffies的方法:

 
 
  1. time_before(jiffies, new_jiffies); 
  2. time_after(new_jiffiesmjiffies); 

**短延时的定义为:延迟事件接近或短于一个jiffy,实现短延时可以调用

 
 
  1. udelay(); 
  2. mdelay(); 

这两个函数都是忙等待函数,大量消耗CPU时间,前者使用软件循环来延迟指定数目的微妙数,后者使用前者的嵌套来实现毫秒级的延时。

定时器

驱动可以注册一个内核定时器,来指定一个函数在未来某个时间来执行。定时器从注册到内核开始计时,达到指定的时间后会执行注册的函数。即超时值是一个jiffies值,当jiffies值大于timer->expires时,timer->function就会被执行。API如下

 
 
  1. //定一个定时器 
  2.  
  3. struct timer_list my_timer;//初始化定时器 
  4.  
  5. void init_timer(struct timer_list *timer); 
  6. mytimer.function = my_function; 
  7. mytimer.expires = jiffies +HZ;//增加定时器 
  8.  
  9. void add_timer(struct timer_list *timer);//删除定时器 
  10.  
  11. int del_tiemr(struct timer_list *timer);  

实例

 
 
  1. static struct timer_list tm; 
  2. struct timeval oldtv;void callback(unsigned long arg){ 
  3.     struct timeval tv; 
  4.     char *strp = (char*)arg; 
  5.     do_gettimeofday(&tv); 
  6.     printk("%s: %ld, %ld\n", __func__, 
  7.         tv.tv_sec - oldtv.tv_sec, 
  8.         tv.tv_usec- oldtv.tv_usec); 
  9.     oldtv = tv; 
  10.     tm.expires = jiffies+1*HZ; 
  11.     add_timer(&tm); 
  12. static int __init demo_init(void){ 
  13.     init_timer(&tm); 
  14.     do_gettimeofday(&oldtv); 
  15.     tm.function= callback; 
  16.     tm.data    = (unsigned long)"hello world"
  17.     tm.expires = jiffies+1*HZ; 
  18.     add_timer(&tm); 
  19.     return 0; 
  20.  

延迟工作

除了使用内核定时器完成定时延迟工作,Linux内核还提供了一套封装好的"快捷方式"-delayed_work,和内核定时器类似,其本质也是利用工作队列和定时器实现,

 
 
  1. //include/linux/workqueue.h 
  2.  struct work_struct {            
  3.          atomic_long_t data; 
  4.          struct list_head entry; 
  5.          work_func_t func; 
  6.  #ifdef CONFIG_LOCKDEP 
  7.          struct lockdep_map lockdep_map; 
  8.  #endif 
  9.  }; 
  10.  struct delayed_work {              114         struct work_struct work
  11.          struct timer_list timer; 
  12.  
  13.   /* target workqueue and CPU ->timer uses to queue ->work */ 
  14.          struct workqueue_struct *wq; 
  15.          int cpu; 
  16.  };  

--103-->需要延迟执行的函数, typedef void (work_func_t)(struct work_struct work);

至此,我们可以使用一个delayed_work对象以及相应的调度API实现对指定任务的延时执行

 
 
  1. //注册一个延迟执行 
  2.  
  3. 591 static inline bool schedule_delayed_work(struct delayed_work *dwork,unsigned long delay)//注销一个延迟执行 
  4. 2975 bool cancel_delayed_work(struct delayed_work *dwork)     

和内核定时器一样,延迟执行只会在超时的时候执行一次,如果要实现循环延迟,只需要在注册的函数中再次注册一个延迟执行函数。

 
 
  1. schedule_delayed_work(&work,msecs_to_jiffies(poll_interval)); 




本文作者:佚名
来源:51CTO
目录
相关文章
|
9天前
|
Linux C语言
Linux内核队列queue.h
Linux内核队列queue.h
|
27天前
|
Linux 数据安全/隐私保护 虚拟化
Linux技术基础(1)——操作系统的安装
本文是龙蜥操作系统(Anolis OS) 8.4 的安装指南,用户可以从[龙蜥社区下载页面](https://openanolis.cn/download)获取ISO镜像。安装方法包括物理机的光驱和USB闪存方式,以及虚拟机中的VMware Workstation Pro设置。安装过程涉及选择语言、配置安装目标、选择软件集合和内核,设置Root密码及创建新用户。安装完成后,可通过文本模式或图形化界面验证系统版本,如Anolis OS 8.4,标志着安装成功。
|
29天前
|
Linux API 调度
Linux系统驱动跟裸机驱动的区别
Linux系统驱动跟裸机驱动的区别
27 0
|
28天前
|
存储 Shell Linux
【Shell 命令集合 系统设置 】Linux 生成并更新内核模块的依赖 depmod命令 使用指南
【Shell 命令集合 系统设置 】Linux 生成并更新内核模块的依赖 depmod命令 使用指南
30 0
|
28天前
|
存储 缓存 Linux
【Shell 命令集合 磁盘维护 】Linux 设置和查看硬盘驱动器参数 hdparm命令使用教程
【Shell 命令集合 磁盘维护 】Linux 设置和查看硬盘驱动器参数 hdparm命令使用教程
35 0
|
28天前
|
Shell Linux C语言
【Shell 命令集合 系统设置 】⭐Linux 卸载已加载的内核模块rmmod命令 使用指南
【Shell 命令集合 系统设置 】⭐Linux 卸载已加载的内核模块rmmod命令 使用指南
29 1
|
4天前
|
Linux Go
Linux命令Top 100驱动人生! 面试必备
探索Linux命令不再迷茫!本文分10部分详解20个基础命令,带你由浅入深掌握文件、目录管理和文本处理。 [1]: <https://cloud.tencent.com/developer/article/2396114> [2]: <https://pan.quark.cn/s/865a0bbd5720> [3]: <https://yv4kfv1n3j.feishu.cn/docx/MRyxdaqz8ow5RjxyL1ucrvOYnnH>
48 0
|
7天前
|
算法 Linux 调度
深度解析:Linux内核的进程调度机制
【4月更文挑战第12天】 在多任务操作系统如Linux中,进程调度机制是系统的核心组成部分之一,它决定了处理器资源如何分配给多个竞争的进程。本文深入探讨了Linux内核中的进程调度策略和相关算法,包括其设计哲学、实现原理及对系统性能的影响。通过分析进程调度器的工作原理,我们能够理解操作系统如何平衡效率、公平性和响应性,进而优化系统表现和用户体验。
18 3
|
14天前
|
负载均衡 算法 Linux
深度解析:Linux内核调度器的演变与优化策略
【4月更文挑战第5天】 在本文中,我们将深入探讨Linux操作系统的核心组成部分——内核调度器。文章将首先回顾Linux内核调度器的发展历程,从早期的简单轮转调度(Round Robin)到现代的完全公平调度器(Completely Fair Scheduler, CFS)。接着,分析当前CFS面临的挑战以及社区提出的各种优化方案,最后提出未来可能的发展趋势和研究方向。通过本文,读者将对Linux调度器的原理、实现及其优化有一个全面的认识。
|
17天前
|
Linux 内存技术
Linux内核读取spi-nor flash sn
Linux内核读取spi-nor flash sn
13 1