免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4063 | 回复: 0
打印 上一主题 下一主题

[FreeBSD] freebsd9.2-APIC TIMER时钟中断的处理 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2014-06-24 21:06 |只看该作者 |倒序浏览
本帖最后由 71v5 于 2014-07-02 01:03 编辑

为什么要选取APIC TIMER来描述,因为APIC TIMER包含在intel cpu中,intel芯片手册中对其有详尽的描述,并且与芯片的其它部分联系比较紧密,这样的话,就会对其有一个整体的把握。
不然的话,就需要看看8254,ACPI-HPET等相关的知识,估计会影响学习兴趣。总之就是用最简单且适用的方法说明一些复杂的问题。


关于如何对APIC TIMER编程,大家可以参考一下"Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 3A: System Programming Guide, Part 1"
中的第十章,从中摘取了一些对APIC TIMER的部分描述,这样就可以大概知道APIC TIMER中断的处理。

The local APIC unit contains a 32-bit programmable timer that is available to software
to time events or operations. This timer is set up by programming four registers:
the divide configuration register , the Initial-Count register,current-count register,
and the LVT timer register.

The time base for the timer is derived from the processor’s bus clock, divided by the
value specified in the divide configuration register。

The timer can be configured through the timer LVT entry for one-shot or periodic
operation. In one-shot mode, the timer is started by programming its initial-count
register. The initial count value is then copied into the current-count register and
count-down begins. After the timer reaches zero, an timer interrupt is generated and
the timer remains at its 0 value until reprogrammed.

In periodic mode, the current-count register is automatically reloaded from the
initial-count register when the count reaches 0 and a timer interrupt is generated,
and the count-down is repeated. If during the count-down process the initial-count
register is set, counting will restart, using the new initial-count value. The initialcount
register is a read-write register; the current-count register is read only.

A write of 0 to the initial-count register effectively stops the local APIC timer, in both
one-shot and periodic mode.

这里做以下假设:
APIC TIMER为periodic mode。
APIC TIMER来跟踪hardclock,statclock,profclock。
描述APIC TIMER的struct eventtimer对象设置了ET_FLAGS_PERCPU标志。

如果没有设置ET_FLAGS_PERCPU标志,在SMP系统中,只有BSP激活了APIC TIMER,而AP上的时钟处理由
BSP向AP发送一个处理期间中断来实现。

和freebsd8.3相比,时钟这块的改动个人觉的还是挺大的:

[freebsd8.3时钟频率]:

APIC TIMER频率为2*hz,即每秒2000次,周期为0.5毫秒,每秒发出2000次中断,每次中断发生时,
相应的中断服务例程都要检查是否运行下面三个时钟:
hardclock:频率为hz,每秒1000次,当发生两次APIC TIMER时钟中断时,才运行一次hardclock。
statclock:频率为128,周期为1/128秒,即1/128秒运行一次statclock。
profclock:频率为2*hz,即每秒2000次,周期为0.5毫秒,每秒运行2000次。

[freebsd9.2时钟频率如下所示,这里假设和freebsd8.3保持一致]:

hardclock:频率为hz,每秒1000次,当发生两次APIC TIMER时钟中断时,才运行一次hardclock。
statclock:频率为128,周期为1/128秒,即1/128秒运行一次statclock。
profclock:频率为2*hz,即每秒2000次,周期为0.5毫秒,每秒运行2000次。
  1. /**********************************************************************
  2. * timerperiod:当APIC-TIMER处于periodic mode时,APIC-TIMER的周期。
  3.    hardperiod:hardclock的周期。
  4.    statperiod:statclock的周期。
  5.    profperiod:profclock的周期。

  6.    这里不用搞清楚这些时钟的频率和周期到底是多少,只要知道这些数据对象
  7.    代表什么就行了,这里就假设跟freebsd8.3中的值保持一致,这样不影响
  8.    下面的分析。
  9. ***************************************/  
  10.     97        static struct bintime        timerperiod;        /* Timer period for periodic mode. */
  11.     98  static struct bintime        hardperiod;        /* hardclock() events period. */
  12.     99        static struct bintime        statperiod;        /* statclock() events period. */
  13.    100        static struct bintime        profperiod;        /* profclock() events period. */

  14. /***********************************************************************
  15. * 只有当描述event timer的struct eventtimer对象没有设置ET_FLAGS_PERCPU
  16.    标志时,才使用下面两个变量。
  17. *****************************************/
  18.    101        static struct bintime        nexttick;        /* Next global timer tick time. */
  19.    102        static struct bintime        nexthard;        /* Next global hardlock() event. */
