ucos实时操作系统的任务间通信有好多种,本人主要学习了sem, mutex, queue, messagebox这四种。系统内核代码中,这几种任务间通信机制的实现机制相似,接下来记录一下本人对核心代码的学习心得,供以后回来看看,不过比较遗憾的是没有仔细学习扩展代码的功能实现部分。ucos操作系统的内核代码实现相对简单,但是对理解其他操作系统内核相同功能有帮助。

ucos的任务间通信机制主要是基于event实现的,其实理解这个event不用翻译成中文事件,就叫event感觉还更容易接收。下面是操作系统event的的数据结构:

typedef struct os_event {
INT8U OSEventType; /* Type of event control block (see OS_EVENT_TYPE_xxxx) */
void *OSEventPtr; /* Pointer to message or queue structure */
INT16U OSEventCnt; /* Semaphore Count (not used if other EVENT type) */
OS_PRIO OSEventGrp; /* Group corresponding to tasks waiting for event to occur */
OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; /* List of tasks waiting for event to occur */
} OS_EVENT;

上面的event是实现sem, mutex, queue, messagebox等必不可少的结构,其实该结构相对简单,主要包括OSEventType用于记录当前event是前面四种机制中的哪一种;OSEventPtr是一个指针用于指向messagebox和queue要传递的内容的地址;OSEventCnt对于sem来说,是一个计数值,而对于mutex来说则是记录任务优先级的一个变量;OSEventGrp和OSEventTbl和前面讲过的OSRdyGrp和OSRdyTbl的类似,主要记录有哪些任务在等待当前event的。对于上面四种机制的实现,首先需要创建一个event才可以实现各自的功能,比如说OSSemCreate,OSMutexCreate等。

ucos的sem主要的功能是对资源使用的一种限制,当信号量的值设置n就代表允许n个任务可以对当前资源进行使用,如果资源被n个任务占用,第n+1个任务要使用该资源时,需要等待前面n个任务中有1个或者多个释放对资源的使用权。sem的具体功能就是OSSemCreate,OSSemPend,OSSemPost等这三个函数实现的,下面将具体分析一下这三个函数。

对sem来说,首先需要创建一个event结构,然后设置这个sem event允许几个任务可以同时使用,具体函数OSSemCreate首先如下:

OS_EVENT  *OSSemCreate (INT16U cnt)
{
OS_EVENT *pevent; if (OSIntNesting > 0u) { /* See if called from ISR ... */
return ((OS_EVENT *)); /* ... can't CREATE from an ISR */
}
OS_ENTER_CRITICAL();
pevent = OSEventFreeList; /* Get next free event control block */
if (OSEventFreeList != (OS_EVENT *)) { /* See if pool of free ECB pool was empty */
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)) { /* Get an event control block */
pevent->OSEventType = OS_EVENT_TYPE_SEM;
pevent->OSEventCnt = cnt; /* Set semaphore value */
pevent->OSEventPtr = (void *); /* Unlink from ECB free list */
OS_EventWaitListInit(pevent); /* Initialize to 'nobody waiting' on sem. */
}
return (pevent);
}

上面提到的四种任务间通信机制不允许在中断中创建,所以OSSemCreate首先判断当前创建过程是不是在中断中,也就是判断OSIntNesting是否大于0,有全局变量OSEventFreeList记录当前操作系统通还有多少个free的event在列表中,OSSemCreate从free list中取一个event机构,然后OSEventFreeList指向下一个未被使用的event,然后设置OSEventType和OSEventFreeListCnt,cnt是有create函数带入的参数,有使用该函数的程序员自己设定,sem不使用OSEventPtr变量,因为是指针,所以设置为0,之后初始化event的event group和 event table,表示当前没有任务等待创建的sem,返回event结构的地址,给之后讲的pend和post使用。

其次,就要讲到sem机制post和pend的两个操作了,这两个操作是成对使用的,简单点讲pend主要功能是检查create的event中的OSEventCnt是否大于0,如果大于0,说明需要保护的资源还允许任务使用,这时候只需要将OSEventCnt减1操作,表示又有一个任务占用了资源的使用权;如果OSEventCnt不大于0,则说明任务可以使用的资源的权限已经达到上限,这时候就要把要使用的该资源的任务挂起(1),让其处于等待状态,并把任务放到event的event group和event table中,处于等待状态的任务只能等其他任务释放对资源的使用权之后才可以继续运行。post的主要功能是要释放被占用的资源的使用权,其操作首先会检查当前event的group和table中有没有在等待当前event的任务存在,如果有使用权直接转给等待的任务(1),不需要对OSEventCnt加操作,如果没有等待当前event的任务,则只需要将OSEventCnt加1操作,说明资源的可使用权又大了一个。接下来我们看一下具体pend和post的代码具体流程。

