Ring Buffer là gì trong firmware embedded?

Giải thích Ring Buffer với head, tail, empty, full và wrap-around trong embedded C.

3 phút đọc
Embedded C cover

Ghi chú nhanh

Ring Buffer là một hàng đợi dùng mảng cố định. Khi ghi tới cuối mảng, vị trí ghi sẽ quay lại đầu mảng.

Hai biến quan trọng nhất:

head = vị trí ghi dữ liệu mới
tail = vị trí đọc dữ liệu cũ

Trong firmware, Ring Buffer rất được ưa chuộng vì nó không cần cấp phát động, chạy nhanh, và dự đoán được bộ nhớ sử dụng.

Hình dung đơn giản

Hãy tưởng tượng một băng chuyền hình tròn.

Producer đặt hàng vào vị trí head. Consumer lấy hàng ở vị trí tail. Khi đi tới cuối băng chuyền, cả hai quay lại đầu.

[0] [1] [2] [3] [4] [5] [6] [7]
         ^head           ^tail

Sau mỗi lần ghi:

head = (head + 1) % RING_BUF_SIZE;

Đây là dòng code nhỏ nhưng xuất hiện ở rất nhiều nơi: UART RX, UART TX logger, USB stream, audio buffer, event queue…

Empty và Full khác nhau thế nào?

Buffer trống khi:

head == tail

Nhưng nếu ta cho phép ghi đầy 100% mảng, trạng thái full cũng có thể làm head == tail. Vì vậy cách đơn giản thường dùng là chừa lại 1 byte trống.

Buffer đầy khi:

next_head == tail

Ví dụ:

uint16_t next = (head + 1) % RING_BUF_SIZE;
if (next == tail) {
    // buffer full
}

Mất 1 byte dung lượng, nhưng đổi lại logic rõ ràng và ít bug hơn. Trong embedded, ít bug hơn thường đáng giá hơn 1 byte RAM.

Vì sao hữu ích trong firmware?

Ring Buffer giúp tách hai tốc độ khác nhau:

  • CPU ghi log rất nhanh vào RAM;
  • UART/DMA truyền log chậm hơn ở background.

Nếu không có buffer, producer phải chờ consumer. Với debug log, điều này có thể làm thay đổi timing của firmware.

Bẫy dễ gặp

1. Quên xử lý wrap-around

DMA không thể luôn truyền một vùng dữ liệu liên tục từ tail đến head, vì dữ liệu có thể bị chia làm hai đoạn khi vòng qua cuối mảng.

[old data .... end][begin .... new data]

Thường phải truyền đoạn từ tail đến cuối buffer trước, sau đó callback truyền tiếp đoạn từ đầu buffer.

2. Cập nhật tail sai trong DMA callback

Không nên làm kiểu:

tail = head; // nguy hiểm

Vì trong lúc DMA đang truyền, producer có thể đã ghi thêm dữ liệu mới làm head thay đổi. Callback chỉ nên tăng tail đúng bằng số byte DMA vừa truyền xong.

Cách nhớ

Ring Buffer = hàng đợi vòng tròn trong RAM.
Producer ghi ở head.
Consumer đọc ở tail.
Full thì phải có policy rõ ràng.

Bài liên quan nên đọc tiếp

Thấy nội dung này hữu ích?

Lưu lại hoặc chia sẻ cho người cũng đang học firmware, BIOS/UEFI và embedded systems.

Nội dung liên quan

Một số bài viết, ghi chú hoặc project có liên quan đến nội dung bạn vừa đọc.

Biến note thành bài viết hoàn chỉnh

Notes là nơi ghi nhanh khái niệm.