• Linux内核学习笔记八——定时器和时间管理


    一 内核中的时间观念

           内核在硬件的帮助下计算和管理时间。硬件为内核提供一个系统定时器用以计算流逝的时间。系

     统定时器以某种频率自行触发,产生时钟中断,进入内核时钟中断处理程序中进行处理。

           墙上时间和系统运行时间根据时钟间隔来计算。

    利用时间中断周期执行的工作:

           更新系统运行时间;

           更新实际时间;

           在smp系统上,均衡调度程序中各处理器上运行队列;

           检查当前进程是否用尽了时间片,重新进行调度;

           运行超时的动态定时器;

           更新资源消耗和处理器时间的统计值;

    二 节拍率

           系统定时器的频率;通过静态预处理定义的——HZ;系统启动按照HZ值对硬件进行设置。体系结构不同,HZ值也不同;HZ可变的。

        //内核时间频率

        #define HZ 1000

    提高节拍率中断产生更加频繁带来的好处:

           提高时间驱动事件的解析度;

           提高时间驱动事件的准确度;

           内核定时器以更高的频度和准确度;

           依赖顶上执行的系统调用poll()和select()能更高的精度运行;

           系统时间测量更精细;

           提高进程抢占的准确度;

    提高节拍率带来的副作用:

           中断频率增高系统负担增加;

           中断处理程序占用处理器时间增多;

           频繁打断处理器高速缓存;

    节拍率HZ值需要在其中进行平衡。

     

    三 jiffies

      jiffies:全局变量,用来记录自系统启动以来产生的节拍总数。启动时内核将该变量初始化为0;

    此后每次时钟中断处理程序增加该变量的值。每一秒钟中断次数HZ,jiffies一秒内增加HZ。系统运行时间 = jiffie/HZ.

    jiffies用途:计算流逝时间和时间管理

    jiffies内部表示:

                  extern u64 jiffies_64;

                  extern unsigned long volatile jiffies;     //位长更系统有关32/64

      32位:497天后溢出

      64位:……

          

    //0.5秒后超时
    
    unsigned long timeout = jiffies + HZ/2;
    ……
    
    //注意jiffies值溢出回绕用宏time_before 而非 直timeout > jiffies
    if(time_before(jiffies,timeout)){
    
           //没有超时
    }else{
    
           //超时
    }

    四 硬时钟和定时器

      两种设备进行计时:系统定时器和实时时钟。

    实时时钟(RTC):用来持久存放系统时间的设备,即便系统关闭后,靠主板上的微型电池提供电力保持系统的计时。

        系统启动内核通过读取RTC来初始化墙上时间,改时间存放在xtime变量中。

    系统定时器:内核定时机制,注册中断处理程序,周期性触发中断,响应中断处理程序,进行处理执行以下工作:

      l  获得xtime_lock锁,访问jiffies和更新墙上时间xtime;

      l  更新实时时钟;

      l  更新资源统计值:当前进程耗时,系统时间等;

      l  执行已到期的动态定时器;

      l  执行scheduler_tick()

     

    //中断处理程序    
    irqreturn_t timer_interrupt(int irq, void *dev)
    {
        //ticks have passed
        long nticks;
    
        xtime_update(nticks);
    
        while (nticks--)
               update_process_times(user_mode(get_irq_regs()));
    
        return IRQ_HANDLED;
    }
     
    
    void xtime_update(unsigned long ticks)
    {
        //seq锁
        write_seqlock(&xtime_lock);
    
        do_timer(ticks);
    
        write_sequnlock(&xtime_lock);
    }
    
    void do_timer(unsigned long ticks)
    {
        jiffies_64 += ticks;
    
        //更新墙上时间 ——实际时间
        update_wall_time();
    
        calc_global_load(ticks);
    }
    
     
    
    void update_process_times(int user_tick)
    {
        struct task_struct *p = current;
    
        //计算当前进程执行时间
        account_process_tick(p, user_tick);
    
        //触发软中断TIMER_SOFTIRQ 超时的timer
        run_local_timers();
    
        //计算进程时间片
        scheduler_tick();
    
    }

     

    五 定时器

           定时器:管理内核时间的基础,推后或执行时间执行某些代码。

    定时器数据结构:

    struct timer_list {
                  struct list_head entry;
    
                  //定时值基于jiffies
                  unsigned long expires;
    
                  //定时器内部值
                  struct tvec_base *base;
    
                  //定时器处理函数
                  void (*function)(unsigned long);
    
                  //定时器处理函数参数
                  unsigned long data;
    
                  ……
           };

    定时器使用:

        struct timer_list my_timer;
    
           //初始化定时器
           init_timer(&my_timer);
    
           ……
          
           //激活定时器
           add_timer(&my_timer);
    
    
           //删除定时器
           del_timer(my_timer);
    
           ……

    六 延迟执行

           使用定时器和下半部机制推迟执行任务。还有其他延迟执行的机制:

    忙等待:

           利用节拍,精确率不高

           unsigned long delay = jiffies + 2*HZ ; //2秒 节拍整数倍才行;

           while(time_before(jiffies,delay))

                  ;

    短延迟:延迟时间精确到毫秒,微妙;短暂等待某个动作完成时,比时钟节拍更短;依靠数次循环达到延迟效果。

           void udelay(unsigned long usecs)

           void mdelay(unsigned long msecs)

     

    schedule_timeout()延迟:使执行的任务睡眠指定时间,达到延迟

     

    signed long __sched schedule_timeout(signed long timeout)
    {
           struct timer_list timer;
           unsigned long expire;
    
           switch (timeout)
           {
             case MAX_SCHEDULE_TIMEOUT:
    
                  //无限期睡眠
                  schedule();
                  goto out;
             default:
                  if (timeout < 0) {
                         current->state = TASK_RUNNING;
                         goto out;
                  }
           }
           //超时时间
           expire = timeout + jiffies;
    
           //初始化一个timer定时器 参数current task
           setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);
    
           __mod_timer(&timer, expire, false, TIMER_NOT_PINNED);
    
           schedule();
    
           del_singleshot_timer_sync(&timer);
     
           /* Remove the timer from the object tracker */
           destroy_timer_on_stack(&timer);
           timeout = expire - jiffies;
     
     out:
           return timeout < 0 ? 0 : timeout;
    }
    
    
    static void process_timeout(unsigned long __data)
    {
           //唤醒被睡眠的任务
           wake_up_process((struct task_struct *)__data);
    }

     

  • 相关阅读:
    Profibus 接线
    如何正确使用Profibus插头以及终端电阻
    Java设计模式(3)——抽象工厂模式
    Java设计模式(2)——工厂方法模式
    Java设计模式(1)——简单工厂模式
    Oracle——控制事务
    Jackson-将对象转为Json字符串
    $.ajax
    Ajax——jQuery实现
    Ajax——三种数据传输格式
  • 原文地址:https://www.cnblogs.com/bastard/p/2696393.html
Copyright © 2020-2023  润新知