免费注册 查看新帖 |

Chinaunix

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

[FreeBSD] freebsd9.2-何时调用mini_switch函数-线程时间片用完 [复制链接]

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

当statclock超时时,就会调用statclock_cnt进行相应的处理:
  1. /**************************************************************************
  2. * Statistics clock.  Updates rusage information and calls the scheduler
  3. * to adjust priorities of the active thread.
  4. *
  5. * This should be called by all active processors.
  6.            参数描述:
  7.            cnt:statclock超时的次数。
  8.            usermode:发生APIC TIMER中断时cpu是否处于用户态。
  9. ***********************************/
  10.    708        void
  11.    709        statclock_cnt(int cnt, int usermode)
  12.    710        {
  13.    711                struct rusage *ru;
  14.    712                struct vmspace *vm;
  15.    713                struct thread *td;
  16.    714                struct proc *p;
  17.    715                long rss;
  18.    716                long *cp_time;
  19.    717        
  20.    718                td = curthread;
  21.    719                p = td->td_proc;
  22.    720               
  23. /***********************************************************************************************************
  24. * struct pcpu数据对象的pc_cp_time成员定义如下:
  25.                    long                pc_cp_time[CPUSTATES];        /* statclock ticks */

  26.                    数组pc_cp_time的索引可以取值如下:
  27.                    #define        CP_USER                0
  28.                    #define        CP_NICE                1
  29.                    #define        CP_SYS                2
  30.                    #define        CP_INTR                3
  31.                    #define        CP_IDLE                4
  32.                    #define        CPUSTATES        5

  33.                    #define        NZERO        0                /* default "nice" */
  34.                    signed char        p_nice;                /* (c) Process "nice" value. */

  35.                    通过722-756之间代码,可以看出什么条件下递增struct thread对象的下列成员,这些成员
  36.                    都是对statclock超时次数进行计数:
  37.                    td_pticks:APIC TIMER中断发生时线程运行在内核态,并且线程不是中断线程或者线程的
  38.                               中断嵌套小于2,此时递增td_pticks。                  
  39.                      u_int                 td_pticks;        /* (t) Statclock hits for profiling */

  40.                    td_sticks:同td_pticks
  41.                           u_int                td_sticks;        /* (t) Statclock hits in system mode. */

  42.                    td_iticks:APIC TIMER中断发生时线程运行在内核态,并且线程是中断线程或者线程的
  43.                               中断嵌套大于2,此时递增td_iticks。
  44.                        u_int                td_iticks;        /* (t) Statclock hits in intr mode. */

  45.                    td_uticks:APIC TIMER中断发生时线程运行在用户态,此时递增td_uticks。
  46.                         u_int                td_uticks;        /* (t) Statclock hits in user mode. */
  47.                   
  48.                    pc_cp_time数组元素也是对statclock的超时次数进行计数,只不过使用另外一种方式
  49.                    来描述,含义是显而易见的。

  50.                    722-731:cpu处于用户态。
  51.                    731-756:cpu处于内核态。
  52. ************************************************************************************************/  
  53.    721                cp_time = (long *)PCPU_PTR(cp_time);
  54.    722                if (usermode) {
  55.    723                        /*
  56.    724                         * Charge the time as appropriate.
  57.    725                         */
  58.    726                        td->td_uticks += cnt;
  59.    727                        if (p->p_nice > NZERO)
  60.    728                                cp_time[CP_NICE] += cnt;
  61.    729                        else
  62.    730                                cp_time[CP_USER] += cnt;
  63.    731                } else {
  64.    732                        /*
  65.    733                         * Came from kernel mode, so we were:
  66.    734                         * - handling an interrupt,
  67.    735                         * - doing syscall or trap work on behalf of the current
  68.    736                         *   user process, or
  69.    737                         * - spinning in the idle loop.
  70.    738                         * Whichever it is, charge the time as appropriate.
  71.    739                         * Note that we charge interrupts to the current process,
  72.    740                         * regardless of whether they are ``for'' that process,
  73.    741                         * so that we know how much of its real time was spent
  74.    742                         * in ``non-process'' (i.e., interrupt) work.
  75.    743                         */
  76.    744                        if ((td->td_pflags & TDP_ITHREAD) ||
  77.    745                            td->td_intr_nesting_level >= 2) {
  78.    746                                td->td_iticks += cnt;
  79.    747                                cp_time[CP_INTR] += cnt;
  80.    748                        } else {
  81.    749                                td->td_pticks += cnt;
  82.    750                                td->td_sticks += cnt;
  83.    751                                if (!TD_IS_IDLETHREAD(td))
  84.    752                                        cp_time[CP_SYS] += cnt;
  85.    753                                else
  86.    754                                        cp_time[CP_IDLE] += cnt;
  87.    755                        }
  88.    756                }
  89.    757        
  90.    758               
  91. /*********************************************************
  92. * Update resource usage integrals and maximums.
  93.    760-767:进程资源使用量相关处理,暂时略过。
  94. ********************************/
  95.    759                MPASS(p->p_vmspace != NULL);
  96.    760                vm = p->p_vmspace;
  97.    761                ru = &td->td_ru;
  98.    762                ru->ru_ixrss += pgtok(vm->vm_tsize) * cnt;
  99.    763                ru->ru_idrss += pgtok(vm->vm_dsize) * cnt;
  100.    764                ru->ru_isrss += pgtok(vm->vm_ssize) * cnt;
  101.    765                rss = pgtok(vmspace_resident_count(vm));
  102.    766                if (ru->ru_maxrss < rss)
  103.    767                        ru->ru_maxrss = rss;
  104.    768                KTR_POINT2(KTR_SCHED, "thread", sched_tdname(td), "statclock",
  105.    769                    "prio:%d", td->td_priority, "stathz:%d", (stathz)?stathz:hz);
  106.    770                SDT_PROBE2(sched, , , tick, td, td->td_proc);
  107.    771                thread_lock_flags(td, MTX_QUIET);
  108. /******************************************************
  109. * 772-773: 重点
  110.     调度程序相关的sched_clock函数。
  111. **************************/
  112.    772                for ( ; cnt > 0; cnt--)
  113.    773                        sched_clock(td);
  114.    774                thread_unlock(td);
  115.    775        #ifdef HWPMC_HOOKS
  116.    776                if (td->td_intr_frame != NULL)
  117.    777                        PMC_SOFT_CALL_TF( , , clock, stat, td->td_intr_frame);
  118.    778        #endif
  119.    779        }
