Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15529053
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类:

2007-09-09 15:23:13

浅析μCOS/II v2.85内核OSQPend()和OSQPost()函数工作原理

文章来源:http://gliethttp.cublog.cn[转载请声明出处]

//----------------------------------------------------------------------
//1.OSQPend()函数
void *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr)
{
    void *pmsg;
    OS_Q *pq;
    INT8U pend_stat;
#if OS_CRITICAL_METHOD == 3
    OS_CPU_SR cpu_sr = 0;
#endif

#if OS_ARG_CHK_EN > 0
    if (perr == (INT8U *)0) {
        return ((void *)0);
    }
    if (pevent == (OS_EVENT *)0) {
        *perr = OS_ERR_PEVENT_NULL;
        return ((void *)0);
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_Q) {
//确保该event控制块是Q类型
        *perr = OS_ERR_EVENT_TYPE;
        return ((void *)0);
    }
#endif
    if (OSIntNesting > 0) {
//ISR中,不能使用OSQPend()
        *perr = OS_ERR_PEND_ISR;
        return ((void *)0);
    }
    if (OSLockNesting > 0) {
//μCOS/II v2.85内核已经被强制锁住
        *perr = OS_ERR_PEND_LOCKED;
        return ((void *)0);
    }
//非法的统统不是,信号正常,所以有必要进一步处理
    OS_ENTER_CRITICAL();
    pq = (OS_Q *)pevent->OSEventPtr;
    if (pq->OSQEntries > 0) {
//程序的其他地方已经触发了事件
//所以该task无需悬停,直接获得事件的使用权
        pmsg = *pq->OSQOut++;//消息采用先进先出方式
        pq->OSQEntries--;//个数-1
        if (pq->OSQOut == pq->OSQEnd) {
//在OSQCreate(void **start, INT16U size)中,做了如下初始化:
//pq->OSQStart    = start;
//pq->OSQEnd      = &start[size];
//pq->OSQIn       = start;
//pq->OSQOut      = start;
//pq->OSQSize     = size;
//pq->OSQEntries  = 0;
            pq->OSQOut = pq->OSQStart;
        }
        OS_EXIT_CRITICAL();
        *perr = OS_ERR_NONE;
        return (pmsg);
    }
//当前还没有任何事件发生,所以本task需要悬停,让出cpu[gliethttp]
    OSTCBCur->OSTCBStat |= OS_STAT_Q;//是Q事件让本task进入悬停等待的
    OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;//假定不是超时,为正常收到信号
//超时,如果timeout=0,那么,本task将一直悬停,仅仅当收到事件触发信号后才重新进入调度队列
    OSTCBCur->OSTCBDly = timeout;
//OS_EventTaskWait()函数实现的功能:
//把本task从就绪控制矩阵中摘下,放到pevent事件专有的进程事件控制矩阵表中.
    OS_EventTaskWait(pevent);
    OS_EXIT_CRITICAL();
//因为本task正在运行,所以本task现在的优先级最高,现在本task已经将自己从就绪控制矩阵--调度器(x,y)矩形阵列中
//把自己摘掉,所以调度函数OS_Sched()一定会切换到另一个task中执行新task的代码[gliethttp]
    OS_Sched();//具体参见《浅析μC/OS-II v2.85内核调度函数》
//2007-09-09 gliethttp
//可能因为OSQPend()中指定的timeout已经超时
//[由OSTimeTick()函数把本task重新置入了就绪队列,具体参考《浅析μC/OS-II v2.85内核OSTimeDly()函数工作原理》],
//又或者确实在应用程序的某个地方调用了OSQPost(),以下代码将具体解析是有什么引起的:1.超时,2.收到正常信号
    OS_ENTER_CRITICAL();
    if (OSTCBCur->OSTCBStatPend != OS_STAT_PEND_OK) {
//是因为timeout超时,使得本task获得重新执行的机会
        pend_stat = OSTCBCur->OSTCBStatPend;
//清除event事件控制矩阵上本task的标志
        OS_EventTOAbort(pevent);
        OS_EXIT_CRITICAL();
        switch (pend_stat) {
            case OS_STAT_PEND_TO:
            default:
                 *perr = OS_ERR_TIMEOUT;
                 break;
            case OS_STAT_PEND_ABORT:
                 *perr = OS_ERR_PEND_ABORT;
                 break;
        }
        return ((void *)0);
    }
//由OSQPost()抛出正常事件,唤醒了本task,因为在OSQPost()时,
//已经将本task在event事件控制矩阵上的对应位清除掉,并且把本task放入了就绪控制矩阵中,
//否则本task也不会执行至此.
    pmsg = OSTCBCur->OSTCBMsg;//由OSQPost()传递来的消息指针
    OSTCBCur->OSTCBMsg = (void *)0;//清空消息指针
//状态ok,等待os调度登记到就绪控制矩阵中的自己
    OSTCBCur->OSTCBStat = OS_STAT_RDY;
    OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;//现在本task不悬停在任何event事件上
    OS_EXIT_CRITICAL();
    *perr = OS_ERR_NONE;
    return (pmsg);//返回OSQPend()到的消息指针
}
//----------------------------------------------------------------------
//2.OS_EventTaskWait()函数
void OS_EventTaskWait (OS_EVENT *pevent)
{
    INT8U y;
//2007-09-09 gliethttp
//pevent为此次task挂起的EventPtr单元
    OSTCBCur->OSTCBEventPtr = pevent;
//清除调度器就绪控制矩阵中该task对应的标志位
    y = OSTCBCur->OSTCBY;
    OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;
    if (OSRdyTbl[y] == 0) {
//当前y行对应的8个或16个task都已经悬停,那么当前y行也清除.
        OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
    }
//2007-09-09 gliethttp
//将该task的prio添加到pevent事件控制矩阵中,这个矩阵的功能和OSRdyGrp、OSRdyTbl就绪控制矩阵没有区别
//都是用来就算出已经就绪tasks中的优先级最高的那个
    pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;
    pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
}
//----------------------------------------------------------------------
//3.OS_EventTOAbort()函数
void OS_EventTOAbort (OS_EVENT *pevent)
{
    INT8U y;
//清除event事件控制矩阵上本task的标志,因为OSTimeTick()函数未清除该单元
//它仅仅把本task放入就绪控制矩阵,使得本task重新获得被OS调度的机会而已
//具体的清除工作还要自己完成
//具体参考《浅析μC/OS-II v2.85内核OSTimeDly()函数工作原理》
    y = OSTCBCur->OSTCBY;
    pevent->OSEventTbl[y] &= ~OSTCBCur->OSTCBBitX;
    if (pevent->OSEventTbl[y] == 0x00) {
        pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY;
    }
    OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
    OSTCBCur->OSTCBStat = OS_STAT_RDY;
    OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;//现在本task不悬停在任何event事件上
}
//----------------------------------------------------------------------
//4.OSQPost()函数
INT8U OSQPost (OS_EVENT *pevent, void *pmsg)
{
    OS_Q *pq;
#if OS_CRITICAL_METHOD == 3
    OS_CPU_SR cpu_sr = 0;//方式3将把cpsr状态寄存器推入临时堆栈cpu_sr中,可以安全返回之前的中断状态
#endif

#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {
        return (OS_ERR_PEVENT_NULL);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_Q) {
        return (OS_ERR_EVENT_TYPE);
    }
    OS_ENTER_CRITICAL();
    if (pevent->OSEventGrp != 0) {
    //2007-09-09 gliethttp
    //OS_EventTaskRdy()函数将摘掉等待在pevent事件控制矩阵上的task中优先级最高的task
    //如果该task仅仅等待该pevent事件,那么将该task添加到就绪控制矩阵中
    //OSRdyGrp |= bity;
    //OSRdyTbl[y] |= bitx;这样调度程序就会根据情况调度OS_Sched()该task了
        (void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_Q, OS_STAT_PEND_OK);
        OS_EXIT_CRITICAL();
    //可能刚刚放到就绪控制矩阵上的被唤醒的task的优先级比调用OSQPost()函数的进程B优先级高
    //所以需要调用shedule函数,
    //如果真的高,那么调用OSQPost()函数的进程B就要被抢占,os将会切换到新的task去执行[gliethttp]
    //如果没有调用OSQPost()函数的进程B优先级高,那么os不会切换,仍然继续执行进程B,OSQPost()正常返回
        OS_Sched();
        return (OS_ERR_NONE);
    }
    //没有任何一个task悬停在本event事件控制矩阵上,
    //那么将此消息入队,进而堆积消息,用来缓冲消息数据[gliethttp]
