第七章:时间、延迟及延缓操作

原创 2012年03月30日 11:26:08

度量时间差

HZ:每秒钟发生的时钟中断数。

每次当时钟中断发生时,内核内部计数器的值加一。计数器是jiffies_64,无论在32位操作系统上还是在64位操作系统上,它都是一个64位的变量,并且是只读变量。

使用jiffies计数器

函数及变量声明在<linux/jiffies.h>中,但此文件包含于<linux/sched.h>中。

jiffies_64被声明为volatile型,避免编译器对访问该变量的语句进行优化。

比较时间先后的函数有:

int time_after(unsigned long a, unsigned long b);

int time_before(unsigned long a, unsigned long b);

int time_after_eq(unsigned long a, unsigned long b);

int time_before_eq(unsigned long a, unsigned long b);

时间差的计算:

diff = (long)t2 – (long)t1;

msec  =  diff * 1000 / HZ; //时间差转化为毫秒

jiffies与用户空间时间表达方法的转换:

unsigned long timspec_to_jiffies(struct timespec *value);

void jiffies_to_timespec(unsigned long jiffies, struct timespec *value);

unsigned long timeval_to_jiffies(struct timeval *value);

void jiffies_to_timeval(unsigned long jiffies, struct timeval *value);

考虑到系统的差异性,取jiffies值建议通过函数进行:

u64 get_jiffies_64(void);

处理器特定的寄存器

x86专用头文件:<asm/msr.h> (machine specific registers及其特有的寄存器);

读取时间戳计数器TSCTimestamp counter)的宏:

rdtsc(low32, high 32);

rdtscl(low32);

rdtscll(var64);

一个与平台无关的函数:

#include <linux/timex.h>

cycles_t get_cycles(void);

SMP系统中,时间戳计数器不会在多个处理器间保持同步。为了确保获得一致的值,我们需要为查询该计数器的代码禁止抢占。

获取当前时间

内核提供将墙钟时间转化为jiffies的函数:

#include <linux/time.h>

unsigned long mktime ( unsigned int year, unsigned int mon,

unsigned int day, unsigned int hour,

unsigned int min, unsigned int sec);

内核空间得到绝对时间戳的方法:

#include <linux/time.h>

void do_gettimeofday(struct timeval *tv);

另外一个精度差一点的方法:

#include <linux/time.h>

struct timespec current_kernel_time(void );

延迟--长延迟

延迟--段延迟

#include <linux/delay.h>

void ndelay(unsigned long nsecs);

void udelay(unsigned long usecs);

void mdelay(unsigned long msecs);

忙等待函数,执行期间无法运行其他任务。

#include <linux/delay.h>

void msleep(unsigned int millisecs);

unsigned long msleep_interruptible(unsigned int millisecs);

void ssleep(unsigned int seconds);

前两个函数将调用进程休眠以达到给定的时间,对interruptible版本的调用是不可中断的。

内核定时器

内核定时器可以用来在未来的某个特定的时间点调度执行某个函数。

<asm/hardirq.h>中声明两个函数:

in_interrupt();

判断进程是否正运行于中断上下文。若是,则返回值非零。

in_atomic();

判断进程是否正运行在原子模式下。若调度不被允许,即原子模式,返回值非零。

关于定时器,谨记:定时器是竞态的潜在来源,这是由于异步的特定导致的,任何通过定时器访问的数据结构应该针对并发的访问进行保护。

定时器API

#include <linux/timer.h>

struct timer_list

{

/*…*/

unsigned long expires;

void (*function)(unsigned long);

unsigned long data;

}

void init_timer(struct timer_list *timer);

struct timer_list TIMER_INITIALIZER(_function, _expires, _data);


void add_timer(struct timer_list *timer);

void del_timer(struct timer_list *timer);


 

expires字段表示期望定时器执行的jiffies值。到达jiffies值时,将调用function函数,并传递data作为参数。

add_timer可以修改三个公共字段。

del_tiemr:在定时器到期前,禁止一个已注册的定时器。

其他API还有:

int mod_timer(struct timer_list *timer, unsigned long expires);

更新某个定时器的到期时间,经常用于超时定时器。

int del_timer_sync(struct timer_list *timer);

del_timer的工作类似,但该函数可以确保在返回时没有任何CPU在运行定时器函数。此函数可以避免竞态,多数情况下建议使用该函数。但是该函数可能休眠,因此不能用于原子上下文中。

int timer_pending(const struct timer_list *timer);

通过读取timer_list结构的一个字段返回定时器是否正在被调用。

Tasklet(小任务)机制

tasklet和内核定时器相似:始终在中断期间运行,始终在调度他们的同一个CPU上运行,都接受一个unsigned long参数。不同点:我们不能要求tasklet在某个给定的时间点执行。

tasklet数据结构以及初始化:

#include <linux/interrupt.h>

struct tasklet_struct

{

/*...*/

void (*func)(unsigned long);

unsigned long data;

}

void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);

DECALRE_TASKLET(name, func, data);

DECLARE_TASKLET_DISABLED(name, func, data);

内核为每个CPU提供了一组ksoftirq内核线程,用于运行“软件中断”处理例程。

<kernel/softirq.h>中定义了tasklet相关的函数操作:

