Usando Arquivos FIFO

Introdução

Uma das características mais importantes de sistemas baseados no Unix é a facilidade que processsos diferentes tem de se comunicar. As formas de comunicação entre processos são chamadas de IPC (Inter-Process Comunication) e as mais usadas são unix socket, pipes e named pipes.

Nesse post iremos falar um pouco mais sobre os named pipes também chamados de FIFO.

Os pipes (Unnamed pipes ou Anonymous pipes)

Os pipes são usados muito frequentemente como IPC. Considere o comando do shell:

$ ls | grep linux

O shell irá ler a linha acima e após verificar que o caractere ‘|’ (pipe) está presente, ele criará dois processos, um para executar o ls e outro o grep. Em seguida, a saída (stdout) do ls será conectada na entrada (stdin) do grep. Esse tipo de comunicação é feita internamente pelo kernel e só existe durante a execução desses dois processos.

Porém, nesse tutorial veremos um outro tipo de comunicação entre processos, o chamado FIFO.

FIFO (Named pipes)

Os named pipes fornecem uma maneira de IPC usando o sistema de arquivos, isso é, um arquivo em disco é criado para representar uma futura comunicação. Toda vez que dois processos precisarem se comunicar só é preciso que eles acessem esse arquivo. A comunicação do FIFO é dita half-duplex, o que significa que os dois processos utilizam o mesmo canal de comunicação porém um processo somente lê ou escreve e nunca os dois ao mesmo tempo.

Características dos FIFOs

Antes de criarmos e usarmos os FIFOs, vamos analisar primeiro suas características:

  • Leitura/Escrita bloqueada: Se um processo abre um FIFO para leitura, ele será bloqueado até que outro processo abra esse mesmo FIFO para escrita, e vice-versa.
  • Sincronização e integridade dos dados: O kernel garante que todos os dados serão sincronizados – a ordem em que eles são enviados será a ordem em que eles serão lidos. O kernel garante também que nenhum dado será perdido durante a comunicação.
  • Persistência além da vida dos processos: Como um named pipe é um arquivo no sistema, ele estará disponível após a vida dos processos (ao contrário dos pipes que persistem somente durante a vida os processos envolvidos). Para apagar um named pipe é preciso fazer isso manualmente com o comando rm.
  • Permissões associadas: Os named pipes têm permissões associadas a eles que podem ser usadas para restringir quem poderá iniciar uma comunicação, aumentando a segurança do sistema. [1]

Limitações dos FIFOs

Algumas limitações:

  • Named pipes só podem ser usados por processos na mesma máquina.
  • Named pipes dependem do sistema de arquivo em questão, então há situações que não podemos criá-los, como em sistemas NFS e sistemas montados somente para leitura.
  • Devido a natureza de bloquear os processos, a programação usando named pipes deve ser feita com cuidado para evitar deadlocks. Um exemplo em que um deadlock pode ocorrer é quando um processo A está esperando algo do processo B e este está conectado no FIFO esperando dados do processo A.

Como criar um FIFO no shell

Em sistemas mais novos para se criar um FIFO usamos o comando mkfifo:

$ mkfifo meufifo

Um FIFO de nome meufifo foi criado. Para verificar, digite:

$ ls -la meufifo
prw-rw-r--. 1 daemonio daemonio 0 Out  1 12:31 myfifo|

O ‘p’ no início e o ‘|’ no fim indicam que o arquivo é um FIFO.

Usando FIFOs

Vamos criar uma comunicação entre dois processos usando named pipes. Primeiro abra dois terminais e digite em um deles:

$ mkfifo meufifo
$ cat /etc/passwd > meufifo

Nesse momento há somente um processo, o cat, conectado no FIFO e ele estará bloqueado até que outro processo participe da comunicação. Como o redirecionamento ‘>’ foi usado, o named pipe foi aberto para escrita pelo shell.

