FreeRTOS学习(十二):时间片轮转调度详解 (众生平等!)
文章目录
前言
在多任务操作系统中,时间片轮转(Round Robin, RR)是一种常见的任务调度算法。它的核心思想是将处理器的时间分成一个个时间片,每个任务获得一个时间片,在时间片用完之前,任务不能被抢占执行。FreeRTOS 是一个广泛应用的实时操作系统,它提供了时间片轮转的调度机制。本文将通过一个实验案例来介绍 FreeRTOS 的时间片轮转调度原理,并结合代码示例来帮助大家更好地理解如何使用这一调度策略。
一、什么是时间片轮转调度?
时间片轮转是一种简单且高效的任务调度算法。在该算法中,每个任务都有一个相等的时间片,操作系统会按照时间片的长度分配 CPU 时间。任务会轮流执行,当一个任务的时间片耗尽时,操作系统会立即将 CPU 切换给下一个任务。这样,每个任务都有平等的机会占用 CPU 时间,从而实现多任务并发执行。
FreeRTOS 是一个多线程的实时操作系统,它支持时间片轮转调度,但也允许开发者选择其他调度策略,如优先级调度。
二、FreeRTOS 中的时间片轮转调度
在 FreeRTOS 中,时间片轮转调度是通过设置系统的调度策略来实现的。FreeRTOS 默认采用基于优先级的抢占式调度,但当多个任务的优先级相同且配置了时间片轮转时,系统会按照时间片轮转的方式来调度任务。
三、实验案例:实现时间片轮转调度
下面我们通过一个简单的实验来展示如何在 FreeRTOS 中使用时间片轮转调度。假设我们有两个任务,它们的优先级相同,且我们希望它们在 CPU 上轮流执行。
步骤一:配置 FreeRTOS 系统
首先,我们需要确保 FreeRTOS 被正确配置以启用时间片轮转调度。这可以通过修改 FreeRTOSConfig.h
文件来实现,特别是以下几个配置项:
#define configUSE_PREEMPTION 1 // 启用抢占式调度
#define configUSE_TIME_SLICING 1 // 启用时间片轮转
#define configIDLE_SHOULD_YIELD 1 // 空闲任务时,调度器会让出 CPU
#define configMAX_PRIORITIES 5 // 任务最大优先级数量
确保 configUSE_TIME_SLICING
设置为 1,这样 FreeRTOS 就会根据时间片轮转进行任务调度
- 当前 configTICK_RATE_HZ 为 1000,意味着系统每秒钟会触发 1000 次滴答定时器。
- 每个滴答定时器的周期为:1/1000=1ms
- 如果时间片为 50ms,那么需要调整定时器频率,使得每个时间片的时长为 50ms。
1/50ms=20Hz,所以这里改为20
步骤二:创建两个优先级相同的任务
接下来,我们创建两个优先级相同的任务,它们会进行简单的任务处理。我们假设这两个任务是 Task1 和 Task2,并且它们的工作是交替输出不同的信息,表示它们在运行。
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 2 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /* 任务函数 */
确保两个任务优先级是一样的。
void task1(void *pvParameters)
{
uint32_t num=0;
while (1)
{
printf("task1111:运行次数%d\r\n",++num);
delay_ms(10);
}
}
void task2(void *pvParameters)
{
uint32_t num=0;
while (1)
{
printf("task2222:运行次数%d\r\n",++num);
delay_ms(10);
}
}
在这个例子中,Task1 和 Task2 具有相同的优先级,系统会根据时间片轮转的规则依次调度它们。
结果:
这里因为没有加临界保护所以导致task1还没执行完task2就来执行了
一旦加上临界保护区之后就完美执行了
void task1(void *pvParameters)
{
uint32_t num=0;
while (1)
{
taskENTER_CRITICAL(); /* 进入临界区 */
printf("task1111:运行次数%d\r\n",++num);
taskEXIT_CRITICAL(); /* 退出临界区 */
delay_ms(10);
}
}
void task2(void *pvParameters)
{
uint32_t num=0;
while (1)
{
taskENTER_CRITICAL(); /* 进入临界区 */
printf("task2222:运行次数%d\r\n",++num);
taskEXIT_CRITICAL(); /* 退出临界区 */
delay_ms(10);
}
}
结果
这里的任务都会执行完再执行下一个
要是优先级不一样会怎么样
让task1的优先级高于task2
结果
结果他只会运行task1的内容不会运行task2。
这是因为
延时函数 delay_ms(10) 可能阻塞任务切换
你在 task1 和 task2 中使用了 delay_ms(10),这是一个阻塞式的延时函数。使用这种延时方式可能导致任务在延时过程中无法被调度,尤其是当延时较长时,其他任务(如 task2)没有机会获得执行。
总结
高优先级任务(如 task2)将抢占低优先级任务(如 task1)的执行机会,即使它们的运行时间片还没有耗尽。
低优先级任务只有在高优先级任务完成或被阻塞时,才会重新获得执行机会。