Newsgroup: it.comp.os.linux.development
Titolo: Segnali che non arrivano al padre

Ho già fatto questa domanda su it.comp.programmazione ma questo gruppo è
probabilmente più adatto.
Questo programma genera 3 figli e gli manda un SIGUSR2, i figli in risposta
gli mandano un SIGUSR1.
Su WinXP con cygwin (gcc 3.2) va, su Red Hat no (gcc 3.2.2).
O meglio, su Red Hat il comportamento è casuale, i figli mandano sempre 3
SIGUSR1, il padre ne riceve 1 o 3 (raramente 2).
Qualcuno mi aiuta a trovare il problema?
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int n_ricev = 0;
int me;
void usr1(int a){
    signal(SIGUSR1, usr1);
    ++n_ricev;
    printf("P: Ricevuto SIGUSR1 (tot=%d)\n", n_ricev);
}
void usr2(int a){
    printf("%d: Mando SIGUSR1 al padre\n", me);
    kill(getppid(), SIGUSR1);
}
int main(){
    int pid[3];
    int tmp;
    int i;
    signal(SIGUSR1, usr1);
    signal(SIGUSR2, usr2);
    for (i = 0; i < 3; i++){
        tmp = fork();
        if (tmp == 0){
            me = i;
            pause();
            return 0;
        }
        else
            pid[i] = tmp;
    }
    for (i = 0; i < 3; ++i)
        kill(pid[i], SIGUSR2);
    while (n_ricev != 3){
        puts("PAUSE");
        pause();
    }
}
-- 
Marco Barisione
http://spazioinwind.libero.it/marcobari/

 
Non so dirti come si comporta WinXP, posso dirti cosa fa Unix.
In Unix i segnali non sono associati ad una coda, questo significa che se
invii un segnale gestito da una funzione e mentre questa funzione è in
esecuzione ne arriva un altro, il secondo è perso.
Il problema è nondeterministico, dipende dallo schedulatore, da cosa è in
esecuzione al momento, da come sono gestiti i figli eccetera...
Quando le cose stanno in questi termini (non si può cioè perdere alcuna
signal) è preferibile fare ricorso a strutture più complesse come i
semafori, i thread al posto dei processi forcati e le variabili condition.
SiMoStro

 semafori, i thread al posto dei processi forcati e le variabili condition.
non sempre ricorrere ai thread č furbo se si hanno problemi coi segnali.
Mi spiego: tutti i thread hanno gli stessi gestori dei segnali, quindi non
puoi mettere un gestore per ogni thread per lo stesso segnale, cosa che
invece puoi fare con i task.

 invece puoi fare con i task.
Mi sembrava ovvio che l'uso dei thread e delle variabili condition
fosse da intendersi "in piena sostituzione" dell'esempio dei signal
a cui si aggiungano i semafori.
Nel suo caso comunque il problema non mi pare sussista visto che le
chiamate a kill(pid_t, int) vengono inoltrate esplicitamente al pid del
processo padre da parte dei figli forkati.
Chiaro che in altri contesti si deve tenere presente che i threads
condividono col processo che li ha creati descrittori, signal handlers  e
dispositions, CWD, user e group ID, dati ed istruzioni...
...ma hanno un loro thread ID, errno e *signal mask*...
SiMoStro

 esecuzione ne arriva un altro, il secondo è perso.
Grazie dell'aiuto, comunque non mi serve realmente quel programma è solo un
esercizio per l'università.
Non ho capito però se perdo tutti i segnali che arrivano mentre ne sto
gestendo un altro, solo quelli gestiti dalla stessa funzione o tutti i
segnali di un solo tipo.


 ...ma hanno un loro thread ID, errno e *signal mask*...
e invece no, mi dispiace contraddirti ma non č cosě. C'č scritto pure
nella documentazione della libreria c. Il fatto che abbiano l'id non
iustifica questo "passo induttivo". La maschera dei segnali e gestori
dei segnali dei thread sono condivisi.
Cito testualmente:KNOWN BUGS AND LIMITATIONS:
 
- Threads share pretty much everything they should share according
  to the standard: memory space, file descriptors, signal handlers,
  current working directory, etc. One thing that they do not share
  is their pid's and parent pid's. According to the standard, they
  should have the same, but that's one thing we cannot achieve
  in this implementation (until the CLONE_PID flag to clone() becomes
  usable).
Se si fa uso pesante di thread e segnali si rischia di incasinarsi la vita.
I nuovi kernel supporteranno nuovi thread, speriamo che la cosa sia un po'
migliore.

 un po' migliore.
