20

Когда я перенаправлю вывод команды в файл (например, echo Hello > file), будут ли в этом файле такие данные сразу после выхода из команды? Или все еще есть очень маленькое окно между выходами команды и данными, записанными в файл? Я хотел бы прочитать файл сразу после выхода из команды, но я не хочу читать пустой файл.

9 ответов9

23

Если в приложении нет внутренних кэшей, изменения будут немедленно записаны в файл. То же самое для вашего примера. Файл является логической сущностью в памяти, которая будет немедленно обновлена. Любые последующие операции над файлом будут видеть изменения, внесенные программой.

Однако это не означает, что изменение было записано на физический диск. Изменения могут сохраняться в кэш-памяти файловой системы ОС или аппаратном кеше. Чтобы очистить буферы файловой системы, используйте команду sync .

Я хотел бы прочитать файл сразу после выхода из команды, но я не хочу читать пустой файл.

Здесь не должно быть никаких практических проблем.

22

Будет ли буфер автоматически сбрасываться на диск при выходе из процесса?

В общем, ответ - нет.

Это зависит от команды. Как упоминалось в других ответах, если команда не выполняет внутреннюю буферизацию данных, все данные будут доступны после ее завершения.

Но большинство, если не все, библиотеки стандарт ввода / вывода этого буфера стандартный вывод по умолчанию (в некоторой степени), и дают разные гарантии о автоматической промывки буферов , когда приложение закрывается.

C гарантирует, что нормальный выход очистит буферы. «Нормальный выход» означает, что exit вызывается - либо явно, либо путем возврата из main . Однако ненормальный выход может обойти этот вызов (и, следовательно, оставить неиспользованные буферы позади).

Вот простой пример:

#include <signal.h>
#include <stdio.h>

int main() {
    printf("test");
    raise(SIGABRT);
}

Если вы скомпилируете это и выполните его, test не обязательно будет записан в stdout.

Другие языки программирования дают еще меньше гарантий: Java, например, автоматически не заподлицо по окончанию программы. Если выходной буфер содержит неопределенную строку, он может быть потерян, если System.out.flush() был вызван явно.

Тем не менее, ваше тело вопроса спрашивает что - то немного другое: если данные поступают в файл на всех, он должен сделать это сразу же после команды прекращается ( при условии предостережений , описанных в других ответах).

20

Есть несколько слоев буферов / кэшей.

  1. Кэш процессора.

    Данные складываются побайтно и сохраняются в кеше ЦП. Если кэш-память ЦП заполнена и данные не были доступны в течение некоторого времени, блок, содержащий наши данные, может быть записан в основную память. По большей части они скрыты от разработчиков приложений.

  2. Внутрипроцессные буферы.

    В процессе сбора данных выделяется некоторая память, поэтому нам нужно сделать как можно меньше запросов к ОС, потому что это сравнительно дорого. Процесс копирует данные в эти буферы, которые снова могут поддерживаться кэшем ЦП, поэтому нет гарантии, что данные будут скопированы в основную память. Приложение должно явно очистить эти буферы, например, используя fclose(3) или fsync(3). Функция exit(3) также делает это до завершения процесса, в то время как функция _exit(2) этого не делает, поэтому на странице руководства есть большое предупреждение для этой функции, чтобы вызывать ее, только если вы знаете, что вы делает.

  3. Буферы ядра

    Затем ОС сохраняет свой собственный кэш, чтобы минимизировать количество запросов, которые необходимо отправить на диски. Этот кэш не относится ни к какому процессу, поэтому данные в нем могут принадлежать процессам, которые уже завершились, и, поскольку все обращения осуществляются здесь, следующая программа увидит данные, если они достигли здесь. Ядро запишет эти данные на диски, когда у него есть время или когда это явно задано.

  4. Кеш накопителя

    Сами диски также хранят кеш для ускорения доступа. Они пишутся довольно быстро, и есть команда, чтобы записать оставшиеся данные в кеши и сообщить, когда это будет выполнено, которую ОС использует при завершении работы, чтобы убедиться, что никакие данные не остаются не записанными до выключения питания.

Для вашего приложения достаточно, чтобы данные были зарегистрированы в буферах ядра (на данный момент фактические данные могут все еще находиться в кэше ЦП и, возможно, не были записаны в основную память): процесс "эхо" завершается, что означает, что любые внутрипроцессные буферы должны быть сброшены, а данные переданы в ОС, и при запуске нового процесса гарантируется, что ОС вернет те же данные при запросе.

9

Я думаю, что ни один вопрос еще не решает эту проблему в достаточной степени:

Я хотел бы прочитать файл сразу после выхода из команды, но я не хочу читать пустой файл.

Как объясняют другие ответы, хорошо работающая программа очищает свои внутренние файловые буферы до нормального завершения процесса. После этого данные могут все еще оставаться в буфере ядра или оборудования, прежде чем они будут записаны в постоянное хранилище. Однако семантика файловой системы Linux гарантирует, что все процессы видят содержимое файлов так же, как ядро, включая внутренние буферы 1.

