Wykorzystanie malloc w PIC

10

Jak mogę używać malloc()i free()funkcjonować w PIC? Sprawdziłem stdlib.hnagłówek i nie ma o nich wzmianki. Używam MCC18.

Czy ktoś musiał z nich skorzystać?

Potrzebuję ich, ponieważ przenoszę bibliotekę z systemu Windows XP do PIC. Przewodnik po przenoszeniu mówi

dostosować specyficzne funkcje systemu operacyjnego do moich PIC

Ale nie wiem, jak „przetłumaczyć” funkcje malloc()i free().

stef
źródło
4
Spróbuj zastosować alokację statyczną, jeśli to możliwe.
Nick T
1
Dlaczego? Problem polega na tym, że piszę dolną warstwę (specyficzną dla platformy) dość dużej biblioteki i wielu funkcji, nie mam pojęcia, do czego służą, i nie mam pojęcia, jak zmienić z dynamiczny na statyczny ..
stef
11
Wygląda na to, że mikrokontroler PIC z <4KB RAM może być nieodpowiedni dla twojej aplikacji. Na komputerze PC zmierz zużycie pamięci biblioteki PC przed uruchomieniem portu. Lepiej może być coś bardziej dynamicznego, jak ARM Cortex-M3. Ogólna zasada: jeśli przenoszona baza kodu jest zbyt duża, aby ją zrozumieć, to nie zmieści się w PIC.
Toby Jaffey,
Sterowniki systemu Windows (i ogólnie aplikacje) są zasadniczo pisane zgodnie z paradygmatem „nieograniczonej pamięci RAM”, ponieważ jeśli fizyczna pamięć RAM zostanie wyczerpana, pamięć wirtualna może zostać zamieniona. W zależności od tego, co robi biblioteka, może zużywać więcej niż 4kB dostępne w PIC18F87J11. Podejrzewam, że nie będziesz w stanie ocenić, ile pamięci zajmie sterownik.
Adam Lawrence,
Kolejny potencjalny problem: Win32 int ma 32 bity, podczas gdy w kompilatorze MCC18 to tylko 16 bitów. Jeśli nie będziesz ostrożny, możesz mieć dziwne problemy z przepełnieniem.
Adam Lawrence,

Odpowiedzi:

8

W wielu aplikacjach trzeba będzie przydzielić pamięć, ale nie trzeba nic zwalniać, zachowując coś, co zostało po tym przydzielone. W takim systemie wystarczy użyć konsolidatora, aby zdefiniować tablicę przy użyciu całej dostępnej pamięci RAM, ustawić wskaźnik na początku tej tablicy, a następnie użyć ładnej, łatwej funkcji malloc:

char * next_alloc;
void * malloc (int size)
{
    char * this_alloc;
    this_alloc = next_alloc;
    if ((END_OF_ALLOC_SPACE - this_alloc) <rozmiar)
      return -1;
    next_alloc + = rozmiar;
    zwróć this_alloc;
}
void free (void * ptr)
{
    if (ptr)
        next_alloc = (char *) ptr;
}

Przyjemny i łatwy, a całkowity narzut wynoszący zaledwie dwa bajty dla dowolnej liczby przydziałów. Wywołanie free () na bloku spowoduje cofnięcie przydziału tego bloku i wszystkiego po nim.

Nieco bardziej skomplikowane wzorce alokacji można obsłużyć za pomocą dwóch wskaźników - jednego, który przydziela rzeczy z dołu pamięci przesuwając się w górę, a drugi z góry pamięci w dół. Możliwe jest również użycie kompaktującego śmietnika, jeśli dane w stercie są jednorodne i wiadomo, gdzie znajdują się wszystkie zewnętrzne odniesienia do niego.

supercat
źródło
Aby uzyskać pełne informacje o możliwościach, przeczytaj odpowiedź na elektronice.stackexchange.com/questions/7850/…
gavioto20
14

malloc()w mikrokontrolerach jest ogólnie uważany za „złą rzecz”. Ale jeśli jest to absolutnie potrzebne, będziesz chciał znaleźć wersję innej firmy.

Jeśli masz szczęście, przenoszony kod może nie polegać na ponownym wykorzystaniu bloków pamięci. W takim przypadku możesz napisać prosty alokator, który zwraca wskaźnik do bufora pamięci RAM, a następnie przesuwa wskaźnik o żądany rozmiar bloku.

Z powodzeniem stosowałem to podejście już wcześniej przy przenoszeniu bibliotek PC na mikrokontrolery.

Poniżej skonfigurujesz alokator za pomocą my_malloc_init()i przydzielisz pamięć za pomocą my_malloc(). my_free()ma na celu zaspokojenie zależności, ale tak naprawdę nic nie zrobi. Ostatecznie oczywiście zabraknie Ci miejsca.

Aby to zadziałało, musisz zmierzyć najgorsze wymagania dotyczące pamięci w kodzie (zrób to na komputerze, jeśli to możliwe), a następnie odpowiednio skonfiguruj HEAP_SIZE. Przed wejściem do części biblioteki wymagającej pamięci dynamicznej zadzwoń my_malloc_init(). Przed ponownym użyciem upewnij się, że nic nie wskazuje 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
}

(uwaga: w prawdziwym świecie może być konieczne rozważenie wyrównania wskaźnika, tj. zaokrąglenia w górę heap_ptro 2 lub 4 bajty)

Inną opcją jest użycie prostszej struktury alokacji, niż malloc()zwykle, na przykład FreeList , chociaż może to nie pozwolić na alokację bloków o zmiennej wielkości.

Toby Jaffey
źródło
3
Zastanawiam się, kiedy Malloc JEST uważany za coś dobrego w osadzaniu.
Kellenjb,
1
Nadal zgadzam się, że nie chcesz dynamicznej alokacji w programach, jak powiedzieli inni, ale jest to świetny sposób, aby to zrobić. Malloc innej firmy zaprojektowany do wbudowania jest zdecydowanie najlepszym wyborem. Unikanie segmentacji jest koniecznością. @jobyTaffey Dobrze napisane.
Kortuk
1
@Kellenjb cóż, to zupełnie nowe pytanie :-)
Toby Jaffey
1
Sugerowałbym, aby my_free ustawił heap_ptr na przekazaną wartość, skutecznie uwalniając wskazany element i wszystko przydzielone po nim. Oczywiście należy przydzielić rzeczy w sekwencji, która pozwala na takie użycie, ale takie wzorce nie są rzadkie. Inną przydatną odmianą jest posiadanie dwóch par funkcji przydziału / zwolnienia, z których jedna przydziela z góry na dół, a druga z dołu do góry.
supercat
13

To nie jest odpowiedź na twoje pytanie, ale dynamiczne przydzielanie pamięci jest generalnie niezadowolone z powodu małych środowisk RAM i przy braku systemu operacyjnego (np. W świecie mikrokontrolerów) ... przestrzeń sterty dostępna w środowisku osadzonym jest zazwyczaj mierzone w setkach bajtów ...

Wdrożenie malloc i free jest zasadniczo utrzymaniem połączonej listy struktur „wolnych segmentów”, a jak można sobie wyobrazić, metadane związane z wolnymi segmentami nie są nieistotne w porównaniu z ilością zwykle dostępnej pamięci… czyli „narzutem” „zarządzania dynamiczną pulą pamięci zużywa znaczną ilość dostępnych zasobów.

vicatcu
źródło
Istnieją implementacje, w których narzut metadanych jest bardzo mały. Do przydzielonego bloku potrzebujesz tylko jego rozmiaru. W przypadku nieużywanych bloków wskaźnik (i) listy połączonej zwykle można dopasować za darmo, nawet przy dość rozsądnych minimalnych rozmiarach bloków.
Przywróć Monikę
Problem z małymi, długo działającymi systemami używającymi mikrokontrolerów zwykle nie dotyczy metadanych, ale fragmentacji pamięci. Co gorsza, niewielkie zmiany w kodzie mogą wprowadzić fragmentację pamięci tam, gdzie wcześniej jej nie było, abyś mógł dokonać niewinnie wyglądającej zmiany, która nagle sprawi, że Twój program przestanie działać „zbyt wcześnie”.
Przywróć Monikę
11

