Newsgroup: it.comp.os.linux.development
Titolo: [LUNGO] waitpid, questa strana funzione....

Ciao a tutti, io ho um problema.
Ho un programma che genera figli. Questi muoiono (quasi) immediatamente, 
e cio' e' normale. Quello che non e' normale e' che se faccio una 
waitpid del tipo waitpid( pid_del_figlio, NULL, 0 ) il figlio viene 
aspettato correttamente (pero' intanto il padre rimane in attesa) mentre 
se faccio una waitpid non bloccante del tipo waitpid( pid_del_figlio, 
NULL, WNOHANG ), il padre ovviamente non resta in attesa, pero' il 
figlio mi rimane defunct finche' non killo il padre (e cio' e' strano).
Inoltre il valore di ritorno della waitpid e' 0, quindi non da errori.
Quindi in pratica la waitpid viene eseguita correttamente ma pare che 
non funzioni.
E' possibile un bug nella waitpid?
L'ambiente e' Debian 3.0r1 su kernel 2.4.18, architettura i586.
A proposito, c'e' differenza tra zombie e defunct?
Gia che ci sono aggiungo un po' di domandine:
1 - Quale chiamata di sistema o funzione devo invocare per resettare un 
terminale?
2 - C'e' modo di far cambiare il terminale con una system call, senza 
che l'utente prema ALT+1, ALT+2, ecc... ?
3 - Quando ci sono determinati errori, il kernel stampa messaggi sul 
terminale attivo in quel momento, anche se la console e' stata settata 
su un terminale specifico (tipo tty12). Si puo' impedire cio'? [forse e' 
un po' OT come domanda...]
4 - Quando finisce la procedura si shutdown, se il computer non supporta 
apm/aspi non si spegne, e a video viene stampata la scritta "Power 
down.", che indica che si puo' spegnere il computer. Come si fa a 
modificare tale scritta? Bisogna modificare i sorgenti del kernel?

 waitpid e' 0, quindi non da errori.
Ma tu la waitpid la chiami una sola volta? Se e' cosi' il problema e'
questo: tu chiami la waitpid() non bloccante che ti restituisce zero perche'
il figlio e' ancora vivo. Alla morte il figlio rimane in sospeso finche' il
padre non ne prende atto attraverso una nuova invocazione della waitpid che
questa volta restituira' il PID del figlio morto.
In pratica devi chiamare waitpid periodicamente.
Alessandro.

 kernel?
Mi sa che devi modificare i sorgenti del kernel.
Alessandro.

 In pratica devi chiamare waitpid periodicamente.
E' proprio questo che mi lascia perplesso: la waitpid non bloccante non 
dovrebbe fare in modo che comunque il figlio venga aspettato 
automaticamente?



nope: quando un figlio muore invia un SIGCHLD al padre, e solo dopo questo
evento dovresti chiamare la waitpid().
la waitpid non bloccante in pratica controlla se il figlio č morto: se sě
agisce di conseguenza per rimuoverlo dalla lista dei processi, altrimenti
non fa nulla e ti fa andare avanti con l'esecuzione del tuo programma.
Tornando al segnale SIGCHLD, forse ti conviene handarlo per richiamare la
waitpid effettivamente quando serve
Ciao
Matic

 automaticamente?
No. waitpid serve per sapere se il figlio e' morto e la versione non
blocccante serve solo a permetterti di fare qualcosa d'altro mentre aspetti.
Se non ti interessa sapere quando il figlio muore puoi mettere la waitpid
nel ciclo principale del programma o seguire il consiglio di matic.
Alessandro.

 waitpid effettivamente quando serve
Gia', ci avevo fatto un pensierino, infatti sto cercando di capire come 
si fa.

 si fa.
Fino a man signal e man sigaction ci sei arrivato?(Usa o l'una o l'altra,
signal è un front-end per sigaction che dà un controllo più fine). Poi
uno dei parametri è un puntatore alla procedura da eseguire; se non lo
sai, se hai la procedura pippo(), pippo senza parentesi è un puntatore a
pippo(). Chiedi pure aiuto.
-- 
cat <<EOSIGN
Paolo Giarrusso, aka Blaisorblade(togliere solo REMOVEONLYTHIS per rispondere)
Kernel 2.4.20/2.5.63 on an i686; Linux registered user n. 292729
EOSIGN

 Forse dipende anche dalle impostazioni di syslogd.
Sì, appunto.


 morti?
Infatti di solito si fa qualcosa del genere:
void child_reaper(int useless) {
        pid_t tmp;
        while(1) {
                tmp = waitpid(WAIT_MYPGRP, NULL, WNOHANG);
                if (tmp == -1) {
                        fprintf(stderr, "%s\n", strerror(errno));
                        break;
                }
                /* Finiti i figli */
                if (tmp == 0)
                        break;
                if (WIFEXITED(tmp)) {
                        ....
                }
        }
}
....
signal(SIGCHLD, child_reaper);
....
ciao,
Kronos
-- 
Home: 
http://kronoz.cjb.net
Dicono che  il cane sia  il miglior  amico dell'uomo. Secondo me  non e`
vero. Quanti dei vostri amici avete fatto castrare, recentemente?

 system call è + complicato, parti da strace chvt che a me ha
[snip su info comunque interessanti]
Non conoscevo chvt, se anche fossi stato al corrente avrei cercato i
sorgenti, ma con strace probabilmente si fa prima... :-)
Alessandro.

 pippo(). Chiedi pure aiuto.
Si' fino a li' c'ero arrivato, poi ho trovato un manuale sulle librerie 
dell'ANSI C online che faceva anche degli esempi.
Ora mi funziona tutto a meraviglia.
Solo non ho capito che significato aveva, nelle manpages, questo typedef:
	typedef void (*sighandler_t)(int)
In particolare non ho capito molto che c'azzeccano l'asterisco e lo 
(int) alla fine.
CMQ alla fine ho semplicemente fatto una funzione del tipo:
	void handler(int signum)
e chiamando signal cosi':
	signal( SIGNUM, handler );
funziona tutto a meraviglia.

 ricordo dove) /dev/tty12 anziché /dev/tty.
