Zuerst war ich durch die Antwort von Jancheta verwirrt, bis ich entdeckte, dass der Zweck von siglongjmp
darin besteht, das empfangene Signal in der Signalmaske zu entsperren, bevor der Sprung ausgeführt wird. Das Signal wird am Eingang des Signal-Handlers blockiert, so dass sich der Handler nicht selbst unterbricht. Wir wollen das Signal nicht blockieren, wenn wir die normale Ausführung fortsetzen, und deshalb verwenden wir siglongjmp
anstelle von longjmp
. AIUI, das ist nur Kurzschrift, wir könnten auch sigprocmask
gefolgt von longjmp
aufrufen, was zu sein scheint, was glibc in siglongjmp
tut.
Ich dachte, es könnte unsicher sein, einen Sprung zu tun, weil readline()
Anrufe malloc
und free
. Wenn das Signal empfangen wird, während eine asynchrone Signal-unsichere Funktion wie malloc
oder free
den globalen Zustand verändert, könnte es zu einer gewissen Beschädigung kommen, wenn wir dann aus dem Signal-Handler herausspringen würden. Aber Readline installiert seine eigenen Signalhandler, die darauf achten. Sie haben nur eine Flagge gesetzt und gehen ab; Wenn die Readline-Bibliothek die Kontrolle wieder erlangt (normalerweise nach einem unterbrochenen "read()" -Aufruf), ruft sie RL_CHECK_SIGNALS()
auf, die dann ein anstehendes Signal unter Verwendung von kill()
an die Client-Anwendung weiterleitet. Es ist also sicher, siglongjmp()
zu verwenden, um einen Signalhandler für ein Signal zu verlassen, das einen Anruf zu unterbrochen hat - das Signal wird garantiert nicht während einer async-Signal-unsicheren Funktion empfangen worden sein.
Eigentlich ist das nicht ganz richtig, weil es ein paar Anrufe zu malloc()
und free()
innerhalb rl_set_prompt()
sind, die readline()
Anrufe kurz vor rl_set_signals()
. Ich frage mich, ob diese Rufreihenfolge geändert werden sollte. In jedem Fall ist die Wahrscheinlichkeit einer Race Condition sehr gering.
Ich schaute auf den Bash-Quellcode und es scheint aus seinem SIGINT-Handler zu springen.
Eine andere Readline-Schnittstelle, die Sie verwenden können, ist die Callback-Schnittstelle. Dies wird von Anwendungen wie Python oder R verwendet, die mehrere Dateideskriptoren gleichzeitig abhören müssen, um z. B. festzustellen, ob die Größe eines Plotfensters geändert wird, während die Befehlszeilenschnittstelle aktiv ist. Sie tun dies in einer select()
Schleife.
Hier eine Nachricht von Chet Ramey, die einige Ideen davon, was zu tun Bash-ähnliches Verhalten zu erhalten, nach dem Empfang SIGINT in der Callback-Schnittstelle: schlägt
https://lists.gnu.org/archive/html/bug-readline/2016-04/msg00071.html
Die Nachrichten, dass Sie etwas tun, wie dies:
rl_free_line_state();
rl_cleanup_after_signal();
RL_UNSETSTATE(RL_STATE_ISEARCH|RL_STATE_NSEARCH|RL_STATE_VIMOTION|RL_STATE_NUMERICARG|RL_STATE_MULTIKEY);
rl_line_buffer[rl_point = rl_end = rl_mark = 0] = 0;
printf("\n");
Wenn Ihr SIGINT empfangen wird, können Sie ein Flag gesetzt könnte, und versuchen Sie es später die Flagge in Ihrer select()
Schleife - da die select()
Anruf erhalten unterbrochen wird durch das Signal mit errno==EINTR
. Wenn Sie feststellen, dass das Flag gesetzt wurde, führen Sie den obigen Code aus.
Meine Meinung ist, dass Readline etwas wie das obige Fragment in seinem eigenen SIGINT-Handling-Code ausführen sollte. Derzeit führt es mehr oder weniger nur die ersten zwei Zeilen aus, weshalb Dinge wie inkrementelle Such- und Tastaturmakros durch^C abgebrochen werden, aber die Zeile wird nicht gelöscht.
Ein anderes Plakat sagte "Call rl_clear_signals()", die mich immer noch verwirrt. Ich habe es nicht ausprobiert, aber ich sehe nicht, wie es irgendwas erreichen würde, vorausgesetzt, dass (1) die Signalhandler von Readline das Signal sowieso weiterleiten, und (2) readline()
installiert die Signalhandler bei der Eingabe (und löscht sie, wenn sie beendet wird)), so dass sie normalerweise außerhalb des Readline-Codes nicht aktiv sind.
Sie können 'printf' nicht sicher von einem Signalhandler aufrufen. – pat