Sử dụng malloc trong PIC


10

Làm thế nào tôi có thể sử dụng malloc()và các free()chức năng trong PIC? Tôi đã kiểm tra stdlib.htiêu đề và không có đề cập đến chúng. Tôi đang sử dụng MCC18.

Có ai phải sử dụng chúng?

Tôi cần chúng vì tôi đang chuyển một thư viện từ Windows XP sang PIC. Hướng dẫn porting nói với

điều chỉnh các chức năng cụ thể của Hệ điều hành cho các PIC của tôi

Nhưng tôi không biết làm thế nào để "dịch" các malloc()free()các chức năng.


4
Cố gắng sử dụng phân bổ tĩnh nếu có thể.
Nick T

1
Tại sao? Vấn đề thực sự là tôi đang viết lớp dưới cùng (một nền tảng cụ thể) của một thư viện khá lớn và rất nhiều chức năng Tôi không biết họ đang sử dụng cái gì .. và tôi không biết làm thế nào để thay đổi động đến tĩnh ..
stef

11
Nghe có vẻ như một vi điều khiển PIC có RAM <4KB có thể sai cho ứng dụng của bạn. Trên PC, đo mức sử dụng bộ nhớ của thư viện PC trước khi bắt đầu một cổng. Bạn có thể tốt hơn với thứ gì đó mạnh mẽ hơn như ARM Cortex-M3. Nguyên tắc nhỏ: nếu codebase bạn chuyển quá lớn để hiểu thì nó sẽ không phù hợp với PIC.
Toby Jaffey

Các trình điều khiển Windows (và các ứng dụng nói chung) về cơ bản được viết với mô hình 'RAM không giới hạn', vì nếu RAM vật lý cạn kiệt, bộ nhớ ảo có thể được hoán đổi. Tùy thuộc vào thư viện đang làm gì, nó có thể tiêu thụ rất nhiều hơn 4kB có sẵn trong PIC18F87J11 của bạn. Tôi nghi ngờ rằng bạn sẽ không thể đánh giá được bộ nhớ mà tài xế sẽ sử dụng.
Adam Lawrence

Một vấn đề tiềm năng khác: một Win32 int là 32 bit, trong khi với trình biên dịch MCC18, nó chỉ có 16 bit. Bạn có thể gặp sự cố tràn kỳ lạ nếu không cẩn thận.
Adam Lawrence

Câu trả lời:


8

Trong nhiều ứng dụng, người ta sẽ cần phân bổ bộ nhớ, nhưng sẽ không cần giải phóng bất cứ thứ gì trong khi vẫn giữ thứ gì đó được phân bổ sau nó. Trên một hệ thống như vậy, tất cả những gì bạn cần làm là sử dụng trình liên kết để xác định một mảng bằng cách sử dụng tất cả RAM có sẵn, đặt một con trỏ đến đầu của mảng đó, sau đó sử dụng hàm malloc dễ dàng:

char * next_alloc;
void * malloc (kích thước int)
{
    char * this_alloc;
    this_alloc = next_alloc;
    if ((END_OF_ALLOCClickACE - this_alloc) <size)
      trả về -1;
    next_alloc + = kích thước;
    trả lại this_alloc;
}
khoảng trống miễn phí (void * ptr)
{
    nếu (ptr)
        next_alloc = (char *) ptr;
}

Đẹp và dễ dàng, và chỉ cần hai byte tổng phí cho bất kỳ số lượng phân bổ. Gọi free () trên một khối sẽ giải quyết khối đó và mọi thứ sau nó.

Các mẫu phân bổ hơi phức tạp hơn có thể được xử lý bằng cách sử dụng hai con trỏ - một mẫu phân bổ các công cụ từ dưới cùng của bộ nhớ di chuyển lên và một trong số đó đi từ đỉnh bộ nhớ xuống. Cũng có thể sử dụng một trình thu gom rác nén nếu dữ liệu trong heap là đồng nhất và người ta biết tất cả các tham chiếu bên ngoài đến nó.


Để được thông báo đầy đủ về tính tích cực, hãy đọc câu trả lời tại Electronics.stackexchange.com/questions/7850/
mẹo

14

malloc()trong vi điều khiển thường được coi là một "điều xấu". Nhưng, nếu bạn thực sự cần nó thì bạn sẽ muốn tìm phiên bản của bên thứ ba.

