| 上一篇 | 下一篇 |
|---|---|
| FreeRTOS 专用延时函数 |
死等延时函数会对任务调度产生什么影响
首先,死等延时函数指的是 忙等待(Busy Wait) ,典型形式是 delay_ms(100) ,具体参考函数如下:
c
/**
* @brief 延时nus
* @param nus: 要延时的us数.
* @note nus取值范围: 0 ~ 477218588(最大值即2^32 / g_fac_us @g_fac_us = 9)
* @retval 无
*/
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
uint32_t reload;
reload = SysTick->LOAD; /* LOAD的值 */
ticks = nus * g_fac_us; /* 需要的节拍数 */
told = SysTick->VAL; /* 刚进入时的计数器值 */
while (1)
{
tnow = SysTick->VAL;
if (tnow != told)
{
if (tnow < told)
{
tcnt += told - tnow; /* 这里注意一下SYSTICK是一个递减的计数器就可以了. */
}
else
{
tcnt += reload - tnow + told;
}
told = tnow;
if (tcnt >= ticks) break; /* 时间超过/等于要延迟的时间,则退出. */
}
}
}
/**
* @brief 延时nms
* @param nms: 要延时的ms数 (0< nms <= 65535)
* @retval 无
*/
void delay_ms(uint16_t nms)
{
uint32_t i;
for (i=0; i<nms; i++)
{
delay_us(1000);
}
}
它的本质是:CPU 一直卡在当前任务,不做"有意义的事"。
调用死等延时函数的任务在整个等待期间:
- 始终处于 Running / Ready 状态
- 不主动进入 Blocked
- 持续占用 CPU 执行空循环
1)对抢占式任务调度的影响
若该任务优先级较高,当其 "霸占CPU" 时:
- 调度器没有机会切换到低优先级任务
- 低优先级任务被长期甚至永久饿死,此时即便低优先级任务 Ready,也无法获得执行机会。
若该任务优先级较低,当其 "霸占CPU" 时:
- 即使低优先级任务在死等,只要 PendSV 中断没关,高优先级任务照样能抢占,被抢占的低优先级任务会在高优先级任务阻塞时,继续执行未结束的死等延时。
- 但如果这个死等延时函数中,涉及到关中断了,那么高优先级任务也无法抢占在执行死等延时的低优先级任务
2)对时间片轮转式任务调度的影响
时间片调度适用于 相同优先级 且处于 Ready 状态的多个任务,当某个任务存在死等延时的情况下:
- 死等延时这部分时间,CPU 被浪费;
- SysTick 中断没关的话,时间片轮转一般不会受到影响,时间片到了之后,依旧会切换到下一个任务(只是说由于之前有一部分时间被浪费了,所以下一个任务需要等满一整个时间片才会被执行,不过这也不是一定的,有可能死等延时任务会占用更多的CPU);
- 如果时间片到了的时候,上一个任务中的死等延时还没结束,会被系统将状态保存下来,等到下一个时间片轮到这个任务的时候,继续死等。
- 但如果这个死等延时函数中,涉及到关中断了,那么时间片轮转会被严重影响,任务无法切换 。
举例:
假设一个时间片 = 1 ms,TaskA、TaskB 同优先级,死等不关中断、不阻塞。
TaskA 每轮逻辑:600 μs 有效执行 + 600 μs 死等;
TaskB 每轮逻辑:1ms 有效执行。
则严格的时间推演为:
c
0 ~ 600 μs --> TaskA 有效执行
600 ~ 1000 μs --> TaskA 死等(结束后剩余200μs)
1000μs ~ 2000 μs --> TaskB 有效执行
2000 ~ 2200 μs --> TaskA 继续死等
2200μs ~ 2800 μs --> TaskA 有效执行
2800 ~ 3000 μs --> TaskA 死等(结束后剩余400μs)
3000μs ~ 4000 μs --> TaskB 有效执行
......