No outro terminal podemos ter um leitor ou até mesmo outro escritor. Nesse exemplo teremos um processo leitor para obter os dados enviados pelo cat:

$ mkfifo meufifo
$ cat meufifo | head -n 5
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

Quantidade de processos em um FIFO

Podem existir vários processos escrevendo e lendo no mesmo FIFO, mas no caso da leitura somente o primeiro processo obterá os dados.

Para um exemplo de múltiplos escritores, digite a sequência de comandos:

$ cat /etc/passwd > myfifo &
$ cat /etc/issue > myfifo &
$ cat myfifo
# Conteudo do arquivo /etc/issue
Fedora release 15 (Lovelock)
Kernel \r on an \m (\l)
# Conteudo do arquivo /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
...

Temos dois comandos escrevendo no FIFO e um realizando a leitura. O interessante aqui é que a saída saiu invertida, ou seja, o conteúdo de /etc/issue saiu primeiro que o do /etc/passwd. Isso aconteceu porque o kernel empilhou os comandos e por isso o último comando foi retirado e executado primeiro.

Exemplos

Abaixo alguns exemplos interessantes usando named pipes (a maioria retirada da referência [3]):

1) Ver tudo o que um usuário faz

O comando script grava em um arquivo tudo o que acontece no shell do usuário desde os comandos digitados e até a saída desses comandos. Colocando o script para escrever em um FIFO é possível que qualquer usuário com permissão de leitura nesse arquivo tenha acesso a tudo que está acontecendo no shell do usuário que invocou o comando script.

$ mkfifo /tmp/fifo; script -f /tmp/fifo # usuario1

O comando script ficará bloqueado, pois ainda não há ninguém lendo o FIFO. Para prosseguir o outro usuário deve digitar:

$ cat /tmp/fifo # usuario2

Pronto! Agora tudo que o usuario1 realizar em seu terminal poderá ser visto pelo usuario2.

2) Bindar uma shell com o netcat sem usar a opção -e

Essa dica eu vi primeiramente no site do Meleu[4] e depois em [3], e ela pode ser usada em versões do netcat que não foram compiladas com a opção -e:

$ mkfifo /tmp/fifo; nc -l -p 6060 < /tmp/fifo | /bin/sh > /tmp/fifo

Quando alguém se conectar na porta 6060, tudo o que ele digitar irá para o pipe e será executado pelo /bin/sh. A saída do shell irá para o arquivo /tmp/fifo que também serve de entrada para o netcat, assim toda a saída do shell será transmitida para o outro nó da conexão.

Para mais detalhes veja a referência [4].

* OBS: Para versões mais novas do netcat (Ubuntu, Fedora, etc), o comando correto é: nc -l 6060 (sem a opção -p).

3) Redirecionando e alterando tráfego

$ mkfifo /tmp/fifo; cat /tmp/fifo | nc -l -p 8080 | tee -a log.1 | nc www.example.com 80 > /tmp/fifo

Aqui vemos um exemplo clássico de redirecionamento de dados. Quando um cliente se conectar na porta 8080 do servidor, ele será redirecionado para a máquina http://www.example.com:80. Os dados do cliente são gravados no arquivo log.1 para que alguma análise posterior possa ser feita.

Aqui listei somente alguns exemplos. Para uma lista mais elaborada, veja a referência [3].

Referências

[1] Introduction to Interprocess Communication Using Named Pipes, by Faisal Faruqui (Acessado em: Outubro/2011)
http://developers.sun.com/solaris/articles/named_pipes.html

[2] Introduction to Named Pipes, by Andy Vaught (Acessado em: Outubro/2011)
http://www.linuxjournal.com/article/2156

[3] Commands using mkfifo (Acessado em: Outubro/2011)
http://www.commandlinefu.com/commands/using/mkfifo

[4] netcat sem -e, by Meleu (Acessado em: Outubro/2011)
http://mdicas.blogspot.com/2008/05/netcat-sem-e.html

Deixe um comentário