[Из песочницы] Интеграция FATFS библиотеки для организации чтения дискового устройства на iOS

Введение


Статья посвящена внедрению open source библиотеки на iOS для чтения/записи данных с MFI дискового устройства на основе FAT12/FAT16/FAT32/Exfat. Представлен способ построения архитектуры приложения, на основе FATFS библиотеки, а также методы отладки и тестирования проводных MFI-устройств. Статья практически не содержит кода из-за соблюдения NDA.

Постановка задачи


Главной целью было создание некого универсального SDK (.framework), способного работать с производителями различных устройств дискового доступа по протоколу MFI, использующих единый стандарт по API.

dad03f8a0da249c79c1215b4dcedda50.png

На рисунке представлена обобщенная структурная схема такого фреймворка. В зависимости от протокола устройства выбирается та или иная библиотека взаимодействия с устройством. В зависимости от того, какая файловая система (FS) используется на диске, выбирается соответствующий алгоритм работы с диском: FAT/exFat/Other FS. При этом, к устройству могут иметь доступ несколько приложений (использующих данный SDK), находящихся в памяти устройства однако запись/чтение единовременно может выполнять только одно. Данный SDK подразумевает использование его во многопоточном приложении c конкурентными задачами чтения и записи.

Поиск решения


Первоначально в качестве базовой библиотеки для реализации FS FAT32 была выбрана реализация FAT32, созданная Apple. Однако, сложность интеграции стандартного API для устройства доступа вследствие привязки к конкретной платформе заставила пойти в направлении поиска готового решения с раздельным абстрактным API для физического уровня доступа c MFI устройством через библиотеку производителя. Были рассмотрены также EFSL и FATFS open source реализации FAT32. FATFS была выбрана вследствие нескольких причин:

• текущая поддержка библиотеки
• независимость платформы и простота портирования
• широкий спектр параметров конфигурации
• поддержка exFAT
• абстрактный уровень доступа для медиа драйверов

Вследствие проблем получения логов работы приложения, учитывая, что физически разъем был занят и не было возможности подключения к XCode, была подключена библиотека NSLogger, позволяющая передавать логи по Wi-Fi почти в real time режиме. Тем не менее, проблема отладки такой библиотеки имела место быть. Было решено разработать симулятор MFI устройства, который обращался к некой области на диске iOS/MAC устройства и эмулировал чтение/запись по тому же API, что и реальное устройство.

Проблемы интеграции


Портирование на Obj-C


FATFS написан на чистом С, а интегрировать приходилось в библиотеку написанной на ObjC. Несмотря на то, что ObjC напрямую поддерживает интеграцию C функций, возникали проблемы с сигнатурами функций и с использованием некоторых типов переменных.

Поддержка Unicode


Опция Unicode адекватно работала только с параметром кодировки Obj-C NSUTF16LittleEndianStringEncoding, несмотря на то, что согласно заявленным требованиям поддерживалось дополнительно UTF-16BE и UTF-8. Все методы, работающие с именами файлов необходимо было сделать потокобезопасными для корректной работы FAT32.

Thread safe


Опция потокобезопасности, активизируемая по параметру FS_REENTRANT и реализуемая через функции ff_req_grant, ff_rel_grant, ff_del_syncobj потребовала дополнительной имплементации через POSIX. Без включенной опции потокобезопасности наблюдалось повреждение таблицы файловой системы и как следствие — потеря данных. Файловый дескриптор pthread_t не сразу удалось корректно имплементировать вследствие того, что функции ff_ для синхронизации потоков передавали данные по значению, а не по ссылке. После замены на ссылочные значения — проблем с потокобезопасностью выполнения операций не возникало.

Кеширование


