Каналы: pipe и FIFO

⏱ 30 мин · 🧩 заданий: 5

Канал pipe

Канал — однонаправленный поток байтов в ядре с двумя концами: в один пишут, из другого читают.

int fds[2];
pipe(fds);        /* fds[0] — для чтения, fds[1] — для записи */

Канал не имеет имени, поэтому им пользуются родственные процессы: создать до fork — и дескрипторы унаследуются.

родитель ядро: буфер канала (кольцевой, 64 КиБ) потомок fds[0] ◀── read ─────── [ h i ... ] ◀─ write ── fds[1] (fds[1] закрыт) (fds[0] закрыт)
int fds[2];
char buf[64];
pipe(fds);
pid_t pid = fork();
if (pid == 0) {                 /* потомок — писатель */
    close(fds[0]);
    const char msg[] = "hello from child\n";
    write(fds[1], msg, sizeof(msg) - 1);
    close(fds[1]);
    _exit(0);
}
close(fds[1]);                  /* родитель — читатель: обязательно закрыть лишний конец */
ssize_t n;
while ((n = read(fds[0], buf, sizeof buf)) > 0)
    write(1, buf, n);
close(fds[0]);
wait(NULL);

Правила работы канала

Ситуация Результат
read, канал пуст, писатели есть блокировка до появления данных
read, канал пуст, все концы для записи закрыты 0 — конец файла
write, есть место запись (до PIPE_BUF = 4096 байт — атомарно)
write, канал полон блокировка, пока читатель не освободит место
write, все концы для чтения закрыты сигнал SIGPIPE (по умолчанию — завершение), write вернёт EPIPE

Закрывайте лишние концы

Конец файла наступает, только когда закрыты все дескрипторы записи во всех процессах. Если читатель не закрыл свою копию fds[1], он никогда не получит 0 и зависнет навсегда.

Канал — ещё и средство синхронизации: read из пустого канала блокирует процесс, пока другой не запишет. Это можно использовать как «семафор для бедных».

Конвейер shell

ls | wc -l:

int fds[2];
pipe(fds);
if (fork() == 0) {              /* ls */
    dup2(fds[1], STDOUT_FILENO);
    close(fds[0]); close(fds[1]);
    execlp("ls", "ls", (char *)NULL);
    _exit(127);
}
if (fork() == 0) {              /* wc -l */
    dup2(fds[0], STDIN_FILENO);
    close(fds[0]); close(fds[1]);
    execlp("wc", "wc", "-l", (char *)NULL);
    _exit(127);
}
close(fds[0]); close(fds[1]);   /* shell закрывает оба конца */
wait(NULL); wait(NULL);

Именованный канал FIFO

FIFO — канал с именем в файловой системе. Им могут пользоваться неродственные процессы.

mkfifo myfifo
cat myfifo &          # читатель ждёт
echo hi > myfifo      # писатель
mkfifo("/tmp/myfifo", 0600);
int fd = open("/tmp/myfifo", O_RDONLY);   /* блокируется, пока кто-то не откроет на запись */

Данные через диск не идут: в ФС только имя (inode типа p), буфер — в ядре.

Обзор средств IPC

Средство Для чего Неродственные процессы
pipe поток байтов, родитель ↔ потомок нет
FIFO поток байтов да
сигналы уведомления (RT — с маленьким значением) да
разделяемая память быстрый обмен большими данными, нужна синхронизация да (shm_open, shmget)
семафоры синхронизация да
очереди сообщений сообщения с границами и приоритетами да
сокеты поток или сообщения, в том числе по сети да

Итог

  • pipe(fds): fds[0] — чтение, fds[1] — запись; создавать до fork, лишние концы закрывать.
  • Пустой канал блокирует читателя; без писателей — конец файла; без читателей — SIGPIPE.
  • Конвейер shell = pipe + fork + dup2 + exec.
  • FIFO — именованный канал для неродственных процессов.

Практика решено 0 из 5

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Написать код

Создайте канал int fds[2]; в потомке закройте конец для чтения, запишите строку "hi" (2 байта) в конец для записи и выйдите _exit(0); в родителе закройте конец для записи и прочитайте до 2 байт в buf.

Задание 2 Заполнить таблицу

Что произойдёт в каждой ситуации?

Процесс блокируется (ждёт)read возвращает 0 (конец файла)SIGPIPE и ошибка EPIPEОперация выполняется сразу
read из пустого канала, писатели ещё есть
read из пустого канала, все концы для записи закрыты
write в канал, все концы для чтения закрыты
write в полный канал (буфер 64 КиБ заполнен), читатели есть
write 100 байт в канал со свободным местом
Отметьте ячейку — «+», пустая — «−».
Задание 3 Один вариант ответа

Родитель создал канал, потомок пишет данные и завершается, но родитель забыл закрыть свой конец для записи и читает в цикле до конца файла. Что будет?

Задание 4 Ввод ответа

Как в shell создать именованный канал с именем myfifo? (команда)

Задание 5 Несколько вариантов

Какие средства IPC позволяют передавать данные между процессами?

Войдите, чтобы отмечать пройденные уроки.