Проще говоря, ваш шаблон кода является мусором.Позвольте мне объяснить, почему.
Каждый канал является однонаправленным.
Если вы используете канал для отправки данных от дочернего элемента к родительскому, закройте конец чтения у дочернего элементаи конец записи в родительском.Это позволяет родителю видеть, когда дочерний элемент (конец записи) закрывает канал или завершает работу, так как read()
вернет -1
с errno == EPIPE
.
Если вы используете канал для отправки данных изродительский для дочернего, закройте конец чтения в родительском и конец записи в дочернем.Это позволяет родителю определить, выходит ли ребенок преждевременно, так как write()
затем вернется с -1
с errno == EPIPE
и в родителе появится сигнал SIGPIPE.
Если вам нужна двунаправленная «труба» между родителем и потомком, используйте пару сокетов потока домена Unix через socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair)
.
Такая пара сокетов очень похожа на трубуза исключением того, что пара сокетов является двунаправленной.Вы также можете использовать send(descriptor, buffer, length, MSG_NOSIGNAL)
вместо write(descriptor, buffer, length)
;в первом случае сигнал SIGPIPE не подается, если другой конец сокета уже закрыт.
Используйте один из дескрипторов в родительском, а другой - в дочернем.И родитель, и ребенок должны закрыть другой дескриптор.В противном случае один конец не сможет определить, когда другой конец закрыл свой дескриптор.
В некоторых случаях пара сокетов дейтаграммы домена Unix может быть предпочтительнее.Каждый send()
генерирует отдельную дейтаграмму, полученную с использованием одного recv()
.(То есть границы сообщения сохраняются.) Если принимающая сторона знает максимальный размер дейтаграммы, которую может отправлять отправляющая сторона, это чрезвычайно надежный и простой способ реализации двунаправленной связи между родительским и дочерним процессами;Лично я им часто пользуюсь.
read()
и write()
для труб и розеток могут быть короткие .
(POSIX заявляет, что вы всегда должны иметь возможность загружать как минимум 512 байт в канал, хотя Linux поддерживает, по крайней мере, до полной страницы, если я правильно помню.)
Это означает, что вместо одноговызов, вам нужно сделать цикл, пока у вас не будет столько данных, сколько вам нужно.
С сокетами send()
либо отправляет все данные, либо завершается с ошибкой -1
(с errno == EMSGSIZE
или некоторымидругой код ошибки).
Для сокетов дейтаграмм (сокеты дейтаграмм домена Unix, сокеты UDP), если буфер достаточно большой, recv()
либо получает всю дейтаграмму, либо завершается с ошибкой -1
(с * 1057)* задавать).Получение дейтаграмм нулевой длины ненадежно, поэтому не пытайтесь это делать.
Для потоковых сокетов recv()
может возвращать только некоторые данные (т. Е. Короткий или частичный прием).
Когда два процесса отправляют и получают или считывают и записывают данные друг другу, взаимоблокировка является серьезной, распространенной проблемой.
Проще говоря, оба конца могут закончить ожиданием одновременного чтения / записи на другом конце, при этом ничего не происходит.
Существует три типичных решения, позволяющих избежать тупика в таких ситуациях:
Используйте протокол запроса-ответа, чтобы одна конечная точка всегда инициировала связь, а затем ожидала ответа другой конечной точки.Не более одной конечной точки передает данные в любой момент времени.
Используйте неблокирующий / асинхронный ввод-вывод.То есть перед попыткой write()
/ send()
каждая конечная точка делает read()
/ recv()
, чтобы увидеть, отправил ли другой конец что-либо еще.Это поддерживает полнодуплексную связь (информация может передаваться в обоих направлениях одновременно).
Использовать отдельный поток для непрерывного read()
/ recv()
, а другой - write()
/send()
.Это по существу разделяет каждую розетку на две однонаправленные «дорожки», причем один поток обрабатывает только их направление.Это полезно для протоколов, когда один конец генерирует много данных, а другой отправляет случайные команды.
Объединив все вышесказанное, мы обнаружим, что нет единого шаблона, который следует использовать.Существуют варианты со значительными различиями, которые делают их лучше в одних случаях, но тяжелее / хуже в других.Нужно выбрать один в зависимости от ситуации под рукой.Если OP хочет увидеть лучший пример («шаблон»), они должны описать реальный проблемный случай, включая желаемое поведение.