当前位置 主页 > 服务器问题 > Linux/apache问题 >
CLOCK_TICK_RATE是整个8253的输入脉冲,如图5.3中所示为1.193180MHz,是近似为1MHz的方波信号,8253内部的三个计数器都对这个时钟进行计数,进而产生不同的输出信号,用于不同的用途。
HZ表示计数器0的频率,也就是时钟中断或系统时钟的频率,在/include/asm/param.h中定义如下:
#define HZ 100
下面我们看时钟中断触发的服务程序,该程序代码比较复杂,分布在不同的源文件中,主要包括如下函数:
时钟中断程序:timer_interrupt( );
中断服务通用例程do_timer_interrupt();
时钟函数:do_timer( );
中断安装程序:setup_irq( );
中断返回函数:ret_from_intr( );
(1) timer_interrupt( )
这个函数大约每10ms被调用一次,实际上, timer_interrupt( )函数是一个封装例程,它真正做的事情并不多,但是,作为一个中断程序,它必须在关中断的情况下执行。如果只考虑单处理机的情况,该函数主要语句就是调用do_timer_interrupt()函数。
(2) do_timer_interrupt()
do_timer_interrupt()函数有两个主要任务,一个是调用do_timer( ),另一个是维持实时时钟(RTC,每隔一定时间段要回写),其实现代码在/arch/i386/kernel/time.c中, 为了突出主题,笔者对以下函数作了改写,以便于读者理解:
static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { do_timer(regs); /* 调用时钟函数,将时钟函数等同于时钟中断未尝不可*/ if(xtime.tv_sec > last_rtc_update + 660) update_RTC(); /*每隔11分钟就更新RTC中的时间信息,以使OS时钟和RTC时钟保持同步,11分钟即660秒,xtime.tv_sec的单位是秒,last_rtc_update记录的是上次RTC更新时的值 */ }
其中,xtime是前面所提到的timeval类型,这是一个全局变量。
(3) 时钟函数do_timer() (在/kernel/sched.c中)
void do_timer(struct pt_regs * regs) { (*(unsigned long *)&jiffies)++; /*更新系统时间,这种写法保证对jiffies 操作的原子性*/ update_process_times(); ++lost_ticks; if( ! user_mode ( regs ) ) ++lost_ticks_system; mark_bh(TIMER_BH); if (tq_timer) mark_bh(TQUEUE_BH); }
其中,update_process_times()函数与进程调度有关,从函数的名子可以看出,它处理的是与当前进程与时间有关的变量,例如,要更新当前进程的时间片计数器counter,如果counter<=0,则要调用调度程序,要处理进程的所有定时器:实时、虚拟、概况,另外还要做一些统计工作。
与时间有关的事情很多,不能全都让这个函数去完成,这是因为这个函数是在关中断的情况下执行,必须处理完最重要的时间信息后退出,以处理其他事情。那么,与时间相关的其他信息谁去处理,何时处理?这就是由第三章讨论的后半部分去去处理。 上面timer_interrupt()(包括它所调用的函数)所做的事情就是上半部分。
在该函数中还有两个变量lost_ticks和lost_ticks_system,这是用来记录timer_bh()执行前时钟中断发生的次数。因为时钟中断发生的频率很高(每10ms一次),所以在timer_bh()执行之前,可能已经有时钟中断发生了,而timer_bh()要提供定时、记费等重要操作,所以为了保证时间计量的准确性,使用了这两个变量。lost_ticks用来记录timer_bh()执行前时钟中断发生的次数,如果时钟中断发生时当前进程运行于内核态,则lost_ticks_system用来记录timer_bh()执行前在内核态发生时钟中断的次数,这样可以对当前进程精确记费。