#c #linux #gcc #xterm
#c #linux #gcc #xterm
Вопрос:
У меня есть простой файл C, который записывает в стандартный вывод, используя функции printf
и write(1,..)
.
Файл: main.c
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("Hellon");
printf("Marsn");
fsync(1);
write(1,"Olan",4);
write(1,"Earthn",6);
}
> gcc main.c -o test
Когда я перенаправляю вывод в файл, я вижу порядок, отличный от вывода в терминале. Кажется, что когда я использую перенаправление, оно write(..)
записывается раньше, чем printf(..)
, даже жестко, printf()
раньше в коде, и есть fsync(..)
промежуточный этап.
Существует ли какой-либо вариант оператора перенаправления, который обеспечит порядок, или я делаю что-то не так.
Вывод в терминале:
> ./test
Hello
Mars
Ola
Earth
Вывод в out
файл:
> ./test >out
# or ./test|tee out
> cat out
Ola
Earth
Hello
Mars
Комментарии:
1. Попробуйте заменить
fsync(1)
наfflush(stdout)
2. Вы действительно никогда не должны смешивать буферизованные записи и небуферизованные записи. Используйте одно из
write
илиprintf
, но не оба.3. @tadman: Нереально. Иногда вы хотите использовать библиотеку, которая может выполнять ввод-вывод по-своему. Таким образом, вызов
fflush
между выводами разных типов является намного более практичным решением.4. @BenVoigt Я не говорю, что вы не можете этого сделать или что вас не могут заставить это делать, но вам следует избегать этого, когда это возможно.
Ответ №1:
printf
записывает в поток C stdout
, который буферизуется стандартными библиотечными процедурами C и в конечном итоге записывается в файловый дескриптор Unix 1.
fsync
и write
работает с файловым дескриптором Unix 1. fsync
удаляет данные из файлового дескриптора 1, но это не влияет на буферы потоков стандартной библиотеки C.
Процедуры ввода-вывода стандартной библиотеки C работают по-разному в зависимости от того, направляется стандартный вывод на терминал или в файл. Это обнаруживается во время запуска программы. Когда выходные данные поступают на терминал, считается важным передать их пользователю, и буфер очищается при n
записи в поток. Это называется буферизацией строк, и для вывода на терминал по умолчанию установлено значение «буферизация строк».
Когда выходные данные отправляются в файл, не считается важным быстро удалять его, поскольку ожидается, что пользователь не увидит его сразу, и производительность выше, если выходные данные полностью буферизованы. Таким образом, буфер очищается только тогда, когда он заполнен полностью, поток закрыт или выполнен явный запрос на очистку. Это называется полностью буферизованным, и для вывода в файл по умолчанию установлено значение «полностью буферизованный».
Обычно это рассматривается в C 2018 7.21.3, в котором в пункте 7 говорится:
… потоки стандартного ввода и стандартного вывода полностью буферизуются тогда и только тогда, когда можно определить, что поток не относится к интерактивному устройству.
(Когда выходные данные являются терминалом, реализация C может также использовать небуферизованный режим вместо буферизации строк, и в этом случае все символы отправляются на терминал сразу после их записи. Это необычно.)
Вы можете явно запросить удаление stdout
с fflush(stdout)
помощью.
После stdout
открытия, но перед выполнением над ним любой другой операции, вы можете изменить его метод буферизации на setvbuf(stdout, NULL, mode, 0)
, где mode
это _IOFBF
, _IOLBF
или _IONBF
для полной, строчной или без буферизации, соответственно. (Вы также можете передать char
массив и его размер вместо NULL
и 0
, и библиотека будет использовать этот массив для буфера, после чего вы не должны использовать массив для каких-либо других целей. Если вы передаете NULL
и размер, библиотека может использовать этот размер для выполнения собственного выделения буфера, но это не гарантируется.) setvbuf
возвращает int
значение, которое может быть ненулевым, если оно не может выполнить запрос.
Комментарии:
1. Спасибо @Eric. Вопрос: Когда я перестраиваю, я вижу, что
printf("Hello")
наконец-то вызовутwrite(1,"Hello",5)
. Итак, если я разбуфферирую поток стандартного вывода в C stdlib, буфер ядра все равно будет использоваться, точно так же, как если бы я вызвалwrite()
сам. Верно?
Ответ №2:
взгляните на системный вызов dup2: https://www.geeksforgeeks.org/dup-dup2-linux-system-call /
вы можете перенаправить стандартный вывод / ввод в файл