Ở bài viết này mình sẽ hướng dẫn các bạn về Mutex. Mutex trong RTOS là gì, có bao nhiêu loại Mutex, nó được sử dụng và khởi tạo như thế nào? Các API của Mutex là những API nào và cách sử dụng nó ra sao ?

Mutex

Sử dụng cho việc loại trừ lẫn nhau(mutial exclution), nghĩa là nó được dùng để hạn chế quyền truy cập tới một số resource, mutex hoạt động như là một token để bảo vệ share resource, về cách hoạt động này khá giống với lại semaphore

Một task nếu muốn access vào share resource thì:

  • Cần acquire (đợi) mutex trước khi truy cập vào share resource.
  • Release token khi kết thúc với resource.

Tại mỗi một thời điểm thì chỉ có 1 task duy nhất có được mutex. Khi task này có mutex thì những task khác cũng muốn mutex này đều sẽ bị block cho tới khi task hiện tại release mutex ra.

Về cơ bản thì Mutex giống như binary semaphore nhưng được sử dụng cho việc loại trừ lẫn nhau chứ không phải đồng bộ.

Ngoài ra thì nó bao gồm cơ chế thừa kế mức độ ưu tiên(Priority inheritance mechanism) để giảm thiểu vấn đề đảo ngược ưu tiên, cơ chế này có thể hiểu đơn giản qua ví dụ sau:

  • Task A (low priority) yêu cầu mutex
  • Task B (high priority) muốn yêu cầu cùng mutex trên.
  • Mức độ ưu tiên của Task A sẽ được đưa tạm về Task B để cho phép Task A được thực thi
  • Task A sẽ thả mutex ra, mức độ ưu tiên sẽ được khôi phục lại và cho phép Task B tiếp tục thực thi.

Tạo Mutex

Bước 1: Định nghĩa mutex bằng hàm

osMutexDef(myMutex01);

Bước 2: Tạo mutex bằng hàm

osMutexId (myMutex01Handle);
myMutex01Handle = osMutexCreate(osMutex(myMutex01);

Với

  • myMutex01: Tên của mutex

Acquire và release mutex

Task có thể acquire/ wait mutex với hàm

osMutexWait(myMutex01, osWaitForever);

Trong đó

  • myMutex01: tên của mutex
  • osWaitForever: giá trị timeout, trường hợp ko có timeout thì bằng 0

Task có thể release mutex với hàm

osMutexRelease(myMutex01);

Trong đó

  • myMutex01: tên của mutex

API của mutex

Feature CMSIS RTOS API FreeRTOS API
Create mutex osMutexCreate xSemaphoreCreateMutex
Delete mutex osMutexDelete vQueueDelete
Acquire mutex osMutexWait xSemaphoreTake
Release mutex osMutexRelease xSemaphoreGive
Create recursivemutex osRecursiveMutexCreate xSemaphoreCreateRecursiveMutex
Acquire recursivemutex osRecursiveMutexWait xSemaphoreTakeRecursive
Release recursivemutex osRecursiveMutexRelease xSemaphoreGiveRecursive

Ví dụ

Trong ví dụ này ta sẽ tạo 2 task đều sử dụng hàm printf và mutex sẽ được dùng để tránh xung đột.

Mở CubeMX chỉnh cấu hình cho FreeRTOS

  • Sử dụng CubeMX để tạo 2 task với cùng priority
  • Cấu hình enable mutex trong config
  • Tạo mutex với tên tương ứng

Sau khi gen code, quan sát ta sẽ thấy

Định nghĩa mutex handle

/* Private variables ---------------------------------------------------------*/
osThreadId Task1Handle;
osThreadId Task2Handle;
osMutexId myMutex01Handle;

Tạo mutex

/* Create the mutex(es) */
/* definition and creation of myMutex01 */
osMutexDef(myMutex01);
myMutex01Handle = osMutexCreate(osMutex(myMutex01));

Điều chỉnh chương trình trong file main.c để Task 1 và 2 sử dụng mutex

Task 1:

/* USER CODE END Header_StartTask1 */
void StartTask1(void const * argument)
{
 
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
        osDelay(2000);
        osMutexWait(myMutex01Handle, 1000);
        printf("Task 1 Print\n");
        osMutexRelease(myMutex01Handle);
  }
  /* USER CODE END 5 */
}

Task 2:

/* USER CODE END Header_StartTask2 */
void StartTask2(void const * argument)
{
  /* USER CODE BEGIN StartTask2 */
  /* Infinite loop */
  for(;;)
  {
        osDelay(2000);
        osMutexWait(myMutex01Handle, 1000);
        printf("Task 2 Print\n");
        osMutexRelease(myMutex01Handle);
  }
  /* USER CODE END StartTask2 */
}

Kết quả

Hình minh họa hoạt động của ví dụ trên

Ví dụ 2: Chờ update nha các bạn 😀

Tạm kết

Vậy là ở bài viết này mình đã làm rõ về Mutex trong RTOS là gì, sơ lược về Mutex , cách tạo Mutex ? Các API của Mutex và cách sử dụng nó trong ví dụ về Mutex. Phần tiếp theo sẽ là Software Timer trong RTOS, mời các bạn lót dép ngồi chờ nhé 😀