Промывка трубы без закрытия в C
Я нашел здесь множество тем, спрашивающих о том, как можно очистить канал после записи в него, не закрывая его. В каждой теме я мог видеть разные предложения, но не мог найти определенного решения.
Вот краткое резюме:
Самый простой способ избежать блокировки чтения в канале - записать точное число читаемых байтов.
Это также можно сделать, используя ptmx вместо канала, но люди говорят, что это может быть слишком много.
Примечание: невозможно использовать fsync с каналами
Есть ли другие более эффективные решения?
Редактировать:
Сброс будет удобен, когда отправитель хочет написать n символов, но клиент читает m символов (где m>n). Клиент заблокирует ожидание еще нескольких символов. Если отправитель хочет снова связаться с клиентом, он оставляет его без возможности закрыть канал, и просто отправка точного количества байтов может стать хорошим источником ошибок.
Приемник работает так, и его нельзя изменить:
while((n=read(0, buf, 100)>0){
process(buf)
поэтому отправитель хочет, чтобы его обработали: "file1" и "file2", для которых придется:
write(pipe[1], "file1\0*95", 100);
write(pipe[1], "file2\0*95", 100);
я ищу способ сделать что-то подобное (без необходимости использовать \n в качестве разделителя):
write(pipe[1], "file1\nfile2", 11); //it would have worked if it was ptmx
(Используя чтение и запись)
1 ответ
Промывка в смысле fflush()
не имеет отношения к каналам, потому что они не представлены как потоки C. Поэтому нет никакого пользовательского буфера для очистки. Так же, fsync()
также не имеет отношения к каналам, потому что нет никакого внутреннего хранилища для данных. Данные, успешно записанные в канал, существуют в ядре и только в ядре, пока они не будут успешно прочитаны, поэтому для fsync()
сделать. В целом, промывка / синхронизация применима только там, где задействовано промежуточное хранилище, что не относится к трубам.
После разъяснения ваш вопрос, похоже, касается установления границ сообщений для связи по каналу. Вы правы, что закрытие конца записи канала будет сигнализировать о границе - не только одного сообщения, но и всего потока сообщений - но, конечно, это окончательно. Вы также правы, что нет внутренних границ сообщения. Тем не менее, вы, кажется, работаете, по крайней мере, от некоторого заблуждения:
Самый простой способ избежать блокировки чтения в канале - записать точное число читаемых байтов.
[...]
Сброс будет удобен, когда отправитель хочет написать n символов, но клиент читает m символов (где m> n). Клиент заблокирует ожидание еще нескольких символов.
Будет ли читатель блокировать, полностью зависит от того, как читатель реализован. В частности, read(2)
Системный вызов никоим образом не гарантирует передачу запрошенного количества байтов перед возвратом. Он может и будет выполнять краткое чтение при некоторых обстоятельствах. Хотя детали не указаны, обычно вы можете рассчитывать на короткое чтение, когда хотя бы один символ может быть передан без блокировки, но не весь запрошенный номер. Аналогичное относится к write(2)
, Таким образом, самый простой способ избежать read()
блокировка должна гарантировать, что вы записываете хотя бы один байт в канал для этого read()
звонок для перевода.
Фактически, люди обычно приходят к этому вопросу с противоположной стороны: необходимо быть уверенным, что они получают определенное количество байтов, и, следовательно, иметь дело с возможностью короткого чтения как осложнения (для решения проблемы нужно выполнить read()
в петле). Вам нужно будет это учитывать, но у вас есть преимущество, которое ваш клиент вряд ли заблокирует при описанных вами обстоятельствах; это не та проблема, о которой вы думаете.
Тем не менее, в любом виде потоковой связи есть внутренняя проблема, связанная с границами сообщений, и вам придется с ней справиться. Есть несколько подходов; Среди наиболее часто используемых
Сообщения фиксированной длины. Получатель может читать до тех пор, пока успешно не передаст необходимое количество байтов; любая вовлеченная блокировка уместна и необходима. При таком подходе сценарий, который вы постулируете, просто не возникает, но автору может потребоваться дополнить его сообщения.
Сообщения с разделителями. Затем получатель читает, пока не обнаружит, что он получил разделитель сообщения (например, символ новой строки или нулевой байт). В этом случае получатель должен быть готов к тому, что границы сообщений не будут согласованы с байтовыми последовательностями, передаваемыми
read()
звонки. Маркировка конца сообщения путем закрытия канала может считаться частным случаем этой альтернативы.Встроенные метаданные длины сообщения. Это может принимать разные формы, но одна из самых простых состоит в том, чтобы структурировать сообщения как поле длины сообщения целочисленной длины с фиксированной длиной, за которым следует такое количество байтов данных сообщения. Затем читатель в любой момент знает, сколько байтов ему нужно прочитать, поэтому он не будет блокировать без необходимости.
Они могут использоваться по отдельности или в комбинации для реализации протокола прикладного уровня для связи между вашими процессами. Естественно, стороны сообщения должны согласовать детали протокола, чтобы сообщение было успешным.