Правильная стратегия с разделением памяти и удалением семафоров в RAII, как класс php
Когда такая ситуация возникает?
Если вы используете разделяемую память и семафоры для межпроцессной блокировки (с расширением pcntl), вам следует позаботиться о жизненном цикле семафора и сегмента разделяемой памяти. Например, вы пишете рабочее приложение backgroud и используете master и некоторый дочерний (разветвленный) процесс для обработки задания. Использование разделяемой памяти и семафоров хорошая идея для IPC между ними. А также RAII
как обертка классов вокруг функций php shm_xxx и sem_xxx, это тоже хорошая идея.
пример
class Semaphore
{
private $file;
private $sem;
public function __construct()
{
$this->file = tempnam(sys_get_temp_dir(), 's');
$semKey = ftok($this->file, 'a');
$this->sem = sem_get($semKey, 1); //auto_release = 1 by default
}
public function __destruct()
{
if (is_resource($this->sem) {
sem_remove($this->sem);
}
}
....
}
Не удачный выбор - после fork у нас есть один экземпляр в родительском и один в дочернем процессах. И деструктор в любом из них уничтожает семафор.
Почему важно
Большинство систем Linux имеет ограничение на семафор подсчета общей памяти. Если у вас есть приложение, которое должно создавать и удалять много общих сегментов памяти семафоров, вы не можете ждать, пока оно будет автоматически освобождено при завершении процесса.
Вопрос
С помощью с
вы можете использовать shmctl с IPC_RMID
- это отмечает сегмент для удаления. Само фактическое удаление происходит, когда последний процесс, в настоящее время присоединенный к сегменту, правильно отсоединил его. Конечно, если в данный момент к сегменту не присоединены никакие процессы, удаление кажется немедленным. Он работает как простой счетчик ссылок. Но php не реализует shmctl
,
Другая стратегия - уничтожать семафор только в деструкторе мастер-процесса:
class Semaphore
{
...
private $pid;
public function __construct()
{
$this->pid = getmypid();
...
}
public function __destruct()
{
if (is_resource($this->sem) && $this->pid === getmypid()) {
sem_remove($this->sem);
}
}
....
}
Итак, вопросы
- Если какой-либо способ использовать IPC_RMID в php?
- Какую стратегию следует использовать в таких случаях? Уничтожить только в мастер-процессе? Другие случаи?
1 ответ
Я проверил текущий исходный код PHP и IPC_RMID
не используется Тем не менее, PHP использует semop()
и с этим, SEM_UNDO
флаг, на случай auto_release
(см. руководство по PHP sem_get()). Но имейте в виду, что это работает на уровне процесса. Поэтому, если вы используете PHP в качестве модуля Apache, или FCGI, или FPM, он может работать не так, как ожидалось. Это должно хорошо работать для CLI, хотя.
Для вашей очистки это зависит от того, заканчивается ли "мастер" последним или нет.
Если вы не знаете, вы можете выполнить подсчет ссылок самостоятельно.
class Semaphore
{
static private $m_referenceCount = 0;
public function __construct()
{
++self::$m_referenceCount;
// aquire semaphore
}
public function __destruct()
{
if (--self::$m_referenceCount <= 0) {
// clean up
}
}
}
Но имейте в виду, что деструктор НЕ выполняется в некоторых обстоятельствах.