comunicazione tra più processi


13

Ho uno script bash, che esegue la funzione manager () come processo separato per x-times. Come è possibile inoltrare i messaggi a tutti i processi manager () dall'interno dello script?

Ho letto di pipe anonime ma non ho idea di come condividere i messaggi con esso .. Ho provato a farlo con named pipe, ma sembra che dovrei creare una pipe con nome separata per ogni processo?

Qual è il modo più elegante per farlo?

Ecco il mio codice finora:

#!/bin/bash

manager () {
    while :
    do
        echo "read what has been passed to \$line"
    done
}

x=1
while [ $x -le 5 ]
do
  manager x &
  x=$(( $x + 1 ))
done


while :
do
while read line
do
  echo "What has been passed through the pipe is ${line}"
  # should pass $line to every manager process

done < $1
done

exit 0

Risposte:


25

Il termine per ciò che stai cercando di realizzare è il multiplexing .

Questo può essere realizzato abbastanza facilmente in bash, ma richiede alcune funzionalità bash più avanzate.

Ho creato uno script basato sul tuo, che penso faccia quello che stai cercando di realizzare. Lo spiego di seguito.

#!/bin/bash
manager() {
  while IFS= read -r line; do
    echo "manager[$1:$BASHPID]: $line"
  done
}

fds=()
for (( i=0; i<5; i++ )); do
  exec {fd}> >(manager $i)
  fds+=( $fd )
done

while IFS= read -r line; do
  echo "master: $line"
  for fd in "${fds[@]}"; do
    printf -- '%s\n' "$line" >&$fd
  done
done

managerè una funzione bash che legge semplicemente da STDIN e scrive il suo identificatore e la linea su STDOUT. Usiamo al $BASHPIDposto di $$as $$non viene aggiornato per i subshells (che è quello che useremo per il lancio manager.

fdsè un array che conterrà i descrittori di file che puntano alle pipe STDIN delle varie managers generate.
Quindi eseguiamo il loop e creiamo 5 processi manageriali. Uso la for (( ))sintassi invece del modo in cui la stavi facendo perché è più pulita. Questo è specifico per Bash, ma molte delle cose che questo script fa sono specifiche per Bash, quindi potrebbe anche andare fino in fondo.
 

Quindi arriviamo a exec {fd}> >(manager $i). Questo fa molte altre cose specifiche.
Il primo dei quali è {fd}>. Questo prende il successivo descrittore di file disponibile sul o dopo il numero 10, apre una pipe con il lato di scrittura della pipe assegnato a quel descrittore di file e assegna il numero del descrittore di file alla variabile $fd.

Il >(manager $i)lancio manager $ie sostanzialmente sostituisce >(manager $i)con un percorso a un STDIN di quel processo. Quindi, se è managerstato lanciato come PID 1234, >(manager $i)potrebbe essere sostituito con /proc/1234/fd/0(dipende dal sistema operativo).

Quindi, supponendo che il prossimo numero del descrittore di file disponibile sia 10 e che il gestore venga avviato con PID 1234, il comando exec {fd}> >(manager $i)sostanzialmente diventa exec 10>/proc/1234/fd/0, e bash ora ha il descrittore di file che punta a STDIN di quel gestore.
Quindi, poiché bash inserisce il numero del descrittore di file $fd, aggiungiamo tale descrittore all'array fdsper un uso successivo.
 

Il resto è piuttosto semplice. Il master legge una riga da STDIN, scorre su tutti i descrittori di file in $fdse invia la riga a quel file desciptor ( printf ... >&$fd).

 

Il risultato è simile al seguente:

$ /tmp/test.sh
hello
master: hello
manager[0:8876]: hello
manager[1:8877]: hello
manager[4:8880]: hello
manager[2:8878]: hello
manager[3:8879]: hello
world
master: world
manager[0:8876]: world
manager[1:8877]: world
manager[3:8879]: world
manager[2:8878]: world
manager[4:8880]: world

Dove ho scritto helloe world.


@Patrick the is funziona, ma hai un refuso, {fd} e dovrebbe essere $ {fd}
c4f4t0r

3
@ c4f4t0r Non è un refuso
Patrick,

@Patrick on suse 11 "bash type.bash type.bash: riga 10: exec: {fd}: non trovato" ho cambiato in exec $ {fd} e funziona così
c4f4t0r

2
@ c4f4t0r La versione di bash in open suse 11 è piuttosto antica (3.2). Questa funzione è stata implementata in bash 4.0.
Patrick,

Grazie per molte buone informazioni! Un nitpick: posso capire perché dovresti dire echo -- "$line"o printf "%s\n" "$line"- ma perché dovresti usare --quando il prossimo argomento è hardcoded (e non inizia con -)?
Scott,

0

teee bash:

cat foo | tee >(manager) >(manager) >(manager) >(manager) >(manager) >/dev/null

Se il numero di gestori deve essere configurabile o se si desidera che l'output di gestori diversi non sia miscelato:

export -f manager
cat foo | parallel --pipe --tee manager ::: {1..10}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.