Nếu bạn may mắn, mã bạn đang chuyển có thể không dựa vào việc sử dụng lại các khối bộ nhớ. Nếu đây là trường hợp, bạn có thể viết một bộ cấp phát đơn giản trả về một con trỏ vào bộ đệm RAM, sau đó tiến bộ con trỏ theo kích thước khối được yêu cầu.

Tôi đã sử dụng thành công phương pháp này trước khi chuyển thư viện PC sang vi điều khiển.

Dưới đây, bạn sẽ thiết lập bộ cấp phát my_malloc_init()và phân bổ bộ nhớ với my_malloc(). my_free()ở đó để thỏa mãn sự phụ thuộc nhưng thực sự sẽ không làm gì cả. Cuối cùng, bạn sẽ hết dung lượng.

Để thực hiện công việc này, bạn sẽ cần đo yêu cầu bộ nhớ trong trường hợp xấu nhất của mã của bạn (thực hiện điều này trên PC nếu có thể) sau đó thiết lập HEAP_SIZEtương ứng. Trước khi vào một phần của thư viện yêu cầu bộ nhớ động, hãy gọi my_malloc_init(). Trước khi sử dụng lại, đảm bảo không có gì vẫn còn điểm heap.

uint8_t heap[HEAP_SIZE];
uint8_t *heap_ptr;

void my_malloc_init(void)
{
    heap_ptr = heap;
}

void *my_malloc(size_t len)
{
    uint8_t *p = heap_ptr;
    heap_ptr += len;
    if (heap_ptr >= heap + HEAP_SIZE)
        return NULL;
    else
        return p;
}

void my_free(void)
{
    // do nothing
}

(lưu ý: trong thế giới thực, bạn có thể cần xem xét căn chỉnh con trỏ, nghĩa là làm tròn lên heap_ptr2 hoặc 4 byte)

Một tùy chọn khác là sử dụng cấu trúc phân bổ đơn giản hơn malloc()thường cung cấp, như FreeList , mặc dù điều này có thể không cho phép bạn phân bổ các khối có kích thước thay đổi.


3
Tôi tự hỏi khi malloc IS được coi là một điều tốt trong nhúng.
Kellenjb

1
Tôi vẫn đồng ý rằng bạn không muốn phân bổ động trong các chương trình, như những người khác đã nói, nhưng đây là một cách tuyệt vời để thực hiện. Malloc bên thứ ba được thiết kế để nhúng cho đến nay là sự lựa chọn tốt nhất. Tránh phân khúc là phải. @jobyTaffey Được viết tốt.
Kortuk

1
@Kellenjb tốt, đó là một câu hỏi hoàn toàn mới :-)
Toby Jaffey

1
Tôi sẽ đề nghị my_free nên đặt heap_ptr thành giá trị được truyền vào, giải phóng hiệu quả mục được chỉ định và mọi thứ được phân bổ sau nó. Rõ ràng người ta phải phân bổ mọi thứ theo trình tự cho phép sử dụng như vậy, nhưng những kiểu như vậy không phải là hiếm. Một biến thể hữu ích khác là có hai cặp hàm cấp phát / miễn phí, một trong số đó phân bổ từ trên xuống và một trong số đó phân bổ từ dưới lên.
supercat

13

Đây không phải là một câu trả lời cho câu hỏi của bạn, nhưng việc phân bổ bộ nhớ động thường bị cau mày trong môi trường RAM nhỏ và không có hệ điều hành (ví dụ như trong thế giới vi điều khiển) ... không gian heap bạn có sẵn trong môi trường nhúng thường là được đo bằng hàng trăm byte ...

Việc triển khai malloc và miễn phí về cơ bản là duy trì một danh sách các cấu trúc "phân đoạn miễn phí" được liên kết và như bạn có thể tưởng tượng, siêu dữ liệu được liên kết với các phân đoạn miễn phí không phải là không đáng kể khi so sánh với lượng bộ nhớ thường có sẵn ... đó là "chi phí chung" "Việc quản lý nhóm bộ nhớ động sẽ tiêu tốn một lượng đáng kể tài nguyên có sẵn.


Có những triển khai trong đó chi phí siêu dữ liệu rất nhỏ. Đối với một khối được phân bổ, bạn chỉ cần kích thước của nó. Đối với các khối không được sử dụng, (các) con trỏ danh sách được liên kết thường có thể phù hợp miễn phí, ngay cả với kích thước khối tối thiểu khá hợp lý.
Phục hồi lại

