Substituir a instância do processo no lugar?

2

Eu suspeito que isso não é possível apenas por causa das implicações de segurança, mas aqui está o que eu gostaria de fazer.

Basicamente, executamos um script de shell bash em nosso servidor CentOS que chama Program-A (no nosso caso, JMeter, mas isso é arbitrário), que executa e despeja dados em um arquivo de log. Depois que o processo terminar, o script de shell será iniciado em Program-B para analisar o log.

O que eu gostaria de fazer é parar a instância de Program-A1 e substituí-la por outra instância de Program-A2 , ou alguma forma de trocá-la para que eu possa terminar com segurança Program-A1 sem iniciar Program-B prematuramente .

Por que eu iria querer fazer isso? A principal razão é que Program-A carrega alguns arquivos de configuração em sua inicialização e, se fizermos alterações nesses arquivos de configuração, temos que reiniciar o programa para que eles entrem em vigor, o que eu gostaria de evitar.

Eu entendo que isso provavelmente não é possível, mas se for, agradeço muito a informação.

EDIT: Eu suponho que eu não estava claro o suficiente. Basicamente meu shellscript é assim:

./Program-A
./Program-B

Quando Program-A termina de descarregar para o nosso log, Program-B pega o log e o analisa. O problema que estou tendo é, por vezes, as configurações Program-A não estão definidas corretamente ou algo está errado com o ambiente quando ele é iniciado, o que significa que precisaremos executar tudo novamente. Gostaríamos de evitar isso apenas substituindo nossa primeira instância de Program-A (chamando Program-A1 ) com uma nova instância de Program-A (chamaremos isso de Program-A2 ). Isso faz mais sentido para todos?

A principal razão pela qual gostaríamos de fazer isso é porque Program-A e Program-B são na verdade uma única parte de um script de shell GIGANTE que leva horas para ser executado. Em vez de reiniciar todo o processo para uma parte individual, gostaríamos de reiniciar o único programa com problemas.

    
por Paul Nelson Baker 28.02.2014 / 21:10

5 respostas

2

Parece-me que você está tentando substituir um processo RUNNING de FORA do processo. Isso é algo radical.

Quando olhei pela primeira vez para a pergunta, parece que você está procurando exatamente por exec . Mas exec é chamado pelo próprio programa. Então, a menos que você tenha codificado o processo para que você possa forçá-lo a apenas executar outro processo, você não poderá fazer isso enquanto estiver sendo executado.

Você poderia criar uma interceptação de sinal em seu Programa-A para exec um programa com nome predefinido (que você pode definir como desejar) e, em seguida, usar kill nesse processo para forçá-lo a% código%. Do lado de fora, parecerá que o processo continuou rodando - acontece, apenas se torna outra pessoa. No entanto, se você não tiver feito isso e quiser fazer isso em um processo em execução, não acredito que possa fazer nada.

No entanto, se o shell externo estiver em execução, mas ainda não iniciou o processo crítico, você pode simplesmente substituir o arquivo do processo problemático.

    
por 12.03.2014 / 20:50
2

Uma das coisas mais simples de se fazer em um script de shell é executar programas sequencialmente. Você pode fazer algo assim:

if Program-A
then
    Program-B
else
    # handle problems
    if Program-A
    then
        Program-B
    else
        # Program-A failed twice in a row, get help.
    fi
fi

Esse tipo de construção impedirá que o Programa-B seja executado prematuramente.

Se você puder alterar o código-fonte para o Programa A, poderá aproveitar a chamada do sistema execve() , que faz com que o kernel sobreponha o programa chamando execve() com o código e dados de outro arquivo executável. Muitas coisas carregam a chamada execve() como descritores de arquivos abertos, ID do processo, mas algumas coisas não. Você terá que ler para ver quais modificações fazer.

Se eu ler nas entrelinhas do que você escreve, você pode querer fazer uma combinação fork / other work / exec de chamadas do sistema: o fork() daria dois processos, um dos quais poderia iniciar o Program-B, enquanto o outro vai para execve() Program-A1. Ou alguma coisa. Você deve esclarecer o que você quer.

Mais à esquerda no campo, é possível fazer um " userland exec ". Se tudo o que você quer fazer é "trocar" um programa por outro, você pode fazer isso sem uma chamada de sistema execve() , embora o execve() seja provavelmente mais eficiente e certamente menos propenso a erros.

    
por 28.02.2014 / 21:26
2

A única maneira de fazer isso é enviar um sinal STOP para o Programa A e usar o gdb (depurador Gnu) para substituir o código suspenso pelo código do Programa-A1. A complexidade disso é provavelmente além do que você está disposto a investir (veja link ), então sugiro uma alternativa:

edite seu script bash para "capturar" a saída do Programa-A. Em seguida, solicite a intervenção manual, nesse ponto você deve responder "y" com certeza se é seguro prosseguir com o Programa-B. Uma maneira de fazer isso é colocar um loop while no Programa -A, capturar o código de saída, se não for uma saída bem-sucedida e, em seguida, solicitar a intervenção do usuário, continue em loop até que uma saída válida seja encontrada. Então prossiga para o Programa-B.

    
por 12.03.2014 / 20:41
2

