FreeRTOS Queue

1. FreeRTOS Queue

Hàng đợi (queue) là hình thức chính giao tiếp giữa các tác vụ (task). Chúng có thể được sử dụng để gởi tin nhắn (message) giữa các task với nhau và giữa các task với các ngắt (interrupt). Trong hầu hết trường hợp, chúng được sử dụng làm bộ đệm FIFO (First In First Out) an toàn cho luồng (thread safe FIFO buffer) với dữ liệu mới được gởi tới phía sau của queue, mặc dù dữ liệu cũng có thể được gởi tới phía trước.

2. Thuận lợi cho người dùng

Mô hình sử dụng queue của FreeRTOS quản lý kết hợp tính đơn giản (simplicity) với tính linh hoạt (flexibility) – các thuộc tính này thường loại trừ lẫn nhau. Message được gởi qua queue bằng cách copy, nghĩa là dữ liệu (có thể là một con trỏ tới bộ đệm lớn hơn) được copy vào queue thay vì queue luôn chỉ lưu trữ một tham chiếu đến dữ liệu. Đây là cách tiếp cận tốt nhất vì:

  • Các message nhỏ chứa sẵn trong các biến C như số nguyên (integer), cấu trúc nhỏ (small structure), v.v. có thể được gởi trực tiếp vào queue mà không cần cấp phát (allocate) bộ đệm cho message rồi copy biến vào bộ đệm được cấp phát. Tương tự như vậy, các message cũng có thể được đọc trực tiếp từ queue vào các biến C.
  • Hơn nữa, việc gởi đến queue theo cách này cho phép task gởi ghi đè ngay lập tức biến hoặc bộ đệm đã được gởi đến queue, ngay cả khi message đã gởi vẫn còn trong queue. Bởi vì dữ liệu chứa trong biến đã được copy vào queue nên bản thân biến đó có thể sử dụng lại. Không có quy định nào yêu cầu task gởi message và task nhận message phải thống nhất là task nào sở hữu message và task nào chịu trách nhiệm giải phóng (free) message khi không còn dùng nữa.
  • Truyền dữ liệu bằng bản sao (pass data by copy) cho queue không ngăn cản việc truyền dữ liệu bằng tham chiếu (pass data by reference) cho queue. Khi kích thước của message đạt đến mức không hợp lý để copy toàn bộ message vào queue theo từng byte, thì hãy define queue để chứa các con trỏ và chỉ cần copy một con trỏ tới message vào queue. Đây chính xác là cách implement FreeRTOS-Plus-UDP để truyền các bộ đệm mạng (network buffer) lớn xung quanh FreeRTOS IP stack.
  • Kernel chịu trách nhiệm hoàn toàn trong việc cấp phát bộ nhớ làm vùng lưu trữ queue.
  • Các message có kích thước thay đổi có thể được gởi bằng cách định nghĩa (define) một queue giữ các cấu trúc chứa một member trỏ đến message được xếp trong queue và một member khác chứa kích thước của message được xếp trong queue.
  • Một queue có thể nhận (receive) nhiều kiểu message khác nhau và các message từ nhiều vị trí, bằng cách define một queue để giữ một cấu trúc (structure) có một member chứa kiểu message (message type) và một member khác chứa dữ liệu message (message data), hoặc một con trỏ tới dữ liệu message (a pointer to the message data). Dữ liệu được diễn giải (interpret) như thế nào tùy thuộc vào message type. Đây chính xác là cách task quản lý FreeRTOS-Plus-UDP IP stack có thể sử dụng một queue duy nhất để nhận thông báo của các ARP timer event, các packet được nhận từ phần cứng Ethernet, các packet được nhận từ ứng dụng, các sự kiện ngừng hoạt động mạng (network down event), v.v.
  • Việc triển khai phù hợp để sử dụng trong môi trường được bảo vệ bộ nhớ (memory protected environment). Một task bị hạn chế trong vùng bộ nhớ được bảo vệ (protected memory area) có thể truyền dữ liệu đến một task bị hạn chế ở một vùng bộ nhớ được bảo vệ khác nhờ RTOS bằng cách gọi hàm send queue để nâng cao mức đặc quyền của bộ vi điều khiển (microcontroller privilege level). Khu vực lưu trữ queue thì chỉ được truy cập bởi RTOS vì có toàn bộ đặc quyền.
  • Một API riêng biệt được cung cấp để sử dụng riêng trong ngắt (interrupt). Việc phân tách API được sử dụng trong RTOS task và API được sử dụng trong trình phục vụ ngắt (interrupt service routine) có nghĩa là việc implement các hàm RTOS API không kiểm tra ngữ cảnh cuộc gọi (call context), mỗi khi chúng thực thi.
  • Trong hầu hết các trường hợp, sử dụng API ngắt riêng cũng có nghĩa là việc tạo interrupt service routine điều khiển bởi RTOS sẽ đơn giản hơn cho người dùng cuối (end user), so sánh với các sản phẩm RTOS thay thế khác.
  • Về mọi mặt, API đơn giản hơn.

Bạn có thể tham khảo sách hướng dẫn FreeRTOS có thông tin bổ sung về queue, binary semaphore, mutex, counting semaphore và recursive semaphore, cùng với các ví dụ đơn giản.

3. Block trong Queue

Các hàm queue API cho phép chỉ định thời gian chặn (block time).

Khi một task cố gắng đọc (read) từ một queue trống (empty queue), task đó sẽ bị đặt vào trạng thái bị chặn (Blocked state), để nó không tiêu tốn bất kỳ thời gian CPU nào và các task khác có thể chạy, cho đến khi có dữ liệu trong queue hoặc block time hết.

Khi một task cố gắng ghi (write) vào một queue đầy (full queue), task đó sẽ bị đặt vào trạng thái bị chặn (Blocked state), để nó không tiêu tốn bất kỳ thời gian CPU nào và các task khác có thể chạy, cho đến khi có khoảng trống trong queue hoặc block time hết.

Nếu có nhiều hơn một task đang block trên cùng một queue thì task có mức độ ưu tiên cao nhất (the highest priority) sẽ là task được bỏ chặn (unblock) đầu tiên.

Xem phần Queue Management của tài liệu người dùng để biết danh sách các hàm API liên quan đến queue. Tìm kiếm các file trong folder FreeRTOS/Demo/Common/Minimal tiết lộ nhiều ví dụ về cách sử dụng chúng.

Lưu ý rằng các interrupt KHÔNG được sử dụng các hàm API không kết thúc bằng “FromISR“.

Icons made by Freepik from www.flaticon.com