Ucos源码分析------信号量

Ucos源码分析

1.Ucos源码分析------任务控制块与任务调度
2.Ucos源码分析------事件控制块与事件控制
3.Ucos源码分析------信号量
4.Ucos源码分析------邮箱与队列
5.Ucos源码分析------事件标志组
6.Ucos源码分析------内存管理
7.Ucos源码分析------临界区与中断管理
8.Ucos源码分析------OS启动
9.Ucos总结



OSSemPost通过调用OS_EventTaskRdy,实现将等待事件的最高优先级任务放入任务就绪表。
OSSemCreate 、OSSemPend 、OSSemPost以及OSSemDel(OS_DEL_ALWAYS)引起任务调度。
OSSemPost可以在中断中调用,但任务调度不是在OSSemPost时实现,任务调度是在退出中断OSIntExit()中实现的

1.创建信号量

OS_EVENT *OSSemCreate (INT16U cnt)
函数建立并初始化一个信号量
cnt:是建立的信号量的初始值,可以取0到65535之间的任何值。
返回值:返回指向分配给所建立的事件控制块的指针

OS_EVENT  *OSSemCreate (INT16U cnt)
{
#if OS_CRITICAL_METHOD == 3                                /* Allocate storage for CPU status register */
    OS_CPU_SR  cpu_sr;
#endif    
    OS_EVENT  *pevent;


    if (OSIntNesting > 0) {                                /* See if called from ISR ...               */
        return ((OS_EVENT *)0);                            /* ... can't CREATE from an ISR             */
    }
    OS_ENTER_CRITICAL();
    //获取一个空的事件控制块
    pevent = OSEventFreeList;                              /* Get next free event control block        */
    //如果此时不是最后一个空中的事件控制块,
    //空事件控制块链表指针移动下一个
    if (OSEventFreeList != (OS_EVENT *)0) {                /* See if pool of free ECB pool was empty   */
        OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
    }
    OS_EXIT_CRITICAL();
    //初始化事件为信号量事件,初始化信号量资源数目
    if (pevent != (OS_EVENT *)0) {                         /* Get an event control block               */
        pevent->OSEventType = OS_EVENT_TYPE_SEM;
        pevent->OSEventCnt  = cnt;                         /* Set semaphore value                      */
        pevent->OSEventPtr  = (void *)0;                   /* Unlink from ECB free list                */
        OS_EventWaitListInit(pevent);                      /* Initialize to 'nobody waiting' on sem.   */
    }
    return (pevent);
}

2. 删除信号量

OS_EVENT *OSSemDel (OS_EVENT *pevent, INT8U opt, INT8U *err)
删除信号量
pevent:需要删除的信号量
opt:出入的参数
  OS_DEL_NO_PEND:任务等待列表没有有任务,才删除信号量
  OS_DEL_ALWAYS:不管任务等待列表是否有任务,都删除信号量
err:传递错误的指针
  OS_ERR_PEND_ISR :从中断调用了该函数。 
  OS_ERR_PEVENT_NULL:pevent 指向的事件为空
  OS_ERR_EVENT_TYPE :pevent 不是指向信号量的指针
  OS_NO_ERR:没有错误
  OS_ERR_TASK_WAITING:OS_DEL_NO_PEND下,当前有等待的任务

