目前更新到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;二进制信号量(二)相关推荐

  1. 第4章第7节nbsp;二进制信号量(一)

    目前更新到5.3节,请在http://dl.dbank.com/c02ackpwp6下载5.3节的全部文档 本节源代码请在http://dl.dbank.com/c0fp2g5z9s下载 第7节 二进 ...

  2. FreeRTOS笔记篇:第七章 -- 资源管理(互斥锁、二进制信号量、死锁)

    测试环境如下 stm32F103C8T6 MDK keil5 stm32cube + FreeRTOS 概述 在多任务处理系统中,如果一个任务开始访问资源,但在脱离运行状态之前没有完成其访问,则有可能 ...

  3. FreeRTOS笔记篇:第六章 - (二进制信号量 计数信号量 队列下)使用中断管理

    目录 测试环境如下 概述 概况 中断对RTOS的影响 中断请求任务切换的宏 延迟中断处理 二进制信号量 xSemaphoreCreateBinary() xSemaphoreTake() xSemap ...

  4. 《UML面向对象设计基础》—第1章1.2节信息/实现隐藏

    本节书摘来自异步社区<UML面向对象设计基础>一书中的第1章1.2节信息/实现隐藏,作者[美]Meliir Page-Jones,更多章节内容可以访问云栖社区"异步社区" ...

  5. 《敏捷迭代开发:管理者指南》—第2章2.5节渐进开发和自适应开发

    本节书摘来自异步社区<敏捷迭代开发:管理者指南>一书中的第2章2.5节渐进开发和自适应开发,作者[美]Craig Larman,更多章节内容可以访问云栖社区"异步社区" ...

  6. 《实现模式(修订版)》—第1章1.2节那么,现在……

    本节书摘来自异步社区<实现模式(修订版)>一书中的第1章1.2节那么,现在--,作者[美]Kent Beck,更多章节内容可以访问云栖社区"异步社区"公众号查看. 1. ...

  7. 《Adobe Acrobat DC经典教程》—第1章1.11节在阅读模式下查看PDF文件

    本节书摘来自异步社区<Adobe Acrobat DC经典教程>一书中的第1章1.11节在阅读模式下查看PDF文件,作者[美]Lisa Fridsma(丽莎 弗里斯玛) , Brie Gy ...

  8. 《术以载道——软件过程改进实践指南》—第1章1.1节对CMMI的基本认识

    本节书摘来自异步社区<术以载道--软件过程改进实践指南>一书中的第1章1.1节对CMMI的基本认识,作者任甲林,更多章节内容可以访问云栖社区"异步社区"公众号查看. 第 ...

  9. 《精通Wireshark》—第2章2.6节总结

    本节书摘来自异步社区<精通Wireshark>一书中的第2章2.6节总结,作者[印度]Charit Mishra(夏里特 米什拉),更多章节内容可以访问云栖社区"异步社区&quo ...

最新文章

  1. 六个方法助您优化云存储成本
  2. 32位 shell.efi x86_通过grub,让32位的efi也能运行64位的Linux发行版
  3. Android服务之AIDL
  4. win7_iis报500.19和500.21错误问题解决
  5. sql的加减乘除运算_实现四则运算的一条sql语句
  6. css设置鼠标滑过背景变色;鼠标滑过背景变色
  7. Java的主要特性有哪些?
  8. void*和void类型
  9. 如何设置谷歌浏览器在新窗口中打开搜索链接
  10. mysql xa 实现_MySQL数据库分布式事务XA的实现原理分析
  11. 【分享】如果我没有那么优秀,我研究生阶段选择机器学习方向还有出路吗?...
  12. C语言大型程序的项目管理与实现
  13. Eclipse 快捷键 (应用中自己总结)
  14. TCGA数据库ensembl id 转为 gene Symbol,提取出需要的RNA种类表达谱列表信息
  15. 太阳诱电 | 汽车用金属功率电感器MCOIL™ LCEN 系列实现商品化
  16. C语言高级用法---container_of()
  17. XShell免费版的安装配置教程以及使用教程
  18. JavaScript中的常用浏览器对象
  19. np.piecewise函数用法
  20. Sentinel-高可用流量管理框架

热门文章

  1. cygwin编译x264:extras/avisynth_c.h 未知类型错误 'HMODULE'
  2. 当代的设计潮流是什么_当代设计如何多元化发展?
  3. 服务器系统各linux占有率,linux服务器 占有率
  4. 数据脱敏,你会了吗(三)
  5. 使用VCG检查服务器与ESXi兼容性 V2.0
  6. 杨洋python课程提高篇_杨洋老师:全民一起玩Python 基础篇+提高篇,老师幽默严谨不枯燥的学习编程...
  7. 论文阅读:multimodal remote sensing survey 遥感多模态综述
  8. window 端口被占用 关闭多个端口
  9. Java中的逆变与协变
  10. 口袋奇兵服务器维护,口袋奇兵跨服规则