spark架构代码分析-时钟机制分析

        时钟在Linux中拥有广泛的应用,比如基于时间片的进程切换,文件的时间戳,定时器等。这些时钟可以分为两种,我们可以以此加以区别:
        1,保存当前的时间和日期,以便能够通过time(),ftime(),和gettimeofday()系统调用把它们返回给用户程序,也可以由内核本身把当前时间作为文件和网络包的事件戳。
        2,维持定时器,这种机制能够告诉内核或用户程序某一实际间隔已经过去了。
        上面的两种时钟机制在Linux内核里面的实现都是及其复杂的,但是这两种时钟机制的原理及实现并不是本文关心的内容,因此阅读本文无法让你了解Linux的时钟机制的原理及实现。本文要讲的内容是Linux上述两种时钟机制的基础,它的主要作用是为上述两种时钟机制提供时钟源,所以它是基于时钟硬件的。
        本文主要包括两方面的内容:
        1,T2平台在硬件上提供了哪些时钟资源以及从硬件层面来看这些硬件时钟的工作机制。
        2,Linux是如何驱动T2平台提供的硬件时钟资源以为Linux的两种时钟机制提供支持的。
        计算机上除了本文所讲的时钟源外还存在一种叫做RTC的时钟硬件,它在系统关闭时仍然维护着机器的时间,当Linux启动后会从RTC里面取得当前时间,然后利用这个时间和本文所讲的时钟源计算系统的当前时间,当关闭系统时会把当前时间写入RTC,本文并不打算讲解RTC的相关内容,因为它取决于所用的RTC芯片,体系结构相关性不是很大。

        T2时钟硬件:
        在T2的每个虚拟处理器上都有两个成对的寄存器:System Tick (STICK) Register 和System Tick Compare (STICK_CMPRP) Register
,这两个寄存器相互协作就可以为系统提供时钟源,它们就是T2提供的时钟硬件资源。
        STICK寄存器可以看作一个CPU的时钟节拍计数器,虚拟器的每个时钟节拍,该寄存器都会自加1。STICK寄存器的各个位域如下示:

 npt	               ounter

63                      62         0

        从图中可以看出STICK寄存器分为两个域,其中63位是npt域,62到0是counter域,各域描述如下:
        npt域:该域控制STICK寄存器在nonprivilged模式下的是否是可访问的,当npt=1,该寄存器在nonprivilged模式下是既不可读也不可写的,当npt=0时,该寄存器在nonprivilged模式下同样是不可写的,但是却是可读的。
        conter域:时钟节拍计数器。

        STICK_CMPR寄存器是与STICK寄存器配对的比较寄存器,当STICK_CMPR寄存器的stick_cmpr域与STICK寄存器counter域匹配时,系统就可以产生trap。STICK_CMPR
寄存器寄存器在nonprivilged模式下是不可访问的。STICK_CMPR寄存器的各个位域如下图所示:

   int_dis	                stick_cmpr
	63                62			   0

        从图中可以看出STICK_CMPR寄存器同样也分为两个域,其中位63是int_dis域,位62到0是 stick_cmpr域。各域描述如下:
        int_dis域:中断允许位,当int_dis=1时,即使STICK_CMPR寄存器的stick_cmpr域与STICK寄存器counter域匹配,相应的trap也不会产生。
        stick_cmpr域:比较域,如果该域与STICK寄存器counter域匹配,且int_dis=0 ,SOFTINT.sm=1其系统中其他中断控制位允许时,则会在相应的虚拟处理器上产生 interrupt_level_14 trap。
        这两个寄存器相互协作,则会在相应的虚拟处理器上产生时钟中断,当然时钟中断的间隔是可编程的。这两个寄存器可以看作一个时钟源。

        Linux T2 时钟软件:
        对于Linux中的维护系统时间和日期的时钟机制(即上文的第一种时钟机制),Linux为了统一不同的时钟硬件,内核把每一个可以用来计时的时钟抽象为clocksource结构。因此驱动T2时钟硬件支持该时钟机制的软件最主要的工作就是实现clocksource对象并注册进系统。相应的,对于实现系统中定时器的时钟机制(上文中的第二种时钟机制),Linux把时钟源抽象为clock_event_device结构,因此驱动T2时钟硬件支持该时钟机制的软件最主要的工作就是实现clock_event_device对象并注册进系统。
        Clocksource结构定义如下:

