PHP proc open блокирует веб-запрос?

по умолчанию в Linux создание процесса через proc_open () делает PHP-скрипт не завершающимся, пока не завершится порожденный процесс? Я не хочу этого, и я сразу закрываю ручку процесса.

proc_open сам по себе не блокирует, это достаточно ясно. Но как насчет общего выполнения HTTP-запроса?

2 ответов


у меня было некоторое время в выходные, поэтому я сделал небольшое исследование 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);

edit: я забыл упомянуть, что PHP не запускает ваш сценарий оболочки мгновенно, поэтому вам нужно немного подождать, прежде чем выполнять другие команды оболочки, но обычно это достаточно быстро (или PHP достаточно медленный), и я ленив, чтобы добавить, что проверяет мои примеры.


Я столкнулся с подобной проблемой и написал небольшой скрипт для ее решения:

https://github.com/peeter-tomberg/php-shell-executer

то, что я сделал, было фоном процесса и все еще имеет доступ к результату фонового процесса (как stderr, так и stdout).