AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 685305
Accepted
Marcus Müller
Marcus Müller
Asked: 2022-01-07 04:27:44 +0800 CST2022-01-07 04:27:44 +0800 CST 2022-01-07 04:27:44 +0800 CST

Pipes nomeados do Linux: não tão FIFO quanto se pensava

  • 772

Resumidamente:

mkfifo fifo; (echo a > fifo) &; (echo b > fifo) &; cat fifo

O que eu esperava:

a
b

já que o primeiro echo … > fifodeve ser o primeiro a abrir o arquivo, então espero que esse processo seja o primeiro a gravar nele (com o desbloqueio aberto primeiro).

O que eu ganho:

b
a

Para minha surpresa, esse comportamento também aconteceu ao abrir dois terminais separados para fazer a escrita em processos definitivamente independentes.

Estou entendendo mal alguma coisa sobre a semântica de primeiro a entrar, primeiro a sair de um pipe nomeado?

Stephen sugeriu adicionar um atraso:

#!/usr/bin/zsh
delay=$1
N=$(( $2 - 1 ))
out=$(for n in {00..$N}; do
  mkfifo /tmp/fifo$n
  (echo $n > /tmp/fifo$n) &
  sleep $delay
  (echo $(( $n + 1000 )) > /tmp/fifo$n )&
  # intentionally using `cat` here to not step into any smartness
  cat /tmp/fifo$n | sort -C || echo +1
  rm /tmp/fifo$n
done)
echo "$(( $res )) inverted out of $(( $N + 1 ))"

Agora, isso funciona 100% correto ( delay = 0.1, N = 100).

Ainda assim, a execução mkfifo fifo; (echo a > fifo) &; sleep 0.1 ; (echo b > fifo) &; cat fifomanual quase sempre produz a ordem invertida.

Na verdade, mesmo copiar e colar o forpróprio loop falha na metade das vezes. Estou muito confuso sobre o que está acontecendo aqui.

linux fifo
  • 2 2 respostas
  • 3542 Views

