Я выполняю несколько тестов параллельно, вызывая процесс из сценария. Каждый процесс выводит только на stdout> файл и завершает 0, если успешно (в противном случае -1).ограничение порожденных параллельных процессов и выход из всех после отказа любого
Если процесс завершает работу с -1, я печатаю что-то в его (или связанном) выходном файле (а именно с аргументами, с которыми он был вызван), убивает все остальные процессы и завершает работу.
Я написал сценарий, используя trap "..." CHLD
, чтобы запустить некоторый код при выходе из подпроцесса, и это работает при определенных условиях, но я считаю, что мой скрипт не очень надежный. Если я посылаю прерывание клавиатуры, иногда подпроцессы продолжаются, и иногда количество подпроцессов просто перегружает машину (-ы), и ни одна из них, похоже, не продвигается вперед.
Я использую это на своем четырехъядерном ноутбуке, а также кластер из 128 процессоров, по которым подпроцессы распределяются автоматически. Как запустить большое количество подпроцессов фона в сценарии bash, ограниченное некоторым количеством из них, выполняемым одновременно, и сделать что-то + выход, если один из них возвращается с неправильным кодом? Мне также нужен сценарий для очистки после прерывания клавиатуры. Должен ли я использовать GNU-параллель? как?
Вот MWE моего сценария до сих пор, который беспрепятственно создает подпроцессы, аннотированные тем, что, как я думаю, означает каждая часть. Я пришла в голову идея использовать trap
из shell - get exit code of background process
$ cat parallel_tests.sh
#!/bin/bash
# some help from https://stackoverflow.com/questions/1570262/shell-get-exit-code-of-background-process
handle_chld() {
#echo pids are ${pids[@]}
local tmp=() ###temporary storage for pids that haven't finished
#for each pid that hadn't finished since the last trap
for((i=0;i<${#pids[@]};++i)); do
#if this pid is still running
if [[ $(ps -p ${pids[i]} -o pid=) ]]
then
tmp+=(${pids[i]}) ### add pid to list of pids that are running
else
wait ${pids[i]} ### put the exit code of this pid into $?
if [ "$?" != "0" ] ### if the exit code $? is non-zero
then
#kill all remaning processes
for((j=0;j<${#pids[@]};++j))
do
if [[ $(ps -p ${pids[j]} -o pid=) ]]
then
echo killing child processes of ${pids[j]}
pkill -P ${pids[j]}
fi
done
cat _tmp${pids[i]}
#print things to the terminal here
echo "FAILED process ${pids[i]} args: `cat _tmpargs${pids[i]}`"
exit 1
else
echo "FINISHED process ${pids[i]} args: `cat _tmpargs${pids[i]}`"
fi
fi
done
#update list of running pids
pids=(${tmp[@]})
}
# set this to monitor SIGCHLD
set -o monitor
# call handle_chld() when SIGCHLD signal is triggered
trap "handle_chld" CHLD
ALL_ARGS="2 32 87" ### ad nauseam
for A in $ALL_ARGS; do
(sleep $A; false) > _tmp$! &
pids+=($!)
echo $A > _tmpargs${pids[${#pids[@]}-1]}
echo "STARTED process ${pids[${#pids[@]}-1]} args: `cat _tmpargs${pids[${#pids[@]}-1]}`"
done
echo "Every process started. Now waiting on PIDS:"
echo ${pids[@]}
wait ${pids[@]} ###wait until every process is finished (or exit in the trap)
Выход этой версии после того, как 2 + эпсилон секунд:
$ ./parallel_tests.sh
STARTED process 66369 args: 2
STARTED process 66374 args: 32
STARTED process 66381 args: 87
Every process started. Now waiting on PIDS:
66369 66374 66381
killing child processes of 66374
./parallel_tests.sh: line 43: 66376 Terminated: 15 sleep $A
killing child processes of 66381
./parallel_tests.sh: line 43: 66383 Terminated: 15 sleep $A
FAILED process 66369 args: 2
По существу, Pid 66369 терпит неудачу первый, а остальные два процесса рассматриваются в ловушке. Я упростил здесь процесс тестирования, поэтому мы не можем предположить, что я буду вручную вставлять wait
с до появления новых. Кроме того, некоторые из тестовых процессов могут быть почти мгновенными. По сути, у меня есть целый беспорядок тестовых процессов, длинных и коротких, начиная, как только ресурсы могут быть выделены.
Я не уверен, что вызывает проблемы, о которых я упоминал выше, так как этот сценарий использует несколько новых функций для меня. Общие указатели приветствуются!
(я видел this question и он не отвечает на мой вопрос)
Убивание задания, выполняющегося на удаленном кластере при нажатии CTRL-C, нетривиально, поэтому, даже если вы найдете решение, которое работает локально, предположим, что он просто работает удаленно. –