Обычно это реализуется с помощью не более одного буфера в ядре на файловый объект и требует, чтобы весь доступ к файлу проходил через этот буфер.

  • Если процесс читает файл, ядро представит процессу содержимое буфера, если запрошенная часть файла в данный момент находится в буфере; если это не так, ядро извлечет данные с базового носителя и поместит их в буфер, а затем вернется к предыдущему шагу.

  • Если процесс записывает в файл, данные сначала помещаются в буфер ядра для этого файла. Со временем содержимое буфера будет сброшено в хранилище. В то же время доступ для чтения удовлетворяется из того же буфера (см. Выше).


1 По крайней мере для обычных файлов, каталогов и символических ссылок. FIFO и сокеты - это другое дело, поскольку их содержимое никогда не хранится постоянно. Есть несколько особых случаев обычных файлов, содержимое которых зависит от того, кто спрашивает; примерами являются файлы в procfs и sysfs (думаю, что /proc/self является символической ссылкой на идентификатор процесса, считывающего символическую ссылку).

5

Предполагая, что ваша команда выполняется какой-либо программой, использующей библиотеку времени выполнения C, в какой-то момент она должна вызвать fclose чтобы закрыть открытый файл.

Страница man для функции fclose C гласит:

ЗАМЕЧАНИЯ Обратите внимание, что fclose() очищает только буферы пользовательского пространства, предоставляемые библиотекой C. Чтобы гарантировать, что данные физически хранятся на диске, буферы ядра также должны быть сброшены, например, с помощью sync(2) или fsync(2).

и страница руководства для fflush имеет ту же заметку. Страница руководства для close говорит:

Успешное закрытие не гарантирует, что данные были успешно сохранены на диск, поскольку ядро откладывает запись. В файловой системе не принято очищать буферы при закрытии потока. Если вам нужно убедиться, что данные физически хранятся, используйте fsync(2). (Это будет зависеть от аппаратного обеспечения диска в этот момент.)

Обратите внимание, что данные доступны другим процессам, даже если они не синхронизированы с диском. Может быть, это уже достаточно хорошо для вас.

Если вы сомневаетесь, напишите тест.

3

Когда я перенаправлю вывод команды в файл (например, echo Hello > file), будут ли в этом файле такие данные сразу после выхода из команды?

Да. Оболочка открывает выходной файл и выводит echo прямо туда. После выхода из команды все готово.

Или все еще есть очень маленькое окно между выходами команды и данными, записанными в файл?

Если данные уже находятся на носителе, это другой вопрос, который имеет значение только в случае сбоя оборудования или проверки действующего раздела с помощью какого-либо криминалистического программного обеспечения в обход смонтированной файловой системы.

Я хотел бы прочитать файл сразу после выхода из команды, но я не хочу читать пустой файл.

Не волнуйтесь, ядро хранит только один просмотр файла, независимо от того, как часто он открывается.

2

Или все еще есть очень маленькое окно между выходами команды и данными, записанными в файл?

Нет, нет

Я хотел бы прочитать файл сразу после выхода из команды, но я не хочу читать пустой файл.

Вы можете прочитать окончательное содержимое файла сразу после выхода из команды, вместо этого вы никогда не будете читать пустой файл. (В C и C++ используйте системные вызовы wait, waitpid, wait3 или wait4, чтобы дождаться завершения программы и только затем прочитать файл. Если вы используете оболочку, другой язык программирования или библиотеку (например , системы вызова библиотеки C или класс процесса Java), он , вероятно , использует один из этих системных вызовов уже.)

Как и другие ответы и комментарии указали, вы можете закончить чтение пустой файл после выхода из программы , если программа завершается без промывки его внутренние буферы вывода (например , из - за _exit, прервать или получения фатального сигнала, или потому , что это Java-программа завершается нормально). Однако на этом этапе вы ничего не можете с этим поделать: незагрязненные данные будут потеряны навсегда, дополнительное ожидание не восстановит их.

2

Как правило, любые данные, принадлежащие ядру , поддерживаются и очищаются ядром, точка. Такие данные включают в себя данные, передаваемые в память ядра системным вызовом, например write(2).

Однако, если ваше приложение (например, библиотека C) выполняет буферизацию поверх этого, то ядро, очевидно, не имеет представления и, следовательно, не гарантирует его очистку.

Более того, я не верю, что есть какая-то временная гарантия для очистки - она, как правило, выполняется на основе "максимальных усилий" (читай: "когда у меня есть секунда").

0

да

Извините за добавление еще одного лишнего ответа, но большинство, похоже, сосредоточено на красной селедке в заголовке вопроса. Но, насколько я могу судить, вопрос вовсе не в буферизации, а в следующем:

Когда я перенаправлю вывод команды в файл (например, echo Hello> file), будут ли в этом файле такие данные сразу после выхода из команды?

Да, безусловно. Использование ">", которое вы описываете, вместе с "|" и «<» - это модель обработки на основе каналов, на которой в значительной степени основаны мир Unix и Linux. В каждой установке Linux вы найдете сотни, если не тысячи сценариев, полностью зависящих от этого поведения.

Он работает так, как вы хотите для каждого проекта, и если бы была хоть малейшая вероятность состояния гонки, это было бы исправлено, вероятно, десятилетия назад.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .