FreeRTOS任务切换——PendSV

前言:

本文分析一下FreeRTOS任务切换相关内容。RTOS系统的核心是进行任务管理,任务切换。本文分SVC和PendSV异常、任务切换场景、pendSV中断函数、FreeRTOS时间片调度4部分讲解FreeRTOS任务切换。

文章目录

  • 前言:
    • 一、SVC和PendSV
      • 1.1 异常与中断
      • 1.2 SVC和PendSV
        • 1.2.1 SVC
        • 1.2.1 PendSV
      • 1.2 为什么在PendSV异常中进行任务切换
    • 二、任务切换场景以及时间片调度
      • 2.1 执行系统调用
      • 2.2 SysTick中断
      • 2.2 FreeRTOS时间片调度

一、SVC和PendSV

看下面内容之前,首先要明确的一点是FreeRTOS的任务切换是通过PendSV异常进行上下文切换。

1.1 异常与中断

ARM Cortex-M3 M4系列在内核基础上搭建了一个异常响应系统,支持系统异常和外部中断,编号1-15为系统异常,编号大于等于16则为外部中断。异常是CPU内部的中断,处理系统内部的指令异常事件或者其他指令执行等,外部中断则是外围硬件产生的中断,cpu响应外部中断事件。

1.2 SVC和PendSV

SVC和PendSV是由ARM硬件核提供的异常,SVC是系统服务调用,PendSV是可挂起的系统调用,它们多用于操作系统软件开发中。

1.2.1 SVC

SVC用于产生调用系统函数的请求,例如操作系统不允许用户应用程序直接访问操作硬件设备,而是通过一些系统服务函数操作硬件设备,当用户程序想要访问某个硬件时,首先触发一个SVC异常,发出对系统服务函数的调用请求,然后操作系统提供的SVC异常服务函数得到执行,该函数再调用操作硬件的系统函数,最终间接访问硬件设备,如下图所示:
FreeRTOS任务切换——PendSV_第1张图片
SVC异常是必须立即得到响应的,如果因为优先级不比当前正在执行的异常高,或者其他原因导致不能立即执行,则将上访成为硬件fault。

1.2.1 PendSV

PendSV是可以挂起的异常,它和SVC协同使用,顾名思义,PendSV和SVC不同,PendSV可以像普通中断一样被挂起而延迟执行,OS可以利用该特点,等待其他重要任务处理完成后才执行该异常。挂起PendSV的方法是:往NVIC的PendSV挂起寄存器写1。

1.2 为什么在PendSV异常中进行任务切换

嵌入式实时操作系统要求能够实时快速响应处理中断请求,如果在处理中断的时候,发生一个任务切换请求,则会触发fault异常,这是不被允许的。为避免此问题,任务切换需要放在可以挂起的异常中去处理,即PendSV,并且将PendSV异常的优先级设为最低,让其处理在所有中断处理完毕后再执行,这有利于任务切换,也是嵌入式实时OS的设计关键,当系统正在处理中断时,此时请求任务切换,系统挂起PendSV异常,未完成的中断处理继续进行,中断处理完毕,退出中断后,PendSV服务程序开始运行,进行任务切换,这样既不耽误中断的处理也不影响任务切换。下图是PendSV控制任务切换,假设只有两个任务A、B,横轴为时间线,纵轴为优先级:
FreeRTOS任务切换——PendSV_第2张图片
触发任务切换的方式有两种,一种是系统调用,一种是systick(系统滴答定时器)异常。上图中具体过程如下:

  1. 用户程序系统调用产生一次任务切换,任务A请求SVC进行任务切换;
  2. OS收到请求后,做好上下文切换准备,并pend(挂起)一个PendSV异常;
  3. CPU退出SVC后,进入PendSV,执行上下文切换;
  4. PendSV执行完毕后,回到用户程序,此时运行的是任务B,任务切换成功;
  5. 任务B运行期间产生一个中断ISR,并且中断服务程序开始执行;
  6. ISR执行期间,发生systick异常,并抢占了改ISR;
  7. OS执行必要的操作,并挂起一个PendSV异常,最好上下文切换准备;
  8. systick异常执行完毕退出后,回到被抢占的ISR中继续执行ISR服务程序;
  9. ISR执行完毕退出后,PendSV服务程序开始执行,进行上下文切换;
  10. 回到用户程序,执行任务A,任务切换成功。

