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

21

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

Эрик
источник
1
Вероятно, он сразу же выполнит команду, но время, необходимое для фактического открытия файла, записи и закрытия, будет зависеть от скорости и типа вашего жесткого диска, любых запущенных программ и т. Д.
freginold
С точки зрения данного примера, что такое «процесс»? Являются ли echoи >не отдельные (недолговечные) процессы? И где вывод echoостается до того, >как выполняется?
oɔɯǝɹ
1
@ oɔɯǝɹ >- перенаправление оболочки. Это так же, как если бы программа открыла именованный файл для записи и заменила на него стандартный вывод, что в точности и делает оболочка.
Дэн Д.
7
Я думаю, что ОС несет ответственность за предоставление вам fileсодержимого Helloнезависимо от того, очищено оно или нет.
Салман А
1
Если программа работает на компьютере A, и вы читаете файл на компьютере B, а файловая система компьютера A подключена по сети, то вы можете в конечном итоге прочитать пустой файл, в зависимости от типа сетевой файловой системы и настроек монтирования. Поэтому вы можете отключить кэширование для этого монтирования.
Очков

Ответы:

21

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

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

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

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

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

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

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

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

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

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

Саймон Рихтер
источник
7
Учитывая, что кеширование процессора кажется мне неактуальным. Это ненужный уровень детализации здесь. Как и во всех деталях, пока не будет изменена некоторая физическая величина, представляющая бит на диске жесткого диска или в ssd-памяти, чтобы перевернуть его.
MVW
3
Действительно, кэш процессора довольно ортогональный.
Саймон Рихтер
2
И что еще более важно, кэш-память ЦП согласована между ядрами, поэтому он полностью не в курсе. На x86 он даже совместим с DMA (а x86 имеет режим упорядочения памяти по общему порядку хранения), поэтому все, что может прочитать память, увидит данные, которые были недавно сохранены по этому адресу в глобальном порядке операций с памятью. (Ядро ЦП увидит свои собственные хранилища еще до того, как они станут глобально видимыми из-за пересылки хранилища из очереди хранилища). На не x86-платформах без DMA-связного с кэшем ядро ​​Linux гарантирует, что кеш очищается перед DMA по этим адресам.
Питер Кордес
1
«По большей части они скрыты от разработчиков приложений». Почему "по большей части"? Я разработчик встраиваемых систем и, кроме случаев, когда я запускаю загрузчик (не "приложение"), я полностью игнорирую кэш процессора. Я не думаю, что какой-либо разработчик приложений может быть затронут эффектами кэша процессора.
Сэм
1
В некоторых ЦП пропуски / попадания в кэш @Sam вместе со спекулятивным выполнением могут использоваться для обхода ограничений доступа для чтения. Может быть, это то, что ответ имел в виду?
Джон Дворак
22

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

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

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

Вы не должны столкнуться с какими-либо практическими проблемами здесь.

mtak
источник
1
«Если у приложения нет внутренних кешей» - это очень большое «если»: подавляющее большинство реализаций библиотек ввода / вывода по умолчанию использует буферный stdout. Тем не менее, стандарт C, например, предписывает, чтобы буфер stdout очищался при выходе (но, возможно, нет, если exitон по крайней мере не вызывается неявно). Другие библиотеки / языки (например, Java!) Дают меньше гарантий.
Конрад Рудольф
Что если просто ограничить его примитивом перенаправления (т. Е. Командой в моем вопросе)? У него нет внутренних кешей, верно?
Эрик
@Eric Нет, с тобой все будет в порядке.
mtak
10
Я не уверен, что получу этот ответ. Вопрос в том, «когда процесс завершится». Каждое приложение с внутренними кешами записи сбрасывает их на диск при выходе из процесса, если этого не произошло раньше. Итак, эти кеши не имеют здесь значения.
MSalters
2
Более того, внутренний буфер будет либо очищен при выходе, либо просто исчезнет из существования, верно? Таким образом, даже если внутренние буферы не сбрасываются, содержимое не будет видимым, независимо от того, как долго можно ждать.
WorldSEnder
21

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

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

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

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

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

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

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

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

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

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

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

Конрад Рудольф
источник
7
Я также видел ненормальный выход, когда инструмент командной строки записывает в файл и в stdout или stderr, как журнал отладки, и пользователь выполнил конвейер или меньше, чем набрал 'q', чтобы выйти меньше. Файл диска не всегда полностью очищается, если средство командной строки не обрабатывает SIGPIPE.
Zan Lynx
+1, но «это должно быть сделано сразу после завершения команды» не совсем верно: любой write()или pwrite()системный вызов произойдет до завершения процесса, и именно тогда изменения файла станут видимыми. Таким образом, последнее изменение файла определенно перед завершением процесса, самое позднее, самое позднее. Я думаю, что даже с mmap(MAP_SHARED)файлом невозможно наблюдать завершение процесса до того, как произойдут все изменения файла.
Питер Кордес
9

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

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

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

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

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

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


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

Дэвид Фёрстер
источник
2
Строго говоря, это гарантирует не семантика файловой системы Linux, а семантика POSIX. В частности, BSD ведет себя точно так же, как macOS и даже Windows (хотя это один из немногих случаев, когда Windows следует семантике POSIX). Это также предполагает, что никто не делает странных вещей с mmap()O_DIRECT, что может привести к несинхронизации между диском и кэшем страницы (но это разрешит момент, когда процесс, выполняющий это, завершится).
Остин Хеммельгарн
2
@AustinHemmelgarn: Строго говоря, мы оба правы, так как Linux был разработан с учетом поддержки приложений Unix (System V), а затем создан для поддержки POSIX, который также основывает многие концепции на System V.
Дэвид Фоерстер,
5

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

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

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

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

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

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

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

MVW
источник
2
C или нет, все будет / должно использовать close()системный вызов для закрытия дескриптора файла.
Attie
@Attie: Вам не нужно , чтобы closeфайлы перед выходом (в Hacky программ , которые не проверяют на наличие ошибок); ядро очистит их, фактически вызвав closeвас после того, как ваш процесс завершится. Однако вам нужно использовать fcloseлюбые буферизованные потоки stdio или позволить libc сделать это за вас exit(3), в отличие от системного вызова exit.
Питер Кордес
Если вы сомневаетесь, напишите тест. Это плохой совет для определения условий гонки. Тестирование на одном ядре, работающем на одном оборудовании, может сказать вам, что гонка не может происходить в условиях программного обеспечения, созданных вашим тестом в этой системе, или если это происходит, это слишком редко для обнаружения. Но он не может сказать вам, должно ли такое поведение быть безопасным для всех файловых систем, ядер и всего оборудования (например, PowerPC). то есть вы не можете сказать, является ли гарантия, от которой вы зависите, деталью реализации или намеренной гарантией на будущее! (В данном случае это так.)
Питер Кордес
Это зависит от ситуации. Некоторым людям, пытающимся запустить его скрипт, может помочь этот совет. Он не был задуман как общее решение для более продвинутых, но менее вероятных сред, например, инженер-программист, работающий над ядром ОС, кто-то, работающий над обновлением микрокода Intel, или кто-то, кто работает над какой-то системой для МКС.
mvw
3

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

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

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

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

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

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

Deduplicator
источник
«ядро хранит только одно представление файла»: не совсем верно для mmap(MAP_SHARED): хранилища в области mmaped не согласованы с чтениями файла (этим потоком или другими процессами). Вот почему msync(2)существует. По крайней мере, об этом предупреждают man-страницы; в зависимости от реализации, Linux может фактически отображать физические страницы из кэша страниц, и в этом случае я бы предположил, что он в основном является последовательным (по модулю упорядочения памяти). Во всяком случае, это все еще происходит раньше _exit(2).
Питер Кордес
2

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

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

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

Mehrdad
источник
Существует гарантия, что любая очистка / очистка буфера произойдет до waitpid()возврата родительского процесса , если очистка вообще произойдет. то есть другие процессы не могут непосредственно наблюдать завершение процесса до того, как какие-либо изменения файла будут выполнены этим процессом. (Я сказал «напрямую», чтобы исключить косвенное наблюдение через временные метки файлов NFS, потому что кэширование NFS не идеально согласовано между хостами.)
Питер Кордес
@PeterCordes: Я полагаю, это зависит от того, что вы подразумеваете под «очисткой», а не «обслуживанием». Для меня «поддерживать» - это «обеспечивать согласованное представление» (которое имеет гарантию, которую вы упомянули), а «очистить» - это «записывать на диск», что, как мне кажется, не дает гарантию синхронизации.
Мехрдад
О, я вижу, вы отвечаете на часть вопроса "сброс на диск", которая не имеет отношения к тому, что последующие процессы увидят при чтении файла. «очистить» в смысле «очистить кэш ввода / вывода / очистить буферную память». Правильно, никакой гарантии синхронизации, если вы не используете fsync/ fdatasync, хотя обратная запись буфера в Linux начнется через /proc/sys/vm/dirty_writeback_centisecsсотые доли секунды (если не задерживается другим трафиком ввода-вывода), и различные другие переменные в этом каталоге procfs также влияют на вещи (например, как большой, чтобы буферы росли перед выполнением обратной записи).
Питер Кордес
2

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

Нет, нет

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

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

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

PTS
источник
0

да

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

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

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

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

Anoe
источник
К сожалению, это лишнее. Лишь пара ответов в основном сосредоточена на том, чтобы передать данные в энергонезависимое хранилище. См . Ответ @ pts и несколько других для ясного описания: изменение файла происходит перед выходом или не происходит вообще.
Питер Кордес