struct clocksource {
         char *name;
         struct list_head list;
         int rating;
         cycle_t (*read)(struct clocksource *cs);
         int (*enable)(struct clocksource *cs);
         void (*disable)(struct clocksource *cs);
         cycle_t mask;
         u32 mult;
         u32 shift;
         u64 max_idle_ns;
         unsigned long flags;
         cycle_t (*vread)(void);
         void (*resume)(void);
 #ifdef CONFIG_IA64
        void *fsys_mmio;        /* used by fsyscall asm code */
 #define CLKSRC_FSYS_MMIO_SET(mmio, addr)      ((mmio) = (addr))
 #else
 #define CLKSRC_FSYS_MMIO_SET(mmio, addr)      do { } while (0)
 #endif
        cycle_t cycle_last ____cacheline_aligned_in_smp;
#ifdef CONFIG_CLOCKSOURCE_WATCHDOG
         /* Watchdog related data, used by the framework */
        struct list_head wd_list;
         cycle_t wd_last;
#endif
};

        该结构各成员的含义解释如下:
        name:该clocksource对象的名称
        rating:优先级
        read:函数指针,该函数的作用是读取时钟源所记录的时钟节拍数
        enable:使能该clocksource对象
        disable:禁止该clocksource对象
        mask:位掩码
        mult:乘数系数
        shift:移位系数
         利用mult和shift可在时钟节拍数和这些时钟节拍经过多少纳秒之间进行转换。

        clock_event_device结构定义如下:

struct clock_event_device {
        const char              *name;
        unsigned int            features;
        unsigned long           max_delta_ns;
        unsigned long           min_delta_ns;
        unsigned long           mult;
        int                     shift;
        int                     rating;
        int                     irq;
        const struct cpumask    *cpumask;
        int                     (*set_next_event)(unsigned long evt,
                                                  struct clock_event_device *);
        void                    (*set_mode)(enum clock_event_mode mode,
                                            struct clock_event_device *);
        void                    (*event_handler)(struct clock_event_device *);
        void                    (*broadcast)(const struct cpumask *mask);
        struct list_head        list;
        enum clock_event_mode   mode;
        ktime_t                 next_event;
};

        该结构各成员解释如下:
        name:名称
        features:表示该对象功能,CLOCK_EVT_FEAT_PERIODIC表示周期性中断,CLOCK_EVT_FEAT_ONESHOT表示单次中断
        max_delta_ns:表示最大中断周期,单位是纳秒
        min_delta_ns:表示最小中断周期,单位是纳秒
        mult,shift成员的含义跟clocksource对象一样
        rate:跟clocksource对象一样表示优先级
        irq:中断号
        cpumask:cpu掩码,表示该对象在哪个cpu上工作
        set_next_event:该函数作用是设置下一次时钟中断的到期时间
        set_mode:设置模式,可以是周期中断模式和单中断模式
        event_hander:时钟中断到达时执行的中断处理函数
        next_event:表示下一次该设备发出的中断时间,单位是纳秒

        从这两个结构的定义可以看出,clocksource对象是不需要时钟中断的。看完这两个结构以后,我们看看Linux是怎么基于T2地时钟硬件实现这两个对象的。该实现位于arch/sparc/kernel/time_64.c文件中。
        为了操作时钟源硬件,我们定义了一个结构struct sparc64_tick_ops,它里面保存了操作时钟硬件的函数,我们看该结构的定义:
 struct sparc64_tick_ops {
         unsigned long long (*get_tick)(void);
         int (*add_compare)(unsigned long);
         unsigned long softint_mask;
         void (*disable_irq)(void);
         void (*init_tick)(void);
         unsigned long (*add_tick)(unsigned long);
         char *name;
 };

        对于我们tlb_type==hypervisor而言,该结构即为stick_operations(后面会看到),stick_operations是 struct sparc64_tick_ops类型变量,它它定义如下:
 static struct sparc64_tick_ops stick_operations __read_mostly = {
         .name           =       "stick",
         .init_tick      =       stick_init_tick,
         .disable_irq    =       stick_disable_irq,
         .get_tick       =       stick_get_tick,
         .add_tick       =       stick_add_tick,
         .add_compare    =       stick_add_compare,
         .softint_mask   =       1UL << 16,
 };

        我们来分析stick_operations的各成员。
         stick_operations名称为”stick”。
        init_tick成员是函数stick_init_tick函数,它是用来初始化时钟硬件的,我们看这个函数:

