Ідентифікатори IPC

Кожен об'єкт IPC (черга чи повідомлень, семафор, або сегмент поділюваної
пам'яті) володіє унікальним ідентифікатором (Id), який дозволяє ядру ОС
ідентифікувати цей об'єкт. Наприклад, для того, щоб послатися на
певний сегмент пам'яті, що розділяється, вам всього лише необхідно знати
унікальний ідентифікатор, призначений цього сегменту.


Врахуйте, що ідентифікатор об'єкта IPC є унікальним тільки для одного
типу об'єктів. Іншими словами, тільки одна черга повідомлень може мати
ідентифікатор 12345, Хоча номер 12345 може також використовуватися
семафорами та / або сегментами розділеної пам'яті.


Ключі IPC


Як створюється ідентифікатор IPC? Для цього необхідний ключ. Першим кроком при
створення середовища взаємодії між додатками є координування
використання ключів. Уявіть собі це так: для того, щоб зателефонувати
кому-небудь, ви повинні знати його телефонний номер. Телефонна компанія повинна
знати, як переслати ваш дзвінок абоненту. І тільки, коли він відповідає,
відбувається з'єднання.


У разі застосування System V IPC "телефон" з'єднує об'єкти одного типу.
Телефонною компанією або способом маршрутизації є "ключ" IPC.


Додатки повинні генерувати свої власні ключі. Функція
ftok() робить це і для клієнта і для сервера. Значення ключа,
повертається функцією ftok(), Залежить від значення inode і молодшого
значення ідентифікатора пристрою для першого аргументу – файлу – і символу –
другого аргументу. Це не забезпечує унікальності, але додатки можуть
проводити перевірки на предмет колізій і генерувати нові ключі в міру
необхідності.

key_t mykey;
mykey = ftok ("/tmp/myapp", “a”);

У прикладі, наведеному вище, директорія /tmp/myapp комбінується
з літеральние ідентифікатором a для генерації ключа. Іншим
поширеним прикладом є використання поточної директорії:

key_t mykey;
mykey = ftok(".", “a”);

Обов'язок проектування алгоритму генерації ключів лежить на програмістові
прикладного застосування. Будь-який з цих методів має враховувати конкуруючі
стану процесів і заходи запобіжників "тупикових" ситуацій. Для досягнення
наших демонстраційних цілей ми обмежимося функцією ftok(). Якщо
ми домовимося, що кожен процес-клієнт буде запущений з власного
унікального домашнього каталогу, то умова унікальності буде дотримано
повністю.


Наступні системні виклики IPC використовують значення повертається ключа для
створення або зміни доступу до об'єктів IPC.


Команда ipcs відображає стан усіх об'єктів System V
IPC.


ipcs-q: показувати тільки черги повідомлень
ipcs-s: показувати тільки семафори
ipcs-m: показувати тільки поділювану пам'ять
ipcs – help: для допитливих

За замовчуванням показуються всі три категорії об'єктів. Розглянемо приклад
найпростішого виведення команди ipcs:

—— Shared Memory Segments ——–
shmid owner perms bytes nattch status
—— Semaphore Arrays ——–
^semid owner perms nsems status
—— Message Queues ——–
msqid owner perms used-bytes messages
0 root 660 5 1

Ми бачимо єдину чергу повідомлень з ідентифікатором 0. Вона
належить користувачеві root і володіє правами доступу 660, Або
-rw-rw—. Черга містить одне 5-ти байтове повідомлення.


Команда ipcs є потужним механізмом для моніторингу пам'яті
ядра об'єктів IPC.


Команда ipcrm.


ipcrm видаляє об'єкти IPC з ядра. Однак, оскільки об'єкти IPC
можуть бути вилучені за допомогою системних викликів з прикладної програми,
необхідність видаляти їх вручну виникає рідко. Команда дуже проста:

ipcrm – type (тип) id (номер)

