驱动开发之始,(六)Linux内核时间,延迟操作

时钟中断由系统定时硬件周期性的间隔产生,这个间隔由内核根据HZ的值设定,HZ是一个与体系结构有关的常数。定义在中(每个人下载的内核版本不同可能位于不同的文件下)。每次当时钟中断发生时,内核内部计数器的值就增加一。这个计数器的值在系统引导时被初始化为0。因此,它的值就是自上次操作系统引导以来的时钟滴答数,成为“jiffies_64”。

1.使用jiffies计数器

该计数器和读取计数器的工具函数包含在中,通常只需包含后自动加入jiffies.h

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)时,最多涉及几十个毫秒,这种情况下,依赖时钟滴答显然不行。在中内核指定了几个完成短任务的函数,mdelay、udelay和ndelay分别指定毫秒、微妙和纳秒

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调用使进程进入不可中断的休眠,以秒计数。

你可能感兴趣的:(Linux驱动开发基础)