Como encadear um comando shell em um processo não-filho que sai sem uma condição de corrida?

3

Às vezes, percebo que preciso executar outro comando quando um processo é concluído. Se estiver no mesmo shell (e eu posso controlar-Z), então fornece muitas boas soluções. No entanto, os processos só funcionam se é um processo filho .

Existe uma linguagem razoavelmente padrão, pelo menos para processos que compartilham o mesmo id de usuário, que se parece com isso no shell:

while kill -s 0 $pid 2> /dev/null; do sleep 1; done

Em sistemas com /proc (como o Linux), você pode usar [ -d /proc/$pid ] em vez de kill para dispensar o mesmo requisito de id de usuário.

Mas ambos têm uma condição de corrida: o processo pode sair durante o sleep e, em seguida, um novo processo pode obter o mesmo PID. O loop continuaria assim, ignorando o processo em que estava realmente interessado.

Existe uma maneira de eliminar a condição de corrida em um script de shell?

    
por derobert 02.06.2017 / 21:50

3 respostas

3

Possivelmente específico do Linux, outros /proc s podem se comportar de maneira diferente, mas no Linux você pode fazer isso:

(   # subshell to preserve CWD
    cd /proc/$pid || exit
    [ "$expected_path" = "$(readlink exe)" ] || exit # optional, and can do more checks
    while [ -d . ]; do sleep 1; done
)

O diretório de trabalho deixará de ser válido (e esse . não existirá mais) quando o processo terminar. No entanto, se um novo processo tomar esse pid, ele não se tornará válido novamente. Se o processo sair (e o pid permanecer sem uso) antes do cd , então cd falhará e a sub-camada será encerrada. Se o pid foi reutilizado antes do cd , então isso será bem-sucedido, mas as verificações "opcionais" (agradecimentos LJKims) o pegarão. Assim, desde que os cheques consigam uma corrida muito curta, não há condição de corrida. Para testar, você pode usar algo como:

# shell 1
$ sleep 10 &
[1] 26453
$ cd /proc/26453

# shell 2 (must be bash for BASHPID)
for ((i=0; i<200000; ++i)) do ( if [ 26453 -eq $BASHPID ]; then echo "I am have the pid — sleeping 60"; sleep 60; echo "done sleeping"; fi ); done
I have the pid — sleeping 60

# back to shell 1
[ -d . ]; echo $?
1

Então, ainda está percebendo que o sono em que ele estava interessado está encerrado, apesar do novo pid 26453.

    
por 02.06.2017 / 21:50
2

Supondo que você pode parar o processo (por exemplo, através de ctrl + z como você mencionou) este é um método não atrevido para simular && prog2 (na verdade, mais com precisão ; prog2 , pois o status de saída não será verificado, mas isso pode ser adicionado).

Encontre o PID. Abra outro terminal e execute

strace -f -e trace=exit_group -p $PID && prog2

Em seguida, retome o processo.

    
por 03.06.2017 / 02:09
1

Presumivelmente, você não quer monitorar um processo baseado apenas em seu PID. Isto é, provavelmente você tem uma idéia do que é o processo. Por que você não monitora o conteúdo de / proc / < pid > / cmdline e, uma vez que deixa de existir ou não corresponde mais ao que você espera, você pode executar seu comando / trabalho?

    
por 02.06.2017 / 23:26

Tags