Posso executar free () ou close () em um manipulador de sinal? [duplicado]

Dec 10 2020

Eu tenho um código parecido com este:

//global variables

void signal_handler() {
    //deallocation of global variables
    free(foo);
    close(foo_2);
    exit(0);
}

int main () {
    signal(SIGINT, signal_handler);

    //irrelevant code
}

Como você pode ver, alterei a CTRL+Cinterrupção para executar a signal_handlerfunção uma vez, em vez de encerrar o processo imediatamente. Eu li em algum lugar que algumas funções como podem ser freenão são assíncronas e NÃO executariam no, signal_handlermas não tenho certeza sobre isso.

Posso executar funções como free, close, exitou até mesmo pthread_joinem um manipulador de sinal?

Respostas

1 dratenik Dec 10 2020 at 15:46

Não. Somente as funções listadas em man 7 signal-safety podem ser chamadas com segurança dentro de um manipulador de sinais.

closeestá listado e deve ser seguro. freenão é. Por razões pelas quais você teria que olhar seu código-fonte (ele contém bloqueios). exitnão é seguro porque pode chamar manipuladores de limpeza arbitrários. Você tem _exitque sai abruptamente sem a limpeza.

1 MarcoBonelli Dec 10 2020 at 15:46

Você tecnicamente pode compilar um programa que chama tais funções em um manipulador de sinal, nada o impede de fazer isso. No entanto, isso resultará em um comportamento indefinido se a função que você está tentando executar não for segura para sinais assíncronos. Não é como se uma função insegura simplesmente "NÃO fosse executado" como você diz, eles poderiam muito bem, mas ainda seria um comportamento indefinido.

Uma lista de funções de sinal seguro assíncrono está documentada em man 7 signal-safety. A close()função é segura, enquanto free()e phtread_join()não. A exit()função também não é segura para chamar a partir de um manipulador de sinais, se você deseja sair desse contexto, terá que fazer isso usando _exit().

A única maneira de chamar com segurança uma função que não é segura para sinais assíncronos ao receber um sinal é "lembrar" que você deve chamá-la (por exemplo, definindo uma variável global) e, em seguida, fazer isso após retornar do manipulador de sinal.

1 JohnBode Dec 10 2020 at 16:30

A resposta curta é não:

7.1.4 Uso de funções de biblioteca
...
4 As funções na biblioteca padrão não são garantidas como reentrantes e podem modificar objetos com duração de armazenamento estático ou thread. 188)
188) Assim, um manipulador de sinal não pode, em geral, chamar funções de biblioteca padrão
Rascunho Online C 2011

Exemplo do mundo real das consequências - trabalhei em um sistema que se comunicava com um banco de dados Access. Houve um manipulador de sinal que tentou gravar uma mensagem de erro no console fprintf, mas de alguma forma durante o processo de manipulação de sinal stderrfoi mapeado para o arquivo .mdb que armazenava o banco de dados, sobrescrevendo o cabeçalho e arruinando o banco de dados irremediavelmente.

Honestamente, não há muito que você possa fazer em um manipulador de sinais além de definir um sinalizador para ser verificado em outro lugar.

BasileStarynkevitch Dec 10 2020 at 16:24

Posso executar free () ou close () em um manipulador de sinal?

Você definitivamente não deveria. Consulte o sinal (7) e o sinal de segurança (7)

Na prática, pode funcionar como você deseja, talvez mais da metade do tempo. IIRC, o compilador GCC está fazendo o que você quer, e geralmente funciona.

Uma abordagem melhor é usar algum write (2) para um pipe (7) (de dentro de seu manipulador de sinal) e de vez em quando verificar esse pipe (em seu programa principal) com poll (2) ou coisas relacionadas.

Ou você pode definir alguns volatile sigatomic_t flag;(talvez deva ser também _Atomic) em seu manipulador de sinal e verificar esse sinalizador em outro lugar (no programa principal, fora dos manipuladores de sinal).

Qt está explicando isso melhor do que eu poderia fazer em alguns minutos.

No Linux, consulte também signalfd (2) e eventfd (2) .