Необхідно позначити тип об'єкту, що видаляється параметром. Зверніться за
подробицями до довідки man. Ідентифікатор IPC можна визначити за допомогою
команди ipcs. Пам'ятайте, що ідентифікатор унікальний тільки для
об'єктів одного типу. Ось чому необхідно вказувати тип об'єкта при його
видаленні.


Семафори.


Найкраще семафори уявити, як лічильник доступу до публічних ресурсів.
Зазвичай вони використовуються, як замки, не дозволяючи одному процесу отримати доступ
до чого-небудь, вже використовуваному іншим процесом. Семафори також можуть
надати ексклюзивний доступ до ресурсів даної машини або обмежити
кількість процесорів, що використовують ресурс одночасно.


Цей механізм також забезпечує функції для роботи з пам'яттю, що
System V. Колективна пам'ять забезпечує доступ до глобальних змінних для
різних процесів. Демони httpd і навіть інші програми (на Perl,
C та іншими мовами) можуть отримати доступ до цих даних з метою глобального
обміну даними. Пам'ятайте, однак, що колективна пам'ять НЕ захищена від
одночасного доступу.


Таблиця 1 демонструє змінні Unix, які визначають параметри
обмежень пам'яті, що розділяється. Кожен підвид Unix має своєї документацією
за цим змінним. Приміром, під FreeBSD вони є частиною конфігурації ядра.
Вам буде потрібно перекомпілювати ядро для прийняття змін.


Table 1. Змінні пам'яті, що розділяється Unix
















SHMMAX Максимальна кількість пам'яті, що розділяється, зазвичай 131072 байт
SHMMIN Мінімальна кількість пам'яті, що розділяється, звичайно 1 байт
SHMMNI Максимальна кількість сегментів пам'яті, що розділяється в системі, зазвичай
100
SHMSEG Максимальна кількість сегментів поділюваної пам'яті для одного процесу,
зазвичай 6

Функції повідомлень можуть посилати повідомлення одним процесам і приймати
повідомлення від інших. Вони є простим і ефективним способом обміну даними
між процесами без необхідності використання системи сокетів Unix.


Використання пам'яті, що розділяється і семафорів


Вивчаючи щось нове, кожен розробник хоче почати використовувати цю
технологію на практиці, хоча б написавши просту програму "Hello, world!". Це
нормально, тому наведемо мінімальні відомості, необхідні для того, щоб ви
могли створити цей простий тестовий приклад.


Колективна пам'ять є найшвидшим видом IPC, але вона вимагає
синхронізації між збереженням і витяганням даних. Який же тоді алгоритм
використання поділюваної пам'яті? Простіше кажучи, він полягає в наступному:



Синхронізація з використанням семафора необхідна, оскільки використання
ресурсу пам'яті, що розділяється практично аналогічно використанню файлового
ресурсу, де тимчасове блокування та розблокування доступу дозволяє уникнути
втрати даних.


Тепер виникає питання, як використовувати семафор. Насправді це вельми
просто і зрозуміло з наведеного прикладу коду. Єдина тонкість полягає
в тому, що, оскільки семафори можуть блокувати і розблокувати ресурси, вони
також можуть блокувати один одного. Без акуратного проектування процеси
можуть спробувати опанувати одним і тим же семафором одночасно, викликаючи довгі
затримки при очікуванні доступу. І, навіть при належному проектуванні, завжди буде
верхня межа продуктивності в мультипроцесорних системах. За
подробицями зверніться до будь-якій книзі, присвяченій багатопроцесорним системам.


Тут наводиться кілька прикладів того, як слід використовувати різні
функції семафорів і пам'яті, що розділяється. У кінці статті також додається більше
довгий приклад коду. Ось ці функції в порядку їх появи.


int sem_get (int key [, int max_acquire [, int perm]])


Ця функція повертає id семафора. Він буде позитивним у випадку успіху і
дорівнює FALSE у разі помилки. Використовуйте id для доступу до семафора
V за допомогою ключа key.


