Какие способы межпроцессного взаимодействия (IPC) в Linux/Unix ты знаешь?

«Какие способы межпроцессного взаимодействия (IPC) в Linux/Unix ты знаешь?» — вопрос из категории Linux и ОС, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

При разработке на C/C++ под Linux я использовал следующие механизмы IPC, каждый со своей областью применения:

  1. Каналы (Pipes):

    • Анонимные каналы (pipe): Создаются для связи между родительским и дочерним процессом. Однонаправленные.
      int fd[2];
      pipe(fd); // fd[0] для чтения, fd[1] для записи
      if (fork() == 0) { /* Дочерний: читает из fd[0] */ }
      else { /* Родительский: пишет в fd[1] */ }
    • Именованные каналы (FIFO) (mkfifo): Существуют как файл в файловой системе, позволяя общаться неродственным процессам.
  2. Очереди сообщений System V / POSIX (msgget, mq_open): Позволяют передавать структурированные сообщения с приоритетами. POSIX-очереди обычно предпочтительнее из-за более простого и надёжного API.

  3. Разделяемая память (Shared Memory): Самый быстрый способ для передачи больших объёмов данных.

    • System V (shmget, shmat): Классический, но несколько устаревший API.
    • POSIX (shm_open, mmap): Более современный и гибкий подход. Критически важный момент: доступ к разделяемой памяти требует синхронизации с помощью семафоров или мьютексов, размещённых в той же разделяемой памяти.
      // Создание/открытие объекта разделяемой памяти
      int shm_fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0666);
      ftruncate(shm_fd, SIZE);
      // Отображение в адресное пространство процесса
      void* ptr = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
      // Теперь ptr — общая память для всех процессов, открывших "/my_shm"
  4. Сокеты (Sockets): Наиболее универсальный механизм. Могут использоваться не только для сетевого взаимодействия, но и для IPC через сокеты домена Unix (AF_UNIX), что очень эффективно.

    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, "/tmp/my_socket");
    bind(sock, (struct sockaddr*)&addr, sizeof(addr));
  5. Сигналы (kill, signal, sigaction): Используются для простых асинхронных уведомлений (например, SIGUSR1, SIGUSR2). Не подходят для передачи данных, только для управления.

Выбор механизма зависит от задачи: разделяемая память + семафоры для высокопроизводительного обмена данными, сокеты AF_UNIX для структурированного клиент-серверного взаимодействия, каналы для простой связи по принципу "один пишет — другой читает".