//在OSQCreate(void **start, INT16U size)中,做了如下初始化:
//pq->OSQStart    = start;
//pq->OSQEnd      = &start[size];
//pq->OSQIn       = start;
//pq->OSQOut      = start;
//pq->OSQSize     = size;
//pq->OSQEntries  = 0;
    pq = (OS_Q *)pevent->OSEventPtr;
    if (pq->OSQEntries >= pq->OSQSize) {
        OS_EXIT_CRITICAL();//Queue满了
        return (OS_ERR_Q_FULL);
    }
    *pq->OSQIn++ = pmsg;//将pmsg推入消息队列
    pq->OSQEntries++;
    if (pq->OSQIn == pq->OSQEnd) {
        pq->OSQIn = pq->OSQStart;
    }
    OS_EXIT_CRITICAL();
    return (OS_ERR_NONE);
}
//----------------------------------------------------------------------
//5.OS_EventTaskRdy()函数
INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *pmsg, INT8U msk, INT8U pend_stat)
{
    OS_TCB *ptcb;
    INT8U x;
    INT8U y;
    INT8U prio;
#if OS_LOWEST_PRIO <= 63
    INT8U bitx;
    INT8U bity;
#else
    INT16U bitx;
    INT16U bity;
    INT16U *ptbl;
#endif

#if OS_LOWEST_PRIO <= 63
//小于64个task时,快速计算
//最有优先权的task位于事件控制矩阵中的第y行的第x列
    y = OSUnMapTbl[pevent->OSEventGrp];
    bity = (INT8U)(1 << y);
    x = OSUnMapTbl[pevent->OSEventTbl[y]];
    bitx = (INT8U)(1 << x);
    prio = (INT8U)((y << 3) + x);
#else
//对于256个task
//最有优先权的task位于事件控制矩阵中的第y行的第x列
//以下的操作原理具体参见《浅析μC/OS-II v2.85内核调度函数》
    if ((pevent->OSEventGrp & 0xFF) != 0) {
        y = OSUnMapTbl[pevent->OSEventGrp & 0xFF];
    } else {
        y = OSUnMapTbl[(pevent->OSEventGrp >> 8) & 0xFF] + 8;
    }
    bity = (INT16U)(1 << y);
    ptbl = &pevent->OSEventTbl[y];
    if ((*ptbl & 0xFF) != 0) {
        x = OSUnMapTbl[*ptbl & 0xFF];
    } else {
        x = OSUnMapTbl[(*ptbl >> 8) & 0xFF] + 8;
    }
    bitx = (INT16U)(1 << x);
    prio = (INT8U)((y << 4) + x);//该task对应的prio优先级值
//ok,等待在event事件控制矩阵上的所有task中,只有在事件控制矩阵中的第y行的第x列task
//优先级最高、最值的成为此次事件的唤醒对象[gliethttp]
#endif
//清除此task在event事件控制矩阵中的bit位
    pevent->OSEventTbl[y] &= ~bitx;
    if (pevent->OSEventTbl[y] == 0) {
        pevent->OSEventGrp &= ~bity;
    }
//通过prio优先级找到该prio唯一对应的task对应的ptcb进程上下文控制块
    ptcb = OSTCBPrioTbl[prio];
    ptcb->OSTCBDly = 0;//复原为正常
    ptcb->OSTCBEventPtr = (OS_EVENT *)0;//现在本task不悬停在任何event事件上
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
    ptcb->OSTCBMsg = pmsg;//传递消息指针
#else
    pmsg = pmsg;
#endif
    ptcb->OSTCBStatPend = pend_stat;//悬停状态值
    ptcb->OSTCBStat &= ~msk;//该msk事件已经发生,清除task上下文控制块上的msk位,如:OS_STAT_Q
    if (ptcb->OSTCBStat == OS_STAT_RDY) {
    //如果当前task只是等待该事件,那么把该task放到就绪控制矩阵中,允许内核调度本task
        OSRdyGrp |= bity;
        OSRdyTbl[y] |= bitx;
    }
    return (prio);//返回本task对应的优先级值
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
PS:"所以从这里来看,os中的各个功能单元管理着自己的事情,就像面向对象的封装一样,"
   "事件控制矩阵和就绪控制矩阵是各个对象独立自治的关键因素"
   "其他对象,都努力说服自己相信别的对象是独立的、可信任的、安全的"[gliethttp]
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

阅读(12008) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~