二、任务切换场景以及时间片调度

	任务切换被处罚的场合有两个:
	1. 执行一个系统调用;
	2. 系统滴答定时器(systick)中断。

2.1 执行系统调用

执行系统调用就是执行FreeRTOS的相关系统API函数,如任务切换函数taskYIELD()等,有些API函数也会调用taskYIELD(),这些API函数都会导致任务切换,任务切换就是挂起PendSV来启动PendSV,具体操作是对中断控制状态寄存器ICSR的bit28写1。taskYIELD()是宏函数,源码注释具体如下:

#define taskYIELD()					portYIELD()
/* Scheduler utilities. */
#define portYIELD() 															\
{																				\
	/* 设置一个PendSV请求上下文切换. */								\
	portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;								\
																				\
	/* Barriers are normally not required but do ensure the code is completely	\
	within the specified behaviour for the architecture. */						\
	__asm volatile( "dsb" ::: "memory" );										\
	__asm volatile( "isb" );													\
}

2.2 SysTick中断

FreeRTOS中滴答定时器(SysTick)中断服务函数中也会进行任务切换,该中断服务函数中主要函数是xTaskIncrementTick(),该函数主要为一个时钟节拍周期后系统应该处理那些事情,函数的返回值决定是否需要进行一次任务切换,xTaskIncrementTick()放在《FreeRTOS时间管理章节》进行讲解,这里暂不说明,SysTick中断服务函数具体源码如下:

void xPortSysTickHandler( void )
{
	vPortRaiseBASEPRI();//关闭中断
	{
		/* 增加时钟计数器xTickCount的数值,并判断是否需要进行一次任务切换 */
		if( xTaskIncrementTick() != pdFALSE )
		{
			/* 要求任务切换,挂起一个PendSV. */
			portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;//对中断控制状态寄存器ICSR的bit28写1
		}
	}
	vPortClearBASEPRIFromISR();//打开中断
}

2.2 FreeRTOS时间片调度

一般情况,嵌入式实时操作系统在进行任务调度的时候,优先运行就绪态任务中优先级最高的任务,但是大多RTOS支持多个任务同时拥有相同的优先级,这些任务的调度需要考虑。FreeRTOS允许一个任务运行一个时间片(一个时钟节拍的时间)让出cpu的使用权,切换到同优先级的下一个任务运行,这种调度方法就是时间片调度。
时间片调度是在滴答定时器中断服务函数中发生的,使用时间片调度的前提是:使用优先级宏configUSE_PREEMPTION 与使用时间片宏configUSE_TIME_SLICING同时定义。 时间片的长度由系统时钟频率configTICK_RATE_HZ决定,滴答定时器的周期就是一个时间片的长度,比如configTICK_RATE_HZ为1000,则时间片就是1ms。
下面看下载滴答定时器中断服务函数中是怎么进行时间片调度的,xTaskIncrementTick()部分注释如下:

//====================其他代码===============================
/*如果启用了抢占,并且开启了时间片功能,则与当前运行的任务具有相同优先级的任务将共享CPU处理时间(时间切片) */
		#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
		{
			//当前任务所对应的优先级下是否还有其他任务已经就绪
			if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
			{
				//如果有,返回ture,请求一次任务切换
				xSwitchRequired = pdTRUE;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */
//========================其他代码==============================

参考《M3权威指南》《FreeRTOS源码详解》

你可能感兴趣的:(FreeRTOS)