Якщо це необхідно, семафор може мати права доступу, зазначеними в
параметрі perm. Значення за замовчуванням: 0666. Параметр
max_acquire управляє кількістю процесів, які можуть
одночасно отримати доступ до семафора. За замовчуванням він дорівнює 1.


Вторинний виклик функції sem_get() для того ж ключа поверне
інший id, але вони обидва будуть вказувати на один і той же семафор.


bool sem_acquire(int sem_identifier)


Ця функція повертає TRUE у разі успіху і FALSE
при невдачі. Вона заблокується, якщо необхідно, до тих пір, поки не займе
запитаний семафор. Процес спроби зайняти запитаний семафор буде тривати
нескінченно, якщо він перевищить значення параметра max_acquire.


Всі семафори, використовувані в процесі, але не звільнені явно, будуть
закриті автоматично. Однак, це породить попередження.


int shm_attach(int key [, int memsize [, int perm]])


Ця функція створює або відкриває сегмент пам'яті, що розділяється.
shm_attach() повертає ідентифікатор для використання при
отриманні доступу до сегмента за допомогою заданого ключа. Розмір сегменту в байтах
буде дорівнює mem_size. Значення за замовчуванням дорівнює sysvshm.
init_mem
у файлі конфігурації PHP (або 10,000 байт, якщо воно не
встановлено у файлі). Необов'язковий параметр perm визначає права
доступу і дорівнює 0666 за замовчуванням.


Перший виклик із заданим ключем створить сегмент. Другий виклик з тим же ключем
поверне інший ідентифікатор, ігноруючи два інших параметра. Обидва ідентифікатора
надають доступ до одного і того ж сегменту пам'яті, що розділяється.


mixed shm_get_var(int id, int variable_key)


Ця функція повертає змінну, ідентифіковану параметром
variable_key, Із сегмента пам'яті, що розділяється з ідентифікатором
id. Змінна залишається в пам'яті, що розділяється.


int shm_put_var(int shm_identifier, int variable_key, mixed
variable)


Ця функція додає або оновлює значення змінної, заданої параметром
variable_key, В сегменті пам'яті, що розділяється, заданому параметром
shm_identifier. Вона дозволяє працювати зі змінними будь-яких
типів.


bool sem_release(int sem_identifier)


Ця функція звільняє семафор, якщо він зайнятий поточним процесом. В іншому
випадку видає попередження (warning). Повертає TRUE у разі
успіху і FALSE у разі помилки.


Після звільнення семафора для того, щоб його повторно використовувати,
викличте sem_acquire().


int shm_remove(int shm_identifier)


Ця функція видаляє сегмент пам'яті, що розділяється і все що містяться в ньому
дані.


bool sem_remove(int sem_identifier)


Ця функція видаляє семафор, заданий за допомогою sem_identifier,
якщо він був створений за допомогою sem_get. Повертає TRUE
у разі успіху і FALSE у разі помилки. Якщо семафора із зазначеним
ідентифікатором не існує, вона породить попередження (warning). Після
видалення семафор недоступний.


Зразок коду, що використовує поділювану пам'ять


Ось зразок коду, що використовує поділювану пам'ять, з попутними
коментарями:

 MEMSIZE = 512; / / обсяг виділюваної пам'яті, що розділяється
$ SEMKEY = 1; / / ключ семафора
$ SHMKEY = 2; / / ключ пам'яті, що розділяється

echo "Старт.
";

/ / Створюємо семафор
$sem_id = sem_get($SEMKEY, 1);
if ($sem_id === false)
{
echo "Помилка при створенні семафора";
    exit;
}
else
echo "Створено семафор $ sem_id.
";

/ / Займаємо семафор
if (! sem_acquire($sem_id))
{
echo "Помилка при спробі зайняти семафор $ sem_id.
";
    sem_remove($sem_id);
    exit;
}
else
echo "Успішно зайнятий семафор $ sem_id.
";

