Pergunta

Eu estou experimentando o que parece ser um stack/heap de colisão em um ambiente embarcado (ver esta pergunta para alguns de fundo).

Eu gostaria de tentar reescrever o código para que ele não alocar memória no heap.

Posso escrever um aplicativo sem usar o heap em C?Por exemplo, como é que eu uso a pilha só se eu tiver uma necessidade de alocação dinâmica de memória?

Foi útil?

Solução

Fiz isso uma vez em um ambiente incorporado, onde estávamos escrevendo código "super seguro" para máquinas biomédicas. Os malloc () foram explicitamente proibidos, em parte para os limites de recursos e para o comportamento inesperado que você pode obter da memória dinâmica (procure Malloc (), VxWorks/Tornado e Fragmentation e você terá um bom exemplo).

De qualquer forma, a solução era planejar com antecedência os recursos necessários e alocar estaticamente os "dinâmicos" em um vetor contido em um módulo separado, tendo algum tipo de alocador de propósito especial dar e receber ponteiros de volta. Essa abordagem evitou os problemas de fragmentação e ajudou a obter informações de erro de granulação mais refinadas, se um recurso estivesse esgotado.

Isso pode parecer bobo no Big Iron, mas em sistemas incorporados, e particularmente nos críticos de segurança, é melhor ter um entendimento muito bom de qual tempo e recursos espaciais são necessários de antemão, mesmo que apenas com o objetivo de dimensionar o hardware.

Outras dicas

Curiosamente, uma vez vi um aplicativo de banco de dados que confiava completamente na memória alocada estática. Este aplicativo teve uma forte restrição no campo e comprimentos de registro. Até o editor de texto incorporado (ainda tremei chamando isso) não conseguiu criar textos com mais de 250 linhas de texto. Isso resolveu algumas perguntas que eu tinha neste momento: por que apenas 40 registros são permitidos por cliente?

Em aplicativos sérios, você não pode calcular antecipadamente os requisitos de memória do seu sistema em execução. Portanto, é uma boa ideia alocar a memória dinamicamente, conforme você precisa. No entanto, é um caso comum em sistemas incorporados para pré -alocar a memória que você realmente precisa para evitar falhas inesperadas devido à escassez de memória.

Você pode alocar memória dinâmica na pilha usando as chamadas da biblioteca aloca (). Mas essa memória é rígida para o contexto de execução do aplicativo e é uma má idéia retornar a memória desse tipo o chamador, porque será substituído por chamadas posteriores da sub -rotina.

Então, eu posso responder sua pergunta com um "depende" nítido e claro ...

Você pode usar alloca() Função que aloca a memória na pilha - essa memória será liberada automaticamente quando você sair da função. alloca() é específico do GNU, você usa o GCC para que ele esteja disponível.

Ver man alloca.

Outra opção é usar matrizes de comprimento variável, mas você precisa usar o modo C99.

É possível alocar uma grande quantidade de memória da pilha em main() e tem seu código sub-atribui-lo mais tarde.É uma coisa boba de se fazer, pois significa que o programa está utilizando a memória que, de fato, não precisa.

Eu não consigo pensar em nenhuma razão (guardar algum tipo de bobagem programação desafio ou exercício de aprendizagem) para, querendo evitar a pilha.Se você já "ouviu falar" que de alocação de heap é lento e alocação de pilha é rápido, é simplesmente porque a pilha envolve a alocação dinâmica de.Se você alocar dinamicamente a memória a partir de um reservados bloco dentro da pilha, ele iria ser tão lento.

Alocação de pilha é fácil e rápido, porque você só pode desalocar o mais "jovem" do item na pilha.Ele trabalha para as variáveis locais.Ele não funciona para estruturas de dados dinâmicos.

Editar:Depois de ter visto a motivação para a pergunta...

Em primeiro lugar, a pilha e a pilha tem que competir para a mesma quantidade de espaço disponível.Geralmente, eles crescem para com os outros.Isso significa que, se você mover todas as suas uso da pilha para a pilha de alguma forma e, em seguida, em vez de pilha colidir com pilha, o tamanho da pilha só vai exceder a quantidade de RAM que você tem disponível.

Eu acho que você precisa para assistir o seu montão, e o uso de pilha (você pode pegar ponteiros para variáveis locais para se ter uma idéia de que a pilha está no momento), e se for alta demais, reduza-a.Se você tiver uma grande quantidade de pequenas alocados dinamicamente objetos, lembre-se de que cada alocação tem alguns sobrecarga de memória, de modo sub-atribuição-los a partir de um conjunto pode ajudar a reduzir os requisitos de memória.Se você usar a recursão em qualquer lugar pensar em substituí-lo com um baseado na matriz de solução.

Você não pode fazer alocação de memória dinâmica em C sem usar a memória da heap. Seria muito difícil escrever um aplicativo do mundo real sem usar o Heap. Pelo menos, não consigo pensar em uma maneira de fazer isso.

BTW, por que você quer evitar o Heap? O que há de tão errado com isso?

1: Sim, você pode - se você não precisar de alocação de memória dinâmica, mas pode ter um desempenho horrível, dependendo do seu aplicativo. (ou seja, não usar o heap não lhe dará melhores aplicativos)

2: Não, não acho que você possa alocar a memória dinamicamente na pilha, pois essa parte é gerenciada pelo compilador.

