- vừa được xem lúc

Vấn đề về quản lý bộ nhớ trên vi điều khiển khi sử dụng FreeRTOS - Phần 1

0 0 8

Người đăng: delinux

Theo Viblo Asia

Giới thiệu nhanh

Ở bài trước, mình đã cùng bạn tìm hiểu về task – một trong những khái niệm quan trọng nhất khi làm việc với hệ điều hành thời gian thực (RTOS) trên vi điều khiển. Chúng ta đã thấy cách task giúp chia nhỏ chương trình thành các luồng xử lý riêng biệt, cho phép thực hiện song song các công việc một cách hiệu quả, thay vì xử lý tuần tự như trong các hệ thống nhúng truyền thống.

Trong bài viết này, mình sẽ cùng bạn đào sâu vào cách mà một task được tạo ra và lưu trữ trong bộ nhớ của vi điều khiển khi sử dụng FreeRTOS – hệ điều hành thời gian thực mã nguồn mở phổ biến nhất hiện nay. Việc hiểu rõ cách mà các tác vụ (tasks) được tạo và quản lý trong bộ nhớ không chỉ giúp bạn tránh các lỗi phổ biến như tràn stack, cạn heap, hay hệ thống bị treo ngẫu nhiên, mà còn giúp bạn tối ưu tài nguyên RAM – vốn rất hạn chế trên hầu hết các dòng vi điều khiển hiện nay.

Vì vậy, nếu bạn đang làm việc với FreeRTOS hoặc bất kỳ hệ điều hành nhúng nào khác, thì việc nắm rõ phần này là một nền tảng quan trọng để bạn có thể xây dựng hệ thống ổn định, đáng tin cậy và dễ bảo trì.


1. Task được tạo sẽ lưu ở đâu

Nếu như bạn đã học về ngôn ngữ C/C++ thì chúng ta đều biết rằng bộ nhớ của một chương trình C được chia làm 5 phần gồm: Text, Initialized data, Uninitialized data, Heap và Stack và mỗi vùng sẽ được quy định để lưu một loại dữ liệu nhất định được quy định (nếu như các bạn chưa biết thì tìm hiểu lại ngay nha, mất gốc nặng rồi đó 😦 )

image.png

Khi một hàm (function) được gọi, bộ nhớ cần thiết cho nó — bao gồm biến cục bộ và địa chỉ trả về — sẽ được cấp phát trên stack. Trong khi đó, khi bạn sử dụng cấp phát động (dynamic allocation), dữ liệu sẽ được lưu trong vùng heap.

Trong FreeRTOS, các task thường được tạo ra thông qua cơ chế cấp phát động. Điều này có nghĩa là, mỗi khi bạn khai báo tạo một task mới, vi điều khiển sẽ cần "cấp phát" một vùng nhớ trong heap để chứa thông tin quản lý task (TCB) cũng như vùng stack riêng của task đó.

Cơ chế này rất linh hoạt, cho phép tạo task tại runtime, nhưng cũng là một điểm dễ gây lỗi nếu bạn quản lý bộ nhớ không tốt. Việc sử dụng không kiểm soát có thể dẫn đến tràn stack, cạn heap, hoặc fragmentation — từ đó gây crash, reset ngẫu nhiên, hoặc khiến hệ thống trở nên không ổn định và khó tin cậy.

2. Cấu trúc bộ nhớ của một tác vụ trong FreeRTOS

Mỗi tác vụ trong FreeRTOS bao gồm hai thành phần chính:

  • Task Control Block (TCB): Là một cấu trúc dữ liệu chứa thông tin về trạng thái của tác vụ, như con trỏ ngăn xếp, độ ưu tiên, và các tham số khác.

  • Stack (Ngăn xếp): Là vùng bộ nhớ mà tác vụ sử dụng để lưu trữ các biến cục bộ, địa chỉ trả về và thông tin ngữ cảnh khi chuyển đổi tác vụ. Lưu ý: Stack này là riêng của Task và nằm trong Heap không liên quan tới Stack trong vùng nhớ của lập trình C.

Khi một tác vụ được tạo bằng hàm xTaskCreate(), FreeRTOS sẽ tự động cấp phát bộ nhớ cho cả TCBStack từ heap. Ngược lại, khi sử dụng xTaskCreateStatic(), lập trình viên phải cung cấp bộ nhớ cho cả hai thành phần này.

image.png

3. Quá trình tạo tác vụ trong FreeRTOS

Quá trình tạo một tác vụ trong FreeRTOS có thể được chia thành ba giai đoạn chính:

