Comando wait com PID

1. Comando wait com PID

André
arsaraiva

(usa Ubuntu)

Enviado em 11/05/2019 - 20:10h

Olá, estou tentando automatizar uma tarefa e estou encontrando problemas com o PID.

Meu código:

for i in 5 10
do
echo "omnetpp.ini Generated! Running."
../../bin/Castalia -c General
PIDCAST=$(ps aux |grep CastaliaBin |tr -s " " | cut -d" " -f2)
wait $PIDCAST
cp Castalia-Trace.txt Castalia-Trace_$i.txt
rm Castalia-Trace.txt
rm omnetpp.ini
done



essa chamada ../../bin/Castalia executa um simulador. Eu gostaria de fazer o script aguardar o fim da execução deste simulador, para copiar o arquivo de log (Catalia-Trace.txt) , apagá-lo após a cópia e executar o for de novo.

O problema é que desta forma está retornando o erro:

omnetpp.ini Generated! Running.

./executa.sh: linha 88: wait: o pid 2573 não é um processo filho deste shell
cp: não foi possível obter estado de 'Castalia-Trace.txt': Arquivo ou diretório inexistente
rm: não foi possível remover 'Castalia-Trace.txt': Arquivo ou diretório inexistente


Poderiam me auxiliar com esta situação de processos?




  


2. Re: Comando wait com PID

Paulo
paulo1205

(usa Ubuntu)

Enviado em 12/05/2019 - 02:52h

De fato, a forma tradicional de sistemas UNIX de lidar com processos somente permite que se chame wait sobre processos que são descendentes diretos daquele que vai fazer a espera.

Se o processo 1000 cria o processo 1001, e este cria o processo 1002, diz-se que o processo 1001 é filho do processo 1000, ou que o processo 1000 é pai do processo 1001, e o processo 1002 é filho do 1001 (ou que o 1001 é pai do 1002). Cada processo pai só pode chamar wait sobre um processo que seja seu filho direto: o processo 1000 só pode fazer wait 1001, e o 1001 só pode fazer wait 1002; nenhum dos três processos pode chamar wait sobre nenhum outro processo do sistema.

Apesar da terminologia “pai-filho”, não existe herança: não há processos avôs ou netos, nem irmãos, tios ou primos. Um processo cujo processo pai termine fica “órfão”, e o sistema passa a considerar que o processo pai desse processo é o processo com o PID igual a 1 (normalmente é o programa init ou um de seus sucessores “modernos” no Linux, como o systemd).

Usando o mesmo exemplo acima, se o processo 1001 terminar antes dos processos 1000 e 1002, o processo “pai” do processo 1002 passa a ser o processo 1. Já o processo 1000 não fica imediatamente sem filhos: mesmo após liberar quase todos os recursos do processo 1001, o sistema operacional vai guardar todas as informações sobre ele que possam ser aproveitadas por uma chamada a wait; até que essa chamada seja feita (se é que já não tenha sido feita, e o processo pai estivesse apenas aguardando ela se completar), o processo que tinha terminado vai ficar num estado “zumbi” até o momento em que a chamada wait do processo pai esteja concluída (ou que o próprio pai termine, caso em que o zumbi fica órfão, e o processo com PID 1 limpe as informações sobre ele).

(Em tempo: não inventei nada nessa nomenclatura de “pai”, “filho”, “zumbi” e “órfão”. Os nomes são esses mesmos desde os primórdios do UNIX.)


Para além da teoria (mal explicada), como resolver o seu problema de sincronização?

A primeira coisa é que você não precisa de cp+rm. Em lugar disso, use mv, que evita desperdício de espaço em disco e funciona de maneira atômica.

Quando à detecção do fim de execução do programa CastaliaBin, entendo que não existe nenhuma chance de ele ter começado a executar sem sem através da chamada explícita a ../../bin/Castalia na linha que vem logo acima no script, certo? Se sim, facilita a vida.

O melhor jeito de fazer é conseguir que o shell detecte a situação de término da aplicação assim que possível e sem sombra de dúvidas. Isso pode ser mais fácil ou mais difícil de conseguir, dependendo do que aconteça dentro do chamador ../../bin/Castalia e da próprio programa CastaliaBin.

Se ../../bin/Castalia tiver uma opção que permita execução em primeiro plano (foreground) e essa opção for herdada de alguma forma pelo executável CastaliaBin, não existe dúvida: dê um jeito d usar essa opção.

Se tal opção não existir, pode ser que você consiga fazer com que um descritor de arquivos que você abra antes da invocação do programa seja herdado e mantido aberto pelo programa. Se esse descritor estiver associado a um pipe ou named pipe (também chamado fifo), você pode fazer com que o shell consiga detectar o fechamento da outra ponta do pipe quando o programa for finalmente encerrado.
# Cria fifo.
mkfifo /tmp/cast_fifo

# Dentro de um subshell, cria um redirecionamento do descritor 3, tentando escrever no fifo. Se
# ../../bin/Castalia e CastaliaBin não fecharem explicatamente esse descritor, ele vai permanecer
# aberto até o final da execução. Note que o subshell é executado em background.
( exec ../../bin/Castalia -c General 3>/tmp/fifo ) &

# No seu script, tentar ler do fifo. Essa leitura vai ficar parada até que nenhum processo do
# subshell acima tenha mais o fifo aberto. Se ele não tiver sido explicitamente fechado no início
# da execução do Castalia, isso só se dará quando todos os processos tiverem acabado.
read dummy < /tmp/cast_fifo

# Remove o fifo.
rm /tmp/cast_fifo


Se não for possível usar a técnica acima (por exemplo, porque algum dos programas de alguma maneira fecha descritores que não lhe interessem, como de fato alguns programas costumam fazer), possivelmente você teria de ficar verificando periodicamente se o programa já acabou de executar. Uma forma de fazer isso pode ser parecida com o que você fez, mas de modo repetido.
../../bin/Castalia -c General

# Tenta descobrir o PID do CastaliaBin cinco vezes. Se não conseguir, dá erro.
for i in 1 2 3 4 5; do
pid=`ps awx | grep -v grep | grep CastaliaBin | awk '{ print $1 }' | head -n 1`
[ -n "$pid" ] && break
sleep 1
done
[ -z "$pid" ] && echo "Não foi possível descobrir PID." >&2 && exit 1

# Enquanto existir /proc/$pid, o processo ainda existe (a segunda condição, testada pela chamada
# a readlink, é para ver se o programa pode já ter acabado, mas ainda estar como zumbi; se você
# quiser, pode suprimir esse segundo teste).
while [ -d "/proc/$pid" ] && readlink -q "/proc/$pid/cwd" >/dev/null; do
sleep 1
done

# Neste ponto, deve ser seguro mexer no arquivo de logs.


Outra abordagem, possivelmente melhor, é, depois de descobrir o PID, verificar se o processo está ativo através da tentativa de lhe enviar um sinal que não interfira com seu funcionamento. A única restrição para isso é que o usuário com o qual você estiver rodando o script tem de ter permissão de enviar o referido sinal (o que geralmente exige que seja o mesmo usuário, ou que o sinal seja enviado pelo root).
for i in 1 2 3 4 5; do
pid=`ps awx | grep -v grep | grep CastaliaBin | awk '{ print $1 }' | head -n 1`
[ -n "$pid" ] && break
sleep 1
done
[ -z "$pid" ] && echo "Não foi possível descobrir PID." >&2 && exit 1

# O sinal zero é neutro, e serve para saber se o processo existe (desde que nós tenhamos
# permissão de enviar-lhe sinais).
while kill -0 $pid 2>/dev/null; do
sleep 1
done



... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts