Inizialmente ero confuso dalla risposta di jancheta, finché non ho scoperto che lo scopo di siglongjmp
è quello di sbloccare il segnale ricevuto nella maschera del segnale, prima di fare il salto. Il segnale è bloccato all'ingresso del gestore di segnale in modo che il gestore non si interrompa. Non vogliamo lasciare il segnale bloccato quando riprendiamo la normale esecuzione, ed è per questo che usiamo siglongjmp
invece di longjmp
. AIUI, questa è solo una scorciatoia, potremmo anche chiamare sigprocmask
seguito da longjmp
, che sembra essere quello che sta facendo glibc in siglongjmp
.
Ho pensato che potrebbe essere pericoloso fare un salto perché readline()
chiamate malloc
e free
. Se il segnale viene ricevuto mentre una funzione non sicura del segnale asincrono come malloc
o free
sta modificando lo stato globale, potrebbe verificarsi un danneggiamento se dovessimo saltare fuori dal gestore del segnale. Ma Readline installa i propri gestori di segnali che sono attenti a questo. Hanno appena impostato una bandiera ed escono; quando la libreria Readline riprende il controllo (di solito dopo una chiamata "read()" interrotta) chiama RL_CHECK_SIGNALS()
che poi inoltra qualsiasi segnale in sospeso all'applicazione client utilizzando kill()
. Quindi è sicuro usare siglongjmp()
per uscire da un gestore di segnale per un segnale che ha interrotto una chiamata a readline()
- il segnale è garantito che non è stato ricevuto durante una funzione non sicura del segnale asincrono.
In realtà, non è del tutto vero, perché ci sono un paio di chiamate a malloc()
e free()
all'interno rl_set_prompt()
, che readline()
chiamate subito prima rl_set_signals()
. Mi chiedo se questo ordine chiamante debba essere cambiato. In ogni caso la probabilità di condizioni di gara è molto ridotta.
Ho guardato il codice sorgente Bash e sembra saltare fuori dal suo gestore SIGINT.
Un'altra interfaccia di Readline che è possibile utilizzare è l'interfaccia di callback. Questo è usato da applicazioni come Python o R che hanno bisogno di ascoltare su più descrittori di file contemporaneamente, ad esempio per dire se una finestra di trama viene ridimensionata mentre l'interfaccia della riga di comando è attiva. Lo faranno in un ciclo select()
.
Ecco un messaggio da Chet Ramey che dà qualche idea di che cosa fare per ottenere un comportamento Bash simile dopo aver ricevuto SIGINT nell'interfaccia di callback:
https://lists.gnu.org/archive/html/bug-readline/2016-04/msg00071.html
I messaggi suggerisce che si fa qualcosa di simile questo:
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");
Quando si riceve la vostra SIGINT, è possibile impostare una bandiera, e poi controllare il flag nella tua select()
ciclo - dal momento che la chiamata select()
otterrà interrotto dal segnale con errno==EINTR
. Se trovi che il flag è stato impostato, esegui il codice sopra.
La mia opinione è che Readline dovrebbe eseguire qualcosa come il frammento sopra nel proprio codice di gestione SIGINT. Attualmente esegue più o meno solo le prime due righe, motivo per cui roba simile alla ricerca incrementale e ai macro della tastiera sono cancellate da^C, ma la riga non viene cancellata.
Un altro poster diceva "Chiama rl_clear_signals()", che ancora mi confonde. Non l'ho provato, ma non vedo come potrebbe ottenere nulla dato che (1) I gestori del segnale di Readline inoltrano il segnale comunque, e (2) readline()
installa i gestori di segnale all'entrata (e li cancella quando esce), quindi normalmente non saranno attivi al di fuori del codice Readline.
Non è possibile chiamare in modo sicuro 'printf' da un gestore di segnale. – pat