Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 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 описан отдельно.

Проверить CLI и конкретные функции без запуска процессовПример для указанного окружения
<?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.3async вспомогательные getter ошибок/dispatch и continued predicate; async_signals появился позже, в7.1.
PHP7.1async_signals и signal_get_handler; siginfo в callback при поддержке.
PHP7.4 /8.1 /8.2unshare / rfork / forkx соответственно.
PHP8.4waitid, affinity/getcpu, namespace setns и QoS API/enum; ужесточены аргументы signal wait/mask.
PHP8.5resource_usage у waitid; exec отвергает NUL в args/env.

Выбор инструмента и стоимость подхода

НужноС чего начать
Выполнить одну внешнюю программу и прочитать stdoutproc_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/деструкторам завершиться: устойчивость данных обеспечивает протокол записи и восстановления.

Понять cooperative stop на обычном массиве заданийСамостоятельный пример
<?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_ALLwaitid: все подходящие собственные дети, id игнорируется.
P_PIDwaitid: конкретный PID ребёнка.
P_PGIDwaitid: дети выбранной process group; это не GID пользователя.
P_PIDFDLinux 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_JAILIDFreeBSD 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Состояние питания на поддерживающей ОС.
SIGINFOBSD запрос статусной информации.
SIGSYSНекорректный системный вызов или ограничение syscall по правилам ОС.
SIGBABYИсторическое альтернативное имя SIGSYS на некоторых системах.
SIGCKPTDragonFly checkpoint request; добавлен PHP8.4 при наличии системного имени.
SIGCKPTEXITDragonFly 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_PROCESSID обозначает процесс; default для getpriority/setpriority.
PRIO_PGRPID обозначает process group; установка затрагивает группу процессов.
PRIO_USERID обозначает пользователя; установка затрагивает его процессы по правилам ОС.
PRIO_DARWIN_THREADApple mode для текущего потока; process_id должен быть0/null.
PRIO_DARWIN_BGApple специальное значение 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_NEWNSLinux mount namespace; propagation и mount policy настраиваются отдельно.
CLONE_NEWIPCLinux IPC namespace: отдельное пространство соответствующих IPC объектов.
CLONE_NEWUTSLinux UTS namespace: hostname/domainname окружение.
CLONE_NEWNETLinux network namespace: интерфейсы, маршруты и сетевой стек; сеть не настраивается автоматически.
CLONE_NEWPIDLinux PID namespace; для unshare влияет на будущих детей, не на PID вызывающего процесса.
CLONE_NEWUSERLinux user namespace; нужны UID/GID mapping и разрешение системной политики.
CLONE_NEWCGROUPLinux cgroup namespace; меняет представление пути, не автоматически назначает CPU/RAM quota.
RFPROCBSD создать процесс; PHP rfork добавляет этот флаг всегда.
RFNOWAITBSD не сохранять обычный waitable статус ребёнка для родителя.
RFCFDGBSD очистить FD table ребёнка, включая стандартные descriptors; конфликтует с RFFDG.
RFFDGBSD скопировать FD table отдельно; сами open file descriptions всё ещё могут быть общими.
RFLINUXTHPNBSD Linux threading-related режим уведомления SIGUSR1; не PHP threads.
RFTSIGZMBBSD использовать второй аргумент signal для завершения ребёнка;0 — без уведомления по этому механизму.
RFTHREADBSD связать process leader semantics; не разрешает общую мутацию PHP heap.
FORK_NOSIGCHLDSolaris/illumos подавить SIGCHLD о завершении этого ребёнка; wait всё равно нужен.
FORK_WAITPIDSolaris/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. Чтение реального конфига должно проверить содержимое до замены действующей конфигурации.

Fork, отдельный сеанс, два handler и конечный циклПример для указанного окружения
<?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\QosClass — все пять случаев enum.

Задание: создайте одного собственного ребёнка, который возвращает код2 при неудаче учебной проверки. Родитель должен отличить normal exit2 от завершения сигналом; WNOHANG0 не считать успешным результатом. Затем добавьте stop flag между короткими задачами и объясните, почему SIGTERM не заменяет HTTP timeout. Правильный результат включает сбор ребёнка и отсутствие бесконечного polling. Готовые основы находятся в карточках fork/waitpid/wifexited/wexitstatus/signal; не начинайте с namespace и повышения приоритета.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

pcntl_forkРазделить обработку независимых заданий на разные процессы: родитель организует работу, ребёнок выполняет своё задание.

PHP4.1+ · pcntl (Unix CLI)

pcntl_fork(): int

Разделить обработку независимых заданий на разные процессы: родитель организует работу, ребёнок выполняет своё задание.

Часть договораОписание
Сигнатураpcntl_fork(): int
АргументыНет.
Результат и ошибкиВ родителе — положительный PID ребёнка; в ребёнке —0; отказ —-1, warning и PCNTL errno, ребёнок не создан.
СредаPHP4.1+; Unix PCNTL, наличие зависит от системных вызовов и сборки.

Вызов продолжается дважды с разными результатами. Это разные процессы, не два потока с общим PHP массивом. Изменение обычной переменной ребёнка не изменит родителя. Бесконтрольный fork в цикле исчерпает процессы/память; ограничьте число живых детей.

Один 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 закончил подготовку.

PHP4.2+ · pcntl (Unix CLI)

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 с наследованием среды.

Заменить процесс известным скриптом без shellПример для указанного окружения
<?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 запись.

PHP4.1+ · pcntl (Unix CLI)

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 означает отсутствие подходящего ещё не собранного ребёнка, а не «он ещё работает».

Получить код7 своего ребёнкаПример для указанного окружения
<?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 заранее не нужен.

PHP5+ · pcntl (Unix CLI)

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.

PHP8.4+ · pcntl (Unix CLI)

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_PIDFDLinux5.4+: настоящий PID file descriptor, не PHP resource ID и не обычный PID.
P_UID / P_GID / P_SIDПлатформенные BSD selectors effective UID/GID/session.
P_JAILIDFreeBSD jail identifier.
Разобрать завершение по CLD_EXITEDПример для указанного окружения
<?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 ребёнка от завершения сигналом, прежде чем извлекать прикладной код.

PHP4.1+ · pcntl (Unix CLI)

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 после обычного завершения ребёнка.

PHP4.1+ · pcntl (Unix CLI)

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. При сигнале эта функция не даёт корректного объяснения.

Получить выбранный код отказа jobПример для указанного окружения
<?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.

PHP4.1+ · pcntl (Unix CLI)

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, даст обычное завершение.

Ребёнок намеренно завершает только себя SIGTERMПример для указанного окружения
<?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.

PHP4.1+ · pcntl (Unix CLI)

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 требуют отдельного журнала ОС.

Узнать свой учебный SIGTERMПример для указанного окружения
<?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.

Раздельный отчёт exit и signalПример для указанного окружения
<?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.

PHP4.1+ · pcntl (Unix CLI)

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 от завершения.

PHP4.1+ · pcntl (Unix CLI)

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 — безусловная остановка; происхождение сигнала из одного номера не известно.

Определить 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.

Единый отчёт для normal exit без неверного decoderПример для указанного окружения
<?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.

PHP5.3+ · pcntl (Unix CLI)

pcntl_wifcontinued(int $status): bool

Узнать событие возобновления ранее остановленного worker.

Часть договораОписание
Сигнатураpcntl_wifcontinued(int $status): bool
АргументыStatus waitpid с WCONTINUED на поддерживающей ОС.
Результат и ошибкиtrue при продолжении через SIGCONT; false при другом событии.
СредаPHP5.3+; Unix PCNTL, наличие зависит от системных вызовов и сборки.

Функция не возобновляет процесс сама. Continue не означает, что задание закончено: окончательный exit ещё нужно собрать. Поддержка WCONTINUED и функции зависит от ОС.

Пройти stop → continue → exit с одним ребёнкомПример для указанного окружения
<?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.

Обычный exit не является continueПример для указанного окружения
<?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 реагировать на просьбу завершиться или обновить настройки, не теряя текущую короткую операцию.

PHP4.1+ · pcntl (Unix CLI)

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Сохранить прежнюю реакцию на сигнал перед временной настройкой и затем восстановить её.

PHP7.1+ · pcntl (Unix CLI)

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.

Временно установить callback и вернуть старыйПример для указанного окружения
<?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 в цикле.

PHP7.1+ · pcntl (Unix CLI)

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Выбрать явную точку, где программа обрабатывает накопленные сигналы.

