- 论坛徽章:
- 0
|
本帖最后由 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线程调度的初始化函数:- /**************************************************************
- * Called from proc0_init() to setup the scheduler fields.
- 函数schedinit主要设置proc0和thread0调度方面的参数。
- ***********************/
- 1575 void
- 1576 schedinit(void)
- 1577 {
- 1578
- 1579
- 1580
- /**********************************************************************************
- * Set up the scheduler specific parts of proc0.
- 变量ticks: volatile int ticks;
- 系统启动以来经过的节拍数,当hardclock超时后,由函数hardclock_cnt来更新,
- 这里只需要明白该变量的含义。
-
- 进程0的相关数据结构都是静态定义的,下面的数据结构都是在proc0_init函数中
- 完成的,schedinit函数紧初始化一些调度方面的成员:
- static struct td_sched td_sched0;
- struct proc proc0;
- struct thread thread0
- struct proc对象的p_sched成员:
- struct p_sched *p_sched; /* (*) Scheduler-specific data. */
- 该成员一般不是用,ULE线程调度使用的是struct td_sched对象。
-
- struct td_sched对象的下面成员:
- int ts_ltick; /* Last tick that we were running on */
- int ts_ftick; /* First tick that we were running on */
- 其实,此时就是线程0在运行,将上面两个成员都设置为ticks。
- static int sched_slice = 12; /*Runtime of each thread before rescheduling */
- struct td_sched对象的ts_slice成员:
- ts_slice:线程剩余的时间片。
- **************************************/
- 1581
- 1582 proc0.p_sched = NULL; /* XXX */
- 1583 thread0.td_sched = &td_sched0;
- 1584 td_sched0.ts_ltick = ticks;
- 1585 td_sched0.ts_ftick = ticks;
- 1586 td_sched0.ts_slice = sched_slice;
- 1587 }
复制代码 2-[ULE线程调度-sched_setup函数]-该函数初始化一些ULE调度程序使用的数据结构:- 1355 static void
- 1356 sched_setup(void *dummy)
- 1357 {
- 1358 struct tdq *tdq;
- 1359
- /*****************************************************************
- * 宏TDQ_SELF始终返回当前cpu对应的struct tdq数据对象的地址。
- SMP系统:
- 数组tdq_cpu以cpu的logical id为索引。
- static struct tdq tdq_cpu[MAXCPU];
- #define TDQ_SELF() (&tdq_cpu[PCPU_GET(cpuid)])
- tdq的值为&tdq_cpu[0]
- 非SMP系统:
- static struct tdq tdq_cpu;
- #define TDQ_SELF() (&tdq_cpu)
- tdq的值为&tdq_cpu。
- SMP系统:调用sched_setup_smp函数处理,见下面的简要分析。
- 非SMP系统:调用tdq_setup函数处理,见下面的简要分析。
- *************************************************/
- 1360 tdq = TDQ_SELF();
- 1361 #ifdef SMP
- 1362 sched_setup_smp();
- 1363 #else
- 1364 tdq_setup(tdq);
- 1365 #endif
- 1366
- 1367 /* Add thread0's load since it's running. */
- 1368 TDQ_LOCK(tdq);
- 1369 thread0.td_lock = TDQ_LOCKPTR(TDQ_SELF());
- /* 函数tdq_load_add简单的递增struct tdq对象中tdq_load成员 */
- 1370 tdq_load_add(tdq, &thread0);
- 1371 tdq->tdq_lowpri = thread0.td_priority;
- 1372 TDQ_UNLOCK(tdq);
- 1373 }
复制代码 [ULE线程调度-tdq_setup函数]-该函数初始化相应struct tdq对象中的tdq_realtime队列,tdq_timeshare队列,tdq_idle队列:- 1312 static void
- 1313 tdq_setup(struct tdq *tdq)
- 1314 {
- 1315
- 1316 if (bootverbose)
- 1317 printf("ULE: setup cpu %d\n", TDQ_ID(tdq));
- /************************************************************************************
- * 1318-1320:
- 函数runq_init完成下面的工作:
- 1:将相应队列的数组rq_status.rqb_bits做清零处理。
- 2:初始化相应队列中的链表数组rq_queues。
- ***********************/
- 1318 runq_init(&tdq->tdq_realtime);
- 1319 runq_init(&tdq->tdq_timeshare);
- 1320 runq_init(&tdq->tdq_idle);
- 1321 snprintf(tdq->tdq_name, sizeof(tdq->tdq_name),
- 1322 "sched lock %d", (int)TDQ_ID(tdq));
- 1323 mtx_init(&tdq->tdq_lock, tdq->tdq_name, "sched lock",
- 1324 MTX_SPIN | MTX_RECURSE);
- 1325 #ifdef KTR
- 1326 snprintf(tdq->tdq_loadname, sizeof(tdq->tdq_loadname),
- 1327 "CPU %d load", (int)TDQ_ID(tdq));
- 1328 #endif
- 1329 }
复制代码 [ULE线程调度-sched_setup_smp函数]:- 1333 sched_setup_smp(void)
- 1334 {
- 1335 struct tdq *tdq;
- 1336 int i;
- 1337
- /*******************************************************************
- * struct cpu_group *cpu_top; CPU topology
- 函数smp_topo建立数据结构来描述cpu拓扑信息。
- ***********************************/
- 1338 cpu_top = smp_topo();
- /************************************************************************
- * 1339-1345:
- 调用函数tdq_setup初始化tdq_cpu[0],tdq_cpu[1],tdq_cpu[2]等等。
- tdq_cpu[0],tdq_cpu[1],tdq_cpu[2],tdq_cpu[3]的td_cg成员指向group[1].
- tdq_cpu[4],tdq_cpu[5],tdq_cpu[6],tdq_cpu[7]的td_cg成员指向group[2].
- *************************************/
- 1339 CPU_FOREACH(i) {
- 1340 tdq = TDQ_CPU(i);
- 1341 tdq_setup(tdq);
- 1342 tdq->tdq_cg = smp_topo_find(cpu_top, i);
- 1343 if (tdq->tdq_cg == NULL)
- 1344 panic("Can't find cpu group for %d\n", i);
- 1345 }
- /****************************************************************************
- * static struct tdq *balance_tdq;
- 1346:
- 这里将balance_tdq设置为&tdq_cpu[0],意味着由BSP来执行sched_balance函数。
-
- 1347:调用函数sched_balance。
- **************/
- 1346 balance_tdq = TDQ_SELF();
- 1347 sched_balance();
- 1348 }
复制代码 3-[ULE线程调度-函数sched_initticks]-该函数主要初始化一些ULE线程调度使用的变量:- 1379 static void
- 1380 sched_initticks(void *dummy)
- 1381 {
- 1382 int incr;
- 1383
- /***********************************************************************************************
- * 参考freebsd8.3中的实现:
- hardclock:频率为hz,每秒1000次,当发生两次APIC TIMER时钟中断时,才运行一次hardclock。
- statclock:频率为128,周期为1/128秒,即1/128秒运行一次statclock。
- profclock:频率为2*hz,即每秒2000次,周期为0.5毫秒,每秒运行2000次。
- 1384-1385:
- realstathz: statclock的真实频率。
- sched_slice: Runtime of each thread before rescheduling.
-
- 默认值如下:
- static int sched_slice = 12;
- static int realstathz = 127;
- 所以可以得到:
- realstathz=128;
- sched_slice=12;
- 从APIC TIMER时钟中断的处理来看,每个进程的时间片大概是12/128 = 0.09s,约0.1s即100ms。
- 1386:
- int hogticks;看了一下,该变量主要在I/O模块中使用,暂时不清楚其含义。
- *********************************************/
- 1384 realstathz = stathz ? stathz : hz;
- 1385 sched_slice = realstathz / 10; /* ~100ms */
- 1386 hogticks = imax(1, (2 * hz * sched_slice + realstathz / 2) /
- 1387 realstathz);
- 1388
- /***********************************************************************************
- * 1389-1400:
- 重新设置变量tickincr,该变量一般被加到线程的运行时间上。
- 默认值如下:
- static int tickincr = 8 << SCHED_TICK_SHIFT;
- #define SCHED_TICK_SHIFT 10
- hz(即1000)的二进制表示为1111101000
- ***********************/
- 1389 /*
- 1390 * tickincr is shifted out by 10 to avoid rounding errors due to
- 1391 * hz not being evenly divisible by stathz on all platforms.
- 1392 */
- 1393 incr = (hz << SCHED_TICK_SHIFT) / realstathz;
- 1394 /*
- 1395 * This does not work for values of stathz that are more than
- 1396 * 1 << SCHED_TICK_SHIFT * hz. In practice this does not happen.
- 1397 */
- 1398 if (incr == 0)
- 1399 incr = 1;
- 1400 tickincr = incr;
- 1401 #ifdef SMP
- 1402
- 1403
- 1404
- 1405
- /*********************************************************************
- * Set the default balance interval now that we know
- * what realstathz is.
- * 1406:
- 重新计算balance_interval,不过依然保持默认值128。
- 该变量在计算balance_ticks时使用,在balance_ticks为0时,将调用
- 函数sched_balance。
- 1407:
- 变量affinity由宏SCHED_AFFINITY使用。
- ******************************/
- 1406 balance_interval = realstathz;
- 1407 affinity = SCHED_AFFINITY_DEFAULT;
- 1408 #endif
- /****************************************************************************
- * static int sched_idlespinthresh = -1;
- 重新计算变量sched_idlespinthresh,当每cpu的idle线程被调度运行后,将
- 执行函数sched_idletd,该函数会使用这个变量。
- ******************************/
- 1409 if (sched_idlespinthresh < 0)
- 1410 sched_idlespinthresh = 2 * max(10000, 6 * hz) / realstathz;
- 1411 }
复制代码 |
|