第13回 等待队列中操作函数
原文链接:http://www.nces.is.nagoya-u.ac.jp/NEXCESS/blog/index.php?catid=22&blogid=4
在上一章节,我们已经对TCB的构造和管理TCB构造的等待队列的构造进行了说明【図13-1】.这次我们就来解说一下操作等待队列的函数.
【13-1 等待队列的构造】
在这里我们先说明一下优先度和优先顺序的概念.
優先度
为了控制任务和消息的处理顺序,通过Application给与其参数,在ASP内核中,是从1到本世纪末6,共分16个阶段.
優先順位
决定执行处理的顺序关系.
※优先度和优先顺序
优先度是根据Application给与的参数,而相对的优先顺位是在使用中的任务根据执行顺序给它排一个序.(例)如果相同优先度的任务存在一个以上的时候,以怎么样的顺序来执行任务呢,就是根据任务的优先顺序.
使任务进入到执行状态下的函数
※p_schedtsk
指向在等待队列中优先顺序最高的任务的指针.换句话说就是应该在执行中的任务.
让我们来看看代码把.
</kernel/task.c>
81 QUEUE ready_queue[TNUM_TPRI];
82
89 uint16_t ready_primap;
232 BOOL
233 make_runnable(TCB *p_tcb)
234 {
235 uint_t pri = p_tcb->priority;
236
237 p_tcb->tstat = TS_RUNNABLE;
238 LOG_TSKSTAT(p_tcb);
239 queue_insert_prev(&(ready_queue[pri]), &(p_tcb->task_queue));
240 primap_set(pri);
241
242 if (p_schedtsk == (TCB *) NULL || pri < p_schedtsk->priority) {
243 p_schedtsk = p_tcb;
244 return(dspflg);
245 }
246 return(FALSE);
247 }
第81行和第89行:任务的16个优先度.和之前说明的一样,为了管理等待状态队列所以才有了这16个优先度.QUEUE型数组和16位的bitmap变量就在这里申明.(【図13-1】参照)
第233行:想放入等待队列的TCB的指针通过参数TCB *p_tcb传进来.
关于任务的状态
TCB内的任务状态是不区分执行中状态(RUNNING)和执行可能状态(READY)的.这两个状态的统称是RUNNABLE.关于等待中的表现将在以后的章节中说明.
</KERNEL task.h>
67 #define TS_DORMANT 0x00U /* 休止状態 */
68 #define TS_RUNNABLE 0x01U /* 実行できる状態 */
69 #define TS_WAITING 0x02U /* 待ち状態 */
70 #define TS_SUSPENDED 0x04U /* 強制待ち状態 */
第239行:在双向连结的等待队列的最末尾的位置插入TCB.通过函数queue_insert_prev()来实现.
以下面的例子来说明.

如果插入的任务的优先度如果是2的话,优先度为2的等待队列中已经有了任务1和任务2了.根据ASP内核的调用规则,在同一优先度中的任务是通过FIFO的方式进行调用的.这个时候,优先度为2的执行可能状态的任务就以任务1,任务2,任务4这种顺序这种顺序排列在一起.
※queue_insert_prev()的概要 (详细的日后再说明)
</INCLUDE queue.h>
85 Inline void
86 queue_insert_prev(QUEUE *p_queue, QUEUE *p_entry)
87 {
88 p_entry->p_prev = p_queue->p_prev;
89 p_entry->p_next = p_queue;
90 p_queue->p_prev->p_next = p_entry;
91 p_queue->p_prev = p_entry;
92 }

【図13-2 开始的状态】
第88行: p_entry->p_prev = p_queue->p_prev;

第89行: p_entry->p_next = p_queue;
将等待队列的地址赋给即将插入任务的下一个任务的指针.

第90行: p_queue->p_prev->p_next = p_entry;

第91行: p_queue->p_prev = p_entry;
把双向等待队列的前指针指向插入的任务.

任务从可执行状态到其他状态改变的函数
</kernel/task.c>
261 BOOL
262 make_non_runnable(TCB *p_tcb)
263 {
264 uint_t pri = p_tcb->priority;
265 QUEUE *p_queue = &(ready_queue[pri]);
266
267 queue_delete(&(p_tcb->task_queue));
268 if (queue_empty(p_queue)) {
269 primap_clear(pri);
270 if (p_schedtsk == p_tcb) {
271 p_schedtsk = primap_empty() ? (TCB * ) NULL : search_schedtsk();
272 return(dspflg);
273 }
274 }
275 else {
276 if (p_schedtsk == p_tcb) {
277 p_schedtsk = (TCB *)(p_queue->p_next);
278 return(dspflg);
279 }
280 }
281 return(FALSE);
282 }
从268行开始的流程就如下面所示]図13-3】

【図13-3 p_schedtsk的更新】

【図13-4 同优先度下任务没有的场合】
STEP1~STEP5中, 从准备队列中消除任务的时候同优先级的任务没有的情况.如图13-4, 也就是任务3从准备队列中消除的情况. p_schedtsk指针指向了任务4.

【図13-5 同优先度的任务中如果有任务的场合】
STEP6~STEP9出准备队列的任务的优先度上,如果还有同优先度的其他任务的话, 【図13-5】的情况下就是任务1从准备队列中出来, p_schedtsk要更新到指向任务2的情况.
STEP1
STEP2
STEP3
STEP4
第271行: 在准备队列中已经没有其他的任务的场合, p_schedtsk设置为NULL, 如果有其他的任务的情况下,找出最高优先度的任务.
STEP5
第272行:返回dispatch的标志.
STEP6
STEP7
第277行: p_schedtsk指向连接该任务的下一个任务.
从准备队列中消除TCB的函数
接下来让我们来看看刚刚在make_non_runnable函数中调用的函数.
99 Inline void
100 queue_delete(QUEUE *p_entry)
101 {
102 p_entry->p_prev->p_next = p_entry->p_next;
103 p_entry->p_next->p_prev = p_entry->p_prev;
104 }

【図13-6 开始的状态】
第102行:p_entry->p_prev->p_next = p_entry->p_next;

第103行: p_entry->p_next->p_prev = p_entry->p_prev;

</KERNEL task.c>
212 TCB *
213 search_schedtsk(void)
214 {
215 uint_t schedpri;
216
217 schedpri = primap_search();
218 return((TCB *)(ready_queue[schedpri].p_next));
219 }