/ / Підключаємо поділювану пам'ять
$shm_id = shm_attach($SHMKEY, $MEMSIZE);
if ($shm_id === false)
{
echo "Помилка при підключенні пам'яті, що розділяється.
";
    sem_remove($sem_id);
    exit;
}
else
echo "Успішне підключення пам'яті, що розділяється: $ shm_id.
";

/ / Пишемо змінну 1
if (! shm_put_var ($ shm_id, 1, "Змінна 1"))
{
echo "Помилка при спробі записати змінну 1 до колективної пам'ять $ shm_id.
";

/ / Овобождаем ресурси.
    sem_remove($sem_id);
    shm_remove($shm_id);
    exit;
}
else
echo "Мінлива 1 записана до колективної пам'ять.
";

/ / Пишемо змінну 2
if (! shm_put_var ($ shm_id, 2, "Змінна 2"))
{
echo "Помилка при спробі записати змінну 2 в поділювану пам'ять $ shm_id.
";

/ / Звільняємо ресурси.
    sem_remove($sem_id);
    shm_remove ($shm_id);
    exit;
}
else
echo "Мінлива 2 записана до колективної пам'ять.
";

/ / Читаємо змінну 1
$var1 = shm_get_var($shm_id, 1);
if ($var1 === false)
{
echo "Помилка при спробі прочитати змінну 1 з пам'яті, що розділяється $ shm_id,".
"Повернене значення = $ var1.
";
}
else
echo "Прочитана змінна 1 = $ var1.
";

/ / Читаємо змінну 2
$var2 = shm_get_var ($shm_id, 2);
if ($var1 === false)
{
echo "Помилка при спробі прочитати змінну 2 з пам'яті, що розділяється $ shm_id,".
"Повернене значення = $ var2.
";
}
else
echo "Прочитана змінна 2 = $ var2.
";

/ / Звільняємо семафор
if (!sem_release($sem_id))
echo "Помилка при спробі звільнити семафор $ sem_id.
";
else
echo "Семафор $ sem_id звільнений.
";

/ / Видаляємо сегмент пам'яті, що розділяється
if (shm_remove ($shm_id))
echo "Сегмент пам'яті, що розділяється успішно знищений.
";
else
echo "Помилка при спробі видалити сегмент пам'яті, що розділяється $ shm_id.
";

/ / Видаляємо семафор.
if (sem_remove($sem_id))
echo "Семафор успішно знищений.
";
else
echo "Помилка при спробі видалити семафор $ sem_id.
";

echo "Кінець.
";

?>


Ось приклад коду, що виконує різні операції з пам'яттю, що:

<?php
/ / Створюємо блок пам'яті, що розділяється розміром 100 байт і з ідентифікатором рівним 0xff3
$shm_id = shmop_open(0xff3, "c", 0644, 100);

if(!$shm_id)
{
echo "Помилка при створенні сегмента пам'яті, що розділяється.
";
}

/ / Отримуємо розмір сегмента пам'яті, що розділяється
$shm_size = shmop_size($shm_id);
echo "Блок пам'яті, що розділяється з розміром:". $ Shm_size. "Створений.
";

/ / Пишемо тестову рядок в сегмент пам'яті, що розділяється
$ Shm_bytes_written = shmop_write ($ shm_id, "Мій блок пам'яті, що розділяється", 0);

if ($ shm_bytes_written! = strlen ("Мій блок пам'яті, що розділяється"))
{
echo "Помилка при спробі записати дані повністю
";
}

/ / Прочитуємо записану рядок
$my_string = shmop_read($shm_id, 0, $shm_size);

if(!$my_string)
{
echo "Помилка при спробі читання пам'яті, що розділяється
";
}

echo "Дані у пам'яті, що розділяється рівні:". $ my_string. "
";

/ / Видаляємо блок і закриваємо сегмент пам'яті, що розділяється

if(!shmop_delete($shm_id))
{
echo "Помилка при спробі позначити блок пам'яті, що розділяється на видалення.";
}

shmop_close($shm_id);

?>


Для більш докладної інформації про цих або інших функціях звертайтеся до
офіційної документації PHP.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*