188 static void stick_init_tick(void)
189 {
190         /* Writes to the %tick and %stick register are not
191          * allowed on sun4v.  The Hypervisor controls that
192          * bit, per-strand.
193          */
194         if (tlb_type != hypervisor) {
195                 tick_disable_protection();
196                 tick_disable_irq();
197 
198                 /* Let the user get at STICK too. */
199                 __asm__ __volatile__(
200                 "       rd      %%asr24, %%g2\n"
201                 "       andn    %%g2, %0, %%g2\n"
202                 "       wr      %%g2, 0, %%asr24"
203                 : /* no outputs */
204                 : "r" (TICK_PRIV_BIT)
205                 : "g1", "g2");
206         }
207 
208         stick_disable_irq();
209 }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第194到206行显然不是我们需要关心的。

         第208行, stick_disable_irq函数禁止当STICK_CMPR寄存器的stick_cmpr域与STICK寄存器counter域匹配时所发生的trap,从前面的时钟硬件的描述来看,它是把STICK_CMPR寄存器的int_dis域设为1,我们看看这个函数:

180 static void stick_disable_irq(void)
181 {
182         __asm__ __volatile__(
183         "wr     %0, 0x0, %%asr25"
184         : /* no outputs */
185         : "r" (TICKCMP_IRQ_BIT));
186 }

        这是段内嵌汇编,它是使用wr指令把 TICKCMP_IRQ_BIT写入asr25寄存器中,asr25寄存器即是STICK_CMPR寄存器。TICKCMP_IRQ_BIT是1<<63。这样STICK_CMPR寄存器的位63就置为1了。
        stick_operations的disable_irq成员初始化为函数stick_disable_irq,stick_disable_irq函数我们上面刚刚讲过
        stick_operations的get_tick成员初始化为函数stick_get_tick,该函数的作用是读取STICK寄存器counter域得到所记录的节拍数,这个函数也使用了内嵌汇编,不过很简单,读者可自行分析。
        stick_operations的add_tick成员初始化为函数stick_add_tick,该函数的作用是跟新STICK寄存器的counter域,把counter域增加某个指定的值,我们看这个函数:

