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结构,有着接近微妙的分辨率。
//kernel/time/timekeeping.c
473 /**
474 * do_gettimeofday - Returns the time of day in a timeval
475 * @tv: pointer to the timeval to be set
476 *
477 * NOTE: Users should be converted to using getnstimeofday()
478 */
479 void do_gettimeofday(struct timeval *tv)
驱动程序为了让硬件有足够的时间完成一些任务,常常需要将特定的代码延后一段时间来执行,根据延时的长短,内核开发中使用长延时和短延时两个概念。长延时的定义为:延时时间>多个jiffies,实现长延时可以用查询jiffies的方法:
time_before(jiffies, new_jiffies);
time_after(new_jiffiesmjiffies);
**短延时的定义为:延迟事件接近或短于一个jiffy,实现短延时可以调用
udelay();
mdelay();
这两个函数都是忙等待函数,大量消耗CPU时间,前者使用软件循环来延迟指定数目的微妙数,后者使用前者的嵌套来实现毫秒级的延时。
定时器
驱动可以注册一个内核定时器,来指定一个函数在未来某个时间来执行。定时器从注册到内核开始计时,达到指定的时间后会执行注册的函数。即超时值是一个jiffies值,当jiffies值大于timer->expires时,timer->function就会被执行。API如下
//定一个定时器
struct timer_list my_timer;
//初始化定时器
void init_timer(struct timer_list *timer);
mytimer.function = my_function;
mytimer.expires = jiffies +HZ;
//增加定时器
void add_timer(struct timer_list *timer);
//删除定时器
int del_tiemr(struct timer_list *timer);
实例
static struct timer_list tm;
struct timeval oldtv;
void callback(unsigned long arg)
{
struct timeval tv;
char *strp = (char*)arg;
do_gettimeofday(&tv);
printk("%s: %ld, %ld\n", __func__,
tv.tv_sec - oldtv.tv_sec,
tv.tv_usec- oldtv.tv_usec);
oldtv = tv;
tm.expires = jiffies+1*HZ;
add_timer(&tm);
}
static int __init demo_init(void)
{
init_timer(&tm);
do_gettimeofday(&oldtv);
tm.function= callback;
tm.data = (unsigned long)"hello world";
tm.expires = jiffies+1*HZ;
add_timer(&tm);
return 0;
}
延迟工作
除了使用内核定时器完成定时延迟工作,Linux内核还提供了一套封装好的"快捷方式"-delayed_work,和内核定时器类似,其本质也是利用工作队列和定时器实现,
//include/linux/workqueue.h
100 struct work_struct {
101 atomic_long_t data;
102 struct list_head entry;
103 work_func_t func;
104 #ifdef CONFIG_LOCKDEP
105 struct lockdep_map lockdep_map;
106 #endif
107 };
113 struct delayed_work {
114 struct work_struct work;
115 struct timer_list timer;
116
117 /* target workqueue and CPU ->timer uses to queue ->work */
118 struct workqueue_struct *wq;
119 int cpu;
120 };
struct work_struct
--103-->需要延迟执行的函数, typedef void (*work_func_t)(struct work_struct *work);
至此,我们可以使用一个delayed_work对象以及相应的调度API实现对指定任务的延时执行
//注册一个延迟执行
591 static inline bool schedule_delayed_work(struct delayed_work *dwork,unsigned long delay)
//注销一个延迟执行
2975 bool cancel_delayed_work(struct delayed_work *dwork)
和内核定时器一样,延迟执行只会在超时的时候执行一次,如果要实现循环延迟,只需要在注册的函数中再次注册一个延迟执行函数。
schedule_delayed_work(&work,msecs_to_jiffies(poll_interval));
Linux驱动技术(七) _内核定时器与延迟工作的更多相关文章
- Linux驱动技术(六) _内核中断
在硬件上,中断源可以通过中断控制器向CPU提交中断,进而引发中断处理程序的执行,不过这种硬件中断体系每一种CPU都不一样,而Linux作为操作系统,需要同时支持这些中断体系,如此一来,Linux中就提 ...
- Linux驱动技术(一) _内存申请
先上基础,下图是Linux的内存映射模型,其中体现了Linux内存映射的几个特点: 每一个进程都有自己的进程空间,进程空间的0-3G是用户空间,3G-4G是内核空间 每个进程的用户空间不在同一个物理内 ...
- Linux驱动技术(八) _并发控制技术
为了实现对临界资源的有效管理,应用层的程序有原子变量,条件变量,信号量来控制并发,同样的问题也存在与驱动开发中,比如一个驱动同时被多个应用层程序调用,此时驱动中的全局变量会同时属于多个应用层进程的进程 ...
- Linux驱动技术(五) _设备阻塞/非阻塞读写
等待队列是内核中实现进程调度的一个十分重要的数据结构,其任务是维护一个链表,链表中每一个节点都是一个PCB(进程控制块),内核会将PCB挂在等待队列中的所有进程都调度为睡眠状态,直到某个唤醒的条件发生 ...
- Linux驱动技术(二) _访问I/O内存
ARM是对内存空间和IO空间统一编址的,所以,通过读写SFR来控制硬件也就变成了通过读写相应的SFR地址来控制硬件.这部分地址也被称为I/O内存.x86中对I/O地址和内存地址是分开编址的,这样的IO ...
- Linux驱动技术(五) _设备阻塞/非阻塞读写【转】
转自:http://www.cnblogs.com/xiaojiang1025/p/6377925.html 等待队列是内核中实现进程调度的一个十分重要的数据结构,其任务是维护一个链表,链表中每一个节 ...
- Linux驱动技术(四) _异步通知技术
异步通知的全称是"信号驱动的异步IO",通过"信号"的方式,放期望获取的资源可用时,驱动会主动通知指定的应用程序,和应用层的"信号"相对应, ...
- Linux驱动技术(四) _异步通知技术【转】
转自:https://www.cnblogs.com/xiaojiang1025/p/6376561.html 异步通知的全称是"信号驱动的异步IO",通过"信号&quo ...
- linux 驱动学习笔记01--Linux 内核的编译
由于用的学习材料是<linux设备驱动开发详解(第二版)>,所以linux驱动学习笔记大部分文字描述来自于这本书,学习笔记系列用于自己学习理解的一种查阅和复习方式. #make confi ...
随机推荐
- LWIP_STM32_ENC28J60_NETCONN_UDP(3)
前面移植了lwip之后只是简单地做了一个dhcp的程序,但是实际工作中经常要用来通讯,那今天就来讲一讲怎么用lwip来进行UDP通讯 要使用数据通信首先第一步得知道lwip是怎么样保存数据的,在使用n ...
- ZOJ 3933 Team Formation
费用流裸题......比赛的时候少写了一句话....导致增加了很多无用的边一直在TLE #include<cstdio> #include<cstring> #include& ...
- MapReduce 表连接
题目描述: 根据给定的关系 child parent Tom Lucy Tom Jack Jone Lucy Jone Jack Lucy Mary Lucy Ben Jack Alice Jack ...
- 《算法导论》插入排序----InsertSort
算法导论,插入排序 public class InsertSort { public static double [] sort(double [] num) { for(int i =1; i< ...
- 在ubuntu上安装k-vim
在ubuntu 上安装k-vim 早就想好好改造一下自己使用的vim了!可惜各种配置都十分复杂,特别是涉及到C语言的语义补全,YouCompleteMe,总是出各种安装问题.今天有人推荐我使用k-vi ...
- sqlserver 设置外键
CREATE TABLE Orders ( O_Id int NOT NULL, OrderNo int NOT NULL, Id_P int, PRIMARY KEY (O_Id), FOREIGN ...
- UVa 573 - The Snail
题目大意:有一只蜗牛位于深一个深度为h米的井底,它白天向上爬u米,晚上向下滑d米,由于疲劳原因,蜗牛白天爬的高度会比上一天少f%(总是相对于第一天),如果白天爬的高度小于0,那么这天它就不再向上爬,问 ...
- javascript中的__proto__和prototype
一.2个参考网址: http://icekiller110.iteye.com/blog/1566768 http://www.cnblogs.com/snandy/archive/2012/09/0 ...
- Pearson相关系数
理解皮尔逊相关的两个角度 其一, 按照高中数学水平来理解, 皮尔逊相关(Pearson Correlation Coefficient)很简单, 可以看做将两组数据首先做Z分数处理之后, 然后两组数据 ...
- Storm学习笔记六
1 Storm的通信机制 说明:1.worker与worker之间跨进程通信: 2.worker内部中各个executor间的通信,Task对象--->输入队列queue--->执行--- ...