Nie wiem, czy biblioteka standardowa C18 obsługuje malloci free, ale Uwaga aplikacji Microchip AN914 pokazuje, jak możesz zaimplementować własną.

W każdym razie Thomas i inni plakaty sugerują, że korzystanie z dynamicznej pamięci na PIC z bardzo małą pamięcią RAM jest obarczone niebezpieczeństwem. Możesz szybko zabraknąć ciągłego miejsca ze względu na brak bardziej zaawansowanych menedżerów pamięci wirtualnej , które dają pełne systemy operacyjne, co prowadzi do nieudanych alokacji i awarii. Co gorsza, może nie być deterministyczny i prawdopodobnie będzie utrudnieniem przy debugowaniu.

Jeśli to, co robisz, jest naprawdę dynamicznie określane w czasie wykonywania (rzadkie w przypadku większości osadzonych rzeczy) i musisz przydzielić przestrzeń tylko w kilku wyjątkowych sytuacjach, to mogę to zobaczyć malloci freebyć do zaakceptowania.

Nick T.
źródło
Skończy się ciągła przestrzeń, czyli fragmentacja sterty, to problem całkowicie niezależny od tego, jak duża jest twoja przestrzeń adresowa i czy masz pamięć wirtualną. Możesz chcieć zamienić trochę zmarnowanej pamięci RAM na mniejsze rozdrobnienie sterty, ale ostatecznie w długim systemie nie masz gwarancji, że nie zabraknie miejsca na sterty. Jedyną różnicą między małymi i dużymi systemami tutaj jest to, ile czasu zajmuje dyskowi rozpoczęcie thrashowania (w systemach z maszyną wirtualną w trybie stronicowania) lub alokator, aby zwrócić NULL (na osadzonych rzeczach).
Przywróć Monikę
@ KubaOber: Zasadniczo możliwe jest zagwarantowanie, że pamięć RAM o określonym rozmiarze będzie w stanie obsłużyć dowolną sekwencję operacji przydzielania i zwalniania, które nigdy nie wymagają więcej niż pewnej (mniejszej) ilości pamięci RAM do przydzielenia jednocześnie. Problem z systemami osadzonymi polega na tym, że zagwarantowanie sukcesu nawet w najgorszym przypadku będzie wymagało znacznie więcej pamięci RAM niż byłoby to konieczne bez fragmentacji.
supercat
@supercat Masz rację. Byłem nadgorliwie dramatyczny. Istnieją formalne dowody tych gwarancji.
Przywróć Monikę
2

Jak duży jest twój PIC pod względem pamięci?

malloc jest bardzo nieefektywnym sposobem alokacji pamięci. Problem polega na tym, że pamięć może ulec fragmentacji przy częstych zwolnieniach i mallocach, a przy zaledwie kilku kilobajtach pamięci błędy alokacji są zbyt częste. Jest całkiem prawdopodobne, że jeśli używasz mniejszego układu lub wcześniejszego PIC18, nie ma wsparcia dla malloc, ponieważ Microchip albo postrzegał to jako bardzo trudne do wdrożenia (a może nawet niemożliwe w niektórych przypadkach) lub niewystarczająco użyte, aby mogło być Warto było. Nie wspominając o tym, ale jest również dość powolny, patrzysz na 1 cykl, aby użyć już dostępnego bufora statycznego i od 100 do 1000 cykli, aby wykonać malloc.

