Learning Platform
Глоссарий Troubleshooting
Урок 09.04 · 20 мин
Начальный
IPCSysVPOSIXMessage QueuesLinux

SysV vs POSIX — shared memory и message queues обзорно

В Linux исторически сложились две параллельные системы межпроцессной коммуникации: System V IPC (унаследована от Unix System V Release 4, 1989 год) и POSIX IPC (стандартизована позже, ~1990-е). Обе обеспечивают три механизма: shared memory, message queues и semaphores. Но API, идентификаторы, видимость и интеграция с остальным kernel-ом разные.

Это не теоретический выбор — разные реальные приложения используют разные системы. PostgreSQL использует SysV shared memory + semaphores (по историческим причинам). systemd, modern message brokers — POSIX. Иногда вы видите процесс, который застрял на semop() — и нужно понимать, какая это система, чтобы продиагностировать через правильные тулзы (ipcs для SysV, ls /dev/shm для POSIX).

В этом уроке: ключевые различия двух систем, когда какую использовать, message queues (часто незаслуженно забытые) и как их сравнить с современными broker-ами типа Redis/Kafka.


Общая картина: три механизма, две системы

Каждая система предлагает три IPC примитива:

SysV vs POSIX -- два набора одних и тех же примитивов
Shared MemoryОбщая память для нескольких процессов. SysV: shmget+shmat. POSIX: shm_open+mmap. POSIX интегрирован с mmap и виден в /dev/shm, SysV отдельная подсистема
Message QueuesОчередь дискретных сообщений между процессами. SysV: msgget+msgsnd+msgrcv. POSIX: mq_open+mq_send+mq_receive. POSIX поддерживает приоритеты и async notification
SemaphoresСемафоры для синхронизации. SysV: semget+semop. POSIX: sem_open+sem_wait+sem_post. POSIX проще API, SysV поддерживает массивы семафоров и SEM_UNDO
SysV: ftok keysSysV использует integer keys, генерируемые ftok(path, id). Старая модель, не очень удобная -- разные процессы должны договориться о пути и id. Объекты видны через ipcs
POSIX: string namesPOSIX использует string names в виде /myobject. Более интуитивно, объекты видны как файлы (для shm в /dev/shm, для mq в /dev/mqueue). Рекомендуется для нового кода

Главное практическое отличие:

  • POSIX — string names как ‘/myobj’, объекты видны как файлы, интегрированы с обычными file descriptors, поддерживаются select/poll/epoll. Это «современный Linux» способ.
  • SysV — integer keys через ftok, отдельные kernel-структуры, отдельная утилита ipcs для просмотра. Старее, иногда удобнее в legacy-коде или специфических сценариях.

SysV API — ipcs и старая магия

# Посмотреть текущие SysV-объекты системы:
ipcs

# ------ Message Queues --------
# key        msqid      owner      perms      used-bytes   messages
#
# ------ Shared Memory Segments --------
# key        shmid      owner      perms      bytes      nattch     status
# 0x52454441 32768      postgres   600        144441344  4
#
# ------ Semaphore Arrays --------
# key        semid      owner      perms      nsems
# 0x52454441 32768      postgres   600        17

postgres использует SysV shared memory для своих shared buffers. nattch = 4 означает 4 процесса присоединены (4 backend-а).

# Минимальный пример SysV shared memory:
cat > sysv_shm.c << 'CEOF'
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(void) {
    // Создаём ключ из пути и проектного id
    key_t key = ftok("/tmp", 'a');

    // Создаём shared memory сегмент
    int shmid = shmget(key, 4096, IPC_CREAT | 0666);

    // Присоединяем к адресному пространству
    char* shared = shmat(shmid, NULL, 0);
    strcpy(shared, "Hello from SysV SHM");

    printf("Wrote, key=0x%x, id=%d\n", key, shmid);
    sleep(30);

    shmdt(shared);
    // Можно: shmctl(shmid, IPC_RMID, NULL) -- удалить
    return 0;
}
CEOF

gcc sysv_shm.c -o sysv_shm
./sysv_shm &
sleep 1
ipcs -m
# Видите свой сегмент с key 0x...

# Через 30 сек сегмент останется (мы не сделали IPC_RMID)
ipcrm shm <shmid>   # удалить вручную

Ключевая особенность SysV: объекты живут в kernel независимо от процессов. Даже если все процессы умерли, shm-сегмент висит, пока кто-то явно не вызовет ipcrm. Это создаёт проблему «утечки» SysV-объектов в системе — их находят через ipcs и чистят.


POSIX API — знакомый

POSIX shared memory мы уже видели в прошлом уроке — через shm_open и mmap. Объекты живут в /dev/shm.

