Học FreeRTOS, p2

Phần 2 trong series này sẽ tiếp tục với phần quản lý bộ nhớ của FreeRTOS; tiếp tục sau Phần 1 đề cập đến việc tạo task.

Nếu lập trình trên PC, bạn sẽ quen thuộc với malloc, free để tạo, hủy một vùng nhớ. Ở trên hệ thống nhúng, việc sử dụng memory dynamic allocation không được khuyến khích. Các nguyên nhân có thể tóm gọn như sau:

  • Không xác định (not deterministic). Một hàm có thể được thực hiện/không tùy thuộc vào thời điểm gọi. Trong hệ thống RTOS điều này có thể khó chấp nhận khi không có người tương tác, và người thiết kế mong đợi chương trình luôn chạy theo kịch bản định sẵn.
  • Chương trình malloc/free có thể chiếm tài nguyên trong các hệ thống nhúng (phụ thuộc vào compiler) thường hạn hẹp RAM/ROM.
  • [Phân mảnh](http://www.design-reuse.com/articles/25090/dynamic-memory-allocation-fragmentation-c.html) bộ nhớ.
  • Một số implement malloc/free thường không thread-safe. Nếu chương trình multitask (đó là lý do bạn dùng FreeRTOS), bạn nên bảo vệ nó.

Do đó, khi viết chương trình nhúng, tôi hạn chế malloc/free.

Với kernel thì không có sự lựa chọn khác. FreeRTOS cần tạo/xóa vùng nhớ động mỗi khi các task, queue, hay semaphore được tạo/hủy. Đây là một phần của kernel.

FreeRTOS định nghĩa tổng số byte heap được cung cấp ở configTOTAL_HEAP_SIZE, trong file FreeRTOSConfig.h. Vùng heap này được dùng cho pvPortMallocvPortFree, hai implementation của malloc/free.

FreeRTOS cung cấp lớp quản lý bộ nhớ này độc lập, để kernel này chạy được với các nền tảng phần cứng khác nhau và trình biên dịch khác nhau, ở FreeRTOS/Source/portable/MemMang. Bạn có thể thấy tất cả các lựa chọn, gồm `heap_1.c, heap_2.c, heap_3.c, heap_4.c, heap_5.c’. Hoặc bạn có thể viết chương trình quản lý bộ nhớ của riêng mình🙂.

Theo mặc định của trong series này https://github.com/nqd/learn_freertos, heap_3.c heap_4.c được sử dụng (nguyên nhân ở bên dưới). Bạn có thể đổi dòng tương ứng ở dòng CFILES += heap_3.c port.c, Makefile.common để có sự lựa chọn khác.

Phần sau tôi sẽ trình bay sự khác nhau của các loại heap_* này.

 

Heap_1.c

Đơn gianr nhất, chỉ cung cấp malloc với hàm pvPortMalloc và không có vPortFree.

Selection_015

Hình trên diễn tả qúa trình các task chiếm dụng configTOTAL_HEAP_SIZE. A là lúc chưa có task nào được khởi tạo. B là lúc có một task được tạo. Mỗi task có stack riêng, quyết định bởi thông số đầu vào usStackDepth của hàm xTaskCreate. Mỗi task còn có vùng task control block (TCB) để làm đại diện cho task đó, xem thêm về TCB.

Như vậy số heap trống sẽ được xác định rõ ràng như trên hình, và lấy được thông qua hàm xPortGetFreeHeapSize.

Kết luận: Nếu chương trình của bạn không hủy task, queue, semaphore, hay gọi malloc, task_1.c là sự lựa chọn cho quản lý bộ nhớ.

 

Heap_2.c

Phức tạp hơn heap_1, với cơ chế free đơn gianr.

Heap_2 sử dụng tốt nhất khi pvPortMalloc sử dụng lại một lượng bộ nhớ vừa đúng với kích thước đang còn trống. Ví dụ trong chương trình tuần tự tạo và xóa một task mà kích thước stack cho task này không thay đổi.

Selection_016

Hình trên diễn tả qúa trình task được khởi tạo, xóa, và sử dụng lại. A là lúc 3 task đang hoạt động. Heap trông nằm ở trên cùng. B là lúc task 2 được free, và có một lỗ trống nằm giữa task 1 và 3. Tuy nhiên, heap_2 không có cơ chế gom bộ nhớ, nên free space giữa hai task vẫn nằm vậy.

C diễn tả qúa trình một task mới được khởi tạo, hai vùng nhớ TCB và stack được gọi tương ứng; task này đặt vừa vào vùng nhớ giữa. Qúa trình này diễn tả trường hợp hoạt động tốt nhất của heap_2.

Heap_2 sử dụng best fit algorithm cho pvPortMalloc. Ví dụ có 3 khoảng trống trong heap với kích thước 5 Bytes, 25 Bytes, và 50 Bytes. pvPortMalloc được gọi với yêu cầu 20B. Lúc này heap_2 chia vùng nhớ 25B làm 2: 20B và 5B, rồi sử dụng 20B này. Để lại 5B cho lần gọi kế tiếp.

Bạn sẽ không muốn dùng heap_2 cho các ứng dụng pvPortMalloc/pvPortFree được gọi tùy ý.

Cũng như heap_1, configTOTAL_HEAP_SIZE trong FreeRTOSConfig.h khai báo vùng nhớ heap tổng cộng. Tổng các vùng nhớ trống được trả về qua hàm xPortGetFreeHeapSize.

 

Heap_3

Heap_3 sử dụng malloc/free của trình biên dịch cho pvPortMalloc/pvPortFree tương ứng. Heap_3 cung cấp thêm cơ chế bảo vệ hai hàm này để chúng thread safe bằng cách dừng task scheduler.

Nếu dùng gcc, bạn sẽ dùng implementation của newlib.

Với heap_3, linker heap và FreeRTOS là một, và configTOTAL_HEAP_SIZE không có tác dụng gì.

 

Heap_4

Heap_4 được xem như là phiên bản cải tiến của heap_2, với khả năng gom các vùng nhớ trống liên tiếp nhau thanh một block lớn hơn.

Ví dụ bạn có var_1, var_2, và var_3 chiếm các vị trí liên tiếp nhau 5, 25, và 100 bytes trong vùng nhớ heap của FreeRTOS. var_1 và var_2 được xóa đi, heap_2 sẽ tạo ra hai vùng nhớ trống 5 và 25 bytes; heap_4 gom lại rồi tạo ra vùng nhớ trống 30 bytes.

Heap_4 nên được sử dụng khi chương trình liên tục tạo/xóa task, queue, semaphore …, vùng heap của hệ thống sẽ ít bị phân mảnh hơn, so với heap_2.

FreeRTOS đồng thời tuyên bố nó tốt hơn hầu hết các malloc implementation khác? (xem http://www.freertos.org/a00111.html).

 

Bình luận

sử dụng heap_*.c nào tùy thuộc vào app của bạn. Đơn gianr như heap_1, hoặc heap_4 với đánh đổi tài nguyên của hệ thóng.

 

Xác nhận: Các hình ở trên được lấy từ sách Using The FreeRTOS Realtime Kernel của Richard Barry.

One thought on “Học FreeRTOS, p2”

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s