第13回 等待队列中操作函数

本文详细介绍了等待队列的构造与操作函数,包括优先度、优先顺序的概念,以及如何将任务插入等待队列、从等待队列中移除任务,并更新调度指针的相关逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第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给与的参数,而相对的优先顺位是在使用中的任务根据执行顺序给它排一个序.(例)如果相同优先度的任务存在一个以上的时候,以怎么样的顺序来执行任务呢,就是根据任务的优先顺序.

使任务进入到执行状态下的函数

执行这个函数,就可以使任务进入到可执行的状态.
具体来说就是把没有连接在等待队列的TCB追加到等待队列中去.这个函数的参数就是要进入可执行状态的任务的TCB.把TCB放入到等待队列中以后,这个时候为了更新p_schedtsk,有必要调用dispatch.如果指针p_schedtsk不变更的话,也就没有必要调用dispatch了.是否需要dispatch由这个函数的返回值决定.

※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传进来.
第235行:取得这个任务的优先度.
第237行:改变这个任务的状态为TS_RUNNABLE.

关于任务的状态
TCB内的任务状态是不区分执行中状态(RUNNING)和执行可能状态(READY)的.这两个状态的统称是RUNNABLE.关于等待中的表现将在以后的章节中说明.
TCB里面的状态是在task.h中定义的.

</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()的概要 (详细的日后再说明)
第一个参数是该优先级等待队列的地址,第二个参数是该任务TCB的成员变量task_queue的地址
在双链接队列的优先度的前面,连接着这个任务.双链接队列会构成环状,如果连接在双链接队列的前面的话,也就是连接在了最末尾. 如果任务连接在双链接队列中第一个任务以后的话就说明该任务有最高的优先级.
第240行:设置该优先度的位图.哪怕原来位图这个位已经是1了的话,也保存进去.
第242,243行:对于最高優先级的指针变量:p_schedtsk是否需要更新在这里做判定.
 
当p_schedtsk有必要更新的时候,也就是到目前为止没有任务能够执行的时候.(也就是p_schedtsk 等于NULL), 又或者是该任务的优先度比p_schedtsk的优先度要来得高的时候.(数字越小优先度越高) 在这种情况下,把最高优先度的指针p_schedtsk设置为该任务.
第244行:dispatch的标志位做为返回值返回.dispatch如果是禁止状态的话是FALSE,如果不是禁止的话就是TRUE. 也就是当p_schedtsk被更新而且dispatch不是被禁状态的话就返回一个TRUE.
ASP内核的列表的规则是以基础列表为内核, 在同一个优先度的内核里面, 再以FIFO为基础进行列表. 因此如果是加入新的TCB到等待队列的时候,就应该在该优先度的末尾的地方插入.

</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 }
 
第一个参数是该优先级等待队列的地址,第二个参数是该任务TCB的成员变量task_queue的地址



【図13-2 开始的状态】

第88行:  p_entry->p_prev = p_queue->p_prev;
把目前准备队列中的头任务的p_prev指针的值赋给准备插入任务的TCB的前指针: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;
把双向等待队列的前指针指向插入的任务.


任务从可执行状态到其他状态改变的函数

任务从可执行状态到其他状态的时候,在准备队列中把TCB消除.
这个函数的参数就是准备消除的TCB.
如果这个函数的参数也就是准备从准备队列中消除的任务是p_schedtsk的情况下,p_schedtsk是需要进行更新的. 准备消除的任务根据状态的不同也分场合处理.

</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的情况.
第262行: 准备消除的任务的TCB作为参数TCB *p_tcb在这里获取.
第264行: 取得该任务的优先度.
第265行:取得这个优先度所对应的地址,在任务所在的准备队列中.
第267行:从准备队列中消除TCB. 通过调用queue_delete()函数来实现.
关于queue_delete()将在以后说明.

STEP1
第268行: 和准备队列中消除的任务的优先级中,没有其他同优先级的任务的时候.
STEP2
第269行:清除位图.
STEP3
第270行:如果消除的任务是p_schedtsk的情况.
STEP4
第271行: 在准备队列中已经没有其他的任务的场合, p_schedtsk设置为NULL, 如果有其他的任务的情况下,找出最高优先度的任务.
※关于search_schedtsk()函数,将在以后进行说明.
STEP5
第272行:返回dispatch的标志.
第275行:从准备队列中出来的优先度的任务中,同优先度的任务还有的情况下.
STEP6
第276行: 如果消除的任务是p_schedtsk的情况下.
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;




接下来在函数make_non_runnable中,来看看在STEP4中调用到的函数search_schedtsk.

</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 }
 
第217行:调用primap_search()函数,来检索位图,在准备队列的所有TCB中,挑选出优先度最高的任务的函数.
第218行:在217行,准备队列里面的TCB中,最高优先度的任务被保存在schedpri的优先级中,所以ready_queue[schedpri].p_next的TCB就是我们在这个函数中所需要的.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值