Win32: запись в файл без буферизации? - PullRequest
12 голосов
/ 25 ноября 2008

Мне нужно создать новый дескриптор файла, чтобы любые операции записи в этот дескриптор немедленно записывались на диск.

Дополнительная информация: дескриптор будет унаследованным STDOUT дочернего процесса, поэтому мне нужно, чтобы любой вывод этого процесса был немедленно записан на диск.

Изучая документацию CreateFile, флаг FILE_FLAG_WRITE_THROUGH выглядел именно так, как мне нужно:

Операции записи не пройдут любой промежуточный кеш они пойдут непосредственно на диск.

Я написал очень простую тестовую программу, и она не работает. Я использовал флаг CreateFile, затем использовал WriteFile(myHandle,...) в длинном цикле, записывая около 100 МБ данных за 15 секунд. (Я добавил Sleep()).

Затем я настроил профессиональную среду мониторинга, состоящую из непрерывного нажатия «F5» в проводнике. Результаты: файл остается на 0 КБ, затем переходит на 100 МБ к моменту окончания тестовой программы.

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

Мой вопрос: разве FILE_FLAG_WRITE_THROUGH не сделал бы это без ручной очистки файла? Я что-то пропустил? В программе «реального мира» я не могу сбросить файл, потому что у меня нет никакого контроля над дочерним процессом, который его использует.

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

EDIT: Я сделал отдельный проект специально для наблюдения за изменением размера файла. Он использует класс .NET FileSystemWatcher. Я также пишу меньше данных - всего около 100 КБ.

Вот вывод. Проверьте секунды в метках времени.

Версия «встроенных без буферов»:

25.11.2008 7:03:22 PM: 10230 bytes added.
25.11.2008 7:03:31 PM: 10240 bytes added.
25.11.2008 7:03:31 PM: 10240 bytes added.
25.11.2008 7:03:31 PM: 10240 bytes added.
25.11.2008 7:03:31 PM: 10200 bytes added.
25.11.2008 7:03:42 PM: 10240 bytes added.
25.11.2008 7:03:42 PM: 10240 bytes added.
25.11.2008 7:03:42 PM: 10240 bytes added.
25.11.2008 7:03:42 PM: 10240 bytes added.
25.11.2008 7:03:42 PM: 10190 bytes added.

... и версия «принудительного (ручного) сброса» (FlushFileBuffers() вызывается каждые ~ 2,5 секунды):

25.11.2008 7:06:10 PM: 10230 bytes added.
25.11.2008 7:06:12 PM: 10230 bytes added.
25.11.2008 7:06:15 PM: 10230 bytes added.
25.11.2008 7:06:17 PM: 10230 bytes added.
25.11.2008 7:06:19 PM: 10230 bytes added.
25.11.2008 7:06:21 PM: 10230 bytes added.
25.11.2008 7:06:23 PM: 10230 bytes added.
25.11.2008 7:06:25 PM: 10230 bytes added.
25.11.2008 7:06:27 PM: 10230 bytes added.
25.11.2008 7:06:29 PM: 10230 bytes added.

Ответы [ 5 ]

12 голосов
/ 25 ноября 2008

Меня это тоже укусило в контексте регистрации сбоев.

FILE_FLAG_WRITE_THROUGH только гарантирует, что отправляемые вами данные будут отправлены в файловую систему до того, как WriteFile вернется; это не гарантирует, что оно действительно отправлено на физическое устройство. Так, например, если вы выполняете ReadFile после WriteFile на дескрипторе с этим флагом, вы гарантируете, что чтение вернет записанные вами байты, независимо от того, получили ли они данные из кэша файловой системы или из базовое устройство.

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

База знаний содержит краткую, но информативную статью о разнице.

В вашем случае, если родительский процесс собирается пережить ребенка, тогда вы можете:

  1. Используйте API CreatePipe для создания наследуемого анонимного канала.
  2. Используйте CreateFile для создания файла с установленным FILE_FLAG_NO_BUFFERING.
  3. Предоставьте доступный для записи дескриптор канала ребенку как его STDOUT.
  4. В родительском процессе считайте из читаемого дескриптора канала в выровненные буферы и запишите их в файл.
5 голосов
/ 24 июля 2013

Это старый вопрос, но я подумал, что могу добавить к нему немного. На самом деле все здесь, я считаю, не правы. Когда вы записываете в поток с помощью write-through и unbuffered-io, он выполняет запись на диск, но НЕ обновляет метаданные, связанные с файловой системой (например, что показывает вам проводник).

Хорошую справку по этому виду можно найти здесь http://winntfs.com/2012/11/29/windows-write-caching-part-2-an-overview-for-application-developers/

Приветствия

Грег

2 голосов
/ 28 августа 2010

Размер, который вы просматриваете в Проводнике, может не полностью совпадать с тем, что файловая система знает о файле, поэтому это не лучший способ его измерить. Просто так получается, что FlushFileBuffers заставит файловую систему обновлять информацию, на которую смотрит Explorer; закрытие и повторное открытие может закончиться тем же.

Помимо проблем с кэшированием на диске, упомянутых другими, программа write делает то, на что вы надеялись. Просто указание «dir» в каталоге может не отображать актуальную информацию.

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

2 голосов
/ 03 марта 2010

Возможно, вас вполне устраивает FlushFileBuffers:

Сбрасывает буферы указанного файла и приводит к записи всех буферизованных данных в файл.

Обычно функции WriteFile и WriteFileEx записывают данные во внутренний буфер, которые операционная система записывает на диск или канал связи. на регулярной основе. Функция FlushFileBuffers записывает всю буферизованную информацию для указанного файла на устройство или канал.

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

Из-за взаимодействия кэширования диска в системе функция FlushFileBuffers может быть неэффективной при использовании после каждой записи на устройство с диском, когда многие записи выполняются отдельно. Если приложение выполняет несколько операций записи на диск, а также необходимо обеспечить запись критических данных на постоянный носитель, приложение должно использовать небуферизованный ввод-вывод вместо частого вызова FlushFileBuffers . Чтобы открыть файл для небуферизованного ввода-вывода, вызовите функцию CreateFile с флагами FILE_FLAG_NO_BUFFERING и FILE_FLAG_WRITE_THROUGH. Это предотвращает кэширование содержимого файла и сбрасывает метаданные на диск при каждой записи. Для получения дополнительной информации см. CreateFile .

Если это не высокопроизводительная ситуация, и вы не будете сбрасывать слишком часто, тогда может быть достаточно (и проще) FlushFileBuffers.

0 голосов
/ 15 февраля 2018

Возможно, вы хотите рассмотреть отображение памяти в этом файле. Как только вы записываете в область отображения памяти, файл обновляется.

Сопоставление файлов Win API

...