Ở bài viết này mình sẽ hướng dẫn các bạn về Semaphore trong RTOS. Semaphore trong RTOS là gì, có bao nhiêu loại Semaphore, nó được sử dụng và khởi tạo như thế nào? Các API của Semaphore là những API nào và cách sử dụng nó ra sao ?
Semaphore
Trước tiên chúng ta cần xem lại khái quát nội dung về Queue trong RTOS ở bài viết RTOS cơ bản. Mình xin nhắc lại bên dưới
Semaphore được dùng để động bộ giữa các
- Task
- Ngắt và task
Có 2 dạng là
- Binary semaphore
- Counting semaphore
Binary semaphore có duy nhất 1 token và sử dụng để đồng bộ một action
Counting semaphore sẽ có nhiều token và đồng bộ nhiều action khác nhau
Mặc định thì có binary semaphore còn counting semaphore được disable trong CubeMX, do đó muốn dùng thì chúng ta phải bật nó lên bằng cờ USE_COUNTING_SEMAPHORES
Người ta thường dùng semaphore để couting event hoặc resouce management
Counting event
- Một event handler sẽ ‘give’ semaphore khi có event xảy ra (tăng giá trị đếm semaphore)
- Một task handler sẽ ‘take’ semaphore khi nó thực thi sự kiện (giảm giá trị đếm semaphore)
- Count value là khác nhau giữa số sự kiện xảy ra và số sự kiện được thực thi
- Trong trường hợp counting event thì semaphore được khởi tạo giá trị đếm bằng 0
Resource management
- Count value sẽ chỉ ra số resource sẵn có
- Để điều khiển và kiểm soát được resource của task dựa trên count value của semaphore(giá trị giảm), nếu count value giảm xuống bằng 0 nghĩa là không có resource nào free.
- Khi một task finish với resource thì nó sẽ give semaphore trở lại để tăng count value của semaphore.
- Trong trường hợp resouce management thì count value sẽ bằng với giá trị max của count value khi semaphore được tạo.
Tạo semaphore
Dưới đây là các bước tạo semaphore cơ bản nhất
Bước 1: Định nghĩa semaphore bằng hàm
osSemaphoreDef (myCountingSem01);
Trong đó:
myCountingSem01
: là tên của semaphore
Bước 2: Tạo semaphore dùng hàm osSemaphoreCreate()
, hàm này sẽ return lại ID/handle được dùng bởi các API khác liên quan tới tạo semaphore
osSemaphoreId (myCountingSem01Handle);
myCountingSem01Handle = osSemaphoreCreate(osSemaphore(myCountingSem01), 2);
Trong đó:
- myCountingSem01: Tên của semaphore
- 2: Số lượng token được dùng để tạo semaphore
Acquire và release semaphore
Task có thể acquire(lấy) semaphore bằng hàm
osSemaphoreWait(myCountingSem01Handle, osWaitForever);
Trong đó
- myCountingSem01Handle: Semaphore Id/handle
- osWaitForever: giá trị timeout, nếu không dùng timeout thì giá trị là 0
Task release semaphore bằng hàm
osSemaphoreRelease(myCountingSem01Handle);
Với myCountingSem01Handle: semaphore ID/handle
Trước khi vào ví dụ cụ thể ta có thể xem ví dụ minh họa dưới đây
Ở ví dụ trên, task A, task B, task C có cùng một priority, semaphore có 2 token
- Task C gọi hàm osSemaphoreWait() nhưng semaphore token đã được take bởi task A và task B
- Trạng thái của task C sẽ được set sang waiting và Task A đã được bắt đầu. Task B thì đang trong ready
- Task B gọi hàm osSemaphoreRelease(). Trạng thái của task C sẽ được set sang ready. Vì tất cả các task có cùng priority nên task B sẽ tiếp tục thực thi
- Cuối cùng là Task C sẽ được thực thi
Semaphore API
Bên dưới là bảng tổng hợp các API có trong Semaphore
Feature | CMSIS RTOS API | FreeRTOS API |
---|---|---|
Create semaphore | osSemaphoreCreate | xSemaphoreCreateCounting vSemaphoreCreateBinary |
Delete semaphore | osSemaphoreDelete | vSemaphoreDelete |
Acquire semaphore | osSemaphoreWait | xSemaphoreTake |
Release semaphore | osSemaphoreRelease | xSemaphoreGive |
Ví dụ
Binary Semaphore
Trong ví dụ này ta sẽ tạo ra 2 task, một task sẽ release semaphore và một task sẽ wait semaphore
Mở CubeMX chỉnh cấu hình cho FreeRTOS
- Tạo ra 2 task với cùng priority
- Tạo binary semaphore, set name
Sau khi gen code, xem chương trình trong file main.c ta thấy
Định nghĩa semaphore handle
/* Private variables ---------------------------------------------------------*/
osThreadId Task1Handle;
osThreadId Task2Handle;
osSemaphoreId myBinarySem01Handle;
Tạo semaphore
/* Create the semaphores(s) */
/* definition and creation of myBinarySem01 */
osSemaphoreDef(myBinarySem01);
myBinarySem01Handle = osSemaphoreCreate(osSemaphore(myBinarySem01), 1);
Thay đổi một chút ở task 1
- Dùng semaphore
- Nếu task/interrupt mà hoàn thành thì semaphore sẽ được release
/* StartTask1 function */
void StartTask1(void const *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for (;;)
{
osDelay(2000);
printf("Task 1 Release semaphore\n");
osSemaphoreRelease(myBinarySem01Handle);
}
/* USER CODE END 5 */
}
Task 2 sẽ được thay đổi thành
- Đợi semaphore
- Task 2 sẽ đợi cho tới khi semaphore release, sau khi task release thì sẽ tiếp tục công việc như bình thường
/* StartTask2 function */
void StartTask2(void const *argument)
{
/* USER CODE BEGIN StartTask2 */
/* Infinite loop */
for (;;)
{
osSemaphoreWait(myBinarySem01Handle, 4000);
printf("Task 2 synchronized\n");
}
/* USER CODE END StartTask2 */
}
Task 1 sẽ được đồng bộ với Task 2 như hình dưới
Kết quả
Counting semaphore
Ở ví dụ này ta sẽ tạo ra 3 task, task 1 và 2 như nhau, task 3 sẽ chờ semaphore release từ 2 task trước
Mở cubeMX cấu hình:
- Tạo 3 task với cùng priority
- Chỉnh cấu hình cho phép add counting semaphore
- Tạo counting semaphore và set số lượng token là 2
Sau khi gen code ra thì điều chỉnh trong file main.c
Tạo Counting semaphore
/* definition and creation of myCountingSem01 */
osSemaphoreDef(myCountingSem01);
myCountingSem01Handle = osSemaphoreCreate(osSemaphore(myCountingSem01), 2);
Task 1 và task 2 giống nhau đều release counting semaphore
Task 1
/* USER CODE END Header_StartTask1 */
void StartTask1(void const *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for (;;)
{
osDelay(2000);
printf("Task 1 Release counting semaphore\n");
osSemaphoreRelease(myBinarySem01Handle);
}
/* 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);
printf("Task 2 Release counting semaphore\n");
osSemaphoreRelease(myBinarySem01Handle);
}
/* USER CODE END StartTask2 */
}
Task 3 sẽ đợi cho tới khi có semaphore được release từ task 1 và task 2
/* USER CODE END Header_StartTask3 */
void StartTask3(void const *argument)
{
/* USER CODE BEGIN StartTask3 */
/* Infinite loop */
for (;;)
{
osSemaphoreWait(myCountingSem01Handle, 4000);
osSemaphoreWait(myCountingSem01Handle, 4000);
printf("Task 3 Synchronized\n");
}
/* USER CODE END StartTask3 */
}
Hoạt động chương trình sẽ như hình minh họa bên dưới
Kết quả
Tạm kết
Vậy là ở bài viết này mình đã làm rõ về Semaphore trong RTOS là gì, sơ lược về Binary Semaphore và Couting semaphore, cách tạo Semaphore ? Các API của semaphore và cách sử dụng nó trong các ví dụ với binary semaphore và counting semaphore. Còn một anh cuối cùng là mutex lại xin hẹn các bạn ở bài viết kế tiếp.