Назад | Перейти на главную страницу

Что может привести к тому, что сценарий PHP, запущенный как задание cron, окажется в состоянии D?

На моем сервере работает Ubuntu 12.04 LTS (точно), и у меня странная проблема. На сервере размещен большой веб-сайт, который используется для сбора данных. Веб-сайт написан на PHP и использует Zend Framework. Данные находятся в базе данных MySQL. Часть этих данных (из опросов) преобразуется в файлы Excel (с использованием библиотеки PHPExcel) каждый час (задание cron). Этот процесс (сценарий PHP / Zend Framework) выполняется для каждого клиента, каждый с другим набором данных, и иногда может занять много времени (30+ минут).

Как только он достигает 30-минутной отметки, состояние процесса меняется с R на D. Что странно, так это то, что процессы в состоянии D обычно «неубиваемы», но этот процесс можно убить, как и любой другой процесс. Вот пример вывода, когда процесс работает нормально:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
www      16089  0.5  0.8 311640 31708 ?        S    09:34   0:39 /usr/sbin/apache2 -k start
www      17635  0.6  0.6 305020 23396 ?        S    10:52   0:18 /usr/sbin/apache2 -k start
www      18520  0.0  0.0  63104  1960 pts/0    S    11:32   0:00 su www
www      18521  0.0  0.1  23236  4516 pts/0    S    11:32   0:00 bash
www      18621 98.8 68.1 2665208 2416568 pts/0 R    11:33  10:48 php run.php -a hourly
www      18659  0.6  0.6 302848 22948 ?        S    11:34   0:03 /usr/sbin/apache2 -k start
www      18876  0.0  0.0  18160  1244 pts/0    R+   11:44   0:00 ps ux

Как видите, этот процесс требует больших ресурсов: он уже сильно обрезан и все еще подвергается операции, но мне нужно его завершить, чтобы я мог получить итоговые файлы Excel. Процесс регистрирует множество своих действий (~ 300 КБ журнала для каждого файла Excel), и файлы журнала всегда заканчиваются внезапно. Я нигде не регистрировал ошибок.

Вот тот же список процессов сразу после 30-минутной отметки:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
www      18034  1.0  0.7 312412 26632 ?        S    11:13   0:33 /usr/sbin/apache2 -k start
www      18245  2.1  0.4 303656 16912 ?        S    11:25   0:49 /usr/sbin/apache2 -k start
www      18520  0.0  0.0  63104   296 pts/0    S    11:32   0:00 su www
www      18521  0.0  0.0  23236   968 pts/0    S    11:32   0:00 bash
www      18621 96.3 85.0 3969192 3012596 pts/0 D    11:33  30:08 php run.php -a hourly
www      18659  0.4  0.5 302856 19136 ?        S    11:34   0:08 /usr/sbin/apache2 -k start
www      19431  0.0  0.0  18160  1240 pts/0    R+   12:04   0:00 ps ux

Если оставить процесс запущенным, он все равно потребляет ресурсы, но ничего не делает. Больше ничего не регистрируется.

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
www      18621  9.1 80.0 4030532 2835032 ?     D    11:33  33:19 php run.php -a hourly

Обратите внимание, что состояние процесса меняется ровно через 30 минут, он будет обрабатывать больше или меньше данных в зависимости от загрузки сервера. Что интересно, это происходит только на рабочем сервере. На моем сервере разработки, Ubuntu 12.04, этой проблемы нет.

Есть ли что-нибудь, что могло бы заставить процессы нормально работать максимум 30 минут, а затем изменить свое состояние на D? Что может привести к тому, что PHP-скрипт окажется в состоянии D?

Во-первых, процесс в состоянии D находится в непрерывный сон (обычно IO).

Обычно то, что вызывает перевод процесса в это состояние, - это выполнение блокировка системного вызова.

Вы можете увидеть, какой последний вызов ваш процесс сделал, используя strace:

strace -fp <pid>

Поскольку состояние D обычно вызывается операцией ввода-вывода, также полезно проверить, какие файлы открыл процесс, используя lsof:

lsof -p <pid>