Будет ли буфер автоматически сбрасываться на диск при выходе из процесса?
Когда я перенаправляю вывод команды в файл (например, echo Hello > file
) будет ли этот файл иметь такие данные сразу после выхода из команды? Или все еще очень маленькое окно между выходами команды и данными, записанными в файл? Я хотел бы прочитать файл сразу после выхода из команды, но я не хочу читать пустой файл.
9 ответов
Есть несколько слоев буферов / кэшей.
Кэш процессора.
Данные складываются побайтно и сохраняются в кэше процессора. Если кэш-память ЦП заполнена и данные не были доступны в течение некоторого времени, блок, содержащий наши данные, может быть записан в основную память. По большей части они скрыты от разработчиков приложений.
Внутрипроцессные буферы.
В процессе сбора данных выделяется некоторая память, поэтому нам нужно сделать как можно меньше запросов к ОС, поскольку это сравнительно дорого. Процесс копирует данные в эти буферы, которые снова могут быть защищены кэш-памятью ЦП, поэтому нет гарантии, что данные будут скопированы в основную память. Приложение должно явно очистить эти буферы, например, используя fclose(3) или fsync(3). Функция exit(3) также делает это до завершения процесса, в то время как функция _exit(2) этого не делает, поэтому на странице руководства есть большое предупреждение для этой функции, чтобы вызывать ее, только если вы знаете, что вы делает.
Буферы ядра
Затем ОС сохраняет свой собственный кэш, чтобы минимизировать количество запросов, которые необходимо отправить на диски. Этот кэш не относится ни к каким процессам, в частности, поэтому данные в нем могут принадлежать процессам, которые уже закончили, и, поскольку все обращения осуществляются здесь, следующая программа увидит данные, если они достигли здесь. Ядро запишет эти данные на диски, когда у него будет время или когда это явно задано.
Кеш накопителя
Сами диски также хранят кеш для ускорения доступа. Они пишутся довольно быстро, и есть команда, чтобы записать оставшиеся данные в кэш-память и сообщить, когда это будет выполнено, которую ОС использует при завершении работы, чтобы убедиться, что никакие данные не остаются незаписанными до выключения питания.
Для вашего приложения достаточно, чтобы данные были зарегистрированы в буферах ядра (на данный момент фактические данные могут все еще находиться в кэше ЦП и, возможно, не были записаны в основную память): процесс "эхо" завершается, что означает, что любые внутрипроцессные буферы должны быть сброшены, а данные переданы в ОС, и при запуске нового процесса гарантируется, что ОС вернет те же данные при запросе.
Если в приложении нет внутренних кэшей, изменения будут немедленно записаны в файл. То же самое для вашего примера. Файл является логической сущностью в памяти, которая будет немедленно обновлена. Любые последующие операции над файлом будут видеть изменения, сделанные программой.
Однако это не означает, что изменение было записано на физический диск. Изменения могут остаться в кэшах файловой системы ОС или аппаратных кешах. Чтобы очистить буферы файловой системы, используйте sync
команда.
Я хотел бы прочитать файл сразу после выхода из команды, но я не хочу читать пустой файл.
Вы не должны сталкиваться с практическими проблемами здесь.
Будет ли буфер автоматически сбрасываться на диск при выходе из процесса?
В общем, ответ - нет.
Это зависит от команды. Как уже упоминалось в других ответах, если команда не выполняет внутреннюю буферизацию данных, все данные будут доступны после ее завершения.
Но большинство, если не все, стандартные библиотеки ввода-вывода по умолчанию (в некоторой степени) по умолчанию используют буферный stdout и дают разные гарантии относительно автоматической очистки буферов при закрытии приложения.
C гарантирует, что нормальный выход очистит буферы. "Нормальный выход" означает, что exit
вызывается - либо явно, либо путем возврата из main
, Однако ненормальный выход может обойти этот вызов (и, следовательно, оставить неиспользованные буферы позади).
Вот простой пример:
#include <signal.h>
#include <stdio.h>
int main() {
printf("test");
raise(SIGABRT);
}
Если вы скомпилируете это и выполните, test
не обязательно будет записано на стандартный вывод.
Другие языки программирования дают еще меньше гарантий: например, Java не выполняет автоматическую очистку после завершения программы. Если выходной буфер содержит неопределенную строку, он может быть потерян, если System.out.flush()
был вызван явно.
Тем не менее, ваше тело вопроса спрашивает что-то немного другое: если данные вообще попадают в файл, это следует делать сразу после завершения команды (с учетом предостережений, описанных в других ответах).
Я думаю, что ни один вопрос еще не решает эту проблему в достаточной степени:
Я хотел бы прочитать файл сразу после выхода из команды, но я не хочу читать пустой файл.
Как объясняют другие ответы, хорошо работающая программа очищает свои внутренние файловые буферы до нормального завершения процесса. После этого данные могут все еще оставаться в буфере ядра или оборудования, прежде чем они будут записаны в постоянное хранилище. Однако семантика файловой системы Linux гарантирует, что все процессы видят содержимое файлов так же, как ядро, включая внутренние буферы1.
Обычно это реализуется с помощью не более одного буфера в ядре на объект файла и требует, чтобы весь доступ к файлу проходил через этот буфер.
Если процесс читает файл, ядро представит процессу содержимое буфера, если запрошенная часть файла в данный момент находится в буфере; если это не так, ядро извлечет данные с основного носителя и поместит их в буфер, а затем вернется к предыдущему шагу.
Если процесс записывает в файл, данные сначала помещаются в буфер ядра для этого файла. Со временем содержимое буфера будет сброшено в хранилище. В то же время доступ для чтения удовлетворяется из того же буфера (см. Выше).
1 По крайней мере для обычных файлов, каталогов и символических ссылок. FIFO и сокеты - это другое дело, поскольку их содержимое никогда не хранится постоянно. Есть несколько особых случаев обычных файлов, содержимое которых зависит от того, кто спрашивает; примеры файлов в procfs и sysfs (подумайте /proc/self
которая является символической ссылкой на идентификатор процесса, считывающего символическую ссылку).
Предполагая, что ваша команда выполняется какой-либо программой, использующей библиотеку времени выполнения C, в какой-то момент она должна вызвать fclose
закрыть открытый файл.
Справочная страница для fclose
С функция говорит:
ЗАМЕЧАНИЯ Обратите внимание, что fclose() очищает только буферы пользовательского пространства, предоставляемые библиотекой C. Чтобы гарантировать, что данные физически хранятся на диске, буферы ядра также должны быть сброшены, например, с помощью sync(2) или fsync(2).
и справочная страница для fflush
имеет ту же ноту. Справочная страница для close
говорит:
Успешное закрытие не гарантирует, что данные были успешно сохранены на диск, поскольку ядро откладывает запись. В файловой системе не принято очищать буферы при закрытии потока. Если вам необходимо убедиться, что данные физически хранятся, используйте fsync(2). (Это будет зависеть от аппаратного обеспечения диска в этот момент.)
Обратите внимание, что данные доступны другим процессам, даже если они не синхронизированы с диском. Может быть, это уже достаточно хорошо для вас.
Если вы сомневаетесь, напишите тест.
Когда я перенаправляю вывод команды в файл (например,
echo Hello > file
) будет ли этот файл иметь такие данные сразу после выхода из команды?
Да. Оболочка открывает выходной файл, и echo
выводит непосредственно к этому. После выхода из команды все готово.
Или все еще очень маленькое окно между выходами команды и данными, записанными в файл?
Если данные уже находятся на носителе, это другой вопрос, который имеет значение только в случае, если после этого произойдет сбой оборудования, или вы проверяете работающий раздел с помощью какого-либо криминалистического программного обеспечения в обход смонтированной файловой системы.
Я хотел бы прочитать файл сразу после выхода из команды, но я не хочу читать пустой файл.
Не волнуйтесь, ядро хранит только один просмотр файла, независимо от того, как часто он открывается.
Как правило, любые данные, принадлежащие ядру, поддерживаются и очищаются ядром, точка. Такие данные включают в себя данные, передаваемые в память ядра системным вызовом, таким как write(2)
,
Однако, если ваше приложение (например, библиотека C) выполняет буферизацию поверх этого, тогда ядро, очевидно, не имеет представления и, следовательно, не гарантирует его очистку.
Более того, я не верю, что есть какая-то временная гарантия для очистки - она, как правило, выполняется на основе "максимальных усилий" (читай: "когда у меня есть секунда").
Или все еще очень маленькое окно между выходами команды и данными, записанными в файл?
Нет, нет
Я хотел бы прочитать файл сразу после выхода из команды, но я не хочу читать пустой файл.
Вы можете прочитать окончательное содержимое файла сразу после завершения команды, вместо этого вы никогда не будете читать пустой файл. (В C и C++ используйте системные вызовы wait, waitpid, wait3 или wait4, чтобы дождаться завершения программы и только затем прочитать файл. Если вы используете оболочку, другой язык программирования или библиотеку (например, библиотека C система вызовов или класс процесса Java), возможно, он уже использует один из этих системных вызовов.)
Как указывалось в других ответах и комментариях, вы можете закончить чтение пустого файла после выхода из программы, если программа вышла без очистки своих внутренних выходных буферов (например, из-за выхода, сброса или получения фатального сигнала, или потому что Java-программа выходит нормально). Однако на этом этапе вы ничего не можете с этим поделать: незагрязненные данные будут потеряны навсегда, дополнительное ожидание не восстановит их.
да
Извините за добавление еще одного лишнего ответа, но большинство, кажется, сосредоточено на красной сельди заголовка вопроса. Но, насколько я могу судить, вопрос вовсе не в буферизации, а в следующем:
Когда я перенаправлю вывод команды в файл (например, echo Hello > file), будут ли в этом файле такие данные сразу после выхода из команды?
Да, безусловно. Использование ">", которое вы описываете, вместе с "|" и "<" - это модель обработки на основе каналов, на которой в значительной степени основаны мир Unix и Linux. В каждой установке Linux вы найдете сотни, если не тысячи сценариев, полностью зависящих от этого поведения.
Он работает так, как вы хотите для каждого дизайна, и если бы была хоть малейшая вероятность состояния гонки, это было бы исправлено, вероятно, десятилетия назад.