免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4240 | 回复: 0

[FreeBSD] freebsd9.2-ULE线程调度-相关数据结构的初始化 [复制链接]

论坛徽章:
0
发表于 2014-07-02 02:02 |显示全部楼层
本帖最后由 71v5 于 2014-07-02 02:08 编辑

注:
在时钟频率这块,之前参考的freebsd8.3中的实现,statclock的频率即stathz为128.其实这个值当时是通过分析其实现得到的,而没有通过sysctl输出其具体值
不过今天通过sysctl输出了freebsd9.2中内核变量sched_idlespinthresh和balance_interval的值,两个变量的值都为127。

sched_idlespinthresh = 2 * max(10000, 6 * hz) / realstathz;

如果realstathz为128,那么sched_idlespinthresh = 20000/128,即156.
如果realstathz为127,那么sched_idlespinthresh = 20000/127,即157.

所以,freebsd9.2中statclock的频率为127,不过这个频率的具体值是多少并不影响理解相关实现的原理。



在对ULE线程调度的数据结构及相关主要函数进行了简要分析之后,下面看看ULE线程调度相关的初始化,这些初始化工作现在
已经很容易明白了。

从"Intel 64 and IA-32 Architectures Software Developer's Manual Volume 3A"第8.4节摘取了一段对BSP和AP的定义:
The MP initialization protocol defines two classes of processors: the bootstrap processor (BSP) and the application
processors (APs). Following a power-up or RESET of an MP system, system hardware dynamically selects one of the
processors on the system bus as the BSP. The remaining processors are designated as APs。


在SMP系统中,BSP和AP都要做一些ULE线程调度相关的初始化工作,只不过大部分工作是由BSP来完成的。
下面的初始化工作由BSP来完成:
1:在SI_SUB_INTRINSIC对应的子系统中,由函数proc0_init调用schedinit函数完成。
2:在SI_SUB_RUN_QUEUE对应的子系统中,由函数sched_setup来完成。
3:在SI_SUB_CLOCKS对应的子系统中,由函数sched_initticks来完成。

对于AP,只是简单的调用函数choosethread选择一个线程,然后执行一个线程切换操作。


1-[ULE线程调度-函数schedinit]-其实该函数不应该算作ULE线程调度的初始化函数:
  1. /**************************************************************
  2. * Called from proc0_init() to setup the scheduler fields.
  3.    函数schedinit主要设置proc0和thread0调度方面的参数。
  4. ***********************/
  5.   1575  void
  6.   1576  schedinit(void)
  7.   1577  {
  8.   1578  
  9.   1579         
  10.   1580
  11. /**********************************************************************************
  12. * Set up the scheduler specific parts of proc0.

  13.    变量ticks: volatile int        ticks;
  14.    系统启动以来经过的节拍数,当hardclock超时后,由函数hardclock_cnt来更新,
  15.    这里只需要明白该变量的含义。
  16.   
  17.    进程0的相关数据结构都是静态定义的,下面的数据结构都是在proc0_init函数中
  18.    完成的,schedinit函数紧初始化一些调度方面的成员:
  19.    static struct td_sched td_sched0;
  20.    struct        proc proc0;
  21.    struct        thread thread0

  22.    struct proc对象的p_sched成员:
  23.    struct p_sched        *p_sched;        /* (*) Scheduler-specific data. */
  24.    该成员一般不是用,ULE线程调度使用的是struct td_sched对象。

  25.    struct td_sched对象的下面成员:
  26.    int             ts_ltick;       /* Last tick that we were running on */
  27.    int             ts_ftick;       /* First tick that we were running on */

  28.    其实,此时就是线程0在运行,将上面两个成员都设置为ticks。

  29.    static int sched_slice = 12; /*Runtime of each thread before rescheduling */
  30.    struct td_sched对象的ts_slice成员:
  31.    ts_slice:线程剩余的时间片。  
  32. **************************************/
  33.   1581         
  34.   1582          proc0.p_sched = NULL; /* XXX */
  35.   1583          thread0.td_sched = &td_sched0;
  36.   1584          td_sched0.ts_ltick = ticks;
  37.   1585          td_sched0.ts_ftick = ticks;
  38.   1586          td_sched0.ts_slice = sched_slice;
  39.   1587  }
复制代码
2-[ULE线程调度-sched_setup函数]-该函数初始化一些ULE调度程序使用的数据结构:
  1.   1355  static void
  2.   1356  sched_setup(void *dummy)
  3.   1357  {
  4.   1358          struct tdq *tdq;
  5.   1359         
  6. /*****************************************************************
  7. * 宏TDQ_SELF始终返回当前cpu对应的struct tdq数据对象的地址。

  8.    SMP系统:
  9.    数组tdq_cpu以cpu的logical id为索引。
  10.    static struct tdq       tdq_cpu[MAXCPU];
  11.    #define TDQ_SELF()      (&tdq_cpu[PCPU_GET(cpuid)])
  12.    tdq的值为&tdq_cpu[0]

  13.    非SMP系统:
  14.    static struct tdq       tdq_cpu;
  15.    #define TDQ_SELF()      (&tdq_cpu)
  16.    tdq的值为&tdq_cpu。

  17.    SMP系统:调用sched_setup_smp函数处理,见下面的简要分析。
  18.    非SMP系统:调用tdq_setup函数处理,见下面的简要分析。
  19. *************************************************/
  20.   1360          tdq = TDQ_SELF();
  21.   1361  #ifdef SMP
  22.   1362          sched_setup_smp();
  23.   1363  #else
  24.   1364          tdq_setup(tdq);
  25.   1365  #endif
  26.   1366  
  27.   1367          /* Add thread0's load since it's running. */
  28.   1368          TDQ_LOCK(tdq);
  29.   1369          thread0.td_lock = TDQ_LOCKPTR(TDQ_SELF());
  30. /* 函数tdq_load_add简单的递增struct tdq对象中tdq_load成员 */
  31.   1370          tdq_load_add(tdq, &thread0);
  32.   1371          tdq->tdq_lowpri = thread0.td_priority;
  33.   1372          TDQ_UNLOCK(tdq);
  34.   1373  }
复制代码
[ULE线程调度-tdq_setup函数]-该函数初始化相应struct tdq对象中的tdq_realtime队列,tdq_timeshare队列,tdq_idle队列:
  1.   1312        static void
  2.   1313        tdq_setup(struct tdq *tdq)
  3.   1314        {
  4.   1315       
  5.   1316                if (bootverbose)
  6.   1317                        printf("ULE: setup cpu %d\n", TDQ_ID(tdq));
  7. /************************************************************************************
  8. * 1318-1320:
  9.    函数runq_init完成下面的工作:
  10.    1:将相应队列的数组rq_status.rqb_bits做清零处理。
  11.    2:初始化相应队列中的链表数组rq_queues。
  12. ***********************/
  13.   1318                runq_init(&tdq->tdq_realtime);
  14.   1319                runq_init(&tdq->tdq_timeshare);
  15.   1320                runq_init(&tdq->tdq_idle);
  16.   1321                snprintf(tdq->tdq_name, sizeof(tdq->tdq_name),
  17.   1322                    "sched lock %d", (int)TDQ_ID(tdq));
  18.   1323                mtx_init(&tdq->tdq_lock, tdq->tdq_name, "sched lock",
  19.   1324                    MTX_SPIN | MTX_RECURSE);
  20.   1325        #ifdef KTR
  21.   1326                snprintf(tdq->tdq_loadname, sizeof(tdq->tdq_loadname),
  22.   1327                    "CPU %d load", (int)TDQ_ID(tdq));
  23.   1328        #endif
  24.   1329        }
复制代码
[ULE线程调度-sched_setup_smp函数]:
  1.   1333  sched_setup_smp(void)
  2.   1334  {
  3.   1335          struct tdq *tdq;
  4.   1336          int i;
  5.   1337         
  6. /*******************************************************************
  7. * struct cpu_group *cpu_top;           CPU topology
  8.    函数smp_topo建立数据结构来描述cpu拓扑信息。
  9. ***********************************/
  10.   1338          cpu_top = smp_topo();
  11. /************************************************************************
  12. * 1339-1345:
  13.    调用函数tdq_setup初始化tdq_cpu[0],tdq_cpu[1],tdq_cpu[2]等等。
  14.    tdq_cpu[0],tdq_cpu[1],tdq_cpu[2],tdq_cpu[3]的td_cg成员指向group[1].
  15.    tdq_cpu[4],tdq_cpu[5],tdq_cpu[6],tdq_cpu[7]的td_cg成员指向group[2].
  16. *************************************/
  17.   1339          CPU_FOREACH(i) {
  18.   1340                  tdq = TDQ_CPU(i);
  19.   1341                  tdq_setup(tdq);
  20.   1342                  tdq->tdq_cg = smp_topo_find(cpu_top, i);
  21.   1343                  if (tdq->tdq_cg == NULL)
  22.   1344                          panic("Can't find cpu group for %d\n", i);
  23.   1345          }
  24. /****************************************************************************
  25. * static struct tdq       *balance_tdq;
  26.    1346:
  27.    这里将balance_tdq设置为&tdq_cpu[0],意味着由BSP来执行sched_balance函数。
  28.    
  29.    1347:调用函数sched_balance。   
  30. **************/
  31.   1346          balance_tdq = TDQ_SELF();
  32.   1347          sched_balance();
  33.   1348  }
