精华内容
下载资源
问答
  • 时钟中断处理程序

    千次阅读 2008-12-23 11:33:00
    时钟中断处理程序可以划分为两个部分:体系结构相关部分和体系结构无关部分。 与体系结构相关的例程作为系统定时器的中断处理程序而注册到内核中,以便在产生时钟中断时,它都能够相应地运行。这部分处理程序主要...

       时钟中断处理程序可以划分为两个部分:体系结构相关部分和体系结构无关部分。

     

     与体系结构相关的例程作为系统定时器的中断处理程序而注册到内核中,以便在产生时钟中断时,它都能够相应地运行。这部分处理程序主要完成:

    获得xtime_lock锁,以便对访问jiffies_64墙上时间xtime进行保护

    需要时应答或重新设置系统时钟

    周期性地使用墙上时间更新实时时钟

    调用体系结构无关的时钟例程:do_timer()

    1. 在<Time.h(incluce/linux)>中
    2. extern struct timespec xtime;
    3. #ifndef _STRUCT_TIMESPEC
    4. #define _STRUCT_TIMESPEC
    5. struct timespec {
    6.     time_t  tv_sec;     /* seconds */
    7.     long    tv_nsec;    /* nanoseconds 十亿分之一秒*/
    8. };
    9. #endif

      中断服务程序主要通过调用与体系结构无关的例程do_timer()执行下面的工作:

    给jiffies_64变量增加1

    更新资源消耗的统计值,比如当前进程所消耗的系统时间和用户时间

    执行已经到期的动态定时器

    执行scheduler_tick()函数

    更新墙上时间,该时间存放在xtime变量中

    计算平均负载

     

    1. 在<Time.c(kernel)>中
    2. /*
    3.  * Called by the timer interrupt. xtime_lock must already be taken
    4.  * by the timer IRQ!
    5.  */
    6. static inline void update_times(unsigned long ticks)
    7. {
    8.     update_wall_time(); /* 更新墙上时间 */
    9.     calc_load(ticks); /* 计算平均负载值*/
    10. }
    11.   
    12. /*
    13.  * The 64-bit jiffies value is not atomic - you MUST NOT read it
    14.  * without sampling the sequence number in xtime_lock.
    15.  * jiffies is defined in the linker script...
    16.  */
    17. void do_timer(unsigned long ticks)
    18. {
    19.     jiffies_64 += ticks;/* 给jiffies_64变量加1: */
    20.     update_times(ticks);
    21. }
    22. /**
    23.  * update_wall_time - Uses the current clocksource to increment the wall time
    24.  *
    25.  * Called from the timer interrupt, must hold a write on xtime_lock.
    26.  */
    27. static void update_wall_time(void)
    28. {
    29.     cycle_t offset;
    30.     /* Make sure we're fully resumed: */
    31.     if (unlikely(timekeeping_suspended))
    32.         return;
    33. #ifdef CONFIG_GENERIC_TIME
    34.     offset = (clocksource_read(clock) - clock->cycle_last) & clock->mask;
    35. #else
    36.     offset = clock->cycle_interval;
    37. #endif
    38.     clock->xtime_nsec += (s64)xtime.tv_nsec << clock->shift;
    39.     /* normally this loop will run just once, however in the
    40.      * case of lost or late ticks, it will accumulate correctly.
    41.      */
    42.     while (offset >= clock->cycle_interval) {
    43.         /* accumulate one interval */
    44.         clock->xtime_nsec += clock->xtime_interval;
    45.         clock->cycle_last += clock->cycle_interval;
    46.         offset -= clock->cycle_interval;
    47.         if (clock->xtime_nsec >= (u64)NSEC_PER_SEC << clock->shift) {
    48.             clock->xtime_nsec -= (u64)NSEC_PER_SEC << clock->shift;
    49.             xtime.tv_sec++;
    50.             second_overflow();
    51.         }
    52.         /* interpolator bits */
    53.         time_interpolator_update(clock->xtime_interval
    54.                         >> clock->shift);
    55.         /* accumulate error between NTP and clock interval */
    56.         clock->error += current_tick_length();
    57.         clock->error -= clock->xtime_interval << (TICK_LENGTH_SHIFT - clock->shift);
    58.     }
    59.     /* correct the clock when NTP error is too big */
    60.     clocksource_adjust(clock, offset);
    61.     /* store full nanoseconds into xtime */
    62.     xtime.tv_nsec = (s64)clock->xtime_nsec >> clock->shift;
    63.     clock->xtime_nsec -= (s64)xtime.tv_nsec << clock->shift;
    64.     /* check to see if there is a new clocksource to use */
    65.     change_clocksource();
    66.     update_vsyscall(&xtime, clock);
    67. }
    68. /*
    69.  * calc_load - given tick count, update the avenrun load estimates.
    70.  * This is called while holding a write_lock on xtime_lock.
    71.  */
    72. static inline void calc_load(unsigned long ticks)
    73. {
    74.     unsigned long active_tasks; /* fixed-point */
    75.     static int count = LOAD_FREQ;
    76.     count -= ticks;
    77.     if (unlikely(count < 0)) {
    78.         active_tasks = count_active_tasks();
    79.         do {
    80.             CALC_LOAD(avenrun[0], EXP_1, active_tasks);
    81.             CALC_LOAD(avenrun[1], EXP_5, active_tasks);
    82.             CALC_LOAD(avenrun[2], EXP_15, active_tasks);
    83.             count += LOAD_FREQ;
    84.         } while (count < 0);
    85.     }
    86. }

      do_timer()执行完毕后返回与体系结构相关的中断处理程序,继续执行后面的工作,释放xtime_lock锁,然后退出。

      以上全部工作每1/Hz秒都要发生一次。

      相关数据类型如下:

     

    1. 在<Clocksource.h(include/linux)>中
    2. /* clocksource cycle base type */
    3. typedef u64 cycle_t;
    4. struct clocksource;
    5. /**
    6.  * struct clocksource - hardware abstraction for a free running counter
    7.  *  Provides mostly state-free accessors to the underlying hardware.
    8.  *
    9.  * @name:       ptr to clocksource name
    10.  * @list:       list head for registration
    11.  * @rating:     rating value for selection (higher is better)
    12.  *          To avoid rating inflation the following
    13.  *          list should give you a guide as to how
    14.  *          to assign your clocksource a rating
    15.  *          1-99: Unfit for real use
    16.  *              Only available for bootup and testing purposes.
    17.  *          100-199: Base level usability.
    18.  *              Functional for real use, but not desired.
    19.  *          200-299: Good.
    20.  *              A correct and usable clocksource.
    21.  *          300-399: Desired.
    22.  *              A reasonably fast and accurate clocksource.
    23.  *          400-499: Perfect
    24.  *              The ideal clocksource. A must-use where
    25.  *              available.
    26.  * @read:       returns a cycle value
    27.  * @mask:       bitmask for two's complement
    28.  *          subtraction of non 64 bit counters
    29.  * @mult:       cycle to nanosecond multiplier
    30.  * @shift:      cycle to nanosecond divisor (power of two)
    31.  * @flags:      flags describing special properties
    32.  * @vread:      vsyscall based read
    33.  * @cycle_interval: Used internally by timekeeping core, please ignore.
    34.  * @xtime_interval: Used internally by timekeeping core, please ignore.
    35.  */
    36. struct clocksource {
    37.     char *name;
    38.     struct list_head list;
    39.     int rating;
    40.     cycle_t (*read)(void);
    41.     cycle_t mask;
    42.     u32 mult;
    43.     u32 shift;
    44.     unsigned long flags;
    45.     cycle_t (*vread)(void);
    46.     /* timekeeping specific data, ignore */
    47.     cycle_t cycle_last, cycle_interval;
    48.     u64 xtime_nsec, xtime_interval;
    49.     s64 error;
    50. #ifdef CONFIG_CLOCKSOURCE_WATCHDOG
    51.     /* Watchdog related data, used by the framework */
    52.     struct list_head wd_list;
    53.     cycle_t wd_last;
    54. #endif

    呵呵

    展开全文
  • 操作系统-时钟中断-模拟时钟中断的产生及设计一个对时钟中断事件进行处理的模拟程序,含详细代码
  • 一、设计内容 ...通过实习了解中断及中断处理程序作用。本实习模拟“时钟中断事件”的处理,对其它中断事件的模拟处理,可根据各中断事件的性质确定处理原则,制定算法,然后依照本实习,自行设计。
  • 本文主要介绍在Linux下的时间实现以及系统如何进行时钟中断处理。 一. Linux的硬件时间 PC机中的时间有三种硬件时钟实现,这三种都是基于晶振产生的方波信号输入。这三种时钟为:(1)实时时钟RTC ( Real ...
    本文主要介绍在Linux下的时间实现以及系统如何进行时钟中断处理。
    一.       Linux的硬件时间
    PC机中的时间有三种硬件时钟实现,这三种都是基于晶振产生的方波信号输入。这三种时钟为:(1)实时时钟RTC ( Real Time Clock) (2)可编程间隔器PIT(Programmable Interval Timer )(3)时间戳计数器TSC(Time Stamp Clock)
    1.     实时时钟 RTC
      用于长时间存放系统时间的设备,即时关机后也可依靠主板CMOS电池继续保持系统的计时,原理图如下:

    Note: Linux与RTC的关系是,当Linux启动时从RTC读取时间和日期的基准值,然后在Kernel运行期间便抛开RTC,以软件的形式维护系统的时间日期,并在适当时机由Kernel将时间写回RTC Register.
    1.1 RTC Register
       (1). 时钟与日历Register
            共10个,地址:0x00-0x09,分别用于保存时间日历的具体信息,详情如下:

            00          Current Second for RTC
            01          Alarm Second
            02          Current Minute
            03          Alarm Minute
            04          Current Hour
            05          Alarm Hour
            06          Current Day of Week(1=Sunday)
            07          Current Date of Month
            08          Current Month
            09          Current Year
     (2).状态和控制Register
        共四个,地址:0x0a-0x0d,控制RTC芯片的工作方式,并表示当前状态。
    l         状态RegisterA , 0x0A 格式如下:
             bit[7]——UIP标志(Update in Progress),为1表示RTC正在更新日历寄存器组中的值,此时日历寄存器组是不可访问的(此时访问它们将得到一个无意义的渐变值)。
    bit6:4]——这三位是用来定义RTC的操作频率。各种可能的值如下: 

    DV2 DV1 DV0        
    0    0   0        4.194304 MHZ
    0    0   1        1.048576 MHZ
    0    1   0        32.769   KHZ
    1    1   0/1      
    任何 
    PC机通常设置成“010”。
    bit3:0]——速率选择位(Rate Selection bits),用于周期性或方波信号输出。
    RS3 RS2 RS1 RS0  周期性中断   方波   周期性中断   方波
     
      0   0   0    None       None       None      None
    0   0   0   1   30.517μs  32.768 KHZ 3.90625ms 256 HZ
    0   0   1   0   61.035μs  16.384 KHZ
    0   0   1   1   122.070μs  8.192KHZ
    0   1   0   0    244.141μs 4.096KHZ
    0   1  0   1    488.281μs 2.048KHZ
    0   1   1   0    976.562μs 1.024KHZ
    0   1   1   1    1.953125ms    512HZ
    1   0   0   0    3.90625ms     256HZ
    1   0   0   1    7.8125ms      128HZ
    1   0   1   0    15.625ms      64HZ
    1   0  1   1    31.25ms       32HZ
    1   1   0   0    62.5ms        16HZ
    1   1   0   1    125ms          8HZ
    1  1   1   0    250ms          4HZ
    1   1   1   1    500ms          2HZ
    PC
    机BIOS对其默认的设置值是“0110”
    l         状态Register B , 0x0B 格式如下:
    bit[7]——SET标志。为1表示RTC的所有更新过程都将终止,用户程序随后马上对日历寄存器组中的值进行初始化设置。为0表示将允许更新过程继续。
    bit[6]——PIE标志,周期性中断enable标志。
    bit[5]——AIE标志,告警中断enable标志。
    bit[4]——UIE标志,更新结束中断enable标志。
    bit[3]——SQWE标志,方波信号enable标志。
    bit[2]——DM标志,用来控制日历寄存器组的数据模式,0=BCD,1=BINARY。BIOS总是将它设置为0。
    bit[1]——24/12标志,用来控制hour寄存器,0表示12小时制,1表示24小时制。PC机BIOS总是将它设置为1。
    bit[0]——DSE标志。BIOS总是将它设置为0。
    l         状态Register C,0x0C 格式如下: 
    bit[7]——IRQF标志,中断请求标志,当该位为1时,说明寄存器B中断请求 发生。

    bit[6]——PF标志,周期性中断标志,为1表示发生周期性中断请求。
     
    bit[5]——AF标志,告警中断标志,为1表示发生告警中断请求。
     
    bit[4]——UF标志,更新结束中断标志,为1表示发生更新结束中断请求。
    l         状态Register D,0x0D 格式如下: 
     bit[7]——VRT标志(Valid RAM and Time),为1表示OK,为0表示RTC 已经掉电。
     
     bit[6:0]——总是为0,未定义。
    2.可编程间隔定时器 PIT
            每个PC机中都有一个PIT,以通过IRQ0产生周期性的时钟中断信号,作为系统定时器 system timer。当前使用最普遍的是Intel 8254 PIT芯片,它的I/O端口地址是0x40~0x43。 
    Intel 8254 PIT有3个计时通道,每个通道都有其不同的用途:
     
    (1) 通道0用来负责更新系统时钟。每当一个时钟滴答过去时,它就会通过IRQ0向       系统 产生一次时钟中断。
     
    (2) 通道1通常用于控制DMAC对RAM的刷新。
     
    (3) 通道2被连接到PC机的扬声器,以产生方波信号。
     
        每 个通道都有一个向下减小的计数器,8254 PIT的输入时钟信号的频率是1.193181MHZ,也即一秒钟输入1193181个clock-cycle。每输入一个clock-cycle其时间 通道的计数器就向下减1,一直减到0值。因此对于通道0而言,当他的计数器减到0时,PIT就向系统产生一次时钟中断,表示一个时钟滴答已经过去了。计数 器为16bit,因此所能表示的最大值是65536,一秒内发生的滴答数是:1193181/65536=18.206482.
            PIT的I/O端口
         0x40   通道0 计数器 Read/Write
         0X41   通道1计数器
     Read/Write
         0X42  通道2计数器
      Read/Write
         0X43   控制字        Write Only
     Note: 因PIT I/O端口是8位,而PIT相应计数器是16位,因此必须对PIT计数器进行两次读写。
       8254 PIT的控制寄存器(0X43)的格式如下
                bit[7:6] — 通道选择位:00 ,通道0;01,通道1;10,通道2;11,read-back command,仅8254。
                bit[5:4] – Read/Write/Latch锁定位,00,锁定当前计数器以便读取计数值;01,只读高字节;10,只读低字节;11,先高后低。
                bit[3:1] – 设定各通道的工作模式。
              000   mode0       当通道处于count out 时产生中断信号,可用于系统定时
              001   mode1       Hardware retriggerable one-shot
              010   mode2       Rate Generator。产生实时时钟中断,通道0通常工作在这个模式下
              011 mode3        方波信号发生器
              100 mode4        Software triggered strobe
              101 mode5        Hardware triggered strobe
    3. 时间戳计数器 TSC
            Pentium开始,所有的Intel 80x86 CPU就都包含一个64位的时间戳记数器(TSC)的寄存器。该寄存器实际上是一个不断增加的计数器,它在CPU的每个时钟信号到来时加1(也即每一个clock-cycle输入CPU时,该计数器的值就加1)。 
        汇编指令rdtsc可以用于读取TSC的值。利用CPU的TSC,操作系统通常可以得到更为精准的时间度量。假如clock-cycle的频率是400MHZ,那么TSC就将每2.5纳秒增加一次。
    二.       Linux时钟中断处理程序
    1.      几个概念
    1)时钟周期(clock cycle)的频率:8253/8254 PIT的本质就是对由晶体振荡器产生的时钟周期进行计数,晶体振荡器在1秒时间内产生的时钟脉冲个数就是时钟周期的频率。Linux用宏 CLOCK_TICK_RATE来表示8254 PIT的输入时钟脉冲的频率(在PC机中这个值通常是1193180HZ),该宏定义在include/asm-i386/timex.h头文件中
    #define CLOCK_TICK_RATE 1193180        kernel=2.4 &2.6

    2)时钟滴答(clock tick):当PIT通道0的计数器减到0值时,它就在IRQ0上产生一次时钟中断,也即一次时钟滴答。PIT通道0的计数器的初始值决定了要过多少时钟周期才产生一次时钟中断,因此也就决定了一次时钟滴答的时间间隔长度。
    3)时钟滴答的频率(HZ):1秒时间内PIT所产生的时钟滴答次数。 这个值也由PIT通道0的计数器初值决定的.Linux内核用宏HZ来表示时钟滴答的频率,而且在不同的平台上HZ有不同的定义值。对于ALPHA和 IA62平台HZ的值是1024,对于SPARC、MIPS、ARM和i386等平台HZ的值都是100。该宏在i386平台上的定义如下 (include/asm-i386/param.h):
    #define HZ 100    kernel=2.4
    #define HZ   CONFIG_HZ       kernel=2.6

    4)宏LATCH定义要写到PIT通道0的计数器中的值,它表示PIT将隔多少个时钟周期产生一次时钟中断。公式计算: 
    LATCH=(1秒之内的时钟周期个数)÷(1秒之内的时钟中断次数)=(CLOCK_TICK_RATE)÷(HZ)

    定义在<include/linux/timex.h>
    #define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ)
    (5)全局变量jiffies:用于记录系统自启动以来产生的滴答总数。启动时,kernel将该变量初始为0,每次时钟中断处理程序timer_interrupt()将该变量加1。因为一秒钟内增加的时钟中断次数等于Hz,所以jiffies一秒内增加的值也是Hz。由此可得系统运行时间是jiffies/Hz 秒。
    jiffies定义于<linux/jiffies.h>中:
    extern unsigned long volatile jiffies;
    Note:
    在kernel 2.4,jiffies是32位无符号数;kernel 2.6,jiffies是64位无符号数。
    6)全局变量xtime: 结构类型变量,用于表示当前时间距UNIX基准时间1970-01-01 00:00:00的相对秒数值。当系统启动时,Kernel通过读取RTC Register中的数据来初始化系统时间(wall_time),该时间存放在xtime中。
    void __init time_init (void) {
                  ... ...
                  xtime.tv_sec = get_cmos_time ();
                  xtime.tv_usec = 0;
    ... ... }
    Note:实时时钟RTC的最主要作用便是在系统启动时用来初始化xtime变量。
    2.Linux的时钟中断处理程序
           Linux下时钟中断处理由time_interrupt() 函数实现,主要完成以下任务:
    l         获得xtime_lock锁,以便对访问的jiffies_64 (kernel2.6)和 xtime进行保护
    l         需要时应答或重新设置系统时钟。
    l         周期性的使用系统时间(wall_time)更新实时时钟RTC
    l         调用体系结构无关的时钟例程:do_timer()。
    do_timer()主要完成以下任务
    l         更新jiffies;
    l         更新系统时间(wall_time),该时间存放在xtime变量中
    l         执行已经到期的动态定时器
    l         计算平均负载值
    void do_timer(unsigned long ticks)
    {
     jiffies_64 += ticks;
     update_process_times(user_mode(regs));
     update_times (ticks);
    }
    static inline void update_times(unsigned long ticks)
    {
     update_wall_time ();
     calc_load (ticks);
    }
                    time_interrupt ()
                
                     static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { 
                       int count;
                       write_lock (&xtime_lock); //获得xtime_lock锁
     
                         if(use_cyclone)
                             mark_timeoffset_cyclone();
                         else if (use_tsc) {
                             rdtscl(last_tsc_low); //TSC register到last_tsc_low
                         spin_lock (&i8253_lock); //对自旋锁i8253_lock加锁,对8254PIT访问
                         outb_p (0x00, 0x43);    
     
                         count = inb_p(0x40);   
                         count |= inb(0x40) << 8;
                         if (count > LATCH) {
                             printk (KERN_WARNING "i8253 count too high! resetting../n");
                             outb_p (0x34, 0x43);
                             outb_p (LATCH & 0xff, 0x40);
                             outb(LATCH >> 8, 0x40);
                             count = LATCH - 1;
                         }
                       spin_unlock (&i8253_lock);
     
                         if (count = = LATCH) {
                                 count- -;
                         }
     
                         count = ((LATCH-1) - count) * TICK_SIZE;
                         delay_at_last_interrupt = (count + LATCH/2) / LATCH;
                         } //end use_tsc
                         do_timer_interrupt (irq, NULL, regs);
                         write_unlock(&xtime_lock);
    }//end time_interrupt
     
    do_timer_interrupt():
        static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
    {
    ……
    do_timer(regs);
    if((time_status & STA_UNSYNC)= =0&&xtime.tv_sec> last_rtc_update + 660 && xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) {
          if (set_rtc_mmss(xtime.tv_sec) == 0)
              last_rtc_update = xtime.tv_sec;
          else
              last_rtc_update = xtime.tv_sec - 600;
    ……
     }
    do_timer_interrupt()主要完成:调用do_timer()和判断是否需要更新CMOS时钟。更新CMOS时钟的条件如下:三个须同时成立
        1.系统全局时间状态变量time_status中没有设置STA_UNSYNC标志,即Linux没有设置外部同步时钟(如NTP)
        2.自从上次CMOS时钟更新已经过去11分钟。全局变量last_rtc_update保存上次更新CMOS时钟的时间.
        3.由于RTC存在Update Cycle,因此应在一秒钟间隔的中间500ms左右调用set_rtc_mmss()函数,将当前时间xtime.tv_sec写回RTC中。
    Note. Linux kernel 中定义了一个类似jiffies的变量wall_jiffies,用于记录kernel上一次更新xtime时,jiffies的值。
     
    Summary: Linux kernel在启动时,通过读取RTC里的时间日期初始化xtime,此后由kernel通过初始PIT来提供软时钟。
                           时钟中断处理过程可归纳为:系统时钟system timer在IRQ0上产生中断;kernel调用time_interrupt();time_interrupt()判断系统是否使用TSC,若使用 则读取TSC register;然后读取PIT 通道0的计数值;调用do_time_interrupt(),实现系统时间更新.
    展开全文
  • linux的时间与时钟中断处理

    千次阅读 2012-09-28 16:13:45
    本文主要介绍在Linux下的时间实现以及系统如何进行时钟中断处理。 一. Linux的硬件时间 PC机中的时间有三种硬件时钟实现,这三种都是基于晶振产生的方波信号输入。这三种时钟为:(1)实时时钟RTC( Real Time ...
    转载地址:http://blog.sina.com.cn/s/blog_62401233010118et.html
    本文主要介绍在Linux下的时间实现以及系统如何进行时钟中断处理。
    一.      Linux的硬件时间
    PC机中的时间有三种硬件时钟实现,这三种都是基于晶振产生的方波信号输入。这三种时钟为:(1)实时时钟RTC( Real Time Clock) (2)可编程间隔器PIT(ProgrammableInterval Timer )(3)时间戳计数器TSC(Time StampClock)
    1.    实时时钟RTC
      用于长时间存放系统时间的设备,即时关机后也可依靠主板CMOS电池继续保持系统的计时,原理图如下:

    Note:Linux与RTC的关系是,当Linux启动时从RTC读取时间和日期的基准值,然后在Kernel运行期间便抛开RTC,以软件的形式维护系统的时间日期,并在适当时机由Kernel将时间写回RTCRegister.
    1.1 RTCRegister
      (1). 时钟与日历Register
           共10个,地址:0x00-0x09,分别用于保存时间日历的具体信息,详情如下:

           00         Current Second for RTC
           01         Alarm Second
           02         Current Minute
           03         Alarm Minute
           04         Current Hour
           05         Alarm Hour
           06         Current Day of Week(1=Sunday)
           07         Current Date of Month
           08         Current Month
           09         Current Year
     (2).状态和控制Register
       共四个,地址:0x0a-0x0d,控制RTC芯片的工作方式,并表示当前状态。
    l        状态RegisterA , 0x0A格式如下:
            bit[7]——UIP标志(UpdateinProgress),为1表示RTC正在更新日历寄存器组中的值,此时日历寄存器组是不可访问的(此时访问它们将得到一个无意义的渐变值)。
    bit6:4]——这三位是用来定义RTC的操作频率。各种可能的值如下:

    DV2 DV1 DV0       
    0    0  0       4.194304MHZ
    0    0  1       1.048576MHZ
    0    1  0       32.769  KHZ
    1    1  0/1     
    任何
    PC机通常设置成“010”。
    bit3:0]——速率选择位(RateSelection bits),用于周期性或方波信号输出。
    RS3 RS2 RS1 RS0  周期性中断  方波  周期性中断  方波

    0   0  0  0   None      None      None     None
    0   0  0  1  30.517μs 32.768 KHZ 3.90625ms 256 HZ
    0   0  1  0  61.035μs 16.384 KHZ
    0   0  1  1  122.070μs 8.192KHZ
    0   1  0  0   244.141μs4.096KHZ
    0  1  0  1   488.281μs2.048KHZ
    0  1  1  0   976.562μs1.024KHZ
    0   1  1  1   1.953125ms   512HZ
    1   0  0  0   3.90625ms    256HZ
    1   0  0  1   7.8125ms     128HZ
    1   0  1  0   15.625ms     64HZ
    1  0  1  1   31.25ms      32HZ
    1   1  0  0   62.5ms       16HZ
    1   1  0  1   125ms         8HZ
    1  1  1  0   250ms         4HZ
    1   1  1  1   500ms         2HZ
    PC
    机BIOS对其默认的设置值是“0110”
    l        状态Register B , 0x0B格式如下:
    bit[7]——SET标志。为1表示RTC的所有更新过程都将终止,用户程序随后马上对日历寄存器组中的值进行初始化设置。为0表示将允许更新过程继续。
    bit[6]——PIE标志,周期性中断enable标志。
    bit[5]——AIE标志,告警中断enable标志。
    bit[4]——UIE标志,更新结束中断enable标志。
    bit[3]——SQWE标志,方波信号enable标志。
    bit[2]——DM标志,用来控制日历寄存器组的数据模式,0=BCD,1=BINARY。BIOS总是将它设置为0。
    bit[1]——24/12标志,用来控制hour寄存器,0表示12小时制,1表示24小时制。PC机BIOS总是将它设置为1。
    bit[0]——DSE标志。BIOS总是将它设置为0。
    l        状态Register C,0x0C 格式如下:
    bit[7]——IRQF标志,中断请求标志,当该位为1时,说明寄存器B中断请求 发生。

    bit[6]——PF标志,周期性中断标志,为1表示发生周期性中断请求。

    bit[5]——AF标志,告警中断标志,为1表示发生告警中断请求。

    bit[4]——UF标志,更新结束中断标志,为1表示发生更新结束中断请求。
    l        状态Register D,0x0D 格式如下:
     bit[7]——VRT标志(Valid RAM andTime),为1表示OK,为0表示RTC 已经掉电。

     bit[6:0]——总是为0,未定义。
    2.可编程间隔定时器 PIT
           每个PC机中都有一个PIT,以通过IRQ0产生周期性的时钟中断信号,作为系统定时器system timer。当前使用最普遍的是Intel 8254PIT芯片,它的I/O端口地址是0x40~0x43。
    Intel 8254 PIT有3个计时通道,每个通道都有其不同的用途:

    (1)通道0用来负责更新系统时钟。每当一个时钟滴答过去时,它就会通过IRQ0向      系统 产生一次时钟中断。

    (2) 通道1通常用于控制DMAC对RAM的刷新。

    (3) 通道2被连接到PC机的扬声器,以产生方波信号。

       每 个通道都有一个向下减小的计数器,8254PIT的输入时钟信号的频率是1.193181MHZ,也即一秒钟输入1193181个clock-cycle。每输入一个clock-cycle其时间通道的计数器就向下减1,一直减到0值。因此对于通道0而言,当他的计数器减到0时,PIT就向系统产生一次时钟中断,表示一个时钟滴答已经过去了。计数器为16bit,因此所能表示的最大值是65536,一秒内发生的滴答数是:1193181/65536=18.206482.
           PIT的I/O端口
        0x40  通道0 计数器 Read/Write
        0X41  通道1计数器
     Read/Write
        0X42  通道2计数器
     Read/Write
        0X43  控制字       Write Only
     Note: 因PITI/O端口是8位,而PIT相应计数器是16位,因此必须对PIT计数器进行两次读写。
      8254PIT的控制寄存器(0X43)的格式如下
               bit[7:6] — 通道选择位:00 ,通道0;01,通道1;10,通道2;11,read-backcommand,仅8254。
               bit[5:4] –Read/Write/Latch锁定位,00,锁定当前计数器以便读取计数值;01,只读高字节;10,只读低字节;11,先高后低。
               bit[3:1] – 设定各通道的工作模式。
             000  mode0      当通道处于count out 时产生中断信号,可用于系统定时
             001  mode1      Hardware retriggerable one-shot
             010  mode2      RateGenerator。产生实时时钟中断,通道0通常工作在这个模式下
             011 mode3       方波信号发生器
             100 mode4       Software triggered strobe
             101 mode5       Hardware triggered strobe
    3.时间戳计数器TSC
           Pentium开始,所有的Intel 80x86CPU就都包含一个64位的时间戳记数器(TSC)的寄存器。该寄存器实际上是一个不断增加的计数器,它在CPU的每个时钟信号到来时加1(也即每一个clock-cycle输入CPU时,该计数器的值就加1)。
       汇编指令rdtsc可以用于读取TSC的值。利用CPU的TSC,操作系统通常可以得到更为精准的时间度量。假如clock-cycle的频率是400MHZ,那么TSC就将每2.5纳秒增加一次。
    二.      Linux时钟中断处理程序
    1.     几个概念
    1)时钟周期(clockcycle)的频率:8253/8254PIT的本质就是对由晶体振荡器产生的时钟周期进行计数,晶体振荡器在1秒时间内产生的时钟脉冲个数就是时钟周期的频率。Linux用宏CLOCK_TICK_RATE来表示8254PIT的输入时钟脉冲的频率(在PC机中这个值通常是1193180HZ),该宏定义在include/asm-i386/timex.h头文件中
    #define CLOCK_TICK_RATE 1193180       kernel=2.4&2.6

    2)时钟滴答(clocktick):当PIT通道0的计数器减到0值时,它就在IRQ0上产生一次时钟中断,也即一次时钟滴答。PIT通道0的计数器的初始值决定了要过多少时钟周期才产生一次时钟中断,因此也就决定了一次时钟滴答的时间间隔长度。
    3)时钟滴答的频率(HZ):1秒时间内PIT所产生的时钟滴答次数。这个值也由PIT通道0的计数器初值决定的.Linux内核用宏HZ来表示时钟滴答的频率,而且在不同的平台上HZ有不同的定义值。对于ALPHA和IA62平台HZ的值是1024,对于SPARC、MIPS、ARM和i386等平台HZ的值都是100。该宏在i386平台上的定义如下(include/asm-i386/param.h):
    #define HZ 100   kernel=2.4
    #define HZ  CONFIG_HZ      kernel=2.6

    4)宏LATCH定义要写到PIT通道0的计数器中的值,它表示PIT将隔多少个时钟周期产生一次时钟中断。公式计算:
    LATCH=(1秒之内的时钟周期个数)÷(1秒之内的时钟中断次数)=(CLOCK_TICK_RATE)÷(HZ)

    定义在<include/linux/timex.h>
    #define LATCH ((CLOCK_TICK_RATE + HZ/2) /HZ)
    (5)全局变量jiffies:用于记录系统自启动以来产生的滴答总数。启动时,kernel将该变量初始为0,每次时钟中断处理程序timer_interrupt()将该变量加1。因为一秒钟内增加的时钟中断次数等于Hz,所以jiffies一秒内增加的值也是Hz。由此可得系统运行时间是jiffies/Hz秒。
    jiffies定义于<linux/jiffies.h>中:
    extern unsigned long volatile jiffies;
    Note:
    在kernel2.4,jiffies是32位无符号数;kernel 2.6,jiffies是64位无符号数。
    6)全局变量xtime:结构类型变量,用于表示当前时间距UNIX基准时间1970-01-0100:00:00的相对秒数值。当系统启动时,Kernel通过读取RTCRegister中的数据来初始化系统时间(wall_time),该时间存放在xtime中。
    void __init time_init (void) {
                 ... ...
                 xtime.tv_sec = get_cmos_time ();
                 xtime.tv_usec = 0;
    ... ... }
    Note:实时时钟RTC的最主要作用便是在系统启动时用来初始化xtime变量。
    2.Linux的时钟中断处理程序
          Linux下时钟中断处理由time_interrupt()函数实现,主要完成以下任务:
    l        获得xtime_lock锁,以便对访问的jiffies_64 (kernel2.6)和xtime进行保护
    l        需要时应答或重新设置系统时钟。
    l        周期性的使用系统时间(wall_time)更新实时时钟RTC
    l        调用体系结构无关的时钟例程:do_timer()。
    do_timer()主要完成以下任务
    l        更新jiffies;
    l        更新系统时间(wall_time),该时间存放在xtime变量中
    l        执行已经到期的动态定时器
    l        计算平均负载值
    void do_timer(unsigned longticks)
    {
     jiffies_64 += ticks;
     update_process_times(user_mode(regs));
     update_times (ticks);
    }
    static inline void update_times(unsigned long ticks)
    {
     update_wall_time ();
     calc_load (ticks);
    }
                   time_interrupt()
                
                    static void timer_interrupt(int irq, void *dev_id, struct pt_regs*regs) { 
                      int count;
                      write_lock (&xtime_lock);//获得xtime_lock锁
     
                       if(use_cyclone)
                           mark_timeoffset_cyclone();
                       else if (use_tsc) {
                           rdtscl(last_tsc_low); //TSCregister到last_tsc_low
                       spin_lock(&i8253_lock); //对自旋锁i8253_lock加锁,对8254PIT访问
                       outb_p (0x00,0x43);    
     
                       count =inb_p(0x40);   
                       count |= inb(0x40) <<8;
                       if (count > LATCH) {
                           printk(KERN_WARNING "i8253 count too high!resetting../n");
                           outb_p (0x34, 0x43);
                           outb_p (LATCH & 0xff, 0x40);
                           outb(LATCH >> 8,0x40);
                           count = LATCH - 1;
                       }
                     spin_unlock(&i8253_lock);
     
                       if (count = = LATCH) {
                               count- -;
                       }
     
                       count = ((LATCH-1) - count) * TICK_SIZE;
                       delay_at_last_interrupt = (count + LATCH/2) /LATCH;
                       } //end use_tsc
                       do_timer_interrupt (irq, NULL,regs);
                       write_unlock(&xtime_lock);
    }//end time_interrupt
     
    do_timer_interrupt():
       static inline void do_timer_interrupt(int irq, void *dev_id, structpt_regs *regs)
    {
    ……
    do_timer(regs);
    if((time_status & STA_UNSYNC)==0&&xtime.tv_sec>last_rtc_update + 660 &&xtime.tv_usec >= 500000 - ((unsigned) tick) / 2&& xtime.tv_usec <=500000 + ((unsigned) tick) / 2) {
         if (set_rtc_mmss(xtime.tv_sec) == 0)
             last_rtc_update = xtime.tv_sec;
         else
             last_rtc_update = xtime.tv_sec - 600;
    ……
     }
    do_timer_interrupt()主要完成:调用do_timer()和判断是否需要更新CMOS时钟。更新CMOS时钟的条件如下:三个须同时成立
       1.系统全局时间状态变量time_status中没有设置STA_UNSYNC标志,即Linux没有设置外部同步时钟(如NTP)
       2.自从上次CMOS时钟更新已经过去11分钟。全局变量last_rtc_update保存上次更新CMOS时钟的时间.
       3.由于RTC存在UpdateCycle,因此应在一秒钟间隔的中间500ms左右调用set_rtc_mmss()函数,将当前时间xtime.tv_sec写回RTC中。
    Note. Linux kernel中定义了一个类似jiffies的变量wall_jiffies,用于记录kernel上一次更新xtime时,jiffies的值。
     
    SummaryLinuxkernel在启动时,通过读取RTC里的时间日期初始化xtime,此后由kernel通过初始PIT来提供软时钟。
                         时钟中断处理过程可归纳为:系统时钟systemtimer在IRQ0上产生中断;kernel调用time_interrupt();time_interrupt()判断系统是否使用TSC,若使用则读取TSC register;然后读取PIT通道0的计数值;调用do_time_interrupt(),实现系统时间更新.

    展开全文
  • Linux之时钟中断详解

    2021-01-20 16:27:30
    另外该中断的中断处理函数除了更新系统时间外,还需要更新本地CPU统计数。指的是调用scheduler_tick递减进程的时间片,若进程的时间片递减到0,进程则被调度出去而放弃CPU使用权。 时钟中断的产生 Linux的OS时钟的...
  • 本文主要介绍在Linux下的时间实现以及系统如何进行时钟中断处理。 一. Linux的硬件时间 PC机中的时间有三种硬件时钟实现,这三种都是基于晶振产生的方波信号输入。这三种时钟为: 实时时钟RTC ( Real Time Clock) ...

    本文主要介绍在Linux下的时间实现以及系统如何进行时钟中断处理。

    一. Linux的硬件时间

    PC机中的时间有三种硬件时钟实现,这三种都是基于晶振产生的方波信号输入。这三种时钟为:

    1. 实时时钟RTC ( Real Time Clock)
    2. 可编程间隔器PIT(Programmable Interval Timer )
    3. 时间戳计数器TSC(Time Stamp Clock)

    实时时钟 RTC

    用于长时间存放系统时间的设备,即时关机后也可依靠主板CMOS电池继续保持系统的计时,原理图如下:
    Note: Linux与RTC的关系是,当Linux启动时从RTC读取时间和日期的基准值,然后在Kernel运行期间便抛开RTC,以软件的形式维护系统的时间日期,并在适当时机由Kernel将时间写回RTC Register.

    1.1 RTC Register

    (1). 时钟与日历Register
    共10个,地址:0x00-0x09,分别用于保存时间日历的具体信息,详情如下:
    00 Current Second for RTC
    01 Alarm Second
    02 Current Minute
    03 Alarm Minute
    04 Current Hour
    05 Alarm Hour
    06 Current Day of Week(1=Sunday)
    07 Current Date of Month
    08 Current Month
    09 Current Year

    (2).状态和控制Register
    共四个,地址:0x0a-0x0d,控制RTC芯片的工作方式,并表示当前状态。

    l 状态RegisterA , 0x0A 格式如下:
    bit[7]——UIP标志(Update in Progress),为1表示RTC正在更新日历寄存器组中的值,此时日历寄存器组是不可访问的(此时访问它们将得到一个无意义的渐变值)。
    bit[6:4]——这三位是用来定义RTC的操作频率。各种可能的值如下:
    
    DV2 DV1 DV0
    0 0 0 4.194304 MHZ
    0 0 1 1.048576 MHZ
    0 1 0 32.769 KHZ
    1 1 0/1 任何
    PC机通常设置成“010”。
    bit[3:0]——速率选择位(Rate Selection bits),用于周期性或方波信号输出。
    RS3 RS2 RS1 RS0 周期性中断 方波 周期性中断 方波
    0 0 0 0 None None None None
    0 0 0 1 30.517μs 32.768 KHZ 3.90625ms 256 HZ
    0 0 1 0 61.035μs 16.384 KHZ
    0 0 1 1 122.070μs 8.192KHZ
    0 1 0 0 244.141μs 4.096KHZ
    0 1 0 1 488.281μs 2.048KHZ
    0 1 1 0 976.562μs 1.024KHZ
    0 1 1 1 1.953125ms 512HZ
    1 0 0 0 3.90625ms 256HZ
    1 0 0 1 7.8125ms 128HZ
    1 0 1 0 15.625ms 64HZ
    1 0 1 1 31.25ms 32HZ
    1 1 0 0 62.5ms 16HZ
    1 1 0 1 125ms 8HZ
    1 1 1 0 250ms 4HZ
    1 1 1 1 500ms 2HZ

    PC机BIOS对其默认的设置值是“0110”

    l 状态Register B , 0x0B 格式如下:
    bit[7]——SET标志。为1表示RTC的所有更新过程都将终止,用户程序随后马上对日历寄存器组中的值进行初始化设置。为0表示将允许更新过程继续。
    bit[6]——PIE标志,周期性中断enable标志。
    bit[5]——AIE标志,告警中断enable标志。
    bit[4]——UIE标志,更新结束中断enable标志。
    bit[3]——SQWE标志,方波信号enable标志。
    bit[2]——DM标志,用来控制日历寄存器组的数据模式,0=BCD,1=BINARY。BIOS总是将它设置为0。
    bit[1]——24/12标志,用来控制hour寄存器,0表示12小时制,1表示24小时制。PC机BIOS总是将它设置为1。
    bit[0]——DSE标志。BIOS总是将它设置为0。
    l 状态Register C,0x0C 格式如下:
    bit[7]——IRQF标志,中断请求标志,当该位为1时,说明寄存器B中断请求 发生。
    bit[6]——PF标志,周期性中断标志,为1表示发生周期性中断请求。
    bit[5]——AF标志,告警中断标志,为1表示发生告警中断请求。
    bit[4]——UF标志,更新结束中断标志,为1表示发生更新结束中断请求。
    l 状态Register D,0x0D 格式如下:
    bit[7]——VRT标志(Valid RAM and Time),为1表示OK,为0表示RTC 已经掉电。
    bit[6:0]——总是为0,未定义。

    2.可编程间隔定时器 PIT

    每个PC机中都有一个PIT,以通过IRQ0产生周期性的时钟中断信号,作为系统定时器 system timer。当前使用最普遍的是Intel 8254 PIT芯片,它的I/O端口地址是0x40~0x43。

    Intel 8254 PIT有3个计时通道,每个通道都有其不同的用途:

    (1) 通道0用来负责更新系统时钟。每当一个时钟滴答过去时,它就会通过IRQ0向 系统 产生一次时钟中断。

    (2) 通道1通常用于控制DMAC对RAM的刷新。

    (3) 通道2被连接到PC机的扬声器,以产生方波信号。

    每 个通道都有一个向下减小的计数器,8254 PIT的输入时钟信号的频率是1.193181MHZ,也即一秒钟输入1193181个clock-cycle。每输入一个clock-cycle其时间 通道的计数器就向下减1,一直减到0值。因此对于通道0而言,当他的计数器减到0时,PIT就向系统产生一次时钟中断,表示一个时钟滴答已经过去了。计数 器为16bit,因此所能表示的最大值是65536,一秒内发生的滴答数是:1193181/65536=18.206482.

    PIT的I/O端口:

    0x40 通道0 计数器 Read/Write

    0X41 通道1计数器 Read/Write

    0X42 通道2计数器 Read/Write

    0X43 控制字 Write Only

    Note: 因PIT I/O端口是8位,而PIT相应计数器是16位,因此必须对PIT计数器进行两次读写。
    8254 PIT的控制寄存器(0X43)的格式如下:
    bit[7:6] — 通道选择位:00 ,通道0;01,通道1;10,通道2;11,read-back command,仅8254。
    bit[5:4] – Read/Write/Latch锁定位,00,锁定当前计数器以便读取计数值;01,只读高字节;10,只读低字节;11,先高后低。
    bit[3:1] – 设定各通道的工作模式。
    000 mode0 当通道处于count out 时产生中断信号,可用于系统定时
    001 mode1 Hardware retriggerable one-shot
    010 mode2 Rate Generator。产生实时时钟中断,通道0通常工作在这个模式下
    011 mode3 方波信号发生器
    100 mode4 Software triggered strobe
    101 mode5 Hardware triggered strobe

    时间戳计数器 TSC

    从Pentium开始,所有的Intel 80x86 CPU就都包含一个64位的时间戳记数器(TSC)的寄存器。该寄存器实际上是一个不断增加的计数器,它在CPU的每个时钟信号到来时加1(也即每一个clock-cycle输入CPU时,该计数器的值就加1)。

    汇编指令rdtsc可以用于读取TSC的值。利用CPU的TSC,操作系统通常可以得到更为精准的时间度量。假如clock-cycle的频率是400MHZ,那么TSC就将每2.5纳秒增加一次。

    二. Linux时钟中断处理程序

    1. 几个概念

    (1)时钟周期(clock cycle)的频率:8253/8254 PIT的本质就是对由晶体振荡器产生的时钟周期进行计数,晶体振荡器在1秒时间内产生的时钟脉冲个数就是时钟周期的频率。Linux用宏 CLOCK_TICK_RATE来表示8254 PIT的输入时钟脉冲的频率(在PC机中这个值通常是1193180HZ),该宏定义在include/asm-i386/timex.h头文件中

    define CLOCK_TICK_RATE 1193180 kernel=2.4 &2.6

    (2)时钟滴答(clock tick):当PIT通道0的计数器减到0值时,它就在IRQ0上产生一次时钟中断,也即一次时钟滴答。PIT通道0的计数器的初始值决定了要过多少时钟周期才产生一次时钟中断,因此也就决定了一次时钟滴答的时间间隔长度。

    (3)时钟滴答的频率(HZ):1秒时间内PIT所产生的时钟滴答次数。 这个值也由PIT通道0的计数器初值决定的.Linux内核用宏HZ来表示时钟滴答的频率,而且在不同的平台上HZ有不同的定义值。对于ALPHA和 IA62平台HZ的值是1024,对于SPARC、MIPS、ARM和i386等平台HZ的值都是100。该宏在i386平台上的定义如下 (include/asm-i386/param.h):

    define HZ 100 kernel=2.4
    define HZ CONFIG_HZ kernel=2.6

    (4)宏LATCH:定义要写到PIT通道0的计数器中的值,它表示PIT将隔多少个时钟周期产生一次时钟中断。公式计算:
    LATCH=(1秒之内的时钟周期个数)÷(1秒之内的时钟中断次数)=(CLOCK_TICK_RATE)÷(HZ)
    定义在<include/linux/timex.h>

    define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ)

    (5)全局变量jiffies:用于记录系统自启动以来产生的滴答总数。启动时,kernel将该变量初始为0,每次时钟中断处理程序timer_interrupt()将该变量加1。因为一秒钟内增加的时钟中断次数等于Hz,所以jiffies一秒内增加的值也是Hz。由此可得系统运行时间是jiffies/Hz 秒。
    jiffies定义于<linux/jiffies.h>中:
    extern unsigned long volatile jiffies;
    Note:在kernel 2.4,jiffies是32位无符号数;kernel 2.6,jiffies是64位无符号数。

    (6)全局变量xtime: 结构类型变量,用于表示当前时间距UNIX基准时间1970-01-01 00:00:00的相对秒数值。当系统启动时,Kernel通过读取RTC Register中的数据来初始化系统时间(wall_time),该时间存放在xtime中。

    void __init time_init (void) {
    ... ...
    xtime.tv_sec = get_cmos_time ();
    xtime.tv_usec = 0;
    ... ... }

    Note:实时时钟RTC的最主要作用便是在系统启动时用来初始化xtime变量。

    2.Linux的时钟中断处理程序

    Linux下时钟中断处理由time_interrupt() 函数实现,主要完成以下任务:

    l 获得xtime_lock锁,以便对访问的jiffies_64 (kernel2.6)和 xtime进行保护
    l 需要时应答或重新设置系统时钟。
    l 周期性的使用系统时间(wall_time)更新实时时钟RTC
    l 调用体系结构无关的时钟例程:do_timer()。
    do_timer()主要完成以下任务:
    l 更新jiffies;
    l 更新系统时间(wall_time),该时间存放在xtime变量中
    l 执行已经到期的动态定时器
    l 计算平均负载值
    
    void do_timer(unsigned long ticks)
    {
    jiffies_64 += ticks;
    update_process_times(user_mode(regs));
    update_times (ticks);
    }
    static inline void update_times(unsigned long ticks)
    {
    update_wall_time ();
    calc_load (ticks);
    }
    time_interrupt ():
    
                 static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { 
                   int count;
                   write_lock (&xtime_lock); //获得xtime_lock锁
    
                     if(use_cyclone)
                         mark_timeoffset_cyclone();
                     else if (use_tsc) {
                         rdtscl(last_tsc_low); //读TSC register到last_tsc_low
                     spin_lock (&i8253_lock); //对自旋锁i8253_lock加锁,对8254PIT访问
                     outb_p (0x00, 0x43);    
    
                     count = inb_p(0x40);   
                     count |= inb(0x40) << 8;
                     if (count > LATCH) {
                         printk (KERN_WARNING "i8253 count too high! resetting../n");
                         outb_p (0x34, 0x43);
                         outb_p (LATCH & 0xff, 0x40);
                         outb(LATCH >> 8, 0x40);
                         count = LATCH - 1;
                     }
                   spin_unlock (&i8253_lock);
    
                     if (count = = LATCH) {
                             count- -;
                     }
    
                     count = ((LATCH-1) - count) * TICK_SIZE;
                     delay_at_last_interrupt = (count + LATCH/2) / LATCH;
                     } //end use_tsc
                     do_timer_interrupt (irq, NULL, regs);
                     write_unlock(&xtime_lock);
    }//end time_interrupt
    
    do_timer_interrupt():
    static inline void do_timer_interrupt(int irq, void dev_id, struct pt_regs regs)
    {
    ……
    do_timer(regs);
    if((time_status & STA_UNSYNC)= =0&&xtime.tv_sec> last_rtc_update + 660 && xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) {
    if (set_rtc_mmss(xtime.tv_sec) == 0)
    last_rtc_update = xtime.tv_sec;
    else
    last_rtc_update = xtime.tv_sec - 600;
    ……
    }

    do_timer_interrupt()主要完成:调用do_timer()和判断是否需要更新CMOS时钟。更新CMOS时钟的条件如下:三个须同时成立

    1.系统全局时间状态变量time_status中没有设置STA_UNSYNC标志,即Linux没有设置外部同步时钟(如NTP)

    2.自从上次CMOS时钟更新已经过去11分钟。全局变量last_rtc_update保存上次更新CMOS时钟的时间.

    3.由于RTC存在Update Cycle,因此应在一秒钟间隔的中间500ms左右调用set_rtc_mmss()函数,将当前时间xtime.tv_sec写回RTC中。
    Note. Linux kernel 中定义了一个类似jiffies的变量wall_jiffies,用于记录kernel上一次更新xtime时,jiffies的值。

    Summary

    Linux kernel在启动时,通过读取RTC里的时间日期初始化xtime,此后由kernel通过初始PIT来提供软时钟。

    时钟中断处理过程可归纳为:系统时钟system timer在IRQ0上产生中断;kernel调用time_interrupt();time_interrupt()判断系统是否使用TSC,若使用 则读取TSC register;然后读取PIT 通道0的计数值;调用do_time_interrupt(),实现系统时间更新.

    转载于:https://www.cnblogs.com/muahao/p/6134202.html

    展开全文
  • 模拟时钟中断的产生及设计一个对时钟中断事件进行处理的模拟...通过实习了解中断及中断处理程序作用。本实习模拟“时钟中断事件”的处理,对其它中断事件的模拟处理,可根据各中断事件的性质确定处理原则,制定算法。
  • 文档介绍:第三章一、问答题1、某系统采用响应比高者优先的处理机调度算法,某个时刻根据用户要求创建了一个进程P,进程P在其存在过程中依次经历了:进程调度选中了进程P占用处理机运行(就绪—>运行),进程P运行中...
  • 时钟中断是系统中最重要的中断,每个时钟滴答都会产生时钟中断,它的中断矢量为(0100)或(0103)。   0533: . = 100^. 0534: kwlp; br6 0535: kwlp; br6   0569: .globl _clock 0570: kwlp: jsr r0,call; _...
  • 中断应用—时钟中断

    千次阅读 2019-08-20 15:40:54
    在所有的外部中断中,时钟中断起特殊的作用。因为计算机是以精确的时间进行数值运算和数据处理的,最基本的时间单元是时钟周期,例如取指令、执行指令、存取内存等,这里讨论的是操作系统建立的时间系统,这个时间...
  • 中断对于开发嵌入式系统来讲的地位绝对是毋庸置疑的,在C51单片机时代,一共只有5个中断,其中2个外部中断,2个定时/计数器中断和一个串口中断,但是在STM32中,中断数量大大增加,而且中断的设置也更加复杂。...
  • 时钟中断

    千次阅读 2011-11-23 15:43:12
    1.时钟源 内核把每一个可用于计时的时钟抽象为时钟源(clocksource)结构: /** * struct clocksource - hardware abstraction for a free running counter * Provides mostly state-fr
  • Linux之时钟中断

    万次阅读 2013-05-02 10:41:13
     时钟中断的产生  Linux的OS时钟的物理产生原因是可编程定时/计数器产生的输出脉冲,这个脉冲送入CPU,就可以引发一个中断请求信号,我们就把它叫做时钟中断。 “时钟中断”是特别重要的一个中断,因为整个操作...
  • 时钟中断非常特殊而且在系统作用也非常重大。 1.他用做记时。这点显而易见。 2.现在流行的操作系统linux和windows他们都是多进程并发执行的,为什么多进程可以并发,这是由中断支持的。一个进程执行的脚本是
  • 时钟中断hook

    2014-07-05 19:56:42
    先调用原时钟中断处理程序 dec cs:tickcnt ; jz show ; tickcnt变为0时刷新显示 iret ; 否则,中断返回 show: mov cs:tickcnt, 18 sti ; 及时开中断,使CPU能响应外设的中断请求 ; 保护现场 pusha push ax ...
  • Linux实现时钟中断的全过程

    千次阅读 2012-09-26 08:42:59
    5.2.2.Linux实现时钟中断的全过程 1.可编程定时/计数器的初始化 IBM PC中使用的是8253或8254芯片。有关该芯片的详细知识我们不再详述,只大体介绍以下它的组成和作用,如下表5.1所示:   表5.1 8253/8254的...
  •  驱动程序可以通过下面的函数注册并激活一个中断处理程序,以便处理中断:int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long irgflags, const char *...
  • 时钟中断(1)

    千次阅读 2016-01-16 12:16:58
    在所有外部中断中,时钟中断起着特殊的作用。因为计算机是以精确的时间进行数值运算的数据处理的,最基本的时间单元是时钟周期,例如取指令,执行指令,存取内存等,我们将要讨论的是操作系统建立的时间系统,这个...
  • 中断处理程序

    2014-08-20 16:47:17
    1、安装中断处理程序 系统中中断信号线很有限,有时只有15或16根。内核维护了一个类似于I/O端口注册表的中断信号线的注册表。一个模块可以申请一个中断请求IRQ,处理完以后也可以释放掉它。相关函数: 头文件  ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 54,085
精华内容 21,634
关键字:

时钟中断处理程序的主要功能