Understanding FreeRTOS Cooperative and Preemptive Scheduling Algorithms

In FreeRTOS, Cooperative Scheduling and Preemptive Scheduling are two different task scheduling algorithms. The core difference between them lies in the triggering mechanism for task switching and how task priorities are handled.

1. Cooperative Scheduling

1.1 Concept

  • Cooperative Scheduling is a non-preemptive scheduling algorithm.
  • In cooperative scheduling mode, task switching only occurs when a task voluntarily yields the CPU (for example, by calling <span>taskYIELD</span>, <span>vTaskDelay</span>, etc.).
  • Tasks are not preempted by higher-priority tasks, even if the higher-priority tasks are ready.

1.2 Features

  • Task switching is triggered by the task itself: A task must explicitly call a function to yield the CPU for task switching to occur.
  • No priority preemption: Even if a higher-priority task is ready, the current task will not be preempted.
  • Suitable for simple task management: Cooperative scheduling is suitable for scenarios where tasks have strong cooperation, allowing tasks to fully control their execution time.

1.3 Use Cases

  • Scenarios where tasks need to cooperate closely.
  • Scenarios where task execution times are short and predictable.

1.4 Configuration Method

In FreeRTOS, cooperative scheduling is achieved by disabling preemptive scheduling. This can be done by configuring as follows:

#define configUSE_PREEMPTION 0
  • Setting <span>configUSE_PREEMPTION</span> to <span>0</span> disables preemptive scheduling and enables cooperative scheduling.

2. Preemptive Scheduling

2.1 Concept

  • Preemptive Scheduling is a preemptive scheduling algorithm.
  • In preemptive scheduling mode, the scheduler dynamically decides which task to run based on task priority.
  • If a higher-priority task is ready, the scheduler will immediately preempt the currently running lower-priority task and switch to the higher-priority task.

2.2 Features

  • Task switching is automatically triggered by the scheduler: The scheduler automatically switches tasks based on task priority and state.
  • Priority preemption: Higher-priority tasks can preempt the CPU usage of lower-priority tasks.
  • Suitable for scenarios with high real-time requirements: Preemptive scheduling ensures that high-priority tasks are executed in a timely manner.

2.3 Use Cases

  • Scenarios with high real-time requirements.
  • Scenarios with significant differences in task priorities.

2.4 Configuration Method

In FreeRTOS, preemptive scheduling is enabled by default. This can be configured as follows:

#define configUSE_PREEMPTION 1
  • Setting <span>configUSE_PREEMPTION</span> to <span>1</span> enables preemptive scheduling.

3. Differences Between Cooperative and Preemptive Scheduling

Feature Cooperative Scheduling Preemptive Scheduling
Task switching trigger Task voluntarily yields the CPU (e.g., calls <span>taskYIELD</span>) Scheduler automatically switches tasks based on priority
Priority preemption No support for priority preemption Supports priority preemption
Real-time capability Lower real-time capability Higher real-time capability
Task control Tasks fully control their execution time Tasks may be preempted by higher-priority tasks
Applicable scenarios Scenarios with strong task cooperation and predictable execution times Scenarios with high real-time requirements and significant task priority differences

4. Configuring Scheduling Algorithms

4.1 Configuring Cooperative Scheduling

In <span>FreeRTOSConfig.h</span>, set <span>configUSE_PREEMPTION</span> to <span>0</span>:

#define configUSE_PREEMPTION 0
  • This disables preemptive scheduling and enables cooperative scheduling.

4.2 Configuring Preemptive Scheduling

In <span>FreeRTOSConfig.h</span>, set <span>configUSE_PREEMPTION</span> to <span>1</span>:

#define configUSE_PREEMPTION 1
  • This is the default configuration for FreeRTOS, enabling preemptive scheduling.

5. Example Code

5.1 Cooperative Scheduling Example

void vTask1(void *pvParameters)
{
    while (1)
    {
        printf("Task 1 is running...\n");
        taskYIELD(); // Voluntarily yield CPU
    }
}

void vTask2(void *pvParameters)
{
    while (1)
    {
        printf("Task 2 is running...\n");
        taskYIELD(); // Voluntarily yield CPU
    }
}

int main(void)
{
    xTaskCreate(vTask1, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
    xTaskCreate(vTask2, "Task 2", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
    vTaskStartScheduler(); // Start the scheduler
    return 0;
}

5.2 Preemptive Scheduling Example

void vTask1(void *pvParameters)
{
    while (1)
    {
        printf("Task 1 is running...\n");
        vTaskDelay(1000); // Delay 1 second
    }
}

void vTask2(void *pvParameters)
{
    while (1)
    {
        printf("Task 2 is running...\n");
        vTaskDelay(1000); // Delay 1 second
    }
}

int main(void)
{
    xTaskCreate(vTask1, "Task 1", configMINIMAL_STACK_SIZE, NULL, 2, NULL); // High priority
    xTaskCreate(vTask2, "Task 2", configMINIMAL_STACK_SIZE, NULL, 1, NULL); // Low priority
    vTaskStartScheduler(); // Start the scheduler
    return 0;
}

6. Summary

  • Cooperative Scheduling: Task switching is triggered by the task itself, does not support priority preemption, and is suitable for scenarios with strong task cooperation.
  • Preemptive Scheduling: Task switching is automatically triggered by the scheduler, supports priority preemption, and is suitable for scenarios with high real-time requirements.
  • Configuration Methods:
    • Cooperative Scheduling:<span>#define configUSE_PREEMPTION 0</span>
    • Preemptive Scheduling:<span>#define configUSE_PREEMPTION 1</span>

Leave a Comment