复制代码
3-[ULE线程调度-函数sched_initticks]-该函数主要初始化一些ULE线程调度使用的变量:
  1.   1379        static void
  2.   1380        sched_initticks(void *dummy)
  3.   1381        {
  4.   1382                int incr;
  5.   1383                
  6. /***********************************************************************************************
  7. * 参考freebsd8.3中的实现:
  8.    hardclock:频率为hz,每秒1000次,当发生两次APIC TIMER时钟中断时,才运行一次hardclock。
  9.    statclock:频率为128,周期为1/128秒,即1/128秒运行一次statclock。
  10.    profclock:频率为2*hz,即每秒2000次,周期为0.5毫秒,每秒运行2000次。

  11.    1384-1385:
  12.    realstathz:  statclock的真实频率。
  13.    sched_slice: Runtime of each thread before rescheduling.
  14.    
  15.    默认值如下:               
  16.    static int sched_slice = 12;
  17.    static int realstathz = 127;

  18.    所以可以得到:
  19.    realstathz=128;
  20.    sched_slice=12;
  21.    从APIC TIMER时钟中断的处理来看,每个进程的时间片大概是12/128 = 0.09s,约0.1s即100ms。

  22.    1386:
  23.    int        hogticks;看了一下,该变量主要在I/O模块中使用,暂时不清楚其含义。
  24. *********************************************/
  25.   1384                realstathz = stathz ? stathz : hz;
  26.   1385                sched_slice = realstathz / 10;        /* ~100ms */
  27.   1386                hogticks = imax(1, (2 * hz * sched_slice + realstathz / 2) /
  28.   1387                    realstathz);
  29.   1388       
  30. /***********************************************************************************
  31. * 1389-1400:
  32.    重新设置变量tickincr,该变量一般被加到线程的运行时间上。

  33.    默认值如下:
  34.    static int tickincr = 8 << SCHED_TICK_SHIFT;

  35.    #define SCHED_TICK_SHIFT        10
  36.    hz(即1000)的二进制表示为1111101000
  37. ***********************/
  38.   1389                /*
  39.   1390                 * tickincr is shifted out by 10 to avoid rounding errors due to
  40.   1391                 * hz not being evenly divisible by stathz on all platforms.
  41.   1392                 */
  42.   1393                incr = (hz << SCHED_TICK_SHIFT) / realstathz;
  43.   1394                /*
  44.   1395                 * This does not work for values of stathz that are more than
  45.   1396                 * 1 << SCHED_TICK_SHIFT * hz.  In practice this does not happen.
  46.   1397                 */
  47.   1398                if (incr == 0)
  48.   1399                        incr = 1;
  49.   1400                tickincr = incr;
  50.   1401        #ifdef SMP
  51.   1402               
  52.   1403
  53.   1404
  54.   1405
  55. /*********************************************************************
  56. * Set the default balance interval now that we know
  57. * what realstathz is.
  58. * 1406:
  59.    重新计算balance_interval,不过依然保持默认值128。
  60.    该变量在计算balance_ticks时使用,在balance_ticks为0时,将调用
  61.    函数sched_balance。

  62.    1407:
  63.    变量affinity由宏SCHED_AFFINITY使用。
  64. ******************************/
  65.   1406                balance_interval = realstathz;
  66.   1407                affinity = SCHED_AFFINITY_DEFAULT;
  67.   1408        #endif
  68. /****************************************************************************
  69. * static int sched_idlespinthresh = -1;
  70.    重新计算变量sched_idlespinthresh,当每cpu的idle线程被调度运行后,将
  71.    执行函数sched_idletd,该函数会使用这个变量。
  72. ******************************/
  73.   1409                if (sched_idlespinthresh < 0)
  74.   1410                        sched_idlespinthresh = 2 * max(10000, 6 * hz) / realstathz;
  75.   1411        }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP