Loại Semaphore
FreeRTOS cung cấp bốn loại semaphore chính:
- Binary semaphore: Dùng cho đồng bộ tác vụ, tương đương với cờ hiệu trong hệ thống không RTOS
- Counting semaphore: Quản lý tài nguyên chia sẻ, giá trị đếm biểu thị số lượng tài nguyên khả dụng
- Mutex: Bảo vệ vùng giao tiếp, giải quyết vấn đề ưu tiên đảo ngược (priority inversion)
- Recursive mutex: Cho phép cùng tác vụ lấy lại mutex mà không cần giải phóng trước
Tạo Semaphore Đếm
/* Tạo semaphore đếm với giới hạn tối đa và giá trị khởi tạo */
SemaphoreHandle_t createCountingSemaphore(UBaseType_t maxCount, UBaseType_t initValue);
Tham số:
- maxCount: Giá trị đếm tối đa
- initValue: Giá trị khởi tạo
Trả về: Handler nếu thành công, NULL nếu thiếu bộ nhớ
Tạo Binary Semaphore
/* Tạo semaphore nhị phân */
SemaphoreHandle_t createBinarySemaphore(void);
Lưu ý: Giá trị khởi tạo là 0. Nên dùng notification message thay vì binary semaphore để tối ưu hiệu năng.
Tạo Mutex
/* Tạo mutex bảo vệ vùng giao tiếp */
SemaphoreHandle_t createMutex(void);
Lưu ý: Không dùng trong ISR, chỉ dùng trong tác vụ.
Giải Phóng Semaphore
/* Giải phóng cho semaphore đếm, nhị phân, mutex */
BaseType_t releaseSemaphore(SemaphoreHandle_t semaHandle);
/* Giải phóng trong ISR */
BaseType_t releaseSemaphoreFromISR(SemaphoreHandle_t semaHandle, BaseType_t* higherTask);
/* Giải phóng recursive mutex */
BaseType_t releaseRecursiveMutex(SemaphoreHandle_t mutexHandle);
Chú ý: Trong ISR, tham số thứ hai xác định có tác vụ ưu tiên cao hơn sẵn sàng. Nếu true, cần gọi taskYIELD() sau khi kết thúc ISR.
Lấy Semaphore
/* Lấy semaphore cho đếm, nhị phân, mutex */
BaseType_t takeSemaphore(SemaphoreHandle_t semaHandle, TickType_t waitTime);
/* Lấy trong ISR */
BaseType_t takeSemaphoreFromISR(SemaphoreHandle_t semaHandle, BaseType_t* higherTask);
/* Lấy recursive mutex */
BaseType_t takeRecursiveMutex(SemaphoreHandle_t mutexHandle, TickType_t waitTime);
Chú ý: Không dùng mutex trong ISR. Tham số waitTime xác định thời gian chờ tối đa.
Vấn Đề Ưu Tiên Đảo Ngược
Xảy ra khi dùng binary semaphore thông thường:
- Tác vụ
HighPriority(ưu tiên cao) vàLowPriority(ưu tiên thấp) chia sẻ tài nguyên LowPrioritychiếm tài nguyên, sau đóHighPrioritytrở về trạng thái sẵn sàngMediumPriority(ưu tiên trung bình) chạy trướcHighPrioritydù ưu tiên thấp hơn
Giải pháp: Mutex sử dụng cơ chế kế thừa ưu tiên. Khi HighPriority chờ tài nguyên, LowPriority sẽ được nâng cấp ưu tiên bằng với HighPriority, đảm bảo LowPriority hoàn thành trước khi HighPriority chạy.