复制代码
[ULE调度程序的sched_clock函数]:
  1.   2174        /*
  2.   2175         * Handle a stathz tick.  This is really only relevant for timeshare
  3.   2176         * threads.
  4.   2177         */
  5.   2178        void
  6.   2179        sched_clock(struct thread *td)
  7.   2180        {
  8.   2181                struct tdq *tdq;
  9.   2182                struct td_sched *ts;
  10.   2183                /* 2185:tdq指向当前cpu对应的struct tdq对象 */
  11.   2184                THREAD_LOCK_ASSERT(td, MA_OWNED);
  12.   2185                tdq = TDQ_SELF();
  13.   2186        #ifdef SMP
  14.   2187               
  15.   2188
  16. /**********************************************************************************************
  17. * We run the long term load balancer infrequently on the first cpu.

  18.                    static struct tdq       *balance_tdq;
  19.                    static int balance_ticks;

  20.                    2190-2193:
  21.                    在ULE调度程序初始化阶段,BSP会将balance_tdq设置为BSP对应的struct tdq
  22.                    数据对象的地址。
  23.                   
  24.                    balance_ticks为调用sched_balance函数在系统中cpu的运行队列间迁移线程
  25.                    的周期,在ULE调度程序初始化阶段设置,具体值是多少应该没什么意义:
  26.                    balance_interval默认值为128.
  27.                    balance_ticks = max(balance_interval / 2, 1);
  28.                    balance_ticks += random() % balance_interval;

  29.                    当balance_ticks为零0时,函数sched_balance就要保持系统中cpu运行队列
  30.                    间的平衡。
  31. *******************************************/
  32.   2189                 
  33.   2190                if (balance_tdq == tdq) {
  34.   2191                        if (balance_ticks && --balance_ticks == 0)
  35.   2192                                sched_balance();
  36.   2193                }
  37.   2194        #endif
  38.   2195                /*
  39.   2196                 * Save the old switch count so we have a record of the last ticks
  40.   2197                 * activity.   Initialize the new switch count based on our load.
  41.   2198                 * If there is some activity seed it to reflect that.
  42.   2199                 */
  43.   2200                tdq->tdq_oldswitchcnt = tdq->tdq_switchcnt;
  44.   2201                tdq->tdq_switchcnt = tdq->tdq_load;
  45. /**************************************************************************
  46. * Advance the insert index once for each tick to ensure that all
  47. * threads get a chance to run.
  48.    2206-2210:防止时间片轮转的线程发生饿死现象。
  49. *************************/
  50.   2205                 */
  51.   2206                if (tdq->tdq_idx == tdq->tdq_ridx) {
  52.   2207                        tdq->tdq_idx = (tdq->tdq_idx + 1) % RQ_NQS;
  53.   2208                        if (TAILQ_EMPTY(&tdq->tdq_timeshare.rq_queues[tdq->tdq_ridx]))
  54.   2209                                tdq->tdq_ridx = tdq->tdq_idx;
  55.   2210                }
  56. /*****************************************************************
  57. * 2211:ts指向和td相关联的struct td_sched数据对象。
  58.    2212:函数sched_pctcpu_updat更新struct td_sched数据对象中的
  59.           相关成员。
  60. *********************************/
  61.   2211                ts = td->td_sched;
  62.   2212                sched_pctcpu_update(ts, 1);
  63. /******************************************************
  64. * #define PRI_FIFO_BIT            8
  65.    如果线程td的调度类为PRI_FIFO_BIT,这里就直接返回。
  66. *****************************/
  67.   2213                if (td->td_pri_class & PRI_FIFO_BIT)
  68.   2214                        return;
  69. /**********************************************************************
  70. * 2215-2223:
  71.    #define PRI_TIMESHARE  3  Time sharing process.
  72.    如果线程td的调度类为时间片轮转,此时就要进行相应的处理:
  73.    2220:给线程运行时间增加一个tickincr,ULE调度程序源码中
  74.          对该变量的描述如下:
  75.          tickincr:Converts a stathz tick into a hz domain scaled by
  76.                   the shift factor.  Without the shift the error rate
  77.                   due to rounding would be unacceptably high.

  78.    2221:sched_interact_update函数调整td的睡眠时间和运行时间。
  79.    
  80.    2222:sched_priority函数调整struct thread对象的td_user_pri,
  81.          td_base_user_pri两个成员。
  82. *********************************************/
  83.   2215                if (PRI_BASE(td->td_pri_class) == PRI_TIMESHARE) {
  84.   2216                        /*
  85.   2217                         * We used a tick; charge it to the thread so
  86.   2218                         * that we can compute our interactivity.
  87.   2219                         */
  88.   2220                        td->td_sched->ts_runtime += tickincr;
  89.   2221                        sched_interact_update(td);
  90.   2222                        sched_priority(td);
  91.   2223                }
  92.   2224        
  93. /************************************************************************
  94. * Force a context switch if the current thread has used up a full
  95. * time slice (default is 100ms).

  96.    #define        TDF_NEEDRESCHED        0x00010000  Thread needs to yield.
  97.    #define TDF_SLICEEND    TDF_SCHED2     Thread time slice is over.

  98.    sched_slice:
  99.    ULE调度程序源码中对该变量的描述为:Runtime of each thread before
  100.    rescheduling,默认值为12,可见sched_slice为每个线程的时间片,
  101.    因为statclock的周期为1/128秒,在使用默认时间片的情况下,一个线程
  102.    用完其时间片的大概时间为(1/128) * 12,这个结果为0.09秒,约为0.1秒,
  103.    即100毫秒。
  104.                   
  105.    2229-2231:
  106.    如果线程td不是idle线程,并且线程td的时间片用完,此时:
  107.    2230:重新设置线程td的时间片。
  108.    2231:给线程td设置TDF_NEEDRESCHED和TDF_SLICEEND标志。
  109.          在中断返回时,会检查线程td是否设置了TDF_NEEDRESCHED标志,
  110.          如果设置,就调用mini_switch函数。            
  111. *******************************/
  112.   2229                if (!TD_IS_IDLETHREAD(td) && --ts->ts_slice <= 0) {
  113.   2230                        ts->ts_slice = sched_slice;
  114.   2231                        td->td_flags |= TDF_NEEDRESCHED | TDF_SLICEEND;
  115.   2232                }
  116.   2233        }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP