Win32: запись в файл без буферизации?

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

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

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

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

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

затем я настроил профессиональную среду мониторинга, состоящую из непрерывного нажатия "F5" в проводнике. Результаты: файл остается на 0kB, а затем переходит на 100MB примерно в то время, когда тестовая программа концы.

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

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

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

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

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

в 'builtin no-buffers' версия:

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 ответов


я тоже был укушен этим в контексте ведения журнала сбоев.

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

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

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

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

  1. использовать CreatePipe API для создания наследуемого анонимного канала.
  2. использовать CreateFile создать файл с FILE_FLAG_NO_BUFFERING set.
  3. снабдите writable ручку трубы ребенок как свое STDOUT.
  4. в Родительском процессе прочитайте из считываемого дескриптора канала в выровненные буферы и запишите их в файл.

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

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

спасибо,

Грег


возможно, вы могли бы быть удовлетворены FlushFileBuffers:

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

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

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

из-за взаимодействия кэширования диска в системе интерфейс flushfilebuffers функция может быть неэффективной при использовании после каждой записи на дисковое устройство, когда многие записи выполняются отдельно. Если приложение выполняет несколько записей на диск, а также необходимо обеспечить запись критических данных на постоянный носитель, приложение должно использовать unbuffered I / O вместо частого вызова интерфейс flushfilebuffers. Чтобы открыть файл для unbuffered ввода-вывода, вызовите CreateFile


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

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

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


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

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