linux中的时间流
来源:互联网
作者:west263.com
时间:2008-04-16
西部数码-全国
虚拟主机10强!40余项虚拟主机管理功能,全国领先!双线多线
虚拟主机南北访问畅通无阻!免费赠送
企业邮局,.CN域名,
自助建站480元起,免费试用7天,满意再付款! P4
主机租用799元/月.月付免压金!

void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
该函数在绝大多数体系结构上是作为内联函数编译的。前者使用软件循环延迟指定数目的微秒数,后者使用 udelay 做循环,用于方便程式研发。udelay 函数里要用到 BogoMips 值:他的循环基于整数值 loops_per_second,这个值是在引导阶段计算 BogoMips 时得到的结果。
udelay函数只能用于获取较短的时间延迟,因为loops_per_second值的精度只有8位,所以,当计算更长的延迟时会积累出相当大的误差。尽管最大能允许的延迟将近1秒(因为更长的延迟就要溢出),推荐的 udelay 函数的参数的最大值是取1000微秒(1毫秒)。延迟大于 1 毫秒时能够使用函数 mdelay。
要特别注意的是 udelay 是个忙等待函数(所以 mdelay 也是),在延迟的时间段内无法运行其他的任务,因此要十分小心,尤其是 mdelay,除非别无他法,要尽量避免使用。
现在在支持大于几个微秒和小于1个时钟滴答的延迟时还是很低效的,但这通常不是个问题,因为延迟需要足够长,以便能够让人或硬件注意到。对人来说,百分之一秒的时间间隔是比较适合的精度,而 1 毫秒对硬件动作来说也足够长了。
mdelay 在 Linux 2.0 中并不存在,头文档 sysdep.h 弥补了这一缺陷。
任务队列
许多驱动程式需要将任务延迟到以后处理,但又不想借助中断。Linux 为此提供了三种方法:任务队列、tasklet(从内核 2.3.43 开始)和内核定时器。任务队列和 tasklet 的使用很灵活,能够或长或短地延迟任务到以后处理,在编写中断处理程式时很有用,我们还将在第9章“Tasklet和底半部处理”一节中继续讨论。内核定时器则用来调度任务在未来某个指定时间执行,将在本章的“内核定时器”一节中讨论。
使用任务队列或tasklet的一个典型情形是,硬件不产生中断,但仍希望提供阻塞型的读取。此时需要对设备进行轮询,同时要小心地不使 CPU 负担过多无谓的操作。将读进程以固定的时间间隔唤醒(例如,使用 current->timeout 变量)并不是个很好的方法,因为每次轮询需要两次上下文转换(一次是转换到读进程中运行轮询代码,另一次是返回执行实际工作的某个进程),而且通常来讲,恰当的轮询机制应该在进程上下文之外实现。
类似的情形更有象不时地给简单的硬件设备提供输入。例如,有一个直接连接到并口的步进马达,需要该马达能一步步地移动,但马达每次只能移动一步。在这种情况下,由控制进程通知设备驱动程式进行移动,但实际上,移动是在 write 返回后,才在周期性的时间间隔内一步一步进行的。
快速完成这类不定操作的恰当方法是注册任务在未来执行。内核提供了对“任务队列”的支持,任务能够累积,而在运行队列时被“消耗”。我们能够声明自己的任务队列,并且在任意时刻触发他,或也能够将任务注册到预定义的任务队列中去,由内核来运行(触发)他。
这一节将首先概述任务队列,然后介绍预定义的任务队列,这使读者能够开始一些有趣的测试(假如出错也可能挂起系统),最后介绍如何运行自己的任务队列。接着,我们来看看新的 tasklet 接口,在 2.4 内核中他在很多情况下取代了任务队列。
任务队列的本质
任务队列其实一个任务链表,每个任务用一个函数指针和一个参数表示。任务运行时,他接受一个void * 类型的参数,返回值类型为 void,而指针参数可用来将一个数据结构传入函数,或能够被忽略。队列本身是个结构(即任务)链表,并由声明和操纵他们的内核模块所拥有。模块要全权负责这些数据结构的分配和释放,为此一般使用静态的数据结构。
队列元素由下面这个结构来描述,这段代码是直接从头文档 拷贝下来的:
struct tq_struct {
struct tq_struct *next; /* linked list of active bh's */
int sync; /* must be initialized to zero */
void (*routine)(void *); /* function to call */
void *data; /* argument to function */
};
第一个注释中的 bh 指的是底半部(bottom-half)。底半部是“中断处理程式的一半部”,我们将在第9章的“tasklet和底半部”一节中介绍中断时周详讨论。现在,我们只要知道底半部是驱动程式实现的一种机制就能够了,他用于处理异步任务,这些任务通常比较大,不适于在处理硬件中断时完成。本章并不需要您理解底半部处理,但必要时也会偶尔提及。
上面的数据结构中最重要的成员是routine和data。为了将随后执行的任务排队,必须先配置好结构的这些成员,并把 next 和 sync 两个字段清零。结构中的 sync 标志位由内核使用,以避免同一任务被插入多次,因为这会破坏 next 指针。一旦任务被排队,该数据结构就被认为由内核“拥有”了,不能再被修改,直到任务开始运行。
和任务队列有关的其他数据结构更有 task_queue,现在他实现为指向 tq_struct 结构的指针,假如将来需要扩充task_queue,只要用typedef将该指针定义为其他符号就能够了。在使用之前,必须将 task_queue 指针初始化为 NULL。
下面汇总了任何能够在任务队列和 tq_struct 结构上执行的操作。
DECLARE_TASK_QUEUE(name);
这个宏用给定的名称 name 声明了一个任务队列,并把他初始化为空。
int queue_task(struct tq_struct *task, task_queue *list);
正如该函数的名字,他用于将任务排进队列中。假如队列中已有该任务,返回0,否则返回非0。
void run_task_queue(task_queue *list);
run_task_queue函数用于运行累积在队列上的任务。除非您要声明和维护自己的任务队列,否则不必调用本函数。
在讨论使用任务队列的细节之前,我们先看一下他们在内核中是怎样工作的。
任务队列的运行
如前所述,一个任务队列,实际上是个函数链表。当调用 run_task_queue 运行某个队列时,列表中的每一项都会被执行。在编写和任务队列有关的函数时,一定要记住,当内核调用 run_task_queue 时,实际的上下文将限制能够进行的操作。也不应对队列中任务的运行顺序做任何假定,他们每个都是单独完成自己的任务的。
那么任务队列在什么时候运行呢?假如使用的是下面一节介绍的预定义的任务队列,则答案是“在内核轮到他那里时”。不同的队列在不同的时间运行,只要内核没有其他更紧要的任务,他们总是会运行的。
更重要的是,当对任务进行排队的进程运行时,任务队列几乎肯定是不会运行的,相反,他们是异步执行的。到现在为止,示例驱动例程中任何的事情都是在这个执行系统调用的进程上下文中完成的。但当任务队列运行时,这个进程可能正在睡眠,或正在另一个处理器上运行,甚至可能已完全退出了。
这种异步执行类似于硬件中断发生时的情景(我们会在第9章周详讨论)。实际上,任务队列常常是作为“软件中断”的结果而运行的。在中断模式(或中断期间)下,代码的运行会受到许多限制。我们现在介绍这些限制,这些限制还会在本书后面多次出现。我们也会多次重复,中断模式下的这些规则必须遵守,否则系统会有大麻烦。
许多动作需要在进程上下文中才能执行。假如处于进程上下文之外(比如在中断模式下),则必须遵守如下规则:
不允许访问用户空间。因为没有进程上下文,无法将进程和用户空间关联起来。
current指针在中断模式下是无效的,不能使用。
不能执行睡眠或调度。中断模式代码不能够调用schedule或sleep_on;也不能调用任何可能引起睡眠的函数。例如,调用kmalloc(...,GFP_KERNEL)就不符合本规则。信号量也不能用,因为可能引起睡眠。
内核代码能够通过调用函数in_interrupt( ) 来判断自己是否正运行于中断模式,该函数无需参数,假如处理器在中断期间运行就返回非0值。
当前的任务队列实现更有一个特性,队列中的一个任务能够将自己重新插回到他原先所在的队列。举个例子,定时器队列中的任务能够在运行时将自己插回到定时器队列中去,从而在下一个定时器滴答又再次被运行。这是通过调用 queue_task 把自己放回队列来实现的。由于在处理任务队列之前,是先用NULL指针替换队列的头指针,因此才可能进行不断的重新调度。结果是,一旦旧的队列开始执行,就有一个新的队列被建立。
尽管一遍遍地重新调度同一个任务看起来似乎没什么意义,但有时这也有些用处。例如,步进马达每次移动一步直到目的地,他的驱动程式就能够通过让任务在定时器队列上不断地重新调度自己来实现。其他的例子更有 jiq 模块,该模块中的打印函数通过重新调度自己来产生输出――结果是利用定时器队列产生多次迭代。
预定义的任务队列
延迟任务执行的最简单方法是使用由内核维护的任务队列。这种队列有好几种,但驱动程式只能使用下面列出的其中三种。任务队列的定义在头文档
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!