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的队列实现,用到了一些简单的数据结构知识.而本部分重点就是进程之间的切换. 参考资料: 课程:哈工大操 ...
随机推荐
- opencv在图像显示中文
在图像定位和模式识别时,经常需要把结果标注到图片上,标注内容可以是数字字母.矩形框等(opencv支持的)或者是中文汉字(借助freetype). 1.显示数字/矩形框 #include <op ...
- 在线浏览PDF之PDF.JS (附demo)
平台之大势何人能挡? 带着你的Net飞奔吧!:http://www.cnblogs.com/dunitian/p/4822808.html#skill 下载地址:http://mozilla.gith ...
- 前端学HTTP之安全HTTP
前面的话 HTTP的主要不足包括通信使用明文(不加密),内容可能会被窃听:不验证通信方的身份,有可能遭遇伪装:无法证明报文的完整性,有可能被篡改 基本认证和摘要认证能够使得用户识别后较安全的访问服务器 ...
- MVC CodeFirst简单的创建数据库(非常详细的步骤)
最近在学习MVC的开发,相信有过开发经验的人初学一个新的框架时候的想法跟我一样最关心的就是这个框架如何架构,每个架构如何分工,以及最最关键的就是如何与数据库通信,再下来才是学习基础的页面设计啊等 ...
- MyBatis基础入门--知识点总结
对原生态jdbc程序的问题总结 下面是一个传统的jdbc连接oracle数据库的标准代码: public static void main(String[] args) throws Exceptio ...
- Web安全相关(五):SQL注入(SQL Injection)
简介 SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据 ...
- 对百度WebUploader开源上传控件的二次封装,精简前端代码(两句代码搞定上传)
前言 首先声明一下,我这个是对WebUploader开源上传控件的二次封装,底层还是WebUploader实现的,只是为了更简洁的使用他而已. 下面先介绍一下WebUploader 简介: WebUp ...
- 编译器开发系列--Ocelot语言7.中间代码
Ocelot的中间代码是仿照国外编译器相关图书Modern Compiler Implementation 中所使用的名为Tree 的中间代码设计的.顾名思义,Tree 是一种树形结构,其特征是简单, ...
- Git学习笔记一:新建本地仓库及初始化
1.百度搜索Git下载安装,直接按默认选项安装即可. 例如:Git-2.7.2-32-bit_setup.1457942412.exe 2.配置Git信息,建立版本仓库 (Alt+PrintScerr ...
- 图解Spark API
初识spark,需要对其API有熟悉的了解才能方便开发上层应用.本文用图形的方式直观表达相关API的工作特点,并提供了解新的API接口使用的方法.例子代码全部使用python实现. 1. 数据源准备 ...