221 static unsigned long stick_add_tick(unsigned long adj)
222 {
223         unsigned long new_tick;
224 
225         __asm__ __volatile__("rd        %%asr24, %0\n\t"
226                              "add       %0, %1, %0\n\t"
227                              "wr        %0, 0, %%asr24\n\t"
228                              : "=&r" (new_tick)
229                             : "r" (adj));
230 
231         return new_tick;
232 }

        这个函数也是一段内嵌汇编
         第225行,读取STICK寄存器的值放在new_tick中
        第226行,new_tick的值加上adj的值放在new_tick中
        第227行,把新的new_tick写会STICK寄存器

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stick_operations的add_compare成员初始化为函数stick_add_compare,该函数的作用把STICK_CMPR寄存器的stick_cmpr域设置为STICK寄存器的counter域加上n个节拍数,同时允许匹配成功时的trap发生,这样做是为经过n个时钟节拍后产生时钟中断,我们看该函数的实现:
234 static int stick_add_compare(unsigned long adj)
235 {
236         unsigned long orig_tick, new_tick;
237 
238         __asm__ __volatile__("rd        %%asr24, %0"
239                              : "=r" (orig_tick));
240         orig_tick &= ~TICKCMP_IRQ_BIT;
241 
242         __asm__ __volatile__("wr        %0, 0, %%asr25"
243                              : /* no outputs */
244                              : "r" (orig_tick + adj));
245 
246         __asm__ __volatile__("rd        %%asr24, %0"
247                              : "=r" (new_tick));
248         new_tick &= ~TICKCMP_IRQ_BIT;
249 
250         return ((long)(new_tick - (orig_tick+adj))) > 0L;
251 }

        第238行,读取STICK寄存器的值放到 orig_tick变量中。
         第240行,清0 orig_tick变量的位63。
         第242行,把orig_tick加上adj后写入STICK_CMPR寄存器,因为 orig_tick变量的位63已经被清0,所以当匹配时会发生trap
        第246到250行,判断操作是否成功,即是否会在未来的某个时候产生时钟中断,判断的方法是再次读取STICK寄存器的counter域,判断其是否超过orig_tick+adj,如果超过则操作失败。

         stick_operations的softint_mask是时钟中断在SOFTINT寄存器中的状态位,如果发生时钟中断,则该位会置位。

         看完stick_operations 后,我们来看Linux T2的时钟硬件初始化函数,很显然,该函数会使用stick_operations实现clocksource对象和clock_event_device对象。初始化函数是time_init函数,会在start_kernel函数执行,我们看看这个函数:

830 void __init time_init(void)
831 {
832         unsigned long freq = sparc64_init_timers();
833 
834         tb_ticks_per_usec = freq / USEC_PER_SEC;
835 
836         timer_ticks_per_nsec_quotient =
837                 clocksource_hz2mult(freq, SPARC64_NSEC_PER_CYC_SHIFT);
838 
839         clocksource_tick.name = tick_ops->name;
840         clocksource_tick.mult =
841                 clocksource_hz2mult(freq,
842                                     clocksource_tick.shift);
843         clocksource_tick.read = clocksource_tick_read;
844 
845         printk("clocksource: mult[%x] shift[%d]\n",
846                clocksource_tick.mult, clocksource_tick.shift);
847 
848         clocksource_register(&clocksource_tick);
849 
850         sparc64_clockevent.name = tick_ops->name;
851 
852         setup_clockevent_multiplier(freq);
853 
854         sparc64_clockevent.max_delta_ns =
855                 
856         sparc64_clockevent.min_delta_ns =
857                 clockevent_delta2ns(0xF, &sparc64_clockevent);
858 
859         printk("clockevent: mult[%lx] shift[%d]\n",
860                sparc64_clockevent.mult, sparc64_clockevent.shift);
861 
862         setup_sparc64_timer();
863 }

        第832行,sparc64_init_timers函数的作用得到时钟硬件的时钟频率,我们这里其实就是CPU的频率,我们看看这个函数:

607 /* This is gets the master TICK_INT timer going. */

608 static unsigned long sparc64_init_timers(void)

