Стандартный поток ввода / вывода - тип буферизации fgets()
Книга "Расширенное программирование в среде Unix" обсуждала канал в главе 15, она показывает, что мы должны обращать внимание на тип буферизации, когда имеем дело со стандартными функциями ввода-вывода.
Типы буферизации для различных открытых стандартных потоков ввода / вывода (обсуждаются в главе 5 книги):
- стандартная ошибка
unbuffered
- потоки, подключенные к оконечным устройствам
line-buffered
- все остальные потоки
fully-buffered
Когда родитель / ребенок подключается к pipe
конец (который должен быть FILE *
тип объекта, в соответствии с интерфейсом), который они использовали для связи должны быть fully-buffered
согласно списку правил выше (так как это поток, связанный с pipe
). Но поведение примера кода из этой главы кажется чем-то НЕ fully-buffered
,
Вот пример кода:
myuclc.c:
1 #include "apue.h"
2 #include <ctype.h>
3 int
4 main(void)
5 {
6 int c;
7 while ((c = getchar()) != EOF) {
8 if (isupper(c))
9 c = tolower(c);
10 if (putchar(c) == EOF)
11 err_sys("output error");
12 if (c == '\n')
13 fflush(stdout);
14 }
15 exit(0);
16 }
popen1.c:
1 #include "apue.h"
2 #include <sys/wait.h>
3 int
4 main(void)
5 {
6 char line[MAXLINE];
7 FILE *fpin;
8
9 if ((fpin = popen("myuclc", "r")) == NULL) // "myuclc" is executable file compile-link by "myuclc.c"
10 err_sys("popen error");
11 for ( ; ; ) {
12 fputs("prompt> ", stdout);
13 fflush(stdout);
14
15 if (fgets(line, MAXLINE, fpin) == NULL) /* read from pipe */
16 break;
17 if (fputs(line, stdout) == EOF)
18 err_sys("fputs error to pipe");
19 }
20 if (pclose(fpin) == -1)
21 err_sys("pclose error");
22 putchar('\n');
23 exit(0);
24 }
Итак, мой вопрос: fgets()
в строке 15 popen1.c
должно быть fully-buffered
в соответствии с правилами буферизации, почему он действует как line-buffered
или же unbuffered
:
Кроме того, я также пытался setvbuf()
до fgets()
специально установить тип буферизации _IOFBF
(полностью буферизованный) из fpin
, до сих пор не работают.
prompt> abc
abc
prompt> ABC
abc
prompt> efg
efg
prompt> EFG
efg
prompt>
2 ответа
В myuclc.c вы выполняете явный сброс для каждой новой строки:
12 if (c == '\n')
13 fflush(stdout);
Это заставляет поток быть буферизованным вручную. Всякий раз, когда вы очищаете канал, процесс на другом конце будет разблокирован и будет читать то, что было в буфере в то время.
"Правила буферизации" говорят о том, когда этот сброс происходит автоматически. Небуферизованные потоки автоматически сбрасываются после каждой команды записи (fprintf, fputc и т. Д.). Строковые буферизованные потоки автоматически сбрасываются всякий раз, когда в поток записывается символ новой строки.
И все потоки сбрасываются, когда буфер заполняется, когда поток закрывается или когда пишущий выполняет явную очистку
Ваш код не соответствует вашему описанию. Вы говорите о pipe
системный вызов, но код использует popen
, popen
это функция не в ISO C, а в POSIX, и к ней применяется собственный набор требований в POSIX. POSIX не говорит, что такое режим буферизации popen
потоки, к сожалению. Однако он имеет такую любопытную формулировку: "Буферизованное чтение перед открытием входного фильтра может оставить неправильным расположение стандартного ввода этого фильтра. Подобные проблемы с выходным фильтром могут быть предотвращены путем тщательной очистки буфера, например, с помощью fflush". Я не могу понять смысл первого предложения: как можно читать перед открытием? Второе предложение, кажется, подразумевает, что popen
потоки могут быть полностью буферизированы, и поэтому явные fflush
может быть необходимо убедиться, что данные передаются в выходной канал. Конечно, если этот процесс сам читает ввод с полной буферизацией, это может не помочь!
Если вы создаете трубу с pipe
Системный вызов, получив пару файловых дескрипторов, вы можете затем создать FILE *
потоки по этим дескрипторам с fdopen
, Это, опять же, не является функцией ISO C. Поэтому оно не связано с тем требованием, которое ISO C предъявляет к fopen
, а именно: "При открытии поток полностью буферизуется тогда и только тогда, когда можно определить, что он не ссылается на интерактивное устройство. Индикаторы ошибок и конца файла для потока очищаются". Чтобы увидеть, правда ли это fdopen
, мы должны посмотреть в POSIX. К сожалению, POSIX об этом молчит; это ничего не говорит о буферизации. Это также не говорит, что fdopen
наследует любые особые требования от fopen
, Он говорит, что значение флагов режима "точно так, как указано в fopen()
за исключением того, что режимы, начинающиеся с w, не должны вызывать усечение файла. "
POSIX имеет описание fopen
и это описание отражает приведенный выше текст ISO C о буферизации, дословно. С описания POSIX fdopen
не имеет такого текста и не требует fdopen
должны следовать требованиям от fopen
(кроме как в отношении значения флагов режима), буферизация, установленная fdopen
в воздухе. Соответствующий fdopen
может установить полную буферизацию, даже если дескриптор файла - TTY.
Таким образом, если вы используете fdopen
или же popen
и выбор буферизации имеет значение в вашей ситуации, вы должны сами setvbuf
,
Что касается:
Я также пытался setvbuf() перед fgets() ...
Буферизация влияет на вывод. stdio
Функции ввода не задерживают доставку буферизованных входных данных в приложение. Тот факт, что вы можете читать отдельные строки из процесса, подключенного к каналу, означает, что этот процесс очищает свой собственный выходной буфер для каждой строки. Эта линия затем передается по каналу и доступна для вашего собственного процесса. stdio
библиотека не собирается задерживать ваш fgets
работа, пока не накопится больше строк, даже при полной буферизации. Это не так, как это работает; полная буферизация означает, что вывод накапливается до тех пор, пока буфер не заполнится или fflush
называется.