POSIX message queues — менее известный, но мощный механизм:

cat > pmq_sender.c << 'CEOF'
#include <mqueue.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(void) {
    struct mq_attr attr = {
        .mq_maxmsg = 10,
        .mq_msgsize = 256
    };

    mqd_t mq = mq_open("/my_queue", O_CREAT | O_WRONLY, 0666, &attr);

    for (int i = 0; i < 5; i++) {
        char msg[64];
        snprintf(msg, sizeof(msg), "Message #%d", i);
        mq_send(mq, msg, strlen(msg) + 1, /*prio*/ 0);
        printf("Sent: %s\n", msg);
    }

    mq_close(mq);
    return 0;
}
CEOF

cat > pmq_receiver.c << 'CEOF'
#include <mqueue.h>
#include <stdio.h>

int main(void) {
    mqd_t mq = mq_open("/my_queue", O_RDONLY);
    char buf[256];
    unsigned int prio;

    for (int i = 0; i < 5; i++) {
        ssize_t n = mq_receive(mq, buf, sizeof(buf), &prio);
        printf("Got [prio %u]: %s\n", prio, buf);
    }

    mq_close(mq);
    mq_unlink("/my_queue");
    return 0;
}
CEOF

gcc pmq_sender.c -o pmq_sender -lrt
gcc pmq_receiver.c -o pmq_receiver -lrt

./pmq_sender
./pmq_receiver
# Sent: Message #0..4
# Got: Message #0..4

# Удалить очередь:
# mq_unlink делается в receiver, но если процессы упали:
ls /dev/mqueue
# my_queue  <-- висит, можно: rm /dev/mqueue/my_queue

POSIX message queues видны через /dev/mqueue (нужно mount -t mqueue none /dev/mqueue, в современных Linux обычно уже смонтировано).


Message queues vs Redis/Kafka

«У меня уже есть Redis/Kafka/RabbitMQ. Зачем мне POSIX MQ?» Хороший вопрос. Сравнение:

POSIX MQ vs Redis vs Kafka -- разные ниши
POSIX MQLocal IPC между процессами на одной машине. Низкая latency (микросекунды). Простой API. Без persistence. Без репликации. Ограничен размером очереди. Не работает кросс-машинно
Use case: tight couplingИдеально для: внутрипроцессного общения worker-ов в pre-fork сервере, локальный rate-limit между процессами одного приложения. Не подходит для бизнес-уровня
Redis pub/sub + listsNetwork-level broker. Один-два миллисекунды latency. In-memory с опц. персистом (AOF/RDB). Без репликации сообщений (только данных). Cross-machine. Простой API через clients
Use case: lightweightCross-service queues, fire-and-forget уведомления, простые worker queues (Celery, RQ, Sidekiq). Когда не нужна сложная durability и репликация
Kafka / RabbitMQПолноценный broker с persistence, репликацией, partition, consumer groups, retries, dead-letter queues. Latency 10-100 мс. Тяжелые в эксплуатации, требуют отдельных машин
Use case: enterpriseEvent streaming, log aggregation, гарантированная доставка, аудит, multi-consumer pattern. Когда сообщения -- источник правды и должны переживать рестарты

POSIX MQ занимает уникальную нишу: ультра-низкая latency для same-machine IPC без overhead-а network broker-а. Используется в high-frequency trading, embedded системах, real-time контроллерах.

Для типичных микросервисов на k8s — забудьте про SysV/POSIX MQ, используйте полноценные брокеры. Но знать, что они есть, полезно: на одной машине sometimes мало 100 мс latency Kafka.

Kafka в compose: реальный message broker для DE

Когда какую использовать

Практические рекомендации:

Используйте POSIX shm, когда:

  • Передача больших бинарных данных между процессами одной машины.
  • Нужна интеграция с mmap и file descriptors.
  • Пишете новый код.

Используйте SysV shm, когда:

  • Поддерживаете legacy-код, который её использует.
  • Нужны точные семантики SEM_UNDO для семафоров (автоматический rollback при смерти процесса).
  • Работаете с базами данных (PostgreSQL, Oracle) — они используют SysV.

Используйте POSIX MQ, когда:

  • Локальный IPC с сообщениями (не байт-потоком).
  • Микросекундная latency между процессами одной машины.
  • Простота API важнее features.

Используйте unix socket вместо MQ, когда:

  • Уже знакомы с socket API.
  • Нужен byte stream.
  • Хотите легко перенести на TCP в будущем.

Используйте Redis/Kafka/RabbitMQ, когда:

  • Cross-machine.
  • Нужна персистентность.
  • Несколько consumers.
  • Сложные паттерны (pub/sub, fanout, dead letters).

Tunables и лимиты

