Зависший PHP на BitrixVM не лечится слепым рестартом FPM
По одному PID на BitrixVM смотрят дерево процессов, открытые файлы и короткий strace: так отличают залипший агент от веб-воркера до решения.

В htop на BitrixVM один PHP живёт уже 10–30 минут: load растёт, сайт то отдаёт 200, то нет, cron «залип». Рука тянется к рестарту PHP-FPM — но без разбора PID это гадание: сбиваете живые запросы и не узнаёте, это агент на cron, ожидание MySQL или внешний сокет.
Вопрос не «какой load», а что делает этот процесс и почему не заканчивается. Рестарт FPM или Apache — крайняя мера, не первый шаг.
Как это выглядит на проде

Типовая картина: CentOS, Percona Server 8.0, Apache 2.4.x, PHP 8.x, Nginx 1.26.x, Redis, Memcached. Разбор обычно из-под root. Сайт часто отвечает частично — пока есть свободные воркеры. Один залипший cron при этом может жить десятки минут при «живом» фронте.
В htop на BitrixVM уже есть нужные рычаги: Command line, сортировка по TIME+, F5 (дерево), F6 (сортировка), F3 (поиск по имени скрипта). Часто проблемный агент виден ещё до strace.
Почему слепой рестарт FPM не лечит

Рестарт снимает симптом и стирает улики. Воркер мог ждать внешний сервис в poll/connect — сайт частично жив, а причина в коде или интеграции. Или flock() на файле сессии при живых MySQL и nginx: процесс занят ожиданием лока, не CPU. Или модуль «Почта»/IMAP держит воркеры — периодический systemctl restart php-fpm убивает сессии и не устраняет зависание.
Для агента на cron status пула FPM агента не покажет: дерево процессов другое. Убивать весь пул, пока не ясен PID и скрипт — плохая идея.
Маршрут без рестарта: один PID

Порядок важен: взять один PID, посмотреть дерево процессов, какие ресурсы держит, на каком syscall реально ждёт — и только потом решать: дождаться, починить код или лимит, либо уже kill и рестарт.
pstree и ps — cron или веб
pstree -ap $PID показывает родителя. Часто видно cron---php---php — это не веб-хит, а фоновый CLI или агент. Так же отличают apache и php-fpm.
Дополняет ps -o pid,ppid,etime,stat,cmd -p $PID: etime (сколько живёт), stat (R/S/D), cmd. Если etime — десятки минут, а cmd указывает на /bitrix/modules/..., почти наверняка агент или крон. Краткий путь: top → PID с COMMAND=PHP → ps -p PID -o args=, чтобы увидеть путь скрипта.
lsof и /proc — что держит процесс
Быстро: ls -l /proc/$PID/fd | head. Подробнее: lsof -p $PID | head -n 50. Смотреть в первую очередь: MySQL и unix-сокет /var/lib/mysql/mysql.sock; пути /bitrix/cache, /upload, /tmp. Если держит MySQL-сокет — процесс внутри бизнес-логики Битрикса, а не «мёртвый PHP в воздухе».
По ситуации: vmstat 1 (I/O wait), iostat (диск), netstat -anp | grep $PID или ss (сеть). Для зависания именно FPM-хита полезны php-fpm status / ?full и slowlog (URI и стек), затем снова /proc и ps по PID — но для ветки cron---php status пула агента не покажет.
strace — точка ожидания
Если etime большой, а CPU почти не ест — процесс ждёт. Рабочая команда: strace -p $PID -tt -T -f -s 128 -o /tmp/strace.cron_$PID.log, снять 10–20 секунд, затем хвост лога. Типовые syscall: futex() (лок, кеш, shm); read()/write() (файл или сокет); poll()/select() (ответ MySQL/Redis); open() на медленном диске.
Оговорка: strace через ptrace замедляет цель в разы (оценки — от нескольких до ~100×). На стенде ок; на нагруженном проде сам усиливает тормоза. Альтернативы с меньшей нагрузкой — eBPF / perf trace. Для одного залипшего PID краткий attach на 10–20 с обычно приемлем; не вешать strace на весь пул FPM.
Почему агент «висит» полчаса — часто по дизайну
По документации 1С-Битрикс «тяжёлый» агент — дольше 10 секунд. Агенты на хитах дают ожидание пользователю и накопительный эффект после простоя. Штатный cron_events.php содержит @set_time_limit(0) и ignore_user_abort(true) — лимит времени снимается, если set_time_limit разрешён. Пример crontab: */1 * * * * /usr/bin/php -f /home/bitrix/www/bitrix/php_interface/cron_events.php. Пользователь CLI должен совпадать с веб-пользователем (bitrix), иначе ломаются права на кеш.
Из-за set_time_limit(0) фоновый PHP на BitrixVM по дизайну может жить дольше любого max_execution_time веба. «Висит 30 минут» для агента часто не баг интерпретатора, а ожидаемое поведение без батчей и таймаута в коде. Практика: копия скрипта в /local/php_interface/, лимит например 60 секунд, путь в crontab на новый файл — иначе зависшие функции не выгружаются и держат ресурсы, в том числе MySQL. Лимит «60 с» — пример из практики, не вендорский default.
Подводные камни и лестница жёсткости
Типичные исходы разбора: агент без лимитов (вечный PHP); ORM без limit в CLI; массовая работа с файловым /bitrix/cache на медленном диске; MySQL-блокировки — процесс не «завис», а ждёт таблицу.
Не путать симптомы:
- stat=D / высокий wa → диск;
- futex / flock → лок;
- poll к внешнему IP → сеть/API;
- MySQL sock + ожидание → блокировка или долгий запрос.
Лестница: понять причину → дать дожить или починить код → timeout и батчи → переписать агент или крон → только потом kill. Слепой kill и слепой рестарт пула — в конце списка, не в начале.
Частые вопросы
Почему сайт ещё отвечает 200, а PHP в htop уже полчаса?
Часто сайт отвечает частично, пока есть свободные воркеры FPM. Один залипший cron или агент при этом может жить десятки минут при «живом» фронте — рестарт всего пула здесь не первая диагностика.
Как отличить cron-агента от воркера PHP-FPM по одному PID?
Команда pstree -ap $PID: ветка вида cron---php---php — фоновый CLI или агент, не веб-хит. Дополнительно ps -o etime,stat,cmd -p $PID: длинный etime и путь в /bitrix/modules/... почти наверняка указывают на агент или крон; status пула FPM такого агента не покажет.
Безопасно ли вешать strace на зависший PHP на горячем проде?
Краткий attach на один PID на 10–20 секунд обычно приемлем. Массовый strace на весь пул FPM на нагруженном проде нежелателен: ptrace замедляет цель в разы (оценки от нескольких до ~100×); для широкого сбора смотрят eBPF или perf trace.
Почему штатный cron_events.php может жить дольше max_execution_time веба?
В официальном запуске агентов из cron в cron_events.php стоят @set_time_limit(0) и ignore_user_abort(true) — лимит времени снимается. «Тяжёлый» агент по документации — дольше 10 секунд; без батчей и своего таймаута процесс может жить десятки минут. Практика: копия в /local/php_interface/ с лимитом (например 60 с) и новый путь в crontab.
Как BX Pulse помогает не гадать по одному PID
BX Pulse следит за доступностью и техническим состоянием проектов на 1С-Битрикс и помогает замечать деградацию до жалобы клиента — когда один процесс или агент уже тянет ресурсы, а фронт ещё частично отвечает. Добавьте первый сайт и настройте уведомления: https://bx-monitor.ru/.


