时钟中断由系统定时硬件周期性的间隔产生,这个间隔由内核根据HZ的值设定,HZ是一个与体系结构有关的常数。定义在
1.使用jiffies计数器
该计数器和读取计数器的工具函数包含在
unsigned long volatile __jiffy_data jiffies
该变量被声明为volatile,这样可避免编译器对访问该变量的语句优化。在代码需要计算未来的时间戳时,必须读取当前的计数器。
有时需要将来自用户空间的时间表示方法(使用struct timeval和struct timespec)和内核表述方法进行转换。这两个结构体使用两个数来表示精确的时间
struct timespec {
__kernel_time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
struct timeval {
__kernel_time_t tv_sec; /* seconds */
__kernel_suseconds_t tv_usec; /* microseconds */
};
内核提供一下四个函数,完成jiffies值和这些结构间的转换
unsigned long timespec_to_jiffies(const struct timespec *value);
void jiffies_to_timespec(const unsigned long jiffies, struct timespec *value);
unsigned long timeval_to_jiffies(const struct timeval *value);
void jiffies_to_timeval(const unsigned long jiffies, struct timeval *value);
对于64位的jiffies_64的访问不像对jiffies那样直接。在64为计算机结构上,两个变量是同一个,但在32位处理器上就靠不住了,因为在读取高 32位和低32位时,可能会发生更新,从而获得错误的值。因此,读取jiffies_64时应该使用内核导出函数
u64 get_jiffies_64(void)
2.获取当前时间
内核一般通过jiffies值来获取当前时间。该数值表示的是自最近一次系统启动到当前的时间间隔,它和设备驱动程序无关,因为它的生命周期只限于系统的运行期间(uptime)。但驱动程序可以利用jiffies的当前值来计算不同事件间的时间间隔,例如输入设备驱动程序就用它来分辨鼠标的单双击。
内核提供了将墙钟时间转换为jiffies值的函数
unsigned long mktime(const unsigned int year, const unsigned int mon,
const unsigned int day, const unsigned int hour,
const unsigned int min, const unsigned int sec);
虽然内核空间不必处理人类表达的时间,但有时也需要处理绝对时间间隔。
3.延迟执行
设备驱动程序经常需要将某些特定代码延迟一段时间后执行——通常是为了让硬件能完成某些任务。
把涉及多个时钟滴答的延迟称为“长延迟”。重点讲一下超时
通过监视jiffies计数器实现的延迟循环可以工作,但不是非常理想。实现延迟的最好方法应该是让内核为我们完成相应的工作,存在两种构造基于jiffies超时的途径,使用哪个则依赖于驱动程序是否在等待其他事件。如果驱动程序使用等待队列来等待其他一些事件,而我们同时希望在特定的时间段中运行,则可以使用wait_event_timeout和wait_event_interruptible_timeout宏
#define wait_event_timeout(wq, condition, timeout)
#define wait_event_interruptible_timeout(wq, condition, timeout)
上述函数会在给定的等待队列上休眠,但是会在超时(用jiffies表示)到期时返回。
当设备驱动需要处理硬件的延迟(latency)时,最多涉及几十个毫秒,这种情况下,依赖时钟滴答显然不行。在
void mdelay(unsigned long msecc)
void udelay(unsigned long usecs);
void ndelay(unsigned long nsecs);
注意,上面这三个延迟函数均是忙等函数,因而在延迟过程中无法运行其他任何任务。
还有另外一种不涉及忙等待,实现延迟的方法
void msleep(unsigned int msecs);
unsigned long msleep_interruptible(unsigned int msecs);
以上两个调用进程休眠以给定的msecs,对msleep的调用是不可中断的;如果驱动程序正在某个等待队列上等待,而又希望有唤醒能够打断这个等待的话,可使用msleep_interruptible
void ssleep(unsigned int seconds);
ssleep调用使进程进入不可中断的休眠,以秒计数。