Loops BASH, contadores, processos filhos; contador não está funcionando

0
  • Problema: script pai gera aleatoriamente um número excessivo de scripts de trabalho.
  • Suspeito : bug bash mas não consegue identificá-lo
  • SO: Ubuntu 16.04.03 LTS
  • GNU bash: versão 4.3.48 (1) -release (x86_64-pc-linux-gnu)
  • MySQL: 5.7.21 (repositório do MySQL)

O que o script pai faz é obter dados do MySQL e executa scripts de trabalho em segundo plano com os dados do MySQL. O script pai é responsável por não manter mais de sete scripts de trabalho em execução até que todos os dados sejam processados. Isso funcionou perfeitamente durante anos até um mês atrás. Eu suspeito que meu problema é causado por uma atualização recente. Aqui está a lógica:

  1. O script pai obtém dados do servidor MySQL
  2. O script pai faz loops e lança scripts de trabalho no segundo plano, transmitindo dados obtidos do MySQL.
  3. Script de trabalho gera e grava um "arquivo de bloqueio"
  4. O script pai mantém até sete scripts de trabalho em execução, monitorando o número de scripts de trabalho gerados E o número de arquivos de bloqueio de trabalho.

Eu sei que o script pai pode fazer um loop e gerar vários processos-filhos antes que o (s) processo (s) filho (s) tenha tempo para configurar e gravar seu arquivo de bloqueio. É por isso que mantenho uma contagem de spawns de filhos (SPWNCNT) para ajudar a evitar esse cenário. Como eu disse, costumava funcionar bem e agora não funciona.

Aqui está a parte do script:

#!/bin/bash

......


###########################
# Loop through all unique codes and process them.
echo "$0: NOTICE: Started processing codes; 'date'."
COMCNT=0
SPWNCNT=0
TPCNT=0
while read COMNUM
do
  # Only permit a certain number of child processes
  # so to not overload the machine or chew up to
  # many MySQL connections.
  PCNT='ls -1 $TEMPDIR/*.Worker.lock 2>/dev/null | wc -l'
  (( TPCNT = PCNT + SPWNCNT ))
  TPCNT='echo ${TPCNT#-}'
  while [[ $TPCNT -gt 6 ]]
  do
    # Too many child processes.
    sleep 1
    PCNT='ls -1 $TEMPDIR/*.Worker.lock 2>/dev/null | wc -l'
    (( TPCNT = PCNT + SPWNCNT ))
    TPCNT='echo ${TPCNT#-}'

    if [[ $SPWNCNT -gt 0 ]]
    then
      (( SPWNCNT = SPWNCNT - PCNT ))
      if [[ $SPWNCNT -lt 0 ]]
      then
        SPWNCNT=0
      fi
    fi

  done # while -gt 6

  # Spawn a worker process
  ./Worker.sh $COMNUM &
  (( SPWNCNT = SPWNCNT + 1 ))
  (( COMCNT = COMCNT + 1 ))

  if [ "$VERBOSE" = "true" ]
  then
    echo "$0: NOTICE: Spawned $COMNUM, count $COMCNT ($SPWNCNT); 'date'"
  fi

done << COMNUM_EOF
  'cat $GEN_RATES_COMNUM_FILE | $MyCMD -B -N $MyDB'
COMNUM_EOF

......

E aqui está uma saída de depuração do problema (usando #! / bin / bash -x)

...... many lines showing same logic working correctly ......
++ wc -l
++ ls -1 '/tmp/*.Worker.lock'
+ PCNT=0
+ ((  TPCNT = 0 + 7  ))
++ echo 7
+ TPCNT=7
+ [[ 7 -gt 0 ]]
+ ((  SPWNCNT = 7 - 0  ))
+ [[ 7 -lt 0 ]]
+ [[ 7 -gt 6 ]]
+ sleep 1
++ wc -l
++ ls -1 /tmp/032500.Worker.lock /tmp/032800.Worker.lock 
/tmp/033300.Worker.lock /tmp/033900.Worker.lock /tmp/034700.Worker.lock 
/tmp/035400.Worker.lock /tmp/035600.Worker.lock /tmp/036000.Worker.lock 
/tmp/036200.Worker.lock /tmp/036400.Worker.lock /tmp/036600.Worker.lock 
/tmp/037000.Worker.lock /tmp/039100.Worker.lock /tmp/039600.Worker.lock 
/tmp/040200.Worker.lock /tmp/040400.Worker.lock /tmp/041000.Worker.lock 
/tmp/041200.Worker.lock /tmp/041600.Worker.lock /tmp/041800.Worker.lock 
/tmp/042000.Worker.lock /tmp/043600.Worker.lock /tmp/046200.Worker.lock 
/tmp/048600.Worker.lock /tmp/049600.Worker.lock /tmp/052000.Worker.lock 
/tmp/052300.Worker.lock /tmp/054300.Worker.lock /tmp/054500.Worker.lock 
/tmp/054900.Worker.lock /tmp/055300.Worker.lock /tmp/056000.Worker.lock 
/tmp/056200.Worker.lock
/tmp/056600.Worker.lock /tmp/056900.Worker.lock /tmp/057800.Worker.lock 
/tmp/058600.Worker.lock /tmp/060400.Worker.lock /tmp/060700.Worker.lock
+ PCNT=39
+ ((  TPCNT = 39 + 7  ))
++ echo 46
+ TPCNT=46
+ [[ 7 -gt 0 ]]
+ ((  SPWNCNT = 7 - 39  ))
+ [[ -32 -lt 0 ]]
+ SPWNCNT=0
+ [[ 46 -gt 6 ]]
+ sleep 1
++ wc -l
++ ls -1 /tmp/032500.Worker.lock /tmp/032800.Worker.lock 
/tmp/033300.Worker.lock /tmp/033900.Worker.lock /tmp/034700.Worker.lock 
/tmp/035400.Worker.lock /tmp/035600.Worker.lock /tmp/036000.Worker.lock 
/tmp/036200.Worker.lock /tmp/036400.Worker.lock /tmp/036600.Worker.lock 
/tmp/037000.Worker.lock /tmp/039100.Worker.lock /tmp/039600.Worker.lock 
/tmp/040200.Worker.lock /tmp/040400.Worker.lock /tmp/041000.Worker.lock 
/tmp/041200.Worker.lock /tmp/041600.Worker.lock /tmp/041800.Worker.lock 
/tmp/042000.Worker.lock /tmp/043600.Worker.lock /tmp/046200.Worker.lock 
/tmp/048600.Worker.lock /tmp/049600.Worker.lock /tmp/052000.Worker.lock 
/tmp/052300.Worker.lock /tmp/054300.Worker.lock /tmp/054500.Worker.lock 
/tmp/054900.Worker.lock /tmp/055300.Worker.lock /tmp/056000.Worker.lock 
/tmp/056200.Worker.lock
/tmp/056600.Worker.lock /tmp/056900.Worker.lock /tmp/057800.Worker.lock 
/tmp/058600.Worker.lock /tmp/060400.Worker.lock /tmp/060700.Worker.lock
+ PCNT=39
+ ((  TPCNT = 39 + 0  ))
++ echo 39
+ TPCNT=39
+ [[ 0 -gt 0 ]]
+ [[ 39 -gt 6 ]]
+ sleep 1

Então, como o script passou de 7 processos em execução (TPCNT) (o alvo adequado) para 39 e depois para 46? Não consigo ver como a lógica permitiria isso e a saída de depuração não parece lançar alguma luz sobre isso, exceto que o shell bash acabou de ser exibido.

    
por Van 26.01.2018 / 13:28

1 resposta

1

Para quem encontrar este post mais tarde na vida .... Aqui está a solução que funcionou para mim com base na resposta do m0dular acima. m0dular na verdade merece o crédito.

#!/bin/bash

.....

# Intialize the Child Process List (array)
CPLIST=()

# Max concurrent child processes
MAXCP=6

# Worker loop to spawn and monitor child (worker) processes
while [[ SOME-CONDITION ]]
do

  # Monitor Child Process List (array)
  # Ensure that we don't exceed Max Child Processes
  if [[ ${#CPLIST[@]} -gt $MAXCP ]]
  then
    while [[ ${#CPLIST[@]} -gt $MAXCP ]]
    do
      sleep 1
      # Check each child processes to see if it's still running.
      for idx in ${!CPLIST[@]}
      do
        # Is child process still alive?
        kill -0 ${CPLIST[$idx]} 2>/dev/null
        if [[ $? -gt 0 ]]
        then
          # Child process is no longer running.
          # Remove it from the child process list (array).
          unset CPLIST[$idx]
        fi # if $?
      done # for idx
    done # while MAXCP
  fi # if MAXCP

  # Spawn a child process
  ./MyProgram &
  # Append Child Process PID to Child Process List
  CPLIST=(${CPLIST[@]} $!)

done # while

.....

# (end of file)
    
por 29.01.2018 / 15:06