Gia' fatto, /dev/tty non ce l'ho piu da nessuna parte in syslog.conf.
Mi sa che certi messaggi di errore del kernel, invece che stamparli 
sulla console, di default li stampa sul terminale attivo, e non ce 
niente da fare (a parte modificare i sorgenti del kernel).

 funziona tutto a meraviglia.
È esattamente quello che devi fare.
Ciao.


 automaticamente?
Il modo giusto di fare quello che serve a te e' di attaccarti una 
funzione al SIGCHILD, che viene chiamato quando muore un child process:
    signal(SIGCHLD, ChildDead);
E nella funzione chiamare waitpid():
void ChildDead(int unused)
{
     int status;
     int ret = waitpid(-1, &status, WNOHANG );
[...]

 void ChildDead(int unused)
"signal" vuole come secondo argomento un pointer a funzione che
accetta un intero e ritorna un pointer a void.
-- 
Maurizio Loreti                         
http://www.pd.infn.it/~loreti/mlo.html
Dept. of Physics, Univ. of Padova, Italy              ROT13: 
ybe...@cq.vasa.vg

 accetta un intero e ritorna un pointer a void.
Evidentemente il gcc che uso di default non fa il check sul return type 
del prototipo :)
Di sicuro lo fa sugli argomenti del function pointer altrimenti non 
avrei messo "int unused" :)
Bye,
  Gabry

 accetta un intero e ritorna un pointer a void.
dalla manpage:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
deve ritornare void, non un pointer a void
Ciao
Matic

 (int) alla fine.
Il sighandler da passare e' un puntatore ad una funzione che prende come
unico parametro un int e ritorna un void.
-- 
  Giuseppe "Cowo" Corbelli \/\ My software: 
http://cowo.yoda2000.net
    **ANTI-SPAM** -- **Substitute IHATESPAM with cowo to reply by mail** 
-<! When I was young I used to pray for a bike. Then I realized that God 
	 doesn't work that way. So I stole a bike and prayed for forgiveness. !>-

 deve ritornare void, non un pointer a void
