Não existe sinal de captura em C .... ou pelo menos assim eu pensei até ler o padrão C99. Acontece que há manipulação de sinal definida em C, mas Ctrl-C não é obrigado a produzir nenhum sinal específico ou sinal. Dependendo da sua plataforma, isso pode ser impossível.
JeremyP
1
A manipulação de sinais depende principalmente da implementação. Nas plataformas * nix, use <signal.h> e, se você estiver no OSX, poderá aproveitar o GCD para tornar as coisas ainda mais fáceis ~.
Dylan Lukes
Respostas:
205
Com um manipulador de sinal.
Aqui está um exemplo simples lançando um boolusado em main():
Editar em junho de 2017 : a quem possa interessar, principalmente aqueles com um desejo insaciável de editar esta resposta. Olha, eu escrevi esta resposta há sete anos. Sim, os padrões de idioma mudam. Se você realmente deve melhorar o mundo, adicione sua nova resposta, mas deixe a minha como está. Como a resposta tem meu nome, eu prefiro que contenha minhas palavras também. Obrigado.
Vamos mencionar que precisamos # incluir <signal.h> para que isso funcione!
precisa saber é o seguinte
13
estático Deve bool volatile keepRunning = true;ser 100% seguro. O compilador é livre para armazenar keepRunningem cache em um registro e o volátil evitará isso. Na prática, é provável que também funcione sem a palavra-chave volátil quando o loop while chama pelo menos uma função não-inline.
Johannes Overmann
2
@DirkEddelbuettel Estou corrigido, pensei que minhas melhorias refletiriam mais suas intenções originais, desculpe se isso não aconteceu. Enfim, o maior problema é que, já que sua resposta tenta ser genérica o suficiente, e como a IMO deve fornecer um trecho que também esteja funcionando sob interrupções assíncronas: eu usaria sig_atomic_tou atomic_booldigitaria lá. Eu apenas perdi essa. Agora, já que estamos conversando: você gostaria que eu revelasse minha edição mais recente? Sem ressentimentos não seria perfeitamente compreensível a partir de seu ponto de vista :)
Peter Varo
2
Isso é muito melhor!
Dirk Eddelbuettel 19/05
2
@JohannesOvermann Não que eu queira escolher, mas estritamente falando, não importa se o código chama funções não-in-line ou não, pois somente ao atravessar uma barreira de memória, o compilador não pode confiar em um valor em cache. Bloquear / desbloquear um mutex seria uma barreira de memória. Como a variável é estática e, portanto, não é visível fora do arquivo atual, o compilador pode assumir que uma função nunca pode alterar seu valor, a menos que você passe uma referência a essa variável para a função. Tão volátil é fortemente recomendado aqui em qualquer caso.
Nota: Obviamente, este é um exemplo simples que explica exatamente como configurar um CtrlCmanipulador, mas como sempre, existem regras que precisam ser obedecidas para não quebrar outra coisa. Por favor, leia os comentários abaixo.
O código de exemplo acima:
#include<stdio.h>#include<signal.h>#include<stdlib.h>voidINThandler(int);int main(void){
signal(SIGINT,INThandler);while(1)
pause();return0;}voidINThandler(int sig){char c;
signal(sig, SIG_IGN);
printf("OUCH, did you hit Ctrl-C?\n""Do you really want to quit? [y/n] ");
c = getchar();if(c =='y'|| c =='Y')
exit(0);else
signal(SIGINT,INThandler);
getchar();// Get new line character}
@Derrick Concord, int mainé uma coisa apropriada, mas gcce outros compiladores compilam isso para programas em execução correta desde os anos 90. Explicou muito bem aqui: eskimo.com/~scs/readings/voidmain.960823.html - é basicamente um "recurso", eu entendo assim.
Icyrock.com
1
@ icyrock.com: Tudo muito verdadeiro (em relação ao void main () em C), mas, ao postar publicamente, é provável que evite o debate usando int main () para não desviar o foco do ponto principal.
Clifford
21
Há uma enorme falha nisso. Você não pode usar printf com segurança no conteúdo de um manipulador de sinal. É uma violação da segurança do sinal assíncrono. Isso ocorre porque printf não é reentrante. O que acontece se o programa estava no meio do uso de printf quando Ctrl-C foi pressionado e o manipulador de sinais começa a usá-lo ao mesmo tempo? Dica: Provavelmente irá quebrar. write e fwrite são iguais para usar neste contexto.
Dylan Lukes
2
@ icyrock.com: Fazer algo complicado em um manipulador de sinais causará dores de cabeça. Especialmente usando o sistema io.
Martin York
2
@stacker Obrigado - acho que vale a pena no final. Se alguém se deparar com esse código no futuro, é melhor acertar o máximo possível, independentemente do tópico da pergunta.
Icyrock.com
30
Adendo referente às plataformas UN * X.
De acordo com a signal(2)página do manual no GNU / Linux, o comportamento de signalnão é tão portátil quanto o comportamento de sigaction:
O comportamento do sinal () varia entre as versões UNIX e também variou historicamente entre diferentes versões do Linux. Evite seu uso: use sigaction (2).
No Sistema V, o sistema não bloqueou a entrega de outras instâncias do sinal e a entrega de um sinal redefiniria o manipulador para o padrão. No BSD, a semântica mudou.
A seguinte variação da resposta anterior de Dirk Eddelbuettel usa em sigactionvez de signal:
Agora deve ser possível ler Ctrl+ Cpressionamentos de teclas usando fgetc(stdin). Cuidado com isso, porque você não pode Ctrl+Z , Ctrl+ Q, Ctrl+ S, etc.
@ Peter Varo atualizou a resposta de Dirk, mas Dirk rejeitou a alteração. Aqui está a nova resposta de Peter:
Embora o snippet acima seja um correto c89Por exemplo, deve-se usar os tipos e garantias mais modernos fornecidos pelos padrões posteriores, se possível. Portanto, aqui está uma alternativa mais segura e moderna para aqueles que buscam oc99 e c11 implementação conforme:
#include<signal.h>#include<stdlib.h>#include<stdio.h>staticvolatilesig_atomic_t keep_running =1;staticvoid sig_handler(int _){(void)_;
keep_running =0;}int main(void){
signal(SIGINT, sig_handler);while(keep_running)
puts("Still running...");
puts("Stopped by signal `SIGINT'");return EXIT_SUCCESS;}
Padrão C11: 7.14§2 O cabeçalho <signal.h>declara um tipo ... sig_atomic_tque é o tipo inteiro (possivelmente qualificado para um volátil) de um objeto que pode ser acessado como uma entidade atômica, mesmo na presença de interrupções assíncronas.
Além disso:
C11 Standard: 7.14.1.1§5 Se o sinal ocorrer como resultado da chamada doabortraise função ou , o comportamento será indefinido se o manipulador de sinal se referir a qualquer objeto com staticou duração de armazenamento de encadeamento que não seja um objeto atômico livre de bloqueios do que atribuir um valor a um objeto declarado como volatile sig_atomic_t...
Acabei de encontrar essa resposta e estava prestes a colar esse link aqui! Obrigado :)
Luis Paulo
5
Em relação às respostas existentes, observe que o tratamento do sinal depende da plataforma. O Win32, por exemplo, lida com muito menos sinais que os sistemas operacionais POSIX; veja aqui . Enquanto o SIGINT é declarado em signs.h no Win32, consulte a nota na documentação que explica que ele não fará o que você poderia esperar.
#include<stdio.h>#include<signal.h>#include<unistd.h>void sig_handler(int signo){if(signo == SIGINT)
printf("received SIGINT\n");}int main(void){if(signal(SIGINT, sig_handler)== SIG_ERR)
printf("\ncan't catch SIGINT\n");// A long long wait so that we can easily issue a signal to this processwhile(1)
sleep(1);return0;}
A função sig_handler verifica se o valor do argumento passado é igual ao SIGINT, então o printf é executado.
Respostas:
Com um manipulador de sinal.
Aqui está um exemplo simples lançando um
bool
usado emmain()
:Editar em junho de 2017 : a quem possa interessar, principalmente aqueles com um desejo insaciável de editar esta resposta. Olha, eu escrevi esta resposta há sete anos. Sim, os padrões de idioma mudam. Se você realmente deve melhorar o mundo, adicione sua nova resposta, mas deixe a minha como está. Como a resposta tem meu nome, eu prefiro que contenha minhas palavras também. Obrigado.
fonte
bool volatile keepRunning = true;
ser 100% seguro. O compilador é livre para armazenarkeepRunning
em cache em um registro e o volátil evitará isso. Na prática, é provável que também funcione sem a palavra-chave volátil quando o loop while chama pelo menos uma função não-inline.sig_atomic_t
ouatomic_bool
digitaria lá. Eu apenas perdi essa. Agora, já que estamos conversando: você gostaria que eu revelasse minha edição mais recente? Sem ressentimentos não seria perfeitamente compreensível a partir de seu ponto de vista :)Verifique aqui:
Nota: Obviamente, este é um exemplo simples que explica exatamente como configurar um CtrlCmanipulador, mas como sempre, existem regras que precisam ser obedecidas para não quebrar outra coisa. Por favor, leia os comentários abaixo.
O código de exemplo acima:
fonte
int main
é uma coisa apropriada, masgcc
e outros compiladores compilam isso para programas em execução correta desde os anos 90. Explicou muito bem aqui: eskimo.com/~scs/readings/voidmain.960823.html - é basicamente um "recurso", eu entendo assim.Adendo referente às plataformas UN * X.
De acordo com a
signal(2)
página do manual no GNU / Linux, o comportamento designal
não é tão portátil quanto o comportamento desigaction
:No Sistema V, o sistema não bloqueou a entrega de outras instâncias do sinal e a entrega de um sinal redefiniria o manipulador para o padrão. No BSD, a semântica mudou.
A seguinte variação da resposta anterior de Dirk Eddelbuettel usa em
sigaction
vez designal
:fonte
volatile sig_atomic_t
para ser bem definidaOu você pode colocar o terminal no modo bruto, assim:
Agora deve ser possível ler Ctrl+ Cpressionamentos de teclas usando
fgetc(stdin)
. Cuidado com isso, porque você não pode Ctrl+Z , Ctrl+ Q, Ctrl+ S, etc.fonte
Configure uma interceptação (você pode interceptar vários sinais com um manipulador):
Manipule o sinal da maneira que desejar, mas esteja ciente das limitações e dicas:
fonte
@ Peter Varo atualizou a resposta de Dirk, mas Dirk rejeitou a alteração. Aqui está a nova resposta de Peter:
Embora o snippet acima seja um correto c89Por exemplo, deve-se usar os tipos e garantias mais modernos fornecidos pelos padrões posteriores, se possível. Portanto, aqui está uma alternativa mais segura e moderna para aqueles que buscam oc99 e c11 implementação conforme:
Além disso:
fonte
(void)_;
... Qual é o seu propósito? É assim que o compilador não avisa sobre uma variável não utilizada?Em relação às respostas existentes, observe que o tratamento do sinal depende da plataforma. O Win32, por exemplo, lida com muito menos sinais que os sistemas operacionais POSIX; veja aqui . Enquanto o SIGINT é declarado em signs.h no Win32, consulte a nota na documentação que explica que ele não fará o que você poderia esperar.
fonte
A função sig_handler verifica se o valor do argumento passado é igual ao SIGINT, então o printf é executado.
fonte
Isso é impresso antes da saída.
fonte