SylixOS里的计数型信号量实现原理详解

实现原理

计数型信号量只使用事件控制块的第一个等待队列,通过EVENT_SEM_Q(0)来索引,最大计数值在创建时设置,事件类型为LW_TYPE_EVENT_SEMC。

计数型信号量的实现原理和二进制信号量极为相识,主要区别就是计数型信号量可以成功请求多次。
请先详细阅读《SylixOS里的二进制信号量实现原理详解》。

源码详解

下面是各接口的实现源码,为了容易理解,这里会对源码做一些调整,包括删除一些条件编译和条件执行代码,删除一些重入保护代码,删除一些异常和错误处理,删除一些条件检查,展开一些关键宏操作,这里还去掉了信号唤醒的相关处理,增加更多注释等。

/*********************************************************************************************************
** 函数名称: API_SemaphoreCCreate
** 功能描述: 计数型信号量建立
** 输 入  : 
**           pcName          事件名缓冲区
**           ulInitCounter   初始化计数值
**           ulMaxCounter    最大计数值
**           ulOption        事件选项
**           pulId           事件ID指针
** 输 出  : 事件句柄
*********************************************************************************************************/
LW_OBJECT_HANDLE  API_SemaphoreCCreate (CPCHAR             pcName,
                                        ULONG              ulInitCounter, 
                                        ULONG              ulMaxCounter,
                                        ULONG              ulOption,
                                        LW_OBJECT_ID      *pulId)
{
     
    REGISTER PLW_CLASS_EVENT       pevent;
    REGISTER ULONG                 ulI;
             PLW_CLASS_WAITQUEUE   pwqTemp[2];
    REGISTER ULONG                 ulIdTemp;
    

    if (!ulMaxCounter) {
     
        return  (LW_OBJECT_HANDLE_INVALID);
    }
    
    if (ulInitCounter > ulMaxCounter) {
     
        return  (LW_OBJECT_HANDLE_INVALID);
    }


    __KERNEL_MODE_PROC(
        pevent = _Allocate_Event_Object();                              /*  获得一个事件控制块          */
    );
    
    if (pcName) {
                                                            /*  拷贝名字                    */
        lib_strcpy(pevent->EVENT_cEventName, pcName);
    } else {
     
        pevent->EVENT_cEventName[0] = PX_EOS;                           /*  清空名字                    */
    }
                                                                        /*  开始初始化                  */
    pevent->EVENT_ucType       = LW_TYPE_EVENT_SEMC;
    pevent->EVENT_ulCounter    = ulInitCounter;
    pevent->EVENT_ulMaxCounter = ulMaxCounter;
    pevent->EVENT_ulOption     = ulOption;
    pevent->EVENT_pvPtr        = LW_NULL;
    pevent->EVENT_pvTcbOwn     = LW_NULL;
    
    pwqTemp[0] = &pevent->EVENT_wqWaitQ[0];
    pwqTemp[1] = &pevent->EVENT_wqWaitQ[1];
    pwqTemp[0]->WQ_usNum = 0;                                           /*  没有线程                    */
    pwqTemp[1]->WQ_usNum = 0;
    
    for (ulI = 0; ulI < __EVENT_Q_SIZE; ulI++) {
     
        pwqTemp[0]->WQ_wlQ.WL_pringPrio[ulI] = LW_NULL;
        pwqTemp[1]->WQ_wlQ.WL_pringPrio[ulI] = LW_NULL;
    }
    
    ulIdTemp = _MakeObjectId(_OBJECT_SEM_C, 
                             LW_CFG_PROCESSOR_NUMBER, 
                             pevent->EVENT_usIndex);                    /*  构建对象 id                 */

    if (pulId) {
     
        *pulId = ulIdTemp;
    }
    
    return  (ulIdTemp);
}
/*********************************************************************************************************
** 函数名称: API_SemaphoreCPend
** 功能描述: 等待计数型信号量
** 输 入  : 
**           ulId            事件句柄
**           ulTimeout       等待时间
** 输 出  : 
*********************************************************************************************************/
ULONG  API_SemaphoreCPend (LW_OBJECT_HANDLE  ulId, ULONG  ulTimeout)
{
     
             INTREG                iregInterLevel;
             
             PLW_CLASS_TCB         ptcbCur;
    REGISTER UINT16                usIndex;
    REGISTER PLW_CLASS_EVENT       pevent;
    REGISTER UINT8                 ucPriorityIndex;
    REGISTER PLW_LIST_RING        *ppringList;
             ULONG                 ulTimeSave;                          /*  系统事件记录                */
             INT                   iSchedRet;
            
             ULONG                 ulEventOption;                       /*  事件创建选项                */
             
    usIndex = _ObjectGetIndex(ulId);
    
    LW_TCB_GET_CUR_SAFE(ptcbCur);                                       /*  当前任务控制块              */
    
    pevent = &_K_eventBuffer[usIndex];
    
    iregInterLevel = __KERNEL_ENTER_IRQ();                              /*  进入内核                    */

    if (pevent->EVENT_ulCounter) {
                                           /*  事件有效                    */
        pevent->EVENT_ulCounter--;
        __KERNEL_EXIT_IRQ(iregInterLevel);                              /*  退出内核                    */
        return  (ERROR_NONE);
    }
    
    if (ulTimeout == LW_OPTION_NOT_WAIT) {
                                   /*  不等待                      */
        __KERNEL_EXIT_IRQ(iregInterLevel);                              /*  退出内核                    */
        return  (ERROR_THREAD_WAIT_TIMEOUT);
    }
    
    ptcbCur->TCB_iPendQ         = EVENT_SEM_Q;
    ptcbCur->TCB_usStatus      |= LW_THREAD_STATUS_SEM;                 /*  写状态位,开始等待          */
    ptcbCur->TCB_ucWaitTimeout  = LW_WAIT_TIME_CLEAR;                   /*  清空等待时间                */
    
    if (ulTimeout == LW_OPTION_WAIT_INFINITE) {
                              /*  是否是无穷等待              */
        ptcbCur->TCB_ulDelay = 0ul;
    } else {
     
        ptcbCur->TCB_ulDelay = ulTimeout;                               /*  设置超时时间                */
    }
    __KERNEL_TIME_GET_NO_SPINLOCK(ulTimeSave, ULONG);                   /*  记录系统时间                */
    
    if (pevent->EVENT_ulOption & LW_OPTION_WAIT_PRIORITY) {
                  /*  按优先级等待                */
        _EVENT_INDEX_Q_PRIORITY(ptcbCur->TCB_ucPriority, ucPriorityIndex);
        _EVENT_PRIORITY_Q_PTR(EVENT_SEM_Q, ppringList, ucPriorityIndex);
        ptcbCur->TCB_ppringPriorityQueue = ppringList;                  /*  记录等待队列位置            */
        _EventWaitPriority(pevent, ppringList);                         /*  加入优先级等待表            */
    
    } else {
                                                                 /*  按 FIFO 等待                */
        _EVENT_FIFO_Q_PTR(EVENT_SEM_Q, ppringList);                     /*  确定 FIFO 队列的位置        */
        _EventWaitFifo(pevent, ppringList);                             /*  加入 FIFO 等待表            */
    }
    
    KN_INT_ENABLE(iregInterLevel);                                      /*  使能中断                    */

    ulEventOption = pevent->EVENT_ulOption;
    
    __KERNEL_EXIT();                                        /*  调度器解锁                  */
    
    if (ptcbCur->TCB_ucWaitTimeout == LW_WAIT_TIME_OUT) {
                    /*  等待超时                    */
        return  (ERROR_THREAD_WAIT_TIMEOUT);
        
    } else {
     
        if (ptcbCur->TCB_ucIsEventDelete == LW_EVENT_EXIST) {
                /*  事件是否存在                */
            return  (ERROR_NONE);
        } else {
                        
            return  (ERROR_EVENT_WAS_DELETED);                          /*  已经被删除                  */
        }
    }
}
/*********************************************************************************************************
** 函数名称: API_SemaphoreCPost
** 功能描述: 释放计数型信号量
** 输 入  : 
**           ulId                   事件句柄
** 输 出  : 
*********************************************************************************************************/
ULONG  API_SemaphoreCPost (LW_OBJECT_HANDLE  ulId)
{
     
             INTREG                iregInterLevel;
    REGISTER UINT16                usIndex;
    REGISTER PLW_CLASS_EVENT       pevent;
    REGISTER PLW_CLASS_TCB         ptcb;
    REGISTER PLW_LIST_RING        *ppringList;                          /*  等待队列地址                */
    
    usIndex = _ObjectGetIndex(ulId);
    pevent = &_K_eventBuffer[usIndex];
    
    iregInterLevel = __KERNEL_ENTER_IRQ();                              /*  进入内核                    */
    
    if (_EventWaitNum(EVENT_SEM_Q, pevent)) {
     
        if (pevent->EVENT_ulOption & LW_OPTION_WAIT_PRIORITY) {
              /*  优先级等待队列              */
            _EVENT_DEL_Q_PRIORITY(EVENT_SEM_Q, ppringList);             /*  激活优先级等待线程          */
            ptcb = _EventReadyPriorityLowLevel(pevent, LW_NULL, ppringList);
        
        } else {
     
            _EVENT_DEL_Q_FIFO(EVENT_SEM_Q, ppringList);                 /*  激活FIFO等待线程            */
            ptcb = _EventReadyFifoLowLevel(pevent, LW_NULL, ppringList);
        }
        
        KN_INT_ENABLE(iregInterLevel);                                  /*  使能中断                    */
        _EventReadyHighLevel(ptcb, LW_THREAD_STATUS_SEM);               /*  处理 TCB                    */
        
        __KERNEL_EXIT();                                                /*  退出内核                    */
        return  (ERROR_NONE);
    
    } else {
                                                                 /*  没有线程等待                */
        if (pevent->EVENT_ulCounter < pevent->EVENT_ulMaxCounter) {
          /*  检查是否还有空间加          */
            pevent->EVENT_ulCounter++;
            __KERNEL_EXIT_IRQ(iregInterLevel);                          /*  退出内核                    */
            return  (ERROR_NONE);
        
        } else {
                                                             /*  已经满了                    */
            __KERNEL_EXIT_IRQ(iregInterLevel);                          /*  退出内核                    */
            return  (ERROR_EVENT_FULL);
        }
    }
}
/*********************************************************************************************************
** 函数名称: API_SemaphoreCFlush
** 功能描述: 释放等待计数型信号量的所有线程
** 输 入  : 
**           ulId                   事件句柄
**           pulThreadUnblockNum    被解锁的线程数量   可以为NULL
** 输 出  : 
*********************************************************************************************************/
ULONG  API_SemaphoreCFlush (LW_OBJECT_HANDLE  ulId, ULONG  *pulThreadUnblockNum)
{
     
             INTREG                iregInterLevel;
    REGISTER UINT16                usIndex;
    REGISTER PLW_CLASS_EVENT       pevent;
    REGISTER PLW_CLASS_TCB         ptcb;
    REGISTER PLW_LIST_RING        *ppringList;                          /*  等待队列地址                */
    
    usIndex = _ObjectGetIndex(ulId);
    pevent = &_K_eventBuffer[usIndex];
    
    iregInterLevel = __KERNEL_ENTER_IRQ();                              /*  进入内核                    */
    
    if (pulThreadUnblockNum) {
                                               /*  保存将要解锁的线程数量      */
        *pulThreadUnblockNum = _EventWaitNum(EVENT_SEM_Q, pevent);
    }

    while (_EventWaitNum(EVENT_SEM_Q, pevent)) {
                             /*  是否存在正在等待的任务      */
        if (pevent->EVENT_ulOption & LW_OPTION_WAIT_PRIORITY) {
              /*  优先级等待队列              */
            _EVENT_DEL_Q_PRIORITY(EVENT_SEM_Q, ppringList);             /*  激活优先级等待线程          */
            ptcb = _EventReadyPriorityLowLevel(pevent, LW_NULL, ppringList);
        
        } else {
     
            _EVENT_DEL_Q_FIFO(EVENT_SEM_Q, ppringList);                 /*  激活FIFO等待线程            */
            ptcb = _EventReadyFifoLowLevel(pevent, LW_NULL, ppringList);
        }

        KN_INT_ENABLE(iregInterLevel);                                  /*  打开中断                    */
        _EventReadyHighLevel(ptcb, LW_THREAD_STATUS_SEM);               /*  处理 TCB                    */
        iregInterLevel = KN_INT_DISABLE();                              /*  关闭中断                    */
    }

    __KERNEL_EXIT_IRQ(iregInterLevel);                                  /*  退出内核                    */
    
    return  (ERROR_NONE);
}
/*********************************************************************************************************
** 函数名称: API_SemaphoreCDelete
** 功能描述: 计数型信号量删除
** 输 入  : 
**           pulId            事件句柄指针
** 输 出  : 
*********************************************************************************************************/
ULONG  API_SemaphoreCDelete (LW_OBJECT_HANDLE  *pulId)
{
     
             INTREG                iregInterLevel;
    REGISTER UINT16                usIndex;
    REGISTER PLW_CLASS_EVENT       pevent;
    REGISTER PLW_CLASS_TCB         ptcb;
    REGISTER PLW_LIST_RING        *ppringList;                          /*  等待队列地址                */
    
    REGISTER LW_OBJECT_HANDLE      ulId;
    
    ulId = *pulId;
    
    usIndex = _ObjectGetIndex(ulId);
    pevent = &_K_eventBuffer[usIndex];
    
    iregInterLevel = __KERNEL_ENTER_IRQ();                              /*  进入内核                    */
    
    _ObjectCloseId(pulId);                                              /*  清除句柄                    */

    pevent->EVENT_ucType = LW_TYPE_EVENT_UNUSED;                        /*  事件类型为空                */
    
    while (_EventWaitNum(EVENT_SEM_Q, pevent)) {
                             /*  是否存在正在等待的任务      */
        if (pevent->EVENT_ulOption & LW_OPTION_WAIT_PRIORITY) {
              /*  优先级等待队列              */
            _EVENT_DEL_Q_PRIORITY(EVENT_SEM_Q, ppringList);             /*  激活优先级等待线程          */
            ptcb = _EventReadyPriorityLowLevel(pevent, LW_NULL, ppringList);
        
        } else {
     
            _EVENT_DEL_Q_FIFO(EVENT_SEM_Q, ppringList);                 /*  激活FIFO等待线程            */
            ptcb = _EventReadyFifoLowLevel(pevent, LW_NULL, ppringList);
        }

        KN_INT_ENABLE(iregInterLevel);                                  /*  打开中断                    */
        ptcb->TCB_ucIsEventDelete = LW_EVENT_DELETE;                    /*  事件已经被删除              */
        _EventReadyHighLevel(ptcb, LW_THREAD_STATUS_SEM);               /*  处理 TCB                    */
        iregInterLevel = KN_INT_DISABLE();                              /*  关闭中断                    */
    }
    
    pevent->EVENT_pvTcbOwn = LW_NULL;

    _Free_Event_Object(pevent);                                         /*  交还控制块                  */
    
    __KERNEL_EXIT_IRQ(iregInterLevel);                                  /*  退出内核                    */
    
    return  (ERROR_NONE);
}

你可能感兴趣的:(SylixOS内核详解,SylixOS,计数型信号量,实现原理详解)