软中断初始化

    技术2024-07-28  88

    5.10.3 软中断初始化

    open_softirq结束后,init_timers就结束了,整个内核就可以享受时钟服务了,接下来start_kernel609行调用hrtimers_init。由于我们没有配置CONFIG_HIGH_RES_TIMERS,所以这个函数仅仅是把全局notifier_block变量hrtimer_cpu_notify加入通知链,供将来的内核各模块使用。

     

    然后,start_kernel610行调用softirq_init来初始化整个软中断系统:

     

    674void __init softirq_init(void)

     675{

     676        int cpu;

     677

     678        for_each_possible_cpu(cpu) {

     679                int i;

     680

     681                per_cpu(tasklet_vec, cpu).tail =

     682                        &per_cpu(tasklet_vec, cpu).head;

     683                per_cpu(tasklet_hi_vec, cpu).tail =

     684                        &per_cpu(tasklet_hi_vec, cpu).head;

     685                for (i = 0; i < NR_SOFTIRQS; i++)

     686                        INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));

     687        }

     688

     689        register_hotcpu_notifier(&remote_softirq_cpu_notifier);

     690

     691        open_softirq(TASKLET_SOFTIRQ, tasklet_action);

     692        open_softirq(HI_SOFTIRQ, tasklet_hi_action);

     693}

     

    我们看到681~686行,初始化每个CPUtasklet_vectasklet_hi_vecsoftirq_work_list[]结构。689行,如果定义了CONFIG_HOTPLUG_CPU配置选项,就将全局notifier_block类型变量remote_softirq_cpu_notifier注册到通知链中。

     

    随后691692行将TASKLET_SOFTIRQHI_SOFTIRQ对应的软中断打开,这样I/O驱动程序中实现可延迟函数的首选方法tasklet就可以使用了,对这方面感兴趣的同学仍然请访问博客“下半部分”

    http://blog.csdn.net/yunsongice/archive/2010/03/07/5354011.aspx

     

    start_kernel611timekeeping_init函数:

     

    534void __init timekeeping_init(void)

     535{

     536        struct clocksource *clock;

     537        unsigned long flags;

     538        struct timespec now, boot;

     539

     540        read_persistent_clock(&now);

     541        read_boot_clock(&boot);

     542

     543        write_seqlock_irqsave(&xtime_lock, flags);

     544

     545        ntp_init();

     546

     547        clock = clocksource_default_clock();

     548        if (clock->enable)

     549                clock->enable(clock);

     550        timekeeper_setup_internals(clock);

     551

     552        xtime.tv_sec = now.tv_sec;

     553        xtime.tv_nsec = now.tv_nsec;

     554        raw_time.tv_sec = 0;

     555        raw_time.tv_nsec = 0;

     556        if (boot.tv_sec == 0 && boot.tv_nsec == 0) {

     557                boot.tv_sec = xtime.tv_sec;

     558                boot.tv_nsec = xtime.tv_nsec;

     559        }

     560        set_normalized_timespec(&wall_to_monotonic,

     561                                -boot.tv_sec, -boot.tv_nsec);

     562        update_xtime_cache(0);

     563        total_sleep_time.tv_sec = 0;

     564        total_sleep_time.tv_nsec = 0;

     565        write_sequnlock_irqrestore(&xtime_lock, flags);

     566}

     

    这个函数主要是通过读取CMOS上的时钟来初始化全局时间变量xtimeraw_time以及total_sleep_time。具体的就不去分析了,主要讲讲读取CMOS数据的方法,来看函数read_persistent_clock

    void read_persistent_clock(struct timespec *ts)

    {

           unsigned long retval, flags;

     

           spin_lock_irqsave(&rtc_lock, flags);

           retval = x86_platform.get_wallclock();

           spin_unlock_irqrestore(&rtc_lock, flags);

     

           ts->tv_sec = retval;

           ts->tv_nsec = 0;

    }

     

    而全局变量x86_platform在编译时候被初始化如下

    struct x86_platform_ops x86_platform = {

           .calibrate_tsc                 = native_calibrate_tsc,

           .get_wallclock               = mach_get_cmos_time,

           .set_wallclock               = mach_set_rtc_mmss,

           .iommu_shutdown                = iommu_shutdown_noop,

           .is_untracked_pat_range        = is_ISA_range,

           .nmi_init               = default_nmi_init

    };

     

    所以通过的是mach_get_cmos_time函数来读取CMOS中的时间数据。这个函数本质上是通过CMOS_READ宏来进行,比如CMOS_READ(RTC_SECONDS)

     

    Linux只用RTC来获取时间和日期,内核通过0x70Ox71 I/O端口访问RTC。系统管理员通过执行Unix系统时钟程序(直接作用于这两个I/O端口)可以设置时钟。MC146818 RTC芯片(或其他兼容芯片,如DS12887)可以在IRQ8上产生周期性的中断,中断的频率在2HZ8192HZ之间。与MC146818 RTC对应的设备驱动程序实现在include/linux/mc146818rtc.hdrivers/char/mc146818rtc.c文件中,而对应的设备文件是/dev/mc146818rtcmajor=10minor=135,只读字符设备)。因此用户进程可以通过对她进行编程以使得当RTC到达某个特定的时间值时激活IRQ8线,从而将RTC当作一个闹钟来用。

     

    所以,宏RTC_SECONDS来自文件include/linux/mc146818rtc.h

    #define RTC_SECONDS              0

    #define CMOS_READ(addr) rtc_cmos_read(addr)

    #define RTC_PORT(x)   (0x70 + (x))

    unsigned char rtc_cmos_read(unsigned char addr)

    {

           unsigned char val;

     

           lock_cmos_prefix(addr);

           outb(addr, RTC_PORT(0));

           val = inb(RTC_PORT(1));

           lock_cmos_suffix(addr);

     

           return val;

    }

     

    最新回复(0)