RomeoGolf

Чт 25 Май 2017

USB-polygon-9: Подготовка к имитации ФС

Единицы информации: секторы, блоки, кластеры…

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

Байт

Будем считать, что тут все просто и понятно. Байт — восемь битов, минимально адресуемая единица информации.

На самом деле, все не так просто, поскольку исторически первый байт был 6 битов, в современных последовательных протоколах передачи информации (например, RS-232 или SPI) байт также может быть меньше или больше 8 битов, и есть современные процессорные системы, в которых минимально адресуемой единицей является тридцатидвухразрядное слово.

Но в рамках USB-полигона это все не важно. Байт — восемь битов, и из них складывается все остальное. Тем более, это на сегодняшний день единственная единица, при помощи которой можно сравнить объем информации в разных системах, используй они 4 бита на байт или шестьдесят четыре: КБ везде считается 1024 байта по 8 битов в каждом, вот и считай…

Сектор (а также цилиндр и головка)

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

Традиционно сектор равен 512 байтов. Настолько традиционно, что в некоторых программах, работающих с секторами, размер сектора — константа. Хотя есть сектора и большего размера. Но будем придерживаться традиций, пусть будет 512 байтов, больше незачем.

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

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

Головка (head) — считывающее устройство, привязанное к конкретному диску, фактически, номер головки равен номеру диска.

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

Логичнее было дать каждому сектору свой адрес. Ну и дали, в конце концов. Назвали «LBA», логическая блоковая адресация, но об этом чуть позже.

Кластер

По-английски «куст». Логическая единица хранения информации в файловой таблице. Понадобилась из-за ограничения размера файловых таблиц в ранних файловых системах семейства FAT: сектора дисков, которые становились все больше, уже не помещались в таблице, пришлось их кучковать, объединять в «пучки». Размер кластера вообще совсем-пресовсем не константа. Может быть один сектор, может и больше, например, восемь, а кое-где и все 128. Зависит не только от типа файловой системы, но и от форматирования конкретного носителя.

Это единица деления файлов. То есть, файл на диске состоит из кластеров. Значит, если кластер равен восьми секторам по 512 байтов, то файл размером в пару байтов будет занимать на диске 4 килобайта. А файл, который на пару байтов больше четырех килобайтов, будет «весить» все восемь.

И тут самое время вернуться к секторам. На сегодняшний день размер сектора в 512 байтов устарел. Крайне редко можно встретить кластер, равный одному или хотя бы двум секторам, обычно минимум четыре килобайта, то есть, восемь секторов. Так, может быть, проще сделать сектор четырехкилобайтным, а кластер размером в один сектор? Ну и сделали, назвали Advanced Format. Но! Флэшек это пока не касается. Тут и объемов таких огромных пока нет, и файловые системы семейства FAT широко используются. Так что, будем помнить, что «сектор = 512 Б» — это не догма, но пользоваться будем именно полукилобайтными.

Логический блок

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

Насколько я понял, встречающееся местами макроопределение #define VIRTUAL_MEMORY_BLOCK_SIZE 512 определяет именно размер логического блока.

Страница

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

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

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

Отсюда появляются такие макроопределения, как #define DATAFLASH_PAGE_SIZE 1024, для которых по-хорошему надо знать размер страницы конкретной микросхемы.

Правда, в контексте USB-полигона это не важно. Потому что нет микросхемы — нет и страниц, а что и сколько имитировать — без разницы.

Итого

  • Байт — просто единица измерения информации. Малополезна для адресации к данным на носителе, но достаточно полезна при приведении одних укрупненных единиц к другим.
  • Сектор или логический блок — минимально читаемая с носителя единица информации. То есть, даже если нужно прочитать один байт, прочитан будет все равно минимум сектор (и то в «сыром» режиме или если кластер равен одному сектору, иначе — еще больше), поэтому экономить на байтах для ускорения обмена с накопителем бесполезно. Экономить надо сразу секторами. Будем считать, что сектор равен 512 байтов.
  • Кластер — единица дробления файла. Файл не может быть меньше одного кластера. Если на носителе есть файловая система и данные читаются файлами, то кластер становится минимально читаемой единицей. Если файл содержит один байт, то все равно занимать он будет кластер, и читаться тоже будет кластер. Пусть будет 8 секторов, 4 килобайта.
  • Страница — единица измерения стираемой информации на флэшке. Стирается за один раз минимум одна страница. Используется не для доступа к информации, а больше во внутренних целях. Зависит от микросхемы.

