【uTenux实验】互斥体
互斥体,维基百科中交互斥锁。其定义是这样的:互斥锁(英语:英语: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获得优先权。
【uTenux实验】互斥体的更多相关文章
- C#互斥体——Mutex
Mutex对象是一个同步基元,可以用来做线程间的同步. 若多个线程需要共享一个资源,可以在这些线程中使用Mutex同步基元.当某一个线程占用Mutex对象时,其他也需要占用Mutex的线程将处于挂起状 ...
- boost 线程、互斥体、条件变量
1.任何技术都是针对特定场景设计的,也就是说,为了解决某个问题而设计的. 2.考虑下面一种场景:一个小旅馆,只有一个卫生间,有清洁人员,店主人,和旅客.卫生间用完之后,就会自动锁闭,必须取钥匙,才能进 ...
- VC防止程序被多次运行 互斥体方法
BOOL CXXXApp::InitInstance() //函数内添加代码 HANDLE hMutex=CreateMutex(NULL,TRUE,"test"); // 用于检 ...
- 互斥体与互锁 <第五篇>
互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex)).互斥体禁止多个线程同时进入受保护的代码“临界区”.因此,在任意时刻,只有一个线程被允许进入这 ...
- linux 内核的rt_mutex (realtime互斥体)
linux 内核有实时互斥体(锁),名为rt_mutex即realtime mutex.说到realtime一定离不开priority(优先级).所谓实时,就是根据优先级的不同对任务作出不同速度的响应 ...
- Windbg调试互斥体(Mutex)死锁
一. 测试代码 #include <windows.h> #include <tchar.h> #include <process.h> HANDLE hMutex ...
- C++多线程同步技巧(三)--- 互斥体
简介 Windows互斥对象机制. 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问,在线程同步与保证程序单体运行上都有相当大的用处. 代码 ...
- Python程序互斥体
Python程序互斥体 有时候我们需要程序只运行一个实例,在windows平台下我们可以很简单的用mutex实现这个目的. 在开始时,程序创建了一个命名的mutex,这个mutex可以被其他进 ...
- 转载 互斥体与互锁 <第五篇>
互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex)).互斥体禁止多个线程同时进入受保护的代码“临界区”.因此,在任意时刻,只有一个线程被允许进入这 ...
随机推荐
- hdu----1686 Oulipo (ac自动机)
Oulipo Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Subm ...
- 并发编程 10—— 任务取消 之 关闭 ExecutorService
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
- meta name="viewport" content="width=device-width,initial-scale=1.0" 解释
<meta name="viewport" content="width=device-width,initial-scale=1.0"> c ...
- State(状态)
props和state.props是在父组件中指定,而且一经指定,在被指定的组件的生命周期中则不再改变. 对于需要改变的数据,我们需要使用state.般来说,你需要在constructor中初始化st ...
- iOS开发数据库篇—FMDB简单介绍
iOS开发数据库篇—FMDB简单介绍 一.简单说明 1.什么是FMDB FMDB是iOS平台的SQLite数据库框架 FMDB以OC的方式封装了SQLite的C语言API 2.FMDB的优点 使用起来 ...
- POJ 2226 最小点覆盖(经典建图)
Muddy Fields Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8881 Accepted: 3300 Desc ...
- golang——concurrency笔记
1.主线程结束后将安全退出子线程
- android属性之excludeFromRecents -- clearTaskOnLaunch 隐身意图 启动activity
这个可以 用android 任务中app 隐藏起来 android属性之clearTaskOnLaunch 此属性是 每次启动app时都会进入 根目录中 android:clearTask ...
- MySQL数据库忘记root密码解决办法
MySQL数据库忘记root密码解决办法 1.在运行输入services.msc打开服务窗体,找到MYSQL服务.右键停止将其关闭.如图:
- C#实现右下角弹出窗口效果
/// <summary> /// 窗体动画函数 注意:要引用System.Runtime.InteropServices; /// </summary> /// <pa ...