Talvez você possa simplesmente suspender o Programa-A e iniciar o Programa-A1, e quando o Programa-A1 terminar, matar o Programa-A e retomar (no ponto em que ele irá lidar com o SIGTERM, morrer e seu script continuará com Programa-B):

killall -s STOP Program-A
Program-A1
killall Program-A
killall -s CONT Program-A

Se você quiser que Program-A1 herde o ambiente de Program-A e se estiver no Linux (e supondo que todas as sequências de ambiente de Program-A contenham pelo menos um caractere = como costumam fazer), você pode faça:

xargs -0 --arg-file="/proc/$(pidof Program-A
  )/environ" sh -c 'exec env -i "$@" Program-A1' sh >> file.log
    
por 28.02.2014 / 21:43
0

Então, abaixo, você encontrará uma maquete realmente longa e provavelmente propensa a erros de como eu acho que isso funcionaria. Ele insere uma função exec ing ab_start entre program_a e program_b para garantir que seus respectivos arquivos lock sejam limpos entre as chamadas para qualquer função e um switch.lock sobreposto é implementado para cada chamada para ab_start . Então:

  • program_a não iniciará a função até que ambos trap sejam configurados e ele remova com sucesso switch.lock . Em seguida, ele faz o que quer que seja até que program_b.lock apareça, ao mesmo tempo em que canaliza seu log por meio de sed .
  • sed monitora a saída de log do program_a para o regex signal_output no momento em que é aberto program_b.lock e escreve a linha capturada para ele.
    • Entretanto, sed tem o prazer de também bombear todos os dados de log transmitidos para program_a.log , como um tee inteligente.
  • program_a finaliza seu último loop e tragicamente kill s em si.
  • mas program_a já interceptou o sinal USR1 , para abrir o arquivo /dev/fd/4 <<SUSPEND anteriormente não avaliado anexado à sua entrada e executá-lo . Isso então:
    • touch es switch.lock
    • & origina uma chamada para ab_start com instruções para iniciar program_b
    • while aguarda que ambos program_b.lock e switch.lock desapareçam magicamente quando então ...
    • touch es switch.lock
    • chama ab_start com instruções para exec de volta para si mesmo.
  • enquanto isso ...
  • ab_start salva seu parâmetro de destino exec e o desloca para fora.
    • se recusa a fazer nada, exceto sleep ou rm , até que ambos {a_b}.lock s tenham desaparecido
    • frustrada touch es {a_b}.lock
    • exec s no seu destino
  • último ...
    • program_b define a si mesmo como trap e depois
    • se recusa a fazer qualquer coisa, exceto sleep ou rm , até que switch.lock desapareça.
    • faz o que faz por quanto tempo demora e quando passa
    • kill s em si
    • , mas encontra alívio temporário em seu próprio DIE acrescentado here-document
    • até que seja removido com sucesso program_b.lock
    • e então ele realmente morre
  • MAS
  • program_a então acorda e exec s como explicado acima.

    HASHBANG

    ab_start() { a_b="$1" ; shift
            while [ -e /locks/program_a.lock ] ||\
                  [ -e /locks/program_b.lock ] ; do {
                  rm /locks/program_[ab].lock
                  sleep 1
           } ; done
           touch /locks/"${a_b}.lock"
           exec "$0" "${a_b}" "$@"
    } 
    
    program_a() { trap '. /dev/fd/4' USR1 ; 
        while [ -e /locks/switch.lock ] ; do {
            rm /locks/switch.lock ; sleep 1
        } ; done
        until [ -e /locks/program_b.lock ] ; do {
            stuff...
            it...
            does...
        } ; done |\
                sed 's/signal_output/w /locks/program_b.lock'\
                    >> /logs/program_a.log
        kill -USR1 $$
    } 4<<-\SUSPEND
        touch /locks/switch.lock
        ab_start program_b "$@" &
        while [ -e /locks/switch.lock ] ||\
            [ -e /locks/program_b.lock ] ; do
            sleep 10 
        done
        touch /locks/switch.lock 
        ab_start program_a "$@"
    #   END
        SUSPEND
    
    program_b() { trap '. /dev/fd/5' INT QUIT TERM EXIT
        while [ -e /locks/switch.lock ] ; do {
            rm /locks/switch.lock ; sleep 1
        } ; done
        while $work ; do { 
            stuff ...
            it ...
            does ...
        } ; done
    } 5<<-\DIE
        while [ -e /locks/program_b.lock ] ; do {
            rm /locks/program_b.lock ; sleep 1
        } ; done
    exec 5<&0 <&-
    #   NOW
        DIE 
    

    "$ @"

Eu vou pedir desculpas agora por qualquer erro que eu tenha cometido acima, mas eu acredito que esse tipo de coisa poderia funcionar ... leve como quiser. Nenhuma garantia embora.

Definitivamente, pelo menos, sed , acho que é uma boa ideia aqui.

    
por 15.03.2014 / 00:40