Операция перенаправления записывает вывод порядка в файл

#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 /

вы можете перенаправить стандартный вывод / ввод в файл