609 {
610         struct device_node *dp;
611         unsigned long freq;
612 
613         dp = of_find_node_by_path("/");
614         if (tlb_type == spitfire) {
615                 unsigned long ver, manuf, impl;
616 
617                 __asm__ __volatile__ ("rdpr %%ver, %0"
618                                       : "=&r" (ver));
619                 manuf = ((ver >> 48) & 0xffff);
620                 impl = ((ver >> 32) & 0xffff);
621                 if (manuf == 0x17 && impl == 0x13) {
622                         /* Hummingbird, aka Ultra-IIe */
623                         tick_ops = &hbtick_operations;
624                         freq = of_getintprop_default(dp, "stick-frequency", 0);
625                 } else {
626                         tick_ops = &tick_operations;
627                         freq = local_cpu_data().clock_tick;
628                 }
629         } else {
630                 tick_ops = &stick_operations;
631                 freq = of_getintprop_default(dp, "stick-frequency", 0);
632         }
633 
634         return freq;
635 }

        第613行,得到设备节点树跟节点的节点号
         第614到628行,这段分支语句我们不用关心,我们tlb_type == hypervisor
         第630行,初始化tick_ops变量,该全局变量就是前面提到的 struct sparc64_tick_ops结构类型的变量,这里把它初始化为stick_operations。
         第631行,得到跟节点的stick-frequency属性,该属性中即存有CPU频率
        可以看出 sparc64_init_timers函数就是取得设备节点树根节点的stick-frequency属性,从而得到时钟硬件的频率的,
        我们回到time_init函数中来,
        第834行,tb_ticks_per_usec变量里面存放的是每微妙的时钟节拍数。
        第836行,clocksource_hz2mult函数是Linux提供的标准函数,这个函数就不分析了,它的作用就是根据指定的shift得到与之对应的mult从而得到一个mult/shift对,mult/shift对在前面说过,通过他们可以在时钟节拍数和纳秒时间之间进行转换,转换的公式如下:
         ns = (ticks*mult)>>shift
        这里的mult保存在timer_ticks_per_nsec_quotient变量中,shift就是宏SPARC64_NSEC_PER_CYC_SHIFT。
        第839行,初始化clocksource_tick的name成员,clocksource_tick就是我们        这里实现的clocksource对象,它的定义如下:

 static struct clocksource clocksource_tick = {
         .rating         = 100,
         .mask           = CLOCKSOURCE_MASK(64),
         .shift          = 16,
         .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
};

        它的name成员初始化为tick_ops结构的name成员,tick_ops即是前面分析过的stick_operations。
        第840行,利用clocksource_hz2mult通过clocksource对象的shift成员计算出mult成员。
        843行,初始化clocksource对象的read成员为clocksource_tick_read函数,该函数实际上就是调用stick_operations的read成员。
        第848行,初始化好clocksource对象后这里把它注册进系统,这样Linux的维护系统时间的时钟机制就会利用该对象维护系统时间了。

        第850行,从这里开始就是初始化clock_event_device对象了。这里的sparc64_clockevent变量就是clock_event_device类型的变量,这条语句把该clock_event对象的name设置为stick_operations的name成员
        sparc64_clockevent成员定义如下:

 static struct clock_event_device sparc64_clockevent = {
         .features       = CLOCK_EVT_FEAT_ONESHOT,
         .set_mode       = sparc64_timer_setup,
         .set_next_event = sparc64_next_event,
         .rating         = 100,
         .shift          = 30,
         .irq            = -1,
};

        上面的定义初始化了features成员为 CLOCK_EVT_FEAT_ONESHOT,即单次中断模式。
        set_mode成员初始化为函数 sparc64_timer_setup,我们看该函数实现:

700 static void sparc64_timer_setup(enum clock_event_mode mode,
701                                 struct clock_event_device *evt)
702 {
703         switch (mode) {
704         case CLOCK_EVT_MODE_ONESHOT:
705         case CLOCK_EVT_MODE_RESUME:
706                 break;
707 
708         case CLOCK_EVT_MODE_SHUTDOWN:
709                 tick_ops->disable_irq();
710                 break;
711 
712         case CLOCK_EVT_MODE_PERIODIC:
713         case CLOCK_EVT_MODE_UNUSED:
714                 WARN_ON(1);
715                 break;
716         };
717 }

        从这个函数可以看出我们的clock_event_device对象只能设置程单中断模式和 CLOCK_EVT_MODE_SHUTDOWN模式,即禁止时钟,实际上就是禁止时钟中断。
        set_next_event成员初始化为函数 sparc64_next_event,这个函数就不分析了,它实际上就是调用stick_operations的add_compare成员。
        回到time_init函数中来
        第852行,setup_clockevent_multiplier函数就不分析了,它实际上是根据shift成员初始化clock_event_device的mult成员。
        第854行和856行,clockevent_delta2ns也是Linux的标准函数,就不分析了,这两行初始化clock_event_device的max_delta_ns成员和min_delta_ns
        第862行,调用函数setup_sparc64_timer,我们来看这个函数:

753 void __devinit setup_sparc64_timer(void)

754 {
755         struct clock_event_device *sevt;
756         unsigned long pstate;
757 
758         /* Guarantee that the following sequences execute
759          * uninterrupted.
760          */
761         __asm__ __volatile__("rdpr      %%pstate, %0\n\t"
762                              "wrpr      %0, %1, %%pstate"
763                              : "=r" (pstate)
764                              : "i" (PSTATE_IE));
765 
766         tick_ops->init_tick();
767 
768         /* Restore PSTATE_IE. */
769         __asm__ __volatile__("wrpr      %0, 0x0, %%pstate"
770                              : /* no outputs */
771                              : "r" (pstate));
772 
773         sevt = &__get_cpu_var(sparc64_events);
774 
775         memcpy(sevt, &sparc64_clockevent, sizeof(*sevt));
776         sevt->cpumask = cpumask_of(smp_processor_id());
777 
778         clockevents_register_device(sevt);
779 }

        解释这个函数之前,先讲解一点背景知识,在T2 里面存在着多个虚拟处理器,相应的每个虚拟处理器上都有硬件时钟源,这样在硬件上系统中就存在着多个时钟源。虽然如此,系统中却只存在一个clocksource对象,因为clocksource对象是用来维护系统时间的,而系统时间只能有一个,从时钟硬件上来看,clocksource对象操作的是STICK寄存器的counter域,而该域是用来保存CPU节拍的,因此在所有的虚拟处理器上,该域的值应该是相等的。stick_operations虽然提供了add_tick函数用来跟新STICK寄存器,但该函数主要使用来保持所有的虚拟处理器上STICK寄存器的counter域的同步的。因此系统中所有的时钟硬件只对应一个clocksource对象。与clocksouce对象不同,clock_event_device对象是使用时钟中断来维持系统中的定时器的,而每个虚拟处理器上都有各自不同的定时器实体,因此每个虚拟处理器上都有一个单独的clock_envent_device对象,系统中的每个时钟硬件都对应一个clock_evnet_device对象,这些clock_evnet_device对象可以看作是 sparc64_clockevent的一个副本,因此系统中真正起作用的clock_evnet_device对象是这些与每个虚拟处理器相关的副本,而不是sparc64_clockevent。这些副本定义为一个per_cpu变量,定义如下:
        static DEFINE_PER_CPU(struct clock_event_device, sparc64_events);
        现在来正式分析这个函数。
        第761到764行,禁止系统中的中断。
        第766行,调用stick_operations的init_tick成员,该成员前面分析过,它实际上就是禁止时钟中断,实际上这里是禁止了当前虚拟处理器的时钟中断
        第769行,允许系统中的中断
        第773行,取得当前虚拟处理器的clock_event_device对象副本
        第775行,把之前初始化过的sparc64_clockevent复制为当前虚拟处理器的clock_event_device对象,这样当前cpu的clock_event_device就被初始化过了。
        第776行设置clock_event_device对象的cpumask成员
        第778行,把初始化号的clock_event_device对象注册如系统中,之后linux的定时器时钟机制就会使用该对象产生时钟中断了。

        这样T2的时钟硬件的初始化就完成了,在linux运行过程中,除了会使用初始化过的clocksource对象和clock_event_device对象中提供的函数驱动时钟硬件。在这个过程中会产生时钟中断,我们来看一看时钟中断是怎么执行的。
        时钟中断实际上是interrupt_level_14 trap,在Linux的trap table中,interrupt_level_14 trap hander是:
        TRAP_IRQ(timer_interrupt, 14)
        这里TRAP_IRQ宏会调用函数timer_interrupt,这个函数是在time_64.c定义的,我们来看这个函数:

