Come evitare di utilizzare printf in un gestore di segnali?


86

Poiché printfnon è rientrante, non dovrebbe essere sicuro usarlo in un gestore di segnali. Ma ho visto molti codici di esempio che usano in printfquesto modo.

Quindi la mia domanda è: quando dobbiamo evitare di utilizzare printfun gestore di segnali e c'è una sostituzione consigliata?


12
Una risposta semplice e poco utile alla domanda nel titolo: vedi quella printfchiamata in quel segnale? Cancellalo.
Keith Thompson

6
Ciao Yu Hao! Penso che troverai link molto interessanti da leggere. "Usa funzioni rientranti per una gestione del segnale più sicura" L' ho letto dopo così tanto tempo, vorrei condividere l'artificiale qui con te. Spero ti diverta.
Grijesh Chauhan

Risposte:


58

È possibile utilizzare alcune variabili flag, impostare tale flag all'interno del gestore del segnale e basarsi su quella printf()funzione di chiamata flag in main () o in un'altra parte del programma durante il normale funzionamento.

Non è sicuro chiamare tutte le funzioni, ad esempio printf, dall'interno di un gestore di segnali. Una tecnica utile consiste nell'usare un gestore di segnali per impostare a, flagquindi verificarlo flag dal programma principale e stampare un messaggio se necessario.

Si noti nell'esempio seguente, il gestore del segnale ding () imposta un flag alarm_firedsu 1 quando SIGALRM viene rilevato e nella funzione principale alarm_firedviene esaminato il valore per chiamare in modo condizionale printf correttamente.

static int alarm_fired = 0;
void ding(int sig) // can be called asynchronously
{
  alarm_fired = 1; // set flag
}
int main()
{
    pid_t pid;
    printf("alarm application starting\n");
    pid = fork();
    switch(pid) {
        case -1:
            /* Failure */
            perror("fork failed");
            exit(1);
        case 0:
            /* child */
            sleep(5);
            kill(getppid(), SIGALRM);
            exit(0);
    }
    /* if we get here we are the parent process */
    printf("waiting for alarm to go off\n");
    (void) signal(SIGALRM, ding);
    pause();
    if (alarm_fired)  // check flag to call printf
      printf("Ding!\n");
    printf("done\n");
    exit(0);
}

Riferimento: Beginning Linux Programming, 4th Edition , In questo libro viene spiegato esattamente il tuo codice (cosa vuoi), Capitolo 11: Processi e segnali, pagina 484

Inoltre, è necessario prestare particolare attenzione nella scrittura delle funzioni del gestore perché possono essere chiamate in modo asincrono. Cioè, un gestore potrebbe essere chiamato in qualsiasi punto del programma, in modo imprevedibile. Se due segnali arrivano durante un intervallo molto breve, un gestore può essere eseguito all'interno di un altro. Ed è considerata una pratica migliore da dichiarare volatile sigatomic_t, questo tipo è sempre accessibile in modo atomico, evita l'incertezza sull'interruzione dell'accesso a una variabile. (leggi: Accesso ai dati atomici e gestione del segnale per l'espiazione dei dettagli).

Leggi Definizione di gestori di segnali : per imparare a scrivere una funzione di gestore di segnali che può essere stabilita con le funzioni signal()o sigaction().
Elenco delle funzioni autorizzate nella pagina di manuale , chiamare questa funzione all'interno del gestore del segnale è sicuro.


18
È considerata una pratica migliore dichiararevolatile sigatomic_t alarm_fired;
Basile Starynkevitch il


1
@GrijeshChauhan: se stiamo lavorando in un codice di prodotto, non possiamo chiamare la funzione di pausa, il flusso può essere ovunque quando si verifica il segnale, quindi in quel caso non sappiamo davvero dove tenere "if (alarm_fired) printf (" Ding! \ n ");" in codice.
pankaj kushwaha

@pankajkushwaha sì, hai ragione, soffre di condizioni di gara
Grijesh Chauhan

@GrijeshChauhan, ci sono due cose che non sono riuscito a capire. 1. Come fai a sapere quando controllare la bandiera? Quindi ci saranno più punti di controllo nel codice in quasi ogni punto da stampare. 2. Ci saranno sicuramente condizioni di gara in cui il segnale potrebbe essere chiamato prima della registrazione del segnale o il segnale potrebbe verificarsi dopo il punto di controllo. Penso che questo aiuterà la stampa solo in alcune condizioni ma non risolverà completamente il problema.
Darshan b

52

Il problema principale è che se il segnale si interrompe malloc()o una funzione simile, lo stato interno può essere temporaneamente incoerente mentre sta spostando blocchi di memoria tra l'elenco libero e quello utilizzato, o altre operazioni simili. Se il codice nel gestore del segnale chiama una funzione che poi invoca malloc(), ciò potrebbe rovinare completamente la gestione della memoria.

Lo standard C ha una visione molto conservativa di ciò che puoi fare in un gestore di segnali:

ISO / IEC 9899: 2011 §7.14.1.1 La signalfunzione

¶5 Se il segnale si verifica in modo diverso dal risultato della chiamata della funzione aborto raise, il comportamento non è definito se il gestore del segnale fa riferimento a qualsiasi oggetto con durata di archiviazione statica o thread che non è un oggetto atomico privo di blocco se non assegnando un valore a un oggetto dichiarato come volatile sig_atomic_t, o il gestore del segnale chiama qualsiasi funzione nella libreria standard diversa dalla abortfunzione, la _Exitfunzione, la quick_exitfunzione o la signalfunzione con il primo argomento uguale al numero del segnale corrispondente al segnale che ha causato l'invocazione del gestore. Inoltre, se tale chiamata alla signalfunzione SIG_ERRrestituisce un ritorno, il valore di errnoè indeterminato. 252)

252) Se un qualsiasi segnale è generato da un gestore di segnali asincrono, il comportamento è indefinito.

POSIX è molto più generoso su ciò che puoi fare in un gestore di segnali.

Signal Concepts nell'edizione POSIX 2008 dice:

Se il processo è multi-thread o se il processo è a thread singolo e viene eseguito un gestore del segnale diverso dal risultato di:

  • La chiamata processo abort(), raise(), kill(), pthread_kill(), o sigqueue()per generare un segnale non bloccato

  • Un segnale in sospeso che viene sbloccato e consegnato prima della chiamata che lo ha sbloccato ritorna

il comportamento è indefinito se il gestore del segnale fa riferimento a qualsiasi oggetto diverso da errnouna durata di memorizzazione statica diversa dall'assegnazione di un valore a un oggetto dichiarato come volatile sig_atomic_t, o se il gestore del segnale chiama una funzione definita in questo standard diversa da una delle funzioni elencate in la tabella seguente.

La tabella seguente definisce un insieme di funzioni che devono essere a prova di segnale asincrono. Pertanto, le applicazioni possono richiamarli, senza restrizioni, dalle funzioni di cattura del segnale:

_Exit()             fexecve()           posix_trace_event() sigprocmask()
_exit()             fork()              pselect()           sigqueue()
…
fcntl()             pipe()              sigpause()          write()
fdatasync()         poll()              sigpending()

Tutte le funzioni non presenti nella tabella sopra sono considerate non sicure rispetto ai segnali. In presenza di segnali, tutte le funzioni definite da questo volume di POSIX.1-2008 devono comportarsi come definito quando chiamate da o interrotte da una funzione di cattura del segnale, con una sola eccezione: quando un segnale interrompe una funzione non sicura e il segnale- la funzione di cattura chiama una funzione non sicura, il comportamento non è definito.

Le operazioni che ottengono il valore di errnoe le operazioni a cui assegnano un valore errnodevono essere a prova di segnale asincrono.

Quando un segnale viene consegnato a un thread, se l'azione di quel segnale specifica la terminazione, l'arresto o la continuazione, l'intero processo deve essere rispettivamente terminato, interrotto o continuato.

Tuttavia, la printf()famiglia di funzioni è notevolmente assente da tale elenco e potrebbe non essere chiamata in modo sicuro da un gestore di segnali.

L' aggiornamento POSIX 2016 estende l'elenco delle funzioni sicure per includere, in particolare, un gran numero di funzioni da <string.h>, che è un'aggiunta particolarmente preziosa (o è stata una supervisione particolarmente frustrante). L'elenco è ora:

_Exit()              getppid()            sendmsg()            tcgetpgrp()
_exit()              getsockname()        sendto()             tcsendbreak()
abort()              getsockopt()         setgid()             tcsetattr()
accept()             getuid()             setpgid()            tcsetpgrp()
access()             htonl()              setsid()             time()
aio_error()          htons()              setsockopt()         timer_getoverrun()
aio_return()         kill()               setuid()             timer_gettime()
aio_suspend()        link()               shutdown()           timer_settime()
alarm()              linkat()             sigaction()          times()
bind()               listen()             sigaddset()          umask()
cfgetispeed()        longjmp()            sigdelset()          uname()
cfgetospeed()        lseek()              sigemptyset()        unlink()
cfsetispeed()        lstat()              sigfillset()         unlinkat()
cfsetospeed()        memccpy()            sigismember()        utime()
chdir()              memchr()             siglongjmp()         utimensat()
chmod()              memcmp()             signal()             utimes()
chown()              memcpy()             sigpause()           wait()
clock_gettime()      memmove()            sigpending()         waitpid()
close()              memset()             sigprocmask()        wcpcpy()
connect()            mkdir()              sigqueue()           wcpncpy()
creat()              mkdirat()            sigset()             wcscat()
dup()                mkfifo()             sigsuspend()         wcschr()
dup2()               mkfifoat()           sleep()              wcscmp()
execl()              mknod()              sockatmark()         wcscpy()
execle()             mknodat()            socket()             wcscspn()
execv()              ntohl()              socketpair()         wcslen()
execve()             ntohs()              stat()               wcsncat()
faccessat()          open()               stpcpy()             wcsncmp()
fchdir()             openat()             stpncpy()            wcsncpy()
fchmod()             pause()              strcat()             wcsnlen()
fchmodat()           pipe()               strchr()             wcspbrk()
fchown()             poll()               strcmp()             wcsrchr()
fchownat()           posix_trace_event()  strcpy()             wcsspn()
fcntl()              pselect()            strcspn()            wcsstr()
fdatasync()          pthread_kill()       strlen()             wcstok()
fexecve()            pthread_self()       strncat()            wmemchr()
ffs()                pthread_sigmask()    strncmp()            wmemcmp()
fork()               raise()              strncpy()            wmemcpy()
fstat()              read()               strnlen()            wmemmove()
fstatat()            readlink()           strpbrk()            wmemset()
fsync()              readlinkat()         strrchr()            write()
ftruncate()          recv()               strspn()
futimens()           recvfrom()           strstr()
getegid()            recvmsg()            strtok_r()
geteuid()            rename()             symlink()
getgid()             renameat()           symlinkat()
getgroups()          rmdir()              tcdrain()
getpeername()        select()             tcflow()
getpgrp()            sem_post()           tcflush()
getpid()             send()               tcgetattr()

Di conseguenza, o si finisce per utilizzare write()senza il supporto di formattazione fornito da printf()et al, oppure si finisce per impostare un flag che si verifica (periodicamente) in punti appropriati del codice. Questa tecnica è abilmente dimostrata nella risposta di Grijesh Chauhan .


Funzioni C standard e sicurezza del segnale

chqrlie pone una domanda interessante, alla quale non ho altro che una risposta parziale:

Come mai la maggior parte delle funzioni di stringa <string.h>o delle funzioni di classe di caratteri <ctype.h>e molte altre funzioni della libreria standard C non sono nell'elenco sopra? Un'implementazione dovrebbe essere volutamente malvagia per rendere strlen()pericoloso chiamare da un gestore di segnali.

Per molte delle funzioni in <string.h>, è difficile capire perché essi non sono stati dichiarati sicuri async-signal, e io sono d'accordo il strlen()è un ottimo esempio, insieme strchr(), strstr()ecc D'altra parte, altre funzioni come strtok(), strcoll()e strxfrm()sono piuttosto complessi e probabilmente non sono sicuri per il segnale asincrono. Perché strtok()conserva lo stato tra le chiamate e il gestore del segnale non potrebbe facilmente dire se una parte del codice che sta usando strtok()sarebbe incasinata. Le funzioni strcoll()e strxfrm()funzionano con dati sensibili alla locale e il caricamento della locale implica tutti i tipi di impostazione dello stato.

Le funzioni (macro) di <ctype.h>sono tutte sensibili alle impostazioni locali e pertanto potrebbero incorrere negli stessi problemi di strcoll()e strxfrm().

Trovo difficile capire perché le funzioni matematiche di <math.h>non siano sicure del segnale asincrono, a meno che non sia perché potrebbero essere influenzate da un SIGFPE (eccezione in virgola mobile), anche se quasi l'unica volta che vedo una di quelle in questi giorni è per intero divisione per zero. Un'incertezza simile deriva da <complex.h>, <fenv.h>e <tgmath.h>.

