ucos实时操作系统学习笔记——任务间通信(互斥锁)
想讲一下ucos任务间通信中的mutex,感觉其设计挺巧妙,同sem一样使用的是event机制实现的,代码不每一行都分析,因为讲的没邵贝贝老师清楚,主要讲一下mutex的内核是如何实现的。可以理解互斥锁是设置信号量值为1时候的特殊情况,与之不同的地方是互斥锁为了避免优先级反转采用了优先级继承机制,本文主要讲一下互斥锁的创建,pend和post,对应的函数是OSMutexCreate,OSMutexPend,OSMutexPost,当然讲函数也不会所有的扩展功能都讲,只是讲一下主干部分,下面贴出来的代码也是简化的代码。
会通过分块的方式讲代码,在讲的过程中会把互斥锁的机制、功能以及具体实现与代码穿插讲一下。
首先,从互斥锁创建讲起,互斥锁同一样使用event机制来实现,不过与sem不同的是互斥锁在创建的时候会传递一个高一点的优先级给event,当低优先级任务获得互斥锁时用于优先级继承,而sem传递的参数是信号量的值。OSMutexCreate创建代码如下所示:
OS_EVENT *OSMutexCreate (INT8U prio,
INT8U *perr)
{
OS_EVENT *pevent;
if (OSIntNesting > 0u) { /* See if called from ISR ... */
*perr = OS_ERR_CREATE_ISR; /* ... can't CREATE mutex from an ISR */
return ((OS_EVENT *));
}
OS_ENTER_CRITICAL();
if (OSTCBPrioTbl[prio] != (OS_TCB *)) { /* Mutex priority must not already exist */
OS_EXIT_CRITICAL(); /* Task already exist at priority ... */
*perr = OS_ERR_PRIO_EXIST; /* ... inheritance priority */
return ((OS_EVENT *));
}
OSTCBPrioTbl[prio] = OS_TCB_RESERVED; /* Reserve the table entry */
pevent = OSEventFreeList; /* Get next free event control block */
if (pevent == (OS_EVENT *)) { /* See if an ECB was available */
OSTCBPrioTbl[prio] = (OS_TCB *); /* No, Release the table entry */
OS_EXIT_CRITICAL();
*perr = OS_ERR_PEVENT_NULL; /* No more event control blocks */
return (pevent);
}
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; /* Adjust the free list */
OS_EXIT_CRITICAL();
pevent->OSEventType = OS_EVENT_TYPE_MUTEX;
pevent->OSEventCnt = (INT16U)((INT16U)prio << 8u) | OS_MUTEX_AVAILABLE; /* Resource is avail. */
pevent->OSEventPtr = (void *); /* No task owning the mutex */
OS_EventWaitListInit(pevent);
*perr = OS_ERR_NONE;
return (pevent);
}
互斥锁的创建同样不允许在中断中进行,mutex首先会检查传入优先级的任务TCB有没有已经被使用,如果传入优先级已经被使用,则创建失败,返回错误,如果参数传入的优先级的TCB没有被占用,则设置为reserved,保证该TCB不会被其他任务使用;然后是跟信号量一样,要取一个空闲的event结构体,判断如果没有空闲event则返回错误;取得event之后要对其进行初始化,OSEventType为OS_EVENT_TYPE_MUTEX类型,OSEventCnt的数值跟sem不同,在mutex中将高8位设置为需要继承的优先级(参数传入的优先级),低8位为OS_MUTEX_AVAILABLE,这个值为0x00FF,个人理解这个值相当于sem中信号量位1的作用,当pend中监测到低8位是这个值时,表示可以将任务本身的优先级放到低8位的位置上,如果低8位已经有优先级存在,则表示需要把想获得锁的任务挂起。之后pend中会讲到;然后会对OSEventPtr清0,具体目的不清楚,对mutex来说,好像没有用到这个参数;之后是对event group和table的清零初始化操作,表示当前没有等待互斥锁的任务在group和table中。
互斥锁创建完成之后就是有关互斥锁的pend和post操作了,这两个操作是成对存在的,两个函数分别是OSMutexPend和OSMutexPost。接下来从OSMutexPend的代码开始分析其主要的操作是怎么样的,其中有一部分的内容和sem是相同的,代码如下所示:
void OSMutexPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr)
{
INT8U pip; /* Priority Inheritance Priority (PIP) */
INT8U mprio; /* Mutex owner priority */
BOOLEAN rdy; /* Flag indicating task was ready */
OS_TCB *ptcb;
OS_EVENT *pevent2;
INT8U y; if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) { /* Validate event block type */
*perr = OS_ERR_EVENT_TYPE;
return;
}
if (OSIntNesting > ) { /* See if called from ISR ... */
*perr = OS_ERR_PEND_ISR; /* ... can't PEND from an ISR */
return;
}
if (OSLockNesting > ) { /* See if called with scheduler locked ... */
*perr = OS_ERR_PEND_LOCKED; /* ... can't PEND when locked */
return;
}
OS_ENTER_CRITICAL();
(1)=========================================================================================================
pip = (INT8U)(pevent->OSEventCnt >> ); /* Get PIP from mutex */
/* Is Mutex available? */
if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) {
pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8; /* Yes, Acquire the resource */
pevent->OSEventCnt |= OSTCBCur->OSTCBPrio; /* Save priority of owning task */
pevent->OSEventPtr = (void *)OSTCBCur; /* Point to owning task's OS_TCB */
if (OSTCBCur->OSTCBPrio <= pip) { /* PIP 'must' have a SMALLER prio ... */
OS_EXIT_CRITICAL(); /* ... than current task! */
*perr = OS_ERR_PIP_LOWER;
} else {
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
}
return;
}
(2)========================================================================================================
mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); /* No, Get priority of mutex owner */
ptcb = (OS_TCB *)(pevent->OSEventPtr); /* Point to TCB of mutex owner */
if (ptcb->OSTCBPrio > pip) { /* Need to promote prio of owner?*/
if (mprio > OSTCBCur->OSTCBPrio) {
y = ptcb->OSTCBY;
if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != ) { /* See if mutex owner is ready */
OSRdyTbl[y] &= ~ptcb->OSTCBBitX; /* Yes, Remove owner from Rdy ...*/
if (OSRdyTbl[y] == ) { /* ... list at current prio */
OSRdyGrp &= ~ptcb->OSTCBBitY;
}
rdy = OS_TRUE;
} else {
pevent2 = ptcb->OSTCBEventPtr;
if (pevent2 != (OS_EVENT *)) { /* Remove from event wait list */
if ((pevent2->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == ) {
pevent2->OSEventGrp &= ~ptcb->OSTCBBitY;
}
}
rdy = OS_FALSE; /* No */
}
ptcb->OSTCBPrio = pip; /* Change owner task prio to PIP */
#if OS_LOWEST_PRIO <= 63
ptcb->OSTCBY = (INT8U)( ptcb->OSTCBPrio >> );
ptcb->OSTCBX = (INT8U)( ptcb->OSTCBPrio & 0x07);
ptcb->OSTCBBitY = (INT8U)( << ptcb->OSTCBY);
ptcb->OSTCBBitX = (INT8U)( << ptcb->OSTCBX);
#else
ptcb->OSTCBY = (INT8U)((ptcb->OSTCBPrio >> ) & 0xFF);
ptcb->OSTCBX = (INT8U)( ptcb->OSTCBPrio & 0x0F);
ptcb->OSTCBBitY = (INT16U)( << ptcb->OSTCBY);
ptcb->OSTCBBitX = (INT16U)( << ptcb->OSTCBX);
#endif
if (rdy == OS_TRUE) { /* If task was ready at owner's priority ...*/
OSRdyGrp |= ptcb->OSTCBBitY; /* ... make it ready at new priority. */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
} else {
pevent2 = ptcb->OSTCBEventPtr;
if (pevent2 != (OS_EVENT *)) { /* Add to event wait list */
pevent2->OSEventGrp |= ptcb->OSTCBBitY;
pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
}
OSTCBPrioTbl[pip] = ptcb;
}
}
(3)==========================================================================================================
OSTCBCur->OSTCBStat |= OS_STAT_MUTEX; /* Mutex not available, pend current task */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
OSTCBCur->OSTCBDly = timeout; /* Store timeout in current task's TCB */
OS_EventTaskWait(pevent); /* Suspend task until event or timeout occurs */
OS_EXIT_CRITICAL();
OS_Sched(); /* Find next highest priority task ready */
OS_ENTER_CRITICAL();
switch (OSTCBCur->OSTCBStatPend) { /* See if we timed-out or aborted */
case OS_STAT_PEND_OK:
*perr = OS_ERR_NONE;
break; case OS_STAT_PEND_ABORT:
*perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted getting mutex */
break; case OS_STAT_PEND_TO:
default:
OS_EventTaskRemove(OSTCBCur, pevent);
*perr = OS_ERR_TIMEOUT; /* Indicate that we didn't get mutex within TO */
break;
}
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set task status to ready */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *); /* Clear event pointers */
OS_EXIT_CRITICAL();
(4)====================================================================================================
}
本文将OSMutexPend函数分为了如上所示的4个部分(使用“=”隔开),第一部分和最后一部分和sem的操作相同,第一部分主要就是检查一下当前的event类型是不是mutex以及检查当前的操作是不是在中断处理程序中进行的, 最后一部分讲的的就是讲任务挂起然后进行任务调度以及当任务重新获得互斥锁时的准备工作;与sem不同的主要是第二和第三部分,其实这两部分的内容是不会同时在一次操作中运行的,当第二部分运行的时候表示没有任务使用互斥锁,这时候运行该函数的任务将获得互斥锁并退出,第三部分则是表示一个新的任务在尝试获得互斥锁,并且互斥锁已经被占用时的操作。
第二部分的运行流程是这样的,之前创建的时候也说过OSEventCnt的低8位存放的是OS_MUTEX_AVAILABLE,如果检查结果仍为OS_MUTEX_AVAILABLE,则说明互斥锁没有被占用,这时候就要保存当前运行任务的优先级到OSEventCnt的低8位中,然后比较一下当前任务的优先级是否比创建时的继承优先级低(优先级值大),如果当前任务的优先级比继承优先级高(小),则返回错误,因为继承优先级是为了防止优先级翻转而设定,如果其优先级低则实现不了这一功能,反之,则返回正确值,此处的返回值是通过参数地址返回的;然后将当前获取互斥锁的任务的TCB保存到OSEventPtr指针中。
第三部分的运行流程是,首先获得占用互斥锁任务的优先级以及TCB,然后将当前想获取互斥锁的任务的优先级(1)和占有互斥锁任务的优先级(2)做比较,如果(1)比(2)的优先级高,则不做任何操作,直接执行第4部分,将当前任务挂起;如果(1)比(2)的优先级低,则需要提升(1)的优先级,同样要挂起(2)。对于(1)比(2)优先级低的情况,首先检查当前任务是不是处于运行状态,然后给占用互斥锁的任务提升优先级到继承优先级级别,然后根据检查任务的运行状态决定把任务继续放到ready table中还是放到任务的event等待列表中。将占有互斥锁的任务的TCB保存到系统的prio table中。
INT8U OSMutexPost (OS_EVENT *pevent)
{
INT8U pip; /* Priority inheritance priority */
INT8U prio; if (OSIntNesting > ) { /* See if called from ISR ... */
return (OS_ERR_POST_ISR); /* ... can't POST mutex from an ISR */
}
if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) { /* Validate event block type */
return (OS_ERR_EVENT_TYPE);
}
(1)====================================================================================================
OS_ENTER_CRITICAL();
pip = (INT8U)(pevent->OSEventCnt >> ); /* Get priority inheritance priority of mutex */
prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); /* Get owner's original priority */
if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr) { /* See if posting task owns the MUTEX */
OS_EXIT_CRITICAL();
return (OS_ERR_NOT_MUTEX_OWNER);
}
if (OSTCBCur->OSTCBPrio == pip) { /* Did we have to raise current task's priority? */
OSMutex_RdyAtPrio(OSTCBCur, prio); /* Restore the task's original priority */
}
OSTCBPrioTbl[pip] = OS_TCB_RESERVED; /* Reserve table entry */
(2)=====================================================================================================
if (pevent->OSEventGrp != ) { /* Any task waiting for the mutex? */
/* Yes, Make HPT waiting for mutex ready */
prio = OS_EventTaskRdy(pevent, (void *), OS_STAT_MUTEX, OS_STAT_PEND_OK);
pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8; /* Save priority of mutex's new owner */
pevent->OSEventCnt |= prio;
pevent->OSEventPtr = OSTCBPrioTbl[prio]; /* Link to new mutex owner's OS_TCB */
if (prio <= pip) { /* PIP 'must' have a SMALLER prio ... */
OS_EXIT_CRITICAL(); /* ... than current task! */
OS_Sched(); /* Find highest priority task ready to run */
return (OS_ERR_PIP_LOWER);
} else {
OS_EXIT_CRITICAL();
OS_Sched(); /* Find highest priority task ready to run */
return (OS_ERR_NONE);
}
}
pevent->OSEventCnt |= OS_MUTEX_AVAILABLE; /* No, Mutex is now available */
pevent->OSEventPtr = (void *);
OS_EXIT_CRITICAL();
return (OS_ERR_NONE);
(3)====================================================================================================
}
对OSMutexPost同样进行分段描述,第一部分是检查event的type和是否在中断中,这跟sem是相同的;第二部分则是讲互斥锁的还原,首先是通过event获取继承优先级以及现在占有互斥锁的任务的优先级,之前讲过互斥锁的pend和post是成对存在的,当任务pend获取互斥锁之后,也需要相对应的任务post释放互斥锁,所以第二部分中会有一个判断当前post释放互斥锁的是否是占有互斥锁的任务,如果不是则会报错,如果是占有互斥锁的任务要释放互斥锁,则会判断任务在pend的时候有没有提升任务的优先级到继承优先级的级别,如果有的话需要把当前任务的优先级通过OSMutex_RdyAtPrio还原到任务自己原来的优先级级别。
第三部分的内容这是会判断有没有任务在等待当前的event互斥锁,如果有的话就通过OS_EventTaskRdy获取等待任务的优先级,在获取优先级的同时会把获取的任务从event等待列表中删除,可以从OS_EventTaskRdy中找到代码,然后设置event mutex相关参数如pend中的操作一样,同样需要判断任务的优先级是否比继承优先级高,如果高则报错,否则会任务调度到另外的任务中继续执行,相当于本任务的post过程完成,如果没有event mutex的等待任务,则会直接设置event的OSEventCnt位OS_MUTEX_AVAILABLE和OSEventPtr的清0操作。
在第三部分中一个比较巧妙的地方是,当有任务在event的等待列表时,会直接将互斥锁交给等待任务,等待任务使用完成时会做post释放操作,如果有多个任务在等待,则会一一释放掉互斥锁,当所有任务释放掉之后,返回到当前任务时,则互斥锁已经完全释放了,这时返回OS_ERR_NONE,如果没有等待任务在event的等待列表中,则需要当前任务自己释放,也就是第三部分最后的4行操作。
ucos实时操作系统学习笔记——任务间通信(互斥锁)的更多相关文章
- ucos实时操作系统学习笔记——任务间通信(消息)
ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...
- ucos实时操作系统学习笔记——任务间通信(信号量)
ucos实时操作系统的任务间通信有好多种,本人主要学习了sem, mutex, queue, messagebox这四种.系统内核代码中,这几种任务间通信机制的实现机制相似,接下来记录一下本人对核心代 ...
- ucos实时操作系统学习笔记——任务间通信(队列)
ucos操作系统中的queue机制同样使用了event机制来实现,其实和前面的sem,mutex实现类似,所不同的是对sem而言,任务想获得信号量,对mutex而言,任务想获得的是互斥锁.任务间通信的 ...
- ucos实时操作系统学习笔记——操作系统在STM32的移植
使用ucos实时操作系统是在上学的时候,导师科研项目中.那时候就是网上找到操作系统移植教程以及应用教程依葫芦画瓢,功能实现也就罢了,没有很深入的去研究过这个东西.后来工作了,闲来无聊就研究了一下这个只 ...
- ucos实时操作系统学习笔记——内核结构和任务创建
对于ucos实时操作系统,邵贝贝的那本书已经写得很详细了,我因为之前不深的研究过ucos,所以在这里做一个笔记,写一些个人对该操作系统的理解,仅仅是个人理解,如果有人看到这边随笔有不对的地方,望给我指 ...
- RTX51 Tiny实时操作系统学习笔记—初识RTX51 Tiny
一,RTX51 Tiny简单介绍 RTX51 Tiny是一种实时操作系统(RTOS),能够用它来建立多个任务(函数)同一时候运行的应用(从宏观上看是同一时候运行的,但从微观上看,还是独立运行的 ...
- 2种方式(线程间通信/互斥锁)实现两个线程,一个线程打印1-52,另一个线程打印字母A-Z,打印顺序为12A34B56C......5152Z
//2019/06/13 本周HT面试遇到的问题,答得不是很好,自己重新做一下.面试只需要写出线程间通信的方式,//我当时大致知道思路,因为之前看过马士兵老师的多线程视频,但是代码写出来估计编译都是报 ...
- 操作系统学习笔记----进程/线程模型----Coursera课程笔记
操作系统学习笔记----进程/线程模型----Coursera课程笔记 进程/线程模型 0. 概述 0.1 进程模型 多道程序设计 进程的概念.进程控制块 进程状态及转换.进程队列 进程控制----进 ...
- 操作系统学习笔记5 | 用户级线程 && 内核级线程
在上一部分中,我们了解到操作系统实现多进程图像需要组织.切换.考虑进程之间的影响,组织就是用PCB的队列实现,用到了一些简单的数据结构知识.而本部分重点就是进程之间的切换. 参考资料: 课程:哈工大操 ...
随机推荐
- 使用redis构建可靠分布式锁
关于分布式锁的概念,具体实现方式,直接参阅下面两个帖子,这里就不多介绍了. 分布式锁的多种实现方式 分布式锁总结 对于分布式锁的几种实现方式的优劣,这里再列举下 1. 数据库实现方式 优点:易理解 缺 ...
- input[tyle="file"]样式修改及上传文件名显示
默认的上传样式我们总觉得不太好看,根据需求总想改成和上下结构统一的风格…… 实现方法和思路: 1.在input元素外加a超链接标签 2.给a标签设置按钮样式 3.设置input[type='file' ...
- Android性能优化之利用Rxlifecycle解决RxJava内存泄漏
前言: 其实RxJava引起的内存泄漏是我无意中发现了,本来是想了解Retrofit与RxJava相结合中是如何通过适配器模式解决的,结果却发现了RxJava是会引起内存泄漏的,所有想着查找一下资料学 ...
- ASP.NET Core的路由[2]:路由系统的核心对象——Router
ASP.NET Core应用中的路由机制实现在RouterMiddleware中间件中,它的目的在于通过路由解析为请求找到一个匹配的处理器,同时将请求携带的数据以路由参数的形式解析出来供后续请求处理流 ...
- 【原创分享·微信支付】 C# MVC 微信支付教程系列之公众号支付
微信支付教程系列之公众号支付 今天,我们接着讲微信支付的系列教程,前面,我们讲了这个微信红包和扫码支付.现在,我们讲讲这个公众号支付.公众号支付的应用环境常见的用户通过公众号,然后再通 ...
- WPF CheckBox 样式
<Style x:Key="FocusVisual"> <Setter Property="Control.Template"> < ...
- 熊乐:H3 BPM为加速企业流程管理提供源动力
近日,在北京·金隅喜来登酒店,H3 BPM以"让天下没有难用的流程"为主题,正式发布H3 BPM10.0版本.全新的业务流程管理系统在易用性方面大大提升,并且全面支持Java与.N ...
- iOS app内存分析套路
iOS app内存分析套路 Xcode下查看app内存使用情况有2中方法: Navigator导航栏中的Debug navigator中的Memory Instruments 一.Debug navi ...
- 查看mac中磁盘空间占用情况
今天发现磁盘空间不够了,首先要找到那些文件夹占用了磁盘空间. du命令很好使 du -c -d 1 -m | sort -n -c 显示当前文件夹总计占用空间 -d 1 层级为1,即只显示当前目录下一 ...
- maven 快照
大型应用软件一般由多个模块组成,一般它是多个团队开发同一个应用程序的不同模块,这是比较常见的场景.例如,一个团队正在对应用程序的应用程序,用户界面项目(app-ui.jar:1.0) 的前端进行开发, ...