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

Какие знаешь способы реализации межпроцессного взаимодействия?

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

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

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

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

Способы реализации межпроцессного взаимодействия (IPC)

1. Pipes (Конвейеры)

#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main() {
    int fd[2];
    char buffer[100];
    
    // Создаём pipe
    pipe(fd);  // fd[0] = читаем, fd[1] = пишем
    
    if (fork() == 0) {
        // Процесс-потребитель
        close(fd[1]);  // Закрываем запись
        read(fd[0], buffer, sizeof(buffer));
        printf("Child received: %s\n", buffer);
        close(fd[0]);
    } else {
        // Процесс-производитель
        close(fd[0]);  // Закрываем чтение
        write(fd[1], "Hello from parent", 17);
        close(fd[1]);
        wait(NULL);
    }
    return 0;
}

Характеристики:

  • Синхронная коммуникация
  • Однонаправленная (unidirectional)
  • Работает между процессами-предками и потомками
  • Данные хранятся в памяти ядра

2. Named Pipes (FIFO)

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>

int main() {
    const char* fifo_path = "/tmp/myfifo";
    
    // Создаём named pipe
    mkfifo(fifo_path, 0666);
    
    if (fork() == 0) {
        // Процесс-производитель
        int fd = open(fifo_path, O_WRONLY);
        write(fd, "Hello FIFO", 10);
        close(fd);
    } else {
        // Процесс-потребитель
        int fd = open(fifo_path, O_RDONLY);
        char buffer[50];
        read(fd, buffer, sizeof(buffer));
        printf("Received: %s\n", buffer);
        close(fd);
        wait(NULL);
    }
    
    unlink(fifo_path);  // Удаляем FIFO
    return 0;
}

Отличия от обычных pipes:

  • Существует в файловой системе (как файл)
  • Работает между любыми процессами (не обязательно родитель-потомок)
  • Более гибко, но медленнее

3. Sockets (TCP/UDP)

TCP сокет (надёжная доставка):

#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

// Сервер
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(9999);

bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
listen(server_fd, 5);

int client = accept(server_fd, NULL, NULL);
char buffer[256];
recv(client, buffer, sizeof(buffer), 0);
printf("Server received: %s\n", buffer);
close(client);
close(server_fd);

UDP сокет (быстрая, ненадёжная доставка):

int sock = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);

bind(sock, (struct sockaddr*)&addr, sizeof(addr));

char buffer[256];
recv(sock, buffer, sizeof(buffer), 0);
sendto(sock, "Response", 8, 0, (struct sockaddr*)&addr, sizeof(addr));
close(sock);

Когда использовать:

  • TCP — когда важна надёжность (web, БД)
  • UDP — когда важна скорость (игры, потоки видео)

4. Shared Memory (Общая память)

#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

struct Message {
    char text[256];
    int value;
};

int main() {
    // Создаём shared memory сегмент
    int shmid = shmget(IPC_PRIVATE, sizeof(Message), IPC_CREAT | 0666);
    
    if (fork() == 0) {
        // Процесс-писатель
        Message* msg = (Message*)shmat(shmid, NULL, 0);
        strcpy(msg->text, "Hello Shared Memory");
        msg->value = 42;
        shmdt(msg);
    } else {
        // Процесс-читатель
        sleep(1);  // Ждём писателя
        Message* msg = (Message*)shmat(shmid, NULL, 0);
        printf("Read: %s = %d\n", msg->text, msg->value);
        shmdt(msg);
        shmctl(shmid, IPC_RMID, NULL);
        wait(NULL);
    }
    return 0;
}

Характеристики:

  • Очень быстро (память, без копирования)
  • Нужна синхронизация (семафоры, мьютексы)
  • Сложнее отлаживать
  • Требует осторожности с race conditions

5. Message Queues (Очереди сообщений)

#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

struct Message {
    long mtype;  // Тип сообщения (должен быть > 0)
    char mtext[256];
};

int main() {
    int msgid = msgget(IPC_PRIVATE, IPC_CREAT | 0666);
    
    if (fork() == 0) {
        // Отправитель
        Message msg;
        msg.mtype = 1;
        strcpy(msg.mtext, "Hello Queue");
        msgsnd(msgid, &msg, sizeof(msg.mtext), 0);
    } else {
        // Получатель
        Message msg;
        msgrcv(msgid, &msg, sizeof(msg.mtext), 0, 0);
        printf("Received: %s\n", msg.mtext);
        msgctl(msgid, IPC_RMID, NULL);
        wait(NULL);
    }
    return 0;
}

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

  • Асинхронная коммуникация
  • Сообщения идентифицируются по типу
  • Порядок гарантирован

6. Memory-Mapped Files

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main() {
    // Создаём файл
    int fd = open("/tmp/mmap_file", O_CREAT | O_RDWR, 0666);
    ftruncate(fd, 4096);  // Размер 4KB
    
    // Отображаем в память
    void* addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                      MAP_SHARED, fd, 0);
    
    if (fork() == 0) {
        // Писатель
        strcpy((char*)addr, "Hello mmap");
    } else {
        // Читатель
        sleep(1);
        printf("Read: %s\n", (char*)addr);
        wait(NULL);
    }
    
    munmap(addr, 4096);
    close(fd);
    return 0;
}

Когда использовать:

  • Обмен большими объёмами данных
  • Совместный доступ к файлам
  • Low-latency IPC

7. Signals (Сигналы)

#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void signal_handler(int sig) {
    printf("Received signal %d\n", sig);
}

int main() {
    signal(SIGUSR1, signal_handler);
    
    if (fork() == 0) {
        sleep(1);
        kill(getppid(), SIGUSR1);  // Отправляем сигнал родителю
    } else {
        sleep(2);
        printf("Parent done\n");
        wait(NULL);
    }
    return 0;
}

Применение:

  • Простые уведомления между процессами
  • Обработка ошибок (SIGSEGV, SIGABRT)
  • Не для передачи больших данных

8. DBus (для Linux систем)

// Требует libdbus
#include <dbus/dbus.h>

int main() {
    DBusConnection* conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
    // Отправляем методы, сигналы через DBus
    return 0;
}

Когда использовать:

  • System-level IPC в Linux
  • Интеграция с системными сервисами
  • High-level interface

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

МетодСкоростьНадёжностьСложностьОбласть
PipesВысокаяВысокаяНизкаяПроцессы-потомки
Named PipesВысокаяВысокаяСредняяЛюбые процессы
TCP/UDPСредняяПеременнаяСредняяСеть
Shared MemoryОчень высокаяНизкаяВысокаяЛокальные процессы
Message QueuesВысокаяВысокаяСредняяАсинхронные
Memory-MappedОчень высокаяВысокаяСредняяБольшие данные
SignalsОчень низкаяНизкаяНизкаяПростые события
DBusСредняяВысокаяСредняяSystem services

Best Practices

1. Выбор метода

// Нужна скорость + большие данные → Shared Memory
// Надёжность + простота → TCP Socket
// Асинхронные сообщения → Message Queue
// Простой сигнал → Signal

2. Синхронизация при shared memory

// ВСЕГДА используй мьютексы/семафоры
struct SharedData {
    sem_t sem;
    int value;
};

3. Обработка ошибок

if (send(sock, data, size, 0) == -1) {
    perror("send failed");
    // Обработка ошибки
}

Итоговые выводы

  • Pipes — простейший способ (parent-child)
  • Named Pipes (FIFO) — для любых процессов
  • Sockets (TCP) — стандарт для надёжной коммуникации
  • Shared Memory — максимальная производительность
  • Message Queues — асинхронная коммуникация
  • Memory-Mapped Files — для больших объёмов данных
  • Signals — для простых событий
  • DBus — система-уровневая коммуникация в Linux