三、任务切换之PendSV异常

PendSV异常是可挂起的系统中断,常用于RTOS的任务切换。当PendSV被触发,它会在当前高优先级异常完成后执行。任务切换包括系统调用或systick中断引发的情况。PendSV异常处理函数在FreeRTOS中重定义,用于切换上下文并找到下一个要运行的任务。时间片调度允许同优先级任务轮流执行,时间片长度由滴答定时器中断周期决定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


PendSV异常

    PendSV翻译为可挂起系统中断,从名字上可以看出,这个系统中断可以被挂起,等到时机成熟的时候再去执行,一般会把它的优先级设置为最低,它对OS操作系统的任务切换时非常重要的,是OS设计的关键。可以通过中断控制和状态寄存器ICSR的bit28,也就是PendSV的挂起位置1,就会触发一次PendSV中断。与SVC异常不同,它是不精确的,因此它可以再更高优先级的异常中来被触发,触发后要等到当前的高优先级异常处理完之后再去执行。
(后面会详细讲一下中断和NVIC)

1. 没有PendSV异常的任务切换

    首先说一下能够引起上下文切换的情况有两种:(1)执行一次系统调用,例如调用taskYIELD_IF_USING_PREEMPTION()->抢占式调度;(2)系统滴答定时器(systick)中断。
    当没有PendSV时,systick引起上下文切换的方式如下:
[外链图片转存失败(img-cgIzRpf9-1567757314457)(2D7FE8294BC841C8A015C12DB5F70C32)]
在每一次systick中断来的时候都会进行一次上下文切换,这种情况并没有考虑其他中断的情况,当在systick来之前有其他中断来了,其他中断执行到一半的时候,systick异常来了,此时具体情况如下:
在这里插入图片描述
当systick打断IRQ再回到任务中的时候就会触发fault,引起系统的异常。

2. 有PendSV异常的任务切换

    当有PendSV异常的时候,具体的过程如下:
在这里插入图片描述
FreeRTOS系统的任务切换最终都是在PendSV中断服务函数中完成的,ucos也是在PendSV中断中完成任务切换的。因为这个原因,PendSV异常触发的条件和上面说的触发任务切换的条件一样。

2.1 系统调用引起的任务切换

    当有更高优先级任务来的时候抢占式调度会暂停当前的任务去执行新来的更高优先级的任务,即调用taskYIELD_IF_USING_PREEMPTION()宏,具体的代码如下:

// Tasks.c
#if( configUSE_PREEMPTION == 0 )
	/* If the cooperative scheduler is being used then a yield should not be
	performed just because a higher priority task has been woken. */
	#define taskYIELD_IF_USING_PREEMPTION()
#else
	#define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API()
#endif

// FreeRTOS.h
#ifndef portYIELD_WITHIN_API
	#define portYIELD_WITHIN_API portYIELD
#endif

// Portmacro.h
/* Scheduler utilities. */
/* 任务切换其实就是挂起了个PendSV异常 */
#define portYIELD()																\
{																				\
	/* Set a PendSV to request a context switch. */								\
	portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;								\ // 中断控制和状态寄存器的Bit28位置1,启动PendSV异常
																				\
	/* Barriers are normally not required but do ensure the code is completely	\
	within the specified behaviour for the architecture. */						\
	__dsb( portSY_FULL_READ_WRITE );											\
	__isb( portSY_FULL_READ_WRITE );											\ // 内存屏障其实没有,为了保证代码的统一性
}

#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD()
#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) // 中断中的任务切换同样调用portYI
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值