复制代码
下面简单分析一下当APIC TIMER中断发生时以及statclock被触发后所做的处理,因为statclock检查thread是否用完其时间片,需要的时候被重新调度,
statclock触发后将调用statclock_cnt函数。

当APIC TIMER中断发生时,内核会调用其相应的中断服务历程,该汇编例程会进一步调用高级C函数lapic_handle_timer,下面大概分析一下lapic_handle_timer函数的
处理过程:
  1.    783        void
  2.    784        lapic_handle_timer(struct trapframe *frame)
  3.    785        {      
  4. /**************************************************************
  5. * 786:
  6.    la指向描述local APIC的struct lapic对象。
  7.    freebsd定义了一个类型为struct lapic的数组lapics
  8.    来描述local APIC。
  9.    数组大小为MAX_APIC_ID + 1,数组lapics的索引为当前
  10.    local apic id。

  11.    787:可以通过oldframe很方便的访问保存在线程内核栈
  12.         上的硬件上下文。

  13.    788:指向中断发生时正在运行线程对应的struct thread对象,
  14.         即curthread。
  15. **************************************/                 
  16.    786                struct lapic *la;
  17.    787                struct trapframe *oldframe;
  18.    788                struct thread *td;
  19.    789       
  20.    790               
  21. /*****************************************************************
  22. * Send EOI first thing.
  23.    根据intel芯片手册上的描述,此时要向EOI Register发出一个写
  24.    操作,对这个写操作,cpu做出下面的回应:

  25.    "Upon receiving an EOI, the APIC clears the highest priority
  26.     bit in the ISR and dispatches the next highest priority
  27.     interrupt to the processor。"

  28. ****************************/
  29.    791                lapic_eoi();
  30.    792       
  31.    793        #if defined(SMP) && !defined(SCHED_ULE)
  32.    794                /*********************************************************************
  33.    795                 * Don't do any accounting for the disabled HTT cores, since it
  34.    796                 * will provide misleading numbers for the userland.
  35.    797                 *
  36.    798                 * No locking is necessary here, since even if we loose the race
  37.    799                 * when hlt_cpus_mask changes it is not a big deal, really.
  38.    800                 *
  39.    801                 * Don't do that for ULE, since ULE doesn't consider hlt_cpus_mask
  40.    802                 * and unlike other schedulers it actually schedules threads to
  41.    803                 * those CPUs.
  42.                    支持SMP但是使用的不是ULE调度程序时,此时检查当前cpu是否被hlt了,
  43.                    如果是,就直接返回。
  44.    804                 */
  45.    805                if (CPU_ISSET(PCPU_GET(cpuid), &hlt_cpus_mask))
  46.    806                        return;
  47.    807        #endif
  48.    808       
  49.    809               
  50. /**********************************************************
  51. * Look up our local APIC structure for the tick counters.
  52.    810:la指向描述当前cpu的local APIC的struct lapic对象。
  53.    811:递增la_timer_count成员,用来标识APIC TIMER发生中断
  54.         的次数。
  55. *****************************/
  56.    810                la = &lapics[PCPU_GET(apic_id)];
  57.    811                (*la->la_timer_count)++;
  58.    812                critical_enter();
  59. /*********************************
  60. * 这里只关心818行的函数调用。
  61. * 818:这里将调用timercb函数。
  62. ************************/
  63.    813                if (lapic_et.et_active) {
  64.    814                        td = curthread;
  65.    815                        td->td_intr_nesting_level++;
  66.    816                        oldframe = td->td_intr_frame;
  67.    817                        td->td_intr_frame = frame;
  68.    818                        lapic_et.et_event_cb(&lapic_et, lapic_et.et_arg);
  69.    819                        td->td_intr_frame = oldframe;
  70.    820                        td->td_intr_nesting_level--;
  71.    821                }
  72.    822                critical_exit();
  73.    823        }
