FreeRTOS Binary Semaphore

Semaphore nhị phân (binary semaphore) được sử dụng cho cả hai mục đích là loại trừ lẫn nhau (mutual exclusion) và đồng bộ hóa (synchronization).

Binary semaphore và mutex rất giống nhau nhưng có một số khác biệt nhỏ: Mutex bao gồm cơ chế kế thừa ưu tiên (priority inheritance mechanism), binary semaphore thì không. Điều này làm cho các binary semaphore trở thành lựa chọn tốt hơn để thực hiện đồng bộ hóa (giữa các task hoặc giữa các task và một ngắt), còn mutex trở thành lựa chọn tốt hơn để thực hiện loại trừ lẫn nhau đơn giản (simple mutual exclusion). Phần trình bày cách sử dụng mutex như một cơ chế loại trừ lẫn nhau giữ cân bằng cho binary semaphore. Phần phụ này chỉ trình bày về việc sử dụng binary semaphore để đồng bộ hóa.

Các hàm Semaphore API cho phép chỉ định thời gian chặn (block time). Block time chỉ thị số lượng ‘tick’ tối đa mà một task sẽ đi vào trạng thái Blocked khi cố gắng ‘take’ một semaphore, nếu semaphore đó không khả dụng (not available) ngay lập tức. Nếu có nhiều hơn một task chặn (block) trên cùng một semaphore thì task có mức độ ưu tiên cao nhất (the highest priority) sẽ là task được bỏ chặn (unblocked) vào lần tiếp theo khi semaphore này khả dụng.

Hãy coi binary semaphore như một hàng đợi (queue) chỉ chứa một item. Do đó, queue chỉ có thể trống (empty) hoặc đầy (full), nên gọi là nhị phân. Các task và ngắt (interrupt) sử dụng queue mà không quan tâm queue chứa cái gì – chúng chỉ muốn biết queue trống hay đầy. Cơ chế này có thể được khai thác để đồng bộ hóa, ví dụ một task với một interrupt.

Hãy xem xét trường hợp một task phục vụ một thiết bị ngoại vi (peripheral).Polling (một phương pháp sử dụng vòng lặp) thiết bị ngoại vi sẽ gây lãng phí tài nguyên CPU và ngăn cản các task khác thực thi. Do đó, tốt nhất là task nên dành phần lớn thời gian ở trạng thái Blocked (để cho phép các task khác thực thi) và chỉ tự thực thi khi thực sự có việc gì đó để nó làm. Cách này sử dụng một binary semaphore để task bị block trong khi cố gắng ‘take’ semaphore. Sau đó, một interrupt routine được viết cho thiết bị ngoại vi sẽ ‘give’ semaphore khi thiết bị ngoại vi yêu cầu phục vụ. Task luôn ‘take’ semaphore (read từ queue để làm cho queue empty), nhưng không bao giờ ‘give’ nó. Interrupt luôn ‘give’ semaphore (write vào queue để full) nhưng không bao giờ ‘take’ nó. Code được cung cấp trên trang tài liệu xSemaphoreGiveFromISR() sẽ làm rõ việc này. Đồng thời xem hãy RTOS task notifications, có thể được sử dụng làm giải pháp thay thế binary semaphore để có tốc độ nhanh hơn và nhẹ hơn trong một số trường hợp.

Tính ưu tiên task được sử dụng để đảm bảo các thiết bị ngoại vi nhận được dịch vụ (sevice) kịp thời – tạo ra sơ đồ ‘ngắt trì hoãn’ (deferred interrupt) một cách hiệu quả (lưu ý FreeRTOS cũng có cơ chế ngắt trì hoãn được tích hợp sẵn). Một cách tiếp cận khác là sử dụng queue thay cho semaphore. Khi cách này được thực hiện, interrupt routine có thể thu thập dữ liệu liên quan đến sự kiện của ngoại vi và gửi nó lên queue đến task. Task sẽ bỏ chặn (unblock) khi có dữ liệu trên queue, truy xuất dữ liệu từ queue, sau đó thực hiện xử lý dữ liệu theo yêu cầu. Sơ đồ thứ hai cho phép interrupt xử lý càng ngắn càng tốt, tất cả quá trình xử lý sau đó đều ở task.

Hãy xem phần Semaphore/Mutex trong tài liệu người dùng để biết danh sách các hàm API liên quan đến semaphore. Tìm kiếm các file trong folder FreeRTOS/Demo/Common/Minimal sẽ tiết lộ nhiều ví dụ về cách sử dụng. Lưu ý rằng interrupt KHÔNG được sử dụng các hàm API không kết thúc bằng “FromISR”.

Hình 1. Sử dụng một semaphore để đồng bộ một task với một interrupt. Interrupt chỉ có ‘gives’ semaphore, trong khi task chỉ có ‘takes’ semaphore.

1 thought on “FreeRTOS Binary Semaphore”

  1. Pingback: FreeRTOS Mutex – Lập Trình Nhúng dành cho Sinh Viên

Comments are closed.

Icons made by Freepik from www.flaticon.com