729 void timer_interrupt(int irq, struct pt_regs *regs)
730 {
731         struct pt_regs *old_regs = set_irq_regs(regs);
732         unsigned long tick_mask = tick_ops->softint_mask;
733         int cpu = smp_processor_id();
734         struct clock_event_device *evt = &per_cpu(sparc64_events, cpu);
735 
736         clear_softint(tick_mask);
737 
738         irq_enter();
739 
740         kstat_incr_irqs_this_cpu(0, irq_to_desc(0));
741 
742         if (unlikely(!evt->event_handler)) {
743                 printk(KERN_WARNING
744                        "Spurious SPARC64 timer interrupt on cpu %d\n", cpu);
745         } else
746                 evt->event_handler(evt);
747 
748         irq_exit();
749
750         set_irq_regs(old_regs);
751 }

        第736行,清除时钟中断状态位以接收下一次时钟中断。
        第738行,进入中断执行环境。
        第740行,在Linux T2的中断机制的讲解中我们知道中断请求号0默认分配给了时钟中断,因此利用中断请求号0的中断描述符中的中断状态域更新中断次数,中断描述符中的中断状态域为每个虚拟处理器都准备了一个子域,它们实际上是个计数器,记录了相应虚拟处理器上该中断发生的次数, kstat_incr_irqs_this_cpu函数是Linux标准函数,就不分析了。
        第746行,执行clock_event_device对象中保存的时钟中断处理函数,在我们之前clock_event_device对象的初始化时,实际上并没有初始化clock_event_device的event_handler成员,实际上该成员的初始化是在Linux体系无关的代码中初始化的,当Linux初始化标准的Linux时钟机制时会为该成员赋值,赋的值也是Linux标准的时钟中断处理函数,由于我们不打算涉及体系结构无关代码,就不分析了。
        第748行到750行,推出中断执行环境。

        Linux有些与时钟节拍相关的操作并没有使用clocksource对象或者clock_event_device对象,它们是直接提供一些标准的API,体系结构相关代码为了支持它们所要做的工作就是实现这些标准的API。在T2平台上,实际上就是调用stick_operations的成员函数来实现这些API的,这些API包括:

void __delay(unsigned long loops);
void udelay(unsigned long usecs);
unsigned long long sched_clock(void)
int __devinit read_current_timer(unsigned long *timer_val)

        我们只分析其中的__delay和udelay延时函数,其他的读者可自行分析。

808 void __delay(unsigned long loops)
809 {
810         unsigned long bclock, now;
811 
812         bclock = tick_ops->get_tick();
813         do {
814                 now = tick_ops->get_tick();
815         } while ((now-bclock) < loops);
816 }
817 EXPORT_SYMBOL(__delay);

        __delay函数的功能是演示loops个时钟节拍,这个函数很简单,它就是一个while循环,先通过 tick_ops->get_tick读出时钟节拍的初始值,然后在while循环中等待,直到HSTICK寄存器中保存的时钟节拍计数器更新过loops,这时 tick_ops->get_tick读出的新的时钟节拍数与初始值的差应该会大于loops

819 void udelay(unsigned long usecs)
820 {
821         __delay(tb_ticks_per_usec * usecs);
822 }
823 EXPORT_SYMBOL(udelay);

        udelay函数就是__delay函数延时一定的时钟节拍数,这个时钟
