В КУРСЕ?

Разбираемся в теме

Писатель закончил, а чтение зависло: как открытый дескриптор мешает завершить UNIX-конвейер

В системном программировании канал передаёт поток байтов между процессами. Частая ошибка возникает в момент завершения: один процесс больше ничего не отправляет, но другой продолжает ждать. Чтобы понять ситуацию, недостаточно посмотреть на последний вызов записи. Нужно выяснить, какие дескрипторы пишущего конца канала всё ещё остаются открытыми.

Пустой канал не всегда означает конец потока

Для обычного блокирующего чтения важно различать два состояния. Первое: сейчас данных нет, но возможность дальнейшей записи сохраняется. Тогда чтение может ожидать поступления байтов. Второе: буфер уже исчерпан и все дескрипторы пишущего конца закрыты. Тогда чтение сообщает конец потока, возвращая ноль. В нашем разборе запрашивается ненулевое количество байтов и не рассматриваются ошибки или прерывания сигналами. Это уточнение помогает не путать нормальное завершение с любым отсутствием результата. Канал также не хранит границы отдельных сообщений: порядок порций чтения не обязан повторять порции записи.

Ненужная копия способна сохранить возможность записи

Представим вымышленную программу: родитель создаёт канал, затем дочерний процесс. Родитель должен читать отчёт, а ребёнок отправлять его. После создания дочернего процесса у обоих оказываются дескрипторы обоих концов. Ребёнок закрывает ненужный читающий конец, записывает отчёт и закрывает пишущий. Родитель получает все байты, но забывает закрыть собственный пишущий конец. Теперь данных уже нет, однако открытая возможность записи остаётся у самого читателя. Поэтому ожидание не доказывает, что ребёнок ещё работает или что отчёт потерян. Ошибка находится в распределении оставшихся дескрипторов.

Завершение проектируют вместе с направлением данных

Полезно заранее назначить каждому процессу роль и отметить, какие концы канала ему действительно нужны. В нашем примере родителю необходим читающий конец, ребёнку пишущий. Остальные копии закрывают в соответствующих процессах; завершение работы отправителя включает закрытие его пишущего конца. Если канал унаследовал дополнительный процесс или появился дублированный дескриптор, его тоже учитывают в схеме. Увеличение задержки перед чтением не устраняет открытую копию. Диагностика должна отвечать на конкретный вопрос: кто ещё сохраняет возможность записать данные, даже если фактически никогда ею не пользуется.

Попробуйте на практике

Разберите состояние вымышленной программы на бумаге. Запуск процессов, изменение системных настроек и работа с настоящими файлами не требуются.

  1. Нарисуйте два прямоугольника: родитель и ребёнок. В каждом обозначьте читающий и пишущий дескрипторы одного канала, существующие сразу после создания дочернего процесса.
  2. Назначьте родителю чтение, ребёнку запись. Вычеркните у ребёнка читающий конец, но намеренно оставьте у родителя оба дескриптора.
  3. Предположите, что ребёнок отправил весь отчёт и закрыл пишущий конец, а родитель прочитал все накопленные байты. Перечислите оставшиеся пишущие дескрипторы.
  4. Исправьте схему, закрыв ненужную пишущую копию у родителя. Объясните, при каком сочетании состояния буфера и дескрипторов следующее чтение сможет сообщить конец потока.

Как проверить результат. В ошибочном варианте остаётся один пишущий дескриптор у родителя. В исправленном варианте пишущих дескрипторов не остаётся, а буфер пуст. Вывод объясняет завершение потока, не приписывая задержку скорости процессора или размеру отчёта.

Частые вопросы

Закрытие пишущего конца сразу уничтожает уже отправленные байты?

Нет. Читатель может получить оставшиеся в канале данные. Конец потока обнаруживается после их исчерпания, когда пишущих дескрипторов больше нет.

Неблокирующий режим автоматически исправляет такую ошибку?

Нет. Он меняет поведение операции при временном отсутствии данных, но не закрывает лишние дескрипторы и не превращает открытый поток в завершённый.

Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.

Зарегистрируйтесь, чтобы уточнить возможность доступа к этому материалу

Зарегистрироваться
← К списку материалов