Linux设备驱动-时间编程

内核通过定时器中断来跟踪时间的流动。定时器中断由系统定时硬件以规律地间隔产生;这个间隔在启动时由内核根据 HZ 值来编程,HZ 是一个体系依赖的值,在中定义或者它所包含的一个子平台文件中。

1.jiffies计数器

jiffies是用于存储从系统启动以来的时钟嘀哒的数目(中断次数)。 这个计数器是一个 64-位 变量( 即便在 32-位的体系上)并且称为 jiffies_64。但是, 驱动编写者正常地存取 jiffies 变量,一个 unsigned long, 或者和jiffies_64是同一个或者它的低有效位。使用 jiffies 常常是首选,因为它更快,并且再所有的体系上存取 64-位的jiffies_64值不是原子的。如下为jiffies读取的例子:

#include <linux/jiffies.h>
unsigned long j, stamp_1, stamp_half, stamp_n;

j = jiffies; /* read the current value */
stamp_1 = j + HZ; /* 1 second in the future */
stamp_half = j + HZ/2; /* half a second */
stamp_n = j + n * HZ / 1000; /* n milliseconds */

这个代码对于jiffies回绕是有问题的,虽然在32-位平台上当HZ是1000时, 计数器只是每 50 天回绕一次,但这样操作还是存在不安全的情况,故系统提供如下宏:

#include <linux/jiffies.h>
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);

注意上述宏返回的是jiffies的差值,单位需要再做转换的:

int diff = time_after(a, b);
int msec = diff * 1000 / HZ;

在需要和用户空间程序交换时间时通常不会送jiffies给用户空间。和用户空间交互时间通常用结构体struct timeval(旧版本)和struct timespec,kernel提供如下函数:

#include <linux/time.h> 
unsigned long timespec_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_64值不是原子的,如果直接读取jiffies_64可能读取到错误的值,kernel提供了函数来解决这个问题:

#include <linux/jiffies.h> 
u64 get_jiffies_64(void);

2.延时

2.1 忙等待

如果你想延时执行多个时钟嘀哒, 允许在值中某些疏忽, 最容易的( 尽管不推荐 ) 的实现是一个监视 jiffy 计数器的循环。如下code是延时1ms:

j1 = jiffies + 1 * HZ / 1000; /* 1 milliseconds */
while (time_before(jiffies, j1))
    cpu_relax();

需要注意的是这种忙等待严重地降低了系统性能。如果你不配置你的内核为抢占操作, 这个循环在延时期间完全锁住了处理器。调度器永远不会抢占一个在内核中运行的进程,并且计算机看起来完全死掉直到时间 j1 到时。所以这种忙等待延时是不推荐使用的。应该在等待的时候让出CPU,如下:

j1 = jiffies + 1 * HZ / 1000; /* 1 milliseconds */
while (time_before(jiffies, j1))
	schedule();
2.2 超时

如果你的驱动使用一个等待队列来等待某些其他事件,但是你也想确保它在一个确定时间段内运行,可以使用 wait_event_timeout 或者 wait_event_interruptible_timeout:

#include <linux/wait.h>
long wait_event_timeout(wait_queue_head_t q, condition, long timeout);
long wait_event_interruptible_timeout(wait_queue_head_t q, condition, long timeout);

这些函数在给定队列上睡眠,它们在超时(以 jiffies 表示)到后返回。注意超时值表示要等待的 jiffies 数,不是一个绝对时间值。如果超时到,这些函数返回 0; 如果这个进程被其他事件唤醒,它返回以jiffies表示的剩余超时值。

2.3 短延时

内核函数 ndelay,udelay,以及mdelay对于短延时好用, 分别延后执行指定的纳秒数,微秒数或者毫秒数:

#include <linux/delay.h>
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);

需要注意的是这3个函数使用一个软件循环基于在启动时计算的处理器速来实现延时的。即 这3 个延时函数是忙等待,其他任务在时间流失时不能运行。
内核中常用的毫秒(和更长)延时而不用涉及到忙等待是如下3个函数:

#include <linux/delay.h>
void msleep(unsigned int millisecs);
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds)

前 2 个函数使调用进程进入睡眠给定的毫秒数. 一个对 msleep 的调用是不可中断的; 你能确保进程睡眠至少给定的毫秒数. 如果你的驱动位于一个等待队列并且你想唤醒来打断睡眠, 使用 msleep_interruptible。从 msleep_interruptible的返回值正常地是0;如果, 但是,这个进程被提早唤醒,返回值是在初始请求睡眠周期中剩余的毫秒数。 对 ssleep 的调用使进程进入一个不可中断的睡眠给定的秒数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值