Почему VFAT "пропускает" записи так медленно?
Если драйвер файловой системы VFAT не был «обманут» в этом отношении, создание больших файлов в файловых системах типа FAT всегда будет занимать много времени. Драйвер, чтобы соответствовать спецификации FAT, должен будет выделить все блоки данных и инициализировать их нулями, даже если вы «пропустите» записи. Это из-за "цепочки кластеров" FAT делает.
Причиной такого поведения является неспособность FAT поддерживать:
- UN * X-стиль "дыр" в файлах (он же "редкие файлы")
это то, что вы создаете в ext3 с помощью своего тестового сценария - файл без блоков данных, выделенных для первых 1 ГБ-1 МБ, и одного блока 1 МБ фактически зафиксированных, инициализированных нулем блоков) в конце.
- Информация о допустимой длине данных в стиле NTFS.
В NTFS для файла могут быть выделены неинициализированные блоки, но метаданные файла сохранят два поля размера - одно для общего размера файла, другое для количества фактически записанных в него байтов (из начало файла).
Без спецификации, поддерживающей какой-либо метод, файловая система всегда должна будет выделять и обнулять все «промежуточные» блоки данных, если вы пропустите диапазон.
Также помните, что в ext3 использованная вами техника на самом деле не выделяет блоков для файла (кроме последних 1 МБ). Если вам требуются предварительно выделенные блоки (а не только размер набора файлов большого размера), вам также потребуется выполнить полную запись.
Как можно изменить драйвер VFAT, чтобы справиться с этим?
На данный момент драйвер использует функцию ядра Linux cont_write_begin()
для запуска даже асинхронной записи в файл; эта функция выглядит так:
/*
* For moronic filesystems that do not allow holes in file.
* We may have to extend the file.
*/
int cont_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata,
get_block_t *get_block, loff_t *bytes)
{
struct inode *inode = mapping->host;
unsigned blocksize = 1 << inode->i_blkbits;
unsigned zerofrom;
int err;
err = cont_expand_zero(file, mapping, pos, bytes);
if (err)
return err;
zerofrom = *bytes & ~PAGE_CACHE_MASK;
if (pos+len > *bytes && zerofrom & (blocksize-1)) {
*bytes |= (blocksize-1);
(*bytes)++;
}
return block_write_begin(mapping, pos, len, flags, pagep, get_block);
}
Это простая стратегия, но также и средство очистки кэша страниц (ваши сообщения журнала являются следствием вызова cont_expand_zero()
, который выполняет всю работу и не является асинхронным). Если бы файловая система разделяла две операции - одну задачу для «реальной» записи, а другую - для заполнения нулями, это выглядело бы быстрее.
Способ достижения этой цели при использовании стандартных утилит файловой системы linux по умолчанию заключался в создании двух «виртуальных» файлов: один для области, подлежащей заполнению нулями, и другой для фактически записываемых данных. , Запись каталога реального файла и цепочка кластеров FAT будут обновлены только после того, как фоновая задача будет фактически завершена, связав свой последний кластер с первым из «файла нулевого заполнения», а последний кластер этого с первым из « актуальный файл записи ". Также было бы желательно выполнить директивную запись, чтобы выполнить нулевое заполнение, чтобы избежать разрушения кэша страниц.
Примечание: Хотя все это технически возможно наверняка, вопрос в том, насколько целесообразным было бы сделать такое изменение? Кому нужна эта операция все время? Какими будут побочные эффекты?
Существующий (простой) код вполне приемлем для небольших пропускающих записей, вы не заметите его присутствия, если создадите файл размером 1 МБ и напишите в конце один байт. Это укусит вас, только если вы перейдете к размерам файлов в порядке пределов того, что файловая система FAT позволяет вам делать.
Другие опции ...
В некоторых ситуациях выполняемая задача включает в себя два (или более) шага:
- только что отформатированный (например) SD-карта с FAT
- поместите на него один или несколько больших файлов, чтобы «предварительно заполнить» карту
- (зависит от приложения, необязательно)
предварительно заполнить файлы, или
поместите в них образ петлевой файловой системы
В одном из случаев, над которым я работал, мы сложили первые два - то есть модифицировали mkdosfs
для предварительного выделения / предварительного создания файлов при создании файловой системы (FAT32). Это довольно просто: при написании таблиц FAT просто создайте выделенные цепочки кластеров вместо кластеров, заполненных маркером «free». Это также дает преимущество в том, что блоки данных гарантированно будут смежными, если ваше приложение выиграет от этого. И вы можете решить mkdosfs
, а не очистить предыдущее содержимое блоков данных. Если вы знаете, например, что один из ваших подготовительных шагов в любом случае включает запись всех данных или выполнение ext3-in-file-on-FAT (довольно распространенная вещь - устройство linux, sd-карта для обмена данными с windows app / gui), тогда нет необходимости обнулять что-либо / дважды записывать (один раз с нулями, один раз с чем-либо еще). Если ваш сценарий использования подходит для этого (то есть форматирование карты - это полезный / обычный шаг процесса «инициализировать его для использования» в любом случае), попробуйте; соответственно измененный mkdosfs
является частью источников dosfsutils TomTom, см. mkdosfs.c
поиск опции командной строки -N
для обработки .
Когда речь идет о предварительном распределении, как уже упоминалось, есть также posix_fallocate()
. В настоящее время в Linux при использовании FAT это будет по сути аналогично ручному вводу dd ...
, то есть дождаться нулевого заполнения. Но спецификация функции не требует, чтобы она была синхронной. Распределение блоков (генерация цепочки кластеров FAT) должно выполняться синхронно, но обновление размера диска на диске VFAT и нулевые заполнения блока данных могут быть задокументированы / отложены (т. Е. Либо выполнено в фоновом режиме с низким приоритетом, либо только если явно запрашивается через fdsync()
/ sync()
, чтобы приложение могло, например, выделять блоки, записывать содержимое с ненулевыми значениями ...). Это техника / дизайн, хотя; Я не знаю никого, кто делал эту модификацию ядра, хотя бы для экспериментов.