0Pricing
C Academy · Урок

Каналы

Обмен данными между родителем и потомком

«Каналы» — бесплатный урок C Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C Academy содержит 4 уроков всего.

Что такое канал

Канал — это однонаправленный канал связи между связанными процессами. Один процесс записывает в него байты, а другой считывает их, словно данные движутся по конвейеру.

Два файловых дескриптора

Вызов pipe() заполняет массив из двух элементов: fd[0] — конец для чтения, а fd[1] — конец для записи. Данные, записанные в fd[1], можно прочитать из fd[0].

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

int make_pipe(int fd[2]) {
    if (pipe(fd) < 0) { perror("pipe"); return -1; }
    /* fd[0] read end, fd[1] write end */
    return 0;
}

Объединение с fork

После fork() родительский и дочерний процессы совместно используют дескрипторы канала. Так родительский и дочерний процессы обмениваются данными: один записывает, другой читает.

Закрывайте неиспользуемые концы

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

#include <unistd.h>

void parent_as_writer(int fd[2]) {
    close(fd[0]);              /* parent will not read */
    write(fd[1], "hello", 5);  /* parent writes */
    close(fd[1]);              /* signal EOF to reader */
}

Пример взаимодействия родителя и ребёнка

Родитель записывает сообщение, а ребёнок читает его. Каждый процесс закрывает ненужный ему конец.

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

void demo(int fd[2]) {
    if (fork() == 0) {           /* child: reader */
        close(fd[1]);
        char buf[64];
        ssize_t n = read(fd[0], buf, sizeof buf - 1);
        buf[n] = 0;
        printf("child got: %s\n", buf);
        close(fd[0]);
    } else {                     /* parent: writer */
        close(fd[0]);
        write(fd[1], "hi child", 8);
        close(fd[1]);
    }
}

Чтение возвращает EOF

Когда все концы для записи закрыты, read возвращает 0, обозначая конец файла. Если конец для записи остаётся открытым, читающий процесс бесконечно блокируется в ожидании новых данных — это распространённая ошибка.

Ёмкость канала и блокировка

У канала есть буфер конечного размера (часто 64 KB). Если буфер заполняется, write блокируется, пока читающий процесс не освободит место. Если буфер пуст, read блокируется до поступления данных. Такое управление потоком синхронизирует два процесса.

Перенаправление с помощью dup2

dup2 может заменить стандартный ввод или стандартный вывод процесса концом канала. Так оболочки реализуют cmd1 | cmd2, направляя вывод одной команды на вход следующей.

#include <unistd.h>

void stdout_to_pipe(int fd[2]) {
    close(fd[0]);
    dup2(fd[1], STDOUT_FILENO); /* writes to stdout go into pipe */
    close(fd[1]);
}

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

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

#include <sys/stat.h>

int make_fifo(const char *path) {
    return mkfifo(path, 0666); /* creates a FIFO file */
}

Опасность SIGPIPE

Запись в канал с закрытым концом для чтения вызывает SIGPIPE, который по умолчанию завершает процесс. Либо игнорируйте этот сигнал, либо проверяйте ошибку EPIPE, возвращённую функцией write.

Когда использовать каналы

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

Быстрая проверка

Проверьте, насколько хорошо Вы понимаете работу каналов.

Повторение

Вы научились использовать каналы для связи между родительским и дочерним процессами.

  • pipe() возвращает fd[0] (для чтения) и fd[1] (для записи)
  • Объединяйте канал с fork и закрывайте неиспользуемые концы
  • read возвращает 0 при EOF, когда все концы для записи закрыты
  • FIFO связывают несвязанные процессы; остерегайтесь SIGPIPE

Часто задаваемые вопросы

Урок «Каналы» бесплатный?

Да — полный текст урока «Каналы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C Academy, подпишись на CoddyKit PRO. Курс C Academy содержит 4 уроков всего.

Чему я научусь в уроке «Каналы»?

Обмен данными между родителем и потомком Ты практикуешь C Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C Academy?

Предыдущий опыт не требуется. C Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Каналы»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C Academy?

Да. Каждый урок C Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Каналы
  2. Разделяемая память
  3. Очереди сообщений
  4. Сигналы
← Назад к C Academy