免费注册 查看新帖 |

Chinaunix

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

[FreeBSD] freebsd9.2-ULE线程调度-为线程挑选一个合适的运行队列-sched_pickcpu函数 [复制链接]

论坛徽章:
0
发表于 2014-06-30 19:27 |显示全部楼层
SMP系统。

之前打算一个帖子简要描述一下如何将一个线程添加到cpu运行队列中,后来发现某些函数比较关键,放在一起的话会比较杂乱,这里就将其
独立出来。既然要将线程添加到cpu的运行队列中,那么就要选择一个合适的cpu的运行队列,这个工作相对来说比较重要,在选好cpu运行
队列后,后续的操作就会很简单了。

[ULE线程调度-sched_pickcpu函数]-执行fork系统调用创建一个新线程后,在将新线程添加到系统中某个cpu的运行队列之前,
必须为该线程选择一个合适的cpu运行队列,这个任务由函数sched_pickcpu来完成:
  1. /***************************************************************
  2. * 这里将td指向的struct thred对象记为add_thread,相应的线程
  3.    记为addtd。

  4.    参数描述:
  5.    td:这里为&add_thread。
  6.    flags:传递给函数sched_pickcpu的一些标志

  7.    函数返回值:系统中某个cpu的logical cpu id,这里简记为cpux,
  8.    cpux有可能和当前cpu是同一个cpu,挑选出合适的cpu也就等于
  9.    挑选出了合适的cpu运行队列。
  10. *******************************/
  11.   1182        static int
  12.   1183        sched_pickcpu(struct thread *td, int flags)
  13.   1184        {
  14.   1185                struct cpu_group *cg, *ccg;
  15.   1186                struct td_sched *ts;
  16.   1187                struct tdq *tdq;
  17.   1188                cpuset_t mask;
  18.   1189                int cpu, pri, self;
  19.   1190                
  20. /*************************************************************
  21. * 1191:self为当前cpu的logical cpu id。
  22.    1193:ts指向和addtd相关联的struct td_sched对象。
  23.    1194:没有启用SMP,这里直接返回self。
  24. *************************************/
  25.   1191                self = PCPU_GET(cpuid);
  26.   1192                ts = td->td_sched;
  27.   1193                if (smp_started == 0)
  28.   1194                        return (self);
  29.   1195               
  30.   1196  
  31. /**********************************************************************************
  32. * 1198-1199:
  33.    Don't migrate a running thread from sched_switch().
  34.    #define        SRQ_OURSELF        0x0002        It is ourself (from mi_switch).
  35.    #define        THREAD_CAN_MIGRATE(td)        ((td)->td_pinned == 0)

  36.    struct thread对象的td_pinned成员,函数sched_pin会递增该成员,
  37.    如果该成员非零,就表示线程addtd不能被迁移到其它cpu的运行队列中:       
  38.    int                td_pinned;Temporary cpu pin count.

  39.    struct td_sched对象的ts_cpu成员:
  40.    ts_cpu: 该成员的值为系统中某个cpu的logical cpu id,和该struct td_sched对象
  41.    相关联的thread将被添加到ts_cpu的运行队列中.

  42.    正常情况下,struct thread对象的td_pinned成员为0,所以只有当参数flags
  43.    设置了SRQ_OURSELF标志时,此时就直接返回struct td_sched对象的ts_cpu成员。
  44. ************************************************/                  
  45.   1197                 
  46.   1198                if ((flags & SRQ_OURSELF) || !THREAD_CAN_MIGRATE(td))
  47.   1199                        return (ts->ts_cpu);
  48.   1200               
  49.   1201
  50.   1202
  51. /******************************************************************************************
  52. * Prefer to run interrupt threads on the processors that generate
  53. * the interrupt.

  54.    struct thread对象的td_cpuset成员:
  55.    td_cpuset:cpu掩码的集合,即CPU affinity sets,一般情况下该字段的值从父进程
  56.               继承,除非调用cpuset系统调用进行修改。td_cpuset->cs_mask为一个类型为
  57.               cpuset_t类型的变量,线程thread只能被添加到td_cpuset->cs_mask
  58.               中设置为1的bit位对应的cpu的运行队列中。

  59.    td_intr_nesting_level; Interrupt recursion. 中断嵌套数。

  60.    #define PRI_MAX_ITHD            (PRI_MIN_REALTIME - 1)  47

  61.    #define THREAD_CAN_SCHED(td, cpu) CPU_ISSET((cpu), &(td)->td_cpuset->cs_mask)
  62.    宏THREAD_CAN_SCHED检查td_cpuset->cs_mask中logical cpu id为cpu对应的bit位是否被
  63.    设置为1,如果为1,THREAD_CAN_SCHED宏的值就为1,否则为零。

  64.    当下面的条件都满足时,就执行1205-1213之间的代码:
  65.    条件1:addtd的优先级小于或者等于47,即addtd属于中断线程。
  66.    条件2:THREAD_CAN_SCHED(td, ts->ts_cpu)宏的值为1,即td可以被添加到logical cpu id
  67.           为ts->ts_cpu的运行队列中。
  68.    条件3:curthread->td_intr_nesting_level非零,表示当前cpu正在处理中断。
  69.    条件4:ts_cpu和self不是同一个cpu。

  70.    pickcpu_affinity, "Picked cpu based on affinity"
  71.    此时将ts_cpu设置为self,这部分代码和中断线程相关,留作以后再详细将其分析。
  72. *****************************************************/                  
  73.   1203                 
  74.   1204                pri = td->td_priority;
  75.   1205                if (td->td_priority <= PRI_MAX_ITHD && THREAD_CAN_SCHED(td, self) &&
  76.   1206                    curthread->td_intr_nesting_level && ts->ts_cpu != self) {
  77.   1207                        SCHED_STAT_INC(pickcpu_intrbind);
  78.   1208                        ts->ts_cpu = self;
  79.   1209                        if (TDQ_CPU(self)->tdq_lowpri > pri) {
  80.   1210                                SCHED_STAT_INC(pickcpu_affinity);
  81.   1211                                return (ts->ts_cpu);
  82.   1212                        }
  83.   1213                }
  84.   1214               
  85.   1215
  86.   1216
  87. /***************************************************************************************************
  88. * If the thread can run on the last cpu and the affinity has not
  89. * expired or it is idle run it there.

  90.    struct tdq对象的tdq_lowpri成员:
  91.    tdq_lowpri:tdq_lowpri成员保存了相应CPU运行队列中线程的最高优先级(数值最小)。

  92.    struct thread对象的td_cpuset成员:
  93.    td_cpuset:cpu掩码的集合,即CPU affinity sets,一般情况下该字段的值从父进程
  94.               继承,除非调用cpuset系统调用进行修改。td_cpuset->cs_mask为一个类型为
  95.               cpuset_t类型的变量,线程thread只能被添加到td_cpuset->cs_mask
  96.               中设置为1的bit位对应的cpu的运行队列中。

  97.    struct td_sched对象的ts_cpu成员:
  98.    ts_cpu: 该成员的值为系统中某个cpu的logical cpu id,和该struct td_sched对象
  99.    相关联的thread将被添加到ts_cpu的运行队列中.

  100.    #define PRI_MIN_IDLE            (224)

  101.    #define THREAD_CAN_SCHED(td, cpu) CPU_ISSET((cpu), &(td)->td_cpuset->cs_mask)
  102.    宏THREAD_CAN_SCHED检查td_cpuset->cs_mask中logical cpu id为cpu对应的bit位是否被
  103.    设置为1,如果为1,THREAD_CAN_SCHED宏的值就为1,否则为零。               
  104.    
  105.    1218:tdq指向ts_cpu对应的运行队列,这里tdq的值为&tdq_cpu[ts->ts_cpu]。
  106.    1219:cg指向group[1]或者group[2],这里假设cg指向group[1]。

  107.    1220-1234:仅当下面的条件都为真时,才执行这个if语句:
  108.    条件1:THREAD_CAN_SCHED(td, ts->ts_cpu)宏的值为1,即td可以被添加到logical cpu id
  109.           为ts->ts_cpu的运行队列中。
  110.    条件2:tdq_lowpri >= PRI_MIN_IDLE为真,表示运行队列tdq上的线程的是IDLE线程。
  111.    条件3:SCHED_AFFINITY宏的值为1。

  112.    宏SCHED_AFFINITY的值为1:可以理解为线程td从ts->ts_cpu上被替换,但是被替换的时间
  113.    不是很长,线程td的相关数据可能还保存在ts->ts_cpu的高速缓存中,还没有被刷新。

  114.    宏SCHED_AFFINITY的值为0:可以理解为线程td从ts->ts_cpu上被替换,但是被替换的时间
  115.    过长,线程td的相关数据可能已经不在ts->ts_cpu的高速缓存中,相关的高速缓存已经被刷新。
  116.    
  117.    当上面三个条件都为真时,因为group[1]和group[2]的cg_flags成员都为0,此时:
  118.   
  119.    一般情况下都会执行1228-1233之间的代码,递增一个每cpu变量pickcpu_idle_affinity,
  120.    返回ts->ts_cpu,宏SCHED_AFFINITY的值为1可以很清楚的说明为什么要选择ts_cpu。
  121.    pickcpu_idle_affinity, "Picked idle cpu based on affinity"
  122. **********************************/
  123.   1217                 
  124.   1218                tdq = TDQ_CPU(ts->ts_cpu);
  125.   1219                cg = tdq->tdq_cg;
  126.   1220                if (THREAD_CAN_SCHED(td, ts->ts_cpu) &&
  127.   1221                    tdq->tdq_lowpri >= PRI_MIN_IDLE &&
  128.   1222                    SCHED_AFFINITY(ts, CG_SHARE_L2)) {
  129.   1223                        if (cg->cg_flags & CG_FLAG_THREAD) {
  130.   1224                                CPUSET_FOREACH(cpu, cg->cg_mask) {
  131.   1225                                        if (TDQ_CPU(cpu)->tdq_lowpri < PRI_MIN_IDLE)
  132.   1226                                                break;
  133.   1227                                }
  134.   1228                        } else
  135.   1229                                cpu = INT_MAX;
  136.   1230                        if (cpu > mp_maxid) {
  137.   1231                                SCHED_STAT_INC(pickcpu_idle_affinity);
  138.   1232                                return (ts->ts_cpu);
  139.   1233                        }
  140.   1234                }
  141. /******************************************************************************************
  142. * Search for the last level cache CPU group in the tree.
  143. * Skip caches with expired affinity time and SMT groups.
  144. * Affinity to higher level caches will be handled less aggressively.
  145.    
  146.    宏SCHED_AFFINITY的值为1:可以理解为线程td从ts->ts_cpu上被替换,但是被替换的时间
  147.    不是很长,线程td的相关数据可能还保存在ts->ts_cpu的高速缓存中,还没有被刷新。

  148.    宏SCHED_AFFINITY的值为0:可以理解为线程td从ts->ts_cpu上被替换,但是被替换的时间
  149.    过长,线程td的相关数据可能已经不在ts->ts_cpu的高速缓存中,相关的高速缓存已经被刷新。
  150.   
  151.    1240-1246:
  152.    检查cpu拓扑图,执行完之间的代码后,ccg或者为group[1],或者为cpu_top。  

  153.    1247-1248:重新设置cg。
  154. *********************************************/
  155.   1240                for (ccg = NULL; cg != NULL; cg = cg->cg_parent) {
  156.   1241                        if (cg->cg_flags & CG_FLAG_THREAD)
  157.   1242                                continue;
  158.   1243                        if (!SCHED_AFFINITY(ts, cg->cg_level))
  159.   1244                                continue;
  160.   1245                        ccg = cg;
  161.   1246                }
  162.   1247                if (ccg != NULL)
  163.   1248                        cg = ccg;
  164.   1249                cpu = -1;
  165.   1250               
  166. /*********************************************************************************************************
  167. * Search the group for the less loaded idle CPU we can run now.
  168.    1251-1261:
  169.    根据相应的条件,以不同的参数调用函数sched_lowest,该函数返回cpu运行队列负载最低
  170.    的cpu的logical cpu id,从这里可以看出,在将一个线程添加到cpu运行队列时,总是添加
  171.    到负载最低的cpu的运行队列中。

  172.    sched_lowest函数的描述请参见之前" freebsd9.2-ULE线程调度-保持cpu运行队列间的平衡-sched_balance函数 "
  173.    中的描述。
  174. *****************/
  175.   1251                mask = td->td_cpuset->cs_mask;
  176.   1252                if (cg != NULL && cg != cpu_top &&
  177.   1253                    CPU_CMP(&cg->cg_mask, &cpu_top->cg_mask) != 0)
  178.   1254                        cpu = sched_lowest(cg, mask, max(pri, PRI_MAX_TIMESHARE),
  179.   1255                            INT_MAX, ts->ts_cpu);
  180.   1256                /* Search globally for the less loaded CPU we can run now. */
  181.   1257                if (cpu == -1)
  182.   1258                        cpu = sched_lowest(cpu_top, mask, pri, INT_MAX, ts->ts_cpu);
  183.   1259                /* Search globally for the less loaded CPU. */
  184.   1260                if (cpu == -1)
  185.   1261                        cpu = sched_lowest(cpu_top, mask, -1, INT_MAX, ts->ts_cpu);
  186.   1262                KASSERT(cpu != -1, ("sched_pickcpu: Failed to find a cpu."));
  187.   1263               
  188.   1264
  189. /*****************************************************************************************************
  190. * Compare the lowest loaded cpu to current cpu.
  191.    执行到这里的话,函数sched_lowest已经成功选择了一个cpu,并将其
  192.    logical cpu id保存在变量cpu中。

  193.    相关的每cpu变量:
  194.    pickcpu_local, "Migrated to current cpu"
  195.    pickcpu_lowest, "Selected lowest load"
  196.    pickcpu_migration, "Selection may have caused migration"

  197.    struct tdq对象的:
  198.    tdq_lowpri:tdq_lowpri成员保存了相应CPU运行队列中线程的最高优先级(数值最小)。

  199.    #define PRI_MIN_IDLE            (224)

  200.    当下面的条件都满足时,执行1266-1271之间的语句:
  201.    条件1:THREAD_CAN_SCHED(td, ts->ts_cpu)宏的值为1,即addtd可以被添加到logical cpu id
  202.           为self的运行队列中。
  203.    条件2:tdq_cpu[self]中tdq_lowpri大于pri,当前在self上运行的线程优先级比addtd的优先级低。
  204.    条件3:tdq_cpu[cpu]中tdq_lowpri小于224,即不属于IDLE线程。
  205.    条件4:运行队列tdq_cpu[self]的负载比运行队列tdq_cpu[cpu]的负载低,这里加1是因为addtd要被添加到
  206.           运行队列tdq_cpu[cpu]中,所以其负载要加1.
  207.    此时递增pickcpu_local变量,将cpu重新设置为self。

  208.    如果上面4个条件任何一个为假,就执行1271-1272之间的代码,此时递增pickcpu_lowest变量。

  209.    1273-1274:检查cpu和ts_cpu是否相等,如果不相等,就表示addtd要被添加到另外一个cpu的
  210. * 运行队列中,同时递增pickcpu_migration变量。
  211. ****************************************/
  212.   1265                 
  213.   1266                if (THREAD_CAN_SCHED(td, self) && TDQ_CPU(self)->tdq_lowpri > pri &&
  214.   1267                    TDQ_CPU(cpu)->tdq_lowpri < PRI_MIN_IDLE &&
  215.   1268                    TDQ_CPU(self)->tdq_load <= TDQ_CPU(cpu)->tdq_load + 1) {
  216.   1269                        SCHED_STAT_INC(pickcpu_local);
  217.   1270                        cpu = self;
  218.   1271                } else
  219.   1272                        SCHED_STAT_INC(pickcpu_lowest);
  220.   1273                if (cpu != ts->ts_cpu)
  221.   1274                        SCHED_STAT_INC(pickcpu_migration);
  222.   1275                return (cpu);
  223.   1276        }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP