Uso de malloc no PIC

10

Como posso usar malloc()e free()funções em um PIC? Eu verifiquei o stdlib.hcabeçalho e não há menção a eles. Estou usando o MCC18.

Alguém já teve que usá-los?

Preciso deles porque estou portando uma biblioteca do Windows XP para o PIC. O guia de portabilidade diz para

adaptar as funções específicas do sistema operacional às funções do meu PIC

Mas não sei como "traduzir" as funções malloc()e free().

stef
fonte
4
Tente usar alocação estática, se possível.
Nick T
11
Por quê? O problema é que estou escrevendo a camada inferior (a específica da plataforma) de uma biblioteca enorme e com muitas funções que não faço ideia do que elas estão usando. E não faço ideia de como mudar de dinâmico para estático ..
stef 14/12/10
11
Parece que um microcontrolador PIC com <4KB RAM pode estar errado para o seu aplicativo. Em um PC, meça o uso da memória da biblioteca do PC antes de iniciar uma porta. Você pode estar melhor com algo mais robusto como um ARM Cortex-M3. Regra prática: se a base de código que você está portando é muito grande para entender, ela não se encaixa em um PIC.
Toby Jaffey
Os drivers do Windows (e aplicativos em geral) são essencialmente escritos com um paradigma de 'RAM ilimitada', pois se a RAM física se esgotar, a memória virtual poderá ser trocada. Dependendo do que a biblioteca está fazendo, ela pode muito bem consumir mais do que 4kB disponível no seu PIC18F87J11. Eu suspeito que você não será capaz de medir quanta memória o driver irá usar.
Adam Lawrence
Outro problema em potencial: um Win32 int é de 32 bits, enquanto o compilador MCC18 é de apenas 16 bits. Você pode ter problemas de transbordamento estranhos se não tomar cuidado.
Adam Lawrence

Respostas:

8

Em muitos aplicativos, será necessário alocar memória, mas não será necessário liberar nada, mantendo algo que foi alocado após ele. Nesse sistema, tudo o que você precisa fazer é usar o vinculador para definir uma matriz usando toda a RAM disponível, definir um ponteiro para o início dessa matriz e usar uma função malloc fácil e agradável:

char * next_alloc;
void * malloc (tamanho int)
{
    char * this_alloc;
    this_alloc = next_alloc;
    if ((END_OF_ALLOC_SPACE - this_alloc) <tamanho)
      retorno -1;
    next_alloc + = tamanho;
    retornar this_alloc;
}
vazio (vazio * ptr)
{
    if (ptr)
        next_alloc = (caractere *) ptr;
}

Agradável e fácil, e sobrecarga total de apenas dois bytes para qualquer número de alocações. Chamar free () em um bloco desalocará esse bloco e tudo o que está depois dele.

Padrões de alocação ligeiramente mais complicados podem ser manipulados usando dois ponteiros - um que aloca coisas da parte inferior da memória subindo e uma das quais vai da parte superior da memória para baixo. Também é possível usar um coletor de lixo compactador se os dados no heap forem homogêneos e se souber onde estão todas as referências externas a ele.

supercat
fonte
Para ser completamente informado sobre as possibilidades, leia a resposta em electronics.stackexchange.com/questions/7850/…
gavioto20
14

malloc()em microcontroladores é geralmente considerado uma "coisa ruim". Mas, se você absolutamente precisar, encontrará uma versão de terceiros.

Se você tiver sorte, o código que você está portando pode não depender da reutilização de blocos de memória. Se for esse o caso, você pode escrever um alocador simples que retorne um ponteiro para um buffer de RAM e avance o ponteiro pelo tamanho do bloco solicitado.

Eu já usei essa abordagem com êxito antes na transferência de bibliotecas de PC para microcontroladores.

Abaixo, você configuraria o alocador my_malloc_init()e alocaria memória com my_malloc(). my_free()existe para satisfazer a dependência, mas na verdade não faz nada. Eventualmente, você ficará sem espaço, é claro.

Para fazer isso funcionar, você precisará medir o pior requisito de memória do seu código (faça isso em um PC, se possível) e configurar de HEAP_SIZEacordo. Antes de entrar na parte da sua biblioteca que requer memória dinâmica, ligue my_malloc_init(). Antes de reutilizar, verifique se ainda não há nada apontado 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
}

(nota: no mundo real, pode ser necessário considerar o alinhamento do ponteiro, ou seja, arredondar para heap_ptr2 ou 4 bytes)

