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

Каналы pipes

Каналы pipes — это механизм межпроцессного взаимодействия (IPC), реализованный в операционных системах семейства Unix (включая Linux) и в некоторых других системах (например, в Microsoft Windows под названием «именованные каналы»), который обеспечивает однонаправленную передачу потока данных от одного процесса к другому. В Unix-подобных системах каналы реализованы через системные вызовы pipe() (для неименованных каналов) и mkfifo() (для именованных каналов, также называемых FIFO). Основное свойство канала — это последовательный поток байтов без структуры сообщений, работающий по принципу «первым вошёл — первым вышел» (FIFO). Каналы являются одним из старейших и наиболее распространённых механизмов IPC в Unix, начиная с первых версий системы.

История

Концепция каналов была впервые реализована в операционной системе Unix в начале 1970-х годов. Автором идеи считается Дуглас Макилрой, который предложил использовать символ вертикальной черты (|) для соединения вывода одной программы с вводом другой. Первая реализация была выполнена Кеном Томпсоном для Unix Version 3 (1973 год). Впоследствии механизм был стандартизирован в рамках POSIX.1 (IEEE Std 1003.1), что обеспечило его переносимость между различными Unix-подобными системами.

В операционной системе MS-DOS и ранних версиях Windows также существовала аналогичная функциональность, но она была менее гибкой. В современных версиях Windows (начиная с Windows NT) реализованы как анонимные, так и именованные каналы, хотя их API отличается от Unix.

Виды каналов

Неименованные (анонимные) каналы

Неименованные каналы создаются системным вызовом pipe(). Они существуют только в пределах одного процесса и его потомков, так как для доступа к каналу процесс должен иметь файловый дескриптор, который передаётся только через наследование при порождении дочерних процессов (fork()). Анонимный канал является однонаправленным: один конец предназначен только для записи, другой — только для чтения. После закрытия всех дескрипторов канал автоматически уничтожается.

Типичное использование анонимного канала — в командной оболочке (shell) при выполнении конвейера команд, например: `` ls -l | grep "txt" | wc -l ` Здесь оболочка создаёт два анонимных канала: один между ls и grep, второй между grep и wc`.

Именованные каналы (FIFO)

Именованные каналы создаются с помощью системного вызова mkfifo() или команды mkfifo. В отличие от анонимных, они имеют имя в файловой системе (как обычный файл) и могут использоваться для обмена данными между любыми процессами, не обязательно связанными родством. Именованный канал существует до тех пор, пока не будет удалён из файловой системы (например, командой rm). Он также является однонаправленным, но допускает множественные операции чтения и записи с разных процессов.

В Windows именованные каналы реализованы через API CreateNamedPipe и ConnectNamedPipe и могут работать как в однонаправленном, так и в двунаправленном режиме.

Устройство и характеристики

Канал представляет собой буфер в памяти ядра операционной системы. Размер буфера по умолчанию в Linux составляет 65536 байт (64 КБ), но может быть изменён с помощью системного вызова fcntl() или через файл /proc/sys/fs/pipe-max-size. Данные записываются в буфер последовательно, а чтение извлекает их в том же порядке.

Основные характеристики:

  • Однонаправленность: данные движутся только от записывающего процесса к читающему. Для двустороннего обмена требуется создание двух каналов.
  • Потоковая природа: данные не имеют границ сообщений — канал воспринимает поток байтов. Если процесс записал 100 байтов, читающий процесс может прочитать их по частям (например, 10 раз по 10 байтов) или целиком.
  • Блокирующий режим: по умолчанию операции чтения и записи на канале являются блокирующими. Чтение из пустого канала блокирует процесс до появления данных. Запись в полный канал блокируется до освобождения места в буфере. Существует возможность установки неблокирующего режима с помощью флага O_NONBLOCK.
  • Ограниченная пропускная способность: скорость передачи данных ограничена размером буфера и частотой системных вызовов.
  • Отсутствие синхронизации: каналы не предоставляют встроенных средств синхронизации доступа; при одновременной записи нескольких процессов данные могут перемешиваться (если размер записи превышает PIPE_BUF — обычно 4096 байт — то атомарность не гарантируется).

Применение

Командные конвейеры (pipes in shell)

Наиболее известное применение каналов — в командных оболочках Unix. Символ | (вертикальная черта) позволяет соединить стандартный вывод одной команды со стандартным вводом другой. Это позволяет строить сложные цепочки обработки данных без создания промежуточных файлов. Примеры: `` cat file.txt | sort | uniq ps aux | grep "python" | awk '{print $2}' ``

Межпроцессное взаимодействие в программах

В программировании на C/C++ каналы используются для организации обмена данными между родительским и дочерними процессами. Типичный сценарий — родительский процесс порождает дочерний, закрывает ненужные концы канала и использует оставшийся для передачи данных. В более сложных архитектурах (например, в веб-серверах) каналы могут применяться для передачи запросов между рабочими процессами.

Именованные каналы как средство интеграции

Именованные каналы (FIFO) часто используются для организации обмена данными между независимыми процессами, например, между сервером и клиентом в локальной сети. В Windows именованные каналы поддерживают сетевое взаимодействие (через протокол SMB) и могут использоваться для удалённого вызова процедур (RPC).

Логирование и мониторинг

Каналы могут применяться для перенаправления логов или потоков данных в реальном времени. Например, процесс, генерирующий логи, может записывать их в именованный канал, а отдельный процесс-анализатор — читать их оттуда.

Ограничения и альтернативы

Основные ограничения каналов:

  • Однонаправленность: для двустороннего обмена требуется два канала.
  • Отсутствие структуры сообщений: канал не сохраняет границы между отдельными записями, что может быть неудобно для некоторых протоколов.
  • Зависимость от файловых дескрипторов: в Unix количество одновременно открытых каналов ограничено системным лимитом (обычно 1024 или 65536 на процесс).
  • Сложность синхронизации: при множественных писателях возможна интерливинг данных.

Альтернативные механизмы IPC включают:

  • Сокеты (Unix domain sockets): обеспечивают двунаправленную связь и поддерживают структурированные сообщения.
  • Разделяемая память (shared memory): обеспечивает высокую скорость передачи больших объёмов данных, но требует дополнительной синхронизации (семафоры, мьютексы).
  • Очереди сообщений (POSIX message queues): сохраняют границы сообщений и поддерживают приоритеты.
  • Сигналы (signals): используются для асинхронного уведомления, но не для передачи данных.

Примеры использования в коде

Пример создания анонимного канала на языке C (Linux): ```c

include <unistd.h>

include <stdio.h>

include <string.h>

int main() { int pipefd[2]; pid_t pid; char buffer[100];

if (pipe(pipefd) == -1) { perror("pipe"); return 1; }

pid = fork(); if (pid == 0) { // дочерний процесс close(pipefd[1]); // закрываем конец для записи read(pipefd[0], buffer, sizeof(buffer)); printf("Дочерний процесс получил: %s\n", buffer); close(pipefd[0]); } else { // родительский процесс close(pipefd[0]); // закрываем конец для чтения char *msg = "Привет от родителя!"; write(pipefd[1], msg, strlen(msg) + 1); close(pipefd[1]); } return 0; } ```

Пример создания именованного канала в командной строке Linux: ``` mkfifo mypipe

В одном терминале:

cat > mypipe

В другом терминале:

cat < mypipe ```

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

  • Символ | для обозначения канала в командной строке был предложен Дугласом Макилроем и впервые появился в Unix Version 3.
  • В ранних версиях Unix каналы были реализованы через временные файлы, но позже были переведены на буферизацию в ядре.
  • В Linux существует системный вызов splice(), позволяющий передавать данные между двумя файловыми дескрипторами без копирования в пользовательское пространство, что повышает производительность каналов.
  • Максимальный размер буфера канала в Linux может быть увеличен до 1 МБ (начиная с ядра 2.6.35).

Источники

  • Stevens, W. Richard. «Advanced Programming in the UNIX Environment». Addison-Wesley, 1992.
  • Kerrisk, Michael. «The Linux Programming Interface». No Starch Press, 2010.
  • IEEE Std 1003.1-2017 (POSIX.1-2017).
  • Документация Linux: man 7 pipe, man 3 mkfifo, man 2 pipe.

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

На главную BFOmetr →