2 respostas

  • Voted
  1. Best Answer
    Stephen Kitt
    2022-01-07T04:31:18+08:002022-01-07T04:31:18+08:00

    Isso não tem nada a ver com a semântica FIFO de pipes e não prova nada sobre eles de qualquer maneira. Tem a ver com o fato de que as FIFOs bloqueiam a abertura até que sejam abertas tanto para escrita quanto para leitura; então nada acontece até que seja cataberto fifopara leitura.

    já que o primeiro echodeve ser o primeiro.

    Iniciar processos em segundo plano significa que você não sabe quando eles serão realmente agendados, portanto, não há garantia de que o primeiro processo em segundo plano fará seu trabalho antes do segundo. O mesmo se aplica ao desbloqueio de processos bloqueados .

    Você pode melhorar as chances, enquanto ainda usa processos em segundo plano, atrasando artificialmente o segundo:

    rm fifo; mkfifo fifo; echo a > fifo & (sleep 0.1; echo b > fifo) & cat fifo
    

    Quanto maior o atraso, melhores as chances: echo a > fifoblocos esperando para terminar abrindo fifo, catinicia e abre o fifoque desbloqueia echo ae depois echo broda.

    Porém o principal fator aqui é quando catabre o FIFO: até então, os shells bloqueiam tentando configurar os redirecionamentos. A ordem de saída vista depende, em última análise, da ordem em que os processos de escrita são desbloqueados.

    Você obterá resultados diferentes se executar catprimeiro:

    rm fifo; mkfifo fifo; cat fifo & echo a > fifo & echo b > fifo
    

    Dessa forma, a abertura fifopara escrita tenderá a não bloquear (ainda, sem garantias), então você verá aprimeiro com uma frequência maior do que na primeira configuração. Você também verá cato acabamento antes das echo bexecuções, ou seja , apenas aa saída.

    • 46
  2. Gilles 'SO- stop being evil'
    2022-01-07T05:40:54+08:002022-01-07T05:40:54+08:00

    Os tubos são primeiro a entrar, primeiro a sair. Seu problema é que você não entende quando o “in” acontece. O evento “in” está escrevendo , não abrindo.

    Removendo a pontuação inútil, seu código é:

    echo a > fifo & echo b > fifo &
    

    Isso executa os comandos echo a > fifoe echo b > fifoem paralelo. O que quer que entre primeiro sairá primeiro, mas é uma corrida mais ou menos igual para saber quem entra primeiro.

    Se você quer aser lido antes b, você tem que providenciar para escrevê-lo antes b. Isso significa que você tem que esperar até que echo a > fifoseja feito antes de começar echo b > fifo.

    { echo a > fifo; echo b > fifo; } & cat fifo
    

    Se você quiser ir mais longe, precisa distinguir entre as operações elementares que acontecem sob o capô. echo a > fifocombina três operações:

    1. Aberto fifopara escrita.
    2. Escreva dois caracteres ( ae uma nova linha) no arquivo.
    3. Feche o arquivo.

    Você pode fazer com que essas operações aconteçam em momentos diferentes:

    (
        exec >fifo     # 1. open
        sleep 1
        echo a         # 2. write
        sleep 1
    )                  # 3. close
    

    Da mesma forma, cat foocombina uma operação de abertura, leitura e fechamento. Você pode separá-los:

    (
        exec <fifo     # 1. open
        sleep 1
        read line      # 2. read
        echo $line
        sleep 1
    )                  # 3. close
    

    (O readshell embutido pode realmente fazer várias readchamadas de sistema, mas isso não é importante agora.)

    Fifos na verdade não são bem tubos. Eles são mais como tubos em potencial. O fifo é uma entrada de diretório e um objeto pipe é criado quando um processo abre o fifo para leitura. Se um processo abre o fifo para escrita enquanto não existe pipe, a openchamada é bloqueada até que um pipe seja criado. Além disso, se um processo abre o fifo para leitura, esta operação também bloqueia até que um processo abra o fifo para escrita (a menos que o leitor abra o pipe no modo não bloqueante, o que não é conveniente do shell). Como consequência, o primeiro aberto para leitura e o primeiro aberto para escrita em um pipe nomeado retornarão ao mesmo tempo.

    Aqui está um script de shell que coloca esse conhecimento em ação.

    #!/bin/sh
    tick () { sleep 0.1; echo tick; echo 0.1; }
    mkfifo fifo
    {
        exec <fifo; echo >&2 opened for reading;
        echo a; echo >&2 wrote a
    } & writer=$!
    tick
    {
        exec >fifo; echo >&2 opened for writing;
        exec cat >&2;
    } & reader=$!
    wait $writer
    kill $reader
    rm fifo
    

    Observe como ambas as aberturas acontecem ao mesmo tempo (o mais próximo que podemos observar). E a escrita só pode acontecer depois disso.

    Nota: na verdade, há uma condição de corrida no script acima — mas não está relacionada aos tubos. Os echo >&2comandos estão correndo contra para cat >&2gravar no terminal e, portanto, você pode ver antes e . Se você quiser ter uma visão mais precisa do tempo, você pode rastrear as chamadas do sistema. Por exemplo, no Linux:acatopening for writingwrote a

    mkfifo fifo
    strace -f -P fifo sh -c '…'
    

    Agora se você colocar dois escritores, ambos os escritores vão bloquear na etapa de abertura até que o leitor chegue. Não importa quem inicia a openchamada primeiro: os pipes são os primeiros a entrar, primeiro a sair, não para tentativas abertas. Quem escreve primeiro é o que importa. Aqui está um script para experimentar isso.

    #!/bin/sh
    mkfifo fifo
    {
        exec >fifo; echo >&2 opened for writing a
        sleep $1
        echo a; echo >&2 wrote a
    } & writer_a=$!
    {
        exec >fifo; echo >&2 opened for writing b
        sleep $2
        echo b; echo >&2 wrote b
    } & writer_b=$!
    sleep 0.2
    cat fifo & reader=$!
    wait $writer_a
    wait $writer_b
    kill $reader
    rm fifo
    

    Chame o script com dois argumentos: o tempo de espera para o leitor a e o tempo de espera para o escritor b. O leitor fica online após 0,2 segundos. Se ambos os tempos de espera forem inferiores a 0,2 segundos, ambos os escritores tentarão escrever assim que os escritores ficarem online, e é uma corrida. Por outro lado, se os tempos de espera forem superiores a 0,2, quem chegar primeiro obtém a saída primeiro.

    $ ./c 0.1 0.1
    # Order of "opened for writing": random
    # Order of "a"/"b": random
    # Order of "wrote": random, might not match a/b due to echo racing against each other
    $ ./c 0.3 0.4
    # Order of "opened for writing": random
    # Order of "wrote": a then b
    # Order of "a"/"b": a then b
    
    • 32

relate perguntas

  • Existe uma maneira de fazer ls mostrar arquivos ocultos apenas para determinados diretórios?

  • Inicie/pare o serviço systemd usando o atalho de teclado [fechado]

  • Necessidade de algumas chamadas de sistema

  • astyle não altera a formatação do arquivo de origem

  • Passe o sistema de arquivos raiz por rótulo para o kernel do Linux

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve