• 时钟初始化 内核初始化部分( start_kernel 函数)和时钟相关的过程主要有以下几个: tick_init()init_timers()hrtimers_init()time_init()  tick_init 函数 函数 tick_init() 很简单,调用 clockevents_...

    
    
    时钟初始化

    内核初始化部分( start_kernel 函数)和时钟相关的过程主要有以下几个:

    1. tick_init()
    2. init_timers()
    3. hrtimers_init()
    4. time_init()

     tick_init 函数

    函数 tick_init() 很简单,调用 clockevents_register_notifier 函数向 clockevents_chain 通知链注册元素: tick_notifier。这个元素的回调函数指明了当时钟事件设备信息发生变化(例如新加入一个时钟事件设备等等)时,应该执行的操作,该回调函数为 tick_notify 。

    /kernel/time/tick-common.c 依赖 CONFIG_GENERIC_CLOCKEVENTS
    /**
     * tick_init - initialize the tick control
     *
     * Register the notifier with the clockevents framework
     */
    void __init tick_init(void)
    {
    	clockevents_register_notifier(&tick_notifier);
    }
    
    !CONFIG_GENERIC_CLOCKEVENTS 
    static inline void tick_init(void) { }
    //内核通知链技术
    /**
     * clockevents_register_notifier - register a clock events change listener
     */
    int clockevents_register_notifier(struct notifier_block *nb)
    {
        unsigned long flags;
        int ret;
    
        raw_spin_lock_irqsave(&clockevents_lock, flags);
        ret = raw_notifier_chain_register(&clockevents_chain, nb);
        raw_spin_unlock_irqrestore(&clockevents_lock, flags);
    
        return ret;
    }
    


     init_timers 函数

    void __init init_timers(void)
    {
        int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE, 
                                            (void *)(long)smp_processor_id());
        ……
        register_cpu_notifier(&timers_nb);
        open_softirq(TIMER_SOFTIRQ,run_timer_softirq, NULL);
    }

    代码解释:

    • 初始化本 CPU 上的软件时钟相关的数据结构,参见3.2节
    • 向 cpu_chain 通知链注册元素 timers_nb ,该元素的回调函数用于初始化指定 CPU 上的软件时钟相关的数据结构
    • 初始化时钟的软中断处理函数

    2.3.3 time_init 函数

    void __init time_init(void)
    {
        ……
        init_tsc_clocksource();
        late_time_init = choose_time_init();
    }

    函数 init_tsc_clocksource 初始化 tsc 时钟源。choose_time_init 实际是函数 hpet_time_init ,其代码清单2-3

    清单2-3 hpet_time_init 函数
    void __init hpet_time_init(void)
    {
        if (!hpet_enable())
            setup_pit_timer();
    
        setup_irq(0, &irq0);
    }

    函数 hpet_enable 检测系统是否可以使用 hpet 时钟,如果可以则初始化 hpet 时钟。否则初始化 pit 时钟。最后设置硬件时钟发生时的处理函数(参见2.4节)。

    初始化硬件时钟这个过程主要包括以下两个过程(参见 hpet_enable 的实现):

    1. 初始化时钟源信息( struct clocksource 类型的变量),并将其添加到时钟源链表中,即 clocksource_list 链表(参见图2-1)。
    2. 初始化时钟事件设备信息( struct clock_event_device 类型的变量),并向通知链 clockevents_chain 发布通知:一个时钟事件设备要被添加到系统中。在通知(执行回调函数)结束后,该时钟事件设备被添加到时钟事件设备链表中,即 clockevent_devices 链表(参见图2-1)。有关通知链的内容参见2.2节。

    需要注意的是在初始化时钟事件设备时,全局变量 global_clock_event 被赋予了相应的值。该变量保存着系统中当前正在使用的时钟事件设备(保存了系统当前使用的硬件时钟中断发生时,要执行的中断处理函数的指针)。

    展开全文
  • 嵌入式Linux内核时钟初始化问题 首先搞清楚RTC在kernel内的作用: linux系统有两个时钟:一个是由主板电池驱动的“Real Time Clock”也叫做RTC或者叫CMOS时钟,硬件时钟。当操作系统关机的时候,用这个来记录时间,...

    嵌入式Linux内核时钟初始化问题

    首先搞清楚RTCkernel内的作用:

    linux系统有两个时钟:一个是由主板电池驱动的“Real Time Clock”也叫做RTC或者叫CMOS时钟,硬件时钟。当操作系统关机的时候,用这个来记录时间,但是对于运行的系统是不用这个时间的。另一个时间是 “System clock”也叫内核时钟或者软件时钟,是由软件根据时间中断来进行计数的,内核时钟在系统关机的情况下是不存在的,所以,当操作系统启动的时候,内核时钟是要读取RTC时间来进行时间同步。并且在系统关机的时候将系统时间写回RTC中进行同步。

    如前所述,Linux内核与RTC进行互操作的时机只有两个:
    1) 内核在启动时从RTC中读取启动时的时间与日期;
    2) 内核在需要时将时间与日期回写到RTC中。

    系统启动时,内核通过读取RTC来初始化内核时钟,又叫墙上时间,该时间放在xtime变量中。
    The current time of day (the wall time) is defined in kernel/timer.c:
    struct timespec xtime;

    The timespec data structure is defined in as:

    struct timespec {
            time_t tv_sec;               /* seconds */
            long tv_nsec;                /* nanoseconds */
    };

    问题1:系统启动时在哪读取RTC的值并设置内核时钟进行时间同步的呢?
    最有可能读取RTC设置内核时钟的位置应该在arch/arm/kernel/time.c里的time_init函数内.

    time.c为系统的时钟驱动部分.time_init函数会在系统初始化时,由init/main.c里的start_kernel函数内调用.X86架构就是在这里读RTC值并初始化系统时钟xtime的.

    ARM架构的time_init代码如下:

    /* arch/arm/kernel/time.c */

    void __init time_init(void)
    {
    if (system_timer->offset == NULL)
      system_timer->offset = dummy_gettimeoffset;
    system_timer->init();

    #ifdef CONFIG_NO_IDLE_HZ
    if (system_timer->dyn_tick)
      system_timer->dyn_tick->lock = SPIN_LOCK_UNLOCKED;
    #endif
    }

    上面system_timer->init()实际执行的是时钟驱动体系架构相关(具体平台)部分定义的init函数,若是s3c2410平台,则执行的为arch/arm/mach-s3c2410/time.c里定义的s3c2410_timer_init函数.不过s3c2410_timer_init()也没有读RTC的代码.整个时钟驱动初始化的过程大致就执行这些代码.

    既然在系统时钟驱动初始化的过程中没有读RTC值并设置内核时钟,那会在哪设置呢?

    我搜了一下,发现内核好象只有在arch/cris/kernel/time.c里有RTC相关代码,如下:
    /* arch/cris/kernel/time.c */
    /* grab the time from the RTC chip */
    //读RTC的函数
    unsigned long get_cmos_time(void)
    {
    unsigned int year, mon, day, hour, min, sec;
    sec = CMOS_READ(RTC_SECONDS);
    min = CMOS_READ(RTC_MINUTES);
    hour = CMOS_READ(RTC_HOURS);
    day = CMOS_READ(RTC_DAY_OF_MONTH);
    mon = CMOS_READ(RTC_MONTH);
    …………
    return mktime(year, mon, day, hour, min, sec);
    }

    这个函数会在update_xtime_from_cmos内被调用:
    void update_xtime_from_cmos(void)
    {
    if(have_rtc) {
      xtime.tv_sec = get_cmos_time();
      xtime.tv_nsec = 0;
    }
    }

    另外还有设置rtc的函数
    int set_rtc_mmss(unsigned long nowtime); /* write time into RTC chip */

    不过我加了printk测试了一下,好象arch/cris/kernel/time.c这个文件和这两个函数只是适用与X86?
    ARM平台启动时并不走这边.因此执行不到这些函数。
    那ARM平台启动时,系统是在哪读RTC的值并对内核时钟(WallTime)进行初始化的呢?

    已解决:
    嵌入式Linux内核(ARM)是在系统启动时执行/etc/init.d/hwclock.sh脚本,这个脚本会调用hwclock小程序读取RTC的值并设置系统时钟。
    (换句话说,这要取决于你制作的文件系统里是否有这样的脚本)

    /* /etc/init.d/hwclock.sh */

    DAEMON1=/sbin/hwclock
    start() {
        local RET ERROR=

        [ ! -f /etc/adjtime ] &&  echo "0.0 0 0.0" > /etc/adjtime
        log_status_msg "Setting the System Clock using the Hardware Clock as reference..." -n

        # Copies Hardware Clock time to System Clock using the correct
        # timezone for hardware clocks in local time, and sets kernel
        # timezone. DO NOT REMOVE.
        [ "$HWCLOCKACCESS" != no ] && $DAEMON1 --hctosys $GMT $BADYEAR

        #
        # Now that /usr/share/zoneinfo should be available,
        # announce the local time.
        #
        log_status_msg "System Clock set. Local time: `date`"
        log_status_msg ""
        return 0
    }

    hwclock最先读取的设备文件是 /dev/rtc  ,busybox里面的hwclock是这样实现的:
    static int xopen_rtc(int flags)
    {
    int rtc;

    if (!rtcname) {
      rtc = open("/dev/rtc", flags);
      if (rtc >= 0)
       return rtc;
      rtc = open("/dev/rtc0", flags);
      if (rtc >= 0)
       return rtc;
      rtcname = "/dev/misc/rtc";
    }
    return xopen(rtcname, flags);
    }

    2. 内核如何更新RTC时钟?
    通过set_rtc函数指针指向的函数,set_rtc在arch/arm/kernel/time.c内
    /* arch/arm/kernel/time.c */
    /*
    * hook for setting the RTC's idea of the current time.
    */
    int (*set_rtc)(void);

    但是set_rtc函数指针在哪初始化的呢?set_rtc应该是和RTC驱动相关的函数.

    搜索kernel源码后发现,好象内核其他地方并没有对其初始化。待解决!

    set_rtc在do_set_rtc内调用
    static inline void do_set_rtc(void)
    {
    ……
    if (set_rtc())
      /*
       * rtc update failed.  Try again in 60s
       */
      next_rtc_update = xtime.tv_sec + 60;
    else
      next_rtc_update = xtime.tv_sec + 660; /* update every ~11 minutes by default*/
    }

    do_set_rtc在timer_tick里调用
    /*
    * Kernel system timer support. 
    */
    void timer_tick(struct pt_regs *regs)
    {
    profile_tick(CPU_PROFILING, regs);
    do_leds();
    do_set_rtc();
    do_timer(1);
    ……
    }
    timer_tick为Kernel提供的体系架构无关的时钟中断处理函数,通常会在体系架构相关的时钟中断处理函数内调用它。如s3c2410是这样的:

    在arch/arm/mach-s3c2410/time.c中
    * IRQ handler for the timer
    */
    static irqreturn_t
    s3c2410_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
    {
    write_seqlock(&xtime_lock);
    timer_tick(regs);
    write_sequnlock(&xtime_lock);
    return IRQ_HANDLED;
    }

    展开全文
  • include/linux/notifier.h struct notifier_block {  int (*notifier_call)(struct notifier_block *, unsigned long, void *);  struct notifier_block *next;  int priority; }; 通知链中的元素,记录了...
    1、相关数据结构
    include/linux/notifier.h
    struct notifier_block {
     int (*notifier_call)(struct notifier_block *, unsigned long, void *);
     struct notifier_block *next;
     int priority;
    };
    通知链中的元素,记录了当发出通知时,应该执行的操作(即回调函数)
    链头中保存着指向元素链表的指针。通知链元素结构则保存着回调函数的类型以及优先级
     
    2、时钟初始化
    2.1 内核初始化部分( start_kernel 函数)和时钟相关的过程主要有以下几个:
    tick_init()
    init_timers()
    hrtimers_init()
    time_init()
    其中函数 hrtimers_init() 和高精度时钟相关,下面将详细介绍这几个函数。
    2.2.1 tick_init 函数
    kernel/time/tick-common.c
    void __init tick_init(void)
    {
    clockevents_register_notifier(&tick_notifier);
    }
    static struct notifier_block tick_notifier = {
    .notifier_call = tick_notify,
    };
    函数 tick_init() 很简单,调用 clockevents_register_notifier 函数向 clockevents_chain 通知链注册元素: tick_notifier。这个元素的回调函数指明了当时钟事件设备信息发生变化(例如新加入一个时钟事件设备等等)时,应该执行的操作,该回调函数为 tick_notify
    kernel/time/tick-common.c
    static int tick_notify(struct notifier_block *nb, unsigned long reason,
              void *dev)
    {
     switch (reason) {
     case CLOCK_EVT_NOTIFY_ADD:
      return tick_check_new_device(dev);
     case CLOCK_EVT_NOTIFY_BROADCAST_ON:
     case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
     case CLOCK_EVT_NOTIFY_BROADCAST_FORCE:
      tick_broadcast_on_off(reason, dev);
      break;
     case CLOCK_EVT_NOTIFY_BROADCAST_ENTER:
     case CLOCK_EVT_NOTIFY_BROADCAST_EXIT:
      tick_broadcast_oneshot_control(reason);
      break;
     case CLOCK_EVT_NOTIFY_CPU_DYING:
      tick_handover_do_timer(dev);
      break;
     case CLOCK_EVT_NOTIFY_CPU_DEAD:
      tick_shutdown_broadcast_oneshot(dev);
      tick_shutdown_broadcast(dev);
      tick_shutdown(dev);
      break;
     case CLOCK_EVT_NOTIFY_SUSPEND:
      tick_suspend();
      tick_suspend_broadcast();
      break;
     case CLOCK_EVT_NOTIFY_RESUME:
      tick_resume();
      break;
     default:
      break;
     }
     return NOTIFY_OK;
    }

     
     
    2.2.2 init_timers 函数 
     
    函数 init_timers() 的实现如清单2-1(省略了部分和
    主要功能无关的内容,以后代码同样方式处理)
    kernel/timer.c
    void __init init_timers(void)
    {
     int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
        (void *)(long)smp_processor_id());
     init_timer_stats();
     BUG_ON(err != NOTIFY_OK);
     register_cpu_notifier(&timers_nb);
     open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
    }
    static struct notifier_block __cpuinitdata timers_nb = {
    .notifier_call = timer_cpu_notify,
    };

    代码解释:
    • 初始化本 CPU 上的软件时钟相关的数据结构,参见3.2节
    • 向 cpu_chain 通知链注册元素 timers_nb ,该元素的回调函数用于初始化指定 CPU 上的软件时钟相关的数据结构
    • 初始化时钟的软中断处理函数
    kernel/timer.c
    static int __cpuinit timer_cpu_notify(struct notifier_block *self,
        unsigned long action, void *hcpu)
    {
     long cpu = (long)hcpu;
     int err;
     switch(action) {
     case CPU_UP_PREPARE:
     case CPU_UP_PREPARE_FROZEN:
      err = init_timers_cpu(cpu);
      if (err < 0)
       return notifier_from_errno(err);
      break;
    #ifdef CONFIG_HOTPLUG_CPU
     case CPU_DEAD:
     case CPU_DEAD_FROZEN:
      migrate_timers(cpu);
      break;
    #endif
     default:
      break;
     }
     return NOTIFY_OK;
    }
    2.2.3 hrtimers_init函数
    kernel/hrtimer.c
    void __init hrtimers_init(void)
    {
     hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE,
         (void *)(long)smp_processor_id());
     register_cpu_notifier(&hrtimers_nb);
    #ifdef CONFIG_HIGH_RES_TIMERS
     open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq);
    #endif
    }
    static struct notifier_block __cpuinitdata hrtimers_nb = {
     .notifier_call = hrtimer_cpu_notify,
    };
     
    kernel/hrtimer.c
    static int __cpuinit hrtimer_cpu_notify(struct notifier_block *self,
         unsigned long action, void *hcpu)
    {
     int scpu = (long)hcpu;
     switch (action) {
     case CPU_UP_PREPARE:
     case CPU_UP_PREPARE_FROZEN:
      init_hrtimers_cpu(scpu);
      break;
    #ifdef CONFIG_HOTPLUG_CPU
     case CPU_DYING:
     case CPU_DYING_FROZEN:
      clockevents_notify(CLOCK_EVT_NOTIFY_CPU_DYING, &scpu);
      break;
     case CPU_DEAD:
     case CPU_DEAD_FROZEN:
     {
      clockevents_notify(CLOCK_EVT_NOTIFY_CPU_DEAD, &scpu);
      migrate_hrtimers(scpu);
      break;
     }
    #endif
     default:
      break;
     }
     return NOTIFY_OK;
    }
    2.2.4 time_init 函数
    相关数据结构
    system_timer定义在arch/arm/kernel/time.c文件开头
    struct sys_timer *system_timer;
    struct sys_timer定义在arch/arm/include/asm/mach/time.h文件
    struct sys_timer {
    struct sys_device dev;
    void (*init)(void);
    void (*suspend)(void);
    void (*resume)(void);
    #ifdef CONFIG_ARCH_USES_GETTIMEOFFSET
    unsigned long (*offset)(void);
    #endif
    };
    在分析time_init 函数前我们先来看看嵌入式Linux内核时钟初始化问题
      首先搞清楚RTC在kernel内的作用: linux系统有两个时钟:一个是由主板电池驱动的“Real Time Clock”也叫做RTC或者叫CMOS时钟,硬件时钟。当操作系统关机的时候,用这个来记录时间,但是对于运行的系统是不用这个时间的。
      另一个时间是 “System clock”也叫内核时钟或者软件时钟,是由软件根据时间中断来进行计数的,
      内核时钟在系统关机的情况下是不存在的,所以,当操作系统启动的时候,内核时钟是要读取RTC时间来进行时间同步。并且在系统关机的时候将系统时间写回RTC中进行同步。 如前所述,Linux内核与RTC进行互操作的时机只有两个:
      1) 内核在启动时从RTC中读取启动时的时间与日期;
      2) 内核在需要时将时间与日期回写到RTC中。 系统启动时,内核通过读取RTC来初始化内核时钟,又叫墙上时间,该时间放在xtime变量中。
     
    struct timespec {
     __kernel_time_t tv_sec;   /* seconds */
     long  tv_nsec;  /* nanoseconds */
    };
    问题1:系统启动时在哪读取RTC的值并设置内核时钟进行时间同步的呢?
      最有可能读取RTC设置内核时钟的位置应该在arch/arm/kernel/time.c里的time_init函数内.time.c为系统的时钟驱动部分.time_init函数会在系统初始化时,由init/main.c里的start_kernel函数内调用.X86架构就是在这里读RTC值并初始化系统时钟xtime的. ARM架构的time_init代码如下
    void __init time_init(void)
    {
     system_timer->init(); //这行实际执行的就是s3c2410_timer_init
    }
    其中system_timer与体系结构相关,对于2410它在arch/arm/plat-samsung/time.c初始化
    struct sys_timer s3c24xx_timer = {
     .init  = s3c2410_timer_init,
     .offset  = s3c2410_gettimeoffset,
     .resume  = s3c2410_timer_setup
    };
    system_timer在setup_arch(arch/arm/kernel/setup.c)内通过map_desc机制被初始化为s3c24xx_timer. 如上面s3c2410时钟驱动代码所示,s3c24xx_timer的init成员即指向s3c2410_timer_init函数。
     
    s3c2410_timer_init
    arch/arm/plat-samsung/time.c
    static void __init s3c2410_timer_init(void)
    {
     s3c2410_timer_resources();//首先初始化timerclk、tin和tdiv三个结构体,后两个和pwm相关
     s3c2410_timer_setup();//这里做了一些时钟初始化设置,但是针对timer4的
     setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);//设置中断处理函数
    }
    不过 s3c2410_timer_init()也没有读RTC的代码.整个时钟驱动初始化的过程大致就执行这些代码.既然在系统时钟驱动初始化的过程中没有读RTC值并设置内核时钟,那会在哪设置呢? 我搜了一下,发现内核好象只有在arch/cris/kernel/time.c里有RTC相关代码,如下
    unsigned long
    get_cmos_time(void)
    {
     unsigned int year, mon, day, hour, min, sec;
     if(!have_rtc)
      return 0;
     sec = CMOS_READ(RTC_SECONDS);
     min = CMOS_READ(RTC_MINUTES);
     hour = CMOS_READ(RTC_HOURS);
     day = CMOS_READ(RTC_DAY_OF_MONTH);
     mon = CMOS_READ(RTC_MONTH);
     year = CMOS_READ(RTC_YEAR);
     sec = bcd2bin(sec);
     min = bcd2bin(min);
     hour = bcd2bin(hour);
     day = bcd2bin(day);
     mon = bcd2bin(mon);
     year = bcd2bin(year);
     if ((year += 1900) < 1970)
      year += 100;
     return mktime(year, mon, day, hour, min, sec);
    }
    这个函数会在read_persistent_clock内被调用:
    void read_persistent_clock(struct timespec *ts)
    {
     ts->tv_sec = get_cmos_time();
     ts->tv_nsec = 0;
    }
    另外还有设置rtc的函数
      int set_rtc_mmss(unsigned long nowtime); /* write time into RTC chip */ 不过我加了printk测试了一下,好象arch/cris/kernel/time.c这个文件和这两个函数只是适用与X86?
      arm平台启动时并不走这边.因此执行不到这些函数。那arm平台启动时,系统是在哪读RTC的值并对内核时钟(WallTime)进行初始化的呢?
      嵌入式Linux内核(arm)是在系统启动时执行/etc/init.d/hwclock.sh脚本,这个脚本会调用hwclock小程序读取RTC的值并设置系统时钟。
      (换句话说,这要取决于你制作的文件系统里是否有这样的脚本)
      /* /etc/init.d/hwclock.sh */DAEMON1=/sbin/hwclock
      start() {
      local RET ERROR=    [ ! -f /etc/adjtime ] &&  echo "0.0 0 0.0" > /etc/adjtime
      log_status_msg "Setting the System Clock using the Hardware Clock as reference..." -n    # Copies Hardware Clock time to System Clock using the correct
      # timezone for hardware clocks in local time, and sets kernel
      # timezone. DO NOT REMOVE.
      [ "$HWCLOCKACCESS" != no ] && $DAEMON1 --hctosys $GMT $BADYEAR    #
      # Now that /usr/share/zoneinfo should be available,
      # announce the local time.
      #
      log_status_msg "System Clock set. Local time: `date`"
      log_status_msg ""
      return 0
      }
      hwclock最先读取的设备文件是 /dev/rtc  ,busybox里面的hwclock是这样实现的:
      static int xopen_rtc(int flags)
      {
      int rtc; if (!rtcname) {
      rtc = open("/dev/rtc", flags);
      if (rtc >= 0)
      return rtc;
      rtc = open("/dev/rtc0", flags);
      if (rtc >= 0)
      return rtc;
      rtcname = "/dev/misc/rtc";
      }
      return xopen(rtcname, flags);
      }
    2. 内核如何更新RTC时钟?
      通过set_rtc函数指针指向的函数,set_rtc在arch/arm/kernel/time.c内
      /* arch/arm/kernel/time.c */
      /*
      * hook for setting the RTC's idea of the current time.
      */
      int (*set_rtc)(void);但是set_rtc函数指针在哪初始化的呢?set_rtc应该是和RTC驱动相关的函数.搜索kernel源码后发现,好象内核其他地方并没有对其初始化。待解决!
    set_rtc在do_set_rtc内调用
    do_set_rtc在timer_tick里调用
      void timer_tick(struct pt_regs *regs)
      {
      profile_tick(CPU_PROFILING, regs);
      do_leds();
      do_set_rtc();
      do_timer(1);
      ……
      }
    arch/arm/kernel/time.c
    #ifndef CONFIG_GENERIC_CLOCKEVENTS
    /*
     * Kernel system timer support.
     */
    void timer_tick(void)
    {
     profile_tick(CPU_PROFILING);
     do_leds();
     write_seqlock(&xtime_lock);
     do_timer(1);
     write_sequnlock(&xtime_lock);
    #ifndef CONFIG_SMP
     update_process_times(user_mode(get_irq_regs()));
    #endif
    }
    #endif
    timer_tick为Kernel提供的体系架构无关的时钟中断处理函数,通常会在体系架构相关的时钟中断处理函数内调用它。如s3c2410是这样的:在arch/arm/plat-samsung/time.c中
    static irqreturn_t
    s3c2410_timer_interrupt(int irq, void *dev_id)
    {
     timer_tick();
     return IRQ_HANDLED;
    }
    static struct irqaction s3c2410_timer_irq = {
     .name  = "S3C2410 Timer Tick",
     .flags  = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
     .handler = s3c2410_timer_interrupt,
    };
     
    Request_irq和setup_irq的区别
    request_irq在2.6.36核中它是request_threaded_irq的封装,如下:
    include/linux/interrupt.h
    static inline int __must_check
    request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
         const char *name, void *dev)
    {
     return request_threaded_irq(irq, handler, NULL, flags, name, dev);
    }
    因此,Linux内核提供了两个注册中断处理函数的接口:setup_irq和request_threaded_irq。这两个函数都定义在kernel/irq/manage.c里。
    这两个函数有什么样的区别呢?

    先看看setup_irq
    Setup_irq通常用在系统时钟(GP Timer)驱动里,注册系统时钟驱动的中断处理函数。
    源码如下:
    int setup_irq(unsigned int irq, struct irqaction *act)
    {
     struct irq_desc *desc = irq_to_desc(irq);
     return __setup_irq(irq, desc, act);
    }
    EXPORT_SYMBOL_GPL(setup_irq);
    下面举个列子, 如s3c2410 timer驱动:
    arch/arm/plat-samsung/time.c初始化
    static struct irqaction s3c2410_timer_irq = {
     .name  = "S3C2410 Timer Tick",
     .flags  = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
     .handler = s3c2410_timer_interrupt,
    };
    static void __init s3c2410_timer_init(void)
    {
     s3c2410_timer_resources();
     s3c2410_timer_setup();
     setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);
    }
    struct sys_timer s3c24xx_timer = {
     .init  = s3c2410_timer_init,
     .offset  = s3c2410_gettimeoffset,
     .resume  = s3c2410_timer_setup
    };
    可以看到,setup_irq的使用流程很简单。首先定义s3c2410 timer驱动的irqaction结构体,该结构体用于描述timer中断的基本属性包括中断名、类别以及该中断handler等。然后通过setup_irq函数将timer的irqaction注册进内核。其中,IRQ_TIMER4为s3c2410 timer的中断号。
     
    再看看request_threaded_irq
     
    request_threaded_irq源码如下:
    /* kernel/irq/manage.c */
    int request_threaded_irq(unsigned int irq, irq_handler_t handler,
        irq_handler_t thread_fn, unsigned long irqflags,
        const char *devname, void *dev_id)
    {
     struct irqaction *action;
     struct irq_desc *desc;
     int retval;
     /*
      * Sanity-check: shared interrupts must pass in a real dev-ID,
      * otherwise we'll have trouble later trying to figure out
      * which interrupt is which (messes up the interrupt freeing
      * logic etc).
      */
     if ((irqflags & IRQF_SHARED) && !dev_id)/* 使用共享中断但没有提供非NULL的dev_id则返回错误 */
      return -EINVAL;
     desc = irq_to_desc(irq);
     if (!desc)
      return -EINVAL;
     if (desc->status & IRQ_NOREQUEST) /* 该中断号已被使用并且未共享 */
      return -EINVAL;
     if (!handler) {
      if (!thread_fn)
       return -EINVAL;
      handler = irq_default_primary_handler;
     }
     action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); /* 动态创建一个irqaction */
     if (!action)
      return -ENOMEM;
    /* 下面几行是根据request_threaded_irq传进来的参数对irqaction结构体赋值 */
     action->handler = handler;
     action->thread_fn = thread_fn;
     action->flags = irqflags;
     action->name = devname;
     action->dev_id = dev_id;
     chip_bus_lock(irq, desc);
     retval = __setup_irq(irq, desc, action);/* 调用__setup_irq注册该中断的irqaction结构体 */
     chip_bus_sync_unlock(irq, desc);
     if (retval)
      kfree(action);
    #ifdef CONFIG_DEBUG_SHIRQ
     if (!retval && (irqflags & IRQF_SHARED)) {
      /*
       * It's a shared IRQ -- the driver ought to be prepared for it
       * to happen immediately, so let's make sure....
       * We disable the irq to make sure that a 'real' IRQ doesn't
       * run in parallel with our fake.
       */
      unsigned long flags;
      disable_irq(irq);
      local_irq_save(flags);
      handler(irq, dev_id);
      local_irq_restore(flags);
      enable_irq(irq);
     }
    #endif
     return retval;
    }
    由上可以看出,request_threaded_irq的大致流程为先对申请的中断线进行安全检测,然后根据request_threaded_irq传进来的参数,动态创建该中断对应的irqaction结构体,最后通过setup_irq函数将该irqaction注册进内核适当的位置。

    这两个函数的使用流程搞清楚了,那么两者之间的联系也就清楚了:
    1) setup_irq的注册过程包含__setup_irq,最终是调用__setup_irq。
    2) request_threaded_irq比setup_irq多一套错误检测机制,即kzalloc前面3行if语句。
    而setup_irq通常是直接注册irqaction,并没针对相应中断线进行错误检测,如该irq 线是否已经被占用等。因此setup_irq通常只用在特定的中断线上,如System timer。除系统时钟驱动外,大部份驱动还是通过request_threaded_irq注册中断。

    这里有个小问题:
    既然request_threaded_irq实际上就是包含了__setup_irq的注册过程,那系统时钟驱动(GP Timer Driver)中断可以用request_threaded_irq来注册吗?

    做个小试验, 将s3c2410 timer驱动的setup_irq那行去掉,改为用request_irq注册。
    修改后代码如下:
    static void __init s3c2410_timer_init(void)
    {
     s3c2410_timer_resources();//首先初始化timerclk、tin和tdiv三个结构体,后两个和pwm相关
     s3c2410_timer_setup();//这里做了一些时钟初始化设置,但是针对timer4的
     //setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);
     request_irq(IRQ_TIMER4, &s3c2410_timer_irq,IRQF_DISABLED | IRQF_TIMER, "S3C2410 Timer Tick", NULL);
    }
    编译运行。
    结果:内核挂掉

    为什么呢?很明显,系统时钟驱动中断不能用request_irq注册,大致搜了一下源码也发现,看到其他平台相关的时钟驱动中断部分都是用的setup_irq注册的。
    我们来分析一下原因。
    看看request_threaded_irq和setup_irq 还有哪些细节不一样?
    仔细观察后注意到request_irq内有这么一行代码:
    action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
    作用为动态创建一个irqaction。
    kzalloc实际上是调用了 kmalloc进行分配的。源码如下:
    /* include/linux/slab.h */
    static inline void *kzalloc(size_t size, gfp_t flags)
    {
     return kmalloc(size, flags | __GFP_ZERO);
    }
    而kmalloc实际上也是使用的slab机制进行分配的。源码如下:
    /* include/linux/slab.h */
    static inline void *kmalloc(size_t size, gfp_t flags)
    {
           if (__builtin_constant_p(size)) {
                  int i = 0;
    #define CACHE(x) \
                  if (size <= x) \
                         goto found; \
                  else \
                         i++;
    #include "kmalloc_sizes.h"
    #undef CACHE
                  {
                        extern void __you_cannot_kmalloc_that_much(void);
                         __you_cannot_kmalloc_that_much();
                  }
    found:
                  return kmem_cache_alloc((flags & GFP_DMA) ?
                         malloc_sizes[i].cs_dmacachep :
                         malloc_sizes[i].cs_cachep, flags);
           }
           return __kmalloc(size, flags);
    }
    使用slab机制分配内存必须先对slab进行初始化,包括mem_init和kmem_cache_init。
    看看kernel的初始化流程:
    /* init/main.c */
    asmlinkage void __init start_kernel(void)
    {
           ……
           time_init();
           ……
     time_init();
     profile_init();
     if (!irqs_disabled())
      printk(KERN_CRIT "start_kernel(): bug: interrupts were "
         "enabled early\n");
     early_boot_irqs_on();
     local_irq_enable();
     /* Interrupts are enabled now so all GFP allocations are safe. */
     gfp_allowed_mask = __GFP_BITS_MASK;
     kmem_cache_init_late();---- set up the general caches
           ……
    }

    Time_init函数在kmem_cache_init_late之前被调用,而time_init会调用体系结构相关部分系统时钟驱动的初始化函数。拿s3c2410的例子来说,time_init最终会调用s3c2410_timer_init函数,进行s3c2410时钟驱动的初始化和注册中断处理函数。
    具体过程如下:
    time_init函数定义在arch/arm/kernel/time.c内:
    void __init time_init(void)
    {
     system_timer->init(); //这行实际执行的就是s3c2410_timer_init
    }
    system_timer在setup_arch(arch/arm/kernel/setup.c)内通过map_desc机制被初始化为s3c24xx_timer. 如上面s3c2410时钟驱动代码所示,s3c24xx_timer的init成员即指向s3c2410_timer_init函数。

    现在我们搞清楚了,我们大概的估计是系统时钟驱动(GP Timer Driver)的中断处理函数不能用request_irq注册是因为request_irq内会调用kmalloc动态分配内存创建timer的irqaction结构体。而kmalloc也是使用的slab内存分配机制,使用kmalloc前必须先对kernel的slab以及mem data structure进行初始化。而这部分初始化工作是在系统时钟驱动初始化之后才进行的,所以造成kmalloc失败,从而造成系统时钟驱动的中断未注册成功,进而内核挂掉。

    展开全文
  • 内核的初始化过程由start_kernel函数(\init\main.c)开始,至第一个用户进程init结束,调用了一系列的初始化函数对所有的内核组件进行初始化。其中,start_kernel、rest_init、kernel_init、init_post4个函数构成了...

    本文参考了https://blog.csdn.net/qing_ping/article/details/17351017博主的内容。
    内核的初始化过程由start_kernel函数(\init\main.c)开始,至第一个用户进程init结束,调用了一系列的初始化函数对所有的内核组件进行初始化。其中,start_kernel、rest_init、kernel_init、init_post4个函数构成了整个初始化过程的主线。下面先来分析start_kernel函数。

    asmlinkage void __init start_kernel(void)
    {
    	char * command_line;
    	extern const struct kernel_param __start___param[], __stop___param[];
    	/*这两个变量为地址指针,指向内核启动该参数处理相关结构体段在内存中位置(虚拟地址)。
    	声明传入参数的外部参数对于arm平台,位于\include\asm-generic\vmlinux.lds.h*/
    	/*
    	 * Need to run as early as possible, to initialize the
    	 * lockdep hash:
    	 */
     	 /*函数中初始化了两个哈希链表,分别是classhash_table和chainhash_table,
     	 主要是用来调试内核锁 的,比如检测加锁的顺序看是否有发生死锁的可能,
     	 具体的作用要看后面怎么使用这两个哈希链表。*/
    	lockdep_init();
    	
    	  /*当只有一个CPU的时候,这个函数什么都不做,但是如果有多个CPU的
    	  时候,那么它就返回在启动的时候那个CPU的代号*/
    	smp_setup_processor_id();
    	
    	  /*该函数的作用是初始化obj_hash、obj_static_pool这2个全局变量*/
    	debug_objects_early_init();
      
    	 /* Set up the the initial canary ASAP:
    	 */
    	 /*该函数主要用于初始化“金丝雀“——canary的值。用于防止栈溢出攻击。*/
    	boot_init_stack_canary();
    	
    	/*1.cgroup:它的全称为control group。即一组进程的行为控制。
    	2.该函数主要是做数据结构和其中链表的初始化
    	*/
    	cgroup_init_early();
    	
    	/*关闭当前CPU的中断,即系统总中断(底层调用汇编指令)*/
    	local_irq_disable();
    	early_boot_irqs_disabled = true;
    
    /*
     * Interrupts are still disabled. Do necessary setups, then
     * enable them
     */
     	/*注册clockevents框架*/
    	tick_init();
    	
    	/*激活当前CPU(在内核全局变量中将当前CPU的状态设为激活状态*/
    	boot_cpu_init();
    	
    	/*初始化页地址,使用链表将其链接起来*/
    	page_address_init();
    	
    	/*显示内核的版本信息*/
    	printk(KERN_NOTICE "%s", linux_banner);
    	
    	/*内核构架相关初始化函数,可以说是非常重要的一个初始化步骤。其中包含了
    	处理器相关参数的初始化、内核启动参数(tagged list)的获取和前期处理、
    	内存子系统的早期的初始化(bootmem分配器)。 主要完成了4个方面的工作,
    	一个就是取得MACHINE和PROCESSOR的信息然或将他们赋值给kernel相应的全
    	局变量,然后呢是对boot_command_line和tags接行解析,再然后就是
    	memory、cache的初始化,最后是为kernel的后续运行请求资源*/
    	setup_arch(&command_line);
    	
    	/*1.初始化代表内核本身内存使用的管理结构体init_mm。
    	2.每一个任务都有一个mm_struct结构以管理内存空间,init_mm是内核的mm_struct
    	3.设置成员变量*mmap指向自己,意味着内核只有一个内存管理结构
    	4.设置*pgd=swapper_pg_dir,swapper_pg_dir是内核的页目录(在arm体系结构有16K,
    	所以init_mm定义了整个kernel的内存空间)
    	5.这些内容涉及到内存管理子系统
    	mm_init_owner(&init_mm, &init_task);
    	
    	/*初始化CPU屏蔽字*/
    	mm_init_cpumask(&init_mm);
    	
    	/*对cmdline进行备份和保存:保存未改变的command_line到字符数组static_command_line[]中,
    	保存boot_command_line到字符数组saved_command_line[]中*/
    	setup_command_line(command_line);
    	
    	/*下面三段式针对SMP处理器的内存初始化函数,对arm来说是空*/
    	setup_nr_cpu_ids();
    	setup_per_cpu_areas();
    	smp_prepare_boot_cpu();	/* arch-specific boot-cpu hooks */
    
    	/*建立系统内存页区(zone)链表*/
    	build_all_zonelists(NULL);
    	
    	/*内存页初始化*/
    	page_alloc_init();
    	
    	/*打印Linux启动命令行参数*/
    	printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
    	/*Kernel command line: console=ttyS1,115200n8 //调试串口为UART1,波特率为115200n8
    	mem=32M@0xc0000000 mem=192M@0xc4000000 //DDR内存分配,为arm分配了224M
    	eth=02:02:30:30:97:81//mac地址
    	da850-panel=TL070A //显示设备
    	da850-emac=mii //网络模式为MII
    	root=ubi0_0 ubi.mtd=4,2048 //文件系统节点、分区及分区偏移量
    	rootfstype=ubifs//文件系统类型为ubifs
    	*/
    
    	/*解析早期格式的内核参数*/
    	parse_early_param();
    	parse_args("Booting kernel", static_command_line, __start___param,
    		   __stop___param - __start___param,
    		   &unknown_bootoption);
    
    	jump_label_init();
    
    	/*
    	 * These use large bootmem allocations and must precede
    	 * kmem_cache_init()
    	 */
    	 /*初始化hash表,以便于从进程的PID获得相应的进程描述指针,按照开发板上的物理
    	 内存初始化pid hash表*/
    	setup_log_buf(0);
    	pidhash_init();
    	
    	/*建立节点哈希表和数据缓冲哈希表*/
    	vfs_caches_init_early();
    	
    	/*对异常处理函数进行排序*/
    	sort_main_extable();
    
    	/*完成对系统保留中断向量(异常、非屏蔽中断以及系统调用)的初始化*/
    	trap_init();
    	
    	/*建立内核的内存分配器*/
    	mm_init();
    
    	/*
    	 * Set up the scheduler prior starting any interrupts (such as the
    	 * timer interrupt). Full topology setup happens at smp_init()
    	 * time - but meanwhile we still have a functioning scheduler.
    	 */
    	 /*先初始化0进程,包括段选择符,描述符GDT,LDT等。然后将其他63个进程的段选择符
    	 描述符置空,设置好后,将任务0的段选择符,描述符GDT,LDT等加载仅某个寄存器中。
    	 接着设置系统中断定时器,中断函数判断是否要切换,意思就是当进程时间片已经消耗完时,
    	 调用定时器中断函数判断是否要切换,最后定义系统调用*/
    	sched_init();
    	
    	/*
    	 * Disable preemption - early bootup scheduling is extremely
    	 * fragile until we cpu_idle() for the first time.
    	 */
    	/*禁止内核抢占*/
    	preempt_disable();
    	
    	/*检查中断是否已经打开,如果已经打开,则关闭中断*/
    	if (!irqs_disabled()) {
    		printk(KERN_WARNING "start_kernel(): bug: interrupts were "
    				"enabled *very* early, fixing it\n");
    		local_irq_disable();
    	}
    	/*创建idr缓冲区*/
    	idr_init_cache();
    	perf_event_init();
    	
    	/*互斥访问机制*/
    	rcu_init();
    	radix_tree_init();
    	/* init some links before init_ISA_irqs() */
    	early_irq_init();
    	
    	/*中断向量初始化*/
    	init_IRQ();
    	
    	/*初始化优先级数组*/
    	prio_tree_init();
    	
    	/*定时器初始化*/
    	init_timers();
    	
    	/*高精度时钟初始化*/
    	hrtimers_init();
    	
    	/*软中断初始化*/
    	softirq_init();
    	
    	/*初始化资源和普通计时器*/
    	timekeeping_init();
    	
    	/*初始化系统时钟源*/
    	time_init();
    	
    	/*对内核的一个性能测试工具profile进行初始化*/
    	profile_init();
    	call_function_init();
    	if (!irqs_disabled())
    		printk(KERN_CRIT "start_kernel(): bug: interrupts were "
    				 "enabled early\n");
    	early_boot_irqs_disabled = false;
    	
    	/*使能中断*/
    	local_irq_enable();
    
    	/* Interrupts are enabled now so all GFP allocations are safe. */
    	gfp_allowed_mask = __GFP_BITS_MASK;
    	
    	/*kmem_cache_init_late的目的在于完善slab分配器的缓存机制*/
    	kmem_cache_init_late();
    
    	/*
    	 * HACK ALERT! This is early. We're enabling the console before
    	 * we've done PCI setups etc, and console_init() must be aware of
    	 * this. But we do want output early, in case something goes wrong.
    	 */
    	 /*初始化控制台以显示printk的内容,在此之前调用的printk只是把数据存到缓冲区*/
    	console_init();
    	if (panic_later)
    		panic(panic_later, panic_param);
    	/*如果定义了CONFIG_LOCKDEP宏,那么就打印锁依赖信息,否则什么也不做*/
    	lockdep_info();
    
    	/*
    	 * Need to run this when irqs are enabled, because it wants
    	 * to self-test [hard/soft]-irqs on/off lock inversion bugs
    	 * too:
    	 */
    	locking_selftest();
    
    #ifdef CONFIG_BLK_DEV_INITRD
    	if (initrd_start && !initrd_below_start_ok &&
    	    page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
    		printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
    		    "disabling it.\n",
    		    page_to_pfn(virt_to_page((void *)initrd_start)),
    		    min_low_pfn);
    		initrd_start = 0;
    	}
    #endif
    	page_cgroup_init();
    	debug_objects_mem_init();
    	kmemleak_init();
    	setup_per_cpu_pageset();
    	numa_policy_init();
    	if (late_time_init)
    		late_time_init();
    	sched_clock_init();
    	/*校准延时函数的精确度*/
    	calibrate_delay();
    	pidmap_init();
    
    	/*匿名虚拟内存域(anonymous VMA)初始化*/
    	anon_vma_init();
    #ifdef CONFIG_X86
    	if (efi_enabled)
    		efi_enter_virtual_mode();
    #endif
    	thread_info_cache_init();//获取thread_info缓存空间,大部分架构为空函数(包括arm)
    	cred_init();//任务信用系统初始化
    	fork_init(totalram_pages);//进程创建机制初始化
    	proc_caches_init();//初始化进程创建机制所需的其他数据结构,为其申请空间
    	buffer_init();//缓存系统初始化,创建缓存头空间,并检查其大小限时
    	key_init();//内核密钥管理系统初始化
    	security_init();//内核安全框架初始化
    	dbg_late_init();
    	vfs_caches_init(totalram_pages);//虚拟文件系统缓存初始化
    	signals_init();//信号管理系统初始化
    	/* rootfs populating might need page-writeback */
    	page_writeback_init();//页写回机制初始化
    #ifdef CONFIG_PROC_FS
    	proc_root_init();//proc文件系统初始化
    #endif
    	cgroup_init();//control group正式初始化
    	cpuset_init();//CPUSET初始化
    	taskstats_init_early();//任务状态早期初始化函数,为结构体获取高速缓存,并初始化互斥机制
    	delayacct_init();//任务延迟初始化
    
    	check_bugs();//检查CPU BUG的函数,通过软件规避BUG
    	
    	/*ACPI-Advanced Configuration and Power Interface 高级配置及电源接口*/
    	acpi_early_init(); /* before LAPIC and SMP init */
    	sfi_init_late();
    
    	ftrace_init();
    
    	/* Do the rest non-__init'ed, we're now alive */
    	rest_init();//虽然从名字来说是剩余的初始化,但是这个函数中的初始化包括了很多的内容
    }
    
    
    
    展开全文
  • init_timers void __init init_timers(void) {  init_timer_cpus();  init_timer_stats();  open_softirq(TIMER_SOFTIRQ, run_timer_softirq); }   static void __init init_timer_cpu(int cpu)... stru
     
    init_timers
    void __init init_timers(void)
    {
     init_timer_cpus();
     init_timer_stats();
     open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
    }
    
     

    static void __init init_timer_cpu(int cpu) {  struct timer_base *base;  int i;

     for (i = 0; i < NR_BASES; i++) {   base = per_cpu_ptr(&timer_bases[i], cpu);   base->cpu = cpu;   spin_lock_init(&base->lock);   base->clk = jiffies;  } }

    static void __init init_timer_cpus(void) {  int cpu;

     for_each_possible_cpu(cpu)   init_timer_cpu(cpu); }

     
    展开全文
  • 原博:http://www.embeddedlinux.org.cn/html/jishuzixun/201402/21-2755.html?bsh_bid=353686139 内核版本:Linux 2.6.18 ...linux系统有两个时钟:一个是由主板电池驱动的“Real Tim
  • /**  * tick_init - initialize the tick control  */ void __init tick_init(void) {  tick_broadcast_init();  tick_nohz_init(); }
  • 使用的编译器为arm-linux-gcc-4.5.1。 包括四个文件head.S main.c main.lds Makefile。 实验目的:把CPU主频提高到400Mhz。(FCLK:400 HCLK:100 PCLK:50) 2440使用MPLL和UPLL将来自晶振或外部时钟倍频...
  • Linux内核初始化过程

    2013-07-11 09:38:04
    了解内核的初始化过程有助理解内核的整个流程。 一、对硬件数据结构的初始化过程。 1、系统首先调用 printk() 函数在屏幕上打印 Linux 内核版本号和编译内核所使用的 gcc 编译器版本号、启用时间等,如果这个...
  • CAN初始化流程

    2020-05-23 18:05:50
    1.配置相关引脚的复用功能,使能CAN时钟 ...通过先设置CAN_MCR寄存器的INRQ位,让CAN进入初始化模式,然后设置CAN_MCR的其他相关控制位。再通过CAN_BTR设置波特率和工作模式(正常模式/环回模式)等信息。最后设置INRQ
  • Linux 网络初始化流程 1.  系统初始化 (1)  X86 PC初始化过程 1)  CPU自身初始化 2)  BIOS加电自检POST 完成系统硬件的检测,包括内存检测、系统总线检测等 3)  BIOS加载内核主引导程序 MBR中...
  • N. 标题n n. 标题n (n) 标题n N. 标题n n. 标题n (n) 标题n 博客内容
  • 日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 ...在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换
  • Linux时钟中断

    2013-05-02 10:42:17
     Linux的OS时钟的物理产生原因是可编程定时/计数器产生的输出脉冲,这个脉冲送入CPU,就可以引发一个中断请求信号,我们就把它叫做时钟中断。 “时钟中断”是特别重要的一个中断,因为整个操作系统的活动都受到它...
  • 最近在解EMMC的一个bug,发现Linux EMMC有点小复杂,先整理个文档出来吧 ...1初始化 1.1mmc_init 2769 static int __init mmc_init(void) 2770 { 2774 2775 workqueue = alloc_ordered_workqueue("kmmcd",
  • 本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。 欢迎和大家交流。...     过去的一周,一直处在纠结的时刻中,一周过去了,基本问题和疑惑也在渐渐的解决中,回过头去想想,原来问题的出现,只是一个小小的...
  • 1. Linux内核时钟系统和定时器实现Linux 2.6.16之前,内核只支持低精度时钟,内核定时器的工作方式: 系统启动后,会读取时钟源设备(RTC, HPET,PIT…),初始化当前系统时间; 内核会根据HZ(系统定时器频率,节拍率)...
  • 硬件资源越来越庞大和复杂,内核的另一个挑战就是要便捷的管理这些资源。同时,面对如此之多的平台不同的CPU ...CPU中各个模块都需要时钟驱动,内核需 要一种机制能通用所有的平台,方便的管理CPU上所有的clk
  • 本文分析了 Linux 2.6.25 内核的时钟处理机制,首先介绍了在计算机系统中的一些硬件计时器,然后重点介绍了 Linux 操作系统中的硬件时钟和软件时钟的处理过程以及软件时钟的应用。最后对全文进行了总结。 ◆1、
1 2 3 4 5 ... 20
收藏数 33,383
精华内容 13,353
关键字:

linux时钟初始化