Pergunta

Estou usando MinGW com GCC 3.4.5 (mingw-special vista r3).

Meu aplicativo C usa muita pilha, então eu queria saber se existe alguma maneira de saber programaticamente quanta pilha resta para que eu possa lidar com a situação de maneira limpa se descobrir que estou prestes a acabar.

Se não, de que outras maneiras você contornaria o problema de potencialmente ficar sem espaço na pilha?

Não tenho ideia do tamanho da pilha com a qual começarei, então também precisaria identificá-la programaticamente.

Foi útil?

Solução

Raimundo Chen (A velha coisa nova) tem uma boa resposta para esse tipo de pergunta:

Se você precisar perguntar, provavelmente está fazendo algo errado.

Aqui estão alguns detalhes do Win32 sobre alocação de pilha: MSDN.

Se você acha que pode estar limitado pelo espaço de pilha, quase certamente estará limitado pela memória virtual disponível; nesse caso, você precisará encontrar uma solução diferente.

O que exatamente você está tentando fazer?

Outras dicas

A função getrusage fornece o uso atual.(ver man getrusage).

O getrlimit no Linux ajudaria a buscar o tamanho da pilha com o RLIMIT_STACK parâmetro.

#include <sys/resource.h>
int main (void)
{
  struct rlimit limit;

  getrlimit (RLIMIT_STACK, &limit);
  printf ("\nStack Limit = %ld and %ld max\n", limit.rlim_cur, limit.rlim_max);
}

Por favor, dê uma olhada man getrlimit.A mesma informação poderia ser obtida por ulimit -s ou ulimit -a linha de tamanho da pilha.Dê uma olhada também setrlimit função que permitiria definir os limites.Mas, como mencionado nas outras respostas, se você precisar ajustar a pilha, provavelmente deverá reconsiderar seu design.Se você quer um array grande, por que não retirar a memória do heap?

Tirar o endereço de uma variável local da pilha funcionaria.Então em uma chamada mais aninhada você pode subtrair o endereço de outro local para encontrar a diferença entre eles

size_t top_of_stack;

void Main()
{
  int x=0;
  top_of_stack = (size_t) &x;

  do_something_very_recursive(....)
}

size_t SizeOfStack()
{
  int x=0;
  return top_of_stack - (size_t) &x;
} 

Se o seu código for multithread, você precisará lidar com o armazenamento da variável top_of_stack por thread.

verifique se o seu compilador suporta stackavail()

Supondo que você saiba o tamanho da pilha completa, provavelmente poderia adicionar algum código assembly para ler o ESP.
Se você ler o ESP e salvá-lo na função principal, poderá comparar o ESP atual com o ESP que você tem na função principal e ver o quanto o ESP mudou.Isso lhe dará uma indicação de quanta pilha você está usando.

Este é um problema do qual desisti.Com muito hacking e (principalmente) oração, você pode obter uma solução que funcione em um determinado momento e em uma determinada máquina.Mas, em geral, parece não haver uma maneira decente de fazer isso.

Você terá que obter a posição e o tamanho da pilha de fora do seu programa (no Linux você pode obtê-la de /proc/<pid>/maps).No seu programa você deve testar de alguma forma onde você está na pilha.É possível usar variáveis ​​locais, mas não há garantia real de que elas estejam realmente na pilha.Você também pode tentar obter o valor do registro do ponteiro da pilha com algum assembly.

Então agora você tem a localização da pilha, seu tamanho e a posição atual e presume que sabe em que direção a pilha cresce.Quando você entra no modo stack-overflow?É melhor não fazer isso perto do fim porque sua estimativa (ou seja,endereço da variável local ou valor do ponteiro da pilha) é provavelmente um pouco otimista demais;não é incomum endereçar a memória além do ponteiro da pilha.Além disso, você não tem ideia de quanto espaço na pilha qualquer função (e as funções que ela chama) precisa.Então você terá que deixar algum espaço no final.

Só posso aconselhar você a não entrar nessa confusão e tentar evitar recursões muito profundas.Você também pode querer aumentar o tamanho do seu stack;no Windows você tem que compilar isso no executável, eu acredito.

Para Windows:Já fiz isso antes de usar a função VirtualQuery de Kernel32.dll.Só tenho um exemplo em C# mas demonstra a técnica:

public static class StackManagement
    {
        [StructLayout(LayoutKind.Sequential)]
        struct MEMORY_BASIC_INFORMATION
        {
            public UIntPtr BaseAddress;
            public UIntPtr AllocationBase;
            public uint AllocationProtect;
            public UIntPtr RegionSize;
            public uint State;
            public uint Protect;
            public uint Type;
        };

        private const long STACK_RESERVED_SPACE = 4096 * 16;

        public unsafe static bool CheckForSufficientStack(UInt64 bytes)
        {
            MEMORY_BASIC_INFORMATION stackInfo = new MEMORY_BASIC_INFORMATION();
            UIntPtr currentAddr = new UIntPtr(&stackInfo);
            VirtualQuery(currentAddr, ref stackInfo, sizeof(MEMORY_BASIC_INFORMATION));

            UInt64 stackBytesLeft = currentAddr.ToUInt64() - stackInfo.AllocationBase.ToUInt64();

            return stackBytesLeft > (bytes + STACK_RESERVED_SPACE);
        }

        [DllImport("kernel32.dll")]
        private static extern int VirtualQuery(UIntPtr lpAddress, ref MEMORY_BASIC_INFORMATION lpBuffer, int dwLength);
    }

POR FALAR NISSO:Este código também pode ser encontrado no StackOverflow em outra pergunta que fiz quando estava tentando corrigir um bug no código: A operação aritmética resultou em um estouro em C# inseguroinsira a descrição do link aqui

talvez isso ajude apenas na plataforma Windows:

no cabeçalho PE (IMAGE_NT_HEADERS) do seu exe existem alguns registros como:

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

typedef struct _IMAGE_OPTIONAL_HEADER {
    ...
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    ...
}

Existe uma maneira simples de obter esses valores:usar GetModuleHandle(NULL) fornecerá a imagebase (handle) do seu módulo, endereço onde você encontrará uma estrutura IMAGE_DOS_HEADER que o ajudará a encontrar a estrutura IMAGE_NT_HEADERS (imagebase+IMAGE_DOS_HEADER.e_lfanew) -> IMAGE_NT_HEADERS, e aí você' encontrarei esses campos: SizeOfStackReserve e SizeOfStackCommit.

A quantidade máxima de espaço que o sistema operacional alocará para sua pilha é SizeOfStackReserve.

Se você considerar tentar isso, me avise e eu o ajudarei.Existe uma maneira de obter o tamanho da pilha utilizada em um determinado ponto.

No Linux, você chamaria o GetRusage e verifica o membro RU_ISRSS da Struct Rusage (tamanho integral de pilha não compartilhada).

No site MINGW e no rastreamento de patches do site sourceforge, vejo que em maio de 2008 foram feitos alguns patches em torno do getrusage e parece que ele já tem suporte há um bom tempo.Você deve verificar cuidadosamente se há alguma advertência em termos de quanto da funcionalidade típica do Linux é suportada pelo MinGW.

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