read timeout parâmetro (-t) não está funcionando?

4

Não sei como explicar o problema em geral, por isso vou usar este exemplo:

#!/bin/bash

cleanup() {
    rm "$myfifo"
    rm "$mylock"
    kill '$(jobs -p)'
}

writer() {
    for i in $(seq 0 100); do
        echo "$(date -R) writing \"$i\"."
        echo "$i" > "$myfifo"
    done
}

reader() {
    while true; do
        flock 3
        read -st 1 line
        status=$?
        if [ $status -eq 0 ]; then
            echo "$(date -R) reading \"$line\" in thread $1."
        else
            echo "$(date -R) status $status in thread $1.
            break
        fi
        flock -u 3
        sleep 10
    done 3<"$mylock" <"$myfifo"
}

trap cleanup EXIT

myfifo="$(mktemp)"
mylock="$(mktemp)"

rm "$myfifo"
mkfifo "$myfifo"

writer &

for i in $(seq 1 10); do
    reader $i &
    sleep 1
done

wait

Agora eu esperaria que os threads de leitura tivessem uma linha (ou algumas linhas), mas o processo de primeira leitura levará todas as linhas (em uma ordem aleatória que eu não entendo, mas tudo bem), coloque-a um buffer em algum lugar e todos os outros processos de leitura não receberão nenhuma linha.

Além disso, o parâmetro timeout fornecido para o comando read parece não funcionar porque os leitores 2-10 não saem.

  1. Por quê?
  2. Como posso consertar isso para que as linhas sejam (de certa forma) distribuídas uniformemente entre os leitores?
por Max Matti 20.03.2017 / 00:12

1 resposta

7

Deixando read timeout

read timeout realmente funciona. O problema aqui é que a abertura de um FIFO em blocos de modo de leitura até o FIFO é aberto no modo de escrita. E nesse caso, isso não é read que está bloqueado, isso é bash , ao redirecionar seu FIFO para stdin.

Quando algum outro processo abrir o FIFO para gravação, bash abrirá com sucesso o FIFO para leitura e executará o comando read (que terminará como esperado).

Se você estiver usando o Linux, a página man do fifo nos diz que " abrir um FIFO para leitura e gravação terá sucesso tanto no modo de bloqueio quanto de não bloqueio ". Portanto, o seguinte comando irá expirar mesmo quando nenhum outro processo abrir o FIFO para gravação:

read -st 1 data <> "$fifo"

Cuidado com a condição da corrida

Uma vez que o seu processo shell abra o FIFO para leitura, o (s) gravador (es) serão desbloqueados e, quando bash redirecionar o FIFO para stdin e chamar read , o gravador poderá abrir o FIFO e escreva várias vezes . Como você lê apenas uma linha por vez, qualquer linha remanescente a ser lida enquanto a FIFO é fechada nas duas extremidades será perdida. Uma solução melhor seria manter o FIFO aberto, redirecionando-o para stdin para todo o loop while ... done , como você fez para o fd 3. Algo como:

while ...; do
    ...
    read -st 1 data
    ...
done 3<"$lock" < "$fifo"

Ou mesmo em um nível superior, se você tiver vários leitores em paralelo. O que importa é manter o FIFO aberto. O mesmo para o lado do escritor.

Por exemplo, com o código que você publicou com sua atualização, o nível superior seria:

# Writer
writer > "$myfifo" &

# Reader
for i in $(seq 1 10); do
    reader $i &
    sleep 1
done < "$myfifo"

Claro, remova os redirecionamentos para / de $myfifo em qualquer outro lugar no seu código, e remova o echo "$(date -R) writing \"$i\"." no seu gravador, ou redirecione-o para stderr, senão ele irá para o FIFO.

    
por 20.03.2017 / 01:32