Como um FIFO (pipe nomeado) difere de um tubo regular (|)? Pelo que entendi da Wikipedia , ao contrário de um pipe regular, um pipe FIFO "continua vivo" após o término do processo e pode ser excluído algum tempo depois.
Mas se o processo for baseado em um comando shell contendo um pipe ( cat x | grep y
), poderíamos "mantê-lo vivo após o processo" se o armazenarmos em uma variável ou arquivo, não é um FIFO?
Além disso, um canal regular também tem o primeiro stdout que obtém, como stdin para outro comando , então também não é uma espécie de canal primeiro a entrar, primeiro a sair?
"Pipe nomeado" é na verdade um nome muito preciso para o que é - é como um pipe normal, exceto que tem um nome (em um sistema de arquivos).
Um canal — o regular, sem nome ("anônimo") usado em
some-command | grep pattern
é um tipo especial de arquivo. E eu quero dizer arquivo, você lê e escreve nele como faz com qualquer outro arquivo. O Grep realmente não se importa¹ se está lendo de um canal em vez de um terminal³ ou um arquivo comum.Tecnicamente, o que acontece nos bastidores é que stdin, stdout e stderr são três arquivos abertos (descritores de arquivo) passados para cada execução de comando. Os descritores de arquivo (que são usados em cada syscall para ler/escrever/etc. arquivos) são apenas números; stdin, stdout e stderr são descritores de arquivo 0, 1 e 2. Portanto, quando seu shell configura, o
some-command | grep
que ele faz é algo assim:Pede ao kernel um canal anônimo. Não há nome, então isso não pode ser feito
open
como para um arquivo normal - em vez disso, é feito compipe
oupipe2
, que retorna dois descritores de arquivo.⁴Bifurca um processo filho (
fork()
cria uma cópia do processo pai; ambos os lados do pipe estão abertos aqui), copia o lado de gravação do pipe para fd 1 (stdout). O kernel tem um syscall para copiar os números do descritor de arquivo; édup2()
oudup3()
. Em seguida, fecha o lado de leitura e outra cópia do lado de gravação. Finalmente, ele usaexecve
para executarsome-command
. Como o tubo é fd 1, stdoutsome-command
é o tubo.Forks de outro processo filho. Desta vez, ele duplica o lado de leitura do pipe para fd 0 (stdin) e executa
grep
. Portanto, o grep lerá o pipe como stdin.Em seguida, espera que ambas as crianças saiam.
Nesse ponto, o kernel percebe que o canal não está mais aberto e o lixo o coleta. Isso é o que realmente destrói o tubo.
Um pipe nomeado apenas dá um nome a esse pipe anônimo, colocando-o no sistema de arquivos. Portanto, agora qualquer processo, em qualquer ponto no futuro, pode obter um descritor de arquivo para o canal usando uma chamada de sistema comum
open
. Conceitualmente, o canal não será destruído até que todos os leitores/gravadores o fechem e ele sejaunlink
removido do sistema de arquivos.²A propósito, é assim que os arquivos em geral funcionam no Unix.
unlink
(o syscall atrásrm
de ) apenas remove um dos nomes do arquivo; somente quando todos os nomes forem removidos e nada tiver o arquivo aberto, ele será realmente excluído. Algumas respostas aqui exploram isso:NOTAS DE RODAPÉ
grep pattern
no seu shell,grep
estará lendo no terminal. O único uso para isso que vem à mente é se você estiver prestes a colar algo no terminal.Acho que você está se confundindo entre a sintaxe do shell para pipelines e a programação subjacente dos sistemas Unix. Um pipe / FIFO é um tipo de arquivo que não é armazenado em disco, mas passa dados de um gravador para um leitor por meio de um buffer no kernel.
Um pipe/FIFO funciona da mesma forma se o gravador e o leitor se conectaram fazendo chamadas de sistema como
open("/path/to/named_pipe", O_WRONLY);
, ou com umpipe(2)
para criar um novo pipe anônimo e retornar descritores de arquivo aberto para os lados de leitura e gravação.fstat(2)
no descritor de arquivo de canal fornecerá a vocêsb.st_mode & S_IFMT == S_IFIFO
de qualquer maneira.Quando você corre
foo | bar
:pipe(2)
chamada de sistema para obter dois descritores de arquivo: a entrada e a saída de um pipe anônimo.fork()
retornou 0)stdout
para o fd de gravação comdup2(pipefd[1], 1)
execve("/usr/bin/foo", ...)
fork()
retornou o PID filho diferente de 0)stdin
do read fd comdup2(pipefd[0], 0)
execve("/usr/bin/bar", ...)
Você entra em uma situação muito semelhante se correr
foo > named_pipe & bar < named_pipe
.Um pipe nomeado é um ponto de encontro para processos estabelecerem pipes entre si.
A situação é semelhante a arquivos tmp anônimos versus arquivos com nomes. Você pode
open("/path/to/dir", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
criar um arquivo temporário sem nome (O_TMPFILE
), como se tivesse aberto"/path/to/dir/tmpfile"
comO_CREAT
e depois desvinculado, deixando você com um descritor de arquivo para um arquivo excluído.Usando
linkat
, você pode até vincular esse arquivo anônimo ao sistema de arquivos, dando-lhe um nome, se tiver sido criado comO_TMPFILE
. ( Você não pode fazer isso no Linux para arquivos que você criou com um nome e depois deletado. )