você está aqui: Home  → Arquivo de Mensagens

Bash - Coprocessos

Colaboração: Júlio Cezar Neves

Data de Publicação: 21 de setembro de 2017

A partir da versão 4.0, o Bash incorporou o comando coproc. Este novo intrínseco (builtin) permite dois processos assíncronos se comunicarem e interagirem. Como cita Chet Ramey no Bash FAQ, ver. 4.01:

"Há uma nova palavra reservada, coproc, que especifica um coprocesso: um comando assíncrono que é executado com 2 pipes conectados ao Shell criador. coproc pode receber nome. Os descritores dos arquivos de entrada e saída e o PID do coprocesso estão disponíveis para o Shell criador em variáveis com nomes específicos do coproc."

George Dimitriu explica:

"coproc é uma facilidade usada na substituição de processos que agora está publicamente disponível."

A comprovação do que disse Dimitriu não é complicada, quer ver? Veja a substituição de processos a seguir:

 $ cat <(echo xxx; sleep 3; echo yyy; sleep 3)

Viu?! O cat não esperou pela conclusão dos comandos entre parênteses, mas foi executado no fim de cada um deles. Isso aconteceu porque estabeleceu-se um pipe temporário/dinâmico e os comandos que estavam sendo executados, mandavam para ele as suas saídas, que por sua vez as mandava para a entrada do cat.

Isso significa que os comandos desta substituição de processos rodaram paralelos, sincronizando somente nas saídas dos echo com a entrada do cat.

A sintaxe de um coprocesso é:

 coproc [nome] cmd redirecionamentos 

Isso criará um coprocesso chamado nome. Se nome for informado, cmd deverá ser um comando composto. Caso contrário (no caso de nome não ser informado), cmd poderá ser um comando simples ou composto.

Quando um coproc é executado, ele cria um vetor com o mesmo nome nome no Shell criador. Sua saída padrão é ligada via um pipe a um descritor de arquivo associado à variável ${nome[0]} à entrada padrão do Shell pai (lembra que a entrada padrão de um processo é sempre associada por default ao descritor zero?). Da mesma forma, a entrada do coproc é ligada à saída padrão do script, via pipe, a um descritor de arquivos chamado ${nome[1]}.

Assim, simplificando, vemos que o script mandará dados para o coproc pela variável ${nome[0]}, e receberá sua saída em ${nome[1]}.

Note que estes pipes serão criados antes dos redirecionamentos especificados pelo comando, já que eles serão as entradas e saídas do coprocesso.

A partir daqui, vou detalhar rapidamente uns estudos que fiz. Isso contém um pouco de divagações e muita teoria. Se você pular para depois desses ls's**, não perderá nada, mas se acompanhar, pode ser bom para a compreensão do mecanismo do coproc.

Após colocar um coproc rodando, se ele está associado a um descritor de arquivo, vamos ver o que tem ativo no diretório correspondente:

$ ls -l /dev/fd 
lrwxrwxrwx 1 root root 13 2010-01-06 09:31 /dev/fd -> /proc/self/fd

Hummm, é um link para o /proc/self/fd... O que será que tem lá?

$ ls -l /proc/self/fd
total 0
lrwx------ 1 julio julio 64 2010-01-06 16:03 0 -> /dev/pts/0
lrwx------ 1 julio julio 64 2010-01-06 16:03 1 -> /dev/pts/0
lrwx------ 1 julio julio 64 2010-01-06 16:03 2 -> /dev/pts/0
lr-x------ 1 julio julio 64 2010-01-06 16:03 3 -> /proc/3127/fd

Epa, que o 0, 1 e 2 apontavam para /dev/pts/0 eu já sabia, pois são a entrada padrão, saída padrão e saída de erros padrão apontando para o pseudo terminal corrente, mas quem será esse maldito device 3? Vejamos:

$ ls -l /proc/$$/fd //($$ É o PID do Shell corrente)//
total 0
lr-x------ 1 julio julio 64 2010-01-06 09:31 0 -> /dev/pts/0
lrwx------ 1 julio julio 64 2010-01-06 09:31 1 -> /dev/pts/0
lrwx------ 1 julio julio 64 2010-01-06 09:31 2 -> /dev/pts/0
lrwx------ 1 julio julio 64 2010-01-06 16:07 255 -> /dev/pts/0
l-wx------ 1 julio julio 64 2010-01-06 16:07 54 -> pipe:[168521]
l-wx------ 1 julio julio 64 2010-01-06 16:07 56 -> pipe:[124461]
l-wx------ 1 julio julio 64 2010-01-06 16:07 58 -> pipe:[122927]
lr-x------ 1 julio julio 64 2010-01-06 16:07 59 -> pipe:[168520]
l-wx------ 1 julio julio 64 2010-01-06 16:07 60 -> pipe:[121302]
lr-x------ 1 julio julio 64 2010-01-06 16:07 61 -> pipe:[124460]
lr-x------ 1 julio julio 64 2010-01-06 16:07 62 -> pipe:[122926]
lr-x------ 1 julio julio 64 2010-01-06 16:07 63 -> pipe:[121301]

Epa, aí estão os links apontando para os pipes. Esse monte de arquivo de pipe que foi listado, deve ser porque estava testando exaustivamente essa nova facilidade do Bash.

Para terminar esta teoria chata, falta dizer que o PID do Shell gerado para interpretar o coproc pode ser obtido na variável $nome_PID e o comando wait pode ser usado para esperar pelo fim do coprocesso. O código de retorno do coprocesso ($?) é o mesmo de cmd.

Exemplos:

Vamos começar com o mais simples: um exemplo sem nome e direto no prompt:

$ coproc while read Entra //coproc ativo//
> do
> echo
-=-=- $Entra -=-=-
> done
[2] 3030
$ echo Olá >&${COPROC[1]}      //Manda Olá para a pipe da saída//
$ read -u ${COPROC[0]} Sai     //Lê do pipe da entrada//
$ echo $Sai
-=-=- Olá -=-=-
$ kill $COPROC_PID               //Isso não pode ser esquecido...//

Como você viu, o vetor COPROC, está associado a dois pipes; o ${COPROC[1]} que contém o endereço do pipe de saída, e por isso a saída do echo está redirecionada para ele e o ${COPROC[0]} que contém o endereço do pipe de entrada, e por isso usamos a opção -u do<samp>read</samp> que lê dados a partir de um descritor de arquivo definido, ao invés da entrada padrão.

Como o coprocesso utilizava a sintaxe sem nome, o padrão do nome do vetor é COPROC.

Só mais uma teoriazinha chata:

$ echo ${COPROC[@]} //Lista todos os elementos do vetor//
59 54

Como você viu ${COPROC[0]} estava usando o pipe apontado por /proc/$$/fd/59 e ${COPROC[1]} usava /proc/$$/fd/54.

Agora chega de teoria mesmo! Vamos agora usar nome neste mesmo exemplo, para ver que pouca coisa muda:

$ coproc teste { 
> while read Entra 
> do 
> echo -=-=- $Entra -=-=- 
> done 
> } 
[6] 3192
$ echo Olá &${teste[1]} 
$ read -u ${teste[0]} Sai 
$ echo $Sai 
-=-=- Olá -=-=-
$ kill $teste_PID

Nesse momento, é bom mostrar uma coisa interessante: Quais são os processos em execução?

$ ps //Somente um Bash em execução// 
PID TTY TIME CMD
1900 pts/0 00:00:01 bash
2882 pts/0 00:00:00 ps

Vamos executar 2 coprocessos simultâneos:

$ coproc nome1 { //Coprocesso nome1// 
> while read x
> do
> echo $x
> done; }
[1] 2883
$ coproc nome2 { //Coprocesso nome2// 
> while read y
> do
> echo $y
> done; }
bash: aviso: execute_coproc: coproc [2883:nome1] still exists
[2] 2884

Xiiii! Acho que deu zebra! Mas será que deu mesmo? Repare que além do PID 2883 de nome1,</samp> ele também me devolveu o PID 2884, que deve ser de nome2. Vamos ver o que está acontecendo:

$ ps 
PID TTY TIME CMD 
1900 pts/0 00:00:01 bash //Esse já existia// 
2883 pts/0 00:00:00 bash //Esse está executando nome1// 
2884 pts/0 00:00:00 bash //Esse está executando nome2// 
2885 pts/0 00:00:00 ps

Parece que foi só um aviso, pois os dois PIDs informados quando iniciamos os dois coprocessos, estão ativos. Então vamos testar esses 2 caras:

$ echo xxxxxxxxx >&${nome1[1]} //Mandando cadeia para nome1// 
$ echo yyyyyyyyy >&${nome2[1]} //Mandando cadeia para nome2// 
$ read -u ${nome1[0]} Recebe 
$ echo $Recebe 
xxxxxxxxx
$ read -u ${nome2[0]} Recebe 
$ echo $Recebe 
yyyyyyyyy
$ kill $nome1_PID 
$ kill $nome2_PID


Veja a relação completa dos artigos de Júlio Cezar Neves

 

 

Opinião dos Leitores

julio Neves
21 Set 2017, 14:36
Olá Jimmy,
concordo com vc, mas não consigo imaginar um exemplo de threads (que é o que o coproc manipula) que seja curto o suficiente para colocar-se em um artigo curto como esse.
Preferi enfatizar a teoria (que não é o que gosto de fazer) e dar exemplos curtos, para qdo vc precisar de um processamento paralelo, saber ele que pode ser tratado e ter um material de referência, pois este é o único material sobre o tema que conheço escrito em pt_BR.
SuShellso!
Jimmy
03 Nov 2010, 13:44
Seria interessante colocar exemplos práticos e úteis junto com a teoria.
Para ficar mais clara a utilização do recurso.

Um conceito novo como esse pode ser difícil de entender sem um caminho de aplicação na realidade...
*Nome:
Email:
Me notifique sobre novos comentários nessa página
Oculte meu email
*Texto:
 
  Para publicar seu comentário, digite o código contido na imagem acima
 


Powered by Scriptsmill Comments Script