🌱 RTOS 08. Đồng bộ hóa giữa các Task với Binary Semaphore
Mặc dù các Task trong RTOS được coi là độc lập với nhau, tuy nhiên, các tài nguyên chúng sử dụng thì lại không hề độc lập. Chẳng hạn một vi điều khiển một core đang chạy RTOS, nhưng các phần bộ nhớ (RAM, FLASH, các bộ ngoại vi) là dùng chung giữa các task. Chính vì vậy, các task có thể truy cập tài nguyên cùng lúc và gây ra xung đột! Giả sử một Task ghi vào một cùng một ô nhớ! ... Việc xung đột sẽ dẫn đến chương trình chạy sai so với mong đợi.
Các nội dung chính
👉 Đồng bộ giữa các Task
Việc đồng bộ giữa các task là vô cùng quan trọng, đặc biệt là khi có những tài nguyên sử dụng chung. Đồng bộ tức là cơ chế giúp cho các task vẫn hoạt động một cách độc lập, nhưng sử dụng một số tài nguyên chung một cách hiệu quả, không bị conflict. Một vài cơ chế đồng bộ giữa các task thường được sử dụng:
- Semaphore: Sử dụng cho việc đồng bộ hóa tín hiệu và khả năng tận dụng tài nguyên.
- Event Flag: Chỉ ra một hoặc vài sự kiện đã xảy ra, Event Flag giống như mở rộng của Semaphore, trong đó cho phép đồng bộ hóa trên các sự kiện hỗn hợp.
- Mailbox, Queue, Pipe: Cơ chế truyền dữ liệu giữa các task.
👉 Semaphore
Hãy xét 2 tác vụ, mỗi môṭ tác vu ̣có nhiệm vụ in một bản tin có nội dung "I am task n" (n là môṭ số thứ tự của tác vụ), bằng môṭ máy in chia sẻ như trong hình dưới. Nếu chúng ta không sử dụng bất cứ môṭ cơ chế đồng bô ̣nào, kết quả có đươc̣ từ máy in sẽ có thể là "II a amm tatasskk 12" @@
Kết quả này rõ ràng không như chúng ta mong muốn rồi, do cơ chế lập lịch nên task này chưa thực hiện xong thì task kia đã nhảy vào thực hiện @@
Vì vậy cần có một cơ chế để các Task có thể chia sẻ tài nguyên một cách hiệu quả, đó chính là Semaphore.
🔑🔑🔑 Semaphore hoạt động giống như một chiếc chìa khóa cho việc truy cập tới tài nguyên. Chỉ có task có chìa khóa này mới có quyền sử dụng tài nguyên.
- Để có thể sử dụng tài nguyên (trong ví dụ trên là chiếc máy in), tác vụ cần yêu cầu chìa khóa để sử dụng - acquire semarphore.
- Nếu chìa khóa ở trạng thái sẵn sàng (đang không có task nào sử dụng) thì task yêu cầu có thể sử dụng tài nguyên (máy in).
- Sau khi dùng xong (in ra dòng chữ theo yêu cầu), task này sẽ phải trả lại chìa khóa - release semaphore để task khác có thể sử dụng.
👉 Binary Semaphore
Trên đây là cách hoạt động của Semaphore, cụ thể chúng ta đang xét đến loại Semaphore đơn giản nhất, đó là Binary Semaphore.
Ở đây chúng ta có một key - một biến tên print, giá trị mặc định là 0 (đang sẵn sàng).
- Hàm acquireSema4 sẽ kéo print = 1, tức là yêu cầu tài nguyên để sử dụng (chiếm dụng chìa khóa Semaphore). Lúc này print = 1, các task khác sẽ kiểm tra thấy tài nguyên đang được sử dụng (print = 1) nên sẽ phải chờ.
- Task này in xong bằng lệnh printf.
- Sau khi thực hiện xong hàm printf, task gọi hàm releaseSema4 để trả lại chìa khóa (print = 0), lúc này những task khác có thể sử dụng tài nguyên chung.
Đặc điểm của Binary Semaphore
- 1: Tài nguyên sẵn sàng hoặc task đã hoàn thành.
- 0: Tài nguyên bận hoặc task đang chờ.
Quy trình hoạt động với Binary Semaphore
➥ Các Task chờ (Take)
➥ Task hoặc ISR giải phóng (Give)
Trên đây là cơ chế hoạt động của Binary Semaphore, ở bài sau mình sẽ tiếp tục với cơ chế Counting Semaphore.