Выбор файловой системы

Хотелось бы получить более-менее универсальное устройство, которое заработает в любой более-менее популярной современной операционной системе. То есть, в Windows начиная с XP, Android начиная хотя бы с 4 и Linux всякого рода. MacOS и iOS не рассматриваю по причине нищебродства (не имею и не предвидится).

Линуксовые файловые системы (Ext2..Ext4, ReiserFS, Xfs и прочие) придется отложить в сторонку — в винде с ними проблемы. Не то, чтобы совсем невозможно работать, нет, способы существуют. Но придется ставить отдельно драйвера ФС, а тогда можно было не заморачиваться с MassStorage и ставить драйвер для своего устройства.

Зато обыкновенные флэшки читаются сегодня без проблем упомянутыми ОС. А флэшки в большинстве своем отформатированы или в какой-нибудь FAT или NTFS.

NTFS — журналируемая файловая система, что напрочь отбивает охоту браться за ее имитацию. Остается FAT.

Выбираем из семейства: FAT, VFAT, FAT12, FAT16, FAT32, extFAT.

Чем ближе к историческому корню, тем проще. Первая версия FAT (для MS DOS до 2.0) имеет маленький корневой каталог, не имеет иерархии каталогов (они же папки, они же директории) — все файлы в корне. Для простых целей USB-полигона эти ограничения несущественны, а реализация FAT довольно проста и незамысловата. Но уж больно ранние версии убогенькие… Да и поддержка их может прекратиться в любой момент за практически полной ненадобностью.

Тогда, может быть, сразу extFAT? Относительно свеженькая, 2006 года, предназначена главным образом именно для флэшек. Однако ее заявленные преимущетва — увеличенные ограничения на размер файла, кластера, улучшение распределения свободного места, поддержка транзакций — в рамках проекта USB-полигона вообще никак не помогают, зато она проприетарная и заметно более сложная, чем даже предшествующая FAT32.

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

Способ имитации

Ну, грубо говоря, тут все довольно просто. Операционная система будет обращаться к устройству, предполагая, что это флэшка. Сперва почитает MBR — master boot record, потом PBR — partition boot record, то есть загрузочную запись раздела, потом захочет узнать подробности о файловой системе и почитает FAT — file allocation table, потом, возможо, если ее попросить, полезет в какой-нибудь файл.

ОС, стало быть, будет читать, а устройство, в свою очередь, будет получать команды чтения, а в них — в командах — есть адреса читаемых данных. MBR без вариантов по нулевому адресу, PBR по адресу, указанному в MBR, место FAT записано в PBR, а в FAT расписано, где какой файл начинается.

Получается, в ответ на запросы чтения можно сфабриковать липовые данные таким образом, что ОС получит примерно то, что ожидает, какие-то правдоподобные данные. При обращении по адресу 0 к MBR ответим, что PBR лежит по адресу А, и будем знать, что если будет обращение к А, то надо отвечать так, как будто прочиталась PBR, ну и так далее.

А отвечать на команды чтения устройство уже умеет, и где взять адрес данных — тоже знает.

Итак, необходимо будет отслеживать обращения к следующим фиктивным областям в виртуальной памяти устройства:

  • главная загрузочная запись устройства
  • загрузочная запись раздела, хватит одной, хотя можно и четыре
  • основная таблица FAT — список кластеров, занятых папками и файлами
  • резервная таблица FAT
  • файловая запись, соответствующая корневому каталогу
  • прочие файловые записи, по обстоятельствам (фиктивные файлы, а при желании — и фиктивные папки)

Теперь надо разобраться во внутренней структуре этих областей, разрисовать их подробнее, но об этом в следующий раз.


Теги: