FreeRTOS (mutex semaphore)

 The information comes from Hardware Home:Information summary - FreeRTOS real-time operating system course (multi-task management)

Table of contents

1. Definition and application of mutually exclusive semaphore

1. Definition of mutually exclusive semaphore

2. Application of mutually exclusive semaphore

3. Briefly understand the recursive mutex semaphore

2. Priority flipping problem

1. Operating conditions

2. Priority flip programming test

3. The operating mechanism of mutually exclusive semaphore

4. Commonly used API functions for mutually exclusive semaphores

 1. Typical process and API of mutually exclusive semaphore

2. Creation and deletion of mutually exclusive semaphores

3. Release of mutually exclusive semaphore

4. Acquisition of mutually exclusive semaphore

5. Application programming of mutually exclusive semaphore

1. Enable mutually exclusive semaphore

 2. Create a mutually exclusive semaphore

 3. Create three task verifications with different priorities


1. Definition and application of mutually exclusive semaphore

1. Definition of mutually exclusive semaphore

As we learned before, a semaphore with only two values ​​0 and 1 is called a binary semaphore.  The mutex semaphore is a special binary semaphore that has the property of preventing priority flipping.

When creating a mutually exclusive semaphore, the system will allocate memory for the created mutually exclusive semaphore.Mutually exclusiveAfter the semaphore is created, The schematic diagram is as follows:

picture

2. Application of mutually exclusive semaphore

In embedded operating systems, mutex semaphores are used for exclusive access to critical resources.Can only be used between tasks, because its unique priority inheritance mechanism can only work in tasks and is meaningless in the context of interrupts.

Application scenarios:

For example, if two tasks need to send data through the same serial port and have only one hardware resource, the two tasks cannot send data at the same time, otherwise data errors will occur. At this time, the mutex semaphore can be used to protect the serial port resources. When task 1 is using the serial port to send data, the mutex semaphore becomes invalid, task 2 cannot use the serial port, and task 2 must wait for the mutex semaphore to be effective (task 1 Release the semaphore) to obtain the right to use the serial port and then send data.

3. Briefly understand the recursive mutex semaphore

The recursive mutex semaphore is a special kind of mutex semaphore, which supports tasks that have the right to use the semaphore to obtain it repeatedly without deadlock. After the task successfully obtains the recursive mutex semaphore several times, it must return it several times. Before that, the recursive mutex semaphore is in an invalid state.

2. Priority flipping problem

Using mutually exclusive semaphores can effectively prevent priority flipping problems.

picture

1. Operating conditions

 Create 3 tasks Task1, Task2 and Task3 with priorities of 3, 2 and 1 respectively. That is, Task1 has the highest priority.​ 

 Tasks Task1 and Task3 have mutually exclusive access to serial port printing printf, and use binary signals to achieve mutually exclusive access.

 Initially, Task3 is calling printf through a binary semaphore, which is preempted by Task1 and starts executing Task1, which is the starting position in the above figure.

 The running process of task Task1 needs to call the function printf. If it is found that task Task3 is calling, task Task1 will be suspended and wait for Task3 to release the function printf.​ 

 Under the action of the scheduler, task Task3 is run. During the running process of Task3, because task Task2 is ready, it preempts the running of Task3. This is where the priority flipping problem lies. Judging from the task execution phenomenon, Task1 needs to wait for Task2 to complete before it has a chance to be executed. This is exactly the opposite of preemptive scheduling. Under normal circumstances, high-priority tasks should be preempted. The execution of low-priority tasks here becomeshigh-priority task Task1 waiting for the completion of low-priority task Task2. This situation is called the priority flip problem

 After task Task2 completes execution, task Task3 resumes execution. After Task3 releases the mutually exclusive resource, task Task1 obtains the mutually exclusive resource and can continue execution.

2. Priority flip programming test

3. The operating mechanism of mutually exclusive semaphore

When a mutex handles access to critical resources by different tasks, the task must obtain a mutex to access the resource. Once a task successfully obtains the mutex, the mutex immediately becomes locked. At this time, other tasks will not be able to access this resource because they cannot obtain the mutex. The task will wait according to the user-defined waiting time until the mutex is released by the task holding it, and other tasks can obtain the mutex and access the critical resource. At this time, the mutex is locked again. This ensures that only one task is accessing this critical resource at a time, ensuring the safety of critical resource operations, as shown in the figure below.

picture

①: Because mutexes have a priority inheritance mechanism, mutexes are generally chosen to protect resources. When a resource protected by a mutex is occupied, tasks that want to use the resource will be blocked regardless of their priority.

②: If task 1 that is using the resource has a lower priority than task 2 that is blocked, then the priority of task 1 will be temporarily raised by the system to be equal to the high-priority task 2 (the priority of task 1 changes from L to H), this is the so-called priority inheritance, which effectively prevents the priority flip problem, because at this time, tasks with priorities between Task 1 and Task 2 cannot seize the CPU.

③: When Task 1 finishes using the resources, the mutex is released. At this time, the priority of Task 1 will change from H back to the original L.

④~⑤: Task 2 can obtain the mutex at this time, and then access the resource. When Task 2 accesses the resource, the status of the mutex changes to the locked state, and other tasks cannot obtain the mutex.

4. Commonly used API functions for mutually exclusive semaphores

 1. Typical process and API of mutually exclusive semaphore

> Create a mutually exclusive semaphore xSemaphoreCreateMutex()

> Release the mutex semaphore xSemaphoreGive()

> Get the mutex semaphore xSemaphoreTake()

> Delete the mutex semaphore vSemaphoreDelete()

2. Creation and deletion of mutually exclusive semaphores

Mutex semaphore control block (handle)

As shown below: The handle of the binary semaphore is the handle of the message queue, because the binary semaphore is a special message queue with a length of 1 and a message size of 0

picture

picture

picture

Mutex semaphore creation

Function prototype: SemaphoreHandle_t xSemaphoreCreateMutex(void)

Function description: The function xSemaphoreCreateMutex is used to create a mutually exclusive semaphore.

 Return value. If the creation is successful, the handle of the mutex semaphore will be returned. If the required space cannot be provided for the mutex semaphore due to insufficient heap size in the FreeRTOSConfig.h file, NULL will be returned.

Please pay attention to the following issues when using this function

1. To use this function, enable macro definition in the FreeRTOSConfig.h file: #define configUSE_MUTEXES 1

Description: This function is implemented based on the message queue function:

picture

Application examples:

picture

Mutex semaphore deletion

Function prototype: void vSemaphoreDelete(SemaphoreHandle_t xSemaphore); /* Semaphore handle */

Function description: The function vSemaphoreDelete can be used to delete a mutex semaphore.​ 

3. Release of mutually exclusive semaphore

Function prototype: xSemaphoreGive(SemaphoreHandle_t xSemaphore); /* Semaphore handle */

Function description: The function xSemaphoreGive is used to release the semaphore in the task code.

 The first parameter is the semaphore handle.

 Return value, if the semaphore is released successfully, it returns pdTRUE, otherwise it returns pdFALSE, because the implementation of the semaphore is based on the message queue, and the main reason for failure to return is that the message queue is full.

Please pay attention to the following issues when using this function:

1. This function is used to be called in the task code and cannot be called in the interrupt service routine.

2. Before using this function, be sure to create a semaphore using the function xSemaphoreCreateBinary(), xSemaphoreCreateMutex() or xSemaphoreCreateCounting().

3. This function does not support semaphores created using xSemaphoreCreateRecursiveMutex().

Application examples:

picture

4. Acquisition of mutually exclusive semaphore

Function prototype: xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait); Function description: Function xSemaphoreTake is used to obtain the semaphore in the task code.​ 

 The first parameter is the semaphore handle.​ 

 The second parameter is the maximum waiting time for the semaphore to be available when no semaphore is available, in system clock beats.  Return value, if the creation is successful, the semaphore will be obtained and pdTRUE will be returned, otherwise pdFALSE will be returned.

Please pay attention to the following issues when using this function:

1. This function is used to be called in the task code and cannot be called in the interrupt service routine.

2. If the message queue is empty and the second parameter is 0, this function returns immediately.

3. If the user configures the macro definition INCLUDE_vTaskSuspend in the FreeRTOSConfig.h file to 1 and the second parameter is configured to portMAX_DELAY, then this function will wait forever until the semaphore is available.

Application examples:

picture

5. Application programming of mutually exclusive semaphore

1. Enable mutually exclusive semaphore

 2. Create a mutually exclusive semaphore

 3. Create three task verifications with different priorities

  if(myMutex01Handle==NULL)
	  HAL_UART_Transmit(&huart2, (uint8_t*)"创建互斥信号量成功 \r\n",16, HAL_MAX_DELAY);
  else
	  HAL_UART_Transmit(&huart2, (uint8_t*)"创建互斥信号量失败 \r\n",16, HAL_MAX_DELAY);
void StartTask03(void const * argument)
{
  /* USER CODE BEGIN StartTask03 */
	BaseType_t xResult;
  /* Infinite loop */
  for(;;)
  {
	  HAL_UART_Transmit(&huart2, (uint8_t*)"H_StartTask03获取互斥信号量...\r\n",32, HAL_MAX_DELAY);
	  xResult=xSemaphoreTake(myMutex01Handle,portMAX_DELAY);

	  if(xResult==pdTRUE)
	  {
		  HAL_UART_Transmit(&huart2, (uint8_t*)"H_StartTask03 Running...\r\n",28, HAL_MAX_DELAY);
	  }
	  HAL_UART_Transmit(&huart2, (uint8_t*)"H_StartTask03 释放互斥信号量...\r\n",35, HAL_MAX_DELAY);
	  xResult=xSemaphoreGive(myMutex01Handle);
    osDelay(500);
  }
  /* USER CODE END StartTask03 */
}
void StartTask04(void const * argument)
{
  /* USER CODE BEGIN StartTask04 */
  /* Infinite loop */
  for(;;)
  {
	  HAL_UART_Transmit(&huart2, (uint8_t*)"M_StartTask04 runing...\r\n",27, HAL_MAX_DELAY);
    osDelay(500);
  }
  /* USER CODE END StartTask04 */
}
void StartTask05(void const * argument)
{
  /* USER CODE BEGIN StartTask05 */
	BaseType_t xResult;
  /* Infinite loop */
  for(;;)
  {
	  HAL_UART_Transmit(&huart2, (uint8_t*)"L_StartTask03获取互斥信号量...\r\n",27, HAL_MAX_DELAY);
	  xResult=xSemaphoreTake(myMutex01Handle,portMAX_DELAY);

	  if(xResult==pdTRUE)
	  {
		  HAL_UART_Transmit(&huart2, (uint8_t*)"L_StartTask03 Running...\r\n",28, HAL_MAX_DELAY);
	  }

	  HAL_Delay(3000);
	  HAL_UART_Transmit(&huart2, (uint8_t*)"L_StartTask03 释放互斥信号量...\r\n",28, HAL_MAX_DELAY);
	  xResult=xSemaphoreGive(myMutex01Handle);
    osDelay(500);
  }
  /* USER CODE END StartTask05 */
}

Guess you like

Origin blog.csdn.net/qq_57594025/article/details/132261925