Страницы

Поиск по вопросам

вторник, 12 марта 2019 г.

Буферизация stdout, работа fflush

Что делает fflush? Многие пишут,что эта функция дает команду ОС сбросить содержимое буфера на диск. Но разве этим занимается ОС? Насколько я понял при работе с файлами буфер создает сама программа, например, функция fopen. В таблице syscall-ов linux я не нашел чего-то похожего на flush. При этом в пример приводят вот такой код:
#include int main() { printf("Hello"); while(true); return 0; }
При запуске такой программы, hello напечатано не будет, если не добавить после printf вызов fflush(stdout). Однако вот такой код вполне нормально работает:
#include #include int main() { char b[] = "Hello"; write(1,(void*)b,sizeof(b)-1); while(true); return 0; }
Тут мы не используем высокоуровневые методы работы с stdout, а работаем на уровне syscall-ов. Я правильно понимаю что буферизация работы с файлами реализована средствами самой программы, а не ОС? И при вызове write, данные сразу будут записаны на диск?


Ответ

Что касается fflush, в сущности всё так и есть. Семейство функций для работы с потока в Си (fopen/fread/fprintf итп) fflush сбрасывает только оные буферы. Они же могут быть сброшены и раньше по исчерпанию (размер стандартного буфера BUFSIZ [8к для glibc/linux/x86]) или в зависимости от настроек буферизации (см. man 3 setbuf), в частности по-умолчанию для обычных файлов используется блочная буферизация (сбрасывается по 4к для glibc/linux), для терминалов (втч для stdout — строчная, сбрасывается по символу '
'), для stderr буферизации нет.
Для Linux собственно во время сброса буфера происходит системный вызов write(), а что происходит с данными после сброса этого стандарт Си не гарантирует.
Из man 3 fflush
ЗАМЕЧАНИЯ Заметим, что fflush() сбрасывает буферы только пользовательского пространства, заданные библиотекой Си. Чтобы гарантировать, что данные действительно физически сохранены на диске, буферы ядра также должны быть сохранены, например, с помощью вызова sync(2) или fsync(2).
Что происходит с данными после write() зависит от того с каким файлом связан дескриптор и как он был открыт. Практически в любом случае ОС практически всегда проводит дополнительную буферизацию/кеширование. Очевидное исключение, например, запись в /dev/null; в принципе возможно существование устройств не буферизирующих ничего, а работающих напрямую с буфером из юзерспейса, но на вскидку я таких не назову
Не вдаваясь в детали в Linux'е для обычного файла на обычной дисковой ФС при не-синхронном вводе-выводе происходит следующее: ядро вносит изменения в кеше страниц и помечает их как грязные, затем возвращает управление процессу. Далее специальный демон (раньше был один pdflush (с несколькими потоками), ныне для каждого диска/блочного устройства/некоторых ФС свой) следит за грязными страницами и отдаёт команды драйверам ФС сохранить данные на диск при одном из условий:
Когда не хватает свободной памяти Когда грязных страниц слишком много (см. /proc/sys/vm/dirty_{ratio,background_ratio}), По прошествии определённого времени (указанного в /proc/sys/vm/dirty_{writeback_centisecs,expire_seconds}) По вызову [f]sync() Когда ещё какая-то умная эвристика сработала.
В этот момент ФС может преобразовать данные (например сжать или зашифровать). Далее создаётся запрос на вывод на блочное устройство. Когда драйвер жёсткого диска обработает этот запрос т.е. отправит данные непосредственно на устройство и получил подтверждение, что всё в порядке, страница снова отмечается, как чистая.
При синхронном вводе-выводе (когда файл открыт с O_SYNC или ФС смонтирована с -o sync) всё точно также, но управление процессу из write() не будет возвращено пока страница не отмечена как чистая.

Комментариев нет:

Отправить комментарий