Qual é a diferença entre exit () e abort ()?

130

Em C e C ++, qual é a diferença entre exit()e abort()? Estou tentando finalizar meu programa após um erro (não uma exceção).

Martin Liversage
fonte

Respostas:

116

abort()sai do seu programa sem chamar funções registradas usando atexit()primeiro e sem chamar primeiro os destruidores de objetos. exit()faz as duas coisas antes de sair do seu programa. Porém, ele não chama destruidores para objetos automáticos. assim

A a;
void test() { 
    static A b;
    A c;
    exit(0);
}

Destruirá ae bcorretamente, mas não chamará destruidores de c. abort()não chamaria destruidores de nenhum dos objetos. Como isso é lamentável, o Padrão C ++ descreve um mecanismo alternativo que garante a finalização adequada:

Objetos com duração de armazenamento automático são todos destruídos em um programa cuja função main()não contém objetos automáticos e executa a chamada para exit(). O controle pode ser transferido diretamente para isso main(), lançando uma exceção capturada main().

struct exit_exception { 
   int c; 
   exit_exception(int c):c(c) { } 
};

int main() {
    try {
        // put all code in here
    } catch(exit_exception& e) {
        exit(e.c);
    }
}

Em vez de ligar exit(), organize esse código throw exit_exception(exit_code);.

Johannes Schaub - litb
fonte
2
+1 porque, enquanto Brian R. Bondy era bom, você levantou o problema de abortar / sair (não destrói os objetos de pilha chamados) e ofereceu a alternativa para um processo C ++ com uso intenso de RAII.
paercebal
Eu estava procurando uma maneira de sair de um programa sem chamar o dtor e sua resposta é exatamente o que eu estava procurando! Graças
acemtp
Isso é perfeitamente correto, claro, se ele realmente importa que seus objetos automáticos destruidores não são chamados :-)
Chris Huang-Leaver
Que eu saiba, uma outra diferença entre sair e interromper seria que esse cancelamento poderia (dependendo da configuração do sistema operacional) levar à geração de um dump principal.
Dirk Herrmann
33

abort envia um sinal SIGABRT, sair apenas fecha o aplicativo executando a limpeza normal.

Você pode manipular um sinal de aborto como desejar, mas o comportamento padrão é fechar o aplicativo também com um código de erro.

abort não executará a destruição de objetos de seus membros estáticos e globais, mas a saída o fará.

Obviamente, quando o aplicativo estiver completamente fechado, o sistema operacional liberará qualquer memória não liberada e outros recursos.

Tanto no cancelamento quanto no encerramento do programa de saída (assumindo que você não substituiu o comportamento padrão), o código de retorno será retornado ao processo pai que iniciou seu aplicativo.

Veja o seguinte exemplo:

SomeClassType someobject;

void myProgramIsTerminating1(void)
{
  cout<<"exit function 1"<<endl;
}

void myProgramIsTerminating2(void)
{
  cout<<"exit function 2"<<endl;
}

int main(int argc, char**argv)
{
  atexit (myProgramIsTerminating1);
  atexit (myProgramIsTerminating2);
  //abort();
  return 0;
}

Comentários:

  • Se o cancelamento for descomentado: nada será impresso e o destruidor de algum objeto não será chamado.

  • Se abortar for comentado como acima: someobject destructor será chamado, você obterá a seguinte saída:

função de
saída 2 função de saída 1

Brian R. Bondy
fonte
Aqui, ele chamou a função de saída 2 ENTÃO a função de saída 1. gcc 4, Linux 2.6.
Strager
1
A página de manual do atexit diz: "As funções [registradas usando o atexit] são chamadas em ordem inversa; nenhum argumento é passado".
Strager
@strager está certo, as funções registradas pelo atexit devem ser chamadas na ordem inversa quando a saída é chamada ou o retorno principal.
Robert Gamble
Executou um teste e parece que os destruidores de instâncias globais são chamados depois de todos os retornos de chamada atexit.
Strager
+1 para lembrar as pessoas de que o SO acabará liberando todos os recursos alocados, mesmo após uma chamada abortar ().
Fingolfin
10

O seguinte acontece quando um programa chama exit():

  • As funções registradas pela atexitfunção são executadas
  • Todos os fluxos abertos são liberados e fechados, os arquivos criados por tmpfilesão removidos
  • O programa termina com o código de saída especificado para o host

A abortfunção () envia o SIGABRTsinal para o processo atual; se não for capturado, o programa será encerrado sem garantia de que os fluxos abertos sejam liberados / fechados ou que os arquivos temporários criados via tmpfilesejam removidos, atexitas funções registradas não sejam chamadas e uma função não- o status de saída zero é retornado ao host.

Robert Gamble
fonte
Hmm. o padrão diz que o programa não será encerrado apenas se o manipulador de sinal "não retornar". você está bem com C. Você pode imaginar qualquer cenário que permita continuar a execução normal sem retornar? imagino longjmp, mas não tenho certeza de como ele se comporta nos manipuladores de sinal.
Johannes Schaub - litb
Em geral, chamar longjmp de um manipulador de sinal é indefinido, mas há um caso especial de quando o sinal foi gerado com aumento / abortamento, então acho que isso seria teoricamente possível, embora eu ache que nunca o vi fazer isso. Agora vou ter que experimentá-lo;)
Robert Gamble
1
Isso parece funcionar (dividido em várias postagens devido ao limite de 300 caracteres): #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <setjmp.h> volátil sig_atomic_t do_abort = 1; jmp_buf env; void abort_handler (int i) {do_abort = 0; longjmp (env, 1);}
Robert Gamble
int main (vazio) {setjmp (env); puts ("At setjmp"); if (do_abort) {signal (SIGABRT, abort_handler); puts ("Chamada abortar"); abortar(); } puts ("Não abortou!"); retornar 0; }
Robert Gamble
No Ubuntu 7.04, é impresso: No setjmp Chamada abortada No setjmp Não abortada!
Robert Gamble
5

Na página do manual exit ():

A função exit () causa o encerramento normal do processo e o valor do status & 0377 é retornado ao pai.

Na página do manual abort ():

O abort () primeiro desbloqueia o sinal SIGABRT e, em seguida, gera esse sinal para o processo de chamada. Isso resulta na finalização anormal do processo, a menos que o sinal SIGABRT seja capturado e o manipulador de sinal não retorne.

Federico A. Ramponi
fonte
4

abortenvia o SIGABRTsinal. abortnão retorna ao chamador. O manipulador padrão para o SIGABRTsinal fecha o aplicativo. stdioos fluxos de arquivos são liberados e depois fechados. Destruidores para instâncias de classe C ++ não são, no entanto (não tenho certeza disso - talvez os resultados sejam indefinidos?).

exitpossui seus próprios retornos de chamada, definidos com atexit. Se retornos de chamada forem especificados (ou apenas um), eles serão chamados na ordem inversa de sua ordem de registro (como uma pilha), e o programa será encerrado. Como com abort, exitnão retorna ao chamador. stdioos fluxos de arquivos são liberados e depois fechados. Além disso, os destruidores para instâncias de classe C ++ são chamados.

strager
fonte
exit pode ter várias funções de retorno de chamada registradas via atexit, quando exit é chamada, todas as funções de retorno de chamada serão chamadas na ordem inversa em que foram registradas.
Robert Gamble
@ Gamble, é claro, eu mencionei isso alguns minutos atrás em um comentário à resposta de Bondy. Vou editar minha própria resposta para refletir isso.
Strager