void tasklet_disable(struct tasklet_struct *t);

void tasklet_disable_nosync(struct tasklet_struct *t);

void tasklet_enable(struct tasklet_struct *t);

void tasklet_schedule(struct tasklet_struct *t);

void tasklet_hi_schedule(struct tasklet_struct *t);

void tasklet_kill(struct tasklet_struct *t);

工作队列

工作队列与tasklet的区别是:

² tasklet在软件中断上下文运行,所以代码必须是原子的,而工作队列可以休眠。

² tasklet始终运行在被初始提交的同一个CPU上,而这只是工作队列的默认工作方式。

² 内核代码可以请求工作队列函数的执行延迟给定的时间间隔。

工作队列结构及相关API定义在<linux/workqueue.h>中。

显式创建一个工作队列:

struct workqueue_struct *create_workqueue(const char *name);

struct workqueue_struct *create_singlethread_workqueue(const char *name);

create_workqueue会在每个CPU上为这个工作队列创建专用的线程,许多情况下,这样会对性能造成严重影响,因此,如果单个工作足够使用,则应该使用create_singlethread_workqueue函数。

要想工作队列提交一个任务,需要填充一个work_struct结构,可以通过下面宏编译完成:

DECLARE_WORK(name, void (*function)(void *), void *data);

如果运行时构造一个work_struct结构,可以通过下宏初始化:

INIT_WORK(struct work_struct *work, void (*fucntion)(void *), void *data);

PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data);

INIT_WORK完成彻底的初始化工作,在首次构造该结构时,应该使用这个宏。PREPARE_WORK初始化不会用来将work_struct结果链接到工作队列,若结构已经被提交到工作队列,只是修改该结构,则使用该宏,而不是INIT_WORK

将工作提交到工作队列使用下函数:

int queue_work(struct workqueue_struct *queue, struct work_struct *work);

int queue_delayed_work( struct workqueue_struct *queue, struct work_struct *work, unsigned long delay);

添加成功返回1,返回非零意味着工作已经存在于队列中。

取消某个挂起的工作对立入口项:

int cancel_delayed_work(struct work_struct *work);

若该工作尚未执行,则函数返回非零值,以后不会执行;若该函数调用时工作已经执行,则返回零值,此后应该调用下函数:

void flush_workqueue(struct workqueue_struct *queue);

之后,该调用之前提交的工作函数都不会再系统任何地方运行。

在结束对工作队列的使用后,可调用下面的函数释放相关资源:

void destroy_workqueue(struct workqueue_struct *queue);

共享队列

详见书

相关文章推荐

【Linux 驱动】第七章 时间 延迟及延缓操作 (例子详解一)

本文所涉及实验为博文http://blog.csdn.net/tianshuai11/article/details/7465587中示例,请先阅读上述博文,然后消化以下例子 一,模块方法  ...

【Linux 驱动】第七章 时间 延迟及延缓操作 (例子详解二)

本文所涉及实验为博文http://blog.csdn.net/tianshuai11/article/details/7465587中示例,请先阅读上述博文,然后消化以下例子 一,模块方法  ...

第七章--时间、延迟及延缓操作

本文作为第七章--时间、延迟及延缓操作,主要讲述: 1、HZ、jiffies。 2、短延迟。 3、定时器。...

linux设备驱动之时间,延迟及延缓操作(实践)

一 jit.函数源码 /* * jit.c -- the just-in-time module * * Copyright (C) 2001,2003 Alessandro Rubini a...

Linux驱动---时间、延迟及延缓操作(转)

度量时间差 时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据 HZ 值来设定,HZ 是一个体系依赖的值,在 中定义或该文件包含的某个子平台相关文件中。作为通用的规则,即便如果知道 HZ 的...
  • lgb571
  • lgb571
  • 2011年05月11日 20:20
  • 636

Linux驱动学习--时间、延迟及延缓操作(转载)

Tasklets另一个有关于定时的内核设施是 tasklet。它类似内核定时器:在中断时间运行且运行同一个 CPU 上, 并接收一个 unsigned long 参数。不同的是:无法要求在一个指定的时...

学习ldd3--计时、延迟操作(第七章)

作者:张伟AreS /******************************************************************************/ 学习此之前先需...

LDD3读书笔记(第8章 时间、延迟及延缓操作)

计时 #include HZ     HZ符号指出每秒钟产生的时钟滴答数。 #include volatile unsigned long jiffies u64 jiffies_64...

Linux Device Driver 3, Chp 7 时间、延迟及延缓操作 示例代码修改 -- for kernel 2.6.32

Chp 3 字符设备驱动程序 实验代码 1. 编译出现 “TASK_INTERRUPTIBLE”的问题就包含 2. current->uid不能直接引用,需要改成 current->cre...

Linux设备驱动程式学习(10)-时间、延迟及延缓操作

度量时间差 时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据 HZ 值来设定,HZ 是个体系依赖的值,在 中定义或该文档包含的某个子平台相关文档中。作为通用的规则,即便假如知道 ...
  • jeffade
  • jeffade
  • 2012年05月11日 14:52
  • 374
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:第七章:时间、延迟及延缓操作
举报原因:
原因补充:

(最多只允许输入30个字)