Domanda

Stavo per aggiungere un gestore di segnale aggiuntivo a un'app che abbiamo qui e ho notato che l'autore aveva usato sigaction () per configurare gli altri gestori di segnale. Stavo per usare signal () . Per seguire la convenzione dovrei usare sigaction () ma se stavo scrivendo da zero, quale dovrei scegliere?

È stato utile?

Soluzione

Usa sigaction () a meno che hai ragioni molto convincenti per non farlo.

L'interfaccia signal () ha antichità (e quindi disponibilità) a suo favore, ed è definito nello standard C. Tuttavia, ha una serie di caratteristiche indesiderabili che sigaction () evita - a meno che tu non usi i flag esplicitamente aggiunti a sigaction () per permettergli di simulare fedelmente il vecchio comportamento signal () .

  1. La funzione signal () non (necessariamente) blocca l'arrivo di altri segnali mentre il gestore corrente sta eseguendo; sigaction () può bloccare altri segnali fino a quando il gestore corrente non ritorna.
  2. La funzione signal () (di solito) reimposta l'azione del segnale su SIG_DFL (impostazione predefinita) per quasi tutti i segnali. Ciò significa che il gestore signal () deve reinstallarsi come prima azione. Si apre anche una finestra di vulnerabilità tra il momento in cui viene rilevato il segnale e il gestore viene reinstallato durante il quale se arriva una seconda istanza del segnale, si verifica il comportamento predefinito (di solito termina, a volte con pregiudizio, noto anche come core dump).
  3. L'esatto comportamento del signal () varia tra i sistemi & # 8212; e le norme consentono tali variazioni.

Questi sono generalmente buoni motivi per usare sigaction () invece di signal () . Tuttavia, l'interfaccia di sigaction () è innegabilmente più complicata.

Qualunque delle due che usi, non essere tentato da interfacce di segnale alternative come sighold () , sigignore () , sigpause () e sigrelse () . Sono nominalmente alternative a sigaction () , ma sono solo a malapena standardizzate e sono presenti in POSIX per compatibilità con le versioni precedenti piuttosto che per uso serio. Si noti che lo standard POSIX afferma che il loro comportamento nei programmi multi-thread non è definito.

Programmi e segnali multi-thread è un'altra storia complicata. AFAIK, sia signal () che sigaction () sono OK nelle applicazioni multi-thread.

Cornstalks osserva :

  

La pagina man di Linux per signal () dice:

     

   Gli effetti di signal() in un processo multi-thread non sono specificati.

     

Quindi, penso che sigaction () sia l'unico che può essere usato in sicurezza in un processo multi-thread.

È interessante. La pagina di manuale di Linux è più restrittiva di POSIX in questo caso. POSIX specifica per signal () :

  

Se il processo è multi-thread o se il processo è single-thread e un gestore del segnale viene eseguito diverso da come risultato di:

     
      
  • Il processo che chiama abort () , raise () , kill () , pthread_kill () , o sigqueue () per generare un segnale che non è bloccato
  •   
  • Un segnale in sospeso viene sbloccato e recapitato prima della chiamata che lo sblocca ritorna
  •   
     

il comportamento non è definito se il gestore del segnale fa riferimento a qualsiasi oggetto diverso da errno con durata di memorizzazione statica diversa dall'assegnazione di un valore a un oggetto dichiarato volatile sig_atomic_t , o se il gestore del segnale chiama una funzione definita in questo standard diversa da una delle funzioni elencate in Concetti relativi al segnale .

Quindi POSIX specifica chiaramente il comportamento di signal () in un'applicazione multi-thread.

Tuttavia, sigaction () deve essere preferito essenzialmente in tutte le circostanze & # 8212; e il codice multi-thread portatile dovrebbe usare sigaction () a meno che non ci sia una ragione schiacciante per cui non può (come " usare solo le funzioni definite dalla norma C " & # 8212; e sì, codice C11 può essere multi-thread). Questo è fondamentalmente ciò che dice anche il paragrafo iniziale di questa risposta.

Altri suggerimenti

Sono interfacce diverse per le strutture di segnale del sistema operativo. Si dovrebbe preferire l'uso della sigazione per segnalare, se possibile, poiché il segnale () ha un comportamento definito dall'implementazione (spesso incline alla razza) e si comporta in modo diverso su Windows, OS X, Linux e altri sistemi UNIX.

Vedi questo nota di sicurezza per i dettagli.

Per me, questa riga è stata sufficiente per decidere:

  

La funzione sigaction () fornisce a   più completo e affidabile   meccanismo di controllo dei segnali; nuovo   le applicazioni dovrebbero usare sigaction ()   piuttosto che signal ()

http://pubs.opengroup.org/onlinepubs/009695399/ funzioni / signal.html # tag_03_690_07

