了解 UCOSIII 中的延迟发布
- 序言
- 打开OS_IntQPost
- OS_IntQTask
- 总结
序言
今天我们一起来看看延迟Post的具体实现机制,我会按照我的学习过程来介绍。
打开OS_IntQPost
函数参数的理解,可以看源码注释,说得很清楚了,我这里主要介绍一些我的疑惑
void OS_IntQPost (OS_OBJ_TYPE type,
void *p_obj,
void *p_void,
OS_MSG_SIZE msg_size,
OS_FLAGS flags,
OS_OPT opt,
CPU_TS ts,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
CPU_CRITICAL_ENTER();
if (OSIntQNbrEntries < OSCfg_IntQSize) { /* Make sure we haven't already filled the ISR queue */
OSIntQNbrEntries++;
if (OSIntQNbrEntriesMax < OSIntQNbrEntries) {
OSIntQNbrEntriesMax = OSIntQNbrEntries;
}
OSIntQInPtr->Type = type; /* Save object type being posted */
OSIntQInPtr->ObjPtr = p_obj; /* Save pointer to object being posted */
OSIntQInPtr->MsgPtr = p_void; /* Save pointer to message if posting to a message queue */
OSIntQInPtr->MsgSize = msg_size; /* Save the message size if posting to a message queue */
OSIntQInPtr->Flags = flags; /* Save the flags if posting to an event flag group */
OSIntQInPtr->Opt = opt; /* Save post options */
OSIntQInPtr->TS = ts; /* Save time stamp */
OSIntQInPtr = OSIntQInPtr->NextPtr; /* Point to the next interrupt handler queue entry */
OSRdyList[0].NbrEntries = (OS_OBJ_QTY)1; /* Make the interrupt handler task ready to run */
OSRdyList[0].HeadPtr = &OSIntQTaskTCB;
OSRdyList[0].TailPtr = &OSIntQTaskTCB;
OS_PrioInsert(0u); /* Add task priority 0 in the priority table */
if (OSPrioCur != 0) { /* Chk if OSIntQTask is not running */
OSPrioSaved = OSPrioCur; /* Save current priority */
}
*p_err = OS_ERR_NONE;
} else {
OSIntQOvfCtr++; /* Count the number of ISR queue overflows */
*p_err = OS_ERR_INT_Q_FULL;
}
CPU_CRITICAL_EXIT();
}
我之前对延迟Post机制的理解是:通过将Post信号这件事情保存在一个队列中,然后由一个OSIntQTask任务来处理这个队列,将队列中的信息重新Post给对应的对象。事实的确如此,只不过我之前的理解过于概念化。下面我将会就代码进行介绍。
我们看上面的代码会发现由很多不懂的地方,比如OSIntQNbrEntries、OSCfg_IntQSize、OSIntQInPtr这三个是什么东西呢,我使用全局查找发现了这样一条代码
OSIntQInPtr = p_int_q_next;
这条代码是来自下面这个函数中
void OS_IntQTaskInit (OS_ERR *p_err)
{
OS_INT_Q *p_int_q;
OS_INT_Q *p_int_q_next;
OS_OBJ_QTY i;
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
OSIntQOvfCtr = (OS_QTY)0u; /* Clear the ISR queue overflow counter */
if (OSCfg_IntQBasePtr == (OS_INT_Q *)0) {
*p_err = OS_ERR_INT_Q;
return;
}
if (OSCfg_IntQSize < (OS_OBJ_QTY)2u) {
*p_err = OS_ERR_INT_Q_SIZE;
return;
}
OSIntQTaskTimeMax = (CPU_TS)0;
p_int_q = OSCfg_IntQBasePtr; /* Initialize the circular ISR queue */
p_int_q_next = p_int_q;
p_int_q_next++;
for (i = 0u; i < OSCfg_IntQSize; i++) {
p_int_q->Type = OS_OBJ_TYPE_NONE;
p_int_q->ObjPtr = (void *)0;
p_int_q->MsgPtr = (void *)0;
p_int_q->MsgSize = (OS_MSG_SIZE)0u;
p_int_q->Flags = (OS_FLAGS )0u;
p_int_q->Opt = (OS_OPT )0u;
p_int_q->NextPtr = p_int_q_next;
p_int_q++;
p_int_q_next++;
}
p_int_q--;
p_int_q_next = OSCfg_IntQBasePtr;
p_int_q->NextPtr = p_int_q_next;
OSIntQInPtr = p_int_q_next;
OSIntQOutPtr = p_int_q_next;
OSIntQNbrEntries = (OS_OBJ_QTY)0u;
OSIntQNbrEntriesMax = (OS_OBJ_QTY)0u;
/* -------------- CREATE THE ISR QUEUE TASK ------------- */
if (OSCfg_IntQTaskStkBasePtr == (CPU_STK *)0) {
*p_err = OS_ERR_INT_Q_STK_INVALID;
return;
}
if (OSCfg_IntQTaskStkSize < OSCfg_StkSi*) {
*p_err = OS_ERR_INT_Q_STK_SIZE_INVALID;
return;
}
OSTaskCreate((OS_TCB *)&OSIntQTaskTCB,
(CPU_CHAR *)((void *)"uC/OS-III ISR Queue Task"),
(OS_TASK_PTR )OS_IntQTask,
(void *)0,
(OS_PRIO )0u, /* This task is ALWAYS at priority '0' (i.e. highest) */
(CPU_STK *)OSCfg_IntQTaskStkBasePtr,
(CPU_STK_SIZE)OSCfg_IntQTaskStkLimit,
(CPU_STK_SIZE)OSCfg_IntQTaskStkSize,
(OS_MSG_QTY )0u,
(OS_TICK )0u,
(void *)0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)p_err);
}
我们我们继续阅读这个函数,不久就又会发现不认识的东西OSCfg_IntQBasePtr,我们继续去看看它的定义
OS_INT_Q * const OSCfg_IntQBasePtr = (OS_INT_Q *)&OSCfg_IntQ[0];
到这里,我们发现了一个数组OSCfg_IntQ,这个数组长什么样呢,我们继续往下看
OS_INT_Q OSCfg_IntQ [OS_CFG_INT_Q_SIZE];
这是一个包含OS_CFG_INT_Q_SIZE个OS_INT_Q的数组,那么OS_INT_Q长什么样呢,我们来看看
struct os_int_q {
OS_OBJ_TYPE Type; /* Type of object placed in the circular list */
OS_INT_Q *NextPtr; /* Pointer to next OS_INT_Q in circular list */
void *ObjPtr; /* Pointer to object placed in the queue */
void *MsgPtr; /* Pointer to message if posting to a message queue */
OS_MSG_SIZE MsgSize; /* Message Size if posting to a message queue */
OS_FLAGS Flags; /* Value of flags if posting to an event flag group */
OS_OPT Opt; /* Post Options */
CPU_TS TS; /* Timestamp */
};
就长这个样子,它是用来保存一个Post过程的(向谁Post,Post什么,Post多少,等等)。我们分析到这里就是得到了一个数组,我们调用OS_IntQTaskInit对这个数组进行初始化,并初始化OSIntQXXX这些参数,让它们与这个数组联系起来,之后我们就可以使用OSIntQXXX这些参数了,现在回到OS_IntQPost
void OS_IntQPost (OS_OBJ_TYPE type,
void *p_obj,
void *p_void,
OS_MSG_SIZE msg_size,
OS_FLAGS flags,
OS_OPT opt,
CPU_TS ts,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
CPU_CRITICAL_ENTER();
if (OSIntQNbrEntries < OSCfg_IntQSize) { /* Make sure we haven't already filled the ISR queue */
OSIntQNbrEntries++;
if (OSIntQNbrEntriesMax < OSIntQNbrEntries) {
OSIntQNbrEntriesMax = OSIntQNbrEntries;
}
OSIntQInPtr->Type = type; /* Save object type being posted */
OSIntQInPtr->ObjPtr = p_obj; /* Save pointer to object being posted */
OSIntQInPtr->MsgPtr = p_void; /* Save pointer to message if posting to a message queue */
OSIntQInPtr->MsgSize = msg_size; /* Save the message size if posting to a message queue */
OSIntQInPtr->Flags = flags; /* Save the flags if posting to an event flag group */
OSIntQInPtr->Opt = opt; /* Save post options */
OSIntQInPtr->TS = ts; /* Save time stamp */
OSIntQInPtr = OSIntQInPtr->NextPtr; /* Point to the next interrupt handler queue entry */
OSRdyList[0].NbrEntries = (OS_OBJ_QTY)1; /* Make the interrupt handler task ready to run */
OSRdyList[0].HeadPtr = &OSIntQTaskTCB;
OSRdyList[0].TailPtr = &OSIntQTaskTCB;
OS_PrioInsert(0u); /* Add task priority 0 in the priority table */
if (OSPrioCur != 0) { /* Chk if OSIntQTask is not running */
OSPrioSaved = OSPrioCur; /* Save current priority */
}
*p_err = OS_ERR_NONE;
} else {
OSIntQOvfCtr++; /* Count the number of ISR queue overflows */
*p_err = OS_ERR_INT_Q_FULL;
}
CPU_CRITICAL_EXIT();
}
现在再看应该没什么问题了,我们把一个Post过程以一个数组元素内容的形式填充某个数组元素,然后让OS_IntQTask就绪,准备处理OSIntQ,接下来我们要探究一下这个处理任务,看看它是如何处理这个队列的
OS_IntQTask
这是一个系统任务,是五个系统任务中第一个被初始化的,
void OS_IntQTask (void *p_arg)
{
CPU_BOOLEAN done;
CPU_TS ts_start;
CPU_TS ts_end;
CPU_SR_ALLOC();
p_arg = p_arg; /* Not using 'p_arg', prevent compiler warning */
while (DEF_ON) {
done = DEF_FALSE;
while (done == DEF_FALSE) {
CPU_CRITICAL_ENTER();
if (OSIntQNbrEntries == (OS_OBJ_QTY)0u) {
OSRdyList[0].NbrEntries = (OS_OBJ_QTY)0u; /* Remove from ready list */
OSRdyList[0].HeadPtr = (OS_TCB *)0;
OSRdyList[0].TailPtr = (OS_TCB *)0;
OS_PrioRemove(0u); /* Remove from the priority table */
CPU_CRITICAL_EXIT();
OSSched();
done = DEF_TRUE; /* No more entries in the queue, we are done */
} else {
CPU_CRITICAL_EXIT();
ts_start = OS_TS_GET();
OS_IntQRePost();
ts_end = OS_TS_GET() - ts_start; /* Measure execution time of tick task */
if (OSIntQTaskTimeMax < ts_end) {
OSIntQTaskTimeMax = ts_end;
}
CPU_CRITICAL_ENTER();
OSIntQOutPtr = OSIntQOutPtr->NextPtr; /* Point to next item in the ISR queue */
OSIntQNbrEntries--;
CPU_CRITICAL_EXIT();
}
}
}
}
具体过程自己看一下吧,这里用到了一个OS_IntQRePost(),里面的内容就是调用普通的Post,剩下的一些细节,自己看看吧,我不看了,反正一看就懂,懂了也记不住,哈哈哈哈。
总结
OS_IntQPost将Post过程信息保存在数组OSCfg_IntQ,然后使OS_IntQTask就绪,去处理这个数组(也就是重新Post里面的内容)。
Stay folish, Stay hungry
下一篇: 气味能唤醒记忆,声音也一样(双语精读)
推荐阅读
-
了解 JavaScript 中的策略模式
-
深入了解 CSS 灵活框中的灵活框
-
了解子网掩码的功能及其在网络规划中的重要性
-
微信 "扫一扫 "物联网,全面揭秘 "扫一扫 "背后的扫盲技术!-1.1 扫一扫感知物体是做什么的? 1.1 微信扫一扫是做什么的? 扫一扫识物是指以图片或视频(商品图片:鞋/包/美妆/服饰/家电/玩具/图书/食品/珠宝/家具/其他商品)为输入媒介,挖掘微信内容生态中的有价值信息(电商+百科+资讯,如图1所示),并展示给用户。这里的电商基本涵盖了微信小程序覆盖上亿SKU的全量优质电商,可以支持用户货比N家并直接下单购买,百科和资讯则聚合了微信内的头部自媒体如搜狗、搜搜、百度等,向用户展示和分享拍摄商品相关的内容资讯。 图 1 扫一扫识别功能示意图 欢迎大家更新iOS新版微信→扫一扫→识货,亲自体验,也欢迎大家通过识货界面的反馈按钮向我们提交反馈意见。 扫一扫识物实景图展示 1.2 扫一扫识物有哪些使用场景? 扫一扫识物的目的是为用户访问微信内部生态内容开辟一个新窗口,以用户扫图片为输入形式,为用户提供微信生态内容中的百科、资讯、电商等作为展示页面。除了用户熟悉的扫一扫操作外,我们还将进一步拓展长按操作,让用户更方便地进行扫一扫操作。"扫一扫知事 "的落地场景主要涵盖三大部分: a. 科普知识: a.科普知识。用户通过扫一扫,可以在微信生态圈中获取该对象的百科、资讯等常识或趣闻,帮助用户更好地了解该对象; b.购物场景。同样的搜索功能支持用户看到喜欢的商品立即检索到微信小程序电商中的同款商品,支持用户即扫即购; c.广告场景。扫一扫识别物体可以辅助公众号文章、视频更好地理解其中蕴含的图片信息,从而更好地投放匹配广告,提高点击率。 1.3 Sweep Sense 为 Sweep 家族带来了哪些新技术? 对于扫一扫来说,大家耳熟能详的应该就是扫一扫二维码、扫一扫小程序码、扫一扫条形码、扫一扫翻译了。无论是各种形式的编码还是文字字符,都可以看作是图片的一种特定编码形式,而物的识别则是对自然场景图片的识别,这对于扫一扫家族来说是一个质的飞跃,我们希望从物的识别入手,进一步拓展扫一扫对自然场景图片的理解能力,比如扫酒、扫车、扫植物、扫人脸等服务,如下图3所示。 图 3 Sweep 家族
-
了解多阶段调查数据分析中权数的使用
-
Java面试 "带你一次性了解面试中的必要问题,谈谈你对ES的理解。
-
深入了解 Open CASCADE 中的 SelectMgr_EntityOwner 和高亮功能
-
了解 DM6 (Damon) 数据库中的用户、登录用户和模式
-
深入了解 blob 和 arrayBuffer 中 xhr 的 responseType
-
快速了解 JavaScript 中的 Promise