Vấn đề với các hệ thống nhỏ, chạy dài sử dụng vi điều khiển thường không phải là về siêu dữ liệu, mà là về sự phân mảnh bộ nhớ. Tệ hơn nữa, những thay đổi nhỏ đối với mã của bạn có thể giới thiệu sự phân mảnh bộ nhớ ở những nơi trước đây không có, do đó bạn có thể thực hiện một thay đổi trông ngây thơ khiến chương trình của bạn ngừng hoạt động "quá sớm".
Phục hồi lại

11

Tôi không biết thư viện chuẩn C18 có hỗ trợ mallochay không free, nhưng Ứng dụng Microchip Note AN914 cho thấy cách bạn có thể tự triển khai.

Trong mọi trường hợp, Thomas và các áp phích khác đã đề xuất, sử dụng bộ nhớ động trên PIC với không gian RAM rất nhỏ của chúng đầy rủi ro. Bạn có thể nhanh chóng hết dung lượng tiếp giáp do thiếu trình quản lý bộ nhớ ảo tiên tiến hơn mà hệ điều hành đầy đủ mang lại cho bạn, dẫn đến việc phân bổ và gặp sự cố không thành công. Tồi tệ hơn, nó có thể không mang tính quyết định và có thể sẽ là một nỗi đau để gỡ lỗi.

Nếu những gì bạn đang làm thực sự được xác định một cách linh hoạt vào thời gian chạy (hiếm khi xảy ra đối với hầu hết mọi thứ được nhúng) và bạn chỉ cần phân bổ không gian trong một vài dịp rất đặc biệt , tôi có thể thấy mallocfreeđược chấp nhận.


Hết không gian tiếp giáp, hay còn gọi là phân mảnh heap, là một vấn đề hoàn toàn độc lập với không gian địa chỉ của bạn lớn như thế nào và bạn có bộ nhớ ảo hay không. Bạn có thể muốn đánh đổi một số RAM bị lãng phí để phân mảnh heap thấp hơn, nhưng cuối cùng, trên một hệ thống chạy dài, bạn không có gì đảm bảo về việc không hết dung lượng heap. Sự khác biệt duy nhất giữa các hệ thống nhỏ và lớn ở đây là một trong những khoảng thời gian để đĩa bắt đầu đập (trên các hệ thống có VM phân trang đĩa) hoặc bộ cấp phát để trả về NULL (trên các công cụ nhúng).
Phục hồi lại

@KubaOber: Thông thường có thể đảm bảo rằng RAM có kích thước nhất định sẽ có thể xử lý bất kỳ chuỗi phân bổ và giải phóng nào không bao giờ cần nhiều hơn một lượng RAM nhất định (nhỏ hơn) được phân bổ đồng thời. Vấn đề với các hệ thống nhúng là sự thành công đảm bảo ngay cả trong trường hợp xấu nhất sẽ cần nhiều RAM hơn mức cần thiết mà không bị phân mảnh.
supercat

@supercat Bạn nói đúng. Tôi thực sự quá kịch tính. Có bằng chứng chính thức về những đảm bảo.
Phục hồi Monica

2

Chà, PIC của bạn lớn đến mức nào về bộ nhớ?

malloc là một cách phân bổ bộ nhớ rất kém hiệu quả. Vấn đề với nó là bộ nhớ có thể bị phân mảnh với các giải phóng và mallocs thường xuyên và chỉ với một vài kilobyte bộ nhớ, các lỗi phân bổ là quá phổ biến. Rất có khả năng là nếu bạn đang sử dụng một con chip nhỏ hơn hoặc PIC18 trước đó thì không có hỗ trợ cho malloc, vì Microchip đã xem nó rất khó thực hiện (hoặc thậm chí là không thể trong một số trường hợp) hoặc không được sử dụng đủ cho nó đáng giá Không đề cập đến nó, nhưng nó cũng khá chậm, bạn đang xem 1 chu kỳ để sử dụng bộ đệm tĩnh đã có sẵn và 100 đến 1.000 chu kỳ để thực hiện một malloc.

