- 论坛徽章:
- 0
|
本帖最后由 71v5 于 2014-07-02 03:39 编辑
[函数choosethread]-当要选择一个线程来运行时,就会调用choosethread函数,比如之前描述的mini_switch函数就会调用该函数:- /*******************************************************************
- * Select the thread that will be run next.
- ***************************/
- 153 struct thread *
- 154 choosethread(void)
- 155 {
- 156 struct thread *td;
- 157
- /********************************************************************
- * 函数sched_choose选择一个线程,见下面的简要分析。
- **************************/
- 158 retry:
- 159 td = sched_choose();
- 160
- /***********************************************************************************
- * If we are in panic, only allow system threads,
- * plus the one we are running in, to be run.
- const char *panicstr;bsd内核中变量panicstr的说明如下:
- Variable panicstr contains argument to first call to panic; used as flag
- to indicate that the kernel has already called panic.
- 说明如果panicstr非空,就表明已经调用了panic函数。
- 对于TDF_INPANIC标志:
- #define TDF_INPANIC 0x00000002 Caused a panic, let it drive crashdump.
- 如果线程td调用了panic函数,就会设置其TDF_INPANIC标志。
- 对于P_SYSTEM标志:
- 只有系统进程,才会设置该标志,内核初始化阶段创建的进程都属于系统进程,比如
- pageout daemon,vm daemon,buffer daemon等等。
- bsd内核中对于函数panic的说明:
- Panic is called on unresolvable fatal errors. It prints "panic: mesg",
- and then reboots. If we are called twice, then we avoid trying to sync
- the disks as this often leads to recursive panics
-
- 这里为了简化讨论,假设线程td没有调用panic函数,所以:
- 如果此时已经调用过panic函数,那么内核只会选择系统进程来运行。
-
- ********************************************/
- 165 if (panicstr && ((td->td_proc->p_flag & P_SYSTEM) == 0 &&
- 166 (td->td_flags & TDF_INPANIC) == 0)) {
- 167 /* note that it is no longer on the run queue */
- 168 TD_SET_CAN_RUN(td);
- 169 goto retry;
- 170 }
- 171
- /***********************************************************************
- * 此时线程td将要被调度运行,重新设置其状态。
- #define TD_SET_RUNNING(td) (td)->td_state = TDS_RUNNING
- ********************************/
- 172 TD_SET_RUNNING(td);
- 173 return (td);
- 174 }
复制代码 这里再来看看struct tdq对象中下面三个成员:- /*****************************************************************************
- * 从sched_add函数的实现可以看出:
- #define PRI_MIN_BATCH 152
- #define PRI_MAX_BATCH 223
- tdq_realtime队列中线程的优先级小于PRI_MIN_BATCH。
- tdq_timeshare队列中线程的优先级在区间[PRI_MIN_BATCH,PRI_MAX_BATCH]中。
- tdq_idle队列中线程的优先级在区间(223,255]中。
-
- 从这里可以得出:
- tdq_realtime队列中线程的优先级最高。
- tdq_timeshare队列中线程的优先级居中。
- tdq_idle队列中线程的优先级最低。
-
- ****************************************************/
- struct runq tdq_realtime; /* real-time run queue. */
- struct runq tdq_timeshare; /* timeshare run queue. */
- struct runq tdq_idle; /* Queue of IDLE threads. */
复制代码 [struct rqbits对象]:- /**************************************************************************************
- * Bit array which maintains the status of a run queue. When a queue is
- * non-empty the bit corresponding to the queue number will be set.
- typedef u_int32_t rqb_word_t;
- #define RQB_LEN (2) Number of priority status words.
- 对于链表rq_queues[i],位图rqb_bits中的第i个bit位用来描述链表rq_queues[i]是否为空:
- 第i个bit位为0:链表rq_queues[i]为空
- 第i个bit位非零:链表rq_queues[i]非空
- ***********************************/
- 51 */
- 52 struct rqbits {
- 53 rqb_word_t rqb_bits[RQB_LEN];
- 54 };
复制代码 [struct runq对象]:- /*******************************************************************************************
- * Run queue structure. Contains an array of run queues on which processes
- * are placed, and a structure to maintain the status of each queue.
- #define RQ_NQS (64) Number of run queues.
- #define RQ_PPQ (4) Priorities per queue.
-
- 成员描述:
- rq_status:
- 类型为struct rqbits,见上面对struct rqbits类型的描述,这里为了分析方便,按下面的方式
- 引用rq_status成员中的bit位:
- rq_status.rqb_bits->num,引用第num个bit位。
- rq_queues:
- 一个数组,数组元素的数目为64,每个数组元素都是一个链表的head,链接了相应优先级的线程,
- 对于数组索引a,b,如果a大于b,那么链表rq_queues[a]上线程的优先级比链表rq_queues[b]
- 上线程的优先级低。
- 在一个特定的队列内,假设在队列tdq_realtime中,只需要确定rq_status.rqb_bits中第一个
- 设置为1的bit位,假设为rq_status.rqb_bits->20,那么就直接从链表rq_queues[20]上fetch
- 第一个元素,该元素就是优先级最高的线程(不一定准确,因为sched_add函数将线程添加到
- 运行队列时,同一个链表上的元素不会再次排序)。
-
- sched_add函数在将线程td添加到tdq_realtime队列或者tdq_timeshare队列或者tdq_idle队列
- 中时,会根据线程td的优先级(struct thread对象的td_priority成员)计算一个"新的优先级",
- "新的优先级"被限制在区间[0,64]中,这个"新的优先级"就作为数组rq_queues的索引。
- "新的优先级"这个用语可能不是很严谨,不过能说明问题就行。
- 假设有4个线程thread0,thread1,thread2,thread3,都将被添加到tdq_realtime队列中,
- 原始优先级分别为self_pri0,self_pri1,self_pri2,self_pri3,经过计算后"新的优先级"
- 分别为new_pri0,new_pri1,new_pri2,new_pri3,并且new_pri0,new_pri1,new_pri2,new_pri3
- 各不相同,那么:
- thread0将被添加到链表rq_queues[new_pri0]。
- thread1将被添加到链表rq_queues[new_pri1]。
- thread2将被添加到链表rq_queues[new_pri2]。
- thread3将被添加到链表rq_queues[new_pri3]。
- rq_status.rqb_bits->new_pri0,rq_status.rqb_bits->new_pri1,
- rq_status.rqb_bits->new_pri2,rq_status.rqb_bits->new_pri3,
- 被设置为1。
- *******************************************/
- 60 struct runq {
- 61 struct rqbits rq_status;
- 62 struct rqhead rq_queues[RQ_NQS];
- 63 };
复制代码 下面一张简图清晰的展示了struct runq对象,runq_*函数都是围绕struct runq对象展开:
[ULE线程调度-sched_choose函数]:- [ULE线程调度-sched_choose函数]:
- /***************************************************************************************
- * Choose the highest priority thread to run. The thread is removed from
- * the run-queue while running however the load remains. For SMP we set
- * the tdq in the global idle bitmask if it idles here.
- *
- 这里假设当前正在执行sched_choose函数的cpu的logical cpu id为5.
-
- 那么函数sched_choose在依次从tdq_cpu[5]包含的tdq_realtime队列,tdq_timeshare队列,
- tdq_idle队列三个运行队列中选择一个优先级最高的线程来运行,同时返回该线程对应的
- struct thread对象。
- ****************************/
- 2273 struct thread *
- 2274 sched_choose(void)
- 2275 {
- 2276 struct thread *td;
- 2277 struct tdq *tdq;
- 2278
- /******************************************************************************************
- * 2279:宏TDQ_SELF获取当前cpu对应的struct tdq对象,这里为&tdq_cpu[5]。
- 2281:调用函数tdq_choose从tdq_cpu[5]包含的tdq_realtime队列,tdq_timeshare队列,
- tdq_idle队列三个运行队列中选择一个优先级最高的线程,函数tdq_choose简要描述如下。
- 2282-2285,如果函数tdq_choose函数成功fetch一个线程,此时:
- 2283-函数tdq_runq_rem完成下面的任务:
- 1:递减tdq_cpu[5]中科迁移线程的数目。
- 2:将线程td从链表rq_queues[td->td_rqindex]删除。
- 3:如果链表rq_queues[td->td_rqindex]为空,就将rq_status.rqb_bits->d_rqindex设置为0.
- 2284-更新tdq_lowpri,tdq_lowpri成员保存的是当前在cpu5上运行线程的优先级(数值小)。
- ****************************/
- 2279 tdq = TDQ_SELF();
- 2280 TDQ_LOCK_ASSERT(tdq, MA_OWNED);
- 2281 td = tdq_choose(tdq);
- 2282 if (td) {
- 2283 tdq_runq_rem(tdq, td);
- 2284 tdq->tdq_lowpri = td->td_priority;
- 2285 return (td);
- 2286 }
- /**************************************************************************************************
- * 如果执行这里,就表示tdq_cpu[5]包含的tdq_realtime队列,tdq_timeshare队列,tdq_idle队列
- 三个运行队列都为空。
-
- #define PRI_MAX_IDLE (PRI_MAX) 255
-
- struct tdq对象的tdq_lowpri成员:
- tdq_lowpri成员保存的是相应CPU运行队列中线程的最高优先级(数值小)。
- 2287:此时将tdq_lowpri成员设置为PRI_MAX_IDLE,意味着将要选择每cpu的idle线程来运行。
- 在系统初始化阶段,idle_setup函数会为系统中每个cpu创建一个对应的idle线程,
- 描述相应idle线程的struct thread对象的地址保存在相应cpu的struct pcpu对象的
- pc_idlethread成员中,并且将idle线程的优先级设置为PRI_MAX_IDLE。
- 这里的话,__pcpu[5].pc_idlethread指向描述cpu5的idle线程的struct thread对象。
- 2288:宏PCPU_GET获取__pcpu[5].pc_idlethread,并返回。
- 从这里看出,只有当相应cpu的tdq_realtime队列,tdq_timeshare队列,tdq_idle队列三个队列都为空
- 时,才会选择每cpu的ilde线程来运行。
- ***********************************************************/
- 2287 tdq->tdq_lowpri = PRI_MAX_IDLE;
- 2288 return (PCPU_GET(idlethread));
- 2289 }
复制代码 [ULE线程调度-tdq_choose函数]:- /***************************************************************************
- * Pick the highest priority task we have and return it.
- 参数描述:
- tdq:这里为&tdq_cpu[5]
-
- ******************************************/
- 1282 static struct thread *
- 1283 tdq_choose(struct tdq *tdq)
- 1284 {
- 1285 struct thread *td;
- 1286
- /*************************************************************************************
- * 1288-1290:
- 首先检查tdq_realtime队列,函数runq_choose从rq_status.rqb_bits的bit0开始按序检查,
- 如果rq_status.rqb_bits->num为1,就从链表rq_queues[num]上fetch一个线程,并返回
- 该线程对应的struct thread对象。
- *******************************/
- 1287 TDQ_LOCK_ASSERT(tdq, MA_OWNED);
- 1288 td = runq_choose(&tdq->tdq_realtime);
- 1289 if (td != NULL)
- 1290 return (td);
- /************************************************************************************
- * 1291-1298:
- 如果执行到这里,就表示tdq_realtime队列中没有线程,此时检查tdq_timeshare队列,
- 函数runq_choose_from从rq_status.rqb_bits->tdq_ridx开始安序检查,如果
- rq_status.rqb_bits->num为1,就从链表rq_queues[num]上fetch一个线程,并返回
- 该线程对应的struct thread对象。
- 函数runq_choose_from比函数runq_choose多了一个参数,在函数runq_choose_from中,
- 这第二个参数idx表示将从rq_status.rqb_bits->idx开始检查,因为tdq_timeshare队列的
- 类型为timeshare,这个额外的参数能够防止其它线程被饿死。
- ******************************************/
- 1291 td = runq_choose_from(&tdq->tdq_timeshare, tdq->tdq_ridx);
- 1292 if (td != NULL) {
- 1293 KASSERT(td->td_priority >= PRI_MIN_BATCH,
- 1294 ("tdq_choose: Invalid priority on timeshare queue %d",
- 1295 td->td_priority));
- 1296 return (td);
- 1297 }
- /************************************************************************************
- * 1298-1303:
- 如果执行到这里,就表示tdq_realtime队列,tdq_timeshare队列两个队列中都没有线程,
- 此时检查tdq_idle队列,函数runq_choose从rq_status.rqb_bits的bit0开始按序检查,
- 如果rq_status.rqb_bits->num为1,就从链表rq_queues[num]上fetch一个线程,并返回
- 该线程对应的struct thread对象。
- ******************************************/
- 1298 td = runq_choose(&tdq->tdq_idle);
- 1299 if (td != NULL) {
- 1300 KASSERT(td->td_priority >= PRI_MIN_IDLE,
- 1301 ("tdq_choose: Invalid priority on idle queue %d",
- 1302 td->td_priority));
- 1303 return (td);
- 1304 }
- 1305
- /************************************************************************************
- * 如果执行到这里,就表示tdq_realtime队列,tdq_timeshare队列,tdq_idle队列三个队列
- 中都没有线程,此时返回NULL。
- **************************************/
- 1306 return (NULL);
- 1307 }
复制代码 |
|