Глава 130 · От переменных к нескольким процессам
System V IPC: сообщения, семафоры и общая память
Полный раздел SysV IPC: 19 функций, три служебных класса, все константы, установка и управление жизнью ресурсов.
Когда нескольким обработчикам нужны общие данные
На одном сервере работают два PHP обработчика: первый принимает заказ, второй делает импорт. Иногда им надо сообщить друг другу о задании, иногда — видеть один снимок состояния, иногда — по очереди менять общий остаток. Обычная переменная $stock одного процесса не становится общей для другого. System V IPC даёт три отдельных инструмента для этой ситуации: сообщения, память и семафоры.
- Процесс сайта создал задание orderId=42
- Очередь передала задание обработчику
- Обработчик запросил пропуск у семафора
- Прочитал остаток A42=7 из общей памяти
- Записал A42=6 и вернул пропуск
| Инструмент | Бытовая модель и её граница |
|---|---|
| Очередь сообщений (sysvmsg) | Лоток с конвертами. Получение забирает конверт; тип позволяет выбрать нужный. Это не почта с автоматическим подтверждением обработки и хранением на диске. |
| Общая память (sysvshm) | Шкаф с пронумерованными ячейками. Значение читается в локальную переменную; это не ссылка на общую PHP переменную и не база с транзакциями. |
| Семафор (sysvsem) | Набор пропусков. При одном пропуске участники входят по очереди. Он не хранит бизнес-данные и не мешает коду, который вообще не попросил пропуск. |
Глава предполагает, что вы уже знаете переменные, вызовы функций и массивы. Долгоживущие процессы и конкуренция — следующий шаг. Начнём с причины их координации без системных расширений: два обработчика прочли одно старое число, затем оба сохранили результат.
<?php
declare(strict_types=1);
$storedStock = 7;
$workerARead = $storedStock;
$workerBRead = $storedStock;
$storedStock = $workerARead - 1;
$storedStock = $workerBRead - 1;
echo 'После двух резервирований: ', $storedStock, "\n";
echo "Ожидалось: 5\n";
Ожидаемый результат
После двух резервирований: 6
Ожидалось: 5
Это модель конкретного порядка событий, не тест потоков. Оба прочли 7; каждый получил 6; вторая запись затёрла первую. Защищать надо весь путь от чтения до записи.
Установка, три расширения, платформы и современные объекты
В официальной книге эти API объединены, но устанавливаются как три расширения. На Unix проверяют наличие SysV IPC у ОС и доступность в контейнере/namespace. При сборке PHP флаги --enable-sysvmsg, --enable-sysvsem, --enable-sysvshm включают соответствующие части. В пакетном дистрибутиве используйте его комплект расширений, совместимый с установленным PHP; имя пакета зависит от ОС. Старые упоминания PHP 3 configuration.h относятся к истории, а не к PHP 8.
<?php
declare(strict_types=1);
foreach (['sysvmsg', 'sysvsem', 'sysvshm'] as $extension) {
echo $extension, ': ', extension_loaded($extension) ? 'есть' : 'нет', "\n";
}
echo 'sem_get: ', function_exists('sem_get') ? 'доступна' : 'недоступна', "\n";
Ожидаемый результат
Четыре строки с фактической доступностью расширений и sem_get.
Запустите в том SAPI, где работает приложение: CLI и PHP-FPM могут читать разные ini. Код не создаёт IPC ресурсы. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
sysvmsg и sysvsem не входят в обычную Windows сборку: для таких примеров нужен Unix/Linux, например отдельная учебная Linux среда. В PHP 8.5.11 существует Windows сборка sysvshm и реализация через mappings; это не обещание одинаковых Unix permissions/lifetime. Исполняемые shm примеры этой главы используют только новые приватные сегменты и завершают доступ в своём процессе. Ни наличие sysvshm, ни наличие shmop не означает наличие sem_get. shmop — другое расширение для байтов, его формат не совместим с PHP хранилищем переменных sysvshm.
Ресурсы PHP 4–7 заменены в PHP 8.0 объектами SysvMessageQueue, SysvSemaphore и SysvSharedMemory. Это не скрытое подключение к сети. Файловые права и контейнерные namespaces определяют, какие процессы видят один IPC ресурс. Две машины не разделяют такой сегмент: для нескольких серверов выбирайте сетевое хранилище/очередь с подходящим договором.
Ёмкость, ini и ограничения операционной системы
| Настройка/лимит | Что проверять |
|---|---|
| sysvshm.init_mem | Единственная ini настройка этой книги: default 10000 байт, INI_SYSTEM. Используется при shm_attach без явного size. Не подменяется ini_set в обычном скрипте. |
| Явный size | Положительный размер нового сегмента. Служебный заголовок, сериализация и выравнивание занимают часть объёма. Existing segment не увеличится от повторного attach с большим size. |
| Unix общая память | Ограничения максимального/минимального размера, числа сегментов и общего объёма зависят от ОС/настроек. Старые SHMMAX/SHMMIN/SHMMNI/SHMSEG примеры документации — названия/исторические примеры, не универсальные современные числа. |
| Семафоры | Лимиты количества наборов и разрешений. Утечки наборов приводят к системному отказу даже при небольшом PHP memory_limit. |
| Очереди | Лимит числа очередей, размера одного сообщения и суммарных байтов очереди. Это разные границы. msg_qbytes читается через stat и меняется с проверкой прав. |
У sysvmsg/sysvsem нет пользовательских ini директив для снятия системных лимитов. Увеличение memory_limit PHP не увеличивает лимит очереди ОС. Сначала найдите утечку/медленного потребителя/неверный размер receive, затем обоснованно меняйте настройки среды. Не публикуйте phpinfo со всей конфигурацией на открытой странице.
Не путать IPC ключ, ключ записи и тип сообщения
В примерах есть три числа с разным смыслом. Системный key выбирает очередь/сегмент/набор; запись key=101 выбирает значение внутри общей памяти; message_type=2 выбирает вид сообщения. Ни одно из них не заменяет orderId=42 внутри данных. Совпадение чисел случайно: именуйте переменные по роли.
Для independently launched участников ftok согласует ключ из защищённого файла, но не гарантирует уникальности. Владелец выбирает отдельный namespace/конфигурацию и режим доступа. Он не перебирает чужие key и не удаляет существующий ресурс «на всякий случай». Приватный key 0 удобен для автономного примера, но при каждом вызове создаёт независимый ресурс; повтор 0 в другом worker не является подключением к первому.
Учебные файлы и безопасное владение ресурсами
Каждый автономный пример поставляется со своими helper файлами. Они не подключаются к production, не выбирают произвольные внешние key и не удаляют чужие объекты. PHP исключение не отменяет finally; при аварийном завершении процесса Unix ресурсы всё равно могут требовать cleanup владельцем. В production supervisor хранит список именно своих ресурсов и проводит согласованное завершение; blanket ipcrm по всем объектам сервера недопустим.
<?php
function lessonMemory(int $size = 16384): SysvSharedMemory {
$memory = shm_attach(0, $size, 0600);
if ($memory === false) throw new RuntimeException('Не создан учебный сегмент');
return $memory;
}
Ожидаемый результат
Файл объявляет lessonMemory(); сам по себе сегмент не создаёт.
Сохраните как ipc-memory.php. 0 — IPC_PRIVATE; созданный объект принадлежит вызвавшему примеру. Примеры всегда remove до detach, а Windows процесс завершает свои mapping handles. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
function lessonQueue(): SysvMessageQueue {
// IPC_PRIVATE: create a queue belonging only to this example.
$queue = msg_get_queue(0, 0600);
if ($queue === false) throw new RuntimeException('Не создана очередь');
// msg_get_queue first calls msgget(key, 0); for key 0 that already creates it.
if (!msg_set_queue($queue, ['msg_perm.mode' => 0600])) {
msg_remove_queue($queue);
throw new RuntimeException('Не установлены права учебной очереди');
}
return $queue;
}
Ожидаемый результат
Файл объявляет lessonQueue(); сам по себе очередь не создаёт.
Сохраните как ipc-queue.php. Дополнительный set исправляет тонкость msg_get_queue(0): первая внутренняя попытка получения уже создаёт приватную очередь с нулевым mode. Владелец назначает 0600, затем пользуется очередью. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
function lessonSemaphore(int $slots = 1): SysvSemaphore {
if ($slots < 1) throw new InvalidArgumentException('Нужен хотя бы один пропуск');
$semaphore = sem_get(0, $slots, 0600, true);
if ($semaphore === false) throw new RuntimeException('Не создан семафор');
return $semaphore;
}
Ожидаемый результат
Файл объявляет lessonSemaphore(); сам по себе набор не создаёт.
Сохраните как ipc-semaphore.php. Этот helper предназначен для изолированного опыта; совместные workers получают согласованный общий key, а не 0. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Резервирование: память для данных, семафор для порядка
Теперь соединим два инструмента на одном результате: резервирование двух товаров A42 должно оставить 5 из 7. Семафор выдаёт доступ, память хранит число. Обработчик сначала получает пропуск, потом читает, проверяет и записывает. Значение количества приходит в функцию аргументом; глобальная переменная здесь не нужна.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-memory.php';
require __DIR__ . '/ipc-semaphore.php';
function reserve(SysvSharedMemory $memory, SysvSemaphore $lock, int $quantity): bool {
if ($quantity < 1) throw new InvalidArgumentException('Количество должно быть положительным');
if (!sem_acquire($lock, true)) throw new RuntimeException('Занято: повторить позже');
try {
if (!shm_has_var($memory, 101)) throw new RuntimeException('Остаток не инициализирован');
$stock = shm_get_var($memory, 101);
if (!is_int($stock) || $stock < 0) throw new RuntimeException('Повреждён остаток');
if ($stock < $quantity) return false;
if (!shm_put_var($memory, 101, $stock - $quantity)) throw new RuntimeException('Запись не удалась');
return true;
} finally { sem_release($lock); }
}
$memory = lessonMemory();
try {
$lock = lessonSemaphore();
try {
if (!shm_put_var($memory, 101, 7)) throw new RuntimeException('Нет места');
echo reserve($memory, $lock, 2) ? "Зарезервировано\n" : "Недостаточно товара\n";
echo 'Осталось: ', shm_get_var($memory, 101), "\n";
} finally { sem_remove($lock); }
} finally { shm_remove($memory); shm_detach($memory); }
Ожидаемый результат
Зарезервировано
Осталось: 5
Unix sysvsem + sysvshm. Это один контролируемый вызов с тем же lock, который должны разделять реальные участники. Для магазина с деньгами общая память сама по себе недостаточна: нужна долговечность и атомарная транзакция БД. Пример учит границе критического участка, не изображает готовую систему продаж. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Если quantity больше остатка, return false выполняется внутри try, но finally всё равно возвращает пропуск. Если функция уже вызвана из участка с удерживаемым тем же max=1 семафором, нельзя повторно захватывать его без специального протокола: разделите внешнюю координацию и внутреннее изменение. Захват нескольких locks в разном порядке создаёт deadlock; установите общий порядок и не допускайте вложенной неопределённости.
Полный каталог констант: флаги отдельно от errno
Все пять констант официальной книги относятся к сообщениям и доступны с sysvmsg. Два имени обозначают errno, три — флаги: подставлять errno в flags нельзя. Комбинируйте флаги побитовым |, а не логическим &&.
| Константа (int) | Роль |
|---|---|
MSG_IPC_NOWAIT | Флаг receive: вернуть false сразу, если нет подходящего сообщения. Для send используется bool blocking=false, а не этот флаг. |
MSG_EAGAIN | Код ошибки в выходном error_code, например очередь полна у неблокирующего send. Не флаг. |
MSG_ENOMSG | Код ошибки receive при отсутствии подходящего сообщения и NOWAIT. Не результат функции вместо bool. |
MSG_NOERROR | Флаг receive: при коротком max_message_size обрезать тело; остаток теряется. Для JSON/заказов обычно не подходит. |
MSG_EXCEPT | Флаг receive: при положительном selector выбрать первый отличающийся тип. Зависит от поддержки ОС. |
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
$error = 0;
msg_send($queue, 1, 'служебное', false, false, $error);
msg_send($queue, 2, 'заказ', false, false, $error);
$type = 0; $message = '';
$flags = MSG_IPC_NOWAIT | MSG_EXCEPT;
if (!msg_receive($queue, 1, $type, 1024, $message, false, $flags, $error)) {
throw new RuntimeException('Не получено или MSG_EXCEPT не поддержан');
}
echo $type, ': ', $message, "\n";
} finally { msg_remove_queue($queue); }
Ожидаемый результат
2: заказ
На Unix с поддержкой MSG_EXCEPT. Отбрасывать ошибки лишь потому, что флаг существует в PHP, нельзя: поддержка системной операции тоже нужна. Сообщение типа 1 осталось в очереди до cleanup. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Кто владеет ресурсом и что означает «освободить»
| Операция | Что происходит и чего не происходит |
|---|---|
| unset объекта очереди | PHP теряет ручку; Unix очередь продолжает существовать. |
| msg_receive | Удаляет одно подходящее сообщение, не подтверждает выполнение бизнес-действия. |
| msg_remove_queue | Удаляет очередь вместе с непрочитанными сообщениями для всех участников. |
| sem_release | Возвращает захват данного объекта; набор остаётся. |
| sem_remove | Удаляет весь системный набор; не повседневная разблокировка. |
| shm_remove_var | Удаляет одну запись внутри сегмента. |
| shm_detach | Отсоединяет свой view. На Unix не удаляет сегмент. |
| shm_remove | На Unix помечает целый сегмент к удалению; освобождение после последнего detach. Windows mapping lifetime отличается. |
Общий ресурс может пережить HTTP запрос, поэтому не полагайтесь на PHP __destruct как на единственное место production cleanup. Аварийный kill/остановка хоста не гарантируют выполнение пользовательского кода. Очередь и память не являются долговечным журналом: перезагрузка/удаление/сбой могут потерять сведения. Восстанавливайте необходимые данные из надёжного источника, повторяйте действия по ID идемпотентно.
Ошибки, стоимость и выбор альтернативы
Измерять здесь надо не только длительность одной функции, но и ожидание, объём сериализации, длину очереди, пиковую память и долю повторов. Общая память не означает «нулевое копирование»: shm_get_var восстанавливает PHP структуру в процессе. В текущем sysvshm поиск записи идёт по хранимым блокам, а удаление сдвигает последующие данные; это не встроенный хеш-массив с гарантией постоянного времени по ключу. Большие часто заменяемые снимки могут оказаться дороже выбранного специализированного кеша.
| Симптом | Практическое направление |
|---|---|
| sem_acquire не возвращается | Не держите ли уже max=1 lock; одинаковый ли порядок разных locks; вернули ли пропуск в finally; не делается ли долгий запрос внутри участка. |
| msg_send: EAGAIN | Очередь полна: сравните скорость producer/consumer, max size receive, необрабатываемый тип, лимит qbytes. Контролируемый повтор/отказ, не бесконечный цикл. |
| Сообщение не приходит | Верный ли key/namespace/type и serialize договор; receive может ждать конкретный тип, пока другие уже есть. |
| shm_get_var вернул false | Проверьте наличие под тем же lock и договор типа; false может быть настоящим значением. |
| shm_put_var отказал | Размер сериализованного значения + накладные расходы, общий бюджет. Замена не гарантирует сохранение старого. |
| Call to undefined function | Проверить именно расширение/платформу/SAPI; sysvshm не предоставляет sem_get. |
Если задача — один локальный файл, рассмотрите файловую блокировку и файловые операции. Для устойчивых заказов — транзакцию БД. Для нескольких серверов — сетевую очередь/хранилище с формальным договором доставки, подтверждения и отказа. Для передачи байтов/запросов между процессами — sockets. Выбор определяется свойствами задачи; утверждение «shared memory всегда быстрее базы» не учитывает синхронизацию и стоимость копирования.
Проверить понимание на изменении требований
Задание 1: в примере lost update поставьте read B после write A. Решение: B прочитает 6 и сохранит 5; семафор именно принуждает участников к такому порядку. Задание 2: сохраните false под 101. Решение: has=true, get=false; не трактуйте это как отсутствие. Задание 3: отправьте raw JSON, а на чтении включите unserialize. Решение: это несовпадающие форматы; исправьте договор обеих сторон, не замалчивайте ошибку.
Задание 4: большой пакет остаётся в очереди. Решение: определить максимальный допустимый размер сообщения и читать в рамках этого договора; не включать NOERROR для структурированного заказа. Задание 5: reserve вызывается дважды под одним захватом. Решение: один владелец критического участка вызывает внутреннюю функцию без повторного acquire; не превращайте max=1 в 2 лишь для обхода deadlock. Далее: функции и параметры по ссылке, JSON контракт, границы компонентов сервиса.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
shm_attachОткрыть общую память для локальных обработчиков.
shm_attach(int $key, ?int $size = null, int $permissions = 0666): SysvSharedMemory|falseОткрыть общую память для локальных обработчиков.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| key | Системный ключ. 0 означает новый приватный сегмент. Одинаковый ненулевой ключ нужен участникам одного IPC договора. |
| size | Байты при создании; null берёт sysvshm.init_mem, по умолчанию 10000. С PHP 8.0 допускается null. |
| permissions | Права Unix при создании; 0666 разрешает чтение/запись всем пользователям, поэтому учебные примеры явно используют 0600. |
| Результат/отказ | Объект с PHP 8.0; раньше resource. false и warning при системном отказе, ValueError при size < 1. |
- key=0 → новый сегмент 16384 байта
- shm_put_var: запись 101 → 7
- shm_get_var: копия 7 в PHP
- remove + detach завершили владение
Представьте шкаф с пронумерованными ячейками: 101 содержит остаток 7. Другой процесс может обратиться к тому же шкафу. Но это не обычный PHP массив и не база данных: формат хранит сериализованные PHP значения, не имеет транзакций и требует согласованной блокировки. В минимальном примере ключ сегмента 0 создаёт отдельный учебный шкаф; 101 — совершенно другой ключ, номер записи внутри.
<?php
declare(strict_types=1);
$memory = shm_attach(0, 16384, 0600);
if ($memory === false) throw new RuntimeException('Нет общей памяти');
try {
if (!shm_put_var($memory, 101, 7)) throw new RuntimeException('Запись не удалась');
echo shm_get_var($memory, 101), "\n";
} finally { shm_remove($memory); shm_detach($memory); }
Ожидаемый результат
7
0 — приватный новый сегмент, 16384 — байты всего хранилища, 0600 — восьмеричные права владельца на Unix. Размер включает служебные данные и сериализацию. Код исполняется последовательно, конкурентных авторов нет.
<?php
declare(strict_types=1);
try { shm_attach(0, 0, 0600); }
catch (ValueError $error) { echo "Размер должен быть положительным\n"; }
Ожидаемый результат
Размер должен быть положительным
Нулевая ёмкость запрещена ещё до создания сегмента. Положительный размер тоже может быть недостаточен для заголовка либо превышать системный лимит.
Для процессов, запускаемых независимо, договоритесь о стабильном ключе через ftok и владельце жизненного цикла. Ключ 0 при каждом attach создаёт отдельный сегмент, поэтому два запуска с 0 не увидят данные друг друга. У существующего сегмента size/permissions не меняют его ёмкость и права. Подключение к случайному чужому ключу опасно: PHP ожидает свой формат и может инициализировать неподходящее содержимое. На Unix сегмент обычно переживает detach/завершение запроса; Windows реализация использует mappings и отличается по сроку жизни и правам.
Рядом: shm_put_var, shm_get_var, shm_remove, ftok.
shm_put_varПередать снимок PHP значения в общую память.
shm_put_var(SysvSharedMemory $shm, int $key, mixed $value): boolПередать снимок PHP значения в общую память.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| shm | Подключённый сегмент. После detach — Error. |
| key | Целочисленный ключ записи внутри сегмента, не IPC ключ. |
| value | Сериализуемое PHP значение. Повтор key заменяет запись. |
| Результат | true записано; false и warning при нехватке места. Запись/замена не является транзакцией. |
- $stock содержит A42=7
- serialize внутри расширения
- байты записаны под 101
- изменение $stock не меняет записанное
Хранить можно строку, число, массив или сериализуемый объект. Внутри значение превращается в байты. После успешной записи другой согласованный читатель получит восстановленное значение; изменение исходной переменной само по себе не меняет сегмент.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-memory.php';
$memory = lessonMemory();
try {
$stock = ['A42' => 7, 'B17' => 3];
if (!shm_put_var($memory, 101, $stock)) throw new RuntimeException('Не сохранено');
$stock['A42'] = 99;
echo shm_get_var($memory, 101)['A42'], "\n";
} finally { shm_remove($memory); shm_detach($memory); }
Ожидаемый результат
7
Запись — снимок, не привязка к $stock. Для публикации 99 требуется новая запись. Нет необходимости вручную serialize перед shm_put_var.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-memory.php';
$memory = lessonMemory(4096);
try {
if (!shm_put_var($memory, 101, 'старый снимок')) throw new RuntimeException('Нет места');
set_error_handler(static fn() => true, E_WARNING);
try { $saved = shm_put_var($memory, 101, str_repeat('x', 20000)); }
finally { restore_error_handler(); }
echo $saved ? "Записано\n" : "Новый снимок не записан\n";
echo shm_has_var($memory, 101) ? "Старый остался\n" : "Старого тоже нет\n";
} finally { shm_remove($memory); shm_detach($memory); }
Ожидаемый результат
Новый снимок не записан
Старого тоже нет
В PHP 8.5.11 реализация удаляет старую запись перед проверкой места для новой. Это не транзакционная замена: нельзя обещать читателю сохранение старого снимка при false. Warning подавлен только вокруг намеренного отказа и результат всё равно проверен.
Ограничивайте объём, учитывайте накладные расходы и проверяйте bool. Для критичных данных используйте транзакционное хранилище; для снимков можно публиковать проверенную версию под новым ключом и переключать указатель под тем же семафором, но это самостоятельный протокол, не гарантия функции. Resource подключения, файловый descriptor и lock не переносятся как рабочие ресурсы. Сериализация объектов может вызывать методы и выбрасывать исключения; не допускайте посторонних писателей.
Рядом: shm_get_var, shm_has_var, shm_remove_var, sem_acquire.
shm_get_varПолучить обычное PHP значение из общей записи.
shm_get_var(SysvSharedMemory $shm, int $key): mixedПолучить обычное PHP значение из общей записи.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| shm/key | Подключение и целый ключ записи. |
| Результат | Восстановленное значение любого поддержанного типа; false может быть реальным значением. |
| Отказы | Нет ключа/повреждение — warning и false; закрытое подключение — Error; hooks могут выбросить исключение. |
- В сегменте 101 → массив A42=7
- get создаёт PHP массив $stock
- локально уменьшаем до 6
- общая запись всё ещё 7
Общий снимок нужно прочитать в рабочую переменную, проверить её форму, затем обработать. Функция возвращает десериализованную копию. Она не возвращает ссылку на ячейку, которую можно незаметно поменять через оператор присваивания.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-memory.php';
$memory = lessonMemory();
try {
shm_put_var($memory, 101, ['A42' => 7]);
$stock = shm_get_var($memory, 101);
$stock['A42']--;
echo $stock['A42'], ' / ', shm_get_var($memory, 101)['A42'], "\n";
} finally { shm_remove($memory); shm_detach($memory); }
Ожидаемый результат
6 / 7
6 осталось в локальном массиве, 7 — в общей памяти. Прочитать → изменить → записать при нескольких авторах нужно под одним семафором на всём участке.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-memory.php';
$memory = lessonMemory();
try {
shm_put_var($memory, 102, false);
foreach ([102, 103] as $key) {
if (!shm_has_var($memory, $key)) { echo "$key: отсутствует\n"; continue; }
$value = shm_get_var($memory, $key);
echo "$key: ", $value === false ? 'сохранён false' : 'другое', "\n";
}
} finally { shm_remove($memory); shm_detach($memory); }
Ожидаемый результат
102: сохранён false
103: отсутствует
Один процесс, поэтому между has/get никто не меняет состояние. При конкуренции эта пара нуждается в общей блокировке. Сравнение результата с false в одиночку неоднозначно.
Отсутствующий ключ даёт warning и false; повреждённая сериализация тоже может дать warning/false, а объектные hooks — исключение. Не используйте разрешение записи всем пользователям ради удобства: чтение PHP сериализации из недоверенного сегмента может создать объекты и запустить их методы. Большой массив полностью восстанавливается в памяти каждого читателя: общая память не отменяет выделение памяти PHP и стоимость разбора.
Рядом: shm_put_var, shm_has_var, sem_acquire.
shm_has_varУзнать, есть ли запись с заданным ключом.
shm_has_var(SysvSharedMemory $shm, int $key): boolУзнать, есть ли запись с заданным ключом.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| key | Целочисленный ключ записи. |
| Результат | true существует, включая null/false; false отсутствует. |
| Граница | Снимок текущего состояния, не блокировка. После detach — Error. |
- Запись 101 содержит null
- has(101) → true
- has(102) → false
- перед совместным get нужна синхронизация
Не каждый обработчик успел положить своё состояние. Проверка наличия помогает различить «ещё не вычислено» и сохранённые null/false. Она не резервирует запись и не запрещает соседу удалить её через мгновение.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-memory.php';
$memory = lessonMemory();
try {
shm_put_var($memory, 101, null);
echo shm_has_var($memory, 101) ? "101 существует\n" : "101 нет\n";
echo shm_has_var($memory, 102) ? "102 существует\n" : "102 нет\n";
} finally { shm_remove($memory); shm_detach($memory); }
Ожидаемый результат
101 существует
102 нет
В отличие от isset обычного массива, shm_has_var видит существующую запись с null.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-memory.php';
$memory = lessonMemory();
try {
foreach ([1, 2] as $attempt) {
if (!shm_has_var($memory, 101)) {
shm_put_var($memory, 101, 0);
echo "Создан счётчик\n";
} else { echo "Счётчик уже есть\n"; }
}
} finally { shm_remove($memory); shm_detach($memory); }
Ожидаемый результат
Создан счётчик
Счётчик уже есть
Два последовательных прохода показывают условие. Это ещё не конкурентный create-if-absent. Для реальных workers весь if и put защищаются одним общим семафором.
Рядом: shm_get_var, shm_put_var, sem_acquire.
shm_remove_varОсвободить одну запись, сохранив остальной сегмент.
shm_remove_var(SysvSharedMemory $shm, int $key): boolОсвободить одну запись, сохранив остальной сегмент.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| shm/key | Подключение и целый ключ удаляемой записи. |
| Результат | true удалено; missing key — warning и false. |
| Граница | Не уменьшает размер системного сегмента; освобождает место внутри него. Detached объект — Error. |
- 101=импорт A, 102=импорт B
- remove_var(101)
- 101 нет, 102 доступна
- место 101 доступно новым записям
Импорт партии закончился: временная запись 101 больше не нужна, а соседняя 102 должна остаться. Удаляется только выбранное значение и освобождается место внутри PHP хранилища.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-memory.php';
$memory = lessonMemory();
try {
shm_put_var($memory, 101, 'импорт A');
shm_put_var($memory, 102, 'импорт B');
if (!shm_remove_var($memory, 101)) throw new RuntimeException('Не удалено');
echo shm_has_var($memory, 101) ? "A есть\n" : "A удалён\n";
echo shm_get_var($memory, 102), "\n";
} finally { shm_remove($memory); shm_detach($memory); }
Ожидаемый результат
A удалён
импорт B
Сегмент продолжает работать. shm_remove_var не удаляет IPC ресурс целиком.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-memory.php';
$memory = lessonMemory();
try {
shm_put_var($memory, 101, 'готово');
foreach ([1, 2] as $attempt) {
if (!shm_has_var($memory, 101)) { echo "Уже очищено\n"; continue; }
if (!shm_remove_var($memory, 101)) throw new RuntimeException('Очистка не удалась');
echo "Очищено\n";
}
} finally { shm_remove($memory); shm_detach($memory); }
Ожидаемый результат
Очищено
Уже очищено
Для отсутствующего ключа remove_var сам выдаёт warning и false. Здесь guard делает повторную последовательную очистку понятной; конкурентная проверка/удаление требует lock.
Рядом: shm_has_var, shm_put_var, shm_remove.
shm_detachЗакрыть своё подключение к общей памяти.
shm_detach(SysvSharedMemory $shm): trueЗакрыть своё подключение к общей памяти.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| shm | Ещё подключённый объект. |
| Результат | С PHP 8.0 точный тип true; раньше bool. |
| Ошибки | Повторное detach либо работа через закрытый объект — Error. |
| Срок жизни | Отсоединение не заменяет shm_remove на Unix. Не переносите Unix lifetime на Windows mappings без проверки. |
- Обработчик прочитал 101
- detach закрывает его view
- общий Unix сегмент остаётся
- владелец удаляет после остановки всех участников
Обработчик закончил читать и освобождает своё подключение. Это похоже на закрытие читателем двери в шкаф, а не на выбрасывание общего шкафа. На Unix остальные участники продолжают работать, данные не удаляются автоматически.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-memory.php';
$memory = lessonMemory();
try { shm_put_var($memory, 101, 7); }
finally {
if (!shm_remove($memory)) throw new RuntimeException('Не отмечен к удалению');
echo shm_detach($memory) ? "Подключение закрыто\n" : "Отказ\n";
}
Ожидаемый результат
Подключение закрыто
Владелец сначала помечает ресурс к удалению, пока ручка ещё действительна, затем отсоединяется. В обычном читателе общего Unix сегмента нужен только detach, удаление поручено владельцу.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-memory.php';
$memory = lessonMemory();
shm_remove($memory);
shm_detach($memory);
try { shm_has_var($memory, 101); }
catch (Error $error) { echo "Подключение уже закрыто\n"; }
Ожидаемый результат
Подключение уже закрыто
Сам объект переменной существует, но доступа к памяти уже нет. Это не проверяется через isset($memory).
Рядом: shm_attach, shm_remove.
shm_removeЗавершить жизнь системного сегмента после работы участников.
shm_remove(SysvSharedMemory $shm): boolЗавершить жизнь системного сегмента после работы участников.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| shm | Активная ручка сегмента, которым вы вправе управлять. |
| Результат | true ОС приняла удаление; false с warning при отказе. |
| Ошибки/порядок | Detached объект — Error. Сначала remove, потом detach; не наоборот. |
| Unix | Существующие attachment могут жить до detach; новые подключения и IPC_RMID зависят от ОС. Не строить переносимый протокол на подключении уже удалённого сегмента. |
- Владелец прекращает новые задания
- Все workers завершены
- remove помечает сегмент
- последний detach освобождает Unix ресурс
Общий сегмент принадлежит приложению, а не каждому читателю. Команда завершения сервиса сначала останавливает новых участников, дожидается текущих и только затем удаляет ресурс. Иначе ещё работающий worker может потерять общий договор.
<?php
declare(strict_types=1);
$memory = shm_attach(0, 16384, 0600);
if ($memory === false) throw new RuntimeException('Не создано');
try {
echo shm_remove($memory) ? "Удаление запрошено\n" : "Удаление не удалось\n";
} finally { shm_detach($memory); }
Ожидаемый результат
Удаление запрошено
На Unix IPC_RMID помечает сегмент; фактическое освобождение после последнего detach. Пример не перебирает системные ключи и не затрагивает чужую память.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-memory.php';
$memory = lessonMemory();
try {
shm_put_var($memory, 101, 'черновик');
shm_put_var($memory, 102, 'итог');
shm_remove_var($memory, 101);
echo shm_get_var($memory, 102), "\n";
echo "После чтения владелец завершит весь сегмент\n";
} finally { shm_remove($memory); shm_detach($memory); }
Ожидаемый результат
итог
После чтения владелец завершит весь сегмент
В finally helper-примера вызываются remove и detach. До него запись 102 доступна; remove_var(101) не равнозначен remove($memory).
Рядом: shm_detach, shm_remove_var, shm_attach.
msg_get_queueНайти либо создать очередь локальных сообщений.
msg_get_queue(int $key, int $permissions = 0666): SysvMessageQueue|falseНайти либо создать очередь локальных сообщений.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| key | Целый системный ключ; одинаковый ненулевой key связывает процессы в одном namespace. 0 создаёт приватную очередь. |
| permissions | Восьмеричные Unix права при создании. Не менять глобально на 0666 для обхода отказа. |
| Результат | SysvMessageQueue с PHP 8.0; раньше resource. false и warning при системном отказе. |
- Сервис выбрал свой IPC ключ
- get вернул ручку очереди
- producer посылает заказ 42
- worker получает через ту же очередь
Производитель задания и обработчик должны найти одну очередь. Число key — адрес в IPC namespace ОС, не ID заказа. Для приложения можно получить его через ftok и хранить правила владения в конфигурации. Минимальные учебные примеры используют отдельную приватную очередь: каждый запуск создаёт свою и гарантированно удаляет её в finally.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
$info = msg_stat_queue($queue);
if ($info === false) throw new RuntimeException('Нет состояния');
echo 'Заданий: ', $info['msg_qnum'], "\n";
} finally { msg_remove_queue($queue); }
Ожидаемый результат
Заданий: 0
Файл ipc-queue.php входит в пример. Он создаёт key=0 и явно назначает права 0600. Особенность текущей реализации: первый msgget(0,0) уже создаёт приватную очередь без указанных прав; поэтому helper вызывает msg_set_queue до отправки. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
$key = ftok(__DIR__ . '/app.ipc', 'Q');
if ($key === -1 || $key === 0) throw new RuntimeException('Не получен общий ключ');
$queue = msg_get_queue($key, 0600);
if ($queue === false) throw new RuntimeException('Очередь недоступна');
echo $queue instanceof SysvMessageQueue ? "Очередь подключена\n" : "Ошибка\n";
// Worker does NOT remove the application's shared queue here.
Ожидаемый результат
Очередь подключена
Контекст: выделенный Unix IPC namespace приложения, файл app.ipc заранее создал владелец; владелец также отвечает за удаление очереди после остановки workers. Пример worker не вызывает remove при каждом завершении. Существующий ключ может обозначать чужой объект: ftok не даёт гарантии уникальности. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Если очередь существует, permissions не перенастраивают её. Если её нет, get создаёт очередь при достаточных правах; это не только «открыть существующую». Состояние и сообщения живут в ОС до удаления, даже когда объект PHP исчез. Лимиты на количество очередей и объём задаёт система. Смена ключа при каждом запросе без удаления приводит к накоплению ресурсов, а не к изоляции приложения.
Рядом: msg_send, msg_receive, msg_remove_queue, ftok.
msg_sendПередать задание в очередь и явно выбрать ожидание.
msg_send(SysvMessageQueue $queue, int $message_type, mixed $message, bool $serialize = true, bool $blocking = true, &$error_code = null): boolПередать задание в очередь и явно выбрать ожидание.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| queue/type | Ручка очереди, положительное целое для категории сообщения. |
| message/serialize | true: PHP сериализация; false: сырая строка либо скаляр, превращаемый в строку. |
| blocking | true ждёт при заполнении; false немедленно сообщает отказ. |
| error_code/результат | true принято ОС, false отказ. При false errno записан в переданную переменную; MSG_EAGAIN может означать заполнение в nonblocking режиме. |
- orderId=42 → JSON строка
- send(type=2, raw, nowait)
- байты находятся в очереди
- receive → JSON decode → проверка
Чтобы попросить фоновый обработчик обновить остаток, передадим строку «заказ 42» сообщением типа 1. Тип — положительное целое для отбора сообщений; он не является PHP типом string/int. Сериализация — способ упаковать значение в байты; raw строка пригодна и для другой программы, PHP serialize — только для согласованных доверенных PHP участников.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
$error = 0;
if (!msg_send($queue, 1, 'заказ 42', false, false, $error)) {
throw new RuntimeException('Не отправлено, errno=' . $error);
}
$type = 0; $message = ''; $error = 0;
if (!msg_receive($queue, 1, $type, 1024, $message, false, MSG_IPC_NOWAIT, $error)) {
throw new RuntimeException('Не получено');
}
echo $type, ': ', $message, "\n";
} finally { msg_remove_queue($queue); }
Ожидаемый результат
1: заказ 42
Первое false отключает PHP сериализацию, второе запрещает ждать заполненную очередь. $error — выходной аргумент по ссылке: функция сама записывает системный errno при отказе. Получатель использует соответствующее unserialize=false. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
$job = ['orderId' => 42, 'action' => 'reserve'];
$json = json_encode($job, JSON_THROW_ON_ERROR);
$error = 0;
if (!msg_send($queue, 2, $json, false, false, $error)) {
throw new RuntimeException($error === MSG_EAGAIN ? 'Очередь заполнена' : 'Ошибка отправки');
}
$type = 0; $payload = '';
if (!msg_receive($queue, 2, $type, 4096, $payload, false, MSG_IPC_NOWAIT, $error)) {
throw new RuntimeException('Нет задания');
}
$received = json_decode($payload, true, 512, JSON_THROW_ON_ERROR);
if (!is_array($received) || !is_int($received['orderId'] ?? null)) {
throw new RuntimeException('Неверный контракт');
}
echo 'Обработать заказ ', $received['orderId'], "\n";
} finally { msg_remove_queue($queue); }
Ожидаемый результат
Обработать заказ 42
JSON кодирует структуру в строку; msg_send передаёт байты, а не HTTP. Получатель проверяет поля после JSON разбора. При MSG_EAGAIN приложение сохраняет задание для контролируемого повтора либо честно возвращает занятость. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
$error = 0;
if (!msg_send($queue, 1, ['orderId' => 42, 'quantity' => 2], true, false, $error)) {
throw new RuntimeException('Массив не отправлен');
}
$type = 0; $job = null;
if (!msg_receive($queue, 1, $type, 4096, $job, true, MSG_IPC_NOWAIT, $error)) {
throw new RuntimeException('Массив не получен');
}
if (!is_array($job) || !is_int($job['quantity'] ?? null)) throw new RuntimeException('Неверный контракт');
echo 'Количество: ', $job['quantity'], "\n";
} finally { msg_remove_queue($queue); }
Ожидаемый результат
Количество: 2
Обе стороны согласовали PHP serialize/unserialize, поэтому ручного JSON разбора нет. Формат подходит только доверенным PHP участникам; разрешение посторонним писать сериализованные объекты недопустимо. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
С serialize=true по умолчанию можно передать массив: расширение сериализует его. В PHP 8.5 аргумент message фактически mixed для этого режима, хотя краткая таблица manual показывает скалярный union. При serialize=false допустимы string/int/float/bool, а массив вызывает TypeError. bool преобразуется в "1"/"0"; для межъязыкового договора лучше явно выбрать формат, а не полагаться на приведение. blocking=true ждёт свободного места; это может занять HTTP worker надолго. Максимальный размер одного сообщения и общая ёмкость очереди — разные лимиты. false сопровождается errno в выходном аргументе, а не значением MSG_EAGAIN вместо bool.
Рядом: msg_receive, msg_stat_queue, msg_set_queue.
msg_receiveИзвлечь подходящее сообщение с контролем размера и ожидания.
msg_receive(SysvMessageQueue $queue, int $desired_message_type, &$received_message_type, int $max_message_size, mixed &$message, bool $unserialize = true, int $flags = 0, &$error_code = null): boolИзвлечь подходящее сообщение с контролем размера и ожидания.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| queue/desired_message_type | Очередь и правило отбора: 0, положительный или отрицательный selector. |
| received_message_type/message | Переменные для типа и тела полученного сообщения. |
| max_message_size | Положительный размер тела в байтах; переполнение без NOERROR оставляет сообщение. |
| unserialize/flags | Восстанавливать PHP значение либо вернуть binary-safe строку. NOWAIT не ждёт; EXCEPT инвертирует положительный тип; NOERROR обрезает. |
| Результат/errno | bool. При отказе errno в error_code. MSG_ENOMSG — нет подходящего сообщения в NOWAIT. |
- В очереди type3, type1, type2
- receive(-3) выбирает type1
- удаляет его из очереди
- сохраняет тип и текст в выходные переменные
Получение не просто заглядывает в очередь: выбранное сообщение извлекается из неё. Если обработчик аварийно завершится после receive, система сама не вернёт задание обратно. Для оплаченных заказов нужен отдельный протокол подтверждения/долговечное хранилище, а для простого локального сигнала часто достаточно такого обмена.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
$error = 0;
foreach ([3 => 'обычное', 1 => 'срочное', 2 => 'среднее'] as $type => $text) {
if (!msg_send($queue, $type, $text, false, false, $error)) throw new RuntimeException('Не отправлено');
}
$type = 0; $message = '';
if (!msg_receive($queue, -3, $type, 1024, $message, false, MSG_IPC_NOWAIT, $error)) {
throw new RuntimeException('Нет подходящего сообщения');
}
echo $type, ': ', $message, "\n";
} finally { msg_remove_queue($queue); }
Ожидаемый результат
1: срочное
На Unix отрицательный selector -3 выбирает первый элемент наименьшего типа ≤ 3. Это не первое по времени сообщение любого типа ≤ 3. Частые приоритетные задания могут оттеснять обычные; нужна политика справедливости. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
$type = 0; $message = null; $error = 0;
$received = msg_receive($queue, 0, $type, 4096, $message, false, MSG_IPC_NOWAIT, $error);
if (!$received && $error === MSG_ENOMSG) {
echo "Сейчас нет заданий\n";
} elseif (!$received) { throw new RuntimeException('Ошибка очереди: ' . $error); }
else { echo "Получено задание\n"; }
} finally { msg_remove_queue($queue); }
Ожидаемый результат
Сейчас нет заданий
MSG_ENOMSG находится в $error, возвращаемое значение функции — false. Не читайте старое значение $message после отказа и не крутите безостановочный busy loop. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
$error = 0;
msg_send($queue, 1, 'ORDER-42', false, false, $error);
$type = 0; $message = '';
$short = msg_receive($queue, 1, $type, 3, $message, false, MSG_IPC_NOWAIT, $error);
echo $short ? "Прочитано\n" : "Буфер мал, сообщение остаётся\n";
if (!msg_receive($queue, 1, $type, 32, $message, false, MSG_IPC_NOWAIT, $error)) {
throw new RuntimeException('Повторное чтение не удалось');
}
echo $message, "\n";
} finally { msg_remove_queue($queue); }
Ожидаемый результат
Буфер мал, сообщение остаётся
ORDER-42
Без MSG_NOERROR слишком длинное сообщение не извлекается (E2BIG). Повтор с тем же малым буфером не поможет. С MSG_NOERROR хвост был бы отброшен навсегда: для JSON заказа такой режим неприемлем. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
| Аргумент selector | Какие сообщения выбираются |
|---|---|
| 0 | Первое в очереди. |
| Положительный N | Первое с типом N. |
| Отрицательный -N | Первое с минимальным типом среди типов ≤ N. |
| N > 0 и MSG_EXCEPT | Первое с типом, не равным N; поддержка зависит от системы. |
received_message_type, message и error_code — выходные параметры: передают переменные, не литералы. max_message_size задаёт байты тела, без системного поля type; значение ≤ 0 вызывает ValueError. unserialize=true должно соответствовать send serialize=true; raw JSON читается с false. Доверяйте только своим сериализованным сообщениям: PHP объекты могут вызвать hooks. При ошибке десериализации сообщение уже извлечено, повтор receive его не восстановит. Успех обновляет last receiver PID/time и уменьшает msg_qnum.
Рядом: msg_send, msg_stat_queue, msg_remove_queue.
msg_queue_existsПроверить наличие очереди без создания.
msg_queue_exists(int $key): boolПроверить наличие очереди без создания.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| key | Заранее согласованный IPC ключ приложения. |
| Результат | true очередь найдена, false не найдена/системный вызов не смог её получить. |
| Ограничение | Не возвращает объект, не проверяет весь рабочий договор и не защищает от гонки. |
- ftok дал ключ приложения
- exists только проверяет
- другой процесс может поменять состояние
- операции всё равно проверяют свой результат
Для диагностики сервиса полезно узнать, есть ли уже очередь под согласованным ключом. Это проверка одного момента времени. Между проверкой и get другой процесс может удалить/создать её; поэтому exists не превращает get в атомарную операцию «открыть только старую».
<?php
declare(strict_types=1);
$key = ftok(__DIR__ . '/app.ipc', 'Q');
if ($key === -1 || $key === 0) throw new RuntimeException('Ключ не получен');
echo msg_queue_exists($key) ? "Очередь уже есть\n" : "Очередь пока не найдена\n";
Ожидаемый результат
Очередь уже есть ИЛИ Очередь пока не найдена (зависит от стенда).
Только проверка согласованного ключа, без создания и удаления. false не доказывает, что у процесса достаточно прав, чтобы создать очередь. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
$key = ftok(__DIR__ . '/app.ipc', 'Q');
if ($key === -1 || $key === 0) throw new RuntimeException('Ключ не получен');
if (!msg_queue_exists($key)) { echo "Попросите владельца запустить сервис\n"; }
else {
// An external shutdown may happen after the condition.
echo "Нужны ещё проверки результатов get/send/receive\n";
}
Ожидаемый результат
Одна из двух диагностических строк в зависимости от состояния стенда.
В production выбранный supervisor управляет жизнью очереди; worker всё равно проверяет каждый вызов. Нельзя удалять очередь только потому, что она «уже существует». Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Рядом: msg_get_queue, ftok, msg_stat_queue.
msg_stat_queueПолучить счётчики и параметры очереди.
msg_stat_queue(SysvMessageQueue $queue): array|falseПолучить счётчики и параметры очереди.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| queue | Действующая очередь. |
| Результат | Массив метаданных либо false при отказе. |
| Динамика | Состояние меняется независимо от полученного массива. |
- send добавил заказ42
- qnum=1
- receive забрал заказ42
- qnum=0
Длина очереди помогает увидеть отставание обработчиков. Метаданные возвращаются обычным массивом с ключами вроде msg_qnum. Значения timestamp/PID меняются при работе; это диагностика, а не надёжное определение автора конкретного уже извлечённого сообщения при нескольких producers.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
$error = 0;
msg_send($queue, 1, 'заказ 42', false, false, $error);
$before = msg_stat_queue($queue);
$type = 0; $message = '';
msg_receive($queue, 1, $type, 1024, $message, false, MSG_IPC_NOWAIT, $error);
$after = msg_stat_queue($queue);
if ($before === false || $after === false) throw new RuntimeException('Нет статистики');
echo $before['msg_qnum'], ' → ', $after['msg_qnum'], "\n";
} finally { msg_remove_queue($queue); }
Ожидаемый результат
1 → 0
При активных других workers счётчик может измениться сразу после чтения. Он не гарантирует успешность следующего receive. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
$info = msg_stat_queue($queue);
if ($info === false) throw new RuntimeException('Нет состояния');
printf("Права: %04o\n", $info['msg_perm.mode'] & 0777);
echo $info['msg_qbytes'] > 0 ? "Ёмкость назначена ОС\n" : "Проверьте лимит\n";
} finally { msg_remove_queue($queue); }
Ожидаемый результат
Права: 0600
Ёмкость назначена ОС
msg_qbytes — ёмкость очереди в байтах, не длина текущего сообщения и не число элементов. Не зашиваем системный лимит в ожидаемый результат. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
| Все поля | Назначение |
|---|---|
| msg_perm.uid / msg_perm.gid | Числовые UID/GID владельца. |
| msg_perm.mode | Биты Unix доступа. |
| msg_stime / msg_rtime / msg_ctime | Время последней отправки, приёма, изменения структуры. |
| msg_qnum | Число сообщений, ожидающих чтения. |
| msg_qbytes | Максимальное число байтов в очереди. |
| msg_lspid / msg_lrpid | PID последнего отправителя/получателя. |
Рядом: msg_set_queue, msg_send, msg_receive.
msg_set_queueИзменить разрешённые параметры очереди владельцем.
msg_set_queue(SysvMessageQueue $queue, array $data): boolИзменить разрешённые параметры очереди владельцем.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| data | Поддержаны msg_perm.uid, msg_perm.gid, msg_perm.mode, msg_qbytes. Остальные поля не являются настройками. |
| Права | Нужны полномочия владельца/создателя либо соответствующие системные привилегии; смена UID/GID дополнительно ограничивается ОС. |
| Лимиты | Повышение msg_qbytes выше системного ограничения требует привилегий; не запускать весь PHP сайт как root. |
| Результат | true применено; false отказ. Проверяйте stat после изменения при необходимости. |
- Очередь принадлежит сервису
- data задаёт только mode/ёмкость
- ОС проверяет полномочия
- stat показывает применённый бюджет
Владелец сервиса задаёт доступ и бюджет очереди. Для этого передают массив только с изменяемыми полями. Не нужно копировать всю структуру stat и пытаться изменить счётчики сообщений или PID — они принадлежат ОС.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
if (!msg_set_queue($queue, ['msg_perm.mode' => 0600])) throw new RuntimeException('Нет прав');
$state = msg_stat_queue($queue);
if ($state === false) throw new RuntimeException('Нет состояния');
printf("Доступ %04o\n", $state['msg_perm.mode'] & 0777);
} finally { msg_remove_queue($queue); }
Ожидаемый результат
Доступ 0600
0600 — запись в восьмеричной системе. Передача десятичного 600 означает другие биты. Разрешения регулируют доступ локальных пользователей, не сетевую авторизацию. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
$state = msg_stat_queue($queue);
if ($state === false || $state['msg_qbytes'] < 1) throw new RuntimeException('Нет лимита');
$budget = min(1024, $state['msg_qbytes']);
if (!msg_set_queue($queue, ['msg_qbytes' => $budget])) throw new RuntimeException('Лимит не изменён');
$changed = msg_stat_queue($queue);
if ($changed === false) throw new RuntimeException('Нет состояния');
echo $changed['msg_qbytes'] === $budget ? "Бюджет применён\n" : "Проверьте ОС\n";
} finally { msg_remove_queue($queue); }
Ожидаемый результат
Бюджет применён
Пример не повышает системные лимиты и не требует root. Ограничение создаёт обратное давление; оно не исправляет медленный worker и не увеличивает лимит одного сообщения. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Рядом: msg_stat_queue, msg_get_queue, msg_send.
msg_remove_queueУдалить системную очередь и оставшиеся сообщения.
msg_remove_queue(SysvMessageQueue $queue): boolУдалить системную очередь и оставшиеся сообщения.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| queue | Ресурс, жизненным циклом которого вы управляете. |
| Результат | true удалён; false при системном отказе, включая отсутствие/недостаточные права. |
| Побочный эффект | Все ожидающие сообщения удалены; blocked отправители/получатели получают ошибку от ОС. |
- Остановить producers
- Завершить/сохранить задания
- Дождаться workers
- владелец удаляет очередь
Удалять очередь следует после завершения всех её участников. Очередь вместе с непрочитанными сообщениями исчезает: это не аналог «закрыть своё подключение». Освобождение PHP переменной не удаляет очередь ОС.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try { echo "Эксперимент закончен\n"; }
finally {
if (!msg_remove_queue($queue)) throw new RuntimeException('Очередь не удалена');
echo "Очередь удалена владельцем\n";
}
Ожидаемый результат
Эксперимент закончен
Очередь удалена владельцем
В finally удаляется только очередь, созданная этим запуском с приватным ключом. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
$error = 0;
if (!msg_send($queue, 1, 'учебное задание', false, false, $error)) throw new RuntimeException('Не отправлено');
$state = msg_stat_queue($queue);
if ($state === false) throw new RuntimeException('Нет состояния');
echo 'Будет потеряно сообщений: ', $state['msg_qnum'], "\n";
} finally { msg_remove_queue($queue); }
Ожидаемый результат
Будет потеряно сообщений: 1
Это специально созданное учебное сообщение. Для настоящей остановки решите заранее: дождаться обработки, сохранить задания или отменить их по явному договору; remove не сохраняет их автоматически. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Рядом: msg_get_queue, msg_stat_queue.
SysvMessageQueueНепрозрачная ручка очереди в PHP 8.
final class SysvMessageQueueНепрозрачная ручка очереди в PHP 8.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| Создание | msg_get_queue возвращает объект либо false. |
| Использование | Передавайте в msg_send/receive/stat/set/remove. |
| Ограничения | Нет пользовательских методов, конструктора, динамических свойств; объект не сериализуется и не клонируется. |
- get нашёл очередь
- вернул SysvMessageQueue
- send получил эту ручку
- remove меняет жизнь ресурса для всех
Объект обозначает очередь ОС. У него нет метода send: отправка остаётся отдельной функцией msg_send($queue, ...). Он final, без публичного конструктора и сериализации; это конкретный способ расширения хранить системный descriptor, а не шаблон архитектуры вашего приложения.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-queue.php';
$queue = lessonQueue();
try {
echo $queue instanceof SysvMessageQueue ? "Ручка очереди\n" : "Другой тип\n";
} finally { msg_remove_queue($queue); }
Ожидаемый результат
Ручка очереди
До PHP 8 использовался resource. Обновляйте проверки вместе с версией PHP. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
try { new SysvMessageQueue(); }
catch (Error $error) { echo "Создавайте очередь через msg_get_queue\n"; }
Ожидаемый результат
Создавайте очередь через msg_get_queue
Здесь системный ресурс не создаётся. Получение валидной ручки требует ключа и проверки системного результата. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Рядом: msg_get_queue, msg_send, msg_remove_queue.
sem_getСоздать либо открыть набор пропусков для взаимного исключения.
sem_get(int $key, int $max_acquire = 1, int $permissions = 0666, bool $auto_release = true): SysvSemaphore|falseСоздать либо открыть набор пропусков для взаимного исключения.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| key | Системный ключ, 0 всегда создаёт новый приватный набор. |
| max_acquire | Максимум одновременно удерживаемых пропусков; 1 для mutex. Приложение должно отклонять значения < 1. |
| permissions | Unix права при создании. |
| auto_release | Автоматически освободить захваты этого объекта при завершении его жизни; с PHP 8.0 bool (ранее int). |
| Результат | SysvSemaphore с PHP 8.0; раньше resource. false и warning при системном отказе; проверяйте также результат acquire. |
- Вызов get создал один пропуск
- A acquire получил его
- B nowait получил false
- A release вернул пропуск
Остался один товар, и два обработчика хотят его зарезервировать. Если оба прочитают «1» до записи, оба решат, что товар доступен. Семафор — выдаваемый ОС пропуск в участок кода. При max_acquire=1 только один участник одновременно выполняет чтение, проверку и запись; все участники должны использовать тот же ключ и соблюдать правило.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-semaphore.php';
$semaphore = lessonSemaphore(1);
try {
if (!sem_acquire($semaphore, true)) throw new RuntimeException('Пропуск недоступен');
try { echo "Внутри защищённого участка\n"; }
finally { sem_release($semaphore); }
} finally { sem_remove($semaphore); }
Ожидаемый результат
Внутри защищённого участка
key=0 даёт новый приватный семафор; max_acquire=1, 0600 и auto_release=true переданы в helper. Один вызов get ещё ничего не блокирует: пропуск запрашивается через acquire. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-semaphore.php';
$semaphore = lessonSemaphore(2);
try {
$held = 0;
try {
for ($attempt = 1; $attempt <= 3; $attempt++) {
if (sem_acquire($semaphore, true)) { $held++; echo "$attempt: пропуск\n"; }
else { echo "$attempt: пока нет места\n"; }
}
} finally {
while ($held > 0) { sem_release($semaphore); $held--; }
}
} finally { sem_remove($semaphore); }
Ожидаемый результат
1: пропуск
2: пропуск
3: пока нет места
Один процесс последовательно занимает два доступных слота, чтобы показать счётчик без fork. Это ограничитель параллельности, не ограничитель запросов в секунду: для временного rate limit нужна другая логика. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Для независимых workers key должен быть одинаковым ненулевым и принадлежать вашему приложению. max_acquire задайте положительным и одинаковым у участников; не используйте sem_get для безопасной перенастройки активного пула. PHP использует набор из трёх системных семафоров, поэтому при взаимодействии с C нельзя подменить его произвольным набором из одного. auto_release=true возвращает захваченные этим объектом разрешения при его освобождении; это страховка, а не замена явному finally. Оно не удаляет системный набор.
Рядом: sem_acquire, sem_release, sem_remove, ftok.
sem_acquireПолучить пропуск перед операцией, которую нельзя выполнять одновременно.
sem_acquire(SysvSemaphore $semaphore, bool $non_blocking = false): boolПолучить пропуск перед операцией, которую нельзя выполнять одновременно.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| semaphore | Ручка общего для участников набора. |
| non_blocking | false ждёт разрешения, true возвращает управление сразу. Параметр появился в PHP 5.6.1. |
| Результат | true получен пропуск; false занят в NOWAIT либо другая системная ошибка. При некоторых ошибках warning, errno параметра нет. |
| Баланс | Каждый успешный acquire требует release. false не следует «освобождать». |
- Проверить acquire=true
- прочитать текущие данные
- проверить и записать новое
- finally release
После acquire защищённым является только код, который все участники выполняют по одному договору. Если второй worker пишет в сегмент без acquire, семафор физически не запрещает ему запись. Поэтому lock охватывает всю операцию read → validate → modify → write, а не только последнюю строку.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-semaphore.php';
$semaphore = lessonSemaphore(1);
try {
if (!sem_acquire($semaphore, true)) throw new RuntimeException('Первый захват не удался');
try {
if (!sem_acquire($semaphore, true)) echo "Занято: отложить действие\n";
else { sem_release($semaphore); throw new RuntimeException('Неожиданный второй пропуск'); }
} finally { sem_release($semaphore); }
} finally { sem_remove($semaphore); }
Ожидаемый результат
Занято: отложить действие
При max=1 повторный блокирующий acquire этим же кодом ждал бы разрешения, которое сам код не успеет вернуть. Семафор не является рекурсивной блокировкой. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-semaphore.php';
$semaphore = lessonSemaphore(1);
try {
if (!sem_acquire($semaphore, true)) throw new RuntimeException('Занято');
try {
try { throw new RuntimeException('Неверная строка импорта'); }
finally { sem_release($semaphore); }
} catch (RuntimeException $error) { echo "Ошибка разобрана\n"; }
if (!sem_acquire($semaphore, true)) throw new RuntimeException('Пропуск потерян');
try { echo "Следующий импорт может работать\n"; }
finally { sem_release($semaphore); }
} finally { sem_remove($semaphore); }
Ожидаемый результат
Ошибка разобрана
Следующий импорт может работать
Внешний catch отвечает за смысл ошибки, внутренний finally — за освобождение. Не удерживайте lock во время долгого HTTP запроса, если можно подготовить данные заранее и кратко опубликовать результат. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Рядом: sem_get, sem_release, shm_put_var.
sem_releaseВернуть ранее полученный пропуск.
sem_release(SysvSemaphore $semaphore): boolВернуть ранее полученный пропуск.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| semaphore | Тот объект, который успешно приобрёл пропуск. |
| Результат | true освобождён; false и обычно warning, если нет захвата/ресурс исчез/системный отказ. |
| Граница | Не удаляет набор, не отменяет данные, уже записанные в критическом участке. |
- acquire уменьшил доступную ёмкость
- операция завершилась
- release увеличил ёмкость
- следующий worker получает пропуск
Возвращение пропуска позволяет следующему worker продолжить работу. Оно не удаляет системный семафор. Освобождать нужно тем объектом, которым выполнен успешный захват: расширение учитывает количество захватов каждого PHP объекта.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-semaphore.php';
$semaphore = lessonSemaphore(1);
try {
foreach (['импорт', 'отчёт'] as $operation) {
if (!sem_acquire($semaphore, true)) throw new RuntimeException('Занято');
try { echo $operation, "\n"; }
finally {
if (!sem_release($semaphore)) throw new RuntimeException('Пропуск не возвращён');
}
}
} finally { sem_remove($semaphore); }
Ожидаемый результат
импорт
отчёт
Обе операции используют один набор. Они выполняются последовательно и не требуют создать новый семафор после release. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-semaphore.php';
$semaphore = lessonSemaphore(1);
try {
set_error_handler(static fn() => true, E_WARNING);
try { $released = sem_release($semaphore); }
finally { restore_error_handler(); }
echo $released ? "Освобождён\n" : "Нечего освобождать\n";
} finally { sem_remove($semaphore); }
Ожидаемый результат
Нечего освобождать
Намеренно не было acquire. Функция сообщает warning и false; перехватывается только ожидаемый warning в этом эксперименте. Release не является способом «на всякий случай разблокировать чужой процесс». Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Рядом: sem_acquire, sem_remove.
sem_removeУдалить системный набор после завершения всех участников.
sem_remove(SysvSemaphore $semaphore): boolУдалить системный набор после завершения всех участников.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| semaphore | Набор, которым вы вправе управлять. |
| Результат | true удалён; false и warning при отсутствии/отказе прав/ошибке ОС. |
| Последствия | Ресурс исчезает для всех, ожидающие операции завершаются ошибкой. PHP переменная не становится автоматически null. |
- Завершены новые и текущие задания
- каждый worker отпустил свои захваты
- владелец remove
- ключ свободен для другого жизненного цикла
После остановки всех обработчиков владелец приложения удаляет системный набор. После каждого HTTP запроса вызывать remove нельзя: соседи могут ждать/использовать тот же semaphore. Для повседневной работы нужен release, для окончательного завершения — remove.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-semaphore.php';
$semaphore = lessonSemaphore();
try { echo "Участников больше нет\n"; }
finally {
if (!sem_remove($semaphore)) throw new RuntimeException('Набор не удалён');
echo "Набор удалён\n";
}
Ожидаемый результат
Участников больше нет
Набор удалён
Приватный key=0 гарантирует, что это ресурс текущего примера. Согласованный ненулевой key production удаляет только владелец после координации. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-semaphore.php';
$semaphore = lessonSemaphore();
try {
if (!sem_acquire($semaphore, true)) throw new RuntimeException('Не захвачен');
try { echo "Работа выполнена\n"; }
finally { sem_release($semaphore); echo "Пропуск возвращён\n"; }
} finally { sem_remove($semaphore); echo "Учебный набор удалён\n"; }
Ожидаемый результат
Работа выполнена
Пропуск возвращён
Учебный набор удалён
В реальном сервисе владелец отдельно знает, что других workers больше нет. Только факт вашего release этого не доказывает. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Рядом: sem_get, sem_release.
SysvSemaphoreРучка набора и счётчик захватов данного PHP объекта.
final class SysvSemaphoreРучка набора и счётчик захватов данного PHP объекта.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| Создание | Только sem_get, с PHP 8.0 объект вместо resource. |
| Действия | Отдельные sem_acquire/release/remove, не методы объекта. |
| Время жизни | auto_release может вернуть захваты при освобождении объекта; не удаляет системный набор. |
- sem_get → объект
- acquire привязан к этому объекту
- release балансирует его захват
- remove — решение владельца ресурса
SysvSemaphore хранит связь с системным набором и баланс захватов данного объекта. Получение ещё одного PHP объекта с тем же key не является переносом этого баланса. Класс final, не имеет пользовательских методов и создаётся только sem_get.
<?php
declare(strict_types=1);
require __DIR__ . '/ipc-semaphore.php';
$semaphore = lessonSemaphore(1);
try {
echo $semaphore instanceof SysvSemaphore ? "Объект системного семафора\n" : "Другой тип\n";
echo "Сам факт существования объекта не означает захват\n";
} finally { sem_remove($semaphore); }
Ожидаемый результат
Объект системного семафора
Сам факт существования объекта не означает захват
Переменная true в обычном массиве процесса не заменяет согласованный межпроцессный semaphore. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
<?php
declare(strict_types=1);
try { new SysvSemaphore(); }
catch (Error $error) { echo "Используйте sem_get\n"; }
Ожидаемый результат
Используйте sem_get
Нет публичного конструктора, сериализации, клонирования и динамических полей. Для собственного класса сервиса храните эту ручку в обычном свойстве и явно управляйте доступом. Контекстный пример для Unix CLI с указанными расширениями и разрешённым SysV IPC. Здесь проверен синтаксис; выполнение Unix IPC не заявляется.
Рядом: sem_get, sem_acquire, sem_release.
ftokСогласовать IPC ключ по известному файлу и метке проекта.
ftok(string $filename, string $project_id): intСогласовать IPC ключ по известному файлу и метке проекта.
IPC означает обмен между процессами одной операционной системы. Объект PHP здесь — ручка доступа к системному ресурсу, а не сами общие данные. Скобки в shm_get_var($memory, 101) передают сегмент и ключ записи; возвращённое значение можно сохранить в обычную переменную. Создание, использование, освобождение доступа и удаление ресурса — разные действия.
Сначала: три механизма и безопасный учебный стенд. Вспомнить: аргументы/return, массивы, try/finally. Для каждого примера ниже указано, нужен ли Unix со своим IPC namespace или доступно выполнение в текущем PHP.
| Договор | Что это означает |
|---|---|
| filename | Существующий доступный путь к одному и тому же файловому объекту у участников. Ограничения open_basedir учитываются. |
| project_id | Ровно один байт, обычно ASCII буква; не длинное имя приложения. |
| Результат | Системный key либо -1 при отказе с warning. Пустой filename/неверная длина метки — ValueError в текущем PHP. |
| Граница | Не выдаёт глобально уникальный ID, не создаёт очередь/сегмент и не резервирует key. |
- app.ipc + Q
- ОС вычисляет key
- оба процесса используют этот key
- get/attach находят ресурс в общем namespace
Несколько запускаемых отдельно программ должны договориться о системном ключе. ftok получает его из существующего файла и однобайтовой метки проекта. Файл может быть пустым: важна его идентичность в файловой системе, а не текст внутри. Не используйте случайное число, если участники должны встретиться в одном IPC ресурсе.
<?php
declare(strict_types=1);
$first = ftok(__DIR__ . '/app.ipc', 'Q');
$second = ftok(__DIR__ . '/app.ipc', 'Q');
if ($first === -1 || $second === -1) throw new RuntimeException('Ключ не получен');
echo $first === $second ? "Участники получили один ключ\n" : "Файл изменился\n";
Ожидаемый результат
Участники получили один ключ
Файл app.ipc приложен к примеру. Числовое значение зависит от ОС и файла, поэтому оно намеренно не зафиксировано. Замена файла новым может поменять key.
<?php
declare(strict_types=1);
try { ftok(__DIR__ . '/app.ipc', 'QUEUE'); }
catch (ValueError $error) { echo "Нужна однобайтовая метка, например Q\n"; }
Ожидаемый результат
Нужна однобайтовая метка, например Q
В современном PHP project_id должен состоять ровно из одного байта. Буква кириллицы в UTF-8 занимает несколько байтов, поэтому используйте ASCII метку.
На Unix ftok сочетает ограниченные части device/inode и метки; уникальность не гарантируется. Один путь внутри разных контейнеров может указывать на разные файловые объекты, а одинаковый числовой key в разных IPC namespaces — на независимые ресурсы. Проверяйте результат -1 отдельно, а для общего ресурса отвергайте 0, означающий IPC_PRIVATE. Не преобразуйте key в unsigned «чтобы стал положительным»: системный key_t может иметь знаковое представление. В каталоге/файле, используемом для ключа, не должно быть посторонней записи.
Рядом: msg_get_queue, sem_get, shm_attach.