Come si può vedere, se killo lo script prova.sh, il comando sleep (che
è un sotto-processo di prova.sh) non viene terminato. Posso vedere con
il comando pstree che prima del kill sleep è effettivamente un sotto-
processo di prova.sh, dopo il kill sleep diventa un sotto-processo di
init.
Come posso fare per killare uno script e gli eventuali comandi
lanciati nello script e ancora in fase di esecuzione?
Due possibilita`:
1) usa un'utility in C con "kill( -pid, segnale )"(da provare)
2) usa "pstree -p" e poi digerisci l'output (da scrivere) trovando tutti i PIDs
da uccidere individualmente
Ciao,
--
Roberto Divia` Love at first sight is one of the greatest
Dep:PH Bat:53 Mailbox:C02110 labour-saving devices the world has ever seen
Route de Meyrin 385 ---------------------------------------------
Case Postale Phone: +41-22-767-4994
CH-1211 Geneve 23 CERN Fax: +41-22-767-9585
Switzerland E-Mail: Robert...@cern.ch
In effetti il reference manual della libc a proposito della funzione
kill() dice:
---
The pid specifies the process or process group to receive the signal:
pid > 0
The process whose identifier is pid.
pid == 0
All processes in the same process group as the sender.
pid < -1
The process group whose identifier is -pid.
pid == -1
If the process is privileged, send the signal to all processes
except
for some special system processes. Otherwise, send the signal to
all processes with the same effective user ID.
---
Sinceramente non conoscevo il concetto di "process group", ma penso
sia un
insieme di processi accomunati da qualcosa. Forse i comandi eseguiti
da uno
script bash appartengono allo stesso "process group".
Proverò a scrivere un comando killg (kill group), ma possibile che
nessun altro
ci abbia già pensato e non esista già qualcosa di standard?
> 2) usa "pstree -p" e poi digerisci l'output (da scrivere) trovando tutti i PIDs
> da uccidere individualmente
In effetti ci avevo pensato... peccato che nella mia piccolissima
linux-box
(sono solo 8MB di Flash) non ho il comando pstree, ma solo uno scarso
ps.
Process group = il processo + tutti i suoi figli.
>> 2) usa "pstree -p" e poi digerisci l'output (da scrivere) trovando tutti i PIDs
>> da uccidere individualmente
>
> In effetti ci avevo pensato... peccato che nella mia piccolissima
> linux-box
> (sono solo 8MB di Flash) non ho il comando pstree, ma solo uno scarso
> ps.
Anche col ps si fa. Ci vuole un po' ma si fa. Guarda "ps --ppid", oppure un
"ps --no-header -eo'pid,ppid'" seguito da un parsing ricorsivo...
In effetti...
Cmq ho fatto il programmino C killpg che usa la kill() per inviare un
segnale
ad un determinato process group, semplicemente passando il PID del
parent
process con il segno meno.
Su una linux box "normale" funziona... sulla mia linux box embedded
non va,
la kill() mi ritorna l'errore ESRCH (non trova il pid). Eppure
passando il PID
positivo per terminare solo il parent process funziona.
La libc utilizzata è la stessa... chissà dov'è la differenza.
Forse la shell che è diversa?
> > In effetti ci avevo pensato... peccato che nella mia piccolissima
> > linux-box
> > (sono solo 8MB di Flash) non ho il comando pstree, ma solo uno scarso
> > ps.
> Anche col ps si fa. Ci vuole un po' ma si fa. Guarda "ps --ppid", oppure un
> "ps --no-header -eo'pid,ppid'" seguito da un parsing ricorsivo...
Eh sì, ma quando ti dicevo che è uno scarso ps, volevo dire che non
c'è l'opzione
--ppid, purtroppo :(
Probabilmente e` la kill ad essere diversa :-(
>>> In effetti ci avevo pensato... peccato che nella mia piccolissima
>>> linux-box
>>> (sono solo 8MB di Flash) non ho il comando pstree, ma solo uno scarso
>>> ps.
>> Anche col ps si fa. Ci vuole un po' ma si fa. Guarda "ps --ppid", oppure un
>> "ps --no-header -eo'pid,ppid'" seguito da un parsing ricorsivo...
>
> Eh sì, ma quando ti dicevo che è uno scarso ps, volevo dire che non
> c'è l'opzione
> --ppid, purtroppo :(
Prova con "ps -efl".
Hai killall? Ha un'opzione per il kill ricorsivo...
verrebbe da dire cosě, ma come puo' essere diversa la kill se la libc č
la stessa?
infatti: sicuro sia la stessa e non, chessň, la microClibc? (di solito
negli embedded c'č quella)
Ho due modalità di funzionamento nel mio sistema embedded.
Una è full-feature e si basa su un filesystem NFS che risiede su un
normale PC. Il kernel in esecuzione sul target (una piattaforma ARM)
viene caricato dalla rete al boot e il root filesystem è su NFS sempre
in rete.
In queste condizioni kill funziona come previsto.
Copiando la stessa libc e lo stesso comando kill nella memoria Flash del
mio sistema e facendolo partire in modalità standalone (kernel e root
filesystem in Flash), il comportamento del comando kill è diverso.
Mi dice che non c'è il processo normalmente elencato da ps.
>>>> In effetti ci avevo pensato... peccato che nella mia piccolissima
>>>> linux-box
>>>> (sono solo 8MB di Flash) non ho il comando pstree, ma solo uno scarso
>>>> ps.
>>> Anche col ps si fa. Ci vuole un po' ma si fa. Guarda "ps --ppid",
>>> oppure un
>>> "ps --no-header -eo'pid,ppid'" seguito da un parsing ricorsivo...
>> Eh sì, ma quando ti dicevo che è uno scarso ps, volevo dire che non
>> c'è l'opzione
>> --ppid, purtroppo :(
> Prova con "ps -efl".
Nada, non accetta praticamente alcuna opzione.
> Hai killall? Ha un'opzione per il kill ricorsivo...
Proverò e ti farò sapere...
Potrebbe essere il comando kill che non parsa correttamente i PID
negativi, cioè quelli relativi ai process group. Ma in realtà la kill è
la stessa, come dicevo nell'altro post.
> infatti: sicuro sia la stessa e non, chessò, la microClibc? (di solito
> negli embedded c'è quella)
Ho copiato il file libc-2.3.5.so dal sistema di sviluppo NFS (vedi altro
post) al mio target.
E si comporta in modo differente anche il mio programma killpg che
richiama semplicemente la kill() della libc passando come PID in un
numero negativo. Lavorando con il sistema NFS funziona correttamente,
lavorando direttamente sul target in modalità stand-alone mi dà errore
di processo inesistente.
I due sistemi hanno molte differenze tra di loro, quello in NFS è molto
accessoriato. Però si basano sullo stesso kernel e sulle stesse libc...
Come sarà implementata la kill() nella libc? Dove potrei andare a
guardare per capire che succede?
Magari è la shell differente (bash nel sistema NFS e msh nel sistema
standalone) che crea processi in modi differenti e che non riescono ad
essere visti dalla kill().
eh io partirei da li
e vedere cosa fa kill() quando gli dai un pid negativo per ricavarsi
l'albero dei pid , perchè magari lui tenta semplicemente di killare un
pid negativo, da cui l'errore.
> Magari è la shell differente (bash nel sistema NFS e msh nel sistema
> standalone) che crea processi in modi differenti e che non riescono ad
> essere visti dalla kill().
potrebbe essere, mai usata msh
> Come posso fare per killare uno script e gli eventuali comandi lanciati
> nello script e ancora in fase di esecuzione?
Se usi bash, puoi provare con il comando trap:
#!/bin/bash
trap "cleaner" EXIT
cleaner() {
for proc in $(jobs -p); do
kill -15 $proc;
done
}
sleep 1000 &
sleep 1500 &
sleep 2000 &
wait
--
no signature yet
E sembra essere proprio così. La msh è una shell minimale riscritta in
busybox (busybox.net) che non crea i process group. Da quello che ho
capito, rimane una gerarchia dei processi (infatti riesco comunque a
vedere
il parent PID di ogni processo) ma non viene creato il process group
relativo agli script e ai comandi eseguiti all'interno.
Questo vuol dire che l'unico modo per killare il processo padre e
tutti i
processi figli è cercare tra _tutti_ i processi quelli con il parent
PID uguale
al processo padre.
Oppure l'altra soluzione è usare la bash che mi occupa un bel po' di
spazio.
ah ecco (l'avevo pure usata allora, ma ai in questo senso)
> Oppure l'altra soluzione č usare la bash che mi occupa un bel po' di
ma esattamente cosa devi fare?
perchč al limite fai tutto in c ed i pid dell'albero te li
tieni in un vettore (globale) che puoi svuotare in un signal handler
forse un po' tortuoso ma dovrebbe andare:
insomma per ogni comando che vuoi dare:
int pid_table[MAXCHILD]
main()
{
signal(SIGTERM, Handler);
signal(SIGINT, Handler);
pid=fork()
if(!pid)
execl(blabla)
else
appenti pid alla pid_table
ciclo di wait sui pid:
se morto elimina dalla pid_table
}
void Handler()
{
for i in pid_table
kill(i)
}
ovviamente ho scritto tutto in una pseudo_amorfo codifica :)
ma il succo si dovrebbe capire
un robo del genere in python verrebbe di certo molto piu' semplice, ma di
sicuro il python non ce l'hai
Hai il /proc ? Se si, un grep in tutti i proc/<ppid>/status per il 'PPid:' del
padre dovrebbe risolvere il tuo problema...
Infatti ho creato uno script semplice che tramite il comando ps mi
tira
fuori tutti i PID dei processi e tramite il grep mi tira fuori solo i
PID che
hanno un certo PPid.
E' un po' lentino, ma lo devo fare solo qualche volta.
Però questa msh è proprio minimale. Ho un altro problema che credo
sia sempre legato a questa shell.
Ho due script:
---
root:/tmpfs/tmp> cat example.sh
#!/bin/sh
while true; do
read X
./flash.sh &
FLASHPID=$!
sleep 5
kill $FLASHPID
done
---
root:/tmpfs/tmp> cat flash.sh
#!/bin/sh
while true; do
echo "ON"
sleep 1
echo "OFF"
sleep 1
done
---
In pratica il primo script, sempre in esecuzione, aspetta un evento
(in questo caso, l'input da tastiera) e successivamente mette in
esecuzione
un altro processo (flash.sh).
Quindi example.sh inizia a fare certi compiti che durano un po' (in
questo
caso una semplice sleep) e dopo killa flash.sh per rimettersi in
attesa
dell'evento.
La prima volta tutto funziona e example.sh si rimette in attesa
dell'evento.
Appena premo di nuovo RET, lo script principale example.sh termina
brutalmente (mi compare killed) non appena cerca di eseguire
nuovamente
flash.sh.
Mi sa che la distribuzione dei segnali con msh è alquanto scarsa.
mmmmm....
non è che quel killed si riferisce al'ultimo flash che avevi ucciso?
che (ipotesi) tornando dal background uccide il padre perchè la shell è
scarsa nel gestire questo tipo di cose?
ed esempio, prova a mettere una sleep 2 dopo il kill e vedi se comunque ti
fa arrivare alla read o muore
Cavolo, non ci riesco ancora!
Con un po' di sforzo sono riuscito a ritargliarmi lo spazio per una
bash completa.
Ora l'esempio semplice funziona:
---
sh-3.00# cat prova.sh
#!/bin/bash
sleep 100
sh-3.00# ./prova.sh &
[1] 2545
sh-3.00# /bin/kill -s TERM -2545
sh-3.00#
[1]+ Terminated ./prova.sh
sh-3.00#
--
Notare che ho usato il comando kill esterno alla shell perchè quello
builtin
non supporta i PID negativi per i process group.
La mia applicazione finale, però, sarà formata da due script: il primo
esegue o killa il secondo a seconda del parametro start/stop (ebbene
sì,
è uno script di /etc/init.d).
In questo caso non funziona:
---
sh-3.00# cat startstop.sh
#!/bin/bash
case "$1" in
start)
echo "Starting prova.sh..."
./prova.sh &
;;
stop)
echo "Stopping prova.sh..."
PID=`pidof -o $$ prova.sh`
if [ ! -z $PID ]; then
/bin/kill -s TERM -$PID
fi
;;
esac
sh-3.00# ./startstop.sh start
Starting prova.sh...
sh-3.00# ./startstop.sh stop
Stopping prova.sh...
kill -2575: No such process
---
Evidentemente il process group viene creato dalla bash al lancio del
primo
script startbutton.sh che rappresenta anche il process group leader.
Tutti i
comandi lanciati da startbutton.sh appartengono allo stesso process
group.
Però startbutton.sh termina il suo lavoro e, probabilmente, cancella
anche
il processo group che aveva creato, lasciando il processo prova.sh (ed
anche
il comando sleep lanciato da questi) privo di process group.
Non so che altro pensare.
Riprendo una tua precedente affermazione:
> Process group = il processo + tutti i suoi figli.
Da quello che ho capito, non è detto. Un process group è un gruppo
eterogeneo
di processi che hanno in comune lo stesso identificativo di gruppo,
nient'altro.
Con opportune funzioni come setpgid() è possibile assegnare un
processo qualsiasi
(con alcune restrizioni) ad un gruppo, anche se questi non è un figlio
del leader
del gruppo (ecco perchè, forse, parlano nella documentazione di group
leader e
non semplicemente di parent process).
In genere la shell, almeno la bash, assegna lo stesso identificativo
di gruppo
ai comandi lanciati da uno script. Ma se muore il group leader che
succede?
non esiste mica solo bash...
ci sono anche tcsh, zsh, ash...
ilSimo
--
now playing:
Le barche mi danno il voltastomaco.
Piu' delle barche odio soltanto gli elfi.
-- Gotrek Gurnisson
da "Sangue di Skaven"
Ho capito l'arcano!
Quando eseguo lo script "startstop.sh start", viene creato un processo
con
un proprio PID, diciamo 100. Viene creato anche un process group in
cui
lo script startstop.sh è il leader, quindi l'identificativo del gruppo
(PGID) è
pari al PID del leader, nel nostro esempio 100.
startstop.sh crea un processo figlio, prova.sh, che ha un altro PID,
diciamo 101.
Questo processo figlio viene assegnato al gruppo PGID=100.
A questo punto, startstop.sh finisce il suo lavoro visto che prova.sh
è stato
lanciato in background e quindi termina. Non esiste più il processo
con PID=100
ma rimane un processo (prova.sh) con PGID=100.
prova.sh lancia "sleep 100" che, a sua volta, avrà PID=102 (esempio) e
PGID=100.
Quindi il comando ps mostrerà
prova.sh con PID=101 e PGID=100
sleep 100 con PID=102 e PGID=100
Per terminare il process group bisogna usare
/bin/kill -s TERM -100
e non
/bin/kill -s TERM -101
come facevo prima.
Il problema è ora capire qual è il PGID che è il PID del primo
startstop.sh che
ormai non c'è più. Con il comando ps full-features è semplice (ps -o
pgid),
nel mio caso dovrei guardare a /proc, ma non so dove.è l'informazione.
kill è una chiamata di sistema.
Quindi desumo che in libc la funzione kill ingloba uno strato
assembler per effettuare la syscall
http://linux.die.net/man/2/syscalls
>
> eh io partirei da li
> e vedere cosa fa kill() quando gli dai un pid negativo per ricavarsi
> l'albero dei pid , perchè magari lui tenta semplicemente di killare un
> pid negativo, da cui l'errore.
E' un punto di partenza, se non è li probabilmente occorrerà leggersi i
sorgenti del kernel
>
>> Magari è la shell differente (bash nel sistema NFS e msh nel sistema
>> standalone) che crea processi in modi differenti e che non riescono ad
>> essere visti dalla kill().
I segnali possono essere trappati ad eccezione del SIGKILL (kill per
default invia un SIGTERM)
Inoltre quando un processo è in kernel mode probabilmente neanche la
SIGKILL ha effetto
Fai fare un wait al primo processo (startstop.sh) anziche` farlo uscire...
In realtà poi ho risolto andando in /proc/<pid>/stat e leggendo il
quinto numero. Rappresenta proprio il PGID.
Poi mi sono ripreso un po' di spazio cancellando la bash (roba troppo
pesante per il mio sistema) e mettendo su la ash che gestisce
correttamente i process group.
Tra l'altro ho scoperto che una variante della ash (la dash, Debian ash)
viene ampiamente usata in Ubuntu dove /bin/sh è un link simbolico
proprio a /bin/dash in modo da eseguire gli script in modo più rapido,
visto che la (d)ash è molto più snella della bash.
Questo mi fa sperare che la ash sia molto simile alla bash, anche se
alcuni script scritti per bash potrebbero incaxxarsi con la ash.