Итак, продолжим тему системного программирования на php.
Мы уже научились работать с очередями, по которым можно пересылать небольшие системные сообщения, и наверняка, столкнулись с задачей передавать большие объёмы данных. В наших любимых системах типа System V уже давно решена задача быстрой передачи и сохранения больших данных в памяти. И этот механизм называется Shared Memory.
Если вкратце, то данные в Shared Memory живут до перезагрузки системы. Так как данные находятся в памяти, то работают они намного быстрее чем если бы сохранялись в базе, где-нибудь в файле, или, прости господи, на стороннем сетевом ресурсе.
Работа с shared memory. Основы.
Давайте попробуем написать простой пример сохранения данных.
shared-memory-write-base.php
// Это самый правильный и рекомендуемый способ получать уникальный идентификатор.
// На основе полученного пути, система вычленит иноды из таблицы ФС,
// и для пущей уникальности преобразует это число на основани второго параметра.
// Вторым параметром всегда идёт одна буква
$id = ftok(__FILE__, 'A');
// Создаём или открываем блок памяти
// Тут можно указать дополнительные параметры, например осообо большой объём блока
// или же права доступа для других пользователей к этому блоку памяти.
// Халявщики могут вместо id просто указать любое целочисленное значение :-)
$shmId = shm_attach($id);
// В шареде у нас есть переменные (любое целочисленное значение)
$var = 1;
// Смотрим, есть ли у нас требуемая переменная
if (shm_has_var($shmId, $var))
{
// Если есть, считываем данные
$data = (array)shm_get_var($shmId, $var);
}
else
{
// Это случай, если данных никаких не было. На всякий пожарный
$data = array();
}
// Сохраняем в полученном массиве значение этого файла
$data[time()] = file_get_contents(__FILE__);
// И записываем этот массив в память, указываем переменную где сохранить
shm_put_var($shmId, $var, $data);
// Просто?
Запустите этот скрипт несколько раз, что бы в памяти сохранились значения. А теперь давайте напишем скрипт только для чтения из памяти.
shared-memory-read-base.php
// Читаем данные из памяти
$id = ftok(__DIR__ . '/shared-memory-write-base.php', 'A');
$shmId = shm_attach($id);
$var = 1;
// Смотрим, есть ли у нас требуемая переменная
if (shm_has_var($shmId, $var))
{
// Если есть, считываем данные
$data = (array)shm_get_var($shmId, $var);
}
else
{
// Это случай, если данных никаких не было. На всякий пожарный
$data = array();
}
// Перебираем полученные значения и сохраняем их в файлы
foreach ($data as $key => $value)
{
// Простой пример, создаём файлы из тех что мы сохранили ранее
$path = '/tmp/' . $key . '.php';
file_put_contents($path, $value);
echo $path . PHP_EOL;
}
Работа с семафорами
Итак, в общих чертах уже должно быть понятно как работать с shared memory. И осталось выяснить пару ньюансов, например: “А что сделать если два процесса одновременно захотят записывать в один блок памяти?” или “Как сохранять бинарные файлы произвольного размера?”.
Для блокировки доступа, мы будем использовать семафоры. Семафоры позволяют нам установить флажок, что сейчас производится какое-то действие. И другой процесс, наткнувшись на этот семафор будет ждать пока его не снимет процесс-родитель.
В коде это ещё выглядит ещё понятнее:
shared-memory-semaphors.php
// Давайте попробуем сохранить какой-нибудь бинарный файл, размером в пару мегабайт
// Этот скрипт выполняет следующий алгоритм:
// Если есть данные - он их читает, нет - Записывает данные в память
// При этом в моменты записи в память мы ставим признак блокировки - семафор
// Тут всё как обычно, читаем предыдущие комментарии
$id = ftok(__FILE__, 'A');
// Получаем ресурс семафора - признака блокировки. Нет ничего страшного, если мы
// используем тот же id что используется для получения ресурса shared memory
$semId = sem_get($id);
// Ставим блокировку. Тут есть ньюанс. Если другой процесс столкнётся с этой
// блокировкой, он будет ожидать пока её не снимут
sem_acquire($semId);
// Укажите свой файл, например картинку
$data = file_get_contents(__DIR__.'/06050396.JPG', FILE_BINARY);
// Данные могут быть большими, поэтому предусмотрительно надо выделить так, что бы хватило
$shmId = shm_attach($id, strlen($data)+4096);
$var = 1;
if (shm_has_var($shmId, $var))
{
// Получаем данные из памяти
$data = shm_get_var($shmId, $var);
// Сохраняем наш файл в другом месте
$filename = '/tmp/' . time();
file_put_contents($filename, $data, FILE_BINARY);
// Удаляем блок памяти, что бы всё началось сначала :-)
shm_remove($shmId);
}
else
{
shm_put_var($shmId, $var, $data);
}
// Освобождаем блокировку
sem_release($semId);
Теперь можно с помощью консольной утилиты md5summ сравнить оба файла, полученный и исходный. Или же, можно запустить полученный файл в графическом редакторе или в чём он у вас должен запускаться?
На этом знакомство с shared memory и семафорами я считаю законченным. А на домашнее задание хочу вас попросить написать код, который в демоне будет использовать семафоры и общую память.