У SysV есть system-wide лимиты:

# Лимиты SysV в /proc/sys/kernel/:
cat /proc/sys/kernel/shmmax    # max size одного segment
# 18446744073709551615 -- ~16 EB на современных Linux

cat /proc/sys/kernel/shmall    # max общая память для всех segments в страницах
cat /proc/sys/kernel/msgmax    # max размер одного message
cat /proc/sys/kernel/msgmnb    # max байт в одной queue

# Изменить:
sysctl -w kernel.shmmax=4G

У POSIX MQ:

# Лимиты POSIX message queues:
cat /proc/sys/fs/mqueue/msg_max         # max messages per queue
cat /proc/sys/fs/mqueue/msgsize_max     # max message size
cat /proc/sys/fs/mqueue/queues_max      # max queues per user

В прошлом эти лимиты были маленькими (256 messages на queue по дефолту), что заставляло многих переходить на сторонние решения. Современные дистрибуции имеют sane defaults.

NOTE

PostgreSQL до версии 9.2 требовал ручной настройки kernel.shmmax под shared_buffers. С 9.3 PostgreSQL стал использовать комбинацию SysV semaphores + mmap-anonymous shared memory — и больше не упирается в kernel.shmmax. Это уроки эволюции IPC use case.


Попробуй сам

Посмотрите SysV-объекты вашей системы:

# Все SysV IPC:
ipcs

# Только shared memory с детальной инфой:
ipcs -m -p
# покажет creator pid и last access pid для каждого segment

# Удалить осиротевшие объекты (где nattch=0 -- никто не подключён):
# Внимание: можно сломать работающие сервисы!
# Сначала просто посмотреть:
ipcs -m | awk '$6==0 {print "Orphan:", $2}'

POSIX-объекты:

# POSIX shared memory:
ls -la /dev/shm/

# POSIX message queues:
ls -la /dev/mqueue/
# Если /dev/mqueue не смонтирована:
sudo mkdir -p /dev/mqueue
sudo mount -t mqueue none /dev/mqueue

Попробуйте простую POSIX MQ на Python:

pip install posix-ipc

python3 << 'EOF'
import posix_ipc

# Создаём очередь
mq = posix_ipc.MessageQueue('/test_mq', flags=posix_ipc.O_CREAT, max_messages=10, max_message_size=256)

# Отправляем
mq.send(b'Hello via POSIX MQ', priority=5)
mq.send(b'Urgent message', priority=10)
mq.send(b'Low priority', priority=1)

# Получаем (порядок по приоритету: высокий первый)
for _ in range(3):
    msg, prio = mq.receive()
    print(f'prio={prio}: {msg}')

mq.close()
mq.unlink()
EOF

# Вывод:
# prio=10: b'Urgent message'
# prio=5:  b'Hello via POSIX MQ'
# prio=1:  b'Low priority'

Priority-based receive — одна из удобных фишек POSIX MQ. Нет аналога в Unix sockets.


Проверка знанийKnowledge check
Команда обнаружила, что на production-сервере с PostgreSQL после миграции на новую версию ipcs показывает много 'осиротевших' SysV shared memory сегментов (nattch=0, никто не подключён). Это нормально или нужно беспокоиться?
ОтветAnswer
Это требует разбирательства, не безусловно нормально. Что может быть: 1) Postgres до 9.3 использовал большой SysV shared memory сегмент для shared_buffers -- при креше или некорректном shutdown сегмент мог осиротеть. С 9.3+ PostgreSQL использует mmap MAP_ANONYMOUS для большей части shared memory, и SysV только для маленького 'sysv setup' сегмента. 2) Если nattch=0, значит, его никто не использует. Можно безопасно удалить через ipcrm -m <shmid>. Но! Сначала убедиться, что: a) postgresql точно не работает в режиме, который ещё может его подцепить, b) нет других процессов, которые могут это сделать. 3) При нормальном shutdown postgresql сам делает shmctl(IPC_RMID) -- сегмент должен исчезать. Если остаются -- значит, был abnormal exit (kill -9, OOM, panic). Проверить логи. 4) System-wide лимит kernel.shmmni (max segments) -- если осиротевшие сегменты накапливаются, можно упереться в этот лимит и новые shmget вернут ENOSPC. Чистка через ipcrm -- паллиатив, но если это происходит часто -- стоит найти причину креша. 5) Альтернатива -- systemd-tmpfiles с RemoveIPC для определённых user-ов, можно настроить автоматическую очистку SysV IPC после смерти их владельца. Это включено по умолчанию на новых дистрибуциях через 'systemd-logind RemoveIPC=yes', но иногда отключается специально для postgresql.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. В чём ключевое различие между SysV и POSIX системами IPC?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4