Nếu bạn muốn phân bổ tĩnh, hãy tạo những thứ như bộ đệm cho các hàm sprintf của bạn (nếu có, khoảng 128 byte), bộ đệm cho thẻ SD của bạn (nếu có), v.v., cho đến khi bạn loại bỏ nhu cầu về malloc. Lý tưởng nhất là bạn chỉ sử dụng nó khi bạn thực sự cần nó và không thể thoát khỏi phân bổ tĩnh, nhưng những tình huống này thường rất hiếm và có thể là dấu hiệu bạn nên xem xét các bộ vi điều khiển lớn hơn / mạnh hơn.

Và nếu bạn đang phát triển / chuyển một "hệ điều hành" trên PIC18 và nếu nó hỗ trợ các bộ vi điều khiển thì có lẽ nó có hỗ trợ cho phân bổ tĩnh. Ví dụ, SQLite3 hỗ trợ phân bổ tĩnh - bạn phân bổ cho nó một mảng bộ đệm lớn và nó sử dụng điều đó khi có thể, mặc dù nó không dành cho vi điều khiển. Nếu không, thì bạn có chắc là nó được thiết kế cho PIC18 nhỏ không?


Tôi hiểu ý của bạn .. Tôi đang sử dụng PIC18F87J11, có 128K ram, liệu có đủ không?
stef

Stefano, con chip đó có 3.904 byte RAM. Nó có 128K bộ nhớ flash chương trình.
W5VO

@Stefao Salati - 3,8KB thì nhỏ xíu.
Thomas O

Phải xin lỗi .. bạn có nghĩ rằng nó có thể là đủ anyway?
stef

@Stefano Salati, không cần phải xin lỗi. Tôi nghĩ rằng bạn sẽ đẩy nó thực sự. Nó có thể hoạt động, nhưng nó sẽ làm giảm hiệu suất và bộ nhớ trống của bạn.
Thomas O

2

Nếu bạn đang xem xét malloc()free()cho phần mềm nhúng của mình, tôi khuyên bạn nên xem uC / OS-II OSMemGet()OSMemPut(). Trong khi malloc()cho phép bạn phân bổ một khối bộ nhớ tùy ý, OSMem*()hãy cung cấp cho bạn một khối có kích thước cố định từ một nhóm được phân bổ trước. Tôi thấy cách tiếp cận này là một sự cân bằng tốt giữa tính linh hoạt malloc()và sự mạnh mẽ của phân bổ tĩnh.


0

AFAIK, để làm điều này một cách chính xác, bạn thực sự cần phải nhìn vào một thiết bị với một loại đơn vị quản lý bộ nhớ (MMU). Mặc dù các cơ chế phân bổ động cho sê-ri PIC18 vẫn tồn tại, nhưng chúng sẽ không thực sự vững chắc - và nói như một người làm việc trên phần mềm đẩy các giới hạn của sê-ri PIC18, tôi có thể nói rằng bạn sẽ không nhận được một ứng dụng khá lớn trong đó nếu bạn dành toàn bộ chi phí cho trình quản lý bộ nhớ.

Giải pháp tốt hơn: cố gắng hiểu những gì nó đang làm và tại sao nó cần phân bổ động. Xem nếu bạn không thể tái xác định nó hoạt động với phân bổ tĩnh. (Có thể là trường hợp đơn giản là không thể - nếu thư viện / ứng dụng được thiết kế để làm một cái gì đó có tỷ lệ tự do hoặc không có giới hạn về số lượng đầu vào mà nó có thể chấp nhận.) Nhưng đôi khi, nếu bạn thực sự nghĩ về những gì bạn đang cố gắng thực hiện, bạn có thể thấy có thể (và thậm chí có thể khá dễ dàng) để sử dụng phân bổ tĩnh thay thế.


1
Bạn không chính xác. MMU cho phép bạn giao tiếp với bộ nhớ ngoài (có thể nhiều hơn 4kB trên PIC). Có rất ít sự khác biệt trong phân bổ động và tĩnh có và không có MMU. Khi bạn bắt đầu đi vào bộ nhớ ảo, sẽ có một sự khác biệt, nhưng điều đó chỉ liên quan một cách hữu hình với malloc.
Kevin Vermeer

1
Các lập trình viên Macintosh ban đầu sử dụng malloc () và free () (hoặc tương đương Pascal của họ) khá thường xuyên, mặc dù thực tế là các máy tính Macintosh đời đầu không có MMU. Ý tưởng rằng "chính xác" sử dụng malloc () yêu cầu MMU dường như không chính xác với tôi.
davidcary
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.