Глава 137 · Практика
PCNTL: процессы, сигналы, ожидание, CPU и изоляция Unix
Полные34функции PCNTL, все константы и QosClass: от одного собственного ребёнка до ограниченного worker, сигналов и платформенных механизмов.
Задача → процесс → ребёнок → проверенный результат
Ночью нужно обработать несколько независимых файлов импорта. Сайт не должен держать HTTP соединение всё это время: отдельный CLI worker берёт задания, а родитель может запускать несколько ограниченных исполнителей, дожидаться результатов и завершать работу аккуратно. PCNTL даёт PHP доступ к Unix процессам и сигналам. Он не превращает обычный запрос сайта в безопасный фоновой сервис одним вызовом.
Процесс — запущенная программа со своим адресным пространством, PID и ресурсами ОС. Родитель — процесс, который создал ребёнка. Worker — прикладное название исполнителя задания. Сигнал — короткое уведомление ОС, например «пора завершиться»; массив заказа передают другим каналом. До этой главы нужны переменные, функции и return, условия и циклы, callback и захват переменных. Для первой страницы PCNTL мы сначала различим результаты fork, затем запустим одного ребёнка, и только потом перейдём к обработчикам/платформенным механизмам.
| Момент | Что действительно происходит |
|---|---|
| До fork | Один PHP процесс и один текущий путь исполнения. |
| Результат fork в родителе | Положительный PID ребёнка: родитель организует ожидание. |
| Результат fork в ребёнке | 0: ребёнок выполняет своё задание и заканчивает свою ветку. |
| Результат -1 | Новый процесс не создан; нельзя запускать ветку ребёнка. |
| Ожидание | Родитель получает wait result, проверяет kind события, затем читает exit value/сигнал. |
<?php
declare(strict_types=1);
foreach ([142, 0, -1] as $forkResult) {
echo match (true) {
$forkResult > 0 => "Родитель знает PID ребёнка\n",
$forkResult === 0 => "Дочерняя ветка\n",
default => "Создание не удалось\n",
};
}
Ожидаемый результат
Родитель знает PID ребёнка
Дочерняя ветка
Создание не удалось
Чистая модель условий:142 — выдуманное значение, по нему не отправляются сигналы. В реальном коде используют только PID от своего fork.
Установка, SAPI, версии и отдельные требования ОС
PCNTL требует Unix системных вызовов и не работает в Windows PHP. Расширение не включается при сборке по умолчанию; сборка PHP использует --enable-pcntl, а готовые пакеты дистрибутива могут уже содержать его. Проверяйте именно CLI executable, которым запускается worker. POSIX — отдельное расширение: наличие pcntl_fork не обещает наличие posix_kill/posix_setsid. Собственных php.ini директив и resource types у PCNTL нет; enum описан отдельно.
<?php
declare(strict_types=1);
echo 'SAPI: ', PHP_SAPI, "\n";
foreach (['pcntl_fork', 'pcntl_waitid', 'pcntl_getcpuaffinity', 'pcntl_setqos_class', 'posix_kill'] as $name) {
echo $name, ': ', function_exists($name) ? 'есть' : 'нет', "\n";
}
echo 'QosClass: ', enum_exists('Pcntl\QosClass', false) ? 'есть' : 'нет', "\n";
Ожидаемый результат
SAPI и наличие пяти функций/enum в конкретном PHP запуске.
Диагностический код переносим; на Windows PCNTL отсутствует. Наличие7из34функций нельзя переносить на весь API: QoSApple, namespacesLinux, rforkBSD, forkxSolaris. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
PCNTL не предназначен для создания процессов внутри web server/PHP-FPM запроса: унаследованные сокеты, состояние расширений и жизненный цикл pool требуют другой модели. HTTP handler ставит задание в очередь; supervisor запускает CLI worker, а сайт получает статус по ID задания. Для вызова Node.js/Python с pipes часто удобнее proc_open. Базовая версия авторских карточек PHP8.5.11; платформенные примеры в этой Windows сборке проверены только синтаксически. Нет заявления об испытании Linux/Apple/BSD runtime.
| Появление | Что учитывать |
|---|---|
| PHP5.3 | async вспомогательные getter ошибок/dispatch и continued predicate; async_signals появился позже, в7.1. |
| PHP7.1 | async_signals и signal_get_handler; siginfo в callback при поддержке. |
| PHP7.4 /8.1 /8.2 | unshare / rfork / forkx соответственно. |
| PHP8.4 | waitid, affinity/getcpu, namespace setns и QoS API/enum; ужесточены аргументы signal wait/mask. |
| PHP8.5 | resource_usage у waitid; exec отвергает NUL в args/env. |
Выбор инструмента и стоимость подхода
| Нужно | С чего начать |
|---|---|
| Выполнить одну внешнюю программу и прочитать stdout | proc_open/exec; PCNTL exec заменяет текущий процесс. |
| Одно независимое PHP задание параллельно | pcntl_fork + pcntl_waitpid. |
| Несколько worker | Ограниченное число PID, учёт каждого результата, очередь заданий и deadline. |
| Попросить worker закончить текущий шаг | pcntl_signal + pcntl_async_signals; callback ставит флаг. |
| Дождаться собственного заблокированного уведомления | pcntl_sigtimedwait; не ждать бесконечно без причины. |
| Получить JSON/массив из ребёнка | pipe/socket stream, IPC очередь или хранилище; exit code только краткий статус. |
| Уступить вычислительные ресурсы | pcntl_setpriority или AppleQoS; ограничения CPU/RAM задаются отдельно. |
| Изолировать служебное окружение | Обычно контейнер/менеджер служб; низкоуровневые pcntl_unshare / pcntl_setns требуют подготовки. |
Fork не обещает ускорение: копирование/учёт памяти, scheduling, открытие новых соединений и IPC стоят ресурсов. CPU-bound работа ограничена CPU quota; I/O-bound — latency и лимитами внешнего сервиса. Не fork на каждую строку миллиона записей. Готовые отдельные workers и пакетная обработка часто дешевле. Не делайте неограниченный while с WNOHANG: без ожидания он сжигает CPU.
От события ОС к callback и безопасной точке остановки
Сигнал проходит несколько этапов: ОС создаёт событие → mask допускает доставку или оставляет pending → установленный handler определяет реакцию → PHP dispatch вызывает callback → основной цикл читает изменённый флаг. SIG_IGN выбрасывает реакцию; SIG_BLOCK откладывает. Обычные сигналы могут объединяться: три SIGUSR1 не являются надёжной очередью из трёх заданий. Синхронное sigwaitinfo/sigtimedwait забирает выбранный заблокированный сигнал вместо callback.
В callback не нужно открывать сетевое соединение и проводить длинную транзакцию. Поставьте stop/reload flag; основной код проверит его между короткими шагами и завершит работу последовательно. Async означает автоматический dispatch PHP, а не независимый параллельный поток. Блокирующие native вызовы могут долго не возвращать управление, поэтому у HTTP/БД/чтения всё равно нужны собственные timeouts. SIGKILL не даёт finally/деструкторам завершиться: устойчивость данных обеспечивает протокол записи и восстановления.
<?php
declare(strict_types=1);
$stop = false;
foreach (['A42', 'B17', 'C03'] as $sku) {
if ($stop) break;
echo "Готово: $sku\n";
$stop = true;
}
echo "Остальные задания остались в очереди\n";
Ожидаемый результат
Готово: A42
Остальные задания остались в очереди
Модель показывает, в какой точке проверяют stop flag. В реальном worker его устанавливает callback, а durable queue подтверждает только успешно завершённое задание.
Флаги ожидания и типы идентификаторов
| Имя константы | Для чего и где используется |
|---|---|
WNOHANG | Вернуть управление, если события ещё нет; return0 у waitpid не exit code. |
WUNTRACED | Получать также остановки собственных детей через waitpid/wait. |
WCONTINUED | Получать возобновление остановленных детей там, где ОС поддерживает событие. |
WEXITED | Выбирать завершение в waitid. |
WSTOPPED | Выбирать остановку в waitid. |
WNOWAIT | Посмотреть событие waitid, оставив его доступным для настоящего сбора позже. |
P_ALL | waitid: все подходящие собственные дети, id игнорируется. |
P_PID | waitid: конкретный PID ребёнка. |
P_PGID | waitid: дети выбранной process group; это не GID пользователя. |
P_PIDFD | Linux waitid: descriptor процесса; не PHP resource ID. |
P_UID | Платформенный waitid selector детей по effective user ID. |
P_GID | Платформенный waitid selector детей по effective group ID. |
P_SID | Платформенный waitid selector детей по session ID. |
P_JAILID | FreeBSD waitid selector по jail ID. |
Значения integer и наличие имён определяет сборка/ОС: проверяйте defined перед платформенным использованием. Числа не переносятся вручную из другой системы. Символы в этой таблице покрывают полный taggedPHP8.5.11 PCNTL stub; разные группы принадлежат разным аргументам, их нельзя перемешивать в одной mask.
Все сигналы и специальные handler значения
| Имя константы | Для чего и где используется |
|---|---|
SIG_IGN | Специальное значение handler: игнорирование сигнала, не номер сигнала. |
SIG_DFL | Восстановить стандартную реакцию ОС на этот сигнал. |
SIG_ERR | Системный sentinel ошибки настройки; не допустимый PHP handler для signal(). |
SIGHUP | Отсоединение терминала; программы часто договариваются использовать для перезагрузки конфигурации. |
SIGINT | Прерывание интерактивной программы, обычно Ctrl+C. |
SIGQUIT | Запрос завершения с диагностикой по правилам ОС, часто Ctrl+\. |
SIGILL | Недопустимая машинная инструкция; не обычная ошибка синтаксиса PHP. |
SIGTRAP | Ловушка отладки/трассировки. |
SIGABRT | Аварийное прекращение процесса через abort. |
SIGIOT | Историческое имя/alias, часто тот же сигнал, что SIGABRT. |
SIGBUS | Ошибка доступа к памяти/объекту на уровне ОС; не PHP индекс массива. |
SIGFPE | Арифметическая ошибка уровня процессора; не обещание сигнала для любого деления PHP на0. |
SIGKILL | Принудительное завершение; нельзя перехватить, заблокировать или выполнить PHP cleanup handler. |
SIGUSR1 | Один из двух сигналов для собственного соглашения приложения; payload массива не содержит. |
SIGSEGV | Недопустимый доступ памяти; обычно сбой native кода/расширения, не PHP исключение. |
SIGUSR2 | Второй сигнал приложения; смысл задаёт само приложение. |
SIGPIPE | Запись в pipe/socket без принимающей стороны по правилам ОС. |
SIGALRM | Срабатывание обычного alarm таймера. |
SIGTERM | Обычная просьба завершиться; подготовленный worker может закончить текущий шаг. |
SIGSTKFLT | Платформенный/исторический stack fault. |
SIGCLD | Платформенное альтернативное имя SIGCHLD. |
SIGCHLD | Изменение состояния ребёнка; одно уведомление не равно ровно одному wait result. |
SIGCONT | Продолжить остановленный процесс; это не завершение задания. |
SIGSTOP | Безусловная остановка; нельзя перехватить/игнорировать/блокировать. |
SIGTSTP | Терминальная остановка, обычно Ctrl+Z; отличается от безусловного SIGSTOP. |
SIGTTIN | Фоновая process group пытается читать controlling terminal. |
SIGTTOU | Фоновая process group выполняет определённые операции вывода/управления терминалом. |
SIGURG | Срочное состояние socket по правилам ОС. |
SIGXCPU | Достигнута CPU time граница; не timeout ожидания сети. |
SIGXFSZ | Достигнуто ограничение размера файла. |
SIGVTALRM | Виртуальный interval timer, считающий user CPU время. |
SIGPROF | Профилирующий interval timer; смысл времени задаётся ОС. |
SIGWINCH | Изменился размер terminal window. |
SIGPOLL | Событие pollable I/O; часто alias SIGIO. |
SIGIO | Асинхронная готовность I/O по настройкам ОС. |
SIGPWR | Состояние питания на поддерживающей ОС. |
SIGINFO | BSD запрос статусной информации. |
SIGSYS | Некорректный системный вызов или ограничение syscall по правилам ОС. |
SIGBABY | Историческое альтернативное имя SIGSYS на некоторых системах. |
SIGCKPT | DragonFly checkpoint request; добавлен PHP8.4 при наличии системного имени. |
SIGCKPTEXIT | DragonFly checkpoint with exit; добавлен PHP8.4 при наличии системного имени. |
SIGRTMIN | Нижняя граница поддерживаемых realtime сигналов, не универсальная числовая константа. |
SIGRTMAX | Верхняя граница realtime диапазона; учитывать резервирование библиотеками/ОС. |
Значения integer и наличие имён определяет сборка/ОС: проверяйте defined перед платформенным использованием. Числа не переносятся вручную из другой системы. Символы в этой таблице покрывают полный taggedPHP8.5.11 PCNTL stub; разные группы принадлежат разным аргументам, их нельзя перемешивать в одной mask.
Реакцию по умолчанию и aliases сверяйте с signal(7) своей ОС. Перехватывать аппаратный SIGSEGV/SIGBUS и «продолжать PHP как ни в чём не бывало» не является восстановлением приложения. Стандартные методы PHP валидации/исключения решают другие ошибки.
Приоритеты и режимы маски сигналов
| Имя константы | Для чего и где используется |
|---|---|
PRIO_PROCESS | ID обозначает процесс; default для getpriority/setpriority. |
PRIO_PGRP | ID обозначает process group; установка затрагивает группу процессов. |
PRIO_USER | ID обозначает пользователя; установка затрагивает его процессы по правилам ОС. |
PRIO_DARWIN_THREAD | Apple mode для текущего потока; process_id должен быть0/null. |
PRIO_DARWIN_BG | Apple специальное значение priority при modePRIO_DARWIN_THREAD, не самостоятельный mode PHP wrapper. |
SIG_BLOCK | Добавить перечисленные сигналы к заблокированным. |
SIG_UNBLOCK | Убрать перечисленные сигналы из заблокированных. |
SIG_SETMASK | Заменить маску целиком; сохранённый список восстанавливают этим режимом. |
Значения integer и наличие имён определяет сборка/ОС: проверяйте defined перед платформенным использованием. Числа не переносятся вручную из другой системы. Символы в этой таблице покрывают полный taggedPHP8.5.11 PCNTL stub; разные группы принадлежат разным аргументам, их нельзя перемешивать в одной mask.
Все коды происхождения и причины siginfo
| Имя константы | Для чего и где используется |
|---|---|
SI_USER | Источник siginfo.code: послан пользователем/kill по правилам ОС. |
SI_NOINFO | Источник/дополнительная информация отсутствует на этой платформе. |
SI_KERNEL | Сигнал сформирован ядром. |
SI_QUEUE | Сигнал отправлен очередным механизмом sigqueue; PHP набор доступных siginfo полей ограничен wrapper. |
SI_TIMER | Сработал POSIX timer. |
SI_MESGQ | Уведомление POSIX message queue, не PHP SysV msg queue. |
SI_ASYNCIO | Завершение асинхронного I/O. |
SI_SIGIO | Очередное SIGIO уведомление. |
SI_TKILL | Источник thread-directed tkill/tgkill на Linux. |
CLD_EXITED | Ребёнок завершён обычным exit/return; info.status — его exit value. |
CLD_KILLED | Ребёнок завершён сигналом; info.status — номер сигнала. |
CLD_DUMPED | Ребёнок завершён сигналом с core-dump событием ОС; наличие доступного файла dump отдельно зависит от политики. |
CLD_TRAPPED | Состояние tracing trap ребёнка. |
CLD_STOPPED | Ребёнок остановлен; info.status — stop signal. |
CLD_CONTINUED | Ребёнок продолжен после остановки. |
TRAP_BRKPT | Причина SIGTRAP: breakpoint. |
TRAP_TRACE | Причина SIGTRAP: trace trap. |
POLL_IN | Причина SIGPOLL: доступны входные данные. |
POLL_OUT | Причина SIGPOLL: возможно выполнение записи. |
POLL_MSG | Причина SIGPOLL: доступно входное сообщение. |
POLL_ERR | Причина SIGPOLL: ошибка I/O. |
POLL_PRI | Причина SIGPOLL: высокоприоритетные входные данные. |
POLL_HUP | Причина SIGPOLL: отключение другой стороны. |
ILL_ILLOPC | Причина SIGILL: недопустимый opcode. |
ILL_ILLOPN | Причина SIGILL: недопустимый operand. |
ILL_ILLADR | Причина SIGILL: недопустимый режим адресации. |
ILL_ILLTRP | Причина SIGILL: недопустимая trap инструкция. |
ILL_PRVOPC | Причина SIGILL: привилегированная операция без необходимых условий. |
ILL_PRVREG | Причина SIGILL: обращение к привилегированному регистру. |
ILL_COPROC | Причина SIGILL: ошибка сопроцессора. |
ILL_BADSTK | Причина SIGILL: внутренняя ошибка стека. |
FPE_INTDIV | Причина SIGFPE: integer division by zero уровня процессора. |
FPE_INTOVF | Причина SIGFPE: integer overflow уровня процессора. |
FPE_FLTDIV | Причина SIGFPE: floating division by zero. |
FPE_FLTOVF | Причина SIGFPE: floating overflow. |
FPE_FLTUND | Причина SIGFPE: floating underflow. |
FPE_FLTRES | Причина SIGFPE: неточный floating результат. |
FPE_FLTINV | Причина SIGFPE: недопустимая floating операция. |
FPE_FLTSUB | Причина SIGFPE: недопустимый subscript на поддерживающей системе. |
SEGV_MAPERR | Причина SIGSEGV: адрес не отображён в память. |
SEGV_ACCERR | Причина SIGSEGV: недостаточные права доступа к отображённому адресу. |
BUS_ADRALN | Причина SIGBUS: недопустимое выравнивание адреса. |
BUS_ADRERR | Причина SIGBUS: отсутствующий физический адрес. |
BUS_OBJERR | Причина SIGBUS: ошибка относящегося к памяти объекта. |
Значения integer и наличие имён определяет сборка/ОС: проверяйте defined перед платформенным использованием. Числа не переносятся вручную из другой системы. Символы в этой таблице покрывают полный taggedPHP8.5.11 PCNTL stub; разные группы принадлежат разным аргументам, их нельзя перемешивать в одной mask.
Сначала определите signo и смысл info.code для этого сигнала. CLD_EXITED и ILL_ILLOPC не взаимозаменяемые общие ошибки. Обязательные поля сигнального info: signo, errno, code; pid/uid/status/utime/stime, addr, band/fd и realtime поля появляются только при соответствующей поддержке и ветке PHP wrapper. SIGUSR1 не гарантирует sender pid/uid в PHP массиве. Не читайте отсутствующие ключи без проверки.
Все namespace и platform fork флаги
| Имя константы | Для чего и где используется |
|---|---|
CLONE_NEWNS | Linux mount namespace; propagation и mount policy настраиваются отдельно. |
CLONE_NEWIPC | Linux IPC namespace: отдельное пространство соответствующих IPC объектов. |
CLONE_NEWUTS | Linux UTS namespace: hostname/domainname окружение. |
CLONE_NEWNET | Linux network namespace: интерфейсы, маршруты и сетевой стек; сеть не настраивается автоматически. |
CLONE_NEWPID | Linux PID namespace; для unshare влияет на будущих детей, не на PID вызывающего процесса. |
CLONE_NEWUSER | Linux user namespace; нужны UID/GID mapping и разрешение системной политики. |
CLONE_NEWCGROUP | Linux cgroup namespace; меняет представление пути, не автоматически назначает CPU/RAM quota. |
RFPROC | BSD создать процесс; PHP rfork добавляет этот флаг всегда. |
RFNOWAIT | BSD не сохранять обычный waitable статус ребёнка для родителя. |
RFCFDG | BSD очистить FD table ребёнка, включая стандартные descriptors; конфликтует с RFFDG. |
RFFDG | BSD скопировать FD table отдельно; сами open file descriptions всё ещё могут быть общими. |
RFLINUXTHPN | BSD Linux threading-related режим уведомления SIGUSR1; не PHP threads. |
RFTSIGZMB | BSD использовать второй аргумент signal для завершения ребёнка;0 — без уведомления по этому механизму. |
RFTHREAD | BSD связать process leader semantics; не разрешает общую мутацию PHP heap. |
FORK_NOSIGCHLD | Solaris/illumos подавить SIGCHLD о завершении этого ребёнка; wait всё равно нужен. |
FORK_WAITPID | Solaris/illumos разрешить сбор только явным ожиданием конкретного PID. |
Значения integer и наличие имён определяет сборка/ОС: проверяйте defined перед платформенным использованием. Числа не переносятся вручную из другой системы. Символы в этой таблице покрывают полный taggedPHP8.5.11 PCNTL stub; разные группы принадлежат разным аргументам, их нельзя перемешивать в одной mask.
Все опубликованные PCNTL errno
| Имя константы | Для чего и где используется |
|---|---|
PCNTL_EINTR | Системный вызов прерван сигналом; retry зависит от операции, stop flag и deadline. |
PCNTL_ECHILD | Нет подходящего waitable ребёнка; это не WNOHANG отсутствие нового события. |
PCNTL_EINVAL | Некорректный аргумент/режим или неподдерживаемая комбинация; ряд wrapper выбрасывает ValueError. |
PCNTL_EAGAIN | Временная недоступность/лимит ресурсов; fork не создал ребёнка. Для timed wait timeout нужно учитывать особый договор wrapper. |
PCNTL_ESRCH | Не найден процесс с указанной целью либо цель уже исчезла. |
PCNTL_EACCES | Недостаточно разрешений доступа, например выполнение файла. |
PCNTL_EPERM | Операция запрещена текущими полномочиями/политикой. |
PCNTL_ENOMEM | Недостаточно памяти/ресурса ядра для операции. |
PCNTL_E2BIG | Слишком большой суммарный блок аргументов и окружения exec. |
PCNTL_EFAULT | Недопустимый адрес данных системного вызова; не обычный отсутствующий PHP array key. |
PCNTL_EIO | Ошибка ввода/вывода при системной операции. |
PCNTL_EISDIR | Там, где нужен исполняемый файл/интерпретатор, оказался каталог. |
PCNTL_ELIBBAD | Неподходящий/повреждённый исполняемый интерпретатор/библиотека по правилам загрузчика. |
PCNTL_ELOOP | Слишком много переходов по symbolic links или рекурсивному interpreter chain. |
PCNTL_EMFILE | Лимит файловых descriptors текущего процесса. |
PCNTL_ENAMETOOLONG | Слишком длинное имя/компонент пути для ОС. |
PCNTL_ENFILE | Системная таблица открытых файлов исчерпана; отличается от собственного EMFILE. |
PCNTL_ENOENT | Не найден executable либо нужный interpreter, даже если основной script существует. |
PCNTL_ENOEXEC | Нераспознанный формат исполняемого файла/архитектура; проверить shebang и executable. |
PCNTL_ENOTDIR | Промежуточный компонент пути оказался не каталогом. |
PCNTL_ETXTBSY | Исполняемый файл открыт для записи или занят по правилам ОС. |
PCNTL_ENOSPC | Не хватает системного ресурса, например превышен namespace nesting limit; не всегда диск. |
PCNTL_EUSERS | Платформенный/исторический лимит вложенности user namespace. |
PCNTL_ECAPMODE | Операция запрещена capability mode FreeBSD. |
Значения integer и наличие имён определяет сборка/ОС: проверяйте defined перед платформенным использованием. Числа не переносятся вручную из другой системы. Символы в этой таблице покрывают полный taggedPHP8.5.11 PCNTL stub; разные группы принадлежат разным аргументам, их нельзя перемешивать в одной mask.
Полный разбор basic daemon example и рабочий путь к сервису
В официальном basic example родитель после fork заканчивается, ребёнок вызывает setsid, устанавливает SIGTERM/SIGHUP и выполняет бесконечный цикл. Это иллюстрация Unix daemonization, а не готовый шаблон эксплуатации. setsid создаёт сеанс, но не закрывает стандартные handles, не устанавливает CWD/umask, не решает логирование, повторный запуск и supervisor policy. Дочерние shutdown callbacks и соединения нужно учитывать отдельно. Под systemd/другим supervisor обычно нужен foreground process без самостоятельного detach.
Сохраним учебные механизмы примера, но ограничим работу тремя короткими шагами, оставим родителя ждать и явно завершим ребёнка. Здесь SIGHUP означает флаг перезагрузки настроек, SIGTERM — просьбу закончить цикл; это соглашение нашего worker. Чтение реального конфига должно проверить содержимое до замены действующей конфигурации.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
if (posix_setsid() === -1) exit(2);
$stop = false;
$reload = false;
if (!pcntl_signal(SIGTERM, function () use (&$stop): void { $stop = true; })) exit(3);
if (!pcntl_signal(SIGHUP, function () use (&$reload): void { $reload = true; })) exit(3);
pcntl_async_signals(true);
for ($step = 1; $step <= 3 && !$stop; $step++) {
if ($reload) { echo "Точка проверки нового конфига\n"; $reload = false; }
echo "Шаг $step завершён\n";
}
exit(0);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
if (!pcntl_wifexited($status) || pcntl_wexitstatus($status) !== 0) {
throw new RuntimeException('Worker завершился неуспешно');
}
echo "Все учебные шаги завершены\n";
Ожидаемый результат
Шаг1/2/3 завершён, затем Все учебные шаги завершены при отсутствии внешних сигналов.
setsid вызывается в новом ребёнке, а родитель собирает его результат. Это короткая демонстрация Unix механики, не оставляемый фоновый демон. Реальный worker принимает задания из очереди и ждёт I/O вместо busy loop. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
План дальнейшей сборки сервиса: HTTP принимает/проверяет данные → сохраняет задание с ID → supervisor держит ограниченный CLI pool → worker создаёт свои DB/API подключения → подтверждает только завершённую операцию → родитель/менеджер замечает отказ и применяет политику retry. Повторное задание требует идемпотентности интеграции, потому что выход по сигналу может произойти после внешнего действия, но до записи его подтверждения. Общую память двух процессов явно организуют через IPC; изменение переменной ребёнка не возвращает результат родителю.
Полный указатель, сочетания и самостоятельная задача
| Категория | Все функции |
|---|---|
| Создание, замена и ожидание | pcntl_fork, pcntl_exec, pcntl_waitpid, pcntl_wait, pcntl_waitid |
| Разбор полученного status | pcntl_wifexited, pcntl_wexitstatus, pcntl_wifsignaled, pcntl_wtermsig, pcntl_wifstopped, pcntl_wstopsig, pcntl_wifcontinued |
| Сигналы и таймер | pcntl_signal, pcntl_signal_get_handler, pcntl_async_signals, pcntl_signal_dispatch, pcntl_sigprocmask, pcntl_sigwaitinfo, pcntl_sigtimedwait, pcntl_alarm |
| Приоритет, CPU и QoS | pcntl_getpriority, pcntl_setpriority, pcntl_getcpuaffinity, pcntl_setcpuaffinity, pcntl_getcpu, pcntl_getqos_class, pcntl_setqos_class |
| Namespace и особый fork | pcntl_unshare, pcntl_setns, pcntl_rfork, pcntl_forkx |
| Ошибки | pcntl_get_last_error, pcntl_errno, pcntl_strerror |
Отдельно: Pcntl\QosClass — все пять случаев enum.
Задание: создайте одного собственного ребёнка, который возвращает код2 при неудаче учебной проверки. Родитель должен отличить normal exit2 от завершения сигналом; WNOHANG0 не считать успешным результатом. Затем добавьте stop flag между короткими задачами и объясните, почему SIGTERM не заменяет HTTP timeout. Правильный результат включает сбор ребёнка и отсутствие бесконечного polling. Готовые основы находятся в карточках fork/waitpid/wifexited/wexitstatus/signal; не начинайте с namespace и повышения приоритета.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
pcntl_forkРазделить обработку независимых заданий на разные процессы: родитель организует работу, ребёнок выполняет своё задание.
pcntl_fork(): intРазделить обработку независимых заданий на разные процессы: родитель организует работу, ребёнок выполняет своё задание.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_fork(): int |
| Аргументы | Нет. |
| Результат и ошибки | В родителе — положительный PID ребёнка; в ребёнке —0; отказ —-1, warning и PCNTL errno, ребёнок не создан. |
| Среда | PHP4.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Вызов продолжается дважды с разными результатами. Это разные процессы, не два потока с общим PHP массивом. Изменение обычной переменной ребёнка не изменит родителя. Бесконтрольный fork в цикле исчерпает процессы/память; ограничьте число живых детей.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
echo "Ребёнок: считаю импорт
";
exit(0);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
echo "Родитель: ребёнок завершён
";
Ожидаемый результат
Ребёнок: считаю импорт
Родитель: ребёнок завершён
Ребёнок заканчивает ветку через exit. Родитель получает его состояние через waitpid, поэтому вывод о завершении идёт после ребёнка. -1 и0 сравниваются строго. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$stock = 7;
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
$stock = 9;
echo "Ребёнок stock=$stock
";
exit(0);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
echo "Родитель stock=$stock
";
Ожидаемый результат
Ребёнок stock=9
Родитель stock=7
При fork начальное содержимое памяти одинаковое, но дальше изменения обычной памяти раздельны. Linux использует copy-on-write; это не обещание нулевой стоимости. Для возвращения большого результата используйте IPC, а не exit code. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Открытые до fork дескрипторы наследуются и могут ссылаться на один open file description/offset. Протокол подключения к БД нельзя безопасно вести двумя независимыми клиентскими объектами через один сокет: открывайте подключения в нужном процессе после fork. Завершение PHP вызывает собственные destructors/shutdown handlers, которые тоже могли быть унаследованы. Активные output buffers способны продублировать накопленный до fork вывод. Фоновые потоки сторонних библиотек усложняют fork; контролируемые отдельные процессы под supervisor часто проще.
Связь: pcntl_waitpid, pcntl_exec, pcntl_get_last_error.
pcntl_execЗаменить текущий одноразовый PHP процесс другой программой, когда launcher закончил подготовку.
pcntl_exec(string $path, array $args = [], array $env_vars = []): falseЗаменить текущий одноразовый PHP процесс другой программой, когда launcher закончил подготовку.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_exec(string $path, array $args = [], array $env_vars = []): false |
| Аргументы | path — исполняемый файл, без аргументов; args — список отдельных строк; env_vars — name=>value. Script executable требует корректного shebang. |
| Результат и ошибки | При успехе управление в старый PHP код не возвращается. При отказе false/warning+errno. PHP8.5 запрещает NUL в аргументах и ключах/значениях окружения (ValueError). |
| Среда | PHP4.2+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Это не PHP exec, который получает вывод дочерней программы. Shell parsing здесь нет: пробел внутри аргумента остаётся частью аргумента, escapeshellarg добавил бы лишние кавычки. В tagged8.5 переданный третий аргумент [] означает пустую среду; если его вообще не передавать, используется execv с наследованием среды.
<?php
declare(strict_types=1);
pcntl_exec(PHP_BINARY, [__DIR__ . '/show-sku.php', 'A42']);
$error = pcntl_get_last_error();
fwrite(STDERR, 'Замена не выполнена: ' . pcntl_strerror($error) . "\n");
exit(1);
Ожидаемый результат
При успехе новый скрипт печатает SKU=A42; MODE=…; старый код после exec не исполняется.
Используем проверенный PHP_BINARY и свой файл. Если нужны stdout/stderr handles и возвращение в родителя, смотрите proc_open. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
pcntl_exec(PHP_BINARY, [__DIR__ . '/show-sku.php', 'A 42'], ['IMPORT_MODE' => 'preview']);
throw new RuntimeException('Не выполнен pcntl_exec: ' . pcntl_strerror(pcntl_get_last_error()));
Ожидаемый результат
SKU=A 42; MODE=preview
Артикул с пробелом передаётся одной строкой массива. Явная среда не включает PATH/HOME автоматически: добавляйте только нужные переменные. Новый PHP может использовать собственную ini конфигурацию. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_fork, pcntl_waitpid, pcntl_strerror.
pcntl_waitpidДождаться своего worker и понять, как он закончился, не оставляя zombie запись.
pcntl_waitpid(int $process_id, int &$status, int $flags = 0, array &$resource_usage = []): intДождаться своего worker и понять, как он закончился, не оставляя zombie запись.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_waitpid(int $process_id, int &$status, int $flags = 0, array &$resource_usage = []): int |
| Аргументы | PID>0 — конкретный ребёнок; -1 — любой;0 — дети своей PGID;<-1 — дети PGID=abs(PID). flags WNOHANG/WUNTRACED/WCONTINUED где поддержан. |
| Результат и ошибки | PID>0 — событие;0 — при WNOHANG пока нет события;-1 — отказ. resource_usage требует wait4; без него параметр не меняется, с ним при0/-1 пустой массив. |
| Среда | PHP4.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Ожидаются собственные дети, а не любой найденный PID. После сбора завершения второй wait этого ребёнка обычно даёт ECHILD. SIGCHLD callback и основной цикл не должны конкурировать за один status.
Аргумент &$status — выходной параметр: wait записывает в переменную упакованное состояние ОС. Сам return — PID/0/-1, не exit code. Читайте status только при положительном PID; код завершения извлекается wexitstatus после wifexited. EINTR означает прерывание сигналом: повторяйте ожидание с учётом своей политики остановки. ECHILD означает отсутствие подходящего ещё не собранного ребёнка, а не «он ещё работает».
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
exit(7);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
if (pcntl_wifexited($status)) echo "Код: ", pcntl_wexitstatus($status), "
";
Ожидаемый результат
Код: 7
Нормальное завершение означает exit/return, а не обязательно успех задачи: значение7 здесь описывает прикладную неудачу. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('fork не выполнен');
if ($pid === 0) exit(0);
$status = 0;
$done = pcntl_waitpid($pid, $status, WNOHANG);
if ($done === 0) {
echo "События пока нет\n";
do { $done = pcntl_waitpid($pid, $status); }
while ($done === -1 && pcntl_get_last_error() === PCNTL_EINTR);
}
if ($done !== $pid) throw new RuntimeException('Не получено завершение');
echo pcntl_wifexited($status) ? "Получено нормальное завершение\n" : "Получено другое завершение\n";
Ожидаемый результат
Может сначала быть События пока нет; затем Получено нормальное завершение.
Не делаем busy loop из WNOHANG. Ветку0 не декодируем: $status там не новое событие. В production event loop должен иметь ожидание/паузы и общий deadline. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_wait, pcntl_waitid, pcntl_wifexited, pcntl_wexitstatus.
pcntl_waitСобрать ближайшее завершение одного из своих детей, когда конкретный PID заранее не нужен.
pcntl_wait(int &$status, int $flags = 0, array &$resource_usage = []): intСобрать ближайшее завершение одного из своих детей, когда конкретный PID заранее не нужен.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_wait(int &$status, int $flags = 0, array &$resource_usage = []): int |
| Аргументы | status по ссылке; flags при наличии wait3, resource_usage также требует wait3. |
| Результат и ошибки | Положительный PID события;0 с WNOHANG без события;-1 отказ. Без wait3 flags игнорируются, resource_usage не меняется. |
| Среда | PHP5+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Базовый смысл — waitpid(-1). Если библиотека создаёт своих детей, глобальный wait может собрать чужого для данной подсистемы ребёнка; предпочтите явное владение PID. Не считать один SIGCHLD равным ровно одному завершению.
Аргумент &$status — выходной параметр: wait записывает в переменную упакованное состояние ОС. Сам return — PID/0/-1, не exit code. Читайте status только при положительном PID; код завершения извлекается wexitstatus после wifexited. EINTR означает прерывание сигналом: повторяйте ожидание с учётом своей политики остановки. ECHILD означает отсутствие подходящего ещё не собранного ребёнка, а не «он ещё работает».
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
exit(0);
}
$status = 0;
do {
$waited = pcntl_wait($status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
echo "Собран ребёнок PID=$waited
";
Ожидаемый результат
Строка с PID собственного ребёнка.
В этом отдельном примере создан только один ребёнок, поэтому результат однозначен. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$usage = [];
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
$sum = array_sum(range(1, 100));
exit($sum === 5050 ? 0 : 1);
}
$status = 0;
do {
$waited = pcntl_wait($status, 0, $usage);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
echo 'Завершение: ', pcntl_wifexited($status) ? pcntl_wexitstatus($status) : 'сигнал', "\n";
echo 'Счётчиков rusage: ', count($usage), "\n";
Ожидаемый результат
Завершение: 0 и количество предоставленных ОС счётчиков.
ru_utime.tv_sec/usec и ru_stime.tv_sec/usec дают CPU части; ru_maxrss имеет платформенные единицы, не автоматически байты. Неподдерживаемую статистику не выдаём за нулевой расход. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_waitpid, pcntl_wifexited, pcntl_wexitstatus.
pcntl_waitidПолучить структурированное событие ребёнка — завершение, остановку или продолжение — вместо упакованного status.
pcntl_waitid(int $idtype = P_ALL, ?int $id = null, array &$info = [], int $flags = WEXITED, array &$resource_usage = []): boolПолучить структурированное событие ребёнка — завершение, остановку или продолжение — вместо упакованного status.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_waitid(int $idtype = P_ALL, ?int $id = null, array &$info = [], int $flags = WEXITED, array &$resource_usage = []): bool |
| Аргументы | idtype выбирает вид ID, id — значение. WEXITED/WSTOPPED/WCONTINUED выбирают события; WNOHANG — без ожидания; WNOWAIT — оставить событие доступным следующему wait. |
| Результат и ошибки | true при событии И при WNOHANG без события; проверяйте info[pid]>0. false — errno. resource_usage добавлен8.5, поддержка wait6/raw Linux waitid зависит от ОС. |
| Среда | PHP8.4+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
info содержит signo/errno/code/status/pid/uid и при наличии utime/stime. info[status] — exit value или номер сигнала согласно code; это не packed status для wifexited. После WNOWAIT завершение нужно действительно собрать без этого флага.
| Выбор детей | Значение |
|---|---|
| P_ALL | Любой ребёнок; id не используется. |
| P_PID / P_PGID | Ребёнок по PID / дети process group. |
| P_PIDFD | Linux5.4+: настоящий PID file descriptor, не PHP resource ID и не обычный PID. |
| P_UID / P_GID / P_SID | Платформенные BSD selectors effective UID/GID/session. |
| P_JAILID | FreeBSD jail identifier. |
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('fork не выполнен');
if ($pid === 0) exit(7);
do { $ok = pcntl_waitid(P_PID, $pid, $info, WEXITED); }
while (!$ok && pcntl_get_last_error() === PCNTL_EINTR);
if (!$ok || ($info['pid'] ?? 0) !== $pid) throw new RuntimeException('Нет события ребёнка');
if ($info['code'] === CLD_EXITED) echo 'Код: ', $info['status'], "\n";
Ожидаемый результат
Код: 7
Здесь status7 уже расшифрован в siginfo. Передача его в wexitstatus была бы ошибкой. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('fork не выполнен');
if ($pid === 0) exit(0);
do { $ok = pcntl_waitid(P_PID, $pid, $peek, WEXITED | WNOWAIT); }
while (!$ok && pcntl_get_last_error() === PCNTL_EINTR);
if (!$ok) throw new RuntimeException('Не прочитано событие');
echo 'Сначала увидели PID: ', $peek['pid'], "\n";
do { $done = pcntl_waitpid($pid, $status); }
while ($done === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($done !== $pid) throw new RuntimeException('Не собран ребёнок');
echo "Теперь завершение собрано\n";
Ожидаемый результат
PID при первом чтении, затем Теперь завершение собрано.
WNOWAIT — не остановка ребёнка и не признак его работы: завершённый ребёнок остаётся waitable, пока второе ожидание не освободит запись. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_waitpid, pcntl_get_last_error.
pcntl_wifexitedОтделить обычный exit ребёнка от завершения сигналом, прежде чем извлекать прикладной код.
pcntl_wifexited(int $status): boolОтделить обычный exit ребёнка от завершения сигналом, прежде чем извлекать прикладной код.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_wifexited(int $status): bool |
| Аргументы | Packed status из успешного wait/waitpid. |
| Результат и ошибки | true при обычном завершении; false для других состояний. |
| Среда | PHP4.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
true не означает код0. Процесс с exit(7) тоже завершился обычным способом. Нельзя передавать сюда PID или raw exit code.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
exit(0);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
echo pcntl_wifexited($status) ? "Обычный exit
" : "Иное событие
";
Ожидаемый результат
Обычный exit
Сначала wait получил реальный status, потом предикат определил вид события. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
exit(7);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
if (pcntl_wifexited($status)) {
echo pcntl_wexitstatus($status) === 0 ? "Задача успешна\n" : "Задача сообщила ошибку\n";
}
Ожидаемый результат
Задача сообщила ошибку
Разделены два вопроса: как закончился процесс и что он сообщил кодом. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_wexitstatus, pcntl_wifsignaled, pcntl_waitpid.
pcntl_wexitstatusДостать маленький код результата job после обычного завершения ребёнка.
pcntl_wexitstatus(int $status): int|falseДостать маленький код результата job после обычного завершения ребёнка.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_wexitstatus(int $status): int|false |
| Аргументы | Status успешного wait/waitpid, для которого wifexitedtrue. |
| Результат и ошибки | Exit code; false если ОС не поддерживает извлечение. |
| Среда | PHP4.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Unix wait обычно передаёт младшие8бит exit code, то есть0…255. Не используйте его для количества строк, суммы заказа или JSON. При сигнале эта функция не даёт корректного объяснения.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
exit(7);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
if (!pcntl_wifexited($status)) throw new RuntimeException('Не обычный выход');
echo 'Exit code: ', pcntl_wexitstatus($status), "\n";
Ожидаемый результат
Exit code: 7
Код7 имеет значение только в договоре вашего job; ОС не знает, что именно он означает. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
exit(2);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
if (!pcntl_wifexited($status)) throw new RuntimeException('Сигнальное завершение');
$code = pcntl_wexitstatus($status);
echo match ($code) {
0 => "Импорт выполнен\n",
2 => "Входные данные не прошли проверку\n",
default => "Неожиданный исход: проверьте журнал\n",
};
Ожидаемый результат
Входные данные не прошли проверку
Не повторяем job автоматически при любой ошибке. Код и журнал вместе позволяют отделить плохие входные данные от временной недоступности. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_wifexited, pcntl_wtermsig.
pcntl_wifsignaledУвидеть, что worker был завершён сигналом, а не вернул код ошибки через exit.
pcntl_wifsignaled(int $status): boolУвидеть, что worker был завершён сигналом, а не вернул код ошибки через exit.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_wifsignaled(int $status): bool |
| Аргументы | Packed status после wait. |
| Результат и ошибки | true для сигнального завершения; false для ordinary exit/stop/continue. |
| Среда | PHP4.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Наличие любого сигнала в жизни процесса не означает true: обработанный SIGUSR1, после которого worker сделал exit0, даст обычное завершение.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
pcntl_signal(SIGTERM, SIG_DFL);
posix_kill(posix_getpid(), SIGTERM);
exit(3);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
echo pcntl_wifsignaled($status) ? "Завершён сигналом
" : "Другой исход
";
Ожидаемый результат
Завершён сигналом
Изолированный ребёнок заранее ставит default SIGTERM; не посылаем сигнал найденному чужому PID. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
exit(7);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
echo pcntl_wifsignaled($status) ? "Сигнал
" : "Это не сигнальное завершение
";
Ожидаемый результат
Это не сигнальное завершение
Даже ненулевой код7 не является номером сигнала. Для него применяют wexitstatus. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_wtermsig, pcntl_wifexited.
pcntl_wtermsigУказать в диагностике, какой сигнал прекратил worker.
pcntl_wtermsig(int $status): int|falseУказать в диагностике, какой сигнал прекратил worker.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_wtermsig(int $status): int|false |
| Аргументы | Status, для которого wifsignaledtrue. |
| Результат и ошибки | Номер terminating signal либо false при отсутствии поддержки ОС. |
| Среда | PHP4.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Сравнивайте с именованным SIGTERM/SIGKILL, не зашитым15/9. Номер не доказывает, кто послал сигнал; OOM и supervisor требуют отдельного журнала ОС.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
pcntl_signal(SIGTERM, SIG_DFL);
posix_kill(posix_getpid(), SIGTERM);
exit(3);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
if (!pcntl_wifsignaled($status)) throw new RuntimeException('Ожидался сигнал');
echo pcntl_wtermsig($status) === SIGTERM ? "Получено завершение SIGTERM\n" : "Другой сигнал\n";
Ожидаемый результат
Получено завершение SIGTERM
Проверка вида события предшествует извлечению номера. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
exit(0);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
if (pcntl_wifsignaled($status)) {
echo 'Worker interrupted by signal ', pcntl_wtermsig($status), "\n";
} elseif (pcntl_wifexited($status)) {
echo 'Worker exit ', pcntl_wexitstatus($status), "\n";
}
Ожидаемый результат
Worker exit 0
Даже в универсальном обработчике wtermsig вызывается только в своей ветке. Нельзя назвать0 «сигналом успешности». Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_wifsignaled, pcntl_wexitstatus.
pcntl_wifstoppedОтличить временно остановленного ребёнка от завершённого: ему ещё может понадобиться SIGCONT.
pcntl_wifstopped(int $status): boolОтличить временно остановленного ребёнка от завершённого: ему ещё может понадобиться SIGCONT.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_wifstopped(int $status): bool |
| Аргументы | Status события waitpid с WUNTRACED. |
| Результат и ошибки | true при остановке сигналом; false для других событий. |
| Среда | PHP4.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Stop не освобождает процесс и не завершает job. Не удаляйте PID из списка живых детей только потому, что wait вернул его. SIGSTOP нельзя перехватить/игнорировать.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
if (!posix_kill(posix_getpid(), SIGSTOP)) exit(2);
exit(0);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status, WUNTRACED);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
try {
echo pcntl_wifstopped($status) ? "Ребёнок остановлен, но жив
" : "Другое событие
";
} finally {
if (pcntl_wifstopped($status)) {
posix_kill($pid, SIGCONT);
do { $end = pcntl_waitpid($pid, $final); }
while ($end === -1 && pcntl_get_last_error() === PCNTL_EINTR);
}
}
Ожидаемый результат
Ребёнок остановлен, но жив
finally отправляет SIGCONT только своему ребёнку и собирает его последующий exit. Родитель не оставляет остановленную копию. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
exit(0);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
echo pcntl_wifstopped($status) ? "Остановка
" : "Это не остановка
";
Ожидаемый результат
Это не остановка
Для exit значениеtrue даст wifexited, а не wifstopped. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_wstopsig, pcntl_wifcontinued, pcntl_waitpid.
pcntl_wstopsigПоказать причину остановки worker, чтобы отличить job control от завершения.
pcntl_wstopsig(int $status): int|falseПоказать причину остановки worker, чтобы отличить job control от завершения.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_wstopsig(int $status): int|false |
| Аргументы | Status, для которого wifstoppedtrue. |
| Результат и ошибки | Номер stop signal либо false при отсутствии поддержки. |
| Среда | PHP4.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Не применять к exit status. SIGTSTP обычно связан с терминальным Ctrl+Z, SIGSTOP — безусловная остановка; происхождение сигнала из одного номера не известно.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
if (!posix_kill(posix_getpid(), SIGSTOP)) exit(2);
exit(0);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status, WUNTRACED);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
try {
if (!pcntl_wifstopped($status)) throw new RuntimeException('Не stop событие');
echo pcntl_wstopsig($status) === SIGSTOP ? "Остановка SIGSTOP\n" : "Другой stop signal\n";
} finally {
if (pcntl_wifstopped($status)) {
posix_kill($pid, SIGCONT);
do { $end = pcntl_waitpid($pid, $final); }
while ($end === -1 && pcntl_get_last_error() === PCNTL_EINTR);
}
}
Ожидаемый результат
Остановка SIGSTOP
После диагностики finally возобновляет и собирает ребёнка. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
exit(0);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
if (pcntl_wifstopped($status)) echo 'Stop signal ', pcntl_wstopsig($status), "\n";
elseif (pcntl_wifexited($status)) echo "Worker уже завершён\n";
Ожидаемый результат
Worker уже завершён
Код, который умеет несколько событий, выбирает decoder по предикату; не читает случайные биты. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_wifstopped, pcntl_wtermsig.
pcntl_wifcontinuedУзнать событие возобновления ранее остановленного worker.
pcntl_wifcontinued(int $status): boolУзнать событие возобновления ранее остановленного worker.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_wifcontinued(int $status): bool |
| Аргументы | Status waitpid с WCONTINUED на поддерживающей ОС. |
| Результат и ошибки | true при продолжении через SIGCONT; false при другом событии. |
| Среда | PHP5.3+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Функция не возобновляет процесс сама. Continue не означает, что задание закончено: окончательный exit ещё нужно собрать. Поддержка WCONTINUED и функции зависит от ОС.
<?php
declare(strict_types=1);
if (!defined('WCONTINUED')) throw new RuntimeException('ОС не поддерживает continue events');
pcntl_sigprocmask(SIG_BLOCK, [SIGUSR1], $oldMask);
$pid = pcntl_fork();
if ($pid === -1) { pcntl_sigprocmask(SIG_SETMASK, $oldMask); throw new RuntimeException('fork failed'); }
if ($pid === 0) {
posix_kill(posix_getpid(), SIGSTOP);
pcntl_sigtimedwait([SIGUSR1], $notice, 2);
exit(0);
}
$reaped = false;
try {
do { $stopped = pcntl_waitpid($pid, $status, WUNTRACED); }
while ($stopped === -1 && pcntl_get_last_error() === PCNTL_EINTR);
$reaped = $stopped === $pid && (pcntl_wifexited($status) || pcntl_wifsignaled($status));
if ($stopped !== $pid || !pcntl_wifstopped($status)) throw new RuntimeException('Нет stop события');
if (!posix_kill($pid, SIGCONT)) throw new RuntimeException('Не продолжен ребёнок');
do { $event = pcntl_waitpid($pid, $status, WCONTINUED); }
while ($event === -1 && pcntl_get_last_error() === PCNTL_EINTR);
$reaped = $event === $pid && (pcntl_wifexited($status) || pcntl_wifsignaled($status));
if ($event === $pid) echo pcntl_wifcontinued($status) ? "Ребёнок продолжен\n" : "Уже другой исход\n";
} finally {
if (!$reaped) {
posix_kill($pid, SIGCONT);
posix_kill($pid, SIGUSR1);
do { $end = pcntl_waitpid($pid, $final); }
while ($end === -1 && pcntl_get_last_error() === PCNTL_EINTR);
}
pcntl_sigprocmask(SIG_SETMASK, $oldMask);
}
Ожидаемый результат
Обычно Ребёнок продолжен; при иной очередности события явно выводится другой исход.
Блокированный SIGUSR1 удерживает ребёнка до команды родителя максимум2секунды после возобновления. Это более глубокий пример жизненного цикла, после уроков mask/sigtimedwait. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
exit(0);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
echo pcntl_wifcontinued($status) ? "Continue
" : "Это не событие продолжения
";
Ожидаемый результат
Это не событие продолжения
Не пытаемся определить текущее состояние по заранее придуманному числу; всегда используем реальный status от ОС. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_wifstopped, pcntl_waitpid, pcntl_sigtimedwait.
pcntl_signalНаучить worker реагировать на просьбу завершиться или обновить настройки, не теряя текущую короткую операцию.
pcntl_signal(int $signal, callable|int $handler, bool $restart_syscalls = true): boolНаучить worker реагировать на просьбу завершиться или обновить настройки, не теряя текущую короткую операцию.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_signal(int $signal, callable|int $handler, bool $restart_syscalls = true): bool |
| Аргументы | Номер сигнала; callable handler(int $signo, mixed $siginfo): void либо SIG_DFL/SIG_IGN; режим перезапуска поддерживаемых syscall. |
| Результат и ошибки | true/false; неверный диапазон signal и недопустимый integer handler дают ValueError, не-callable — TypeError. SIGKILL/SIGSTOP нельзя перехватить. |
| Среда | PHP4.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Новый handler заменяет старый, не добавляется в список. Вызов не включает dispatch сам по себе. Callback получает array|null siginfo по ОС/сигналу. Ссылка на object method удерживает объект. Handler лучше ставит флаг, а запись/закрытие ресурсов выполняет основной цикл.
<?php
declare(strict_types=1);
pcntl_async_signals(true);
$reload = false;
if (!pcntl_signal(SIGUSR1, function (int $signal, mixed $info): void { echo "Получен SIGUSR1\n"; })) {
throw new RuntimeException('Handler не установлен');
}
if (!posix_kill(posix_getpid(), SIGUSR1)) throw new RuntimeException('Сигнал не отправлен');
pcntl_signal_dispatch();
Ожидаемый результат
Получен SIGUSR1
Только свой процесс и известный установленный обработчик. Основная программа не получает из сигнала строку JSON: это уведомление. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
pcntl_async_signals(true);
$stop = false;
if (!pcntl_signal(SIGTERM, function () use (&$stop): void { $stop = true; })) throw new RuntimeException('Нет handler');
$jobs = ['A42', 'B17'];
foreach ($jobs as $sku) {
if ($stop) break;
echo "Короткая операция для $sku завершена\n";
if ($sku === 'A42') posix_kill(posix_getpid(), SIGTERM);
pcntl_signal_dispatch();
}
echo "Ресурсы можно закрыть в обычном потоке выполнения\n";
Ожидаемый результат
Короткая операция для A42 завершена
Ресурсы можно закрыть в обычном потоке выполнения
В демонстрации после первого задания посылаем сигнал себе. В production текущая операция тоже должна иметь timeout; один флаг не прерывает зависший драйвер. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
restart_syscalls управляет SA_RESTART там, где ОС применяет его: это не гарантия повторения всех I/O. Для SIGALRM пропущенный третий аргумент исторически означает без перезапуска, несмотря на отражённый defaulttrue; явно переданный true учитывается с PHP7.4. Sleep может завершиться раньше. Асинхронный PHP handler всё равно исполняется в точках, где движок получает управление, а не параллельно в новом PHP потоке.
Связь: pcntl_async_signals, pcntl_signal_dispatch, pcntl_signal_get_handler, pcntl_alarm.
pcntl_signal_get_handlerСохранить прежнюю реакцию на сигнал перед временной настройкой и затем восстановить её.
pcntl_signal_get_handler(int $signal): callable|intСохранить прежнюю реакцию на сигнал перед временной настройкой и затем восстановить её.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_signal_get_handler(int $signal): callable|int |
| Аргументы | Номер сигнала. |
| Результат и ошибки | Callable либо SIG_DFL/SIG_IGN; неверный signal — ValueError. |
| Среда | PHP7.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Возвращённый callable — описание обработчика, не его результат. Чтение не посылает сигнал. Общий процессный handler может принадлежать другой части программы; сначала согласуйте владение.
<?php
declare(strict_types=1);
$handler = pcntl_signal_get_handler(SIGUSR1);
echo match (true) {
$handler === SIG_DFL => "Стандартная реакция ОС\n",
$handler === SIG_IGN => "Сигнал игнорируется\n",
default => "Установлен PHP callable\n",
};
Ожидаемый результат
Одна из трёх подписанных реакций.
Не сравниваем с магическими0/1: используем константы ОС. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$before = pcntl_signal_get_handler(SIGUSR1);
try {
if (!pcntl_signal(SIGUSR1, function () {})) throw new RuntimeException('Handler не установлен');
echo is_callable(pcntl_signal_get_handler(SIGUSR1)) ? "Временный callback установлен\n" : "Не callback\n";
} finally {
if (!pcntl_signal(SIGUSR1, $before)) throw new RuntimeException('Handler не восстановлен');
}
Ожидаемый результат
Временный callback установлен
В этом примере сигналов нет, поэтому нечему остаться pending на границе восстановления. Для реального ожидания также согласуют mask и pending events. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_signal, pcntl_sigprocmask.
pcntl_async_signalsВключить вызов подготовленных обработчиков без постоянного ручного dispatch в цикле.
pcntl_async_signals(?bool $enable = null): boolВключить вызов подготовленных обработчиков без постоянного ручного dispatch в цикле.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_async_signals(?bool $enable = null): bool |
| Аргументы | null/без аргумента — прочитать; true/false — установить режим. |
| Результат и ошибки | При чтении — текущий bool; при установке — предыдущий bool, а не признак успеха. |
| Среда | PHP7.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
false после pcntl_async_signals(true) часто означает, что раньше режим был выключен. Это не отдельный поток и не отмена timeout для блокирующих расширений. С PHP8 аргумент nullable.
<?php
declare(strict_types=1);
$old = pcntl_async_signals(true);
echo 'Было: ', $old ? 'включено' : 'выключено', "\n";
echo 'Стало: ', pcntl_async_signals() ? 'включено' : 'выключено', "\n";
pcntl_async_signals($old);
Ожидаемый результат
Первое состояние зависит от запуска; второе Стало: включено.
Сохранённый bool позволяет вернуть настройку. Не пишем if(!pcntl_async_signals(true)) как проверку ошибки. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$seen = false;
pcntl_signal(SIGUSR1, function () use (&$seen): void { $seen = true; });
pcntl_async_signals(true);
if (!posix_kill(posix_getpid(), SIGUSR1)) throw new RuntimeException('Сигнал не отправлен');
echo $seen ? "Флаг обработан асинхронно\n" : "Обработка ещё ожидается\n";
Ожидаемый результат
После доставки Флаг обработан асинхронно; код явно допускает ещё ожидающую обработку.
Включение dispatch не делает процесс здоровым или завершённым; программа всё равно выбирает, что означает этот флаг. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_signal, pcntl_signal_dispatch.
pcntl_signal_dispatchВыбрать явную точку, где программа обрабатывает накопленные сигналы.
pcntl_signal_dispatch(): boolВыбрать явную точку, где программа обрабатывает накопленные сигналы.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_signal_dispatch(): bool |
| Аргументы | Нет. |
| Результат и ошибки | true/false по API; число обработанных сигналов не возвращается. |
| Среда | PHP5.3+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Dispatch не создаёт сигнал и не блокируется до события. Если основной цикл застрял внутри вызова, до следующего ручного dispatch он не дойдёт. В обычном worker удобнее async_signals; ручной режим нужен осознанному управлению точками обработки.
<?php
declare(strict_types=1);
pcntl_async_signals(false);
$seen = false;
pcntl_signal(SIGUSR1, function () use (&$seen): void { $seen = true; });
if (!posix_kill(posix_getpid(), SIGUSR1)) throw new RuntimeException('Сигнал не отправлен');
echo $seen ? "Уже обработан\n" : "До dispatch флаг ещё false\n";
pcntl_signal_dispatch();
echo $seen ? "После dispatch флаг true\n" : "Событие ещё не получено\n";
Ожидаемый результат
До dispatch флаг ещё false
После dispatch флаг true
Callback меняет ту же переменную благодаря use(&$seen). Это не означает общую память двух процессов. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
pcntl_async_signals(false);
$handled = 0;
pcntl_signal(SIGUSR1, function () use (&$handled): void { $handled++; });
$ok = pcntl_signal_dispatch();
echo 'Вызов: ', $ok ? 'успешен' : 'отказ', '; обработано: ', $handled, "\n";
Ожидаемый результат
Вызов: успешен; обработано: 0 при отсутствии внешнего сигнала.
Свой счётчик показывает различие между успешным dispatch и фактом события. Цикл из таких вызовов без ожидания станет busy loop. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_async_signals, pcntl_signal, pcntl_sigwaitinfo.
pcntl_sigprocmaskВременно отложить доставку выбранных сигналов либо подготовить их синхронное ожидание.
pcntl_sigprocmask(int $mode, array $signals, array &$old_signals = null): boolВременно отложить доставку выбранных сигналов либо подготовить их синхронное ожидание.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_sigprocmask(int $mode, array $signals, array &$old_signals = null): bool |
| Аргументы | SIG_BLOCK добавляет к mask, SIG_UNBLOCK удаляет, SIG_SETMASK заменяет целиком. signals — список integer сигналов, old_signals получает прошлый список. |
| Результат и ошибки | bool; с8.4 неверный режим/номер/пустой список для BLOCK/UNBLOCK — ValueError, не-int элемент — TypeError. Пустой SETMASK допустим. |
| Среда | PHP5.3+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Блокировать не значит игнорировать: сигнал может стать pending и доставиться позже. SIGKILL/SIGSTOP не блокируются. Обычные сигналы могут объединяться, это не счётчик каждого события. Сохраняйте и восстанавливайте всю старую маску, а не blindly unblock всего.
<?php
declare(strict_types=1);
if (!pcntl_sigprocmask(SIG_BLOCK, [SIGUSR1], $before)) throw new RuntimeException('Не изменена маска');
try {
echo 'Ранее блокировалось сигналов: ', count($before), "\n";
} finally {
if (!pcntl_sigprocmask(SIG_SETMASK, $before)) throw new RuntimeException('Не восстановлена маска');
}
Ожидаемый результат
Количество сигналов в прошлой маске.
Мы не предполагаем, что при запуске все сигналы были разблокированы. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$oldHandler = pcntl_signal_get_handler(SIGUSR1);
pcntl_signal(SIGUSR1, function () {});
if (!pcntl_sigprocmask(SIG_BLOCK, [SIGUSR1], $oldMask)) throw new RuntimeException('Маска не установлена');
try {
if (!posix_kill(posix_getpid(), SIGUSR1)) throw new RuntimeException('Сигнал не отправлен');
$signal = pcntl_sigtimedwait([SIGUSR1], $info, 1);
echo $signal === SIGUSR1 ? "Pending уведомление принято\n" : "Уведомление не получено\n";
} finally {
pcntl_sigprocmask(SIG_SETMASK, $oldMask);
pcntl_signal_dispatch();
pcntl_signal(SIGUSR1, $oldHandler);
}
Ожидаемый результат
Pending уведомление принято при успешной собственной отправке.
Mask установлен до posix_kill, поэтому стандартная реакция SIGUSR1 не завершает процесс. Синхронный wait потребляет событие; finally возвращает состояние. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_sigtimedwait, pcntl_sigwaitinfo, pcntl_signal.
pcntl_sigwaitinfoДождаться конкретного уведомления в последовательном CLI протоколе, получив номер сигнала и его описание.
pcntl_sigwaitinfo(array $signals, array &$info = []): int|falseДождаться конкретного уведомления в последовательном CLI протоколе, получив номер сигнала и его описание.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_sigwaitinfo(array $signals, array &$info = []): int|false |
| Аргументы | Непустой список сигналов, заблокированных до ожидания; info — выходной массив. |
| Результат и ошибки | Номер принятого сигнала либо false/предупреждение. С8.4 пустой/неверный список даёт ValueError, не-int элемент — TypeError. |
| Среда | PHP5.3+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Ожидание без таймаута может быть бесконечным. Для сервиса обычно нужен sigtimedwait с deadline. Синхронный wait потребляет pending событие без вызова установленного PHP callback для него. Не смешивайте двух потребителей одного сигнала.
В info есть signo, errno, code. В зависимости от сигнала/ОС/ветки wrapper возможны status, pid, uid, utime, stime для SIGCHLD; addr для некоторых аппаратных ошибок; band/fd для SIGPOLL, другие поля платформенные. Не обращайтесь к необязательному ключу без проверки. code описывает источник/событие через SI_*/CLD_* и не является exit code для любого сигнала. PHP8.4 унифицировал отказ в false вместо прежнего-1 в части случаев.
<?php
declare(strict_types=1);
$oldHandler = pcntl_signal_get_handler(SIGUSR1);
pcntl_signal(SIGUSR1, function () {});
if (!pcntl_sigprocmask(SIG_BLOCK, [SIGUSR1], $oldMask)) throw new RuntimeException('Маска не установлена');
try {
if (!posix_kill(posix_getpid(), SIGUSR1)) throw new RuntimeException('Сигнал не отправлен');
$number = pcntl_sigwaitinfo([SIGUSR1], $info);
if ($number === false) throw new RuntimeException('Не принято уведомление');
echo $number === SIGUSR1 ? "Принят SIGUSR1\n" : "Другой сигнал\n";
} finally {
pcntl_sigprocmask(SIG_SETMASK, $oldMask);
pcntl_signal_dispatch();
pcntl_signal(SIGUSR1, $oldHandler);
}
Ожидаемый результат
Принят SIGUSR1
Сначала mask, затем проверенная отправка, затем wait: так пример не ждёт неизвестного внешнего отправителя. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$oldHandler = pcntl_signal_get_handler(SIGUSR1);
pcntl_signal(SIGUSR1, function () {});
if (!pcntl_sigprocmask(SIG_BLOCK, [SIGUSR1], $oldMask)) throw new RuntimeException('Маска не установлена');
try {
if (!posix_kill(posix_getpid(), SIGUSR1)) throw new RuntimeException('Сигнал не отправлен');
$number = pcntl_sigwaitinfo([SIGUSR1], $info);
if ($number === false) throw new RuntimeException('Ошибка ожидания');
echo 'signo=', $info['signo'], '; code=', $info['code'], "\n";
} finally {
pcntl_sigprocmask(SIG_SETMASK, $oldMask);
pcntl_signal_dispatch();
pcntl_signal(SIGUSR1, $oldHandler);
}
Ожидаемый результат
Номер SIGUSR1 и системный code.
Не обещаем pid/uid для всякого SIGUSR1: набор полей задаёт PHP wrapper и ОС. Для имени типа сравниваем с константами, а не фиксированным числом. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_sigtimedwait, pcntl_sigprocmask.
pcntl_sigtimedwaitЖдать уведомление ограниченное время, чтобы worker мог проверить остановку или deadline.
pcntl_sigtimedwait(array $signals, array &$info = [], int $seconds = 0, int $nanoseconds = 0): int|falseЖдать уведомление ограниченное время, чтобы worker мог проверить остановку или deadline.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_sigtimedwait(array $signals, array &$info = [], int $seconds = 0, int $nanoseconds = 0): int|false |
| Аргументы | Список сигналов; seconds>=0, nanoseconds0…999999999; с8.4 хотя бы одно время должно быть>0, хотя сигнатура показывает нули по умолчанию. |
| Результат и ошибки | Номер сигнала либо false при timeout/ошибке. PHP8.4 неверное время/список — ValueError/TypeError. |
| Среда | PHP5.3+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
В PHP8.5.11 timeout EAGAIN возвращает false БЕЗ обновления PCNTL last_error. Поэтому старый EINTR/EINVAL нельзя приписывать свежему timeout. Другие ошибки дают warning и сохраняют код. Не классифицируйте false как гарантированную ошибку либо гарантированный timeout по stale errno.
В info есть signo, errno, code. В зависимости от сигнала/ОС/ветки wrapper возможны status, pid, uid, utime, stime для SIGCHLD; addr для некоторых аппаратных ошибок; band/fd для SIGPOLL, другие поля платформенные. Не обращайтесь к необязательному ключу без проверки. code описывает источник/событие через SI_*/CLD_* и не является exit code для любого сигнала. PHP8.4 унифицировал отказ в false вместо прежнего-1 в части случаев.
<?php
declare(strict_types=1);
$oldHandler = pcntl_signal_get_handler(SIGUSR1);
pcntl_signal(SIGUSR1, function () {});
if (!pcntl_sigprocmask(SIG_BLOCK, [SIGUSR1], $oldMask)) throw new RuntimeException('Маска не установлена');
try {
if (!posix_kill(posix_getpid(), SIGUSR1)) throw new RuntimeException('Сигнал не отправлен');
$number = pcntl_sigtimedwait([SIGUSR1], $info, 1, 0);
echo $number === SIGUSR1 ? "Уведомление принято\n" : "Нет подтверждённого уведомления\n";
} finally {
pcntl_sigprocmask(SIG_SETMASK, $oldMask);
pcntl_signal_dispatch();
pcntl_signal(SIGUSR1, $oldHandler);
}
Ожидаемый результат
Уведомление принято
Верхняя граница относится к этому ожиданию. Серия вызовов по1секунде без общего deadline может продолжаться бесконечно. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$oldHandler = pcntl_signal_get_handler(SIGUSR1);
pcntl_signal(SIGUSR1, function () {});
if (!pcntl_sigprocmask(SIG_BLOCK, [SIGUSR1], $oldMask)) throw new RuntimeException('Маска не установлена');
try {
$info = [];
$number = pcntl_sigtimedwait([SIGUSR1], $info, 0, 10_000_000);
echo $number === false ? "За интервал сигнал не получен или ожидание не выполнено\n" : "Получен сигнал\n";
} finally {
pcntl_sigprocmask(SIG_SETMASK, $oldMask);
pcntl_signal_dispatch();
pcntl_signal(SIGUSR1, $oldHandler);
}
Ожидаемый результат
Без внешнего SIGUSR1 — строка о неполученном сигнале.
Интервал10миллисекунд выражен в наносекундах. Старый массив info заранее очищен; после false его нельзя выдавать за данные нового события. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_sigwaitinfo, pcntl_sigprocmask, pcntl_get_last_error.
pcntl_alarmПоставить процессу однократное уведомление SIGALRM, например как вспомогательный сигнал остановки короткого job.
pcntl_alarm(int $seconds): intПоставить процессу однократное уведомление SIGALRM, например как вспомогательный сигнал остановки короткого job.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_alarm(int $seconds): int |
| Аргументы | Неотрицательное число секунд;0 отменяет предыдущий alarm и не создаёт новый. Ограничьте вход сами, не полагайтесь на преобразование отрицательного int в системное unsigned. |
| Результат и ошибки | Количество оставшихся секунд предыдущего alarm, либо0 если его не было. Это не ID таймера. |
| Среда | PHP4.3+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
В процессе один alarm: новый вызов заменяет старый. Без handler стандартный SIGALRM может завершить процесс. Timer не гарантирует немедленного выхода из любого библиотечного вызова. Не используйте его вместо HTTP connect/read timeout; после операции отменяйте, чтобы поздний сигнал не попал в следующий job.
<?php
declare(strict_types=1);
pcntl_async_signals(true);
pcntl_signal(SIGALRM, function (): void { echo "Срок уведомления наступил\n"; });
pcntl_alarm(2);
$remaining = pcntl_alarm(0);
echo 'Отменён alarm, оставалось секунд: ', $remaining, "\n";
Ожидаемый результат
Обычно оставалось2секунды; точное число зависит от времени исполнения.
0 отменяет уведомление. Этот пример не ждёт2секунды и не назначает таймер другому процессу. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
pcntl_async_signals(true);
$expired = false;
pcntl_signal(SIGALRM, function () use (&$expired): void { $expired = true; });
pcntl_alarm(1);
try {
foreach (['A42', 'B17', 'C09'] as $sku) {
if ($expired) break;
echo "Обработан $sku\n";
}
echo $expired ? "Сработал alarm\n" : "Короткая работа закончена до alarm\n";
} finally { pcntl_alarm(0); }
Ожидаемый результат
Обычно три строки товаров и Короткая работа закончена до alarm.
Флаг проверяется между короткими действиями. Это иллюстрация кооперативной остановки, не жёсткая гарантия времени выполнения внешнего вызова. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_signal, pcntl_async_signals, pcntl_sigtimedwait.
pcntl_getpriorityУзнать nice value импортёра, когда он конкурирует с другими задачами за процессор.
pcntl_getpriority(?int $process_id = null, int $mode = PRIO_PROCESS): int|falseУзнать nice value импортёра, когда он конкурирует с другими задачами за процессор.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_getpriority(?int $process_id = null, int $mode = PRIO_PROCESS): int|false |
| Аргументы | null/0 — текущая цель согласно mode: процесс, process group или пользователь. PRIO_PROCESS/PRIO_PGRP/PRIO_USER выбирают смысл ID. |
| Результат и ошибки | Integer nice value, в том числе допустимый -1; false/warning при отказе. Неверный mode — ValueError. |
| Среда | PHP4.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Меньшее nice обычно означает больший приоритет. На Linux диапазон -20…19; это не процент CPU и не задержка в секундах. Для группы/пользователя ОС возвращает наиболее высокий приоритет выбранной совокупности. Значение по умолчанию относится к своему процессу.
<?php
declare(strict_types=1);
$nice = pcntl_getpriority();
if ($nice === false) throw new RuntimeException(pcntl_strerror(pcntl_get_last_error()));
echo "Текущий nice: $nice\n";
Ожидаемый результат
Число nice текущего процесса по настройке ОС.
Сравнение ===false сохраняет корректное значение -1. Не меняем параметры соседних процессов. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$nice = pcntl_getpriority(null, PRIO_PROCESS);
if ($nice === false) throw new RuntimeException('Не прочитан nice');
echo $nice >= 10 ? "Уже уступаем обычной работе\n" : "Можно отдельно согласовать фоновый режим\n";
Ожидаемый результат
Одна из двух диагностических строк согласно nice.
Порог10 — выбранная политика примера, не универсальная граница ОС. Реальная scheduling policy/cgroup quota также влияет на работу. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_setpriority, pcntl_getcpuaffinity.
pcntl_setpriorityСделать отдельный пакетный импорт менее приоритетным, чтобы он меньше мешал интерактивной работе.
pcntl_setpriority(int $priority, ?int $process_id = null, int $mode = PRIO_PROCESS): boolСделать отдельный пакетный импорт менее приоритетным, чтобы он меньше мешал интерактивной работе.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_setpriority(int $priority, ?int $process_id = null, int $mode = PRIO_PROCESS): bool |
| Аргументы | priority — nice value; process_id/mode выбирают цель как у getpriority. На Apple PRIO_DARWIN_THREAD выбирает текущий поток с ID0, а PRIO_DARWIN_BG передают как priority. |
| Результат и ошибки | true/false+warning/errno; неверный mode или неподходящий Apple ID/value — ValueError. |
| Среда | PHP4.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Повышение nice обычно разрешено своему непривилегированному процессу, обратное снижение может потребовать прав. Поэтому не обещаем восстановить старый приоритет finally: изменение выполняет отдельный одноразовый worker. Не назначайте modePRIO_USER для эксперимента — это затрагивает много процессов.
<?php
declare(strict_types=1);
$before = pcntl_getpriority();
if ($before === false) throw new RuntimeException('Не прочитан nice');
$target = max($before, 10);
if (!pcntl_setpriority($target)) throw new RuntimeException(pcntl_strerror(pcntl_get_last_error()));
echo 'Фоновый worker, nice=', pcntl_getpriority(), "\n";
echo "Здесь выполняется ограниченная пакетная работа\n";
Ожидаемый результат
Nice не меньше прежнего/10 и строка о пакетной работе.
Подходит для Linux nice диапазона. Процесс завершается после задачи; не пытаемся повышать права для отката. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$nice = pcntl_getpriority(null, PRIO_PROCESS);
if ($nice === false) throw new RuntimeException('Не прочитан nice');
if (!pcntl_setpriority($nice, null, PRIO_PROCESS)) throw new RuntimeException('Установка запрещена политикой ОС');
echo "Параметр своего процесса сохранён\n";
Ожидаемый результат
Параметр своего процесса сохранён при разрешённом вызове.
Пример показывает правильное расположение аргументов и обработку отказа без изменения чужих целей. Nice не заменяет rate limit API или ограничение числа worker. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_getpriority, pcntl_setqos_class.
pcntl_getcpuaffinityУзнать, на каких логических CPU вообще разрешено запускать worker внутри контейнера или сервера.
pcntl_getcpuaffinity(?int $process_id = null): array|falseУзнать, на каких логических CPU вообще разрешено запускать worker внутри контейнера или сервера.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_getcpuaffinity(?int $process_id = null): array|false |
| Аргументы | null/0 — текущий процесс, положительный ID — выбранный процесс с доступом по правилам ОС. |
| Результат и ошибки | Список integer CPU IDs либо false при системном отказе. Неверный ID/невозможная mask — ValueError; права/OS ошибки сопровождаются warning. |
| Среда | PHP8.4+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
В Linux affinity относится к task/thread; для обычного PHP CLI рассматриваем основной поток. [2,4] означает разрешённые CPU2 и4, а не булевы флаги. Количество элементов — число разрешённых CPU, не обязательно полное число процессоров машины.
<?php
declare(strict_types=1);
$cpus = pcntl_getcpuaffinity();
if ($cpus === false) throw new RuntimeException(pcntl_strerror(pcntl_get_last_error()));
echo 'Разрешены: ', implode(', ', $cpus), "\n";
Ожидаемый результат
Список реально разрешённых логических CPU.
Контейнер может дать непоследовательные ID, поэтому не строим range(0,count-1). Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$cpus = pcntl_getcpuaffinity();
if ($cpus === false || $cpus === []) throw new RuntimeException('Нет доступной CPU mask');
$jobs = 3;
$workers = min($jobs, count($cpus));
echo "Верхняя оценка worker: $workers\n";
Ожидаемый результат
Число1…3 по количеству доступных CPU.
Это лишь отправная точка: CPU quota может быть меньше mask, а память/БД ограничивают параллелизм отдельно. Для ожидания сети CPU count не универсальная формула. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_setcpuaffinity, pcntl_getcpu, pcntl_fork.
pcntl_setcpuaffinityВ контролируемом измерении оставить worker на выбранном разрешённом логическом CPU.
pcntl_setcpuaffinity(?int $process_id = null, array $cpu_ids = []): boolВ контролируемом измерении оставить worker на выбранном разрешённом логическом CPU.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_setcpuaffinity(?int $process_id = null, array $cpu_ids = []): bool |
| Аргументы | Своя цель null/0; непустой список CPU IDs. Tagged8.5 допускает integer и строковые integer IDs, но используйте int из getcpuaffinity. |
| Результат и ошибки | true/false; пустой список, отрицательные/недопустимые ID и невозможная маска — ValueError; неподходящие типы элементов — TypeError; OS/permission ошибки могут дать warningfalse. |
| Среда | PHP8.4+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Не считайте CPU0 доступным и не начинайте с произвольного списка. Affinity ограничивает размещение, не резервирует CPU эксклюзивно и может сделать работу медленнее. Сохранение/восстановление маски тоже может отказать, если политика или доступные CPU изменились.
<?php
declare(strict_types=1);
$before = pcntl_getcpuaffinity();
if ($before === false || $before === []) throw new RuntimeException('Нет исходной mask');
try {
if (!pcntl_setcpuaffinity(null, [$before[0]])) throw new RuntimeException('Не установлена mask');
echo 'На время разрешён CPU ', $before[0], "\n";
} finally {
if (!pcntl_setcpuaffinity(null, $before)) throw new RuntimeException('Не восстановлена исходная mask');
}
Ожидаемый результат
Выбранный разрешённый CPU; затем восстановлена исходная mask.
Это настройка отдельного CLI измерения, не совет закреплять каждый HTTP запрос. Для вычисления результата PHP pinning обычно не требуется. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
try {
pcntl_setcpuaffinity(null, []);
} catch (ValueError $error) {
echo "Нужен непустой список CPU\n";
}
Ожидаемый результат
Нужен непустой список CPU
Ошибка аргумента возникает до изменения ОС. Полный разрешённый список получают чтением, а не пустым массивом. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_getcpuaffinity, pcntl_getcpu.
pcntl_getcpuДиагностически узнать логический CPU, на котором выполняется текущий поток в момент измерения.
pcntl_getcpu(): intДиагностически узнать логический CPU, на котором выполняется текущий поток в момент измерения.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_getcpu(): int |
| Аргументы | Нет. |
| Результат и ошибки | CPU ID>=0; Linux sched_getcpu может вернуть -1 при отказе. TaggedPHP8.5 возвращает его напрямую и не сохраняет новую PCNTL errno. |
| Среда | PHP8.4+; Linux sched_getcpu; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Это снимок текущего выполнения; сразу после вызова планировщик способен переместить поток. Не количество ядер, не процент загрузки и не обещание следующего размещения.
<?php
declare(strict_types=1);
$cpu = pcntl_getcpu();
echo $cpu >= 0 ? "Снимок CPU: $cpu\n" : "Снимок CPU недоступен\n";
Ожидаемый результат
Снимок CPU с integer ID либо явное отсутствие снимка.
Не подставляем старую pcntl_get_last_error в объяснение -1 этого wrapper. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$samples = [];
for ($i = 0; $i < 3; $i++) $samples[] = pcntl_getcpu();
echo implode(' → ', $samples), "\n";
Ожидаемый результат
Три CPU ID, которые могут совпадать или различаться.
Равенство снимков не доказывает отсутствие миграций между вызовами. Для времени используйте hrtime; для CPU budget — инструменты ОС. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_getcpuaffinity, pcntl_setcpuaffinity.
pcntl_getqos_classПрочитать класс важности текущего потока на Apple перед временной настройкой длительной задачи.
pcntl_getqos_class(): Pcntl\QosClassПрочитать класс важности текущего потока на Apple перед временной настройкой длительной задачи.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_getqos_class(): Pcntl\QosClass |
| Аргументы | Нет. |
| Результат и ошибки | Case enumPcntl\QosClass; ошибка pthread lookup выбрасывает Error. |
| Среда | PHP8.4+; Apple pthread QoS; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
QoS описывает важность для планировщика Apple. Это не одинаковая с nice шкала, не rate limit и не изменение всех процессов сервиса. Функция зависит от Apple pthread API.
<?php
declare(strict_types=1);
$qos = pcntl_getqos_class();
echo $qos->name, "\n";
Ожидаемый результат
Имя одного из пяти случаев enum по среде.
У unit enum есть name; поле value здесь отсутствует. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$qos = pcntl_getqos_class();
echo $qos === Pcntl\QosClass::Background ? "Фоновое обслуживание\n" : "Другой класс важности\n";
Ожидаемый результат
Подписанная ветка согласно текущему QoS.
Сравниваем enum case, не название произвольной строки из HTTP формы. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_setqos_class, pcntl_getpriority.
pcntl_setqos_classПометить отдельный длительный импорт как Utility или обслуживание как Background, чтобы ОС могла выбирать компромисс ресурсов.
pcntl_setqos_class(Pcntl\QosClass $qos_class = Pcntl\QosClass::Default): voidПометить отдельный длительный импорт как Utility или обслуживание как Background, чтобы ОС могла выбирать компромисс ресурсов.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_setqos_class(Pcntl\QosClass $qos_class = Pcntl\QosClass::Default): void |
| Аргументы | Один case enum; в примерах передаём явно. |
| Результат и ошибки | void; pthread ошибка — Error, иной тип — TypeError. В taggedPHP8.5.11 implementation требует1 аргумент: вызов без него даёт ArgumentCountError, хотя stub/Manual показывает default. |
| Среда | PHP8.4+; Apple pthread QoS; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Приоритет не даёт гарантированного срока и не ускоряет алгоритм. UserInteractive не следует ставить каждому cron. Сохраните старый класс перед ограниченной задачей; восстановление также может выбросить Error.
<?php
declare(strict_types=1);
$before = pcntl_getqos_class();
try {
pcntl_setqos_class(Pcntl\QosClass::Utility);
echo "Здесь короткий фрагмент пакетного импорта\n";
} finally {
pcntl_setqos_class($before);
}
Ожидаемый результат
Строка о пакетном импорте при успешной настройке/восстановлении.
Передаём case явно. Пакетная задача всё равно должна иметь разумные размеры/таймауты. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
pcntl_setqos_class(Pcntl\QosClass::Background);
echo 'Класс worker: ', pcntl_getqos_class()->name, "\n";
Ожидаемый результат
Класс worker: Background при поддержке и успешном вызове.
Процесс предназначен только для фоновой задачи. При смене роли нужно пересмотреть класс, а не навсегда распространять его на чужую работу. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_getqos_class, pcntl_setpriority.
Pcntl\QosClassПять именованных классов важности для планирования задач Apple.
enum Pcntl\QosClass implements UnitEnumКласс важности помогает назвать намерение: пользователь прямо сейчас ждёт ответ или обслуживание может уступить. Это unit enum с фиксированными case, не объект с произвольным числовым уровнем.
| Case | Когда соответствует задаче |
|---|---|
| UserInteractive | Короткая работа, непосредственно обслуживающая интерактивное действие. |
| UserInitiated | Пользователь инициировал работу и ждёт её результата. |
| Default | Обычный класс по умолчанию. |
| Utility | Длительная полезная работа без требования немедленного ответа: пакетный импорт. |
| Background | Отложенное обслуживание, очистка, индексация. |
Enum Pcntl\QosClass зарегистрирован PCNTL с PHP8.4; сами getter/setter доступны только при Apple QoS API. Наличие enum не доказывает наличие функций. new Pcntl\QosClass невозможен; name/cases() есть, backing value/from()/tryFrom() нет. Подробнее: как работает enum.
<?php
declare(strict_types=1);
foreach (Pcntl\QosClass::cases() as $case) echo $case->name, "\n";
Ожидаемый результат
UserInteractive
UserInitiated
Default
Utility
Background
Только чтение cases; не меняет приоритет процесса. Требует PCNTL, но не запускает scheduling API Apple. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$task = 'catalog-import';
$qos = match ($task) {
'catalog-import' => Pcntl\QosClass::Utility,
'cache-cleanup' => Pcntl\QosClass::Background,
default => Pcntl\QosClass::Default,
};
echo $qos->name, "\n";
Ожидаемый результат
Utility
Сначала прикладная политика, затем отдельный вызов setter в совместимой среде. Этот список задач принадлежит нашему приложению. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
pcntl_setnsВ отдельном служебном процессе войти в существующее namespace окружение известного собственного процесса.
pcntl_setns(?int $process_id = null, int $nstype = CLONE_NEWNET): boolВ отдельном служебном процессе войти в существующее namespace окружение известного собственного процесса.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_setns(?int $process_id = null, int $nstype = CLONE_NEWNET): bool |
| Аргументы | null — собственный PID; положительный PID — живая известная цель. nstype — mask разрешённых CLONE_NEW* namespaces. Значение0 не значит автоматически все namespaces при pidfd. |
| Результат и ошибки | true/false+warning/errno. Недоступный/недействительный PID — ValueError; несовместимый nstype — ValueError; отсутствие capabilities обычно false. |
| Среда | PHP8.4+; Linux pidfd/setns; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
PHP использует pidfd_open и setns(pidfd): нужен Linux с pidfd_open (5.3+) и поддержкой pidfd в setns (5.8+), плюс необходимые права. Сам namespace не создаётся. PHP закрывает внутренний pidfd. null отличается от явного0: pidfd_open(0) не является выбором текущего процесса.
<?php
declare(strict_types=1);
if (!pcntl_setns(null, CLONE_NEWNET)) {
throw new RuntimeException('Вход не разрешён: ' . pcntl_strerror(pcntl_get_last_error()));
}
echo "Выбрано существующее сетевое окружение своего процесса\n";
Ожидаемый результат
Подтверждение при достаточных правах либо обработанный отказ.
Даже вход в свой namespace требует правил setns/capabilities. Это показывает API без PID из внешнего запроса и без контакта с сетью. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$owner = posix_getpid();
$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
if (!pcntl_unshare(CLONE_NEWUTS)) exit(2);
if (!pcntl_setns($owner, CLONE_NEWUTS)) exit(3);
echo "Ребёнок вернулся в исходное UTS окружение\n";
exit(0);
}
$status = 0;
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($waited !== $pid) throw new RuntimeException('Не получено состояние своего ребёнка');
echo 'Код ребёнка: ', pcntl_wifexited($status) ? pcntl_wexitstatus($status) : 'сигнал', "\n";
Ожидаемый результат
При поддержке/правах: сообщение ребёнка и Код ребёнка:0; иначе соответствующий отказ.
Родитель жив и ждёт именно этого ребёнка. Изменяется только контекст ребёнка, не родителя; имена хостов не записываются. Если цель завершилась, её PID нельзя использовать повторно наугад. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_unshare, pcntl_fork, pcntl_waitpid.
pcntl_rforkНа BSD выбрать особенности создания дочернего процесса и наследования его descriptor table.
pcntl_rfork(int $flags, int $signal = 0): intНа BSD выбрать особенности создания дочернего процесса и наследования его descriptor table.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_rfork(int $flags, int $signal = 0): int |
| Аргументы | Флаги RF*; signal используется вместе с RFTSIGZMB для сигнала родителю при завершении. Поддержка каждого флага платформенная. |
| Результат и ошибки | PID ребёнка в родителе,0 в ребёнке,-1/warning/errno при системном отказе. Конфликт RFFDG|RFCFDG — ValueError. |
| Среда | PHP8.1+; BSD rfork; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
PHP wrapper всегда добавляет RFPROC: даже без указанного RFPROC создаётся процесс, не выполняется только in-place изменение как у некоторых OS rfork сценариев. Общая память RFMEM и RFSIGSHARE отвергается PHP. Это не API многопоточного PHP и не способ совместно мутировать массив.
<?php
declare(strict_types=1);
$pid = pcntl_rfork(RFPROC | RFFDG);
if ($pid === -1) throw new RuntimeException(pcntl_strerror(pcntl_get_last_error()));
if ($pid === 0) { echo "Собственный ребёнок\n"; exit(0); }
do { $done = pcntl_waitpid($pid, $status); }
while ($done === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($done !== $pid) throw new RuntimeException('Ребёнок не собран');
echo "Родитель собрал ребёнка\n";
Ожидаемый результат
Собственный ребёнок
Родитель собрал ребёнка
RFFDG отделяет descriptor table, но открытые файлы могут всё ещё ссылаться на общие open file descriptions и offsets. Сокетный протокол БД не становится безопасным для совместного использования. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
try {
pcntl_rfork(RFPROC | RFFDG | RFCFDG);
} catch (ValueError $error) {
echo "Нельзя одновременно копировать и очищать descriptor table\n";
}
Ожидаемый результат
Нельзя одновременно копировать и очищать descriptor table
Только в BSD сборке с обоими флагами; rejected arguments не создают ребёнка. RFCFDG без конфликта закрыл бы также привычные стандартные handles. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
RFNOWAIT не оставляет родителю обычный waitable результат: не выбирайте его для отслеживаемой очереди заданий. RFLINUXTHPN меняет уведомление на SIGUSR1; RFTSIGZMB использует указанный signal (0 — без такого уведомления). Установленный обработчик должен соответствовать выбранному сигналу. RFTHREAD здесь — особенности связанного BSD process leader, а не общий PHP heap.
Связь: pcntl_fork, pcntl_waitpid, pcntl_signal.
pcntl_forkxНа Solaris/illumos создать дочерний процесс с особым правилом получения его завершения.
pcntl_forkx(int $flags): intНа Solaris/illumos создать дочерний процесс с особым правилом получения его завершения.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_forkx(int $flags): int |
| Аргументы | FORK_WAITPID требует wait конкретного PID; FORK_NOSIGCHLD подавляет SIGCHLD о завершении, но не отменяет необходимость сбора. |
| Результат и ошибки | PID/0/-1 как fork; недопустимые flags — ValueError, системный отказ — warning+errno. |
| Среда | PHP8.2+; Solaris/illumos forkx; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
OS forkx допускает0 и OR флагов, но taggedPHP8.5.11 wrapper проверяет диапазон между FORK_NOSIGCHLD и FORK_WAITPID. Не переносите OS комбинации автоматически: здесь выбираем один явный флаг, поддержанный PHP. Оба примера собирают именно созданного ребёнка.
<?php
declare(strict_types=1);
$pid = pcntl_forkx(FORK_WAITPID);
if ($pid === -1) throw new RuntimeException(pcntl_strerror(pcntl_get_last_error()));
if ($pid === 0) { echo "Собственный ребёнок\n"; exit(0); }
do { $done = pcntl_waitpid($pid, $status); }
while ($done === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($done !== $pid) throw new RuntimeException('Ребёнок не собран');
echo "Родитель собрал ребёнка\n";
Ожидаемый результат
Собственный ребёнок
Родитель собрал ребёнка
Общий wait()/waitid(P_ALL) не должен забирать этого ребёнка; нужен конкретный PID. Если его не собрать, он остаётся zombie. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$pid = pcntl_forkx(FORK_NOSIGCHLD);
if ($pid === -1) throw new RuntimeException(pcntl_strerror(pcntl_get_last_error()));
if ($pid === 0) { echo "Собственный ребёнок\n"; exit(0); }
do { $done = pcntl_waitpid($pid, $status); }
while ($done === -1 && pcntl_get_last_error() === PCNTL_EINTR);
if ($done !== $pid) throw new RuntimeException('Ребёнок не собран');
echo "Родитель собрал ребёнка\n";
Ожидаемый результат
Собственный ребёнок
Родитель собрал ребёнка
Флаг относится к уведомлению о завершении; job control stop/continue уведомления могут иметь другие правила. Отсутствие сигнала не значит отсутствие wait result. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_fork, pcntl_waitpid, pcntl_wait.
pcntl_get_last_errorПосле отказа PCNTL узнать числовую причину и выбрать осмысленную ветку восстановления.
pcntl_get_last_error(): intПосле отказа PCNTL узнать числовую причину и выбрать осмысленную ветку восстановления.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_get_last_error(): int |
| Аргументы | Нет. |
| Результат и ошибки | Сохранённый integer errno PCNTL; чтение не очищает его. |
| Среда | PHP5.3+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Успешный последующий вызов не обязан сбрасывать прошлую ошибку. Сохраняйте код немедленно после документированного отказа. getcpu-1 и sigtimedwait timeout в tagged8.5 не обновляют его; PHP ValueError/TypeError тоже нужно обрабатывать отдельно.
<?php
declare(strict_types=1);
$result = pcntl_waitpid(-1, $status, WNOHANG);
if ($result === -1) {
$code = pcntl_get_last_error();
echo $code === PCNTL_ECHILD ? "В этом отдельном CLI нет детей\n" : "Иной отказ ожидания\n";
} else {
echo "Есть состояние созданных ранее детей\n";
}
Ожидаемый результат
В этом отдельном CLI нет детей
Новый CLI не создавал детей. ECHILD приходит при -1; WNOHANG0 означал бы другой случай — ребёнок есть, нового события пока нет. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$done = pcntl_waitpid(-1, $status, WNOHANG);
if ($done === -1) {
$code = pcntl_get_last_error();
$report = ['operation' => 'waitpid', 'errno' => $code, 'message' => pcntl_strerror($code)];
echo json_encode($report, JSON_THROW_ON_ERROR), "\n";
}
Ожидаемый результат
JSON с operationwaitpid, кодом ECHILD и системным текстом в отдельном CLI без детей.
Локализованный текст полезен человеку, но ветвление делается по числовой константе. Нет подавления warning оператором @. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_errno, pcntl_strerror, pcntl_waitpid.
pcntl_errnoИспользовать краткий alias get_last_error при сопровождении существующего worker.
pcntl_errno(): intИспользовать краткий alias get_last_error при сопровождении существующего worker.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_errno(): int |
| Аргументы | Нет. |
| Результат и ошибки | Тот же сохранённый PCNTL errno; нового состояния не создаёт. |
| Среда | PHP5.3+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Alias не является отдельным системным errno всей программы: error от sockets/POSIX/cURL читают через их собственный API. Успех другого вызова не превращает старый PCNTL код в актуальный.
<?php
declare(strict_types=1);
pcntl_waitpid(-1, $status, WNOHANG);
$short = pcntl_errno();
$long = pcntl_get_last_error();
echo $short === $long ? "Один сохранённый код\n" : "Код изменился между вызовами\n";
Ожидаемый результат
Один сохранённый код
В отдельном CLI без созданных детей ожидается ECHILD. Между чтениями нет операции, записывающей другую PCNTL ошибку. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$done = pcntl_waitpid(-1, $status, WNOHANG);
if ($done === -1 && pcntl_errno() === PCNTL_ECHILD) echo "Ожидание закончено: детей нет\n";
elseif ($done === 0) echo "Ребёнок есть, события пока нет\n";
elseif ($done > 0) echo "Получено событие ребёнка\n";
else throw new RuntimeException('Иной отказ wait');
Ожидаемый результат
Ожидание закончено: детей нет в свежем CLI.
Ошибку отсутствия подходящего ребёнка нельзя лечить быстрым бесконечным повтором. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_get_last_error, pcntl_strerror.
pcntl_strerrorПревратить сохранённый системный код в читаемое пояснение журнала.
pcntl_strerror(int $error_code): stringПревратить сохранённый системный код в читаемое пояснение журнала.
| Часть договора | Описание |
|---|---|
| Сигнатура | pcntl_strerror(int $error_code): string |
| Аргументы | Integer errno, обычно сразу сохранённый после отказа PCNTL. |
| Результат и ошибки | Строка libc strerror; язык/формулировка зависит от ОС и locale. Неизвестный код получает системное описание, не гарантированное исключение. |
| Среда | PHP5.3+; Unix PCNTL, наличие зависит от системных вызовов и сборки. |
Функция не проверяет процесс, не повторяет вызов и не сбрасывает ошибку. Текст может содержать служебную диагностику: показывайте его в локальном журнале, не как публичный HTML без экранирования.
<?php
declare(strict_types=1);
echo 'ECHILD: ', pcntl_strerror(PCNTL_ECHILD), "\n";
Ожидаемый результат
Системный текст о том, что подходящих дочерних процессов нет.
Не фиксируем английскую фразу как критерий проверки на любой ОС. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
<?php
declare(strict_types=1);
$labels = [PCNTL_EINTR => 'Можно решить, повторять ли ожидание', PCNTL_ECHILD => 'Детей для ожидания нет'];
foreach ($labels as $code => $action) echo $code, ': ', pcntl_strerror($code), ' — ', $action, "\n";
Ожидаемый результат
Две подписанные причины с платформенными числовыми значениями и текстом.
Это справочный вывод без выполнения fork/сигналов. EINTR retry должен учитывать запрос остановки и deadline конкретной задачи. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.
Связь: pcntl_get_last_error, pcntl_errno.