Jeśli chcesz przydzielić statycznie, twórz takie rzeczy, jak bufor dla funkcji sprintf (jeśli istnieje, około 128 bajtów), bufor dla karty SD (jeśli istnieje) i tak dalej, dopóki nie usuniesz potrzeby Malloc. Idealnie, używasz go tylko tam, gdzie go absolutnie potrzebujesz i nie możesz uciec od statycznego przydzielania, ale te sytuacje są zwykle rzadkie i może być znakiem, że powinieneś patrzeć na większe / mocniejsze mikrokontrolery.

A jeśli opracowujesz / przenosisz „system operacyjny” na PIC18 i jeśli obsługuje on mikrokontrolery, prawdopodobnie obsługuje on alokację statyczną. Na przykład SQLite3 obsługuje przydział statyczny - przydzielasz mu dużą tablicę buforów i używa go tam, gdzie to możliwe, nawet jeśli nie jest to dla mikrokontrolerów. Jeśli nie, to czy na pewno jest przeznaczony dla małego PIC18?

Thomas O
źródło
Rozumiem, co masz na myśli .. Używam PIC18F87J11, który ma 128K pamięci RAM, czy może to wystarczyć?
stef
Stefano, ten układ ma 3990 bajtów pamięci RAM. Ma 128K pamięci flash programu.
W5VO,
@Stefao Salati - 3,8 KB jest niewielki.
Thomas O
Racja przepraszam .. czy myślisz, że i tak może wystarczyć?
stef
@Stefano Salati, nie trzeba przepraszać. Myślę, że naprawdę byś to popychał. Może to działać, ale wymagałoby to dużej wydajności i wolnej pamięci.
Thomas O
2

Jeśli rozważają malloc()i free()dla wbudowanego oprogramowania Proponuję spojrzeć na uC / OS-II i OSMemGet()i OSMemPut(). O ile malloc()pozwalają przeznaczyć dowolny blok pamięci, OSMem*()daje stałej wielkości bloku z wstępnie przydzielone basenie. Uważam to podejście za właściwą równowagę między elastycznością malloc()i odpornością statycznej alokacji.

trondd
źródło
0

AFAIK, aby to zrobić poprawnie, naprawdę musisz patrzeć na urządzenie z jakimś rodzajem jednostki zarządzania pamięcią (MMU). Chociaż istnieją mechanizmy dynamicznej alokacji dla serii PIC18, to tak naprawdę nie będą tak solidne - a mówiąc o kimś, kto pracował nad oprogramowaniem układowym, które przesuwa granice serii PIC18, mogę powiedzieć, że nie dostaniesz spora aplikacja, jeśli wydasz cały narzut na menedżera pamięci.

Lepsze rozwiązanie: spróbuj zrozumieć, co robi i dlaczego potrzebuje dynamicznej alokacji. Sprawdź, czy nie można go ponownie uwzględnić, aby działał z alokacją statyczną. (Może się zdarzyć, że po prostu nie jest to możliwe - jeśli biblioteka / aplikacja jest zaprojektowana do robienia czegoś, co można dowolnie skalować, lub nie ma ograniczenia ilości danych wejściowych, które może zaakceptować.) Ale czasami, jeśli naprawdę myślisz jeśli chodzi o to, co próbujesz zrobić, może okazać się, że jest możliwe (a może nawet całkiem łatwe) użycie zamiast tego przypisania statycznego.

andersop
źródło
1
Jesteś w błędzie MMU pozwala na połączenie z pamięcią zewnętrzną (prawdopodobnie więcej niż 4kB na PIC). Różnica w alokacji dynamicznej i statycznej jest bardzo niewielka z MMU i bez. Kiedy zaczniesz wchodzić w pamięć wirtualną, jest różnica, ale jest to tylko stycznie związane z malloc.
Kevin Vermeer,
1
Pierwsi programiści Macintosha dość często używali malloc () i free () (lub ich odpowiedników Pascal), mimo że wczesne komputery Macintosh nie miały MMU. Pomysł, że „prawidłowe” użycie malloc () wymaga MMU, wydaje mi się błędny.
Davidcary