软中断初始化

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;

}

 

你可能感兴趣的:(linux,list,unix,struct,action,X86)