Во избежание потери данных при копировании и повреждении таблицы файлов, применялось синхронизация кэша данных записываемого файла с помощью функции f_sync. Учитывая, что размер дискового пространства мог быть более 16 Гб, некоторые базовые типы переменных пришлось поменять на «long log». FATFS изначально проектировалась с расчетом на встраиваемые устройства, характеризующиеся ограниченным размером памяти и низкой производительностью. Как следствие функции чтения/записи выполнялись по одному кластеру дискретно в единицу времени, что существенно ниже непрерывного чтения/записи. Подобная же проблема возникла при чтении структуры каталогов при наличии в директории более 100 файлов. Чтение/запись одного файла линейно росла с увеличением количества файлов, как показано на рисунке. По оси «x«отражено количество файлов в директории, а по оси «y» — время чтения в секундах. Типы кривых на графике для: V2 Sim Fat –симулятор дискового устройства, V1 Fat файловая система FAT32, для которой отсутствует данная проблема (не FATFS), V2 Fat64/128 FATFS c размером диска 64 и 128 Гб, соответственно.

1c9e645bd8094414b1ebc1afe17ecf50.png

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

1ce1e1f592b54b2e80e07f7884b6d84a.png

Уникальный доступ приложения к устройству


Приложения с интегрированным SDK не должны одновременно иметь доступ к внешнему дисковому устройству. Правила которым должно подчиняться приложение с используемым SDK должно выглядеть как показано ниже на диаграмме состояний.

487a4e9d6a764a31a99638bec8d1f1b8.png

Для установления правил доступа к устройству был имплементирован механизм основанный на Darwin Notification Center позволяющий с помощью уведомлений разрешать или блокировать работу с устройством приложения когда с устройством работает еще одно приложение с данным SDK. При первом запуске приложения отправляется запрос всем приложениям, подписанным на получение определенных уведомлений. Каждое приложение публикует свое состояние для остальных приложений, чтобы новое приложение могло перейти в адекватное состояние. В случае, если устройство свободно, приложение переходит в состояние USING. Состояние PENDING является промежуточным для BUSY и WAIT на тот случай, когда устройство не доступно или занято. В случае сбоя в коммуникациях используется состояние INVALID. После завершения использования устройства приложение переходит в состояние INACTIVE.

Тестирование Фреймворка


Написание Unit Tests для всех функций библиотеки было критически необходимо вследствие жёстких детерминированных требований к поведению API для конечных пользователей. Первоначально все тесты выполнялись на симуляторе дискового устройства. Однако, симулятор и библиотеку для низкоуровневого доступа диска писали разные люди, не имеющие возможности взаимодействия, в связи с чем поведение симулятора не всегда совпадало с поведением драйвера устройства. Для этого была разработана схема тестирования на реальном устройстве, представленная ниже. Тестируемый framework интегрировался в приложение, написанное с использованием Private API, а установка производилась через Xcode Server Over-the-Air installation. Private API позволяло выводить приложение из Background состояния приложения по Push Notification и запускать необходимые тесты. Результаты тестов отправлялись по REST протоколу на сервер статистики, где впоследствии они представлялись в виде графиков и таблиц. В библиотеку также включены Activity Tracing функции для получения более подробного crash лога.

da47e2bd8657448aa469b3ac62ec1f0a.png

Заключение


Несмотря на серьезные недостатки FATFS opensourse библиотеки, требующие доработки, другой альтернативы с возможностью использования Fat32 и ExFat, а также с абстрактным уровнем поддержки media drivers — найти не удалось. При корректной модификации библиотеки с подключенными дополнительными функциями кеширования и буферного чтения/записи, функции копирования и чтения файлов FAT32 API ниже скорости копирования LBA блоков медиа драйвера производителя железа всего на 10–15%. Для новичков в области разработки и интеграции файловых систем — FATFS может являться вполне достойным выбором.

Полезные ссылки


→ FatFs — Generic FAT File System Module
→ Microsoft EFI FAT32 File System Specification
→ Darwin Notification Concepts
→ About Continuous Integration in Xcode

Комментарии (0)

© Habrahabr.ru