第4章第7节nbsp;二进制信号量(二)
目前更新到5.3节,请在http://dl.dbank.com/c02ackpwp6下载5.3节的全部文档
本节源代码请在http://dl.dbank.com/c0fp2g5z9s下载
释放信号量的函数MDS_SemGive的代码如下:
00143 U32 MDS_SemGive(M_SEM* pstrSem)
00144 {
00145 M_TCB* pstrTcb;
00146 M_CHAIN* pstrChain;
00147 M_CHAIN* pstrNode;
00148 M_PRIOFLAG* pstrPrioFlag;
00149 U8 ucTaskPrio;
00150
00151
00152 if(NULL == pstrSem)
00153 {
00154 return RTN_FAIL;
00155 }
00156
00157 (void)MDS_IntLock();
00158
00159
00160 if(SEMEMPTY == pstrSem->uiCounter)
00161 {
00162
00163 pstrTcb = MDS_SemGetActiveTask(pstrSem);
00164
00165
00166 if(NULL != pstrTcb)
00167 {
00168
00169 (void)MDS_TaskDelFromSemTab(pstrTcb);
00170
00171
00172 if(DELAYQUEFLAG == (pstrTcb->uiTaskFlag & DELAYQUEFLAG))
00173 {
00174 pstrNode = &pstrTcb->strDelayQue.strQueHead;
00175 (void)MDS_ChainCurNodeDelete(&gstrDelayTab, pstrNode);
00176
00177
00178 pstrTcb->uiTaskFlag &= (~((U32)DELAYQUEFLAG));
00179 }
00180
00181
00182 pstrTcb->strTaskOpt.ucTaskSta &= ~((U8)TASKPEND);
00183
00184
00185 pstrTcb->strTaskOpt.uiDelayTick = RTN_SUCD;
00186
00187
00188 pstrNode = &pstrTcb->strTcbQue.strQueHead;
00189 ucTaskPrio = pstrTcb->ucTaskPrio;
00190 pstrChain = &gstrReadyTab.astrChain[ucTaskPrio];
00191 pstrPrioFlag = &gstrReadyTab.strFlag;
00192
00193 MDS_TaskAddToSchedTab(pstrChain, pstrNode, pstrPrioFlag, ucTaskPrio);
00194
00195
00196 pstrTcb->strTaskOpt.ucTaskSta |= TASKREADY;
00197
00198 (void)MDS_IntUnlock();
00199
00200
00201 MDS_TaskSwiSched();
00202
00203 return RTN_SUCD;
00204 }
00205 else
00206 {
00207 pstrSem->uiCounter = SEMFULL;
00208 }
00209 }
00210
00211 (void)MDS_IntUnlock();
00212
00213 return RTN_SUCD;
00214 }
00143行,函数返回值RTN_SUCD代表释放信号量成功,RTN_FAIL代表释放信号量失败。入口参数pstrSem为释放的信号量的指针。
00152~00155行,对入口参数pstrSem进行检查,若为空则返回失败。
00157行,锁中断,防止多个任务同时操作信号量。
00160行,若信号量处于空状态走此分支。
00163行,从信号量pstrSem的调度表里面被阻塞的任务中查找需要被释放的任务,返回该任务的TCB。
00166行,若该任务的TCB不为空,说明有被阻塞的任务,走此分支。
00169行,将这个任务从信号量调度表中删除。
00172行,如果这个任务处于delay表中,走此分支。
00174行,获取这个任务TCB中挂接到delay表的节点。
00175行,将该任务从delay表中删除。
00178行,将该任务的标志修改为不在delay表中。
00182行,清除该任务的pend状态。
00185行,将该任务的返回值保存在TCB的strTaskOpt.uiDelayTick中,当该任务重新运行时就会得到RTN_SUCD这个返回值。注意,该任务是当前运行任务使用信号量释放出来的任务,而不是当前正在运行的任务。
00188~00196行,将该任务添加到ready表中,并将任务的状态修改为ready状态。
00198行,对信号量操作完毕,解锁中断。
00201行,已经有任务新加入到ready表中,使用软中断函数调度任务。
00205行,信号量中没有被阻塞的任务,走此分支。
00207行,走到此分支,说明信号量是在空的状态下被释放,并且没有被阻塞的任务需要获取信号量,因此直接将信号量置为满状态。
00211行,对信号量操作完毕,解锁中断。
00213行,对信号量操作完毕,返回释放信号量成功。
如果有任务被信号量阻塞,在中断中使用MDS_SemGive函数释放信号量时并不会立刻发生任务调度,因为MDS_SemGive函数所使用的软中断调度函数MDS_TaskSwiSched无法在中断中执行,任务调度需要等到下个tick中断到来时才能执行,因此在中断中释放信号量激活的最高优先级任务可能会有一个小的延迟后才能运行。在后面章节我们会将Mindows移植到cortex内核的芯片上,cortex内核拥有pendsv软中断,使用pendsv软中断可以解决这个问题,不会有延迟产生。
MDS_SemGetActiveTask函数的功能是从信号量的调度表中获取需要被最先释放的任务,如果信号量采用的是优先级调度方式,则使用MDS_TaskHighestPrioGet函数从信号量调度表中找出最高优先级任务的TCB,如果信号量采用的是先进先出的调度方式,则从信号量调度表中取出最先入链表的头节点。函数比较简单,不再详细介绍,代码如下:
00424 M_TCB* MDS_SemGetActiveTask(M_SEM* pstrSem)
00425 {
00426 M_CHAIN* pstrNode;
00427 M_TCBQUE* pstrTaskQue;
00428 U8 ucTaskPrio;
00429
00430
00431 if(SEMPRIO == (SEMSCHEDOPTMASK & pstrSem->uiSemOpt))
00432 {
00433
00434 ucTaskPrio = MDS_TaskHighestPrioGet(&pstrSem->strSemTab.strFlag);
00435 }
00436 else
00437 {
00438
00439 ucTaskPrio = LOWESTPRIO;
00440 }
00441
00442 pstrNode = MDS_ChainEmpInq(&pstrSem->strSemTab.astrChain[ucTaskPrio]);
00443
00444
00445 if(NULL == pstrNode)
00446 {
00447 return (M_TCB*)NULL;
00448 }
00449 else
00450 {
00451 pstrTaskQue = (M_TCBQUE*)pstrNode;
00452
00453 return pstrTaskQue->pstrTcb;
00454 }
00455 }
MDS_TaskDelFromSemTab函数的功能是将任务从信号量调度表中删除,如果信号量采用的是优先级调度方式,则从任务TCB中找出任务的相关属性,使用MDS_TaskDelFromSchedTab函数将任务从信号量调度表中删除,这个过程与从ready表中删除任务的过程是一样的。如果信号量采用的是先进先出的调度方式,则从信号量调度表中删除最先入链表的头节点。函数比较简单,不再详细介绍,代码如下:
00388 M_CHAIN* MDS_TaskDelFromSemTab(M_TCB* pstrTcb)
00389 {
00390 M_SEM* pstrSem;
00391 M_CHAIN* pstrChain;
00392 M_PRIOFLAG* pstrPrioFlag;
00393 U8 ucTaskPrio;
00394
00395
00396 pstrSem = pstrTcb->pstrSem;
00397
00398
00399 if(SEMPRIO == (SEMSCHEDOPTMASK & pstrSem->uiSemOpt))
00400 {
00401
00402 ucTaskPrio = pstrTcb->ucTaskPrio;
00403 pstrChain = &pstrSem->strSemTab.astrChain[ucTaskPrio];
00404 pstrPrioFlag = &pstrSem->strSemTab.strFlag;
00405
00406
00407 return MDS_TaskDelFromSchedTab(pstrChain, pstrPrioFlag, ucTaskPrio);
00408 }
00409 else
00410 {
00411
00412 pstrChain = &pstrSem->strSemTab.astrChain[LOWESTPRIO];
00413
00414
00415 return MDS_ChainNodeDelete(pstrChain);
00416 }
00417 }
MDS_SemGive函数一次只能释放一个被阻塞的任务,MDS_SemFlush可以一次性释放被信号量阻塞的所有任务。MDS_SemFlush函数的原理与MDS_SemGive函数是一样的,MDS_SemFlush函数会循环查找信号量调度表中的所有任务,将他们全部释放掉,不再做详细介绍,代码如下,请读者自行分析:
00301 U32 MDS_SemFlush(M_SEM* pstrSem)
00302 {
00303
00304 return MDS_SemFlushValue(pstrSem, RTN_SUCD);
00305 }
00224 U32 MDS_SemFlushValue(M_SEM* pstrSem, U32 uiRtnValue)
00225 {
00226 M_TCB* pstrTcb;
00227 M_CHAIN* pstrChain;
00228 M_CHAIN* pstrNode;
00229 M_PRIOFLAG* pstrPrioFlag;
00230 U8 ucTaskPrio;
00231
00232
00233 if(NULL == pstrSem)
00234 {
00235 return RTN_FAIL;
00236 }
00237
00238 (void)MDS_IntLock();
00239
00240
00241 while(1)
00242 {
00243 pstrTcb = MDS_SemGetActiveTask(pstrSem);
00244
00245
00246 if(NULL != pstrTcb)
00247 {
00248
00249 (void)MDS_TaskDelFromSemTab(pstrTcb);
00250
00251
00252 if(DELAYQUEFLAG == (pstrTcb->uiTaskFlag & DELAYQUEFLAG))
00253 {
00254 pstrNode = &pstrTcb->strDelayQue.strQueHead;
00255 (void)MDS_ChainCurNodeDelete(&gstrDelayTab, pstrNode);
00256
00257
00258 pstrTcb->uiTaskFlag &= (~((U32)DELAYQUEFLAG));
00259 }
00260
00261
00262 pstrTcb->strTaskOpt.ucTaskSta &= ~((U8)TASKPEND);
00263
00264
00265 pstrTcb->strTaskOpt.uiDelayTick = uiRtnValue;
00266
00267
00268 pstrNode = &pstrTcb->strTcbQue.strQueHead;
00269 ucTaskPrio = pstrTcb->ucTaskPrio;
00270 pstrChain = &gstrReadyTab.astrChain[ucTaskPrio];
00271 pstrPrioFlag = &gstrReadyTab.strFlag;
00272
00273 MDS_TaskAddToSchedTab(pstrChain, pstrNode, pstrPrioFlag, ucTaskPrio);
00274
00275
00276 pstrTcb->strTaskOpt.ucTaskSta |= TASKREADY;
00277 }
00278 else
00279 {
00280 break;
00281 }
00282 }
00283
00284
00285 pstrSem->uiCounter = SEMEMPTY;
00286
00287 (void)MDS_IntUnlock();
00288
00289
00290 MDS_TaskSwiSched();
00291
00292 return RTN_SUCD;
00293 }
当信号量不再需要使用时,可以使用MDS_SemDelete函数删除信号量,释放信号量所占用的资源。删除信号量时,被阻塞在该信号量上的所有任务全部会被激活,重新挂入ready表中参与任务调度,这个过程使用MDS_SemFlushValue函数就可以实现。MDS_SemDelete函数比较简单,代码如下,不再详细介绍。
00313 U32 MDS_SemDelete(M_SEM* pstrSem)
00314 {
00315
00316 if(NULL == pstrSem)
00317 {
00318 return RTN_FAIL;
00319 }
00320
00321
00322 if(RTN_SUCD != MDS_SemFlushValue(pstrSem, RTN_SMTKDL))
00323 {
00324 return RTN_FAIL;
00325 }
00326
00327 return RTN_SUCD;
00328 }
本节新增加了一个pend任务状态,那么在任务调度的时候也需要对这个状态进行处理,需要在MDS_TaskDelayTabSched函数里增加对pend状态的任务的处理,该函数改动较小,只是增加了一个对pend状态处理的分支。
00544 void MDS_TaskDelayTabSched(void)
00545 {
…… ……
00588
00589
00590 else if(TASKPEND == (TASKPEND& pstrTcb->strTaskOpt.ucTaskSta))
00591 {
00592
00593 (void)MDS_TaskDelFromSemTab(gpstrCurTcb);
00594
00595
00596 pstrTcb->strTaskOpt.ucTaskSta &= ~((U8)TASKPEND);
00597
00598
00599 pstrTcb->strTaskOpt.uiDelayTick = RTN_SMTKTO;
00600 }
00601
…… ……
00631 }
00590行,走到此处说明delay表中有时间耗尽的任务,如果这个任务是pend状态,那么走此分支。
00593行,走到此处说明该任务是pend状态,并且时间耗尽,需要从信号量调度表删除。
00596行,清除任务的pend状态。
00599行,将返回值RTN_SMTKTO存入超时任务的TCB中的strTaskOpt.uiDelayTick变量中,当这个超时任务返回时,返回值RTN_SMTKTO可以说明这个任务是由于时间耗尽超时返回的。
任务增加了pend状态,还影响到了MDS_TaskDelete函数,在删除处于pend状态的任务时需要将这个任务从相关的信号量调度表中拆除,MDS_TaskDelete函数新增的代码如下:
00121 U32 MDS_TaskDelete(M_TCB* pstrTcb)
00122 {
…… ……
00167
00168
00169 if(TASKPEND == (TASKPEND & ucTaskSta))
00170 {
00171
00172 (void)MDS_TaskDelFromSemTab(pstrTcb);
00173 }
00174
…… ……
00191 }
当我们需要串行访问一个需要多步骤操作的资源时,就可以使用二进制信号量对其进行保护,例如对串口、FLASH等外设操作工程的保护。在使用信号量保护资源时要避免出现信号量死锁的情况,所谓信号量死锁就是指每个任务占有一个信号量,它们都因无法获取到对方已占有的信号量而无法运行,因此本身占有的信号量也无法被释放,这样,多个任务之间形成互相等待信号量的情况,所有任务都处于一种互相等待的永久等待状态,形成死锁。例如任务A已经获取到了信号量a,任务B已经获取到了信号量b,但任务A需要获取到信号量b才能继续运行,而任务B则需要获取到信号量a才能继续运行,这样2个任务就会形成死锁,这两个任务也就永远无法运行了。
void TaskA(void) { 已获取信号量a; (void)MDS_SemTake(&b, SEMWAITFEV); …… } |
void TaskB(void) { 已获取信号量b; (void)MDS_SemTake(&a, SEMWAITFEV); …… } |
前面我们主要介绍了二进制信号量互斥的作用,二进制信号量互斥的这个特点也可以用来触发任务,当同步事件使用。例如,当我们需要由任务B来触发任务A的运行时,先将信号量a初始化为空状态,由任务A获取信号量a,由任务B释放a。当任务B没有释放信号量a时,任务A就会因为获取不到信号量a而被阻塞,一旦任务B释放了信号量a,任务B就会被激活,开始运行。
void TaskA(void) { …… (void)MDS_SemTake(&a, SEMWAITFEV); …… } |
void TaskB(void) { …… (void)MDS_SemGive(&a); …… } |
到目前为止操作系统已经有了ready表、delay表和多个信号量表,系统运行时需要不断的更新任务TCB中的结构与各种调度表之间的关系。strTcbQue结构中的strQueHead节点可以挂接到ready表或者信号量调度表,strDelayQue结构中的strQueHead节点可以挂接到delay表,pstrSem指针需要指向阻塞任务的信号量。
第4章第7节nbsp;二进制信号量(二)相关推荐
- 第4章第7节nbsp;二进制信号量(一)
目前更新到5.3节,请在http://dl.dbank.com/c02ackpwp6下载5.3节的全部文档 本节源代码请在http://dl.dbank.com/c0fp2g5z9s下载 第7节 二进 ...
- FreeRTOS笔记篇:第七章 -- 资源管理(互斥锁、二进制信号量、死锁)
测试环境如下 stm32F103C8T6 MDK keil5 stm32cube + FreeRTOS 概述 在多任务处理系统中,如果一个任务开始访问资源,但在脱离运行状态之前没有完成其访问,则有可能 ...
- FreeRTOS笔记篇:第六章 - (二进制信号量 计数信号量 队列下)使用中断管理
目录 测试环境如下 概述 概况 中断对RTOS的影响 中断请求任务切换的宏 延迟中断处理 二进制信号量 xSemaphoreCreateBinary() xSemaphoreTake() xSemap ...
- 《UML面向对象设计基础》—第1章1.2节信息/实现隐藏
本节书摘来自异步社区<UML面向对象设计基础>一书中的第1章1.2节信息/实现隐藏,作者[美]Meliir Page-Jones,更多章节内容可以访问云栖社区"异步社区" ...
- 《敏捷迭代开发:管理者指南》—第2章2.5节渐进开发和自适应开发
本节书摘来自异步社区<敏捷迭代开发:管理者指南>一书中的第2章2.5节渐进开发和自适应开发,作者[美]Craig Larman,更多章节内容可以访问云栖社区"异步社区" ...
- 《实现模式(修订版)》—第1章1.2节那么,现在……
本节书摘来自异步社区<实现模式(修订版)>一书中的第1章1.2节那么,现在--,作者[美]Kent Beck,更多章节内容可以访问云栖社区"异步社区"公众号查看. 1. ...
- 《Adobe Acrobat DC经典教程》—第1章1.11节在阅读模式下查看PDF文件
本节书摘来自异步社区<Adobe Acrobat DC经典教程>一书中的第1章1.11节在阅读模式下查看PDF文件,作者[美]Lisa Fridsma(丽莎 弗里斯玛) , Brie Gy ...
- 《术以载道——软件过程改进实践指南》—第1章1.1节对CMMI的基本认识
本节书摘来自异步社区<术以载道--软件过程改进实践指南>一书中的第1章1.1节对CMMI的基本认识,作者任甲林,更多章节内容可以访问云栖社区"异步社区"公众号查看. 第 ...
- 《精通Wireshark》—第2章2.6节总结
本节书摘来自异步社区<精通Wireshark>一书中的第2章2.6节总结,作者[印度]Charit Mishra(夏里特 米什拉),更多章节内容可以访问云栖社区"异步社区&quo ...
最新文章
- 六个方法助您优化云存储成本
- 32位 shell.efi x86_通过grub,让32位的efi也能运行64位的Linux发行版
- Android服务之AIDL
- win7_iis报500.19和500.21错误问题解决
- sql的加减乘除运算_实现四则运算的一条sql语句
- css设置鼠标滑过背景变色;鼠标滑过背景变色
- Java的主要特性有哪些?
- void*和void类型
- 如何设置谷歌浏览器在新窗口中打开搜索链接
- mysql xa 实现_MySQL数据库分布式事务XA的实现原理分析
- 【分享】如果我没有那么优秀,我研究生阶段选择机器学习方向还有出路吗?...
- C语言大型程序的项目管理与实现
- Eclipse 快捷键 (应用中自己总结)
- TCGA数据库ensembl id 转为 gene Symbol,提取出需要的RNA种类表达谱列表信息
- 太阳诱电 | 汽车用金属功率电感器MCOIL™ LCEN 系列实现商品化
- C语言高级用法---container_of()
- XShell免费版的安装配置教程以及使用教程
- JavaScript中的常用浏览器对象
- np.piecewise函数用法
- Sentinel-高可用流量管理框架
热门文章
- cygwin编译x264:extras/avisynth_c.h 未知类型错误 'HMODULE'
- 当代的设计潮流是什么_当代设计如何多元化发展?
- 服务器系统各linux占有率,linux服务器 占有率
- 数据脱敏,你会了吗(三)
- 使用VCG检查服务器与ESXi兼容性 V2.0
- 杨洋python课程提高篇_杨洋老师:全民一起玩Python 基础篇+提高篇,老师幽默严谨不枯燥的学习编程...
- 论文阅读:multimodal remote sensing survey 遥感多模态综述
- window 端口被占用 关闭多个端口
- Java中的逆变与协变
- 口袋奇兵服务器维护,口袋奇兵跨服规则