复制代码
[函数timercb]:
  1. /***************************************************************
  2. * Hardware timer callback function.
  3.    参数描述:
  4.    et:指向lapic_et,即&lapic_et。
  5.    arg:NULL。
  6. *****************************/
  7.    338        static void
  8.    339        timercb(struct eventtimer *et, void *arg)
  9.    340        {
  10.    341                struct bintime now;
  11.    342                struct bintime *next;
  12.    343                struct pcpu_state *state;
  13.    344        #ifdef SMP
  14.    345                int cpu, bcast;
  15.    346        #endif
  16.    347       
  17.    348               
  18. /************************************************************************
  19. * Do not touch anything if somebody reconfiguring timers.
  20.    static u_int                busy = 0;    Reconfiguration is in progress.
  21.                   
  22.    如果要重新配置timers,此时就要以参数0调用configtimer函数,之后紧接着
  23.    以参数1调用configtimer函数。在以参数0调用configtimer函数的过程中,
  24.    会将busy变量设置为1。
  25. ****************************/
  26.    349                if (busy)
  27.    350                        return;
  28.    351               
  29. /*********************************************************************************
  30. * Update present and next tick times.
  31.    这里的state指向的数据类型为struct pcpu_state,这里的struct pcpu_state
  32.    类似于linux中的每cpu变量,系统中每个cpu都有其对应的struct pcpu_state
  33.    对象,用来描述该cpu上是否有相应的调度事件,hardlock event,statclock event等等。
  34.    
  35.    timerperiod:当APIC-TIMER处于periodic mode时,APIC-TIMER的周期。
  36.                
  37.    353-363:执行完后,struct pcpu_state对象的nexttick成员已经被更新,保存了下一次
  38.             APIC TIMER中断发生时的时间戳。
  39. *******************************************/
  40.    352                state = DPCPU_PTR(timerstate);
  41.    353                if (et->et_flags & ET_FLAGS_PERCPU) {
  42.    354                        next = &state->nexttick;
  43.    355                } else
  44.    356                        next = &nexttick;
  45. /*********************************************
  46. * 357-360:APIC TIMER为periodic mode。
  47. *************************/  
  48.    357                if (periodic) {
  49.    358                        now = *next;        /* Ex-next tick time becomes present time. */
  50.    359                        bintime_add(next, &timerperiod); /* Next tick in 1 period. */
  51.    360                } else {
  52.    361                        binuptime(&now);        /* Get present time from hardware. */
  53.    362                        next->sec = -1;                /* Next tick is not scheduled yet. */
  54.    363                }
  55. /* 364:更新struct pcpu_state对象的now成员 */
  56.    364                state->now = now;
  57.    365                CTR4(KTR_SPARE2, "intr at %d:    now  %d.%08x%08x",
  58.    366                    curcpu, now.sec, (unsigned int)(now.frac >> 32),
  59.    367                                     (unsigned int)(now.frac & 0xffffffff));
  60.    368               
  61. /*******************************************************************************
  62. * 369-386:
  63.    在SMP中,仅在描述APIC TIMER的struct eventtimer对象没有ET_FLAGS_PERCPU标志时
  64.    进行相应的处理。这段代码仅在BSP上执行,BSP查看AP是否有相应的时钟事件发生,
  65.    如果有,就将其对应的struct pcpu_state对象的ipi成员设置为1,表示需要给相应
  66.    的AP发送一个处理器间中断。
  67. *****************************************/
  68.    369        #ifdef SMP
  69.    370                /* Prepare broadcasting to other CPUs for non-per-CPU timers. */
  70.    371                bcast = 0;
  71.    372                if ((et->et_flags & ET_FLAGS_PERCPU) == 0 && smp_started) {
  72.    373                        CPU_FOREACH(cpu) {
  73.    374                                state = DPCPU_ID_PTR(cpu, timerstate);
  74.    375                                ET_HW_LOCK(state);
  75.    376                                state->now = now;
  76.    377                                if (bintime_cmp(&now, &state->nextevent, >=)) {
  77.    378                                        state->nextevent.sec++;
  78.    379                                        if (curcpu != cpu) {
  79.    380                                                state->ipi = 1;
  80.    381                                                bcast = 1;
  81.    382                                        }
  82.    383                                }
  83.    384                                ET_HW_UNLOCK(state);
  84.    385                        }
  85.    386                }
  86.    387        #endif
  87.    388       
  88.    389               
  89. /***************************************************************
  90. * Handle events for this time on this CPU.
  91.    390:调用函数handleevents处理相应的时钟事件。
  92. ***********************/
  93.    390                handleevents(&now, 0);
  94.    391               
  95. /**************************************************************
  96. * 392-405:
  97.    根据上面369-387之间的结果,来决定是否向AP发送一个类型为
  98.    IPI_HARDCLOCK的处理器间中断。
  99. *****************************/
  100.    392        #ifdef SMP
  101.    393                /* Broadcast interrupt to other CPUs for non-per-CPU timers. */
  102.    394                if (bcast) {
  103.    395                        CPU_FOREACH(cpu) {
  104.    396                                if (curcpu == cpu)
  105.    397                                        continue;
  106.    398                                state = DPCPU_ID_PTR(cpu, timerstate);
  107.    399                                if (state->ipi) {
  108.    400                                        state->ipi = 0;
  109.    401                                        ipi_cpu(cpu, IPI_HARDCLOCK);
  110.    402                                }
  111.    403                        }
  112.    404                }
  113.    405        #endif
  114.    406        }
