2011-01-28 14 views
6

C'è qualche differenza tra "elenco di codici 1" e "elenco di codici 2"? Perché nel Codice 1, il processo figlio è in grado di catturare il segnale SIGTERM ed uscire piacevolmente. Ma il codice listng 2 termina bruscamente sul segnale SIGTERM.segnale di chiamata dopo la forcella

Sto usando Linux e C.

Codice 1

if (signal(SIGTERM, stopChild) == SIG_ERR) { 
    printf("Could not attach signal handler\n"); 
    return EXIT_FAILURE; 
} 
pid = fork(); 

Codice 2

pid = fork(); 
if (signal(SIGTERM, stopChild) == SIG_ERR) { 
    printf("Could not attach signal handler\n"); 
    return EXIT_FAILURE; 
} 

La parte strana è che nel Codice 2, il processo figlio e genitore imposta il gestore di segnale per SIGTERM. Quindi, dovrebbe funzionare. Non è vero?

+0

L'ho appena testato, funziona bene per me. Entrambi i processi escono con garbo tramite la chiamata stopChild() in entrambi i casi. –

+0

Sei per caso chiamando fork() da una discussione? –

+2

Potete fornire un programma di esempio completo che mostri il comportamento? – wich

risposta

5

Se si invia SIGTERM dal genitore, il risultato finale dipende dall'ordine in cui vengono pianificati i processi.

Se il bambino viene programmato prima, tutto funziona:

           +---------------+ 
               | pid = fork(); | 
               +-------+-------+ 
        parent        |        child 
          +-----------------------------+-----------------------------+ 
          |               | 
          |         +-------------------------+--------------------------+ 
          |         | if (signal(SIGTERM, stopChild) == SIG_ERR) {  | 
          |         |  printf("Could not attach signal handler\n"); | 
          |         |  return EXIT_FAILURE;       | 
          |         | }             | 
          |         +-------------------------+--------------------------+ 
          |               | 
          .               . 
          .               . 
          .               . 
          |               | 
+-------------------------+--------------------------+        | 
| if (signal(SIGTERM, stopChild) == SIG_ERR) {  |        | 
|  printf("Could not attach signal handler\n"); |        | 
|  return EXIT_FAILURE;       |        | 
| }             |        | 
+-------------------------+--------------------------+        | 
          |               | 
          |               | 
          |               | 
      +-------------+-------------+            | 
      | if (pid > 0) {   |            | 
      |  kill(pid, SIGTERM); |            | 
      | }       |            | 
      +-------------+-------------+            | 
          |               | 
          |               | 
          |               | 

Ma se il paren viene programmato prima, il bambino può non hanno avuto il tempo di impostare il gestore di segnale:

           +---------------+ 
               | pid = fork(); | 
               +-------+-------+ 
        parent        |        child 
          +-----------------------------+-----------------------------+ 
          |               | 
+-------------------------+--------------------------+        | 
| if (signal(SIGTERM, stopChild) == SIG_ERR) {  |        | 
|  printf("Could not attach signal handler\n"); |        | 
|  return EXIT_FAILURE;       |        | 
| }             |        | 
+-------------------------+--------------------------+        | 
          |               | 
          |               | 
          |               | 
      +-------------+-------------+            | 
      | if (pid > 0) {   |            | 
      |  kill(pid, SIGTERM); |            | 
      | }       |            | 
      +-------------+-------------+            | 
          |               | 
          .               . 
          .               . 
          .               . 
          |               | 
          |         +-------------------------+--------------------------+ 
          |         | if (signal(SIGTERM, stopChild) == SIG_ERR) {  | 
          |         |  printf("Could not attach signal handler\n"); | 
          |         |  return EXIT_FAILURE;       | 
          |         | }             | 
          |         +-------------------------+--------------------------+ 
          |               | 
          |               | 
          |               | 

Questo Si chiama race condition, perché il risultato finale dipende da chi deve essere eseguito per primo.

+0

Non penso che questo sia stato il problema perché il processo genitore ha ricevuto un sonno di 10 secondi prima di inviare il segnale. Ad ogni modo, lo accetto come risposta ora che non riesco a riprodurre il problema. – Sabya

0

Ebbene, secondo l'uomo forcella:

La forcella(), fork1(), e forkall (funzioni) creare un nuovo processo. Lo spazio indirizzo del nuovo processo (processo figlio) è una copia esatta dello spazio indirizzo del processo chiamante (processo genitore). Il processo figlio eredita i seguenti attributi dal processo principale:

...

impostazioni di gestione o del segnale (che è, SIG_DFL, SIG_IGN, SIG_HOLD, indirizzo di funzione)

Nel primo esempio il gestore del segnale verrà copiato dal contesto del genitore al figlio biforcuto. Ma non riesco a spiegare perché nel secondo esempio l'impostazione del gestore di segnale nel bambino sarebbe fallita.

+1

Ma nel secondo esempio viene chiamato signal() per entrambi i processi. Quindi dovrebbe impostare anche il gestore di segnale per il processo figlio, non dovrebbe? –

+0

@Sergey, esattamente questo è il mio dubbio! – Sabya

+0

POSIX dice che tutti i segnali _pending_ per il genitore non vengono inviati al figlio, inizia con un set di segnali inizializzato a zero. Ma il figlio deve ereditare gli handler. Calling fork() all'interno di un thread in assenza di pthread_atfork() potrebbe tuttavia spiegare che cosa sta accadendo l'OP. –

3

Innanzitutto, signal() è obsoleto, è preferibile utilizzare sigaction(). Non penso che fork() rischi di scomparire del tutto dal momento che così tante cose lo usano, ma sigaction() fornisce un'interfaccia molto più bella.

Il comportamento che si verifica è comunemente causato dalla chiamata di fork() all'interno di un thread. POSIX affronta questo specifically:

Un processo deve essere creato con un filo singolo . Se un processo multi-thread chiama fork(), il nuovo processo deve contenere una replica del thread di chiamata e il suo intero spazio di indirizzamento, possibilmente compresi gli stati di mutex e altre risorse. Di conseguenza, per evitare errori, la procedura figlio può eseguire solo operazioni con segnale asincrono fino a nel momento in cui viene chiamata una delle funzioni exec . [THR] I gestori di forche possono essere pthread_atfork() per pthread_atfork() per mantenere invarianti applicazione attraverso chiamate fork().

Quando la forcella applicazione chiama() da un gestore di segnale e qualsiasi della forcella movimentatori registrate dal pthread_atfork() chiama una funzione che non è Asynch-segnale-safe, il comportamento è indefinito.

Ciò significa, piuttosto che ereditare una copia di tutto lo spazio indirizzo del genitore, si eredita solo una copia della vocazione discussioni spazio di indirizzamento, che non contiene i gestori. Potrebbe essere concepibile che tu sia, anzi (forse anche inconsapevolmente) chiamando fork() da una discussione.

Un processo figlio riceve una copia carbone dello spazio indirizzo del genitore. L'unica differenza con i segnali sarebbe in attesa di segnali, che il bambino non riceverà poiché riceve un set di segnali inizializzato a zero. Ma sì, ottiene una copia dei gestori.

+0

prima che il fork sia effettivamente quello di successo, quindi le disposizioni del segnale vengono copiate sul bambino, chiamandolo dopo che la forcella è il caso che si interrompe, cioè in qualche modo il segnale di chiamata nel bambino non funziona. – wich

+0

@wich: ecco perché sono molto sospettoso che fork() venga chiamato in una discussione. –