节拍数是通过要延时的微妙数计算出来的。 tb_ticks_per_usec变量前面的分析中已经初始化过,它保存的是每微妙有多少个时钟节拍。

        这样Linux T2 时钟机制就分析完了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 使用spark-submit命令提交Spark应用程序到YARN集群模式,可以按照以下步骤进行操作: 1. 确保已经安装了Spark和YARN,并且配置了正确的环境变量。 2. 编写Spark应用程序代码,并将其打包成jar包。 3. 打开终端,输入以下命令: ``` spark-submit --class <main-class> --master yarn --deploy-mode cluster <jar-file> <args> ``` 其中,`<main-class>`是Spark应用程序的主类名,`<jar-file>`是打包好的jar包路径,`<args>`是传递给应用程序的参数。 4. 提交命令后,Spark会将应用程序提交到YARN集群中,并在集群中启动应用程序的Driver程序。 5. 可以通过YARN的Web界面或命令行工具来监控应用程序的运行状态和日志输出。 注意事项: - 在提交应用程序时,需要指定`--master yarn`和`--deploy-mode cluster`参数,以告诉Spark将应用程序提交到YARN集群中运行。 - 如果应用程序需要访问HDFS或其他外部资源,需要在应用程序中指定相应的路径或URL,并确保YARN集群中的节点也能够访问这些资源。 - 在提交应用程序时,可以通过`--num-executors`、`--executor-memory`、`--executor-cores`等参数来指定应用程序在集群中的资源分配情况。 ### 回答2: Spark是一个快速、通用、可扩展的大数据处理引擎,能够处理包括离线批处理、实时流处理、图形处理等多种数据处理场景。其中,Spark中常见的数据处理方式是通过RDD(弹性分布式数据集)来进行计算处理。 对于Spark应用的部署,有两种方式:一种是通过Spark Standalone模式,将Spark应用作为单一进程的方式运行;另一种则是通过YARN模式,将Spark应用分布到一组计算节点中去运行。在这里,我们介绍一种常见的部署方式:通过spark-submit命令将应用提交到YARN集群上运行。 spark-submit命令是Spark提供的专门用于提交应用的命令,根据不同的运行模式,有不同的参数指定方式。其中,将应用部署到YARN模式的一般命令如下: ``` ./bin/spark-submit \ --class [app-class] \ --master yarn \ --deploy-mode cluster \ [--executor-memory <memory>] \ [--num-executors <num>] \ [path-to-app-jar] [app-arguments] ``` 其中,各参数含义如下: 1. --class:指定应用的入口类。 2. --master:指定Spark应用运行在YARN模式下。 3. --deploy-mode:指定应用在YARN模式下的部署方式,有两种模式:client和cluster。其中,client模式是指在本地运行应用,而cluster模式则是将应用提交到YARN集群上运行。 4. --executor-memory:指定每个executor占用的内存大小。 5. --num-executors:指定在YARN集群上运行的executor数目。 6. [path-to-app-jar]:指定应用程序的jar包路径。 7. [app-arguments]:应用的命令行参数。 需要注意的是,将应用提交到YARN集群上运行时,需要提前从HDFS中将数据加载到内存中,否则可能会降低应用的性能。在代码中,可以通过使用SparkContext的textFile等方法,将HDFS中的数据读取到RDD中进行处理。 总之,通过spark-submit命令将Spark应用部署到YARN集群上运行,可以充分利用集群资源,实现高效处理大规模数据。而在代码编写方面,需要注意处理好HDFS中数据的读取和分布式操作等问题。 ### 回答3: Spark是一种开源的大数据处理框架,其可以通过Spark-submit进行提交。Spark-submit是一个命令行工具,可用于将Spark应用程序提交到集群中运行。它支持多种模式,包括local模式、standalone模式和yarn-cluster模式等。其中,在yarn-cluster模式中,Spark应用程序将在YARN集群上运行。 在使用Spark-submit提交Spark应用程序到YARN集群的时候,需要考虑以下几个方面: 1. 配置参数 首先,需要指定Spark-submit的参数,例如--class参数用于指定要运行的主类。在YARN集群上运行Spark应用程序需要使用--master参数,并将其设置为yarn-cluster。同时,可以添加其它的参数,例如--num-executors用于设置执行程序的数量,--executor-memory用于设置每个执行程序的内存。 示例: spark-submit --class com.test.TestApp \ --master yarn-cluster \ --num-executors 5 \ --executor-memory 4G \ /path/to/your/application.jar 2. 配置环境 为了让Spark应用程序在YARN集群上运行,需要为其配置适当的环境。需要确保所有必要的依赖项都已安装,并将其添加到Spark-submit命令中。在集群节点上,需要确保Spark和Hadoop已正确配置并运行。 3. 访问资源 将应用程序提交到YARN集群后,需要确保它能够访问必要的资源。这些资源包括存储在HDFS中的数据和应用程序所需的库和文件。如果应用程序需要访问外部资源,则还需要配置适当的访问权限。 总的来说,Spark-submit提交yarn-cluster模式是一种将Spark应用程序提交到YARN集群上运行的方法。在提交之前,需要考虑配置参数、配置环境和访问资源,以确保应用程序能够正确运行并访问所需的资源和库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值