Outra opção é usar uma estrutura de alocação mais simples do que malloc()normalmente é fornecida, como uma FreeList , embora isso possa não permitir a alocação de blocos de tamanho variável.

Toby Jaffey
fonte
3
Gostaria de saber quando malloc é considerado uma coisa boa no incorporado.
Kellenjb
11
Ainda concordo que você não deseja alocação dinâmica em programas, como outros já disseram, mas essa é uma ótima maneira de fazer isso. Malloc de terceiros projetados para embutidos é de longe a melhor escolha. Evitar a segmentação é uma obrigação. @jobyTaffey Bem escrito.
Kortuk
11
@Kellenjb bem, isso é uma nova questão toda :-)
Toby Jaffey
11
Sugiro que my_free defina heap_ptr como o valor passado, liberando efetivamente o item indicado e tudo o que for alocado após ele. Obviamente, é preciso alocar as coisas em uma sequência que permita esse uso, mas esses padrões não são incomuns. Outra variação útil é ter dois pares de funções de alocação / livre, um dos quais aloca de cima para baixo e o outro aloca de baixo para cima.
Supercat 24/03
13

Isso dificilmente é uma resposta para sua pergunta, mas a alocação dinâmica de memória geralmente é desaprovada em pequenos ambientes de RAM e na ausência de um sistema operacional (por exemplo, no mundo dos microcontroladores) ... o espaço de pilha disponível em um ambiente incorporado geralmente é medido em centenas de bytes ...

Implementar malloc e free é essencialmente a manutenção de uma lista vinculada de estruturas de "segmento livre" e, como você pode imaginar, os metadados associados aos segmentos livres não são substanciais quando comparados à quantidade de memória normalmente disponível ... que é a sobrecarga " "do gerenciamento de um conjunto de memórias dinâmicas consome uma quantidade significativa dos recursos disponíveis.

vicatcu
fonte
Existem implementações em que a sobrecarga de metadados é muito pequena. Para um bloco alocado, você só precisa do seu tamanho. Para blocos não utilizados, o (s) ponteiro (s) da lista vinculada geralmente pode caber gratuitamente, mesmo com tamanhos mínimos razoáveis ​​de bloco.
Restabeleça Monica
O problema com sistemas pequenos e de longa execução que usam microcontroladores geralmente não é sobre metadados, mas sobre fragmentação de memória. O que é pior: pequenas alterações em seu código podem introduzir fragmentação de memória onde não havia anteriormente, para que você possa fazer uma alteração de aparência inocente que de repente faz com que seu programa pare de funcionar "muito cedo".
Restabeleça Monica
11

Não sei se a biblioteca padrão C18 suporta malloce free, mas a Microchip App Note AN914 mostra como você pode implementar seus próprios.

De qualquer forma, Thomas e outros pôsteres sugeriram que o uso de memória dinâmica em PICs com seu espaço de RAM muito pequeno é repleto de perigos. Você pode ficar rapidamente sem espaço contíguo devido à falta de gerenciadores de memória virtual mais avançados que os sistemas operacionais completos oferecem, levando a falhas de alocações e falhas. Pior, pode não ser determinístico e provavelmente será um problema para depuração.

Se o que você está fazendo é realmente determinado dinamicamente em tempo de execução (raro para a maioria das coisas incorporadas), e você só precisa alocar espaço em algumas ocasiões muito especiais , eu poderia ver malloce freeser aceitável.

Nick T
fonte
A falta de espaço contíguo, também conhecido como fragmentação de heap, é um problema totalmente independente do tamanho do seu espaço de endereço e se você possui memória virtual. Você pode trocar uma RAM desperdiçada por uma menor fragmentação de heap, mas eventualmente, em um sistema de longa execução, você não tem garantias sobre a falta de espaço no heap. A única diferença entre sistemas pequenos e grandes aqui é a de quanto tempo leva para o disco começar a ser debulhado (em sistemas com VM paginada por disco) ou o alocador para retornar NULL (em itens incorporados).
Restabeleça Monica
@KubaOber: Geralmente, é possível garantir que um determinado tamanho de RAM seja capaz de lidar com qualquer sequência de operações de alocação e liberação que nunca precise de mais do que uma certa quantidade (menor) de RAM para ser alocada simultaneamente. O problema com os sistemas embarcados é que o sucesso garantido, mesmo nos piores casos, exigirá muito mais RAM do que seria necessário sem fragmentação.
supercat
@supercat Você está certo. Eu era excessivamente zeloso de fato. Existem provas formais dessas garantias.
Reinstale Monica
2

