Pergunta

Estou programando em C para RAM limitada incorporado microcontrolador com RTOS.

Eu regularmente quebrar meu código para funções curtas, mas cada função de chamada exigem a mais memória de pilha. Cada tarefa precisa de sua pilha, e este é um dos consumidores de memória significativas no projeto.

Existe uma alternativa para manter o código bem organizado e legível, ainda preservar a memória?

Foi útil?

Solução

Tente fazer o mais plana pilha de chamadas, então ao invés de a() chamando b() que chama c() que chama d(), tem a() b() chamada, c() e d() si.

Se uma função só é referenciada uma vez, marcá-lo inline (assumindo que o seu compilador suporta isso).

Outras dicas

Existem 3 componentes para o seu uso da pilha:

  • endereços de retorno Chamada de função
  • parâmetros de função de chamada
  • variáveis ??automáticas (locais)

A chave para minimizar o uso da pilha é minimizar passagem de parâmetros e variáveis ??automáticas. O consumo de espaço do próprio chamada de função real é bastante mínima.

Parâmetros

Uma maneira de abordar a questão parâmetro é passar uma estrutura (via ponteiro) em vez de um grande número de parâmetros.


foo(int a, int b, int c, int d)
{
...
   bar(int a, int b);
}

fazer isso em vez disso:


struct my_params {
   int a;
   int b;
   int c;
   int d;
};
foo(struct my_params* p)
{
   ...
   bar(p);
};

Esta estratégia é boa se você passar por uma série de parâmetros. Se os parâmetros são todos diferentes, então não pode funcionar bem para você. Você iria acabar com uma grande estrutura que está sendo passada ao redor que contém muitos parâmetros diferentes.

variáveis ??automáticas (locais)

Esta tendem a ser o maior consumidor de espaço de pilha.

  • As matrizes são o assassino. Não definir matrizes em suas funções locais!
  • Minimizar o número de variáveis ??locais.
  • Use o menor tipo necessário.
  • Se reentrada não é um problema, você pode usar variáveis ??estáticas módulo.

Tenha em mente que se você está simplesmente movendo todas as suas variáveis ??locais de âmbito local para escopo do módulo, você não salvou qualquer espaço. Trocou espaço de pilha para o espaço de segmento de dados.

Alguns RTOS segmento de suporte de armazenamento local, que aloca armazenamento "global" em uma base por thread. Isso pode permitir que você tenha múltiplas variáveis ??globais independentes em uma base por tarefa, mas isso fará com que seu código não é tão simples.

Em caso você pode poupar uma grande quantidade de memória principal, mas têm apenas um pequeno fragmento da pilha, sugiro avaliar alocações estáticas.

Em C, todas as variáveis ??declaradas dentro de uma função são "gerenciados automaticamente", o que significa que eles são alocados na pilha.

Pré-as declarações como lojas de "estáticos"-os na memória principal em vez de na pilha. Eles basicamente comportam-se como variáveis ??globais, mas ainda permitem que você evitar os maus hábitos que vêm com globals uso excessivo. Você pode fazer um bom caso para declarar grandes, buffers de vida longa / variáveis ??como estático para reduzir a pressão sobre a pilha.

Tenha em atenção que isto não funciona bem / em tudo se sua aplicação é multithreaded ou se você usar a recursividade.

Ligue optimization, inline especificamente agressivo. O compilador deve ser capaz para inline métodos para minimizar chamadas. Dependendo do compilador e otimização muda você usa, marcando alguns métodos como inline podem ajudar (ou pode ser ignorado).

Com CCG, tente adicionar os "-finline-funções" (ou O3) sinalizador e, possivelmente, o "-finline-limite = n" bandeira.

Um truque que eu li em algum lugar para agradar a avaliar os requisitos de pilha do código em uma configuração integrada é preencher o espaço de pilha no início com um padrão conhecido (DEAD em hexadecimal sendo o meu favorito) e deixe o sistema funcionar para uma tempo.

Depois de uma corrida normal, leia o espaço de pilha e ver quanto do espaço de pilha não foi substituído durante o curso da operação. Projetar de modo a deixar pelo menos 150% do que, de modo a enfrentar todos os caminhos de código obsure que pode não ter sido exercidas.

Você pode substituir algumas de suas variáveis ??locais por globals? Arrays em particular, podem comer pilha.

Se a situação permite que você compartilhe algumas globals entre alguns daqueles entre as funções, há uma chance de que você pode reduzir a sua cópia do pé de memória.

O trade off custo é maior complexidade, e maior risco de efeitos colaterais indesejados entre as funções vs uma cópia do pé de memória, possivelmente menor.

Que tipo de variáveis ??que você tem em suas funções? Que tamanhos e limites que estamos falando?

Dependendo do seu compilador, e como agressiva suas opções de otimização são, você terá uso da pilha para cada chamada de função que você faz. Então, para começar, você provavelmente terá que limitar a profundidade das chamadas de função. Alguns compiladores fazer uso saltos em vez de ramos para funções simples que irá reduzir o uso de pilha. Obviamente, você pode fazer a mesma coisa usando, por exemplo, uma macro assembler para saltar para as suas funções, em vez de uma chamada de função direta.

Como mencionado em outras respostas, inlining é uma opção disponível embora isso vem à custa de maior tamanho do código.

A outra área que come pilha é os parâmetros locais. Nesta área, você tem algum controle sobre. Usando estática (nível de arquivo) irá evitar alocação de pilha com o custo de sua alocação de memória RAM estática. Globals da mesma forma.

Em (verdadeiramente) casos extremos você pode vir até com uma convenção para funções que usa um número fixo de variáveis ??globais como armazenamento temporário em vez de moradores na pilha. O pouco complicado é ter certeza de que nenhuma das funções que usam as mesmas globals nunca ter chamado ao mesmo tempo. (Daí a convenção)

Se você precisa para começar a preservar espaço de pilha que você deve obter um compilador melhor ou mais de memória.

O software será normalmente crescem (novas funcionalidades, ...), por isso, se você tem que começar um projeto pensando sobre como preservar espaço de pilha que está condenado desde o início.

Sim, um RTOS pode realmente comer RAM para uso da pilha de tarefas. A minha experiência é que, como um novo usuário de um RTOS, há uma tendência a usar mais tarefas do que o necessário.

Para um sistema embarcado usando um RTOS, RAM pode ser um bem precioso. Para preservar a memória RAM, por características simples que ainda pode ser eficaz para implementar vários recursos dentro de uma tarefa, em execução de modo round-robin, com um design multitarefa cooperativa. Assim, reduzir o número total de tarefas.

Eu acho que você pode estar imaginando um problema que não esta disponível aqui. A maioria dos compiladores não fazem nada quando eles "alocar" variáveis ??automaticic na pilha.

A pilha é alocado antes "main ()" é executado. Quando você chamar a função b () de função a () o endereço da área de armazenamento imediatamente após a última variável utilizada por um é passado para b (). Isto torna-se o início de b () 's pilha se b (), em seguida, chama a função c (), então pilha começa do c após a última variável automática definida por b ().

Note que a memória de pilha já está lá e alocado, que nenhuma inicialização ocorre ea única processamento envolvido está passando um ponteiro de pilha.

A única vez que isso se torna um problema seria onde todas as três funções usam grandes quantidades de armazenamento da pilha, então tem de acomodar a memória de todas as três funções. Tente manter funções que alocar grandes quantidades de armazenamento na parte inferior da pilha de chamadas ou seja, não faça chamar uma outra função deles.

Outro truque para sistemas de memória constained é a divisão das partes hogging memória de uma função em auto separados contidos funções.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top