Давайте сначала разберемся с вашим первоначальным вопросом. Обратите внимание, что man 7 pipe
должен дать некоторую полезную информацию об этом.
Скажем, у нас стандартный ввод перенаправлен на сторону ввода дескриптора, созданного с помощью вызова pipe
, например:
pipe(p);
// ... fork a child to write to the output side of the pipe ...
dup2(p[0], 0); // redirect standard input to input side
и мы звоним:
bytes = read(0, buf, 100);
Во-первых, обратите внимание, что это ведет себя не иначе, чем простое чтение непосредственно из p[0]
, поэтому мы могли бы просто сделать:
pipe(p);
// fork child
bytes = read(p[0], buf, 100);
Тогда, по сути, есть три случая:
Если в канале есть байты (т. Е. Хотя бы один байт был записан, но еще не прочитан), то вызов чтения немедленно вернется и вернет все доступные байты, максимум до 100 байт. Возвращаемым значением будет количество прочитанных байтов, и оно всегда будет положительным числом от 1 до 100.
Если канал пуст (без байтов) и выходная сторона закрыта, буфер не будет затронут, и вызов немедленно вернется с возвращаемым значением 0.
В противном случае вызов чтения будет блокироваться до тех пор, пока что-то не будет записано в канал, или если выходная сторона не будет закрыта, а затем вызов чтения немедленно вернется с использованием правил в случаях 1 и 2.
Итак, если вызов read()
возвращает 0, это означает, что достигнут конец файла, и больше не ожидается никаких байтов. Ожидание дополнительных данных происходит автоматически, и после ожидания вы получите либо данные (положительное возвращаемое значение), либо сигнал конца файла (нулевое возвращаемое значение). В особом случае, когда другой процесс записывает несколько байтов, а затем немедленно закрывает (выходную сторону) канал, следующий вызов read()
вернет положительное значение до указанного count
. Последующие read()
вызовы продолжат возвращать положительные значения, пока есть больше данных для чтения. Когда данные исчерпаны, вызов read()
вернет 0 (поскольку канал закрыт).
В Linux вышеприведенное всегда верно для каналов и любых положительных значений count
. Там могут быть различия для вещей других , чем трубы. Кроме того, если count
равно 0, вызов read()
всегда будет возвращаться немедленно с возвращаемым значением 0. Обратите внимание, что если вы пытаетесь написать код, работающий на платформах, отличных от Linux, вам, возможно, следует быть более осторожным. Реализация может возвращать ненулевое число байтов меньше, чем запрошенное число, даже если в конвейере доступно больше байтов - это может означать, что существует предел, определенный реализацией (поэтому вы никогда не получите больше 4096 байтов, независимо от того, сколько вы запрашиваете, например) или что это ограничение, определяемое реализацией, изменяется от вызова к вызову (поэтому, если вы запрашиваете байты за границей страницы в буфере ядра, вы получаете только конец страницы или что-то в этом роде). В Linux ограничений нет - вызов read всегда вернет все доступное значение до count
, независимо от того, насколько велик count
.
В любом случае, идея заключается в том, что что-то вроде следующего кода должно надежно считывать все байты из канала до тех пор, пока выходная сторона не будет закрыта, даже на платформах, отличных от Linux:
#define _GNU_SOURCE 1
#include <errno.h>
#include <unistd.h>
/* ... */
while ((count = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)))) > 0) {
// process "count" bytes in "buffer"
}
if (count == -1) {
// handle error
}
// otherwise, end of data reached
Если канал никогда не закрывается («бесконечный» или «непрерывный» поток), цикл while
будет работать вечно, потому что read
будет блокироваться, пока не вернет ненулевой счетчик байтов.
Обратите внимание, что труба также может быть переведена в неблокирующий режим, что существенно меняет поведение, но приведенное выше является режимом блокировки по умолчанию.
Относительно ваших UPD вопросов:
Да, read
задерживает выполнение программы до тех пор, пока данные не станут доступны, но NO , он не обязательно ожидает count
байтов. Он будет ожидать хотя бы одного непустого write
в трубу, и это разбудит процесс; когда процесс получит возможность запуска, он вернет все доступные до , но не обязательно равные count
байтов. Обычно это означает, что если другой процесс записывает 5 байтов, заблокированный вызов read(fd, buffer, 100)
вернет 5 и выполнение продолжится. Да, если read
возвращает 0
, это сигнал о том, что больше нет данных для чтения, и сторона записи канала была закрыта (поэтому больше никаких данных никогда не будет доступно). Нет , значение EOF
не отображается в буфере. Там будут отображаться только считанные байты, и буфер не будет затронут, когда read()
вернет 0, поэтому он будет содержать все, что было до вызова read()
.
Относительно вашего UPD2 комментария:
Да, в Linux EOF - это константа, равная целому числу -1
. (Технически, в соответствии со стандартом C99, это целочисленная константа, равная отрицательному значению; возможно, кто-то знает платформу, где она отличается от -1
.) Эта константа не используется интерфейсом read()
, и она конечно не записывается в буфер. Хотя read()
возвращает -1 в случае ошибки, было бы плохой практикой сравнивать возвращаемое значение из read()
с EOF вместо -1. Как вы заметили, значение EOF действительно используется только для функций библиотеки C, таких как getc()
и getchar()
, чтобы отличить конец файла от успешно прочитанного символа.