resumo:
yes
exibe um comportamento semelhante à maioria dos outros utilitários padrão que, em geral, escrevem para um FILE STREAM com saída em buffer pela libC via stdio . Eles só fazem o syscall write()
a cada 4kb (16kb ou 64kb) ou qualquer que seja o bloco de saída BUFSIZ . echo
é write()
por GNU
. Isso é um muito de modo de comutação (que não é, aparentemente, tão caro como um interruptor de contexto ) .
E isso não é de todo para mencionar que, além de seu loop de otimização inicial, yes
é um loop C muito simples, pequeno e compilado e seu loop de shell não é de forma alguma comparável a um programa otimizado de compilador.
mas eu estava errado:
Quando eu disse antes que yes
usava o stdio, eu apenas supus que isso acontecia porque se comporta muito como aqueles que fazem. Isso não estava correto - apenas emula o comportamento deles dessa maneira. O que ele realmente faz é muito parecido com um analógico para a coisa que fiz abaixo com o shell: ele primeiro faz um loop para combinar seus argumentos (ou y
se nenhum) até que eles não possam crescer mais sem exceder BUFSIZ
.
Um comentário da fonte imediatamente anterior ao loop for
relevante afirma:
/* Buffer data locally once, rather than having the
large overhead of stdio buffering each item. */
yes
faz o seu próprio write()
s depois disso.
digressão:
(Como originalmente incluído na pergunta e retido por contexto para uma explicação possivelmente informativa já escrita aqui) :
I've tried
timeout 1 $(while true; do echo "GNU">>file2; done;)
but unable to stop loop.
O problema timeout
que você tem com a substituição de comando - acho que entendi agora e posso explicar por que ele não para. timeout
não inicia porque sua linha de comando nunca é executada. Seu shell bifurca um shell filho, abre um pipe em seu stdout e o lê. Ele parará de ler quando o filho sair e interpretará tudo o que o filho escreveu para $IFS
mangling e glob expansionions e, com os resultados, ele substituirá tudo de $(
pelo )
correspondente.
Mas se o filho for um loop infinito que nunca grava no pipe, ele nunca pára de fazer loop e a linha de comandos do timeout
nunca é concluída antes (como eu suponho) você faz CTRL-C
e mata o loop filho. Portanto, timeout
pode nunca matar o loop que precisa ser concluído antes que possa ser iniciado.
outro timeout
s:
... simplesmente não são tão relevantes para seus problemas de desempenho quanto a quantidade de tempo que o seu programa shell deve passar alternando entre o modo de usuário e o modo kernel para manipular a saída. O timeout
, no entanto, não é tão flexível quanto um shell pode ser para essa finalidade: onde os shells se destacam em sua capacidade de manipular argumentos e gerenciar outros processos.
Como é notado em outro lugar, simplesmente mover seu redirecionamento [fd-num] >> named_file
para o destino de saída do loop, em vez de apenas direcionar a saída para o comando em loop, pode melhorar substancialmente o desempenho porque, pelo menos, o open()
syscall só precisa ser feito uma vez. Isso também é feito abaixo com o canal |
direcionado como saída para os loops internos.
comparação direta:
Você pode gostar de:
for cmd in exec\ yes 'while echo y; do :; done'
do set +m
sh -c '{ sleep 1; kill "$$"; }&'"$cmd" | wc -l
set -m
done
256659456
505401
Qual é tipo semelhante ao sub-relacionamento de comando descrito anteriormente, mas não há nenhum canal e o filho é em segundo plano até matar o pai. No caso yes
o pai foi realmente substituído desde que o filho foi gerado, mas o shell chama yes
sobrepondo seu próprio processo com o novo e assim o PID permanece o mesmo e seu filho zumbi ainda sabe quem matar afinal de contas.
buffer maior:
Agora vamos ver como aumentar o buffer write()
do shell.
IFS="
"; set y "" ### sets up the macro expansion
until [ "${512+1}" ] ### gather at least 512 args
do set "$@$@";done ### exponentially expands "$@"
printf %s "$*"| wc -c ### 1 write of 512 concatenated "y\n"'s
1024
Eu escolhi esse número porque as sequências de saída de mais de 1kb estavam sendo divididas em write()
separadas para mim. E aqui está o loop novamente:
for cmd in 'exec yes' \
'until [ "${512+:}" ]; do set "$@$@"; done
while printf %s "$*"; do :; done'
do set +m
sh -c $'IFS="\n"; { sleep 1; kill "$$"; }&'"$cmd" shyes y ""| wc -l
set -m
done
268627968
15850496
Isso é 300 vezes a quantidade de dados gravados pelo shell no mesmo período de tempo que o último. Não é muito pobre. Mas não é yes
.
relacionado:
Conforme solicitado, há uma descrição mais detalhada do que os simples comentários de código sobre o que é feito aqui em este link .