PHP5.3+ · pcntl (Unix CLI)

pcntl_signal_dispatch(): bool

Выбрать явную точку, где программа обрабатывает накопленные сигналы.

Часть договораОписание
Сигнатураpcntl_signal_dispatch(): bool
АргументыНет.
Результат и ошибкиtrue/false по API; число обработанных сигналов не возвращается.
СредаPHP5.3+; Unix PCNTL, наличие зависит от системных вызовов и сборки.

Dispatch не создаёт сигнал и не блокируется до события. Если основной цикл застрял внутри вызова, до следующего ручного dispatch он не дойдёт. В обычном worker удобнее async_signals; ручной режим нужен осознанному управлению точками обработки.

Получить сигнал сейчас, вызвать callback в указанной точкеПример для указанного окружения
<?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.

Вызов без pending сигналов не ждётПример для указанного окружения
<?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Временно отложить доставку выбранных сигналов либо подготовить их синхронное ожидание.

PHP5.3+ · pcntl (Unix CLI)

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 протоколе, получив номер сигнала и его описание.

PHP5.3+ · pcntl (Unix 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.

PHP5.3+ · pcntl (Unix CLI)

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 в части случаев.

Принять уже ожидающее событие с верхней границей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.

Разобрать отсутствие события за10миллисекундПример для указанного окружения
<?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.

PHP4.3+ · pcntl (Unix CLI)

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.

Назначить и отменить собственный alarmПример для указанного окружения
<?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.

Флаг времени и обязательная отмена в finallyПример для указанного окружения
<?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 импортёра, когда он конкурирует с другими задачами за процессор.

PHP4.1+ · pcntl (Unix CLI)

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 и не задержка в секундах. Для группы/пользователя ОС возвращает наиболее высокий приоритет выбранной совокупности. Значение по умолчанию относится к своему процессу.

Прочитать nice своего процессаПример для указанного окружения
<?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Сделать отдельный пакетный импорт менее приоритетным, чтобы он меньше мешал интерактивной работе.

PHP4.1+ · pcntl (Unix CLI)

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 для эксперимента — это затрагивает много процессов.

Уступить CPU только в текущем одноразовом CLIПример для указанного окружения
<?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.

Явно сохранить собственный текущий niceПример для указанного окружения
<?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 внутри контейнера или сервера.

PHP8.4+ · pcntl (Unix CLI)

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, не обязательно полное число процессоров машины.

Показать разрешённые 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.

Ограничить число независимых вычислительных workerПример для указанного окружения
<?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.

PHP8.4+ · pcntl (Unix CLI)

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 изменились.

На короткое время выбрать один из уже разрешённых 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.

Увидеть, что default[] не означает все CPUПример для указанного окружения
<?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, на котором выполняется текущий поток в момент измерения.

PHP8.4+; Linux sched_getcpu · pcntl (Unix CLI)

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, наличие зависит от системных вызовов и сборки.

Это снимок текущего выполнения; сразу после вызова планировщик способен переместить поток. Не количество ядер, не процент загрузки и не обещание следующего размещения.

Снять ID без ложного чтения errnoПример для указанного окружения
<?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 перед временной настройкой длительной задачи.

PHP8.4+; Apple pthread QoS · pcntl (Unix CLI)

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.

Отделить maintenance от работы, ожидаемой пользователемПример для указанного окружения
<?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, чтобы ОС могла выбирать компромисс ресурсов.

PHP8.4+; Apple pthread QoS · pcntl (Unix CLI)

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.

Временный Utility с возвратом прежнего классаПример для указанного окружения
<?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.

Выбрать класс одноразового maintenance workerПример для указанного окружения
<?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.

PHP8.4+ · pcntl

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.

Сопоставить задачу с case без изменения ОСПример для указанного окружения
<?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_unshareОтделить выбранную часть Linux окружения одноразового worker, когда проекту действительно нужны namespaces.

PHP7.4+; Linux unshare · pcntl (Unix CLI)

pcntl_unshare(int $flags): bool

Отделить выбранную часть Linux окружения одноразового worker, когда проекту действительно нужны namespaces.

Часть договораОписание
Сигнатураpcntl_unshare(int $flags): bool
АргументыПобитовое объединение поддерживаемых CLONE_NEW*; набор зависит от ядра, capabilities и системной политики.
Результат и ошибкиtrue/false+errno; EINVAL, включая неподдержанный ядром флаг, превращается в ValueError. EPERM/ENOMEM/ENOSPC/EUSERS сопровождаются warningfalse.
СредаPHP7.4+; Linux unshare; Unix PCNTL, наличие зависит от системных вызовов и сборки.

Вызов не создаёт ребёнка и не имеет отношения к PHP namespace. Он изменяет контекст вызывающего процесса. Namespace — только часть изоляции: mounts, UID/GID mappings, capabilities, seccomp и лимиты настраиваются отдельно. Не делайте fallback «продолжить на хосте» после отказа изоляции.

Создать отдельный UTS контекст только внутри собственного ребёнкаПример для указанного окружения
<?php
declare(strict_types=1);

$pid = pcntl_fork();
if ($pid === -1) throw new RuntimeException('Не создан ребёнок');
if ($pid === 0) {
    if (!pcntl_unshare(CLONE_NEWUTS)) exit(2);
    echo "Ребёнок получил отдельный UTS namespace\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; иначе код2/диагностика отказа.

ОС должна разрешать namespace operation. Имя хоста не меняем, чужие процессы не затрагиваем. Родитель остаётся в исходном namespace. Контекст: отдельный 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) {
    if (!pcntl_unshare(CLONE_NEWNET)) exit(2);
    $name = readlink('/proc/self/ns/net');
    echo 'Локальный network namespace: ', $name === false ? 'не прочитан' : $name, "\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) === 0 ? "Изолированный шаг завершён\n" : "Изоляция не подтверждена\n";
Ожидаемый результат
Идентификатор своего нового network namespace и завершение либо явный отказ.

Linux /proc должен быть доступен. Мы не поднимаем интерфейсы и не отправляем пакеты. В новом namespace требуется отдельная настройка сети; отсутствие маршрута не исправляют отправкой через контекст хоста. Контекст: отдельный Unix CLI с PCNTL; примеры с posix_* также требуют POSIX. Здесь проверен PHP синтаксис; Unix процессы/сигналы на Windows не исполнялись. Все цели — свой процесс или собственные короткоживущие дети; не вставляйте пример в PHP-FPM.

CLONE_NEWPID меняет PID namespace для будущих детей, а не переносит PID вызывающего процесса. CLONE_NEWNS создаёт mount namespace, но propagation может ещё связывать mounts; настройка private propagation — отдельный системный шаг. CLONE_NEWUSER требует корректного UID/GID mapping и разрешения политики. Для типового веб-сервиса контейнер или supervisor с заранее настроенной изоляцией понятнее низкоуровневого PHP launcher.

Связь: pcntl_setns, pcntl_fork.

pcntl_setnsВ отдельном служебном процессе войти в существующее namespace окружение известного собственного процесса.

PHP8.4+; Linux pidfd/setns · pcntl (Unix CLI)

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) не является выбором текущего процесса.

Явно проверить вход в своё существующее network окружениеПример для указанного окружения
<?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.

В своём ребёнке отделить UTS и вернуться в окружение живого родителяПример для указанного окружения
<?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.

PHP8.1+; BSD rfork · pcntl (Unix CLI)

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 и не способ совместно мутировать массив.

Создать ребёнка с собственной копией таблицы FDПример для указанного окружения
<?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 создать дочерний процесс с особым правилом получения его завершения.

PHP8.2+; Solaris/illumos forkx · pcntl (Unix CLI)

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. Оба примера собирают именно созданного ребёнка.

Сделать результат доступным только явному ожиданию PIDПример для указанного окружения
<?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.

Собрать завершение без уведомления SIGCHLDПример для указанного окружения
<?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 узнать числовую причину и выбрать осмысленную ветку восстановления.

PHP5.3+ · pcntl (Unix CLI)

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.

PHP5.3+ · pcntl (Unix CLI)

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 код в актуальный.

Увидеть равенство двух имён одного getterПример для указанного окружения
<?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.

Не повторять wait бесконечно после ECHILDПример для указанного окружения
<?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Превратить сохранённый системный код в читаемое пояснение журнала.

PHP5.3+ · pcntl (Unix CLI)

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.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.