Alcune delle funzioni in <stdlib.h>potrebbero essere esentate, abs()ad esempio. Altri sono particolarmente problematici: malloc()e la famiglia ne è il primo esempio.

Una valutazione simile potrebbe essere fatta per le altre intestazioni nello Standard C (2011) utilizzate in un ambiente POSIX. (Lo Standard C è così restrittivo che non c'è alcun interesse ad analizzarli in un ambiente Standard C puro.) Quelli contrassegnati come 'dipendenti dalle impostazioni locali' non sono sicuri perché la manipolazione delle impostazioni locali potrebbe richiedere l'allocazione della memoria, ecc.

  • <assert.h>- Probabilmente non è sicuro
  • <complex.h>- Forse al sicuro
  • <ctype.h> - Non sicuro
  • <errno.h> - Sicuro
  • <fenv.h>- Probabilmente non è sicuro
  • <float.h> - Nessuna funzione
  • <inttypes.h> - Funzioni sensibili alle impostazioni locali (non sicure)
  • <iso646.h> - Nessuna funzione
  • <limits.h> - Nessuna funzione
  • <locale.h> - Funzioni sensibili alle impostazioni locali (non sicure)
  • <math.h>- Forse al sicuro
  • <setjmp.h> - Non sicuro
  • <signal.h> - Permesso
  • <stdalign.h> - Nessuna funzione
  • <stdarg.h> - Nessuna funzione
  • <stdatomic.h>- Forse al sicuro, probabilmente non al sicuro
  • <stdbool.h> - Nessuna funzione
  • <stddef.h> - Nessuna funzione
  • <stdint.h> - Nessuna funzione
  • <stdio.h> - Non sicuro
  • <stdlib.h> - Non tutti sicuri (alcuni sono consentiti, altri no)
  • <stdnoreturn.h> - Nessuna funzione
  • <string.h> - Non tutti al sicuro
  • <tgmath.h>- Forse al sicuro
  • <threads.h>- Probabilmente non è sicuro
  • <time.h>- Dipende dalle impostazioni locali (ma time()è esplicitamente consentito)
  • <uchar.h> - Dipende dalle impostazioni locali
  • <wchar.h> - Dipende dalle impostazioni locali
  • <wctype.h> - Dipende dalle impostazioni locali

Analizzare le intestazioni POSIX sarebbe ... più difficile in quanto ce ne sono molte, e alcune funzioni potrebbero essere sicure ma molte non lo saranno ... ma anche più semplice perché POSIX dice quali funzioni sono sicure per il segnale asincrono (non molte di esse). Nota che un'intestazione come <pthread.h>ha tre funzioni sicure e molte funzioni non sicure.

NB: Quasi tutta la valutazione delle funzioni C e delle intestazioni in un ambiente POSIX è un'ipotesi semi-istruita. Non ha senso una dichiarazione definitiva di un organismo di normalizzazione.


Come mai la maggior parte delle funzioni di stringa <string.h>o delle funzioni di classe di caratteri <ctype.h>e molte altre funzioni della libreria standard C non sono nell'elenco sopra? Un'implementazione dovrebbe essere volutamente malvagia per rendere strlen()pericoloso chiamare da un gestore di segnali.
chqrlie

@chqrlie: domanda interessante - guarda l'aggiornamento (non c'era modo di inserire così tanto nei commenti in modo sensato).
Jonathan Leffler

Grazie per la tua analisi approfondita. Per quanto riguarda il <ctype.h>materiale, è specifico del locale e potrebbe causare problemi se il segnale interrompe una funzione di impostazione del locale, ma una volta caricato il locale, il loro utilizzo dovrebbe essere sicuro. Immagino che, in alcune situazioni complesse, il caricamento dei dati delle impostazioni locali possa essere eseguito in modo incrementale, rendendo così le funzioni <ctype.h>non sicure. La conclusione rimane: in caso di dubbio, astenersi.
chqrlie

@chqrlie: Sono d'accordo che la morale della storia dovrebbe essere In caso di dubbio, astenersi . Questo è un bel riassunto.
Jonathan Leffler

13

Come evitare di utilizzare printfin un gestore di segnali?

  1. Evitalo sempre, dirà: semplicemente non usare printf()nei gestori di segnali.

  2. Almeno su sistemi conformi a POSIX, puoi usare al write(STDOUT_FILENO, ...)posto di printf(). Tuttavia, la formattazione potrebbe non essere facile: stampa int dal gestore del segnale utilizzando funzioni di scrittura o di sicurezza asincrona


