Jak się łapie Ctrl + Cw C?
Jak się łapie Ctrl + Cw C?
Odpowiedzi:
Z obsługą sygnału.
Oto prosty przykład odwracający bool
używany w main()
:
#include <signal.h>
static volatile int keepRunning = 1;
void intHandler(int dummy) {
keepRunning = 0;
}
// ...
int main(void) {
signal(SIGINT, intHandler);
while (keepRunning) {
// ...
Edytuj w czerwcu 2017 r . : Kogo to może dotyczyć, zwłaszcza tych, którzy mają nienasyconą potrzebę edycji tej odpowiedzi. Słuchaj, napisałem tę odpowiedź siedem lat temu. Tak, zmieniają się standardy językowe. Jeśli naprawdę musisz ulepszyć świat, dodaj swoją nową odpowiedź, ale moją pozostaw taką, jaka jest. Ponieważ odpowiedź zawiera moje imię, wolałbym, aby zawierała również moje słowa. Dziękuję Ci.
bool volatile keepRunning = true;
w 100% bezpieczny. Kompilator może swobodnie buforować keepRunning
w rejestrze, a ulotność temu zapobiegnie. W praktyce najprawdopodobniej może również działać bez słowa kluczowego volatile, gdy pętla while wywołuje co najmniej jedną funkcję nieliniową.
sig_atomic_t
albo atomic_bool
wpiszę. Właśnie to przegapiłem. Skoro już rozmawiamy: czy mam wycofać moją ostatnią edycję? Żadnych urazy, byłoby to całkowicie zrozumiałe z twojego punktu widzenia :)
Sprawdź tutaj:
Uwaga: Oczywiście, jest to prosty przykład wyjaśniający tylko jak uruchamiamyCtrlC program obsługi, ale jak zawsze istnieją zasady, które muszą być przestrzegane, aby nie złamać coś innego. Przeczytaj poniższe komentarze.
Przykładowy kod z góry:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void INThandler(int);
int main(void)
{
signal(SIGINT, INThandler);
while (1)
pause();
return 0;
}
void INThandler(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
}
int main
to właściwa rzecz, ale gcc
i inne kompilatory kompilują to do poprawnie działających programów od lat 90-tych. Całkiem dobrze wyjaśniono tutaj: eskimo.com/~scs/readings/voidmain.960823.html - to po prostu „funkcja”, tak to odbieram .
Dodatek dotyczący platform UN * X.
Zgodnie ze signal(2)
stroną podręcznika systemowego dotyczącą systemu GNU / Linux zachowanie signal
nie jest tak przenośne, jak zachowanie sigaction
:
Zachowanie signal () różni się w różnych wersjach systemu UNIX, a także różniło się w przeszłości w różnych wersjach Linuksa. Unikaj jego używania: zamiast tego użyj sigaction (2).
W Systemie V system nie blokował dostarczania kolejnych wystąpień sygnału, a dostarczenie sygnału zresetowałoby program obsługi do domyślnego. W BSD zmieniła się semantyka.
Następująca odmiana poprzedniej odpowiedzi autorstwa Dirka Eddelbuettela używa sigaction
zamiast signal
:
#include <signal.h>
#include <stdlib.h>
static bool keepRunning = true;
void intHandler(int) {
keepRunning = false;
}
int main(int argc, char *argv[]) {
struct sigaction act;
act.sa_handler = intHandler;
sigaction(SIGINT, &act, NULL);
while (keepRunning) {
// main loop
}
}
Lub możesz ustawić terminal w trybie surowym, na przykład:
struct termios term;
term.c_iflag |= IGNBRK;
term.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF);
term.c_lflag &= ~(ICANON | ECHO | ECHOK | ECHOE | ECHONL | ISIG | IEXTEN);
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
tcsetattr(fileno(stdin), TCSANOW, &term);
Teraz powinno być możliwe odczytanie Ctrl+ Cnaciśnięć klawiszy za pomocą fgetc(stdin)
. Uważaj jednak na używanie tego, ponieważ nie możesz już Ctrl+ Z, Ctrl+ Q, Ctrl+ Sitd. Jak zwykle.
Ustaw pułapkę (możesz złapać kilka sygnałów jednym przewodnikiem):
sygnał (SIGQUIT, my_handler); sygnał (SIGINT, my_handler);
Obsługuj sygnał, jak chcesz, ale pamiętaj o ograniczeniach i pułapkach:
void my_handler (int sig) { / * Twój kod tutaj. * / }
@Peter Varo zaktualizował odpowiedź Dirka, ale Dirk odrzucił zmianę. Oto nowa odpowiedź Petera:
Chociaż powyższy fragment kodu jest poprawny c89Przykładowo, w miarę możliwości należy korzystać z nowocześniejszych typów i gwarancji oferowanych przez późniejsze standardy. Dlatego tutaj jest bezpieczniejsza i nowoczesna alternatywa dla tych, którzy szukając99 i c11 zgodna implementacja:
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
static volatile sig_atomic_t keep_running = 1;
static void 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;
}
C11 Standard: 7.14§2 Nagłówek
<signal.h>
deklaruje typ ...sig_atomic_t
będący (prawdopodobnie zmienną kwalifikowaną) liczbą całkowitą obiektu, do którego można uzyskać dostęp jako niepodzielna jednostka, nawet w obecności przerwań asynchronicznych.
Ponadto:
C11 Standard: 7.14.1.1§5 Jeśli sygnał występuje inaczej niż w wyniku wywołania
abort
lubraise
funkcji , zachowanie jest niezdefiniowane, jeśli program obsługi sygnału odnosi się do dowolnego obiektu zstatic
czasem przechowywania wątków, który nie jest niepodzielnym obiektem bez blokady inny niż przez przypisanie wartości do obiektu zadeklarowanego jakovolatile sig_atomic_t
...
(void)_;
... Jaki jest jego cel? Czy to dlatego, że kompilator nie ostrzega o nieużywanej zmiennej?
Jeśli chodzi o istniejące odpowiedzi, zwróć uwagę, że obsługa sygnału zależy od platformy. Na przykład Win32 obsługuje znacznie mniej sygnałów niż systemy operacyjne POSIX; zobacz tutaj . Chociaż SIGINT jest zadeklarowany w pliku signal.h na Win32, zobacz uwagę w dokumentacji, która wyjaśnia, że nie zrobi tego, czego można się spodziewać.
#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 process
while(1)
sleep(1);
return 0;
}
Funkcja sig_handler sprawdza, czy wartość przekazanego argumentu jest równa SIGINT, a następnie wykonywany jest printf.
To po prostu wydrukuj przed wyjściem.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void sigint_handler(int);
int main(void)
{
signal(SIGINT, sigint_handler);
while (1){
pause();
}
return 0;
}
void sigint_handler(int sig)
{
/*do something*/
printf("killing process %d\n",getpid());
exit(0);
}