3.1. Cấp phát bộ nhớ

  • Cấp phát động (Dynamic Allocation): Sử dụng xTaskCreate(), FreeRTOS sẽ tự động cấp phát bộ nhớ cho TCB và ngăn xếp từ heap. Điều này thuận tiện nhưng có thể dẫn đến phân mảnh bộ nhớ nếu không được quản lý cẩn thận.

  • Cấp phát tĩnh (Static Allocation): Sử dụng xTaskCreateStatic(), lập trình viên cung cấp bộ nhớ cho TCB và ngăn xếp. Phương pháp này giúp kiểm soát tốt hơn việc sử dụng bộ nhớ và tránh phân mảnh bộ nhớ.

3.2. Khởi tạo TCB

Sau khi cấp phát bộ nhớ, FreeRTOS sẽ khởi tạo TCB với các thông tin như:

  • Tên tác vụ: Giúp dễ dàng nhận diện tác vụ trong quá trình debug.

  • Độ ưu tiên: Xác định thứ tự ưu tiên khi lập lịch tác vụ.

  • Con trỏ ngăn xếp: Trỏ đến vùng bộ nhớ ngăn xếp đã cấp phát.

3.3. Thêm vào danh sách lập lịch

Cuối cùng, TCB của tác vụ mới sẽ được thêm vào danh sách các tác vụ sẵn sàng (ready list) của FreeRTOS, chờ được CPU chọn để thực thi.


4. Hậu quả nếu như không quản lý bộ nhớ một cách hợp lý

RTOS (như FreeRTOS) rất mạnh mẽ trong việc giúp bạn tổ chức các luồng xử lý đồng thời, nhưng bộ nhớ là điểm nghẽn lớn nhất. Nếu không quản lý bộ nhớ cẩn thận, đặc biệt là Stack và Heap, bạn sẽ phải đối mặt với nhiều lỗi "ám ảnh" và khó truy vết.

Dưới đây là những hậu quả phổ biến nếu không quản lý bộ nhớ đúng cách trong hệ thống RTOS:

  • Stack overflow

  • Heap exhaustion

  • Memory fragmentatio

5. Các chiến lược quản lý bộ nhớ hiệu quả

Để có thể quản lý bộ nhớ hiệu quả khi sử dụng RTOS và thao tác với các task có rất nhiều kỹ thuật và phương pháp khác nhau. Dưới đây là một vài điểm lưu ý cơ bản giúp các bạn quản lý bộ nhớ hiệu quả hơn:

  • Ước lượng kích thước ngăn xếp: Dựa trên độ phức tạp của tác vụ, ước lượng kích thước ngăn xếp phù hợp để tránh lãng phí bộ nhớ hoặc tràn ngăn xếp.

  • Sử dụng cấp phát tĩnh: Khi có thể, ưu tiên sử dụng xTaskCreateStatic() để kiểm soát tốt hơn việc sử dụng bộ nhớ và tránh phân mảnh.

  • Theo dõi mức sử dụng ngăn xếp: Thường xuyên kiểm tra mức sử dụng ngăn xếp của các tác vụ để điều chỉnh kích thước ngăn xếp phù hợp.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giải mã AUTOSAR: Kiến trúc tiêu chuẩn ngành Automotive

Giới thiệu. AUTOSAR có vẻ khá xa lạ đối với người làm về công nghệ thông tin, nhưng đối với những bạn làm về Embedded System, đặc biệt là trong lĩnh vực Automotive, thì cũng.

0 0 28

- vừa được xem lúc

Từ khoá volatile trong lập trình C

Trong lập trình nhúng chắc hẵn bạn đã từng gặp phải tình huống khi chương trình C của bạn cho ra kết quả không đúng, mặc dù mã code có vẻ đúng? Một nguyên nhân có thể gây ra vấn đề này là việc tối ưu

0 0 28

- vừa được xem lúc

Giải mã AUTOSAR: Kiến trúc để đời ngành Automotive - Phần 2

Updating. Trong bài viết này, ta cùng tìm hiểu kỹ hơn về lớp trên cùng của kiến trúc, chính là lớp Application Layer (ASW).

0 0 23

- vừa được xem lúc

Con Trỏ Hàm - C/C++

Hàm Con Trỏ (Function Pointer) là gì. . Con trỏ tới hàm (A pointer to a function): Đây là biến lưu trữ địa chỉ hàm mà sau này có thể gọi bằng con trỏ này. .

0 0 20

- vừa được xem lúc

Cơ bản về WatchDog Timer trong hệ thống nhúng (Embedded System)

Khái Niệm. Trong hệ thống nhúng, a WatchDog Timer (Bộ đếm thời gian giám sát) là một thành phần hoặc tính năng của phần cứng được thiết kế để giám sát hoạt động của hệ thống và thực hiện hành động khắ

0 0 18

- vừa được xem lúc

Tối ưu hóa thời gian khởi động Nvidia Xavier

1. Vai trò.

0 0 16