1
Alk Always avoid it.significa? Evitare printf()?
Grijesh Chauhan

2
@GrijeshChauhan: Sì, poiché l'OP chiedeva quando evitare di utilizzare i printf()gestori di segnale.
alk

Alk +1 per il 2punto, controlla OP chiedendo come evitare di utilizzare printf()nei gestori di segnale?
Grijesh Chauhan

7

A scopo di debug, ho scritto uno strumento che verifica che in realtà stai chiamando solo funzioni async-signal-safenell'elenco e stampa un messaggio di avviso per ogni funzione non sicura chiamata all'interno di un contesto di segnale. Sebbene non risolva il problema di voler chiamare funzioni non asincrone da un contesto di segnale, almeno ti aiuta a trovare casi in cui lo hai fatto accidentalmente.

Il codice sorgente è su GitHub . Funziona sovraccaricando signal/sigaction, quindi dirottando temporaneamente le PLTvoci di funzioni non sicure; questo fa sì che le chiamate a funzioni non sicure vengano reindirizzate a un wrapper.



1

Implementa il tuo segnale asincrono sicuro snprintf("%de usawrite

Non è così male come pensavo, come convertire un int in una stringa in C? ha diverse implementazioni.

Poiché ci sono solo due tipi interessanti di dati a cui possono accedere i gestori di segnali:

  • sig_atomic_t globali
  • int argomento del segnale

questo fondamentalmente copre tutti i casi d'uso interessanti.

Il fatto che strcpysia anche sicuro per i segnali rende le cose ancora migliori.

Il programma POSIX seguente stampa allo stdout il numero di volte che ha ricevuto SIGINT finora, che è possibile attivare con Ctrl + C, e l'ID segnale e.

È possibile uscire dal programma con Ctrl + \(SIGQUIT).

main.c:

#define _XOPEN_SOURCE 700
#include <assert.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

/* Calculate the minimal buffer size for a given type.
 *
 * Here we overestimate and reserve 8 chars per byte.
 *
 * With this size we could even print a binary string.
 *
 * - +1 for NULL terminator
 * - +1 for '-' sign
 *
 * A tight limit for base 10 can be found at:
 * /programming/8257714/how-to-convert-an-int-to-string-in-c/32871108#32871108
 *
 * TODO: get tight limits for all bases, possibly by looking into
 * glibc's atoi: /programming/190229/where-is-the-itoa-function-in-linux/52127877#52127877
 */
#define ITOA_SAFE_STRLEN(type) sizeof(type) * CHAR_BIT + 2

/* async-signal-safe implementation of integer to string conversion.
 *
 * Null terminates the output string.
 *
 * The input buffer size must be large enough to contain the output,
 * the caller must calculate it properly.
 *
 * @param[out] value  Input integer value to convert.
 * @param[out] result Buffer to output to.
 * @param[in]  base   Base to convert to.
 * @return     Pointer to the end of the written string.
 */
char *itoa_safe(intmax_t value, char *result, int base) {
    intmax_t tmp_value;
    char *ptr, *ptr2, tmp_char;
    if (base < 2 || base > 36) {
        return NULL;
    }

    ptr = result;
    do {
        tmp_value = value;
        value /= base;
        *ptr++ = "ZYXWVUTSRQPONMLKJIHGFEDCBA9876543210123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[35 + (tmp_value - value * base)];
    } while (value);
    if (tmp_value < 0)
        *ptr++ = '-';
    ptr2 = result;
    result = ptr;
    *ptr-- = '\0';
    while (ptr2 < ptr) {
        tmp_char = *ptr;
        *ptr--= *ptr2;
        *ptr2++ = tmp_char;
    }
    return result;
}

volatile sig_atomic_t global = 0;

void signal_handler(int sig) {
    char key_str[] = "count, sigid: ";
    /* This is exact:
     * - the null after the first int will contain the space
     * - the null after the second int will contain the newline
     */
    char buf[2 * ITOA_SAFE_STRLEN(sig_atomic_t) + sizeof(key_str)];
    enum { base = 10 };
    char *end;
    end = buf;
    strcpy(end, key_str);
    end += sizeof(key_str);
    end = itoa_safe(global, end, base);
    *end++ = ' ';
    end = itoa_safe(sig, end, base);
    *end++ = '\n';
    write(STDOUT_FILENO, buf, end - buf);
    global += 1;
    signal(sig, signal_handler);
}

int main(int argc, char **argv) {
    /* Unit test itoa_safe. */
    {
        typedef struct {
            intmax_t n;
            int base;
            char out[1024];
        } InOut;
        char result[1024];
        size_t i;
        InOut io;
        InOut ios[] = {
            /* Base 10. */
            {0, 10, "0"},
            {1, 10, "1"},
            {9, 10, "9"},
            {10, 10, "10"},
            {100, 10, "100"},
            {-1, 10, "-1"},
            {-9, 10, "-9"},
            {-10, 10, "-10"},
            {-100, 10, "-100"},

            /* Base 2. */
            {0, 2, "0"},
            {1, 2, "1"},
            {10, 2, "1010"},
            {100, 2, "1100100"},
            {-1, 2, "-1"},
            {-100, 2, "-1100100"},

            /* Base 35. */
            {0, 35, "0"},
            {1, 35, "1"},
            {34, 35, "Y"},
            {35, 35, "10"},
            {100, 35, "2U"},
            {-1, 35, "-1"},
            {-34, 35, "-Y"},
            {-35, 35, "-10"},
            {-100, 35, "-2U"},
        };
        for (i = 0; i < sizeof(ios)/sizeof(ios[0]); ++i) {
            io = ios[i];
            itoa_safe(io.n, result, io.base);
            if (strcmp(result, io.out)) {
                printf("%ju %d %s\n", io.n, io.base, io.out);
                assert(0);
            }
        }
    }

    /* Handle the signals. */
    if (argc > 1 && !strcmp(argv[1], "1")) {
        signal(SIGINT, signal_handler);
        while(1);
    }

    return EXIT_SUCCESS;
}

Compila ed esegui:

gcc -std=c99 -Wall -Wextra -o main main.c
./main 1

Dopo aver premuto Ctrl + C quindici volte, il terminale mostra:

^Ccount, sigid: 0 2
^Ccount, sigid: 1 2
^Ccount, sigid: 2 2
^Ccount, sigid: 3 2
^Ccount, sigid: 4 2
^Ccount, sigid: 5 2
^Ccount, sigid: 6 2
^Ccount, sigid: 7 2
^Ccount, sigid: 8 2
^Ccount, sigid: 9 2
^Ccount, sigid: 10 2
^Ccount, sigid: 11 2
^Ccount, sigid: 12 2
^Ccount, sigid: 13 2
^Ccount, sigid: 14 2

dov'è 2il numero del segnale per SIGINT.

Testato su Ubuntu 18.04. GitHub a monte .


0

Una tecnica che è particolarmente utile nei programmi che hanno un ciclo di selezione consiste nello scrivere un singolo byte su una pipe alla ricezione di un segnale e quindi gestire il segnale nel ciclo di selezione. Qualcosa del genere (gestione degli errori e altri dettagli omessi per brevità) :

static int sigPipe[2];

static void gotSig ( int num ) { write(sigPipe[1], "!", 1); }

int main ( void ) {
    pipe(sigPipe);
    /* use sigaction to point signal(s) at gotSig() */

    FD_SET(sigPipe[0], &readFDs);

    for (;;) {
        n = select(nFDs, &readFDs, ...);
        if (FD_ISSET(sigPipe[0], &readFDs)) {
            read(sigPipe[0], ch, 1);
            /* do something about the signal here */
        }
        /* ... the rest of your select loop */
    }
}

Se ti interessa quale segnale fosse, il byte lungo il tubo può essere il numero del segnale.


-1

Puoi usare printf nei gestori di segnali se stai usando la libreria pthread. unix / posix specifica che printf è atomico per i thread cf Dave Butenhof risponde qui: https://groups.google.com/forum/#!topic/comp.programming.threads/1-bU71nYgqw Nota che per ottenere un'immagine più chiara dell'output di printf, dovresti eseguire la tua applicazione in una console (su Linux usa ctl + alt + f1 per avviare la console 1), piuttosto che uno pseudo-tty creato dalla GUI.


3
I gestori di segnale non vengono eseguiti in un thread separato, vengono eseguiti nel contesto del thread in esecuzione quando si è verificato l'interrupt del segnale. Questa risposta è completamente sbagliata.
Itaych
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.