一、freeRTOS任务死锁

FreeRTOS任务死锁是一种常见的问题,通常发生在多个任务相互等待对方释放资源的情况下。以下是一个简单的例子,用于说明FreeRTOS任务死锁的情况:

假设有两个任务Task1和Task2,它们需要共享两个资源ResourceA和ResourceB。每个任务都需要同时访问这两个资源才能完成它们的工作。如果两个任务同时试图获得ResourceA和ResourceB,但在尝试获取其中一个资源时被阻塞,那么就会导致死锁。

例如,Task1需要首先获得ResourceA,然后才能获得ResourceB,而Task2则需要首先获得ResourceB,然后才能获得ResourceA。如果Task1已经获取了ResourceA,但此时Task2正在占用ResourceB,那么Task1将等待Task2释放ResourceB才能继续执行。 同时, Task2也将等待Task1释放ResourceA才能继续执行, 这样两个任务都无法继续执行,进程就处于死锁状态了。

当发生任务死锁时,系统将永远无法正常运行,因为任务在等待其它任务释放资源,而该其它任务却也在等待某个资源,从而使得整个系统陷入僵局。要避免这种情况,我们需要仔细规划任务之间的资源使用,并确保任务只在确实需要使用资源时才进行请求。

二、以下是一个可能导致死锁的代码示例:

#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"

#define TASK1_STACK_SIZE     configMINIMAL_STACK_SIZE
#define TASK2_STACK_SIZE     configMINIMAL_STACK_SIZE
#define TASK1_PRIORITY      ( tskIDLE_PRIORITY + 1 )
#define TASK2_PRIORITY      ( tskIDLE_PRIORITY + 2 )

TaskHandle_t xTask1Handle, xTask2Handle;

SemaphoreHandle_t xSemaphoreA, xSemaphoreB;

void vTask1( void *pvParameters )
{
    while( 1 )
    {
        /* 尝试获取ResourceA */
        if( xSemaphoreTake( xSemaphoreA, portMAX_DELAY ) == pdPASS )
        {
            printf("Task1 has acquired ResourceA\n");
            
            /* 尝试获取ResourceB */
            if( xSemaphoreTake( xSemaphoreB, portMAX_DELAY ) == pdPASS )
            {
                printf("Task1 has acquired ResourceB\n");

                /* 访问两个资源 */
                printf("Task1 is accessing ResourceA and ResourceB...\n");
                
                /* 释放资源 */
                xSemaphoreGive( xSemaphoreB );
                printf("Task1 has released ResourceB\n");
                
                xSemaphoreGive( xSemaphoreA );
                printf("Task1 has released ResourceA\n");
            }
        }

        vTaskDelay( pdMS_TO_TICKS( 1000 ) );
    }
}

void vTask2( void *pvParameters )
{
    while( 1 )
    {
        /* 尝试获取ResourceB */
        if( xSemaphoreTake( xSemaphoreB, portMAX_DELAY ) == pdPASS )
        {
            printf("Task2 has acquired ResourceB\n");
            
            /* 尝试获取ResourceA */
            if( xSemaphoreTake( xSemaphoreA, portMAX_DELAY ) == pdPASS )
            {
                printf("Task2 has acquired ResourceA\n");

                /* 访问两个资源 */
                printf("Task2 is accessing ResourceA and ResourceB...\n");
                
                /* 释放资源 */
                xSemaphoreGive( xSemaphoreA );
                printf("Task2 has released ResourceA\n");
                
                xSemaphoreGive( xSemaphoreB );
                printf("Task2 has released ResourceB\n");
            }
        }

        vTaskDelay( pdMS_TO_TICKS( 1000 ) );
    }
}

void app_main( void )
{
    xSemaphoreA = xSemaphoreCreateMutex();
    xSemaphoreB = xSemaphoreCreateMutex();
    
    xTaskCreate( vTask1, "Task1", TASK1_STACK_SIZE, NULL, TASK1_PRIORITY, &xTask1Handle );
    xTaskCreate( vTask2, "Task2", TASK2_STACK_SIZE, NULL, TASK2_PRIORITY, &xTask2Handle );

    vTaskStartScheduler();
}

在这个示例代码中,Task1和Task2都需要同时获取ResourceA和ResourceB,但是它们以不同的顺序尝试获取这两个资源,这可能会导致死锁。因为如果Task1首先获得了ResourceA,而Task2首先获得了ResourceB,那么它们就会相互等待,导致死锁。

此外,示例代码中使用的是无限等待的方式获取资源,如果获取失败则一直等待,这样也很容易导致死锁的发生。

三、以下是一个示例代码,其中Task1和Task2都需要同时访问ResourceA和ResourceB,并且使用信号量来避免死锁。

#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"

#define TASK1_STACK_SIZE     configMINIMAL_STACK_SIZE
#define TASK2_STACK_SIZE     configMINIMAL_STACK_SIZE
#define TASK1_PRIORITY      ( tskIDLE_PRIORITY + 1 )
#define TASK2_PRIORITY      ( tskIDLE_PRIORITY + 2 )

TaskHandle_t xTask1Handle, xTask2Handle;

SemaphoreHandle_t xSemaphoreA, xSemaphoreB;

void vTask1( void *pvParameters )
{
    while( 1 )
    {
        /* 尝试同时获取ResourceA和ResourceB */
        if( xSemaphoreTake( xSemaphoreA, pdMS_TO_TICKS( 1000 ) ) == pdPASS )
        {
            printf("Task1 has acquired ResourceA\n");
            
            if( xSemaphoreTake( xSemaphoreB, pdMS_TO_TICKS( 1000 ) ) == pdPASS )
            {
                printf("Task1 has acquired ResourceB\n");

                /* 访问两个资源 */
                printf("Task1 is accessing ResourceA and ResourceB...\n");
                
                /* 释放资源 */
                xSemaphoreGive( xSemaphoreB );
                printf("Task1 has released ResourceB\n");
                
                xSemaphoreGive( xSemaphoreA );
                printf("Task1 has released ResourceA\n");
            }
            else
            {
                /* 如果无法获取ResourceB,释放ResourceA并等待一段时间后重新尝试获取 */
                xSemaphoreGive( xSemaphoreA );
                printf("Task1 has released ResourceA\n");
                
                vTaskDelay( pdMS_TO_TICKS( 1000 ) );
            }
        }
        else
        {
            /* 如果无法获取ResourceA,等待一段时间后重新尝试获取 */
            vTaskDelay( pdMS_TO_TICKS( 1000 ) );
        }

        vTaskDelay( pdMS_TO_TICKS( 1000 ) );
    }
}

void vTask2( void *pvParameters )
{
    while( 1 )
    {
        /* 尝试同时获取ResourceB和ResourceA */
        if( xSemaphoreTake( xSemaphoreB, pdMS_TO_TICKS( 1000 ) ) == pdPASS )
        {
            printf("Task2 has acquired ResourceB\n");
            
            if( xSemaphoreTake( xSemaphoreA, pdMS_TO_TICKS( 1000 ) ) == pdPASS )
            {
                printf("Task2 has acquired ResourceA\n");

                /* 访问两个资源 */
                printf("Task2 is accessing ResourceA and ResourceB...\n");
                
                /* 释放资源 */
                xSemaphoreGive( xSemaphoreA );
                printf("Task2 has released ResourceA\n");
                
                xSemaphoreGive( xSemaphoreB );
                printf("Task2 has released ResourceB\n");
            }
            else
            {
                /* 如果无法获取ResourceA,释放ResourceB并等待一段时间后重新尝试获取 */
                xSemaphoreGive( xSemaphoreB );
                printf("Task2 has released ResourceB\n");
                
                vTaskDelay( pdMS_TO_TICKS( 1000 ) );
            }
        }
        else
        {
            /* 如果无法获取ResourceB,等待一段时间后重新尝试获取 */
            vTaskDelay( pdMS_TO_TICKS( 1000 ) );
        }

        vTaskDelay( pdMS_TO_TICKS( 1000 ) );
    }
}

void app_main( void )
{
    xSemaphoreA = xSemaphoreCreateMutex();
    xSemaphoreB = xSemaphoreCreateMutex();
    
    xTaskCreate( vTask1, "Task1", TASK1_STACK_SIZE, NULL, TASK1_PRIORITY, &xTask1Handle );
    xTaskCreate( vTask2, "Task2", TASK2_STACK_SIZE, NULL, TASK2_PRIORITY, &xTask2Handle );

    vTaskStartScheduler();
}

在该示例中,Task1和Task2在尝试获取两个资源时都会等待一定时间,如果在这段时间内无法成功获取两个资源,则会放弃当前的获取尝试并重新开始。由于使用了信号量,并且在获取资源时设置了最大等待时间,因此能够有效地避免死锁的发生。