Che tu stia iniziando da zero o modificando un vecchio programma, la sigazione dovrebbe essere l'opzione giusta.

signal () è lo standard C, sigaction () non lo è.

Se sei in grado di usare uno dei due (ovvero, sei su un sistema POSIX), allora usa sigaction (); non è specificato se signal () reimposta il gestore, il che significa che per essere portatile è necessario chiamare nuovamente il segnale () all'interno del gestore. Quel che è peggio è che c'è una gara: se ricevi due segnali in rapida successione, e il secondo viene consegnato prima di reinstallare il gestore, avrai l'azione predefinita, che probabilmente ucciderà il tuo processo. sigaction () , d'altra parte, è garantito l'uso di & # 8220; affidabile & # 8221; semantica del segnale. Non è necessario reinstallare il gestore, perché non verrà mai ripristinato. Con SA_RESTART, puoi anche ottenere alcune chiamate di sistema per riavviare automaticamente (quindi non devi controllare manualmente la presenza di EINTR). sigaction () ha più opzioni ed è affidabile, quindi il suo uso è incoraggiato.

Psst ... non dire a nessuno che te l'ho detto, ma POSIX attualmente ha una funzione bsd_signal () che agisce come segnale () ma fornisce semantica BSD, il che significa che è affidabile. Il suo uso principale è per il porting di vecchie applicazioni che assumevano segnali affidabili e POSIX non consiglia di usarlo.

Dalla pagina man signal(3) :

  

DESCRIZIONE

 This signal() facility is a simplified interface to the more
 general sigaction(2) facility.

Entrambi invocano la stessa struttura sottostante. Presumibilmente non dovresti manipolare la risposta di un singolo segnale con entrambi, ma mescolarli non dovrebbe causare la rottura di nulla ...

Userei signal () poiché è più portatile, almeno in teoria. Voterò qualsiasi commentatore che può inventare un sistema moderno che non ha un livello di compatibilità POSIX e supporta il segnale ().

Citando dalla documentazione GLIBC :

  

È possibile utilizzare sia le funzioni di segnale che di sigazione all'interno   un singolo programma, ma devi stare attento perché possono   interagire in modi leggermente strani.

     

La funzione di sigazione specifica più informazioni rispetto al segnale   funzione, quindi il valore di ritorno dal segnale non può esprimere il pieno   gamma di possibilità di sigazione. Pertanto, se si utilizza il segnale per   salvare e in seguito ristabilire un'azione, potrebbe non essere possibile   ristabilire correttamente un gestore stabilito con sigazione.

     

Per evitare problemi, utilizzare sempre la sigazione per salvare   e ripristinare un gestore se il programma utilizza affatto la sigazione. Da   la sigazione è più generale, può salvare e ristabilire correttamente qualsiasi   azione, indipendentemente dal fatto che sia stata stabilita originariamente con   segnale o sigazione.

     

Su alcuni sistemi se si stabilisce un'azione con segnale e quindi   esaminalo con sigazione, l'indirizzo del gestore che potresti non ottenere   essere uguale a quello specificato con signal. Potrebbe anche non esserlo   adatto per l'uso come argomento di azione con segnale. Ma puoi contare   su come usarlo come argomento per la sigazione. Questo problema non si verifica mai   sul sistema GNU.

     

Quindi, stai meglio usando l'uno o l'altro dei meccanismi   coerentemente all'interno di un singolo programma.

     

Nota sulla portabilità: la funzione di segnale di base è una caratteristica di ISO C,   mentre la sigazione fa parte dello standard POSIX.1. Se sei   preoccupato per la portabilità a sistemi non POSIX, quindi dovresti   utilizzare invece la funzione del segnale.

     
    

Copyright (C) 1996-2008 Free Software Foundation, Inc.

         

È concessa l'autorizzazione per copiare, distribuire e / o modificare questo     documento ai sensi della GNU Free Documentation License,     Versione 1.2 o successive pubblicata dal software libero     Fondazione; senza sezioni invarianti, senza testi di copertina,     e senza testi di copertina. Una copia della licenza è inclusa in     la sezione intitolata " GNU Free Documentation License " ;.

  

Vorrei anche suggerire di usare sigaction () over signal () e vorrei aggiungere un altro punto. sigaction () ti dà più opzioni come pid del processo che è morto (possibile usando la struttura siginfo_t).

Dalla pagina man signal (7)

  

Un segnale diretto al processo può essere inviato a qualsiasi     uno dei thread che al momento non ha il segnale bloccato.     Se più di uno dei thread ha il segnale sbloccato, allora il     il kernel sceglie un thread arbitrario a cui inviare il segnale.

E direi questo "problema". esiste per signal (2) e sigaction (2) . Quindi fai attenzione con segnali e pthreads.

... e signal (2) sembra chiamare sigaction (2) sotto in Linux con glibc.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top