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

6598
Eric

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

18
Вероятно, он выполняет команду сразу, но время, необходимое для фактического открытия файла, записи и закрытия, будет зависеть от скорости и типа вашего жесткого диска, любых запущенных программ и т. Д. freginold 6 лет назад 1
С точки зрения данного примера, что такое «процесс»? Разве `echo` и`> `не являются отдельными (недолговечными) процессами? И где вывод `echo` остается до выполнения`> `? oɔɯǝɹ 6 лет назад 0
Если пользователь закрывает программу, предположим, что его не волнует потеря данных. Uğur Gümüşhan 6 лет назад 0
@ oɔɯǝɹ `>` - перенаправление оболочки. Это так же, как если бы программа открыла именованный файл для записи и заменила на него стандартный вывод, что в точности и делает оболочка. Dan D. 6 лет назад 1
Я думаю, что ОС несет ответственность за предоставление вам `файла`, содержащего` Hello`, независимо от того, сброшен он или нет. Salman A 6 лет назад 6
@Eric Ваш пример команды является частью некоторого сценария оболочки и команды чтения несколько позже в том же сценарии оболочки? А какую оболочку вы используете? mvw 6 лет назад 0
Если программа работает на компьютере A, и вы читаете файл на компьютере B, а файловая система компьютера A подключена по сети, то вы можете в конечном итоге прочитать пустой файл, в зависимости от типа сетевой файловой системы и настроек монтирования. Поэтому вы можете отключить кэширование для этого монтирования. pts 6 лет назад 1
К сведению, некоторые программы, такие как `echo`,` cat` и `dd`, не используют внутреннее кэширование записи в процессе, поэтому их выходные данные немедленно отображаются в выходном файле. pts 6 лет назад 0
Я немного боюсь, во что это превратилось. :-) mvw 6 лет назад 0
Я думаю, что этот вопрос нуждается в уточнении: файл хранится в общей сетевой папке (например, NFS, Samba) или на локальной машине? Процесс, участвующий в чтении файла, находится на том же компьютере или другом компьютере в сети, чем тот, который создает файл? Ответы, представленные до сих пор, предполагают, что один и тот же компьютер, но возможны разные машины в сети. Когда задействованы сетевые машины, все становится намного сложнее (например, частичные файлы, проблемы с сетью, кэширование). CubicleSoft 6 лет назад 0

9 ответов на вопрос

23
mtak

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

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

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

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

«Если в приложении нет внутренних кешей» - это * очень * большое «если»: подавляющее большинство реализаций библиотек ввода / вывода по умолчанию использует буфер stdout. Тем не менее, стандарт C, например, требует, чтобы буфер stdout очищался при выходе (но, возможно, нет, если `выход` по крайней мере неявно вызывается). Другие библиотеки / языки (например, Java!) Дают меньше гарантий. Konrad Rudolph 6 лет назад 1
Что если просто ограничить его примитивом перенаправления (т. Е. Командой в моем вопросе)? У него нет внутренних кешей, верно? Eric 6 лет назад 0
@Eric Нет, с тобой все будет в порядке. mtak 6 лет назад 0
Я не уверен, что получу этот ответ. Вопрос в том, «когда процесс завершится». Каждое приложение с внутренними кешами записи сбрасывает их на диск при выходе из процесса, если это не произошло раньше. Итак, эти кеши не имеют здесь значения. MSalters 6 лет назад 10
Более того, внутренний буфер будет либо очищен при выходе, либо просто исчезнет из существования, верно? Таким образом, даже если внутренние буферы не сбрасываются, содержимое не будет видимым, независимо от того, как долго можно ждать. WorldSEnder 6 лет назад 2
@Eric: Нет, многие программы используют функции C stdio, и stdio по умолчанию выполняет полный буфер `stdout`, когда он не tty (в пространстве пользователя внутри процесса записи). То, что делает его безопасным, это * не * отсутствие буферизации, это то, что сказал MSalters: любая и вся очистка буфера происходит в процессе записи перед выходом; после этого ничего не случится. См. [@ Pts's answer] (https://superuser.com/questions/1288890/will-buffer-be-automatics-flushed-to-disk-when-a-process-exits/1289498#1289498) для получения дополнительной информации о системе. звонки. Peter Cordes 6 лет назад 0
22
Konrad Rudolph

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

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

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

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

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

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

#include <signal.h> #include <stdio.h>  int main() { printf("test"); raise(SIGABRT); } 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Учитывая, что кеширование процессора кажется мне не актуальным. Это ненужный уровень детализации здесь. Как и во всех деталях, пока некоторая физическая величина, представляющая бит на диске жесткого диска или ssd-памяти, не будет изменена, чтобы перевернуть его. mvw 6 лет назад 7
Действительно, кэш процессора довольно ортогональный. Simon Richter 6 лет назад 3
И что еще более важно, кэш-память ЦП согласована между ядрами, поэтому он полностью не в курсе. На x86 он даже совместим с DMA (а x86 имеет режим упорядочения памяти по общему порядку хранения), поэтому все, что может прочитать память, увидит данные, которые были недавно сохранены по этому адресу в глобальном порядке операций с памятью. (Ядро ЦП увидит свои собственные хранилища еще до того, как они станут глобально видимыми из-за пересылки хранилища из очереди хранилища). На не x86-платформах без связанного с кэшем DMA ядро ​​Linux обеспечивает очистку кэша перед DMA по этим адресам. Peter Cordes 6 лет назад 2
«По большей части они скрыты от разработчиков приложений». Почему "по большей части"? Я разработчик встраиваемых систем и, кроме случаев, когда я запускаю загрузчик (не "приложение"), я полностью игнорирую кэш процессора. Я не думаю, что какой-либо разработчик приложений может быть затронут эффектами кэша процессора. Sam 6 лет назад 1
В некоторых ЦП пропуски / попадания в кэш @Sam вместе со спекулятивным выполнением могут использоваться для обхода ограничений на чтение. Может быть, это то, что ответ имел в виду? John Dvorak 6 лет назад 1
@JohnDvorak, говоря «некоторые процессоры», я бы сказал, что вы, по сути, обесценили его как инструмент «разработчиков приложений», потому что «разработчики приложений» этого не делают и не заботятся о том, на каком процессоре они работают. Конечно, можно сказать, что хакеры могут использовать эксплойты в духе Meltdown и Spectre, но не включают их в терминологию «разработчики приложений». Sam 6 лет назад 0
9
David Foerster

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

«ядро хранит только один вид файла»: не совсем верно для [`mmap (MAP_SHARED)`] (http://man7.org/linux/man-pages/man2/mmap.2.html): хранилища в области mmaped не согласованы с чтениями файла (этим потоком или другими процессами). Вот почему существует [`msync (2)`] (http://man7.org/linux/man-pages/man2/msync.2.html). По крайней мере, об этом предупреждают man-страницы; в зависимости от реализации, Linux может фактически отображать физические страницы из кэша страниц, и в этом случае я бы предположил, что он в основном является последовательным (по модулю упорядочения памяти). В любом случае, все это происходит до `_exit (2)`. Peter Cordes 6 лет назад 0
2
Mehrdad

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

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

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

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

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

Нет, нет

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

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

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

0
AnoE

да

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

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

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

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

Это лишнее, к сожалению. Лишь пара ответов в основном сосредоточена на том, чтобы передать данные в энергонезависимое хранилище. См. [@ Pts's answer] (https://superuser.com/a/1289498/20798) и несколько других для ясного описания: изменение файла происходит перед выходом или не происходит вообще. Peter Cordes 6 лет назад 0

Похожие вопросы