Как правильно fork'нутся на php4?

Inspirra

Новичок
Как правильно fork'нутся на php4?

Подсобите пожалуйста!

Задача на первый взгляд тривиальная:
1. Запускается скрипт
2. Форкается (pcntl_fork)
3. Родитель пишет в броузер нечто и завершается - разрывая соединение с броузером
4. А потомок продолжает работать до самостоятельного завершения, или же его убъет другой скрипт-слежения через posix_kill.

А проблемы следующие:

Если PHP как модуль:
1. если потомок завершился по "exit", то:
- если раньше чем родитель, то он закрывает соединение и от родителя в броузер уже ничего не попадает.
- а так же начинают плодится httpd-процессы в виде потомков
2. если потомка завершить через "posix_kill", или он сам завершится через "pcntl_exec", то от него остаются зомби (zombie).


Если PHP как CGI:
Переконфигурил apache/php так, что бы php работал как cgi. В результате зомби не порождаются и лишние процессы тоже, но возникли другие проблемы:
- родитель завершается, а соединение с браузером не закрывается пока на завершится потомок.
- и до кучи, по завершение потомка (если тот завершился по exit), он выкидывает в браузер свой заголовок который и отображается "X-Powered-By: PHP/4.4.2 Content-type: text/html"

Т.о. получается:
- В обоих случаях (модуль||CGI), и потомок, и родитель сбрасывают данные в браузер,
- но в первом случае (модуль), процесс закончившийся первым разрывает соединение с браузером,
- а во втором случае (cgi), соединение не закрывается пока оба не завершатся.

Как же все таки правильно fork'нутся?
Главное что бы завершение родителя, разрывало связь с браузером, а потомок продолжал работать до победного конца.

Всего наилучшего
Андрей М.

P.S.
PHP 4.4.2, FreeBSD-6.1
 

mani13

Новичок
[m]exec[/m]
в комментах есть про запуск в background
fork в вебе вещь совершенно бестолковая и ненужная :)
 

Inspirra

Новичок
Дело в том что, я не вижу как это еще можно реализовать...
Задача такова:

Требуется:
1. Необходимо обработать и зарегистрировать в базе графические файлы.
2. Все файлы имеют разный источник - поэтому они все разноформатные, их размер может быть от нескольких килобайт, до нескольких сотен мегабайт и их колличество от одного файла - до нескольких тысяч.
3. Поэтому их обработка может длится о-о-очень долго (до суток) , так как обрабатывать их надо с наименьшим приоритетом "nice 20 и idprio 31" что бы не мешало остальным.
4. А так же возможны нештатные ситуации.

Как я это сделал:
- Пользовательский интерфейс через броузер. Имеет три кнопки ("запустить", "остановить", "пропустить задачу") и пространство в котором отображается текущее состояние работы.
- Пользователь сгружает на сервер файлы. И запускает обработку
- Далее пользователь будет получать статистику работы через "XMLHttpRequest (запросы с некоторым интервалом)", при этом, пользователь може спокойно закрывать броузер и при последующем открытии (учитывая сессию) он опять будет продолжать следить за состоянием работы.
- При этом у него есть возможность контролировать процесс.

Тем временем fork'нутый потомок:
- обходит файлы и с помощью ImageMagick'а делает по два preview-файла, при определенных условиях накладывает некий copyright, раскладывает все по своим местам, заносит информацию в базу(MySQL) и пишет в лог-файлы лог-работы который и отслеживает пользователь (если захочет).

В чем проблемы:
А проблема в том что ImageMagick иногда выкидывает разные неприятные фокусы в виде зависаний на некоторых файлах. Поэтому:
- скрипт должен уметь убить процесс ImageMagick'а по timeout'у
- занести проблемный файл в спецхран
- продолжить обрабатывать дальше (предварительно запротоколировав ошибку)
- и наравне с этим, у пользователя тоже должна быть возможность влиять на процесс, как то - прервать полностью, или пропустить файл если он обрабатывается слишком долго.

Но что бы влиять на ImageMagick надо:
- знать его PID
- и процесс потомок сам должен fork'атся что бы, уже в своем потомке, запустить IM, а родитель должен следить за ним
- при этом, процесс-обработчик должен именно fork'аться а не просто запускать exec(даже и в фоне), потому что нужно получить новый PID и запустить IM через pcntl_exec, что бы родитель мог отслеживать работу IM.

В общем:
я все это уже сделал и осталась только одна проблема - КАК СДЕЛАТЬ ТАК, что бы при завершении родителя соединение с браузером закрывалось а не ждало пока не завершиться fork'нутый потомок.

Наверное я сильно лоханулся:
Сначала я хотел писать это на PERL, но меня убедили что и в PHP есть fork и я поверил...
 

mani13

Новичок
В общем, берёте совет выше и общаетесь через fifo-файл.
Либо берёте всё тот же exec и ps...

Вообще, fork в интерпретируемых языках это нечто... fork'ается что? эмулируется fork в пределах одного php процесса? вряд ли... создаётся fork всего процесса? ну, тогда всё вполне понятно -- модуль ждёт определённого сигнала, который заложен в самом php и, соответственно, при окончании родителя или потомка этот сигнал посылается, тогда вопрос к тому как работает CGI...

P.S.: "несамостоятельный" fork(наш случай как раз...) это вообще что-то неадекватное, ибо невозможно предсказать как оно себя поведёт(копируется то ВСЁ)...
P.P.S.: perl, скорее всего, так же будет себя вести...
 

Inspirra

Новичок
Автор оригинала: mani13
В общем, берёте совет выше и общаетесь через fifo-файл.
Простите, а выше это где?

Либо берёте всё тот же exec и ps...
Вы имеете в виду "ps" тот что shell-комманда? А не могли бы вы хотя бы намекнуть как получить PID в PHP4 и не прибегая к pcntl_функциям?
Самое правильное, в данной ситуации, это использовать proc_open, но как его можно kill'нуть при необходимости?
В PHP5 есть proc_get_status и proc_terminate - это как раз то что мне не хватает!!! Но возможности использовать PHP5 у меня нет - а вот как получить PID в PHP4?

С exec у меня что то не выходит - не получается увести в background по примеру из "комментов". Вернее в background то оно уходит, но коннект с браузером ни как не хочет разрывать.
Но у меня возникла другая мысль, как отделится от браузера без fork'а - через register_shutdown_function.
Попробовал – почти работает. А "почти", потому что все равно, при выходе из скрипта соединение не разрывается пока не завершатся "register_shutdown_function" - т.е. все буфера сгружаются, но соединение не рвется, но если вызвать:
header("HTTP/1.1 200 OK");
header("Content-Length: $size");
header("Connection: close");
то соединение разрывается а процес, как мне и нужно, идет далее. Причем, этот же метод, с header'ами работает и при fork'е, но не работает с exec(background).

Вопрос: на сколько "криминально" использовать register_shutdown_function в этой ситуации?
 

camka

не самка
Сгружай файлы в предварительное хранилище, установи очередность обработки этих файлов, скажем, в базе данных, и оповещай неких демонов о том, что надо бы начать работу, и спокойно возвращай ответ браузеру пользователя. Из браузера, опять же, с помощью Ajax сможешь по состоянию базы определять текущий статус обработки файлов. Демоны будут обрабатывать файлы в очередности заложеной в базе и после успешной/неудачной обработки складывать статус в ту же базу до тех пор, пока в очереди емеются необработанные файлы, а потом останавливаются и ждут нового сигнала от веб скриптов. Довольно расширяемая система, поскольку машин с демонами можно поставить немеренное количество, тем самым распаралелив нагрузку между ними всеми. Для оповещения демонов можно использовать широковещательную UDP рассылку, чтобы безболезненно можно было добавлять/удалять демонов в независимости от их адресов в локальной сети.
 

Gorynych

Посетитель PHP-Клуба
Inspirra

черт, не помню точно, но ведь делалось это как-то, года два-три назад :)

совет 1

попробуйте послать заголовок 204 No Content (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
The server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation. The response MAY include new or updated metainformation in the form of entity-headers, which if present SHOULD be associated with the requested variant.

If the client is a user agent, it SHOULD NOT change its document view from that which caused the request to be sent. This response is primarily intended to allow input for actions to take place without causing a change to the user agent's active document view, although any new or updated metainformation SHOULD be applied to the document currently in the user agent's active view.

The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields.
совет 2
второй момент - если я правильно помню, то при написании демонов на Perl важным шагом в дочернем процессе было пристреливание потоков ввода/вывод/ошибок (проще говоря - нужно отделиться от терминала).

совет 3
ну и еще вариант: а почему бы не повесить скрипт-обработчик на cron, например - проверять если ли необработанные файлы в таком-то каталоге, если есть - обрабатывать и писать лог. При успешной обработке - удалять их из "рабочего" каталога
 

Inspirra

Новичок
> по поводу демонов.
Возможно это будет верных выход. Да и для моей задачи, наверное и самый правильный. Но у меня только одна проблема - знаний не так много :). Но если ничего путного в ближайший день не придумаю - прийдется мудрить через демонов.

>совет 1
Попробую.

>совет 3
По условиям задачи, требуется немедленный запуск.

Gorynych, mani13 забыл сказать - спасибо за участие! :)
 

Rbv

Новичок
Получилось ?
А то я ищу тоже и ни как не получается :(
 
Сверху