复制代码
[函数handleevents]:
  1. /*************************************************************
  2. * Handle all events for specified time on this CPU
  3.    now:当前的时间戳。
  4.    fake:正常情况下调用handleevents函数时,fake参数都为0.
  5.          不过,在SMP系统中,当AP被启动后并初始化自己的时钟
  6.          时,会以参数2调用handleevents函数,这里就忽略这种
  7.          情况。
  8. *************************/
  9.    180        static int
  10.    181        handleevents(struct bintime *now, int fake)
  11.    182        {
  12.    183                struct bintime t;
  13.    184                struct trapframe *frame;
  14.    185                struct pcpu_state *state;
  15.    186                uintfptr_t pc;
  16.    187                int usermode;
  17.    188                int done, runs;
  18.    189       
  19.    190                CTR4(KTR_SPARE2, "handle at %d:  now  %d.%08x%08x",
  20.    191                    curcpu, now->sec, (unsigned int)(now->frac >> 32),
  21.    192                             (unsigned int)(now->frac & 0xffffffff));
  22. /**********************************************************************************************************
  23. * 194-202:
  24. * 此时将执行else条件语句。
  25.    #define TRAPF_USERMODE(framep) ((ISPL((framep)->tf_cs) == SEL_UPL) || ((framep)->tf_eflags & PSL_VM))
  26.    #define TRAPF_PC(framep)        ((framep)->tf_eip)

  27.    200:如果发生APIC TIMER中断时cpu处于用户态或者处于vm86模式,usermode的值为非零,这里只关心
  28.         cpu是否处于用户态。
  29.         pc:保存在线程内核栈上的eip(中断发生时eip的值)
  30. *******************************************/
  31.    193                done = 0;
  32.    194                if (fake) {
  33.    195                        frame = NULL;
  34.    196                        usermode = 0;
  35.    197                        pc = 0;
  36.    198                } else {
  37.    199                        frame = curthread->td_intr_frame;
  38.    200                        usermode = TRAPF_USERMODE(frame);
  39.    201                        pc = TRAPF_PC(frame);
  40.    202                }
  41.    203                /* 204:state指向当前cpu对应的struct pcpu_state对象 */
  42.    204                state = DPCPU_PTR(timerstate);
  43.    205               
  44. /**********************************************************
  45.   * 206-217: runs记录相应clock被触发的次数。
  46.              处理hardclock,并更新nexthard成员。
  47.   ********************************/
  48.    206                runs = 0;
  49.    207                while (bintime_cmp(now, &state->nexthard, >=)) {
  50.    208                        bintime_add(&state->nexthard, &hardperiod);
  51.    209                        runs++;
  52.    210                }
  53.    211                if ((timer->et_flags & ET_FLAGS_PERCPU) == 0 &&
  54.    212                    bintime_cmp(&state->nexthard, &nexthard, >))
  55.    213                        nexthard = state->nexthard;
  56.    214                if (runs && fake < 2) {
  57.    215                        hardclock_cnt(runs, usermode);
  58.    216                        done = 1;
  59.    217                }
  60. /**********************************************************
  61. * 218-226: runs记录相应clock被触发的次数。
  62.             处理statclock,并更新nextstat成员,statclock检查线程的
  63.             时间片是否用完。
  64. ********************************/
  65.    218                runs = 0;
  66.    219                while (bintime_cmp(now, &state->nextstat, >=)) {
  67.    220                        bintime_add(&state->nextstat, &statperiod);
  68.    221                        runs++;
  69.    222                }
  70.    223                if (runs && fake < 2) {
  71.    224                        statclock_cnt(runs, usermode);
  72.    225                        done = 1;
  73.    226                }
  74. /***********************************************************
  75. * 227-238:
  76.    只有当线程执行了profil系统调用时,这里才会检查profclock。
  77.    static int        profiling = 0;         Profiling events enabled.
  78. ******************************/  
  79.    227                if (profiling) {
  80.    228                        runs = 0;
  81.    229                        while (bintime_cmp(now, &state->nextprof, >=)) {
  82.    230                                bintime_add(&state->nextprof, &profperiod);
  83.    231                                runs++;
  84.    232                        }
  85.    233                        if (runs && !fake) {
  86.    234                                profclock_cnt(runs, usermode, pc);
  87.    235                                done = 1;
  88.    236                        }
  89.    237                } else
  90.    238                        state->nextprof = state->nextstat;
  91.    239       
  92.    240        #ifdef KDTRACE_HOOKS
  93.    241                if (fake == 0 && cyclic_clock_func != NULL &&
  94.    242                    state->nextcyc.sec != -1 &&
  95.    243                    bintime_cmp(now, &state->nextcyc, >=)) {
  96.    244                        state->nextcyc.sec = -1;
  97.    245                        (*cyclic_clock_func)(frame);
  98.    246                }
  99.    247        #endif
  100.    248                /* 249-261:忽略 */
  101.    249                getnextcpuevent(&t, 0);
  102.    250                if (fake == 2) {
  103.    251                        state->nextevent = t;
  104.    252                        return (done);
  105.    253                }
  106.    254                ET_HW_LOCK(state);
  107.    255                if (!busy) {
  108.    256                        state->idle = 0;
  109.    257                        state->nextevent = t;
  110.    258                        loadtimer(now, 0);
  111.    259                }
  112.    260                ET_HW_UNLOCK(state);
  113.    261                return (done);
  114.    262        }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP