时钟驱动程序clock_task(11098行)首先调用init_clock来把可编程时钟的频率初始化为60HZ。当收到其他消息时,它把pending_ticks加到realtime上,然后把pend_ticks复位。这个操作可能会和时钟中断发生冲突,因此调用了lock和unlock两个函数来避免冲突(11115行至11118行)。时钟中断处理程序除主循环以外的其他部分:接收一条消息,调用一个完成所需要工作的函数,然后回送一个应答消息。
do_clocktick(11140行)只有在中断处理程序确定有一些重要的工作需要做时才调用它。首先检查是否有信号或监视定时器事件到。如果其中之一发生,则检查在进程表中所有的闹钟项。在一遍对进程表的扫描中可能会发现多个闹钟时间到,也有可能接收下一个闹钟的进程已经终止。当发现一个进程其闹钟时刻比当前时间要小,但不为零时,则检查和该进程相对应的在数组watch_dog中的项。如果在watch_dog数组中存放一个有效地址,11161行的测试将返回TRUE,并在11163行间接调用对应的函数。如果发现为空指针(NULL pointer),其测试结果将为FALSE,并调用cause_sig来发送一个SIGALRM信号。当需要同步闹钟时,也使用监视器项,在这种情况下,存放的地址为cause_alarm而不是属于特定任务的监视器函数的地址。
cause_sig的工作是发一条消息到存储管理器。这里需要检查是否存储管理器正在等待该消息。如果确实如此,它发一条消息通知闹钟时间已到。如果存储管理器忙,那么在第一次时将做一个标记来通知它。
当循环检查进程表中每个进程的p_alarming值时,更新next_alarm。在启动循环以前,它被设置为一个很大的数(11151行),然后对于每一个在发送了闹钟或信号后其闹钟值为非零的进程,将其闹钟值和next_alarm进行比较,后者被设置为较小的值(11171行和11172行)。
处理完闹钟以后,do_clockticks继续执行,检查是否到了调度另一进程的时刻。执行时间片的值保存在变量shed_clocktick中,一般在每次时钟中断处理程序中将其减1。然而,在那些do_clocktick被激活的嘀哒中,中断处理程序并不减小它的值,而是让do_clocktick自己执行这项工作并测试结果是否为零(11178行)。当调度新进程时并不对sched_ticks复位,而是每次SCHED_RATE嘀哒后对其复位。11179行中的比较是用来保证当进程在被剥夺CPU以前确实至少运行了一个完整的调度滴答。
下一个过程do_getuptime(11189行)只有一行,它把realtime的当前值存入返回消息的正确域中,任何进程都可以通过这种方式取得已经过的时间。对于访问时间的任务,消息传递的代价是很大的,因此提供了一个相关的函数get_uptime(11200行)。因为不是通过发送给时钟任务的消息激活其运行,所以它必须把耽误的时钟滴答加到当前的realtime上。在这里,lock和unlock也是必须使用的,用来防止当访问pend_ticks时发生时钟中断。
为了取得当前的时间,do_get_time(1121行)根据realtime和boottime计算当前时间。do_set_time (11270行)则与do_get_time相反,它根据给定的当前时间和从启动开始计数的滴答数为boot_time计算一个新值。
do_setalarm(11242行)和do_setsyn_alarm(11269行)非常相似。二者都从消息中提取参数,一个参数说明要向其发送信号的进程,另一个参数说明等待消息的时间。do_setalarm也需要提取一个调用函数的参数。如果目标进程是用户进程而不是任务,调用函数可以取空指针。这两个函数也计算闹钟需等待的时间并把计算结果设置在返回的消息中。然后二者都调用common_setalarm完成相应的操作。如果是do_setsyn_alarm,传给common_setalarm的函数参数将永远是cause_alarm。
common_setalarm(11291行)完成上面所讨论的两个函数所启动的工作,然后把闹钟时间存放在进程表中,把指向监视定时器过程的指针存在watch_dog数组中(也可能是指向cause_alarm的指针或是空指针),然后它象do_clocktick一样扫描整个进程表寻找下一个闹钟。
cause_alarm(11318行)很简单,它把syn_table数组中对应于同步闹钟目标的项设置为TRUE。如果同步闹钟任务没有处于活跃状态,就发送一条消息唤醒它。
此外,还有一些函数用来实现同步闹钟,时间中断处理等。
--
原文链接: http://202.107.127.126/ncourse/OS/chapter3/section8/3.8.4.htm
|