FreeRTOS学习(十二):时间片轮转调度详解 (众生平等!)

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)的执行机会,即使它们的运行时间片还没有耗尽。
低优先级任务只有在高优先级任务完成或被阻塞时,才会重新获得执行机会。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值