- 论坛徽章:
- 0
|
SMP系统。
之前打算一个帖子简要描述一下如何将一个线程添加到cpu运行队列中,后来发现某些函数比较关键,放在一起的话会比较杂乱,这里就将其
独立出来。既然要将线程添加到cpu的运行队列中,那么就要选择一个合适的cpu的运行队列,这个工作相对来说比较重要,在选好cpu运行
队列后,后续的操作就会很简单了。
[ULE线程调度-sched_pickcpu函数]-执行fork系统调用创建一个新线程后,在将新线程添加到系统中某个cpu的运行队列之前,
必须为该线程选择一个合适的cpu运行队列,这个任务由函数sched_pickcpu来完成:- /***************************************************************
- * 这里将td指向的struct thred对象记为add_thread,相应的线程
- 记为addtd。
- 参数描述:
- td:这里为&add_thread。
- flags:传递给函数sched_pickcpu的一些标志
- 函数返回值:系统中某个cpu的logical cpu id,这里简记为cpux,
- cpux有可能和当前cpu是同一个cpu,挑选出合适的cpu也就等于
- 挑选出了合适的cpu运行队列。
- *******************************/
- 1182 static int
- 1183 sched_pickcpu(struct thread *td, int flags)
- 1184 {
- 1185 struct cpu_group *cg, *ccg;
- 1186 struct td_sched *ts;
- 1187 struct tdq *tdq;
- 1188 cpuset_t mask;
- 1189 int cpu, pri, self;
- 1190
- /*************************************************************
- * 1191:self为当前cpu的logical cpu id。
- 1193:ts指向和addtd相关联的struct td_sched对象。
- 1194:没有启用SMP,这里直接返回self。
- *************************************/
- 1191 self = PCPU_GET(cpuid);
- 1192 ts = td->td_sched;
- 1193 if (smp_started == 0)
- 1194 return (self);
- 1195
- 1196
- /**********************************************************************************
- * 1198-1199:
- Don't migrate a running thread from sched_switch().
- #define SRQ_OURSELF 0x0002 It is ourself (from mi_switch).
- #define THREAD_CAN_MIGRATE(td) ((td)->td_pinned == 0)
- struct thread对象的td_pinned成员,函数sched_pin会递增该成员,
- 如果该成员非零,就表示线程addtd不能被迁移到其它cpu的运行队列中:
- int td_pinned;Temporary cpu pin count.
- struct td_sched对象的ts_cpu成员:
- ts_cpu: 该成员的值为系统中某个cpu的logical cpu id,和该struct td_sched对象
- 相关联的thread将被添加到ts_cpu的运行队列中.
- 正常情况下,struct thread对象的td_pinned成员为0,所以只有当参数flags
- 设置了SRQ_OURSELF标志时,此时就直接返回struct td_sched对象的ts_cpu成员。
- ************************************************/
- 1197
- 1198 if ((flags & SRQ_OURSELF) || !THREAD_CAN_MIGRATE(td))
- 1199 return (ts->ts_cpu);
- 1200
- 1201
- 1202
- /******************************************************************************************
- * Prefer to run interrupt threads on the processors that generate
- * the interrupt.
- struct thread对象的td_cpuset成员:
- td_cpuset:cpu掩码的集合,即CPU affinity sets,一般情况下该字段的值从父进程
- 继承,除非调用cpuset系统调用进行修改。td_cpuset->cs_mask为一个类型为
- cpuset_t类型的变量,线程thread只能被添加到td_cpuset->cs_mask
- 中设置为1的bit位对应的cpu的运行队列中。
- td_intr_nesting_level; Interrupt recursion. 中断嵌套数。
- #define PRI_MAX_ITHD (PRI_MIN_REALTIME - 1) 47
- #define THREAD_CAN_SCHED(td, cpu) CPU_ISSET((cpu), &(td)->td_cpuset->cs_mask)
- 宏THREAD_CAN_SCHED检查td_cpuset->cs_mask中logical cpu id为cpu对应的bit位是否被
- 设置为1,如果为1,THREAD_CAN_SCHED宏的值就为1,否则为零。
- 当下面的条件都满足时,就执行1205-1213之间的代码:
- 条件1:addtd的优先级小于或者等于47,即addtd属于中断线程。
- 条件2:THREAD_CAN_SCHED(td, ts->ts_cpu)宏的值为1,即td可以被添加到logical cpu id
- 为ts->ts_cpu的运行队列中。
- 条件3:curthread->td_intr_nesting_level非零,表示当前cpu正在处理中断。
- 条件4:ts_cpu和self不是同一个cpu。
- pickcpu_affinity, "Picked cpu based on affinity"
- 此时将ts_cpu设置为self,这部分代码和中断线程相关,留作以后再详细将其分析。
- *****************************************************/
- 1203
- 1204 pri = td->td_priority;
- 1205 if (td->td_priority <= PRI_MAX_ITHD && THREAD_CAN_SCHED(td, self) &&
- 1206 curthread->td_intr_nesting_level && ts->ts_cpu != self) {
- 1207 SCHED_STAT_INC(pickcpu_intrbind);
- 1208 ts->ts_cpu = self;
- 1209 if (TDQ_CPU(self)->tdq_lowpri > pri) {
- 1210 SCHED_STAT_INC(pickcpu_affinity);
- 1211 return (ts->ts_cpu);
- 1212 }
- 1213 }
- 1214
- 1215
- 1216
- /***************************************************************************************************
- * If the thread can run on the last cpu and the affinity has not
- * expired or it is idle run it there.
- struct tdq对象的tdq_lowpri成员:
- tdq_lowpri:tdq_lowpri成员保存了相应CPU运行队列中线程的最高优先级(数值最小)。
- struct thread对象的td_cpuset成员:
- td_cpuset:cpu掩码的集合,即CPU affinity sets,一般情况下该字段的值从父进程
- 继承,除非调用cpuset系统调用进行修改。td_cpuset->cs_mask为一个类型为
- cpuset_t类型的变量,线程thread只能被添加到td_cpuset->cs_mask
- 中设置为1的bit位对应的cpu的运行队列中。
- struct td_sched对象的ts_cpu成员:
- ts_cpu: 该成员的值为系统中某个cpu的logical cpu id,和该struct td_sched对象
- 相关联的thread将被添加到ts_cpu的运行队列中.
-
- #define PRI_MIN_IDLE (224)
- #define THREAD_CAN_SCHED(td, cpu) CPU_ISSET((cpu), &(td)->td_cpuset->cs_mask)
- 宏THREAD_CAN_SCHED检查td_cpuset->cs_mask中logical cpu id为cpu对应的bit位是否被
- 设置为1,如果为1,THREAD_CAN_SCHED宏的值就为1,否则为零。
-
- 1218:tdq指向ts_cpu对应的运行队列,这里tdq的值为&tdq_cpu[ts->ts_cpu]。
- 1219:cg指向group[1]或者group[2],这里假设cg指向group[1]。
- 1220-1234:仅当下面的条件都为真时,才执行这个if语句:
- 条件1:THREAD_CAN_SCHED(td, ts->ts_cpu)宏的值为1,即td可以被添加到logical cpu id
- 为ts->ts_cpu的运行队列中。
- 条件2:tdq_lowpri >= PRI_MIN_IDLE为真,表示运行队列tdq上的线程的是IDLE线程。
- 条件3:SCHED_AFFINITY宏的值为1。
- 宏SCHED_AFFINITY的值为1:可以理解为线程td从ts->ts_cpu上被替换,但是被替换的时间
- 不是很长,线程td的相关数据可能还保存在ts->ts_cpu的高速缓存中,还没有被刷新。
- 宏SCHED_AFFINITY的值为0:可以理解为线程td从ts->ts_cpu上被替换,但是被替换的时间
- 过长,线程td的相关数据可能已经不在ts->ts_cpu的高速缓存中,相关的高速缓存已经被刷新。
-
- 当上面三个条件都为真时,因为group[1]和group[2]的cg_flags成员都为0,此时:
-
- 一般情况下都会执行1228-1233之间的代码,递增一个每cpu变量pickcpu_idle_affinity,
- 返回ts->ts_cpu,宏SCHED_AFFINITY的值为1可以很清楚的说明为什么要选择ts_cpu。
- pickcpu_idle_affinity, "Picked idle cpu based on affinity"
- **********************************/
- 1217
- 1218 tdq = TDQ_CPU(ts->ts_cpu);
- 1219 cg = tdq->tdq_cg;
- 1220 if (THREAD_CAN_SCHED(td, ts->ts_cpu) &&
- 1221 tdq->tdq_lowpri >= PRI_MIN_IDLE &&
- 1222 SCHED_AFFINITY(ts, CG_SHARE_L2)) {
- 1223 if (cg->cg_flags & CG_FLAG_THREAD) {
- 1224 CPUSET_FOREACH(cpu, cg->cg_mask) {
- 1225 if (TDQ_CPU(cpu)->tdq_lowpri < PRI_MIN_IDLE)
- 1226 break;
- 1227 }
- 1228 } else
- 1229 cpu = INT_MAX;
- 1230 if (cpu > mp_maxid) {
- 1231 SCHED_STAT_INC(pickcpu_idle_affinity);
- 1232 return (ts->ts_cpu);
- 1233 }
- 1234 }
- /******************************************************************************************
- * Search for the last level cache CPU group in the tree.
- * Skip caches with expired affinity time and SMT groups.
- * Affinity to higher level caches will be handled less aggressively.
-
- 宏SCHED_AFFINITY的值为1:可以理解为线程td从ts->ts_cpu上被替换,但是被替换的时间
- 不是很长,线程td的相关数据可能还保存在ts->ts_cpu的高速缓存中,还没有被刷新。
- 宏SCHED_AFFINITY的值为0:可以理解为线程td从ts->ts_cpu上被替换,但是被替换的时间
- 过长,线程td的相关数据可能已经不在ts->ts_cpu的高速缓存中,相关的高速缓存已经被刷新。
-
- 1240-1246:
- 检查cpu拓扑图,执行完之间的代码后,ccg或者为group[1],或者为cpu_top。
-
- 1247-1248:重新设置cg。
- *********************************************/
- 1240 for (ccg = NULL; cg != NULL; cg = cg->cg_parent) {
- 1241 if (cg->cg_flags & CG_FLAG_THREAD)
- 1242 continue;
- 1243 if (!SCHED_AFFINITY(ts, cg->cg_level))
- 1244 continue;
- 1245 ccg = cg;
- 1246 }
- 1247 if (ccg != NULL)
- 1248 cg = ccg;
- 1249 cpu = -1;
- 1250
- /*********************************************************************************************************
- * Search the group for the less loaded idle CPU we can run now.
- 1251-1261:
- 根据相应的条件,以不同的参数调用函数sched_lowest,该函数返回cpu运行队列负载最低
- 的cpu的logical cpu id,从这里可以看出,在将一个线程添加到cpu运行队列时,总是添加
- 到负载最低的cpu的运行队列中。
- sched_lowest函数的描述请参见之前" freebsd9.2-ULE线程调度-保持cpu运行队列间的平衡-sched_balance函数 "
- 中的描述。
- *****************/
- 1251 mask = td->td_cpuset->cs_mask;
- 1252 if (cg != NULL && cg != cpu_top &&
- 1253 CPU_CMP(&cg->cg_mask, &cpu_top->cg_mask) != 0)
- 1254 cpu = sched_lowest(cg, mask, max(pri, PRI_MAX_TIMESHARE),
- 1255 INT_MAX, ts->ts_cpu);
- 1256 /* Search globally for the less loaded CPU we can run now. */
- 1257 if (cpu == -1)
- 1258 cpu = sched_lowest(cpu_top, mask, pri, INT_MAX, ts->ts_cpu);
- 1259 /* Search globally for the less loaded CPU. */
- 1260 if (cpu == -1)
- 1261 cpu = sched_lowest(cpu_top, mask, -1, INT_MAX, ts->ts_cpu);
- 1262 KASSERT(cpu != -1, ("sched_pickcpu: Failed to find a cpu."));
- 1263
- 1264
- /*****************************************************************************************************
- * Compare the lowest loaded cpu to current cpu.
- 执行到这里的话,函数sched_lowest已经成功选择了一个cpu,并将其
- logical cpu id保存在变量cpu中。
- 相关的每cpu变量:
- pickcpu_local, "Migrated to current cpu"
- pickcpu_lowest, "Selected lowest load"
- pickcpu_migration, "Selection may have caused migration"
- struct tdq对象的:
- tdq_lowpri:tdq_lowpri成员保存了相应CPU运行队列中线程的最高优先级(数值最小)。
- #define PRI_MIN_IDLE (224)
- 当下面的条件都满足时,执行1266-1271之间的语句:
- 条件1:THREAD_CAN_SCHED(td, ts->ts_cpu)宏的值为1,即addtd可以被添加到logical cpu id
- 为self的运行队列中。
- 条件2:tdq_cpu[self]中tdq_lowpri大于pri,当前在self上运行的线程优先级比addtd的优先级低。
- 条件3:tdq_cpu[cpu]中tdq_lowpri小于224,即不属于IDLE线程。
- 条件4:运行队列tdq_cpu[self]的负载比运行队列tdq_cpu[cpu]的负载低,这里加1是因为addtd要被添加到
- 运行队列tdq_cpu[cpu]中,所以其负载要加1.
- 此时递增pickcpu_local变量,将cpu重新设置为self。
- 如果上面4个条件任何一个为假,就执行1271-1272之间的代码,此时递增pickcpu_lowest变量。
- 1273-1274:检查cpu和ts_cpu是否相等,如果不相等,就表示addtd要被添加到另外一个cpu的
- * 运行队列中,同时递增pickcpu_migration变量。
- ****************************************/
- 1265
- 1266 if (THREAD_CAN_SCHED(td, self) && TDQ_CPU(self)->tdq_lowpri > pri &&
- 1267 TDQ_CPU(cpu)->tdq_lowpri < PRI_MIN_IDLE &&
- 1268 TDQ_CPU(self)->tdq_load <= TDQ_CPU(cpu)->tdq_load + 1) {
- 1269 SCHED_STAT_INC(pickcpu_local);
- 1270 cpu = self;
- 1271 } else
- 1272 SCHED_STAT_INC(pickcpu_lowest);
- 1273 if (cpu != ts->ts_cpu)
- 1274 SCHED_STAT_INC(pickcpu_migration);
- 1275 return (cpu);
- 1276 }
复制代码 |
|