número de controle de programas iniciados no bash

2

Como parte do meu projeto de pesquisa, estou processando uma grande quantidade de dados divididos em vários arquivos.

Todos os arquivos na pasta foo precisam ser processados pelo script myScript envolvendo todos os elementos da pasta bar .

Isso é myScript :

for f in bar/*
do
    awk 'NR==FNR{a[$0]=$0;next}!a[$0]' $f $1 > tmp
    cp tmp $1
done

A primeira ideia para processar todos os arquivos com um loop for é válida:

for f in foo/*
do
    ./myScript $f
done

No entanto, isso simplesmente leva uma eternidade. Simplesmente iniciar cada myScript em segundo plano adicionando & criaria milhares de instâncias paralelas de awk e cp com entradas enormes, o que obviamente é ruim.

Pensei em limitar o número de "segmentos" criados com o seguinte

for f in foo/*
do
    THREAD_COUNT=$(ps | wc -f)
    while [ $THREAD_COUNT -ge 12 ]
    do
        sleep 1
        THREAD_COUNT=$(ps | wc -f)
    done
    ./myScript $f &
done

Como observação: estou comparando com 12, porque tenho 8 núcleos em meus nós e aparentemente há sempre bash , ps e wc em execução, além da linha de cabeçalho no momento da chamada de ps | wc -l .

Infelizmente, a chamada de myScript causa mais de uma entrada adicional em ps , portanto, o comportamento do meu script não era o desejado.

Então aqui está a minha pergunta: Existe uma maneira mais simples? Uma maneira que é mais estável?

Eu não estou fazendo mais nada nos nós, então tudo o que acontece é causado apenas pelos scripts.

    
por stefan 06.09.2012 / 22:55

3 respostas

3

Embora você possa fazer isso com um script de shell, isso é difícil. Os scripts do shell não são muito bons em manipular vários trabalhos em segundo plano.

Minha recomendação é usar o make do GNU ou alguma outra versão do make que tenha um -j opção para executar vários trabalhos em paralelo. Escreva cada subtarefa como uma regra de makefile.

Acho que o snippet do makefile abaixo implementa suas regras, mas seu código era difícil de ser seguido, então talvez eu tenha acertado. A primeira linha enumera os arquivos de saída dos arquivos de entrada (nota: nunca sobrescreva nenhum arquivo de entrada! Se o trabalho parar no meio por qualquer motivo, você terminará com dados para os quais você não saberá se ele foi processado) . As linhas recuadas são os comandos a serem executados. Use uma aba para recuar cada comando, não 8 espaços. Nesses comandos, $< representa o arquivo de origem (um arquivo .in ), $@ representa o destino (o arquivo .out ) e $* é o destino sem sua extensão. Todos os sinais $ nos comandos shell devem ser duplicados, e cada linha de comando é executada em um subshell separado, a menos que você coloque um \ no final que cancela essa nova linha (assim, o shell vê uma linha longa começando com set -e e terminando com done ).

all: $(patsubst %.in,%.out,$(wildcard foo/*.in))
%.out: %.in
        cp $< $*.tmp.in
        set -e; \
        for f in bar/*; do \
          awk 'NR==FNR{a[$$0]=$$0;next}!a[$$0]' $$f $*.tmp.in >$*.tmp.out; \
          mv $*.tmp.out $*.tmp.in; \
        done
        mv $*.tmp.in $@

Coloque isso em um arquivo chamado Makefile e chame make -j12 .

    
por 07.09.2012 / 03:09
2

Usando o GNU Parallel (http://www.gnu.org/software/parallel/), é assim:

parallel awk \'NR==FNR\{a\[\
parallel ./myScript ::: foo/*
\]=\
parallel awk \'NR==FNR\{a\[\
parallel ./myScript ::: foo/*
\]=\%pre%\;next\}\!a\[\%pre%\]\' {1} {2} '>{2}.tmp; mv {2}.tmp {2}' ::: bar/* ::: foo/*
\;next\}\!a\[\%pre%\]\' {1} {2} '>{2}.tmp; mv {2}.tmp {2}' ::: bar/* ::: foo/*

Isso executará um trabalho por núcleo. Use -j150% para executar 1,5 trabalhos por núcleo.

Se você quiser apenas executar vários myScript em paralelo, faça:

%pre%

Assista aos vídeos de introdução para saber mais: link

    
por 07.09.2012 / 14:15
1

Você pode tentar usar ulimit . De bash man-page:

ulimit [-HSTabcdefilmnpqrstuvx [limit]]
Provides control over the resources available to the shell and to processes started  by  it, 
on systems  that  allow  such control.
[...]
-u     The maximum number of processes available to a single user

Portanto, se você colocar ulimit -u 8 em uma posição apropriada dentro do script, isso limitará os processos disponíveis para esse shell em 8.

Não testou, no entanto.

    
por 06.09.2012 / 23:27