OS_EVENT  *OSSemDel (OS_EVENT *pevent, INT8U opt, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3                                /* Allocate storage for CPU status register */
    OS_CPU_SR  cpu_sr;
#endif    
    BOOLEAN    tasks_waiting;

    //在中断中不能删除信号量
    if (OSIntNesting > 0) {                                /* See if called from ISR ...               */
        *err = OS_ERR_DEL_ISR;                             /* ... can't DELETE from an ISR             */
        return (pevent);
    }
#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {                         /* Validate 'pevent'                        */
        *err = OS_ERR_PEVENT_NULL;
        return (pevent);
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {        /* Validate event block type                */
        *err = OS_ERR_EVENT_TYPE;
        return (pevent);
    }
#endif
    OS_ENTER_CRITICAL();
    //判断释放是否有任务处于事件等待中
    if (pevent->OSEventGrp != 0x00) {                      /* See if any tasks waiting on semaphore    */
        tasks_waiting = TRUE;                              /* Yes                                      */
    } else {
        tasks_waiting = FALSE;                             /* No                                       */
    }
    switch (opt) {
        //任务等待列表没有任务时,删除信号量
        case OS_DEL_NO_PEND:                               /* Delete semaphore only if no task waiting */
            //任务等待列表没有任务时,删除信号量
             if (tasks_waiting == FALSE) {
                //清楚事件控制块的类型
                 pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
                 //将当前事件控制块放入,空闲事件控制块列表中
                 pevent->OSEventPtr  = OSEventFreeList;    /* Return Event Control Block to free list  */
                 OSEventFreeList     = pevent;             /* Get next free event control block        */
                 OS_EXIT_CRITICAL();
                 *err = OS_NO_ERR;
                 return ((OS_EVENT *)0);                   /* Semaphore has been deleted               */
             }//任务等待列表有任务时,错误并返回
             else {
                 OS_EXIT_CRITICAL();
                 *err = OS_ERR_TASK_WAITING;
                 return (pevent);
             }
        //不管任务等待列表是否有任务,都删除信号量
        case OS_DEL_ALWAYS:                                /* Always delete the semaphore              */
             //将事件控制块的等待任务列表的任务,放入就绪列表
             while (pevent->OSEventGrp != 0x00) {          /* Ready ALL tasks waiting for semaphore    */
                 OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM);
             }
             //清楚事件控制块的类型
             pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
             //将当前事件控制块放入,空闲事件控制块列表中
             pevent->OSEventPtr  = OSEventFreeList;        /* Return Event Control Block to free list  */
             OSEventFreeList     = pevent;                 /* Get next free event control block        */
             OS_EXIT_CRITICAL();
             //如果之前有任务处于事件等待,在删除信号量后,进行系统调度
             if (tasks_waiting == TRUE) {                  /* Reschedule only if task(s) were waiting  */
                 OS_Sched();                               /* Find highest priority task ready to run  */
             }
             *err = OS_NO_ERR;
             return ((OS_EVENT *)0);                       /* Semaphore has been deleted               */

        default:
             OS_EXIT_CRITICAL();
             *err = OS_ERR_INVALID_OPT;
             return (pevent);
    }
}

创建与删除信号量的过程
请添加图片描述


3.信号量等待

void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
pevent :指向信号量的指针
timeout:任务允许没有得到信号量时的最大时钟节拍恢复运行
    为0表示任务将持续等待信号量。
err:向包含错误码的变量的指针
  OS_NO_ERR :信号量不为零,无错
  OS_TIMEOUT :任务等待信号量超时
  OS_ERR_PEND_ISR :从中断调用了该函数
  OS_ERR_EVENT_TYPE :pevent 不是指向信号量的指针

void  OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3                           /* Allocate storage for CPU status register      */
    OS_CPU_SR  cpu_sr;
#endif    


    if (OSIntNesting > 0) {                           /* See if called from ISR ...                    */
        *err = OS_ERR_PEND_ISR;                       /* ... can't PEND from an ISR                    */
        return;
    }
#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */
        *err = OS_ERR_PEVENT_NULL;
        return;
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   /* Validate event block type                     */
        *err = OS_ERR_EVENT_TYPE;
        return;
    }
#endif
    OS_ENTER_CRITICAL();
    //如果有资源返回
    //占用一个资源,返回执行
    if (pevent->OSEventCnt > 0) {                     /* If sem. is positive, resource available ...   */
        pevent->OSEventCnt--;                         /* ... decrement semaphore only if positive.     */
        OS_EXIT_CRITICAL();
        *err = OS_NO_ERR;
        return;
    }
    //如果没有资源    
    //当前任务状态为等待信号量                                         /* Otherwise, must wait until event occurs       */
    OSTCBCur->OSTCBStat |= OS_STAT_SEM;               /* Resource not available, pend on semaphore     */
    //设置等待延时
    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();
    //判断是由于资源释放调度回的,还是超时调度回的
    //如果是超时情况
    if (OSTCBCur->OSTCBStat & OS_STAT_SEM) {          /* Must have timed out if still waiting for event*/
        OS_EventTO(pevent);
        OS_EXIT_CRITICAL();
        *err = OS_TIMEOUT;                            /* Indicate that didn't get event within TO      */
        return;
    }
    //如果是获得资源调度回的
    OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
    OS_EXIT_CRITICAL();
    *err = OS_NO_ERR;
}

4.信号量释放

INT8U OSSemPost (OS_EVENT *pevent)
释放一个信号量
pevent:指向信号量的指针
返回参数:
  OS_NO_ERR :成功释放信号量
  OS_SEM_OVF :当前信号量的值溢出
  OS_ERR_EVENT_TYPE :pevent 不是指向信号量的指针

INT8U  OSSemPost (OS_EVENT *pevent)
{
    //确定进入临界区的方式
#if OS_CRITICAL_METHOD == 3                                /* Allocate storage for CPU status register */
    OS_CPU_SR  cpu_sr;                               
#endif    


#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {                         /* Validate 'pevent'                        */
        return (OS_ERR_PEVENT_NULL);
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {        /* Validate event block type                */
        return (OS_ERR_EVENT_TYPE);
    }
#endif
    OS_ENTER_CRITICAL();
    //如果有任务在等待信号量事件
    if (pevent->OSEventGrp != 0x00) {                      /* See if any task waiting for semaphore    */
    //将等待信号量事件的最高优先级的任务中等待信号量状态置0
        OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM);   /* Ready highest prio task waiting on event */
        OS_EXIT_CRITICAL();
        //发生一次调度
        OS_Sched();                                        /* Find highest priority task ready to run  */
        return (OS_NO_ERR);
    }
     //如果无任务在等待信号量事件,并且信号量的数目小于没有超过最多的信号量
    if (pevent->OSEventCnt < 65535) {                 /* Make sure semaphore will not overflow         */
    //信号量资源加一
        pevent->OSEventCnt++;                         /* Increment semaphore count to register event   */
        OS_EXIT_CRITICAL();
        return (OS_NO_ERR);
    }
    OS_EXIT_CRITICAL();                               /* Semaphore value has reached its maximum       */
    return (OS_SEM_OVF);
}

5.优先级翻转

当任务以独占方式使用共享资源时,会出现低优先级任务先于高优先级任务而被运行的现象,这种现象叫做任务优先级反转。

任务运行受到优先级和事件两个方面影响。

A、B、C三个任务的运行情况。其中,任务A的优先级别高于任务B,任务B的优先级别高于任务C。任务A和任务C都要使用同一个信号量,而用于保护该资源的信号量在同一时间只能允许一个任务以独占的方式对该资源进行访问,当C正在使用信号量,A处于阻塞等待信号两时,比C优先级高的任务B进行调度后运行,出现了任务B由于任务A运行的情况

在这里插入图片描述

6.互斥信号量

解决优先级翻转的办法之一,是使获得信号量任务的优先级别,在使用共享资源期间暂时提升到所有任务最高优先级的高一个级别上,使该任务不被其他的任务所打断,从而能尽快地使用完共享资源并释放信号量,然后在释放了信号量之后再恢复该任务原来的优先级别。
与信号量的区别是事件控制块的OS_EVENT的OSEventCnt用来存放被提升到的任务优先级。

pevent->OSEventCnt  = (prio << 8) | OS_MUTEX_AVAILABLE;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值