Bem, qual é o tamanho do seu PIC em termos de memória?

O malloc é uma maneira muito ineficiente de alocar memória. O problema é que a memória pode se fragmentar com libertações frequentes e mallocs e com apenas alguns kilobytes de memória, as falhas de alocação são muito comuns. É bem provável que, se você estiver usando um chip menor ou um PIC18 anterior, não haja suporte para malloc, pois o Microchip o considerou muito difícil de implementar (ou talvez até impossível em alguns casos) ou não foi usado o suficiente para ser utilizado. Vale a pena. Sem mencionar isso, mas também é bastante lento, você está olhando para 1 ciclo para usar um buffer estático já disponível e de 100 a 1.000 para fazer um malloc.

Se você deseja alocar estaticamente, crie coisas como um buffer para as funções do sprintf (se houver, em torno de 128 bytes), um buffer para o cartão SD (se houver) e assim por diante, até remover a necessidade de malloc. Idealmente, você o usa apenas onde é absolutamente necessário e não pode se safar da alocação estática, mas essas situações geralmente são raras e talvez um sinal de que você deva observar microcontroladores maiores / mais poderosos.

E se você estiver desenvolvendo / portando um "sistema operacional" no PIC18 e se ele suportar microcontroladores, provavelmente terá suporte para alocação estática. Por exemplo, o SQLite3 suporta alocação estática - você aloca uma grande matriz de buffer e a utiliza sempre que possível, mesmo que não seja para microcontroladores. Caso contrário, você tem certeza de que foi projetado para um pequeno PIC18?

Thomas O
fonte
Entendo o que você quer dizer .. Estou usando o PIC18F87J11, que tem 128K de RAM, pode ser suficiente?
stef
Stefano, esse chip possui 3.904 bytes de RAM. Possui 128K de memória flash de programa.
W5VO
@Stefao Salati - 3,8KB é pequeno.
Thomas O
Desculpe, você acha que pode ser o suficiente?
stef
@ Stefano Salati, não precisa se desculpar. Eu acho que você estaria pressionando realmente. Pode funcionar, mas exigiria um pouco do desempenho e da memória livre.
Thomas O
2

Se você está considerando malloc()e free()para o seu software incorporado, sugiro que você dê uma olhada no uC / OS-II OSMemGet()e em OSMemPut(). Embora malloc()você aloque um bloco de memória arbitrário, OSMem*()ofereça um bloco de tamanho fixo de um pool pré-alocado. Acho essa abordagem um bom equilíbrio entre a flexibilidade malloc()e a robustez da alocação estática.

trondd
fonte
0

AFAIK, para fazer isso corretamente, você realmente precisa estar olhando para um dispositivo com algum tipo de unidade de gerenciamento de memória (MMU). Embora existam mecanismos de alocação dinâmica para a série PIC18, eles não serão realmente sólidos - e falando como alguém que trabalhou em firmware que ultrapassa os limites da série PIC18, posso dizer que você não vai conseguir um aplicativo considerável lá se você gastar toda a sobrecarga em um gerenciador de memória.

Melhor solução: tente entender o que está fazendo e por que ele precisa de alocação dinâmica. Veja se você não pode fatorar novamente para trabalhar com alocação estática. (Pode ser que isso simplesmente não seja possível - se a biblioteca / aplicativo for projetado para fazer algo que seja escalável livremente ou que não tenha limites da quantidade de entrada que pode aceitar.) Mas, às vezes, se você realmente pensa sobre o que você está tentando fazer, você pode achar que é possível (e possivelmente até fácil) usar a alocação estática.

andersop
fonte
11
Você está incorreto. Uma MMU permite que você faça interface com a memória externa (provavelmente superior a 4kB no PIC). Há muito pouca diferença na alocação dinâmica e estática com e sem uma MMU. Depois que você começa a entrar na memória virtual, há uma diferença, mas isso é apenas tangencialmente relacionado ao malloc.
Kevin Vermeer
11
Os primeiros programadores do Macintosh usavam malloc () e free () (ou seus equivalentes em Pascal) com bastante frequência, apesar do fato de os primeiros computadores Macintosh não possuírem MMU. A ideia de que "usar corretamente" malloc () requer uma MMU parece incorreta para mim.
Davidcary