Pergunta

Eu tenho um programa que segfaults de aritmética de ponteiro, às vezes. Eu sei que isso acontece, mas eu não posso facilmente verificar antes do tempo para ver se ele segfaults ou não - ou eu posso dados de entrada "pré-scan" para ver se ele irá causar um segfault (que pode ser impossível determinar), ou eu posso montá-la para não usar a aritmética de ponteiro, o que exigiria uma quantidade significativamente maior de trabalho, ou eu posso tentar pegar um segfault. Então, minha pergunta:

1) Como, em C, posso pegar um segfault? Sei algo no sistema operacional faz com que um segfault, mas o que um programa C pode fazer no caso em que ele segfaults morrer um pouco mais graciosamente do que apenas Segmentation fault?

2) Como portátil é isso?

Eu imagino que este é um comportamento altamente portável, então se você postar qualquer código para pegar um segfault, por favor me diga o que trabalha. Eu estou no Mac OS X, mas eu gostaria meu programa para trabalhar em tantas plataformas quanto ele pode e eu quero ver quais são minhas opções.

E não se preocupe - basicamente tudo que eu quero fazer é imprimir uma mensagem de erro mais user-friendly e liberar memória malloc()ed, e depois morrer. Eu não estou pensando em simplesmente ignorar todos os segfaults que recebo e arar a frente.

Foi útil?

Solução

Você tem que definir um manipulador de sinal. Isso é feito em sistemas Unix usando a função sigaction . Eu fiz isso com o mesmo código no Fedora 64 e 32 bits, e em Sun Solaris.

Outras dicas

Bem, SIGSEGV é detectável, e este é POSIX, por isso é portátil nesse sentido.

Bu Eu estou preocupado que você parece querer lidar com o segfault ao invés de resolver o problema que faz com que o segfault. Se eu tivesse que escolher se era o SO em falta, ou o meu próprio código, eu sei que eu escolheria. Eu sugiro que você caçar esse erro, corrigi-lo, em seguida, escrever um caso de teste para se certificar de que nunca te morde novamente.

Você pode usar a função sinal para instalar um novo manipulador de sinal para o sinal:

   #include <signal.h>
   void (*signal(int signum, void (*sighandler)(int)))(int);

Algo como o seguinte código:

signal(SIGINT , clean_exit_on_sig);
signal(SIGABRT , clean_exit_on_sig);
signal(SIGILL , clean_exit_on_sig);
signal(SIGFPE , clean_exit_on_sig);
signal(SIGSEGV, clean_exit_on_sig); // <-- this one is for segmentation fault
signal(SIGTERM , clean_exit_on_sig);

void 
clean_exit_on_sig(int sig_num)
{
        printf ("\n Signal %d received",sig_num);
}

As ações de segurança em um manipulador de sinal são muito limitado. É inseguro para chamar qualquer função de biblioteca não conhecido por ser re-entrantes, que exclui, por exemplo, free() e printf(). A melhor prática é definir uma variável e retorno, mas isso não ajuda muito. Também é seguro usar chamadas de sistema, como write().

Note que nos dois exemplos backtrace dadas aqui, a função backtrace_symbols_fd() será seguro porque ele usa o fd matéria diretamente, mas a chamada para fprintf() é incorreto e deve ser substituído por um uso de write().

Eu acho que você está tentando resolver um problema que não existe. Pelo menos você está trabalhando no lado errado. Você não será capaz de captura uma falha de segmentação, como este erro / exceção é lançada pelo sistema operacional (é causou pelo seu programa, o sistema operacional apenas capturas -lo).

Eu aconselho você a repensar a sua estratégia relativa à entrada: Por que é impossível para higienizar-lo? O mais importante a fazer é verificar o tamanho, por isso, o C stdlib tem funções apropriadas. Então é claro que você tem que verificar para entrada válida em relação ao conteúdo. Sim, isso provavelmente resultará em um monte de trabalho, mas é a única maneira de escrever um programa robusto.

EDIT: Eu não sou muito de um especialista em C, não sabia que mesmo uma falha de segmentação poderia ser tratado por um manipulador de sinal. Ainda assim, eu acho que não é o caminho certo a seguir, pelas razões mencionadas acima.

Você precisa fornecer um manipulador SIGSEGV, este parece bastante decente .

manipulação de sinal é (relativamente) portáveis ??entre máquinas Unix (incluindo Mac e Linux). As grandes diferenças estão em detalhe a excepção, que é passada como argumento para a rotina de tratamento de sinal. Sorrty, mas provavelmente você vai precisar de um monte de #ifdefs por isso, se você pretende imprimir mensagens de erro mais razoáveis ??(tais como onde e devido a que abordar a falha aconteceu) ...

ok, aqui está um fragmento de código para que você possa começar com:

#include <signal.h>

/* reached when a segv occurrs */
void
SEGVFunction( SIGARGS )
{
     ...
}

...
main(...) {
    signal(SIGSEGV, SEGVFunction); /* tell the OS, where to go in case... */
    ...
    ... do your work ...
}

Sua tarefa é:

  • Verifique o que SIGARGS é (dependente OS, então use um ifdef)
  • ver como extrair a falhas endereço e pc a partir da informação de exceção em sigArgs
  • imprimir mensagem razoável
  • exit

em teoria, você poderia até mesmo corrigir o pc no manipulador de sinal (para depois da instrução de falha), e prossiga. No entanto, manipuladores de sinais típicos tanto exit () ou a uma volta longjmp () em um save lugar no principal.

relação

Há um exemplo de como pegar SIGSEGV e imprimir um rastreamento de pilha usando backtrace da glibc () aqui:

como gerar um stacktrace quando meu C ++ aplicativo acidentes

Você pode usar isso para recuperar o segfault e limpar, mas lembre-se: você não deveria estar fazendo coisas demais em um manipulador de sinal, especialmente coisas que envolvem fazer chamadas como malloc (). Há uma grande quantidade de chamadas que não são seguro de sinal, e você pode acabar atirando no próprio pé se você faz, digamos, uma chamada para malloc de dentro malloc.

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