[转帖]浅析FreeRTOS_v4.5.0延时机制---vTaskDelay()的实现
文章来源:http://gliethttp.cublog.cn
void vTaskDelay( portTickType xTicksToDelay )
{
portTickType xTimeToWake;
signed portBASE_TYPE xAlreadyYielded = pdFALSE;
if( xTicksToDelay > ( portTickType ) 0 )
{
//真的是要延时
vTaskSuspendAll();//那么锁住调度器
{
//在调度器锁定期间,如果因为IRQ中断之类,引起了,某个task从事件等待状态
//变为了可运行状态,在IRQ中,调用了xQueueGenericSendFromISR()事件函数,
//并不会直接将该task从Event事件队列链表中摘除,
//该task会被临时放到xPendingReadyList队列链表上,当调度器解锁的时候,
//会将xPendingReadyList队列链表上所有暂时被登记的tasks们,添加到就绪运行队列链表
//pxReadyTasksLists[x],同时将tasks们依次从他们对应的Event事件链表上摘下
//能够调用vTaskDelay()的task肯定没有等待Event事件,否则不会执行到这里,
//所以仅仅对task的xGenericListItem进行操作,即可,不用理会xEventListItem
//下面计算从当前系统滴答xTickCount开始,经过xTicksToDelay延时之后数据为多少,可能溢出,
//如果会发生时间上的溢出,那么把本task添加到溢出时间队列链表上,否则添加到当前时间
//队列链表上,比如:xTickCount现在等于0xfffffffe,xTicksToDelay的值定义为延时10个ticks滴答,
//那么xTimeToWake最后因为溢出将会等于8,所以在溢出时间队列链表上的第8个ticks滴答
//会唤醒本task(gliethttp)
xTimeToWake = xTickCount + xTicksToDelay;
//task把自己从就绪运行队列链表pxReadyTasksLists[x]中摘掉,这之后本task已经不在就绪运行队列链表中,
//因为调度器在就绪运行队列链表中,再也找不本task的信息,进而调度器也就不会调度本task了
vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) );
//xGenericListItem的xItemValue用来存放延时ticks值xTimeToWake
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake );
if( xTimeToWake < xTickCount )
{
//说明发生了时间溢出,所以把本task添加到pxOverflowDelayedTaskList队列,
//当vTaskIncrementTick()执行系统滴答时,发现xTickCount == 0时,FreeRTOS会自动
//将pxOverflowDelayedTaskList切换为当前时间队列链表,同时当前时间链表会作为下一个
//溢出时间链表被置到后台,
//即:
//pxTemp = pxDelayedTaskList;
//pxDelayedTaskList = pxOverflowDelayedTaskList;//将后台的时间队列链表置到前台,正式使用
//pxOverflowDelayedTaskList = pxTemp;//将正在使用的时间队列链表置到后台,等待下一次时间溢出时使用.
vListInsert( ( xList * ) pxOverflowDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) );
}
else
{
//延时时间计算之后,并没有发生溢出现象,那么将本task挂接到前台正在使用中的时间队列链表
//pxDelayedTaskList上
vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) );
}
}
//ok,我们已经将task从就绪运行队列链表中摘下来,并且把自己放到了时间队列链表上,
//现在执行xTaskResumeAll(),
//1.看看,在我们执行上面代码期间内是否有其他task
//发生了调度申请,也就是xPendingReadyList队列链表上是否有task了,
//如果有task登记到xPendingReadyList队列链表上,那么
//分别把他们从相应的Event事件队列链表上摘下
//(因为IRQ中发现调度器锁住之后,不会进行摘除工作,仅仅把task添加到xPendingReadyList队列链表上),
//同时把他们也从等待的时间队列链表pxDelayedTaskList或者pxOverflowDelayedTaskList上摘下来,
//之后把他们添加到就绪运行队列链表pxReadyTasksLists[x]上,如果添加的task的优先级比当前运行
//的task的优先级高,那么标示随后需要内核调度器调度.
//2.看看,在我们执行上面代码期间内是否有发生了系统tick滴答,
//如果发生了,那么模拟定时器,连续调用vTaskIncrementTick()函数uxMissedTicks次,补上错过的uxMissedTicks个
//系统tick滴答,之后需要调度,所以调用taskYIELD()产生调度,
//3.看看,是否因为内核被锁住而有些tasks暂时被推迟调度的现象,
//3.1>是否已经有task申请了内核调度vTaskSwitchContext()或者
//3.2>prvUnlockQueue()发现有优先级高于当前正在运行的task的被唤醒,
//进而通过vTaskMissedYield()登记需要执行内核调度器.
//只要上面有一个条件符合,那么xTaskResumeAll()函数中就会执行taskYIELD()调度,
//进而本task被抢占,直到恢复获得cpu时,xAlreadyYielded将等于true,所以下面的
//调度语句taskYIELD(),就不用再执行了,因为本task刚刚才获得cpu使用权(gliethttp).
xAlreadyYielded = xTaskResumeAll();
}
if( !xAlreadyYielded )
{
//如果在xTaskResumeAll()中没有发生调度,没有发生其他task抢占本task的现象,
//本task没能切换出去,进而让出cpu,
//那么这里将调用taskYIELD(),本task自己主动让出cpu,供其他task使用(gliethttp).
taskYIELD();
}
}
1楼
0
0
回复
文章来源:http://gliethttp.cublog.cn
void vTaskDelay( portTickType xTicksToDelay )
{
portTickType xTimeToWake;
signed portBASE_TYPE xAlreadyYielded = pdFALSE;
if( xTicksToDelay > ( portTickType ) 0 )
{
//真的是要延时
vTaskSuspendAll();//那么锁住调度器
{
//在调度器锁定期间,如果因为IRQ中断之类,引起了,某个task从事件等待状态
//变为了可运行状态,在IRQ中,调用了xQueueGenericSendFromISR()事件函数,
//并不会直接将该task从Event事件队列链表中摘除,
//该task会被临时放到xPendingReadyList队列链表上,当调度器解锁的时候,
//会将xPendingReadyList队列链表上所有暂时被登记的tasks们,添加到就绪运行队列链表
//pxReadyTasksLists[x],同时将tasks们依次从他们对应的Event事件链表上摘下
//能够调用vTaskDelay()的task肯定没有等待Event事件,否则不会执行到这里,
//所以仅仅对task的xGenericListItem进行操作,即可,不用理会xEventListItem
//下面计算从当前系统滴答xTickCount开始,经过xTicksToDelay延时之后数据为多少,可能溢出,
//如果会发生时间上的溢出,那么把本task添加到溢出时间队列链表上,否则添加到当前时间
//队列链表上,比如:xTickCount现在等于0xfffffffe,xTicksToDelay的值定义为延时10个ticks滴答,
//那么xTimeToWake最后因为溢出将会等于8,所以在溢出时间队列链表上的第8个ticks滴答
//会唤醒本task(gliethttp)
xTimeToWake = xTickCount + xTicksToDelay;
//task把自己从就绪运行队列链表pxReadyTasksLists[x]中摘掉,这之后本task已经不在就绪运行队列链表中,
//因为调度器在就绪运行队列链表中,再也找不本task的信息,进而调度器也就不会调度本task了
vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) );
//xGenericListItem的xItemValue用来存放延时ticks值xTimeToWake
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake );
if( xTimeToWake < xTickCount )
{
//说明发生了时间溢出,所以把本task添加到pxOverflowDelayedTaskList队列,
//当vTaskIncrementTick()执行系统滴答时,发现xTickCount == 0时,FreeRTOS会自动
//将pxOverflowDelayedTaskList切换为当前时间队列链表,同时当前时间链表会作为下一个
//溢出时间链表被置到后台,
//即:
//pxTemp = pxDelayedTaskList;
//pxDelayedTaskList = pxOverflowDelayedTaskList;//将后台的时间队列链表置到前台,正式使用
//pxOverflowDelayedTaskList = pxTemp;//将正在使用的时间队列链表置到后台,等待下一次时间溢出时使用.
vListInsert( ( xList * ) pxOverflowDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) );
}
else
{
//延时时间计算之后,并没有发生溢出现象,那么将本task挂接到前台正在使用中的时间队列链表
//pxDelayedTaskList上
vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) );
}
}
//ok,我们已经将task从就绪运行队列链表中摘下来,并且把自己放到了时间队列链表上,
//现在执行xTaskResumeAll(),
//1.看看,在我们执行上面代码期间内是否有其他task
//发生了调度申请,也就是xPendingReadyList队列链表上是否有task了,
//如果有task登记到xPendingReadyList队列链表上,那么
//分别把他们从相应的Event事件队列链表上摘下
//(因为IRQ中发现调度器锁住之后,不会进行摘除工作,仅仅把task添加到xPendingReadyList队列链表上),
//同时把他们也从等待的时间队列链表pxDelayedTaskList或者pxOverflowDelayedTaskList上摘下来,
//之后把他们添加到就绪运行队列链表pxReadyTasksLists[x]上,如果添加的task的优先级比当前运行
//的task的优先级高,那么标示随后需要内核调度器调度.
//2.看看,在我们执行上面代码期间内是否有发生了系统tick滴答,
//如果发生了,那么模拟定时器,连续调用vTaskIncrementTick()函数uxMissedTicks次,补上错过的uxMissedTicks个
//系统tick滴答,之后需要调度,所以调用taskYIELD()产生调度,
//3.看看,是否因为内核被锁住而有些tasks暂时被推迟调度的现象,
//3.1>是否已经有task申请了内核调度vTaskSwitchContext()或者
//3.2>prvUnlockQueue()发现有优先级高于当前正在运行的task的被唤醒,
//进而通过vTaskMissedYield()登记需要执行内核调度器.
//只要上面有一个条件符合,那么xTaskResumeAll()函数中就会执行taskYIELD()调度,
//进而本task被抢占,直到恢复获得cpu时,xAlreadyYielded将等于true,所以下面的
//调度语句taskYIELD(),就不用再执行了,因为本task刚刚才获得cpu使用权(gliethttp).
xAlreadyYielded = xTaskResumeAll();
}
if( !xAlreadyYielded )
{
//如果在xTaskResumeAll()中没有发生调度,没有发生其他task抢占本task的现象,
//本task没能切换出去,进而让出cpu,
//那么这里将调用taskYIELD(),本task自己主动让出cpu,供其他task使用(gliethttp).
taskYIELD();
}
}