Sim, é factível. Shift Your Dynamic precisa fora da memória e para o disco (ou qualquer armazenamento em massa que você tenha disponível) - e sofra a conseqüente penalidade de desempenho.

Por exemplo, você precisa construir e fazer referência a uma árvore binária de tamanho desconhecido. Especifique um layout de registro que descreva um nó da árvore, onde os ponteiros para outros nós são na verdade números recordes no seu arquivo de árvore. Escreva rotinas que permitam adicionar à árvore escrevendo um registro adicional para arquivar e andar na árvore lendo um registro, encontrando seu filho como outro número de registro, lendo esse registro, etc.

Essa técnica aloca o espaço dinamicamente, mas é espaço em disco, não espaço na RAM. Todas as rotinas envolvidas podem ser escritas usando espaço alocado estaticamente - na pilha.

Os aplicativos incorporados precisam ter cuidado com as alocações de memória, mas não acho que usar a pilha ou sua própria pilha pré-alocada seja a resposta. Se possível, aloque toda a memória necessária (geralmente buffers e grandes estruturas de dados) no tempo de inicialização a partir de uma pilha. Isso requer um estilo diferente de programa do que a maioria de nós está acostumada agora, mas é a melhor maneira de se aproximar do comportamento determinístico.

Uma pilha grande que é alocada posteriormente ainda estaria sujeita a ficar sem memória e a única coisa a fazer é ter um chute de vigilância (ou ação semelhante). Usando os sons da pilha atraente, mas se você quiser alocar grandes buffers/estruturas de dados na pilha, precisará ter certeza de que a pilha é grande o suficiente para lidar com todos os caminhos de código possíveis que seu programa poderá executar. Isso não é fácil e, no final, é semelhante a uma pilha subalocada.

Minha principal preocupação é: abolir a pilha realmente ajuda?

Como seu desejo de não usar hastes de pilha da colisão de pilha/heap, assumindo que o início da pilha e o início da pilha seja definido corretamente (por exemplo, na mesma configuração, pequenos programas de amostra não têm esse problema de colisão), então a colisão significa que o hardware tem Não há memória suficiente para o seu programa.

Não usando pilha, pode -se realmente economizar espaço de resíduos da fragmentação da heap; Mas se o seu programa não usar a pilha para um monte de alocação irregular de tamanho grande, os resíduos provavelmente não são muito. Verei seu problema de colisão mais um problema fora da memória, algo que não é corrigível apenas evitando a pilha.

Meus conselhos sobre como enfrentar este caso:

  1. Calcule o uso total de memória potencial do seu programa. Se estiver muito próximo, mas ainda não excede a quantidade de memória que você preparou para o hardware, então você pode
  2. Tente usar menos memória (melhorar os algoritmos) ou usar a memória com mais eficiência (por exemplo, menor e de tamanho mais regular malloc() reduzir a fragmentação da pilha); ou
  3. Basta comprar mais memória para o hardware

É claro que você pode tentar empurrar tudo para o espaço estático de memória predefinido, mas é muito provável que ele seja sobrescrevendo a memória estática desta vez. Portanto, melhore o algoritmo para ser menos consumido pela memória primeiro e compre mais memória no segundo.

Eu atacaria esse problema de uma maneira diferente - se você acha que a pilha e a pilha estão colidindo, testava isso protegendo -o.

Por exemplo (assumindo um sistema *ix) tente mprotect()na última página da pilha (assumindo uma pilha de tamanho fixo) para que não seja acessível. Ou - se sua pilha crescer - então mmap Uma página no meio da pilha e da pilha. Se você receber um SEGV na página de guarda, sabe que fugirá do final da pilha ou da pilha; E olhando para o endereço da falha do SEG, você pode ver qual da pilha e heap colidir.

Muitas vezes é possível escrever a sua aplicação embarcada, sem o uso de alocação dinâmica de memória.Em muitas aplicações embarcadas o uso de alocação dinâmica é preterido por causa dos problemas que podem surgir devido a fragmentação de memória.Ao longo do tempo ele se torna altamente provável que não vai ser de um tamanho adequado região de pilha espaço livre para permitir que a memória a ser alocada e, a menos que haja um esquema para lidar com esse erro o aplicativo irá falhar.Existem diversos esquemas para contornar esse problema, sendo que uma é para sempre alocar tamanho fixo de objetos na pilha para que uma nova atribuição será sempre caber em uma memória liberada área.Outro para detectar a falha de alocação e de efectuar um processo de desfragmentação em todos os objetos no heap (deixado como um exercício para o leitor!)

Você não dizer o que o processador ou conjunto de ferramentas que você está usando, mas em muitos estático, pilha e heap são alocados para separar os segmentos definidos no linker.Se este for o caso, então deve ser que a sua pilha está crescendo fora do espaço de memória que você definiu para ele.A solução que você precisa é reduzir a pilha e/ou estático de tamanho variável (supondo que esses dois são contíguos) de modo que não está mais disponível para a pilha.Pode ser possível reduzir a pilha, unilateralmente, embora isso pode aumentar a probabilidade de problemas de fragmentação.Garantindo que não há desnecessários variáveis estáticas vai libertar algum espaço, ao custo de possivelmente aumentando o uso de pilha, se a variável é feita automaticamente.

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