По умолчанию, в Linux, создает ли процесс через proc_open (), чтобы PHP-скрипт не заканчивался до тех пор, пока не завершится процесс порождения? Я не хочу этого, и я сразу же закрываю дескриптор процесса.
proc_open сам не блокирует, это достаточно ясно. Но как насчет выполнения общего HTTP-запроса?
У меня было какое-то время в выходные, поэтому я сделал небольшое исследование по proc_open () на системах * nix.
Хотя proc_open () не блокирует выполнение скрипта PHP, даже если сценарий оболочки не запускается в фоновом режиме, PHP автоматически вызывает proc_close () после того, как скрипт PHP полностью выполнен, если вы не вызываете его самостоятельно. Итак, мы можем представить, что в конце скрипта всегда есть строка с proc_close ().
Проблема кроется в неочевидном, но логичном прообразе proc_close (). Представим себе, что у нас есть скрипт вроде:
$proc = proc_open('top -b -n 10000', array( array('pipe', 'r'), array('pipe', 'w')), $pipes); //Process some data outputted by our script, but not all data echo fread($pipes[1],100); //Don't wait till scipt execution ended - exit //close pipes array_map('fclose',$pipes); //close process proc_close($proc);
Странно, proc_close (), который должен был ждать завершения выполнения скриптового скрипта, но наш скрипт вскоре был прерван. Это происходит потому, что мы закрыли трубы (кажется, что PHP делает это тихо, если мы забыли), так как только этот скрипт пытается написать что-то уже несуществующему трубу – он получает ошибку и завершает работу.
Теперь давайте попробуем без труб (ну, будет, но они будут использовать текущий tty без ссылки на PHP):
$proc = proc_open("top -b -n 10000", array(), $pipes); proc_close($proc);
Теперь наш PHP-скрипт ждет завершения нашего сценария оболочки. Мы можем избежать этого? К счастью, PHP порождает сценарии оболочки с
sh -c 'shell_script'
поэтому мы можем просто убить процесс sh и оставить наш скрипт запущенным:
$proc = proc_open("top -b -n 10000", array(), $pipes); $proc_status=proc_get_status($proc); exec('kill -9 '.$proc_status['pid']); proc_close($proc);
Конечно, мы могли бы просто запустить процесс в фоновом режиме, например:
$proc = proc_open("top -b -n 10000 &", array(), $pipes); proc_close($proc);
и никаких проблем не возникает, но эта функция приводит нас к самому сложному вопросу: можем ли мы запустить процесс с proc_open (), прочитав некоторый вывод, а затем заставьте процесс заново? Ну, в некотором смысле – да.
Основная проблема здесь – это трубы: мы не можем их закрыть или наш процесс умрет, но нам нужно, чтобы они прочитали полезные данные из этого процесса. Оказывается, мы можем использовать здесь магический трюк – gdb.
Сначала создайте файл где-нибудь (/ usr / share / gdb_null_descr в моем примере) со следующим содержимым:
p dup2(open("/dev/null",0),1) p dup2(open("/dev/null",0),2)
Он скажет gdb изменить дескрипторы 1 и 2 (ну, как правило, stdout и stderr) для новых обработчиков файлов (/ dev / null в этом примере, но вы можете его изменить).
Итак, последнее: убедитесь, что gdb может подключаться к другим запущенным процессам – по умолчанию это относится к некоторым системам, но, например, на ubuntu 10.10 вам нужно установить / proc / sys / kernel / yama / ptrace_scope на 0, если вы этого не сделаете запустите его как root.
Наслаждаться:
$proc = proc_open('top -b -n 10000', array( array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w')), $pipes); //Process some data outputted by our script, but not all data echo fread($pipes[1],100); $proc_status=proc_get_status($proc); //Find real pid of our process(we need to go down one step in process tree) $pid=trim(exec('ps h -o pid --ppid '.$proc_status['pid'])); //Kill parent sh process exec('kill -s 9 '.$proc_status['pid']); //Change stdin/stdout handlers in our process exec('gdb -p '.$pid.' --batch -x /usr/share/gdb_null_descr'); array_map('fclose',$pipes); proc_close($proc);
Я забыл упомянуть, что PHP не запускает ваш сценарий оболочки мгновенно, поэтому вам нужно немного подождать, прежде чем выполнять другие команды оболочки, но обычно это достаточно быстро (или PHP достаточно медленный), и я должен лениться добавьте, что проверяет мои примеры.
Я столкнулся с подобной проблемой и написал небольшой скрипт для его обработки:
https://github.com/peeter-tomberg/php-shell-executer
То, что я сделал, было фоновым процессом и по-прежнему имеет доступ к результатам фонового процесса (как stderr, так и stdout).