void  OSSemPend (OS_EVENT  *pevent,
INT32U timeout,
INT8U *perr)
{
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* Validate event block type */
*perr = OS_ERR_EVENT_TYPE;
return;
}
if (OSIntNesting > 0u) { /* See if called from ISR ... */
*perr = OS_ERR_PEND_ISR; /* ... can't PEND from an ISR */
return;
}
OS_ENTER_CRITICAL();
(1)===================================================================================================
if (pevent->OSEventCnt > 0u) { /* If sem. is positive, resource available ... */
pevent->OSEventCnt--; /* ... decrement semaphore only if positive. */
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
return;
}
(2)===================================================================================================
/* Otherwise, must wait until event occurs */
OSTCBCur->OSTCBStat |= OS_STAT_SEM; /* Resource not available, pend on semaphore */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
OSTCBCur->OSTCBDly = timeout; /* Store pend timeout in 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 */
break; case OS_STAT_PEND_TO:
default:
OS_EventTaskRemove(OSTCBCur, pevent);
*perr = OS_ERR_TIMEOUT; /* Indicate that we didn't get event 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();
(3)=====================================================================================================
}

OSSemPend函数首先判断当前的任务操作是否是一个sem event;然后判断是否中断中,如果是则返回错误;之后检查该sem event的资源限号量是否大于0,如果是则进行减操作表示信号量被一个任务占用,如果不大于0,说明对改event来说,已经没有信号量供使用需要的操作是挂起当前任务,并调用任务切换函数,在上面的代码中在OS_Sched()上面的四步操作是挂起任务操作,直到有可以使用的信号量再次执行任务切换时,会切换到当前任务继续从OS_Sched开始执行,可以看出下面的操作是从event的等待group和table中删除该任务,并且改变任务的状态为OS_STAT_RDY。当然任务的还有statpend状态,在此不做详细介绍。

INT8U  OSSemPost (OS_EVENT *pevent)
{
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* Validate event block type */
return (OS_ERR_EVENT_TYPE);
}
OS_ENTER_CRITICAL();
(1)=====================================================================================================
if (pevent->OSEventGrp != 0u) { /* See if any task waiting for semaphore */
/* Ready HPT waiting on event */
(void)OS_EventTaskRdy(pevent, (void *), OS_STAT_SEM, OS_STAT_PEND_OK);
OS_EXIT_CRITICAL();
OS_Sched(); /* Find HPT ready to run */
return (OS_ERR_NONE);
}
(2)====================================================================================================
if (pevent->OSEventCnt < 65535u) { /* Make sure semaphore will not overflow */
pevent->OSEventCnt++; /* Increment semaphore count to register event */
OS_EXIT_CRITICAL();
return (OS_ERR_NONE);
}
OS_EXIT_CRITICAL(); /* Semaphore value has reached its maximum */
return (OS_ERR_SEM_OVF);
(3)===================================================================================================
}

OSSemPost函数实现相对简单,在判断完当前操作是否是sem event之后,就判断当前的event等待group中是否有等待该event的任务,如果有就设置等待该event的任务拥有该信号量的使用权限设置改任务处于RDY状态然后执行任务调度,让等待的任务得以执行,在此有一个巧妙地设计就是对于信号量的计量值OSEventCnt不做操作,因为如果event等待group中有等待任务的话,意味着在此释放信号量,等待任务就获得了信号量。如果在等待group中没有等待任务的话,就会给OSEventCnt做加操作,表示有一个任务释放了该信号量。在此说明的是该函数主要的操作在OS_EventTaskRdy中,其主要功能就是把event的等待group和table中的任务解析出来放到任务Rdy列表中,并把event中的等待任务从列表中删除。

如果信号量的值设置为1的话,就是一个特殊情况,表示只有1个任务拥有信号量,有点互斥的意味,但是与互斥锁不同的是,互斥锁为了防止低优先级占有资源,却因为优先级的低不被执行,而高优先级得不到被低优先级占有的资源而不能执行,使用了优先级继承机制。之后会介绍ucos互斥锁的内核实现。

ucos实时操作系统学习笔记——任务间通信(信号量)的更多相关文章

  1. ucos实时操作系统学习笔记——任务间通信(消息)

    ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...

  2. ucos实时操作系统学习笔记——任务间通信(队列)

    ucos操作系统中的queue机制同样使用了event机制来实现,其实和前面的sem,mutex实现类似,所不同的是对sem而言,任务想获得信号量,对mutex而言,任务想获得的是互斥锁.任务间通信的 ...

  3. ucos实时操作系统学习笔记——任务间通信(互斥锁)

    想讲一下ucos任务间通信中的mutex,感觉其设计挺巧妙,同sem一样使用的是event机制实现的,代码不每一行都分析,因为讲的没邵贝贝老师清楚,主要讲一下mutex的内核是如何实现的.可以理解互斥 ...

  4. ucos实时操作系统学习笔记——操作系统在STM32的移植

    使用ucos实时操作系统是在上学的时候,导师科研项目中.那时候就是网上找到操作系统移植教程以及应用教程依葫芦画瓢,功能实现也就罢了,没有很深入的去研究过这个东西.后来工作了,闲来无聊就研究了一下这个只 ...

  5. ucos实时操作系统学习笔记——内核结构和任务创建

    对于ucos实时操作系统,邵贝贝的那本书已经写得很详细了,我因为之前不深的研究过ucos,所以在这里做一个笔记,写一些个人对该操作系统的理解,仅仅是个人理解,如果有人看到这边随笔有不对的地方,望给我指 ...

  6. RTX51 Tiny实时操作系统学习笔记—初识RTX51 Tiny

     一,RTX51 Tiny简单介绍    RTX51 Tiny是一种实时操作系统(RTOS),能够用它来建立多个任务(函数)同一时候运行的应用(从宏观上看是同一时候运行的,但从微观上看,还是独立运行的 ...

  7. 操作系统学习笔记----进程/线程模型----Coursera课程笔记

    操作系统学习笔记----进程/线程模型----Coursera课程笔记 进程/线程模型 0. 概述 0.1 进程模型 多道程序设计 进程的概念.进程控制块 进程状态及转换.进程队列 进程控制----进 ...

  8. 操作系统学习笔记5 | 用户级线程 && 内核级线程

    在上一部分中,我们了解到操作系统实现多进程图像需要组织.切换.考虑进程之间的影响,组织就是用PCB的队列实现,用到了一些简单的数据结构知识.而本部分重点就是进程之间的切换. 参考资料: 课程:哈工大操 ...

  9. 深挖计算机基础:趣谈Linux操作系统学习笔记

    参考极客时间专栏<趣谈Linux操作系统>学习笔记 核心原理篇:内存管理 趣谈Linux操作系统学习笔记:第二十讲 趣谈Linux操作系统学习笔记:第二十一讲 趣谈Linux操作系统学习笔 ...

随机推荐

  1. C语言 · Torry的困惑(基本型)

    问题描述 Torry从小喜爱数学.一天,老师告诉他,像2.3.5.7--这样的数叫做质数.Torry突然想到一个问题,前10.100.1000.10000--个质数的乘积是多少呢?他把这个问题告诉老师 ...

  2. 我为NET狂官方面试题-数据库篇答案

    题目:http://www.cnblogs.com/dunitian/p/6028838.html 汇总:http://www.cnblogs.com/dunitian/p/5977425.html ...

  3. .NET Core的日志[3]:将日志写入Debug窗口

    定义在NuGet包"Microsoft.Extensions.Logging.Debug"中的DebugLogger会直接调用Debug的WriteLine方法来写入分发给它的日志 ...

  4. IOS FMDB 获取数据库表和表中的数据

    ios开发中,经常会用到数据库sqlite的知识,除了增,删,改,查之外,我们说说如何获取数据库中有多少表和表相关的内容. 前言 跟数据库使用相关的一般的增删改查的语句,这里就不做解释了.在网上有很多 ...

  5. scanf类型不匹配造成死循环

        int i = 0; while (flag) { printf("please input a number >>> "); scanf("% ...

  6. Jqprint实现页面打印

    好些项目需要实现页面打印,特别是一些后台管理类系统,下面介绍一款轻量级的打印插件: 1.实现页面打印要引入jQuery和Jqprint.点击下载Jqprint插件 <script languag ...

  7. arcgis api for js入门开发系列六地图分屏对比(含源代码)

    上一篇实现了demo的地图标绘模块,本篇新增地图地图分屏对比模块,截图如下(源代码见文章底部): 对效果图的简单介绍一下,在demo只采用了两分屏对比,感兴趣的话,可以在两分屏的基础上拓展,修改css ...

  8. Unity C#最佳实践(上)

    本文为<effective c#>的读书笔记,此书类似于大名鼎鼎的<effective c++>,是入门后提高水平的进阶读物,此书提出了50个改进c#代码的原则,但是由于主要针 ...

  9. javascript中的变量作用域以及变量提升

    在javascript中, 理解变量的作用域以及变量提升是非常有必要的.这个看起来是否很简单,但其实并不是你想的那样,还要一些重要的细节你需要理解. 变量作用域 “一个变量的作用域表示这个变量存在的上 ...

  10. 初尝Brnshop移植到Linux Mono Jexus环境运行

    brnshop是最近社区上比较火的开源商城. Jexus是Linux上的web服务器,简单说就是Linux的iis吧.特别感谢作者宇内流云的指点 一.根据http://www.cnblogs.com/ ...