🌱 Memory Leak trong Lập Trình Nhúng: Nguyên Nhân và Ví Dụ
Khi phát triển phần mềm nhúng, lập trình viên thường gặp nhiều khó khăn, đặc biệt là những "bug kỳ lạ" - kỳ lạ một phần do chẳng biết nó đến từ đâu cả 😅 Đặc biệt với anh em làm phần mềm nhúng, thì mất cái kỳ lạ này còn thường xuyên xảy ra hơn:
- Code vừa chạy ngon xong nhưng mà chạy lại thì báo lỗi.
- Run thì báo lỗi tùm lum nhưng debug từng bước thì lại đúng.
- Treo mạch 😑
👉 Các vấn đề thường gặp khi thiết kế phần mềm nhúng
- Vấn đề về quản lý tài nguyên (chân cẳng, bộ nhớ)
- Vấn đề quản lý các ngắt - thiết kế luồng quản lý chương trình hợp lý
- Vấn đề lập lịch - quản lý task trong hệ điều hành
- Vấn đề thiết kế chương trình
👉 Memory Leak là gì?
Trong khoa học máy tính, Memory Leak - Rò rỉ bộ nhớ là một dạng rò rỉ tài nguyên xảy ra khi chương trình máy tính quản lý không chính xác việc cấp phát bộ nhớ theo cách bộ nhớ không cần dùng nữa, mà lại không được giải phóng.
Anh em lập trình C nghe có thể nghĩ ngay đến trường hợp dùng malloc() nhưng không free() đúng không? Trên thực tế thì có rất nhiều trường hợp có thể gây ra Memory Leak:
- Thiếu bộ nhớ RAM (Stack Overflow, Heap Overflow, ...)
- Đọc/Ghi tràn mảng, khi đọc ghi quá số phần tử max của mảng cũng có thể gây ra các vấn đề về bộ nhớ. Tuy nhiên, một số Compiler đã hỗ trợ báo build fail để cảnh báo cho người lập trình.
- Tràn biến, chia cho 0, ...
- Và cái lỗi thường gặp, malloc() nhưng quên free(). Một khi đã malloc() thì anh em thường rất "tham lam" dùng toàn mảng với struct (Đối với bộ nhớ nhỏ của Vi điều khiển thì chỗ này chiếm khá nhiều). Mà một khi dùng xong quên free thì không khác gì đặt thêm mấy cái quả tạ trăm cân ở giữa nhà bếp !!!
👉 Một số ví dụ về Memory Leak trong lập trình nhúng
💬 Ví dụ 1: Cấp phát bộ nhớ trong vòng lặp
#include <stdlib.h>
#include <stdio.h>
#define LOOPS 10
#define MAXSIZE 256
int main(int argc, char **argv)
{
int count = 0;
char *pointer = NULL;
for(count=0; count<LOOPS; count++)
{
pointer = (char *)malloc(sizeof(char) * MAXSIZE);
}
free(pointer);
return count;
}
Trong ví dụ này, chúng ta được cấp phát 10 block ô nhớ với kích thước là MAXSIZE = 256 để có thể làm những công việc khác nhau. Với một vòng for như trên thì pointer sẽ chỉ trỏ được vào block cuối cùng.
Vì vậy, các block được cấp phát ban đầu sẽ không có pointer trỏ vào, và không thể free chúng được ⇨ Tiêu tốn cực nhiều bộ nhớ.
Kiểu này thường gặp nhưng cũng dễ để xử lý, bằng cách đưa hàm free() vào trong vòng lặp for.
💬 Ví dụ 2: Không giải phóng bộ nhớ khi gặp lỗi
char* getBlock(int fd)
{
char* buf = (char*) malloc(BLOCK_SIZE);
if (!buf)
{
return NULL;
}
if (read(fd, buf, BLOCK_SIZE) != BLOCK_SIZE)
{
return NULL;
}
return buf;
}
Trên đây là một ví dụ đơn giản khác về Memory Leak. Hàm trên sẽ đọc một block nhớ mong muốn, nếu đọc được sẽ free vùng nhớ được phân bổ trong hàm.
Tuy nhiên, nếu hàm read() trả về sai số bytes mong muốn (BLOCK_SIZE) thì hàm sẽ trả về NULL, từ đó block nhớ đã malloc() trong hàm sẽ không được free() và gây ra Memory Leak.
Ngoài ra còn rất nhiều ví dụ khác về Memory Leak mà các bạn có thể tham khảo hoặc đã từng gặp phải 😆
👉 Kết luận và cách phòng tránh Memory Leak
Memory Leak là một vấn đề nghiêm trọng trong lập trình nhúng, có thể dẫn đến sự cố hệ thống và giảm hiệu suất. Để phòng tránh Memory Leak, các lập trình viên nhúng nên:
- Luôn giải phóng bộ nhớ đã cấp phát khi không còn sử dụng.
- Sử dụng các công cụ phân tích bộ nhớ để phát hiện Memory Leak.
- Áp dụng các best practices trong quản lý bộ nhớ.
- Thường xuyên review code và kiểm tra các trường hợp có thể gây ra Memory Leak.
>>>= Follow ngay để cập nhật thông tin mới nhất về lập trình nhúng =<<<