RAW-OS学习之mutex源码注释与解析
上面刚开始任务task的优先级为11,占有锁mutex1,mutex2,mutex6,当有任务task1来获取它,这时阻塞在该锁上,task优先级继承task1变为10。又有任务task2来获取mutex2,task2被阻塞但是优先级不高于11不高于10,所以不继承。之后task获取mutex3,置顶优先级为9比task当前优先级高,所以需要拉高task优先级变为9,以此类推下去。
/*用于释放指定互斥锁*/
RAW_U16 raw_mutex_put(RAW_MUTEX *mutex_ptr)
{
LIST *block_list_head;
RAW_TASK_OBJ *tcb;
RAW_SR_ALLOC();
#if (RAW_MUTEX_FUNCTION_CHECK > 0)
if (mutex_ptr == 0) {
return RAW_NULL_OBJECT;
}
if (raw_int_nesting) {
return RAW_NOT_CALLED_BY_ISR;
}
#endif
if (mutex_ptr->common_block_obj.object_type != RAW_MUTEX_OBJ_TYPE) {
return RAW_ERROR_OBJECT_TYPE;
}
block_list_head = &mutex_ptr->common_block_obj.block_list;//获取阻塞链表头
RAW_CRITICAL_ENTER();
if (raw_task_active != mutex_ptr->mtxtsk) {//互斥锁只能由占有它的任务释放
RAW_CRITICAL_EXIT();
return RAW_MUTEX_NOT_RELEASE_BY_OCCYPY;
}
release_mutex(raw_task_active, mutex_ptr);//释放指定互斥锁,会还原任务前一阶段的优先级
if (is_list_empty(block_list_head)) {//没有阻塞任务则仅仅返回就行了
mutex_ptr->mtxtsk = 0;
RAW_CRITICAL_EXIT();
return RAW_SUCCESS;
}
/*以下是对存在阻塞任务的情况进行处理*/
//获取阻塞链表的第一个任务控制块(也是优先级别最高的)
tcb = list_entry(block_list_head->next, RAW_TASK_OBJ, task_list);
//唤醒该任务,但还不会调度
raw_wake_object(tcb);
/*填充该互斥锁的成员 */
mutex_ptr->mtxtsk = tcb;
mutex_ptr->mtxlist = tcb->mtxlist;
tcb->mtxlist = mutex_ptr;
if (mutex_ptr->policy == RAW_MUTEX_CEILING_POLICY) {//如果采用优先级置顶策略,还需要改变新占有锁任务优先级
if (tcb->priority > mutex_ptr->ceiling_prio) {
/*
********************************************************************************************************************
*如果tcb是就绪态的,则改变当前任务优先级后,更新就绪队列。如果是当前运行任务,则插到相应链表的最前端,
*否则插到相应链表的最后端
*
*
*如果tcb是带有阻塞的,而且必须是阻塞在该互斥锁上,函数才会调用mtx_chg_pri(tcb, old_pri)的有效部分,
*在mtx_chg_pri函数中
*
*对于采用优先级置顶的,因为所有可能获取该mutex的任务优先级都不会高于置顶优先级,所有不需要做什么。
*
*而对于采用优先级继承的,一旦有阻塞在该mutex的任务改变了优先级,则需要判断并按照需要改变当前占有锁任务的优先级:
*(情况①:当前占有锁任务优先级 低于 已被改变优先级的任务的新优先级,则需要把占有锁任务优先级拉高。
* 情况②:当前占有锁任务优先级 高于或等于 已被改变优先级的任务的新优先级,
* 则需要先判断占有锁任务优先级是否跟被改变优先级的任务的旧优先级一样,
* 如果一样,说明占有锁任务必须重新在该mutex的阻塞任务链表找到最高优先级,并赋给占有锁任务优先级)
*
*********************************************************************************************************************
*/
//占有锁任务已被更新并为就绪态,下面函数只是简单的改变优先级并更新就绪任务队列
change_internal_task_priority(tcb, mutex_ptr->ceiling_prio);
}
}
RAW_CRITICAL_EXIT();
//最后需要执行一次可能的调度
do_possible_sche();
return RAW_SUCCESS;
}
/*
******************************************************************************************
*释放指定任务的指定互斥锁,部分说明参见该函数后的流程图
*①relmtxcb为0,该函数相当于寻找整条阻塞任务链表中的最高优先级然后赋给指定任务tcb
*②relmtxcb不为0,把指定mutex从tcb的占有锁链表里释放,并把tcb的mtxlist中mutex的最高优先级
* 赋给tcb的当前优先级
******************************************************************************************
*/
static RAW_VOID release_mutex(RAW_TASK_OBJ *tcb, RAW_MUTEX *relmtxcb)
{
RAW_MUTEX *mtxcb, **prev;
RAW_U8 newpri, pri;
RAW_TASK_OBJ *first_block_task;
LIST *block_list_head;
/* (B) The base priority of task */
newpri = tcb->bpriority;
/* (A) The highest priority in mutex which is locked */
pri = newpri;
prev = &tcb->mtxlist;
while ((mtxcb = *prev) != 0) {
if (mtxcb == relmtxcb) {//如果当前提取的互斥锁与指定互斥锁相匹配,则把该互斥锁从链表里摘除
/* Delete self from list and tcb->mtxlist point to next*/
*prev = mtxcb->mtxlist;
continue;//重新回到while开始处,对剩余mutex进行处理
}
//以下switch是用于对每个匹配不到的mutex所涉及优先级的判断及处理
switch (mtxcb->policy) {
case RAW_MUTEX_CEILING_POLICY: //如果该互斥锁的策略为优先级置顶,则只需取出置顶优先级值
pri = mtxcb->ceiling_prio;
break;
case RAW_MUTEX_INHERIT_POLICY://如果使用优先级继承,则需要取出阻塞在该锁的第一个任务的优先级值
block_list_head = &mtxcb->common_block_obj.block_list;
if (!is_list_empty(block_list_head)) {
first_block_task = list_entry(block_list_head->next, RAW_TASK_OBJ, task_list);
pri = first_block_task->priority;
}
break;
default:
break;
}
//newpri永远保持当前对链表遍历过的mutex涉及优先级最高的那一个
if (newpri > pri) {
newpri = pri;
}
prev = &mtxcb->mtxlist;
}
//最后改变任务的优先级
if ( newpri != tcb->priority ) {
/* Change priority of lock get task */
change_internal_task_priority(tcb, newpri);
}
}
注:
从上面的源码及分析可以得知:在任务每次释放互斥锁后总是要更新任务的当前优先级为所以占有锁中涉及到的最高优先级,这样对应与获取互斥锁时,总是保持占有锁任务的优先级为最高值。从而实现了优先级的逐步还原。
/*
*****************************************************************************************************************
**********************************主要用在阻塞于mutex的任务被改变了优先级了**************************************
*
*对于采用优先级置顶的,因为所有可能获取该mutex的任务优先级都不会高于置顶优先级,所有不需要做什么。
*
*而对于采用优先级继承的,一旦有阻塞在该mutex的任务改变了优先级,则需要判断并按照需要改变当前占有锁任务的优先级:
*(情况①:当前占有锁任务优先级 低于 已被改变优先级的任务的新优先级,则需要把占有锁任务优先级拉高。
* 情况②:当前占有锁任务优先级 高于或等于 已被改变优先级的任务的新优先级,
* 则需要先判断占有锁任务优先级是否跟被改变优先级的任务的旧优先级一样,
* 如果一样,说明占有锁任务必须重新在该mutex的阻塞任务链表找到最高优先级,并赋给占有锁任务优先级)
*****************************************************************************************************************
*/
RAW_VOID mtx_chg_pri(RAW_TASK_OBJ *tcb, RAW_U8 oldpri)
{
RAW_MUTEX *mtxcb;
RAW_TASK_OBJ *mtxtsk;
mtxcb = (RAW_MUTEX *)(tcb->block_obj);
if (mtxcb->common_block_obj.object_type == RAW_MUTEX_OBJ_TYPE) {//必须是阻塞于互斥锁的任务才能进行该优先级转换
if (mtxcb->policy == RAW_MUTEX_INHERIT_POLICY) {//对优先级继承才需要处理
mtxtsk = mtxcb->mtxtsk;
if (mtxtsk->priority > tcb->priority) {//指定任务的优先级高于当前占有锁任务优先级
/*
********************************************************************************************************************
*如果mtxtsk是就绪态的,则改变当前任务优先级后,更新就绪队列。如果是当前运行任务,则插到相应链表的最前端,
*否则插到相应链表的最后端
*
*
*如果mtxtsk是带有阻塞的,而且必须是阻塞在该互斥锁上,函数才会调用mtx_chg_pri(mtxtsk, old_pri)的有效部分,
*在mtx_chg_pri函数中
*
*对于采用优先级置顶的,因为所有可能获取该mutex的任务优先级都不会高于置顶优先级,所有不需要做什么。
*
*而对于采用优先级继承的,一旦有阻塞在该mutex的任务改变了优先级,则需要判断并按照需要改变当前占有锁任务的优先级:
*(情况①:当前占有锁任务优先级 低于 已被改变优先级的任务的新优先级,则需要把占有锁任务优先级拉高。
* 情况②:当前占有锁任务优先级 高于或等于 已被改变优先级的任务的新优先级,
* 则需要先判断占有锁任务优先级是否跟被改变优先级的任务的旧优先级一样,
* 如果一样,说明占有锁任务必须重新在该mutex的阻塞任务链表找到最高优先级,并赋给占有锁任务优先级)
*
*********************************************************************************************************************
*/
//在这个函数下,mtxtsk只有两种情况,①就绪态 ②非mutex的阻塞
//所有该函数不会执行mtx_chg_pri的有效部分,也就是该函数在mtx_chg_pri下最多被执行一次。
change_internal_task_priority(mtxtsk, tcb->priority);
}
/*
*如果指定任务(被改变了优先级别的任务)的优先级 低于或等于 占有锁任务优先级,
*并且 占有锁任务优先级 等于 指定任务的旧优先级,
*则说明占有锁任务必须重新改变优先级为该mutex的最高优先级
*/
else if(mtxtsk->priority == oldpri) {//使用用户指定优先级来填充
/*
******************************************************************************************
*static RAW_VOID release_mutex(RAW_TASK_OBJ *tcb, RAW_MUTEX *relmtxcb)
*①relmtxcb为0,该函数相当于寻找整条阻塞任务链表中的最高优先级然后赋给指定任务tcb
*②relmtxcb不为0,把指定mutex从tcb的占有锁链表里释放,并把tcb的mtxlist中mutex的最高优先级
* 赋给tcb的当前优先级
******************************************************************************************
*/
release_mutex(mtxtsk, 0);
}
}
}
}
/*
*********************************************************************************************
*主要用在阻塞于mutex的任务超时了或异常或被删除了(改变了tcb优先级的不能使用该函数,而应该使用mtx_chg_pri)
*这时,需要更新该mutex的占有锁任务的优先级(把摘除了tcb后的mutex的最高优先级赋给占有锁任务)
**********************************************************************************************
*/
RAW_VOID mutex_state_change(RAW_TASK_OBJ *tcb)
{
RAW_MUTEX *mtxcb;
RAW_TASK_OBJ *mtxtsk;
mtxcb = (RAW_MUTEX *)(tcb->block_obj);
if (mtxcb->common_block_obj.object_type == RAW_MUTEX_OBJ_TYPE) {//必须是阻塞于mutex的
if (mtxcb->policy == RAW_MUTEX_INHERIT_POLICY) {//对优先级继承才需要处理
mtxtsk = mtxcb->mtxtsk;
/*the highest priority task blocked on this mutex may decrease priority so reset the mutex task priority*/
if(mtxtsk->priority == tcb->priority) {
release_mutex(mtxtsk, 0);
}
}
}
}
//释放指定任务当前占有的所有锁
RAW_VOID raw_task_free_mutex(RAW_TASK_OBJ *tcb)
{
RAW_MUTEX *mtxcb, *next_mtxcb;
RAW_TASK_OBJ *next_tcb;
LIST *block_list_head;
next_mtxcb = tcb->mtxlist;
//下面前两句用于遍历任务的占有mutex链表
while ((mtxcb = next_mtxcb) != 0) {
next_mtxcb = mtxcb->mtxlist;
//取出当前mutex的阻塞任务链表
block_list_head = &mtxcb->common_block_obj.block_list;
if (!is_list_empty(block_list_head)) {//对于有阻塞在该mutex的任务的处理
//取出阻塞任务链表的第一个任务块
next_tcb = list_entry(block_list_head->next, RAW_TASK_OBJ, task_list);
/*唤醒该任务,还不会调度*/
raw_wake_object(next_tcb);
/*改变mutex的拥有者*/
mtxcb->mtxtsk = next_tcb;
mtxcb->mtxlist = next_tcb->mtxlist;
next_tcb->mtxlist = mtxcb;
if (mtxcb->policy == RAW_MUTEX_CEILING_POLICY) {//如果是采用优先级置顶的话,还有修改任务优先级
if (next_tcb->priority > mtxcb->ceiling_prio) {
//next_tcb处于就绪态,所有只是做简单的更新
change_internal_task_priority(next_tcb, mtxcb->ceiling_prio);
}
}
}
else { //对于没有阻塞任务的mutex的处理
/*直接释放互斥锁 */
mtxcb->mtxtsk = 0;
}
}
}