Se ho capito bene avete ragione tutti e due, e' un problema filosofico: 
il nome di una funzione che ritorna void e' di per se' un puntatore a void.
Quindi quando definisco la funzione scrivo:
void handler(int signum)
mentre se voglio definire il tipo di quella funzione scrivo
typedef void (*sighandler_t)(int)
ma e' la stessa cosa

 END SPIEGONE.
Grazie dello "SPIEGONE", cmq gia' conoscevo puntatori e typedef, solo 
che non avevo mai visto typedef applicato a una funzione, ne' sapevo che 
un nome di funzione fosse un puntatore (in effetti e' logico, ma nn ci 
avevo mai pensato). Scusa se ti ho fatto spiegare piu' del dovuto, cmq 
grazie, spero che lo "SPIEGONE" sia utile per qualcuno che sta lurkando 
'sto thread! :-)
PS: ma com'e' che typedef usato con le funzioni nei manuali di C non c'e'?

 (int) alla fine.
cdecl e' tuo amico:
$ echo 'void (*sighandler_t)(int);' | cdecl
declare sighandler_t as pointer to function that expects (int)
returning void;
-- 
There are only 10 kinds of people in the world --
Those who understand binary, and those who don't.

>> accetta un intero e ritorna un pointer a void.
il soggetto di 'ritorna un pointer a void' è signal(), non l'argomento di
signal, che non deve ritornare niente (void).
Matic

 signal(SIGCHLD, child_reaper);
Io la chiamata alla signal l'ho sempre messa _dentro_ il corpo di
child_reaper...
-- 
 Ciao, Bianco.                         Reply to luca.bianco (at) 
tiscali.it
|--------[Powered by Debian GNU/Linux Sarge - 2.4.19 - AMD K6-200]--------|
|------------[Linux Registered User #287769 - Machine #169192]------------|

 child_reaper...
e immagino non ti abbia mai funzionato ..

 child_reaper...
Uh? child_reaper e` il gestore del  segnale, se non viene registrato non
viene chiamato.
ciao
Kronos
-- 
Home: 
http://kronoz.cjb.net
Tentare e` il primo passo verso il fallimento.
Homer J. Simpson

 viene chiamato.
Aspetta, mi sono dimenticato di scrivere una parte di quello che stavo
pensando: la _prima_ chiamata a child_reaper all'inizio del main, e l'altra
alla fine di child_reaper stesso, altrimenti viene gestito con child_reaper
solo il primo SIGCHLD ricevuto e per gli altri viene utilizzato il
comportamento di default.


 comportamento di default.
questo non e' piu' vero da molto tempo sotto linux, mentre su altri
unix funziona ancora cosi'.
Dalla man page di signal(2)
PORTABILITY
       The  original  Unix  signal()  would  reset the handler to
       SIG_DFL, and System V (and the Linux kernel  and  libc4,5)
       does  the same.  On the other hand, BSD does not reset the
       handler, but blocks new  instances  of  this  signal  from
       occurring  during  a  call  of  the  handler.   The glibc2
       library follows the BSD behaviour.
       In a libc5  system  including  <bsd/signal.h>  instead  of
       <signal.h>  makes  signal  to be redefined as __bsd_signal
       and signal has the BSD semantics. This is not recommended.
       In  a  glibc2 system defining a feature test macro such as
       _XOPEN_SOURCE or using a  separate  sysv_signal  function,
       the  classical  behaviour  is obtained. This is not recom­
       mended.
       Trying to change the semantics of this call using  defines
       and  includes  is  not  a good idea. It is better to avoid
       signal altogether, and use sigaction(2) instead.
Quindi se vuoi scrivere del codice portabile dovrai destreggiarti con
qualche #ifdef. E' in casi come questi che tornano utili gli
autotools :)


 unix funziona ancora cosi'.
I primi esperimenti in C che ho fatto erano su un HP-UNIX, non ricordo che
versione... quando ho cominciato ad usare linux, ho pensato che funzionasse
allo stesso modo, funzionava e non mi sono fatto grossi problemi ;-)
