В общем история такая. Было мне задание написать демона для «форка» или просто клонирования определенных скриптов, да так, чтоб можно было управлять количеством одновременно запущенных процессов – что-то вроде универсального демона. Идея очень интересная! Позволяет не думать более о форках в php или чем-то другом. Принципиально позволяет отделить эту реализацию от ваших программ или скриптов, будь то php, C, C++, bash.
Плюсом ко всему этому нужно было как-то сообщать скрипту, который должен будет клонироваться какие его копии еще работают, для этого придется искать pid копиий «команды».
Для реализаци был выбран bash, т.к. он скриптовой и наиболее удобен всем, да и разобраться в нем не сложно.
1. Создаем файл, например clone_daemon.sh
2. Ставим ему права на исполнение
3. Копируем в него этот код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | #!/bin/bash PROCESSPID=$$ showhelp () { echo " Usage: $0 [PAREMETRS] Run the specified command $0 --help Display this help and exit $0 --version Output version information and exit Parametrs: -с - Command to run -n - Number of simultaneously running command clones. Must be integer and greater than zero -s - Timeout for the eternal loop. Must be integer and greater than zero -l - For the eternal loop " exit $? } case $1 in "" | "--help" | "--help" | "--version" | "-version") showhelp;; esac # Вечный цикл loopWhile=0 # Время задержки в секундах sleepTime=null # Лимит одновременно запущенных процессов countCloneLimit=0 # Получаем параметры # Очень хорошо написано про них тут http://www.ibm.com/developerworks/ru/library/l-bash-parameters/index.html while getopts ":c:n:s:l" optname do case "$optname" in "l") loopWhile=1;; "s") sleepTime=$OPTARG;; "n") countCloneLimit=$OPTARG;; "c") command=$OPTARG;; "?") echo "Unknown option -$OPTARG" exit 1 ;; ":") echo "No argument value for option -$OPTARG. Run '$0 --help'" exit 1 ;; esac done # Проверяем команду на выполнение. Там обязательно должны быть буквы if [ `echo $command | grep -c [^0-9]` = "0" ] then echo "Wrong command. '$command'. Run '$0 --help'" exit 1 fi # sleepTime должен быть числом и > 0 if [ "$sleepTime" != "null" ] && [ `echo $sleepTime | grep -c [^0-9]` = "1" ] || [ "$sleepTime" = "0" ] then echo " Invalid parameter [-s]. Run '$0 --help'" exit 1 fi # countCloneLimit должен быть числом и > 0 if [ `echo "$countCloneLimit" | grep -c [^0-9]` = "1" ] || [ $countCloneLimit = "0" ] then echo " Invalid parameter [-c]. Run '$0 --help'" exit 1 fi PIDFILESDIR=/tmp PIDFILENAME=`echo $0 $command | md5sum`.pid PIDFILEFULL=$PIDFILESDIR/$PIDFILENAME # Проверяем существует ли файл pid для ткого процесса if [ `ls "$PIDFILESDIR" | grep "$PIDFILENAME" | grep -c -v grep` = "1" ] then pid=`cat "$PIDFILEFULL"` # Проверяем если ли такой процесс в памяти, если нет, то удаляем файл if [ `ps -p $pid | grep $pid | grep -c -v grep` = "0" ] then `rm "$PIDFILEFULL"` else echo "Process with this command is already running. exit..." exit 1 fi fi # Пишем pid процесса в файл `echo "$PROCESSPID" > "$PIDFILEFULL"` # Экранируем $command escapeCommand=`echo "$command" | sed -e "s/\//\\\\\\\\\\//g" | sed "s/'/'\"'\"'/g"` # Делаем бесконечный цикл while(true) do countClone=0 # Получаем список pid запущенных процессов "$command" pidList=`ps ax -o "%p %a" | grep "$command" | grep -v "$0" | grep -v grep | sed "s/$escapeCommand//I" | grep -v 'sed s///I' | awk '{print $1,"$command"}'` # Если в $pidList есть цифры, значит процессы есть и значит, мы их считаем if [ `echo $pidList | grep -c [0-9]` \> 0 ] then # Считаем общее количество процессов countClone=`echo "$pidList" | wc -l` echo $countClone fi # Считаем сколько процессов нужно запустить countNeed=`expr "$countCloneLimit" - "$countClone"` pidString=`echo $pidList | sed -e "s/[^ 0-9]//g"` for i in $(seq 1 $countNeed); do eval "nohup $command $pidString >> /dev/null&" done # Если не установлен флаг вечного цикла, то выходим из зацикливания if [ "$loopWhile" = "0" ] then exit $? fi # Засыпаем, чтобы не делать бесконечный цикл sleep $sleepTime done exit $? |
4. Запускаем с параметрами:
- [-c] команда на выполнение в кавычках
- [-n] максимальное количество одновременно запущенных процессов
- [-s] Время торможения в цикле while, т.е. через какой промежуток времени в секундах надо проверить количество экземпляров «команды» [-c], и запустить количество недостающих
- [-l] Говорит о том, что нужен «вечный цикл»
Например:
1 | $ ./clone_daemon -c "php script.php" -s 1 -n 5 -l |
Даст вам 5 одновременно работающих клонов команды [-c] в бесконечном цикле [-l] с задержкой [-s] между новым проходом
Если же вам надо просто единоразово запустить несколько клонов в фоновом режиме, то следующая команда для вас:
1 | $ ./clone_daemon -c "php script.php" -n 5 |
Это даст вам 5 одновременно работающих клонов команды [-c]
Для языка фонового скрипта был выбран php. Там мы и будем разбирать pidList.
Пример:
1 2 3 4 5 6 7 8 9 10 11 | <?php $pidArray = array(); if (isset($argv[1])) { foreach (explode("\n", $argv[1]) as $pid) { $pidArray[] = trim($pid); } } print_r ($pidArray); ?> |
Так мы можем узнать какие скрипты у нас вылетели, сделать стату или просто защититься от сбоя в скрипте и перезапустить его.
Похожие статьи:


А процессорное время не отжирает в бесконечном цикле???
Я обычно такие вещи делаю по крону раз в 2-5 минут…
Ты видать не обратил внимание на строчки:
# Засыпаем, чтобы не делать бесконечный цикл
sleep $sleepTime
Они дают разгузку CPU, т.к. $sleepTime не может быть менее 1 сек.
не в ту ветку ответил
Ну тогда по проще
) но всё равно – зачем заморачиваться со своим циклом, если есть крон?
Самое главное, для чего нужен данный скрипт – постоянное удерживание того количества одновременно выполняемых экземпляров команд (приложений), которое было указано. С проверкой раз в [-s] секунд.
Например, если нужен парсер постоянно работающий в 5 экземплярах. Т.е. каждый дочерний скрипт может быть настолько атомарным, насколько можно – допустим парсит один урл, разбирает содержимое и умирает. На работе мы теперь успешно применяем этот bash скрипт для многих задач.
ну тогда да..