Prevenzione della propagazione di SIGINT nel processo principale


10

Considerando uno scenario in cui un programma Parent (potrebbe essere un programma C ++ o uno script di shell) esegue uno script di shell figlio, quando si preme Control + C (o qualunque carattere sia configurato per essere il carattere INTR) mentre lo script di shell figlio è in esecuzione, un SIGINT viene inviato a tutti i processi nel gruppo di processi in primo piano. Ciò include il processo padre.

Fonte: POSIX.1-2008 XBD sezione 11.1.9

C'è un modo per ignorare questo comportamento predefinito? Che solo il processo BAMBINO gestisce il SEGNALE senza che si propaghi al genitore?

Riferimento: Stack Overflow Post - Processo principale non completato quando Child viene interrotto (TRAP INT)

Risposte:


11

(Ispirato dalla risposta di Gilles)

Con il ISIGflag impostato, l'unico modo per Childottenere lo script SIGINTsenza il suo genitore SIGINTè che sia nel suo gruppo di processi. Questo può essere realizzato con l' set -mopzione.

Se si attiva l' -mopzione nello Childscript della shell, eseguirà il controllo del lavoro senza essere interattivo. Ciò causerà l'esecuzione di elementi in un gruppo di processi separato, impedendo al genitore di ricevere il messaggio SIGINTquando il INTRpersonaggio viene letto.

Ecco la descrizione POSIX -mdell'opzione :

-mQuesta opzione è supportata se l'implementazione supporta l'opzione Utilità di portabilità dell'utente. Tutti i lavori devono essere eseguiti nei rispettivi gruppi di processi. Immediatamente prima che la shell emetta un prompt dopo il completamento del processo in background, un messaggio che riporta lo stato di uscita del processo in background deve essere scritto nell'errore standard. Se un lavoro in primo piano si interrompe, la shell deve scrivere un messaggio di errore standard in tal senso, formattato come descritto dall'utilità lavori. Inoltre, se un lavoro cambia stato diverso dall'uscita (ad esempio, se si arresta per input o output o viene interrotto da un segnale SIGSTOP), la shell deve scrivere un messaggio simile immediatamente prima di scrivere il prompt successivo. Questa opzione è abilitata per impostazione predefinita per le shell interattive.

L' -mopzione è simile a -i, ma non altera il comportamento della shell quasi quanto -ifa.

Esempio:

  • la Parentsceneggiatura:

    #!/bin/sh
    
    trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
    
    echo "PARENT: pid=$$"
    echo "PARENT: Spawning child..."
    ./Child
    echo "PARENT: child returned"
    echo "PARENT: exiting normally"
  • la Childsceneggiatura:

    #!/bin/sh -m
    #         ^^        
    # notice the -m option above!
    
    trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
    
    echo "CHILD: pid=$$"
    echo "CHILD: hit enter to exit"
    read foo
    echo "CHILD: exiting normally"

Questo è ciò che accade quando si preme Control+ Cmentre Childè in attesa di input:

$ ./Parent
PARENT: pid=12233
PARENT: Spawning child...
CHILD: pid=12234
CHILD: hit enter to exit
^CCHILD: caught SIGINT; exiting
PARENT: child returned
PARENT: exiting normally

Nota come il SIGINTgestore del genitore non viene mai eseguito.

In alternativa, se preferisci modificare Parentinvece di Child, puoi farlo:

  • la Parentsceneggiatura:

    #!/bin/sh
    
    trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
    
    echo "PARENT: pid=$$"
    echo "PARENT: Spawning child..."
    sh -m ./Child  # or 'sh -m -c ./Child' if Child isn't a shell script
    echo "PARENT: child returned"
    echo "PARENT: exiting normally"
  • lo Childscript (normale; non è necessario -m):

    #!/bin/sh
    
    trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
    
    echo "CHILD: pid=$$"
    echo "CHILD: hit enter to exit"
    read foo
    echo "CHILD: exiting normally"

Idee alternative

  1. Modificare gli altri processi nel gruppo di processi in primo piano da ignorare SIGINTper la durata di Child. Questo non risponde alla tua domanda, ma può darti quello che vuoi.
  2. Modifica Childper:
    1. Utilizzare stty -gper eseguire il backup delle impostazioni correnti del terminale.
    2. Eseguire stty -isigper non generare segnali con i INTR, QUITe SUSPcaratteri.
    3. Nei precedenti, leggere l'input terminale e inviare i segnali stessi come appropriato (ad esempio, eseguire kill -QUIT 0quando Control+ \viene letto, kill -INT $$quando Control+ Cviene letto). Questo non è banale e potrebbe non essere possibile farlo funzionare senza problemi se lo Childscript o qualsiasi altra cosa venga eseguita è pensata per essere interattiva.
    4. Ripristina le impostazioni del terminale prima di uscire (idealmente da una trappola attivata EXIT).
  3. Come il n. 2 tranne che in esecuzione stty -isig, attendere che l'utente prema Entero qualche altra chiave non speciale prima di uccidere Child.
  4. Scrivi la tua setpgidutility in C, Python, Perl, ecc. Che puoi usare per chiamare setpgid(). Ecco un'implementazione di C grezza:

    #define _XOPEN_SOURCE 700
    #include <unistd.h>
    #include <signal.h>
    
    int
    main(int argc, char *argv[])
    {
        // todo: add error checking
        void (*backup)(int);
        setpgid(0, 0);
        backup = signal(SIGTTOU, SIG_IGN);
        tcsetpgrp(0, getpid());
        signal(SIGTTOU, backup);
        execvp(argv[1], argv + 1);
        return 1;
    }

    Esempio di utilizzo da Child:

    #!/bin/sh
    
    [ "${DID_SETPGID}" = true ] || {
        # restart self after calling setpgid(0, 0)
        exec env DID_SETPGID=true setpgid "$0" "$@"
        # exec failed if control reached this point
        exit 1
    }
    unset DID_SETPGID
    
    # do stuff here

4

Come spiega il capitolo citato da POSIX, SIGINT viene inviato all'intero gruppo di processi in primo piano. Pertanto, per evitare di uccidere il genitore del programma, predisporre l'esecuzione nel proprio gruppo di processi.

Le shell non danno accesso setpgrptramite un costrutto incorporato o sintattico, ma c'è un modo indiretto per raggiungerlo, che è quello di eseguire la shell in modo interattivo. (Grazie a Stéphane Gimenez per il trucco.)

ksh -ic '
  … the part that needs to be interruptible without bothering the parent …
'

+1 per l'idea intelligente, anche se attenzione che il comportamento della shell cambia quando si tratta di una shell interattiva (almeno la shell POSIX fa; non ho familiarità con i dettagli di ksh). Esempi: ${ENV}è di provenienza, la shell non si chiude immediatamente quando rileva un errore SIGQUITe SIGTERMviene ignorata.
Richard Hansen,

1

Bene, dalla domanda Stack Overflow a cui hai fatto riferimento afferma chiaramente che il genitore deve essere configurato per gestire il segnale.

In secondo luogo, il riferimento POSIX afferma chiaramente che "Se è impostato ISIG, il carattere INTR deve essere scartato durante l'elaborazione".

Quindi sono due opzioni. Il terzo sarebbe quello di eseguire il figlio nel proprio gruppo di processi.


Grazie per la risposta. Devo anche trovare un modo per far uscire l'utente dallo script. C'è un modo per impostare ISIG e allo stesso tempo consentire ad alcune combinazioni di tasti CONTRL (potrebbe essere CTRL-Q) di uscire dallo script della shell senza inviare segnali al genitore e allo stesso modo? Inoltre, potresti dirmi come posso eseguire il bambino in un gruppo di processi diverso rispetto al suo genitore?
Guddu,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.