互斥体,维基百科中交互斥锁。其定义是这样的:互斥锁(英语:英语:Mutual exclusion,缩写 Mutex)是一种用于多线程编程中,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制。该目的通过将代码切片成一个一个的临界区域(critical section)达成。临界区域指的是一块对公共资源进行访问的代码,并非一种机制或是算法。一个程序、进程、线程可以拥有多个临界区域,但是并不一定会应用互斥锁。
关于互斥体,最经典的有两个例子。一个是串口的使用,另一个是打印机的使用。
串口在发送字符串的时候,是不能被中断的。比如发送一个字符串abcdefg,如光刚发完abc时候搞优先级的任务来了,要使用串口发送123456789。这时候如果高优先级任务抢占了低优先级任务对MCU的控制,那么接收端收到的字符串将会是这样的:abc123456789defg。接收到的数据被中断搞混乱了。
再有就是打印机,当低优先级任务在打印时候,刚打印完半页纸,搞优先级任务来了。下半页纸上打出来的将是高优先级任务的数据。整个一张纸上既不是高优先级的东西也不是低优先级任务的东西。
于是就出来了一个互斥体的概念。我使用资源的时候,设置个互斥体将资源锁住,表明正在使用的资源不能被抢断。不管你就绪的任务优先级有多高都得等我用完。
uTenux的互斥体就是一个资源锁。用于确保使用资源的任务不被抢断。
使用优先级继承或者优先级置顶的方法来避免优先级反转。这两种方式,用户不用管,OS已经完全搞定。用户只需要在创建互斥体的时候,告诉OS使用哪种方法即可。
uTenux的互斥体都有一个状态和一个等待锁定互斥体的任务队列
问题:关于创建互斥体函数中的PRI ceilpri 互斥体顶置优先级
函数说明中只有这么一句:仅当TA_CEILING被设置时,ceilpri才有效,才可以设置互斥体的顶置优先级。即使用优先级顶置协议时ceilpri才有效。
我的问题是,ceilpri究竟该怎样设置?设置成所有使用这个互斥他中的最高优先级吗?如果设置的低了会怎样?比如有一个优先级为10的任务使用这个互斥他,而我将ceilpri设置成15.
解答:实验结果是任务的优先级不变化,仍旧保持原有的较高优先级
我想,这里这么用虽然不对,但是也不至于导致系统崩溃。保持原有优先级,虽然可能导致优先级反转的问题,但是系统依旧能够运行。这个算是做软件时候的一个BUG了。
【实验描述】
1、首先创建任务TaskA、TaskB和互斥体MtxID。TaskA和 TaskB优先级相同,都是20。然后启动TaskA。
2、TaskA中首先启动TaskB。两个任务优先级相同,所以此时TaskA具有较高的优先权。TaskB进入就绪等待状态。TaskA继续执行,进入循环体。
3、在循环体中,TaskA首先输出自己当前的优先级,然后锁定Mtx并延时一段时间。最后解锁互斥体。
4、当TaskA解锁互斥体之后,TaskB获得优先权,开始执行。同样TaskB也是先输出优先级然后锁定Mtx之后再次输出优先级。最后解锁互斥体。
5、这个实验使用了支持优先级顶置协议,即任务锁定互斥体之后会将自己的优先级提升到创建互斥体时候设置的级别。
6、使用优先级继承协议在本次实验也有验证,结果是当低优先级任务获得mtx之后并不会被立即提升的优先级,而是当有较高优先级任务请求这个mtx时才会提升优先级。这个实验我在代码中也有
【实验代码和输出】
#include "MutexSample.h" void MtxSampleTaskA(W stacd,VP exinf); void MtxSampleTaskB(W stacd,VP exinf); void PutTaskPriority(ID taskid); static ID TaskID_A; static ID TaskID_B; static ID MtxID; ER MtxSample( void) { T_CTSK ctsk; T_CMTX cmtx; tm_putstring((UB*)"开始创建Mtx\n"); cmtx.exinf = NULL; cmtx.mtxatr = TA_TFIFO | TA_CEILING; cmtx.ceilpri = 15; MtxID = tk_cre_mtx(&cmtx); tm_putstring((UB*)"开始创建TaskA\n"); ctsk.bufptr = (VP*)NULL; ctsk.exinf = NULL; ctsk.itskpri = 20; ctsk.stksz = 512; ctsk.task = MtxSampleTaskA; ctsk.tskatr = TA_HLNG | TA_RNG0; TaskID_A = tk_cre_tsk(&ctsk); ctsk.itskpri = 20; tm_putstring((UB*)"开始创建TaskB\n"); ctsk.task = MtxSampleTaskB; TaskID_B = tk_cre_tsk(&ctsk); tm_putstring((UB*)"启动TaskA\n"); tk_sta_tsk(TaskID_A,5); return E_OK; } void MtxSampleTaskA(W stacd,VP exinf) { if(E_OK == tk_sta_tsk(TaskID_B,0)) { tm_putstring((UB*)"Task A start TaskB sucessfully\n"); } while(1) { PutTaskPriority(TaskID_A); tk_loc_mtx(MtxID,-1); tm_putstring((UB*)"TaskA locked Mtx\n"); PutTaskPriority(TaskID_A); Delay(0x1000000); tk_unl_mtx(MtxID); tm_putstring((UB*)"TaskA unlocked Mtx\n"); PutTaskPriority(TaskID_A); tm_putstring((UB*)"TaskA will sleep a while\n"); //tk_slp_tsk(10); } } void MtxSampleTaskB(W stacd,VP exinf) { while(1) { PutTaskPriority(TaskID_B); tm_putstring((UB*)"TaskB will lock Mtx\n"); tk_loc_mtx(MtxID,-1); PutTaskPriority(TaskID_B); tm_putstring((UB*)"TaskB locked Mtx secussfully\n"); Delay(0x1000000); PutTaskPriority(TaskID_B); tm_putstring((UB*)"TaskB will unlock Mtx\n"); tk_unl_mtx(MtxID); PutTaskPriority(TaskID_B); } } void PutTaskPriority(ID taskid) { T_RTSK rtsk; ER ercd; B tskpri[10]; ercd = tk_ref_tsk(taskid, &rtsk); if(E_OK == ercd){ if(taskid == TaskID_A){ tm_putstring((UB*)"Task A"); }else{ tm_putstring((UB*)"Task B"); } tm_putstring((UB*)" current priority is "); ltostr(rtsk.tskpri,tskpri,10,10); tm_putstring((UB*)tskpri); tm_putstring((UB*)"\n"); } }
输出:
----------------------------------------------------
micro Tenux Version 1.6.00(build 0180)
Supported MCU is ST STM32F407VG
Copyright(c) 2008-2013 by Dalian uLoong Co.,Ltd.
----------------------------------------------------
开始创建Mtx
开始创建TaskA
开始创建TaskB
启动TaskA
Task A start TaskB sucessfully
Task A current priority is 20
TaskA locked Mtx
Task A current priority is 15
Task B current priority is 20
TaskB will lock Mtx
Task B current priority is 15
TaskB locked Mtx secussfully
Task B current priority is 15
TaskB will unlock Mtx
TaskA unlocked Mtx
Task A current priority is 20
TaskA will sleep a while
Task A current priority is 20
TaskA locked Mtx
Task A current priority is 15
Task B current priority is 20
Task B current priority is 20
TaskB will lock Mtx
Task B current priority is 15
TaskB locked Mtx secussfully
……………………………
【关于优先权的一点讨论】
原问题和解答在这里:http://forum.eepw.com.cn/thread/233310/1
在实验中碰到了个问题。就是顶置优先级时候,任务的优先级在什么时候切换。在实验中,使用优先级顶置协议时候,两个任务,任务A和任务B是怎样切换的? 第一个实验是这样的:顶置优先级设置成15,任务A、B优先级设置成20. 实验结果是当任务A释放mtx时候,任务B立即获得了执行权。此时任务B还没有开始申请mtx,不会将自己的优先级提升。根据输出,B的优先级仍然是20,但它竟然得到了优先权将A抢断。
再次实验,顶置优先级设置成20,任务A、B的优先级仍认识20.
实验结果是,任务A即使释放了mtx,任务B也不会执行。为什么上个实验,B在优先级为20的时候能抢断A,而这次却不行了?
我的问题是,两个任务的优先级是在什么时候切换的?优先权分配机制是怎样的?
【悠龙工程师给出的解答】
首先说一下互斥体的顶置优先级必须要比任务的优先级高,如果相等,内核代码不做任务处理,如果小于任务的优先级,那么会返回错误代码E_ILUSE!
然后说一下我们互斥体的实验例子:任务A和任务B优先级都是20,互斥体的顶置优先级为15!例子首先启动任务B,然后进入任务B,任务B启动任务A,由于两个人的优先级一样,所以任务A无法运行,处于ready状态!
任务B锁定互斥体之后,如果任务处于ready或wait状态就会改变任务的优先级,变成互斥体的顶置优先级:
void knl_change_task_priority( TCB *tcb, INT priority )改变任务优先级函数:
EXPORT void knl_change_task_priority( TCB *tcb, INT priority ) { INT oldpri; if ( tcb->state == TS_READY ) { /* * When deleting a task from the ready queue, * a value in the 'priority' field in TCB is needed. * Therefore you need to delete the task from the * ready queue before changing 'tcb->priority.' */ knl_ready_queue_delete(&knl_ready_queue, tcb); tcb->priority = (UB)priority; knl_ready_queue_insert(&knl_ready_queue, tcb); knl_reschedule(); } else { oldpri = tcb->priority; tcb->priority = (UB)priority; /* If the hook routine at the task priority change is defined, execute it */ if ( (tcb->state & TS_WAIT) != 0 && tcb->wspec->chg_pri_hook) { (*tcb->wspec->chg_pri_hook)(tcb, oldpri); } } }
首先从ready队列中删除原来的优先级所处的位置,然后向队列中插入新的优先级!
锁定互斥体没有问题,那么在解锁互斥体的时候,同样的到道理,也会改变任务的优先级!
正如问题所在,在调用tk_unl_mtx的时候,改变任务的优先级,从15变为20,那么此时任务B和任务A优先级是一样的,那么任务A为何抢占任务B得意执行呢?
这是问题所在:改变任务优先级的时候也会调用上面的函数,从ready队列中删除原来的优先级所处的位置,然后向队列中插入到新的优先级的位置。在ready队列优先级20的位置此时是任务A,但是任务B优先级也是20,所以任务B插入队列的时候,只能插入到优先级20,排在任务A之后(按照FIFO插入),然后任务重新调度,所以任务A就会执行,抢占任务B!
简单地说,还是优先级排队。。。 解锁时候,从OS的优先级队列中删除当前任务。然后根据优先级重新排队。任务A和任务B优先级相同,只能排到任务B后边了。。。
因此,任务B获得优先权。