← Назад к вопросам

Какая функция создаёт объект разделяемой памяти?

3.0 Senior🔥 101 комментариев
#Linux и операционные системы#Многопоточность и синхронизация

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Функции создания разделяемой памяти

Для создания объектов разделяемой памяти в POSIX-системах используется несколько функций в зависимости от типа ресурса, который вы хотите разделить между процессами. Это критично для написания многопроцессных приложений и IPC (Inter-Process Communication) механизмов.

Основные функции создания разделяемой памяти

1. shmget() — для System V shared memory

Функция создаёт или получает доступ к сегменту разделяемой памяти:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
#include <iostream>

int main() {
    // Создаём сегмент разделяемой памяти размером 1024 байта
    int shmid = shmget(IPC_PRIVATE,  // или key_t key
                       1024,          // размер в байтах
                       IPC_CREAT |    // создать, если не существует
                       0666);         // права доступа
    
    if (shmid == -1) {
        perror("shmget failed");
        return 1;
    }
    
    std::cout << "Shared memory ID: " << shmid << std::endl;
    
    // Удалить сегмент
    shmctl(shmid, IPC_RMID, nullptr);
    
    return 0;
}

2. shmat() — подключение к разделяемой памяти

После создания сегмента нужно подключить его к адресному пространству процесса:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
#include <iostream>

int main() {
    // Создаём сегмент
    int shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | 0666);
    
    // Подключаемся к сегменту
    void* shmaddr = shmat(shmid,     // ID сегмента
                          nullptr,   // адрес (nullptr = автоматический)
                          0);        // флаги (0 = чтение/запись)
    
    if (shmaddr == (void*)-1) {
        perror("shmat failed");
        return 1;
    }
    
    // Теперь можем работать с памятью
    char* shared_data = (char*)shmaddr;
    strcpy(shared_data, "Hello from process!");
    
    // Отключиться от сегмента
    shmdt(shmaddr);
    
    // Удалить сегмент
    shmctl(shmid, IPC_RMID, nullptr);
    
    return 0;
}

POSIX Shared Memory: mmap()

Современный подход использует mmap() с файлом или с /dev/zero:

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include <iostream>

int main() {
    const int SIZE = 1024;
    
    // Способ 1: через обычный файл
    int fd = open("shared_file.bin", O_CREAT | O_RDWR, 0666);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }
    
    // Изменяем размер файла
    lseek(fd, SIZE - 1, SEEK_SET);
    write(fd, "", 1);
    
    // Отображаем файл в память
    void* addr = mmap(nullptr,         // адрес (nullptr = автоматический)
                      SIZE,           // размер
                      PROT_READ | PROT_WRITE,  // права доступа
                      MAP_SHARED,     // разделяемое отображение
                      fd,            // файловый дескриптор
                      0);            // смещение в файле
    
    if (addr == MAP_FAILED) {
        perror("mmap failed");
        return 1;
    }
    
    // Работаем с памятью
    char* data = (char*)addr;
    strcpy(data, "Shared via mmap!");
    
    // Синхронизируем изменения на диск
    msync(addr, SIZE, MS_SYNC);
    
    // Отображаем
    munmap(addr, SIZE);
    close(fd);
    
    return 0;
}

POSIX Named Shared Memory: shm_open()

Современный POSIX способ создания именованной разделяемой памяти:

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include <iostream>

int main() {
    const int SIZE = 4096;
    const char* SHM_NAME = "/myapp_shared_memory";
    
    // Создаём или открываем именованный объект разделяемой памяти
    int shm_fd = shm_open(SHM_NAME,              // имя
                          O_CREAT | O_RDWR,      // флаги создания
                          0666);                 // права доступа
    
    if (shm_fd == -1) {
        perror("shm_open failed");
        return 1;
    }
    
    // Устанавливаем размер
    if (ftruncate(shm_fd, SIZE) == -1) {
        perror("ftruncate failed");
        return 1;
    }
    
    // Отображаем в память
    void* addr = mmap(nullptr, SIZE, 
                      PROT_READ | PROT_WRITE, 
                      MAP_SHARED, 
                      shm_fd, 0);
    
    if (addr == MAP_FAILED) {
        perror("mmap failed");
        return 1;
    }
    
    // Используем память
    char* data = (char*)addr;
    strcpy(data, "POSIX shared memory!");
    
    // Очистка
    munmap(addr, SIZE);
    close(shm_fd);
    shm_unlink(SHM_NAME);  // удалить объект разделяемой памяти
    
    return 0;
}

Сравнение методов

ФункцияТипИспользованиеПреимуществаНедостатки
shmget()System VНаследование, совместимостьРаботает вездеСложнее управлять
mmap()POSIX fileФайловое отображениеГибкостьПривязано к файлу
shm_open()POSIX namedСовременные приложенияПростота, названияТолько POSIX
memory_mapped_fileWindowsIPC на WindowsЭффективностьОС-зависимая

Практический пример: многопроцессная работа

#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <cstring>
#include <iostream>

struct SharedData {
    int counter;
    char message[256];
};

int main() {
    key_t key = ftok("/tmp", 65);  // генерируем ключ
    int shmid = shmget(key, sizeof(SharedData), IPC_CREAT | 0666);
    
    SharedData* shared = (SharedData*)shmat(shmid, nullptr, 0);
    
    if (fork() == 0) {
        // Дочерний процесс
        for (int i = 0; i < 5; i++) {
            shared->counter++;
            sleep(1);
        }
        shmdt((void*)shared);
    } else {
        // Родительский процесс
        sleep(2);
        std::cout << "Counter: " << shared->counter << std::endl;
        shmdt((void*)shared);
        shmctl(shmid, IPC_RMID, nullptr);
    }
    
    return 0;
}

Лучшие практики

  • Используйте shm_open() для новых приложений (POSIX стандарт)
  • Синхронизируйте доступ через мьютексы или семафоры
  • Проверяйте ошибки всех функций (shmget(), shmat(), mmap())
  • Очищайте ресурсы через shmdt(), shmctl() или shm_unlink()
  • Тестируйте на разных ОС, если требуется портативность

Выбор функции зависит от требований: System V сегменты для совместимости, POSIX функции для современных приложений.