Открыть сервис

Разделяемая память

Разделяемая память — это область оперативной памяти, доступная для одновременного чтения и записи нескольким процессам или потокам (нити) одного процесса. Является одним из основных механизмов межпроцессного взаимодействия (IPC, Inter-Process Communication), позволяющим обмениваться данными без использования промежуточных каналов, таких как файлы, сокеты или каналы (pipes). Разделяемая память обеспечивает наиболее быстрый способ передачи информации между процессами, так как данные копируются только один раз — из адресного пространства одного процесса в разделяемую область, откуда они становятся немедленно доступны другому процессу.

История

Концепция разделяемой памяти возникла в 1960-х годах с развитием многозадачных операционных систем. Первые реализации появились в системах с разделением времени, таких как CTSS (Compatible Time-Sharing System) и Multics. В этих системах несколько пользовательских процессов могли одновременно использовать одни и те же области памяти для обмена данными.

В 1970-х годах, с появлением Unix, механизмы разделяемой памяти были стандартизированы. В System V Unix (1983 год) была введена одна из первых широко распространённых реализаций — System V Shared Memory (shmget, shmat, shmdt, shmctl). Позднее, в 1990-х годах, в операционных системах на основе ядра Linux и Windows NT были разработаны собственные API. В современных системах (Linux, FreeBSD, macOS, Windows) существуют как классические интерфейсы (System V, POSIX), так и более новые, например memfd_create в Linux или файлы, отображаемые в память (memory-mapped files).

Классификация

Разделяемую память можно классифицировать по способу создания и управления:

По типу создания

  • Явная разделяемая память — создаётся специальными системными вызовами (например, shmget в System V или shm_open в POSIX). Процессы должны явно присоединять (attach) эту память к своему адресному пространству.
  • Неявная разделяемая память — возникает при создании процессов-потомков (через fork()). В этом случае память родительского процесса копируется (Copy-on-Write) и становится разделяемой до момента модификации. В некоторых системах (например, в Linux с флагом MAP_SHARED) можно создавать разделяемые отображения файлов.

По способу синхронизации

  • Несинхронизированная — процессы обращаются к памяти без дополнительных механизмов. Требует внешней синхронизации (например, семафоров, мьютексов, условных переменных) для предотвращения состояний гонки.
  • Синхронизированная — использует встроенные примитивы (например, атомарные операции, блокировки) для обеспечения согласованности данных.

Устройство и механизмы

Основные системные вызовы (на примере POSIX и System V)

POSIX Shared Memory:

  • shm_open() — создаёт или открывает объект разделяемой памяти (файл в виртуальной файловой системе /dev/shm).
  • ftruncate() — устанавливает размер объекта.
  • mmap() — отображает объект в адресное пространство процесса.
  • munmap() — удаляет отображение.
  • shm_unlink() — удаляет объект.

System V Shared Memory:

  • shmget() — создаёт или получает идентификатор сегмента разделяемой памяти.
  • shmat() — присоединяет сегмент к адресному пространству.
  • shmdt() — отсоединяет сегмент.
  • shmctl() — управляет сегментом (удаление, получение информации).

Аппаратная поддержка

На уровне процессора разделяемая память реализуется через механизмы виртуальной памяти. Страницы оперативной памяти могут быть отображены в адресные пространства нескольких процессов одновременно. Операционная система управляет таблицами страниц и обеспечивает когерентность кэшей (cache coherence) на многоядерных системах. В современных процессорах (x86-64, ARM) используются TLB (Translation Lookaside Buffer) и протоколы когерентности, такие как MESI.

Синхронизация

Без синхронизации разделяемая память может привести к состояниям гонки. Типичные механизмы:

  • Семафоры — POSIX (sem_open, sem_wait, sem_post) или System V (semget, semop).
  • Мьютексы — обычно реализуются через атомарные операции (например, pthread_mutex_t в разделяемой памяти, если поддерживается PTHREAD_PROCESS_SHARED).
  • Условные переменные — доступны в разделяемой памяти при установке атрибута PTHREAD_PROCESS_SHARED.
  • Атомарные операции__sync_fetch_and_add, std::atomic в C++.

Применение

В операционных системах

Разделяемая память широко используется в ядре ОС для обмена данными между драйверами, модулями и пользовательскими процессами. Например, в Linux буферы сокетов (sk_buff) могут быть разделяемыми, а механизм mmap позволяет отображать файлы устройств (например, /dev/fb0 для Framebuffer).

В базах данных

Системы управления базами данных (СУБД), такие как PostgreSQL, MySQL, Oracle, используют разделяемую память для кэширования данных (буферный кэш), журналов транзакций и управления блокировками. Это позволяет нескольким процессам (например, фоновым воркерам) быстро получать доступ к общим структурам.

В веб-серверах и приложениях

Веб-серверы (Nginx, Apache) используют разделяемую память для кэширования часто запрашиваемых страниц, сессий пользователей и статистики. В языках программирования (C, C++, Rust, Go) существуют библиотеки для разделяемой памяти (например, boost::interprocess в C++, shm в Go).

В научных вычислениях

В параллельных вычислениях (MPI, OpenMP) разделяемая память применяется для обмена данными между потоками одного процесса или между процессами на одной машине. Это ускоряет выполнение задач, требующих интенсивного обмена (например, симуляции, обработка изображений).

В контейнеризации

Технологии контейнеров (Docker, Podman) используют разделяемую память для обмена данными между контейнерами и хост-системой. Например, через tmpfs или /dev/shm можно организовать быстрый обмен.

Примеры реализации

Пример на C (POSIX)

```c

include <sys/mman.h>

include <sys/stat.h>

include <fcntl.h>

include <unistd.h>

include <stdio.h>

include <string.h>

int main() { const char name = "/myshm"; int shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666); ftruncate(shm_fd, 4096); void ptr = mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); sprintf(ptr, "Hello from process %d", getpid()); printf("Data written: %s\n", (char*)ptr); munmap(ptr, 4096); shm_unlink(name); return 0; } ```

Пример на Python (multiprocessing.shared_memory)

```python from multiprocessing import shared_memory import numpy as np

Создание разделяемой памяти

shm = shared_memory.SharedMemory(name='test', create=True, size=100) data = np.ndarray((10,), dtype=np.int64, buffer=shm.buf) data[:] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] print(data) shm.close() shm.unlink() ```

Преимущества и недостатки

Преимущества

  • Высокая скорость — данные не копируются между процессами, а передаются через общую область памяти.
  • Низкая задержка — минимальные накладные расходы по сравнению с каналами или сокетами.
  • Гибкость — можно передавать сложные структуры данных (массивы, графы, очереди).

Недостатки

  • Необходимость синхронизации — без явных примитивов возможны состояния гонки и повреждение данных.
  • Сложность отладки — ошибки синхронизации (deadlock, livelock) трудно воспроизвести и исправить.
  • Ограничения безопасности — разделяемая память может быть доступна другим процессам, если не настроены права доступа.
  • Зависимость от платформы — API различаются в разных ОС (POSIX, System V, Windows).

Безопасность и ограничения

Разделяемая память может быть источником уязвимостей, если не контролировать доступ. В многопользовательских системах злоумышленник может прочитать или изменить данные, если сегмент памяти имеет неправильные разрешения. Для защиты используются:

  • Ограничение прав доступа (chmod, ACL).
  • Использование механизмов изоляции (например, пространства имён в Linux).
  • Шифрование данных перед записью.

В современных ОС (Linux, Windows) существуют средства аудита доступа к разделяемой памяти (например, auditd в Linux, Event Tracing в Windows).

Интересные факты

  • В Linux разделяемая память POSIX по умолчанию размещается в файловой системе tmpfs, смонтированной в /dev/shm. Размер этого раздела по умолчанию составляет половину объёма оперативной памяти.
  • В суперкомпьютерах разделяемая память используется в технологии PGAS (Partitioned Global Address Space), например, в языке UPC (Unified Parallel C).
  • В операционной системе Windows разделяемая память реализуется через объекты File Mapping (CreateFileMapping, MapViewOfFile), которые могут быть именованными и безымянными.

Источники

  • Стивенс У. Р., Раго С. А. — «UNIX. Профессиональное программирование» (3-е издание, 2014)
  • Таненбаум Э., Бос Х. — «Современные операционные системы» (4-е издание, 2015)
  • Документация Linux: man 7 shm_overview, man 2 shmget, man 2 mmap
  • Документация Microsoft: «File Mapping» (MSDN)
  • ISO/IEC 9945:2009 (POSIX.1-2008) — раздел о разделяемой памяти

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →