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

http://blog.ednchina.com/frenkie/123245/Message.aspx#

浅析μ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]

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编辑推荐 本书主要特点: 分为原理篇和应用篇,原理与实践相结合,两篇相对独立,又紧密联系。 原理篇以分析源码为特色,深入浅出地介绍RTOS的基本概念以及Small RTOS51的工作原理。 应用篇以DP-51单片综合仿真实验仪为硬件平台,通过对单片机常用和热门外围器件实用驱动程序的分析,详细介绍基于Small RTOS51的编程方法,以及如何为这些外围器件编写中间件,并给出一个完整的例子,让读者全面掌握基于Small RTOS51的编程方法,体会了RTOS下编程的优点。 内容简介 Small RTOS51是为51系列单片机而编写的。它是完全免费的、源代码公开的多任务实时操作系统。它可在无任何外部数据存储器的单片80C51系统上运行,并且是可移植的。全书分两部分。第一部分为基础篇,介绍Small RTOS51和一些基本概念,并详细分析Small RTOS51的工作原理。第二部分为应用篇,给出部分常用硬件在Small RTOS51下驱动程序的源代码。这些源代码在DP—51单片机仿真实验仪上全部调试通过,且只要经过很少的改动,或是不改动,就可以在其他环境下使用。应用篇还通过对这些源代码的分析,让读者理解基于RTOS的编程方法,并给出完整的例子让读者全面掌握基于RTOS的编程方法。 本书可以作为高等院校相关专业的课程教材、实验参考资料或课外读物,对嵌入工应用开发人员也有重要的参考价值。 目录 第一部分 原理篇 第0章 绪论 第1章 Small RTOS51简介 1.1 Small RTOS51的特点 1.2 Small RTOS51的运行条件 1.3 Small RTOS51的存储器需求 1.4 Small RTOS51的任务堆栈的计算 1.5 关于可重入功能 1.6 关于C51的库函数 1.7 关于51系列单片机派生类型的多数据指针和数学单元 1.8 关于51系列单片机的寄存器段 1.9 关于局部变量 第2章 基本概念 2.1 嵌入式系统 2.2 实时系统 2.3 前、后台系统 2.4 操作系统 2.5 实时操作系统 2.6 代码的临界区 2.7 资源 2.8 共享资源 2.9 任务 2.10 任务切换 2.11 内核 2.12 调度 2.13 非占先式内核 2.14 占先式内核 2.15 可重入性 2.16 任务优先级 2.17 信号量 2.18 死锁 2.19 消息队列 2.20 中断 2.21 时钟节拍 第3章 一个简单的例子 3.1 Small RTOS51的安装及目录结构 3.2 例子简介 3.3 Config、h、Os_cfg、h和Os_cpu.h 3.4 与编程器无关的数据类型 3.5 OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL() 3.6 main()函数 3.7 建立任务 3.8 删除任务 3.9 用户任务 3.10 软非屏蔽中断 3.11 中断服务程序的编写 3.12 Os_cpu.h可改变的其他内容 3.13 Small RTOS51的其他注意事项 第4章 任务管理—核心的核心 4.1 临界区 4.2 任务 4.3 任务状态 4.4 与任务相关的数据结构 4.5 任务调度 4.6 Small RTOS51中的中断处理 4.7 时钟节拍 4.8 Small RTOS51初始化和启动 4.9 建立任务 4.10 任务堆栈 4.11 删除任务 4.12 时间服务及任务的挂起和恢复 4.13 获取当前Small RTOS51的版本号 4.14 OSInt…()函数 第5章 如何切换任务 5.1 CPU可以执行多个任务的原因 5.2 CPU怎样运行才能执行多个任务 5.3 何时进行任务切换 5.4 Small RTOS51任务切换时的程序框图 5.5 数组OSTsakStackBotton[]和Small RTOS51的堆栈结构 5.6 变量OSFastSwap 5.7 常量数值OSMapTb[] 5.8 软非屏蔽中断的堆栈SP2[] 5.9 中断切换函数OSIntCtxSw()OSIntCtxSw() 5.10 任务主动放弃CPU-OS_TASK_SW() 5.11 堆栈变换函数C_OSCtxSw() 5.12 恢复新任务环境LoadCtx() 5.13 优先级最低的任务OSIdle() 5.14 Small RTOS51初始化函数OSStart() 5.15 系统时钟节拍中断OSTickISR() 第6章 任务之间的通信和同步之信号量 6.1 概述 6.2 使Keil C51函数具有重入性的特殊方法 6.3 数据结构 6.4 IN_OS_SEM_CHK宏及相关代码 6.5 初始化一个信号量 6.6 等待一个信号量 6.7 发送一个信号量 6.8 无等待地请求一

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值