Capisco il gusto della contraddizione ad oltranza; "rischiare di
incasinarsi" e "fare le cose come vanno fatte" sono due concetti differenti
ma se sul primo si puņ opinare quanto si vuole, sul secondo grazie al cielo
non si puņ.
Non so cosa tu intenda per "nuovi thread", non ho conoscenze sui vari kernel
che si susseguono tali da distinguere le varie (?) implementazioni,
comunque, giusto per fugare qualche inesattezza, mi permetto di allegare
due stralci tratti dalla rete:
------------------------------------------------------------------
Standard POSIX
Threads
The semantics of signals are not changed by using threads, as far as the
outside world is concerned. Signals are still delivered to the process as a
whole rather than individual threads. This should make intuitive sense.
Think of the kill program used to send signals to processes. When you run
the command, you give it a process ID number as a command line parameter,
but you do not specify which thread in that progam should receive the
signal.)
Once inside the process, the signals are delivered to individual threads via
the magic of pthreads. One important safety tip is that pthreads constructs
like mutexes, condition variables, etc. do not have defined behavior inside
signal handlers. So you cannot use pthreads calls or synchronize with other
threads while inside a signal handler.
Here it gets a bit complicated. Since you have threads, you can have a few
different ways that signals are handled inside the process.
A signal may be sent in reaction to something a thread does. For example, if
a particular thread segfaults or divides by zero, the resulting signal is
sent directly to the thread that generated the problem.
One thread may deliberately send a signal to another using pthread_kill().
The signal is always sent to the target thread.
There are signals sent from outside the process by the kernel or another
process. When one of these signals is delivered to the process, the process
sends it to any thread that can handle it. If more than one thread has that
signal unblocked, then the thread to handle it is chosen arbitrarily. If
you want to have a particular thread handle a signal from outside the
system, all other threads in your process must block the signal to force it
to go to the one thread you want to handle. If all threads are blocking a
signal, the signal waits for the first available thread that unblocks the
signal.
Another consideration is that signal actions (handlers called when the
signal is caught) are global to the entire process, not particular to any
one thread. Thus, while you can block a signal for just one thread, just a
few threads, or all threads, you can only choose to ignore a signal for no
threads or all threads. Similarly, you can choose which set of threads are
not blocking a signal and may handle it, but they all use the same handler
for the signal. You are controlling which threads may have to yield time to
execute the handler, but you cannot control the handler they will execute
on a thread by thread basis. Any thread can use sigaction() to set the
handler (or SIG_DFL or SIG_IGN) for a signal.
---------------------------------------------------------------------
For POSIX, there are two kinds of signals, asynchronous, and synchronous.
Signal handlers are set for the entire process, meaning that different
threads may not set their own signal handlers.  HOWEVER, each thread may set
a separate signal mask, allowing or disallowing signals to be delivered to
that thread.
In general, it is recommended by POSIX that instead of handling asynchronous
signals in random threads, you should create a separate thread that waits
for asynchronous signals (using the new sigwait() API) and then uses IPC to
let the other threads know that an asynchronous signal has arrived.
Under Linux, things are a little tricky, because at the moment, each thread
has it's own process id.  This means that another process can target a
specific thread with a signal, which isn't allowed by the POSIX standard.
Also, sending a signal to a process group (e.g. Ctrl-C)  signals all
threads in the process, also violating the POSIX standard.
----------------------------------------------------------------------
Ricordo che in Linux i threads sono "built-in", come ricorda lo stesso
Torvalds:
----------------------------------------------------------------------
According to Chaim Frenkel:
 >  <stupid question>
 >    Why should a signal mask per_THREAD, be any harder than a signal mask
 >    per_PROCESS?
 >  </stupid question>
 
 Because POSIX threads are _supposed_ to be implementable on top of a
 UNIX system that doesn't have kernel threads.  And that means that a
 robust implementation has to do a lot of user-mode processing on each
 signal delivery, *or* has to make at least one and maybe more system
 calls on every user-mode-thread context switch.
 
 On a system like Linux with kernel threads, that part isn't _so_ bad.
 But there's still an unholy mess surrounding the semantics of signal
 delivery; a group of threads is in some ways a bunch of separate
 processes, but in some ways is still supposed to act like one process.
-------------------------------------------------------------------------

 Torvalds:
è risaputo che si usa la syscall clone(), ma questo non contraddice nulla.