曲径通幽论坛

标题: 时间处理 [打印本页]

作者: beyes    时间: 2009-9-28 00:19
标题: 时间处理
概述
创建设备驱动程序的过程中会涉及到多种时间相关内容。内核中为了处理这些时间相关内容,把全局变量 jiffies_64 ( 2.6 内核 )作为时间的基准值 ( 2.4 内核中用 jiffies )。

设备驱动程序利用该基准值测定时间的流逝,并根据该时间控制硬件。为了进行时间的相关处理,内核提供了 Hz 、USER_Hz 等常数,以及处理年,月,日,时,分,秒的 do_gettimeofday() 、 do_settimeofday() 和 mktime() 等函数。

在控制硬件的过程中,偶尔会出现必须的延迟 (硬件的特性及时序上的要求) 。例如,为了访问一些芯片或设备的内部寄存器,需要较短的延迟时间,而同步数据输入输出时需要较长的时间。

通常,延迟较短时间时,会使用以 1ms 为单位的 mdelay() 函数或这是以 1us 为单位的 udelay() 函数等; 而对于延迟相对较长的时间,则会用 jiffies_64 ( 2.4 内核用 jiffies ) 全局变量。
作者: beyes    时间: 2009-9-28 01:58
标题: 定时器中断 (timer interrupt)
操作系统的核心功能就是调度 ( scheduling ),为了实现调度,需要定时器中断,Linux 内核在初期就设置了称为 Hz 的常数,该常数设置了发生定时器中断的周期,内核利用该值,确定各个进程的运行时间 ( 进程分配到系统资源的时间 ),并进行调度。

为了处理相关的时间操作,设备驱动程序中使用了下列全局变量和函数:
下图是 2.6 内核定时器中断更新 jiffies_64 值的过程:


下面根据内核版本求出 0.3s 后的时间方法和使用 USER_Hz 值的方法:
#define DIFF_TIME ( 3*Hz/10 )
u32 aftertime;
aftertime = jiffies + DIFF_TIME;
这里 DIFF_TIME 的值为 30, 因为 2.4 内核是每 10ms 定时器中断一次,故秒数为 30 x 10 = 300ms 。

#define DIFF_TIME (30)
u32  aftertime;
aftertime = jiffies + DIFF_TIME;

#define DIFF_TIME (30)
u64 aftertime;
aftertime = get_jiffies_64() + DIFF_TIME * (Hz / USER_Hz);

#define DIFF_TIME (3*Hz/10)
u64  aftertime;
aftertime = get_jiffies_64() + DIFF_TIME;

作者: beyes    时间: 2009-9-28 13:29
标题: 处理较短的延迟时间
创建设备驱动程序的过程中会遇到需要处理短时间延迟的情况,这里的短时间指的是小于 1ms (0.001s) 的时间,或者是小于 1us (0.000001s) 的时间,此类短时间延迟多用于同步 (synchronization) 硬件,此时使用下列函数:

上面 3 个函数属于宏函数,需要包含头文件 <linux/delay.h> 。使用这些函数时,注意不能延迟过长的时间,这些函数在运行时,系统会终止,CPU 会闲置,调用该函数的例程如果出现形无限循环或者多次重复调用该函数,可能会造成系统中止的现象。

如果延迟时间超过 5ms ,就不能使用 mdelay() 函数。

如果延迟时间超过 1000us ,不能使用 udelay() 函数。

ndelay() 是 2.6 内核支持的函数。该函数在 1GHz 以上的快速系统中才有意义。在低于 GHz 的系统中使用该函数时,ndelay() 函数的作用和 udelay() 相同。
作者: beyes    时间: 2009-9-28 14:00
标题: 处理较长的延迟时间
设备驱动程序中,要尽量避免使用超过 1ms 的延迟时间。如果不得不使用长时间延迟,可以使用下列方法。对于控制硬件的设备驱动程序而言,该方法并不值得推荐,要尽可能避免。比如 300ms 的延迟方法:

1、对于 2.4 内核
#define DELAY_TIME_MSEC   (3*Hz/10)

unsigned long endtime = jiffies + DELAY_TIME_MSEC;

while (jiffies < endtimeschedule();

在时间 endtime 还未到达时,调用 schedule() 函数启动其他进程。这里需要注意,这个延迟可能并不是精确的时间延迟,往往可能会超时,所以这样的方式不能用在有精确时间延迟的终端处理程序上。

2、对于 2.6 内核
2.6 内核属于抢占型内核,这一点与 2.4 内核不同,可以使用下列结构:
#define DELAY_TIME_MSEC   (3*Hz/10)

u64 endtime = get_jiffies_64() + DELAY_TIME_MSEC;

while (jiffies < endtime); 
如果在 2.4 内核中使用该方法,会产生类似系统停止运行的效果,但 2.6 内核为抢占式内核,在循环语句的运行中调度被转换,所以没有必要调用 schedule() 函数。
作者: beyes    时间: 2009-9-28 16:51
标题: 设置系统时间
通常,设备驱动程序不需要设置年,月,日,时,分,秒形态的系统时间,但是也不能排除有这种可能性,系统还是为此提供了几个函数。下面是与系统时间相关的函数和结构体:

do_gettimeofday() ,  do_settimeofday() 函数与应用程序中使用的 gettimeofday() 函数或 settimeofday() 函数具有相同的功能。上述函数没有使用常用的时间格式,即其单位不是年,月,日,时,分,秒等,而是把当前时间换算为秒。

mktime() 是辅助函数,即输入年,月,日,时,分,秒后换算为秒。(详见:http://www.groad.net/bbs/read.php?tid-1224.html)

在 2.4 内核中,do_gettimeofday() 和 do_settimeofday() 函数的变量类型均使用 struct timeval 结构体;
在 2.6 内核中,do_gettimeofday() 函数的变量类型为 struct timeval ,而 do_settimeofday() 函数的变量类型为 struct timespec 。

系统时间的设定
设备驱动程序中设定系统时间时,系统整体的时间会改变,因此要特别注意。应用程序正在延迟时间,或者正在进行时间换算是,如果系统时间发生变化,就会处理错误信息。比如,在 2.6 内核中设置系统时间 2009年9月28日17 时 40 分 20 秒的方法。
timeval.tv_usec = 0;
timeval.tv_sec = (unsigned long) mktime (2009, 9, 28, 17, 40, 20);
do_settimeofday (&timeval);





欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) Powered by Discuz! X3.2