EFSL: I/O Manager (менеджер ввода/вывода) Печать
Добавил(а) microsin   

Тут представлен дословный перевод раздела 6.4 документации EFSL (версии 2.8, 2005 год), посвященного подсистеме IOMan.

IOManager является вторым по счету самым нижним уровнем библиотеки EFSL (после hwInterface). IOManager отвечает за координацию операций ввода и вывода диска, а также управление системой кэша. Документация описывает вторую реализацию IOMan, которая предоставляет следующие возможности:
    - отложенная запись (delayed write)
    - статистика обращения к буферу
    - экспортируемый для пользователей буфер
    - поддерживает кэшированный прямой ввод/вывод (I/O), как и непрямой I/O
    - может выделять память самостоятельно (через стек), или Вы можете это делать вручную (heap).

[Основные операции]

Поскольку ограниченная память присуща большинству встраиваемых устройств, для которых предназначена эта библиотека, в ней предпринято несколько решений с целью минимизировать расход памяти. Некоторые из них потребовали, чтобы разработчики пошли на некоторые уступки. Одна из них - отсутствие защиты памяти, поскольку большинство устройств не имеют памяти для поддержки этой возможности, или вообще не могут делать защиту памяти.
Когда IOMan получает запрос на сектор, он убеждается, что в собственном кэше есть данные сектора, и передает инициатору запроса указатель euint8* на кэш. Таким образом, пользователь свободен от операций с памятью, он просто указывает IOMan, что делать. В этом случае могут быть некоторые ошибки: Вы можете запросить сектор на чтение и потом записать данные в кэш, чем испортить его. Или Вы можете запросить сектор, но никогда не отдаёте его (разновидность утечки памяти), в результате получаете очень плохую производительность и блокирование I/O менеджера (deadlock).

Однако, принимая во внимание малый объем памяти, требуемый для операций, если Вы будете следовать правилам работы с IOMan, то получите отличный кэшированный объект, который сделает простой работу написания кода для файловой системы.

[Технология кэша]

Всякий раз, когда ioman получает запрос выбрать сектор (на чтение или запись), он должен будет удостовериться, что имеет, или может получить сектор, который Вы хотите. IOMan следует определенному алгоритму, чтобы проделать это.

1. Сначала он сканирует диапазон кэша, чтобы проверить - есть ли в нем уже этот сектор. Если сектор найден, и был запрос на запись, этот участок кэша помечается как записываемый. Информация по использованию и ссылка инкрементируются, и запрашивающей процедуре передается указатель (на кэш). Если содержимое кэша, соответствующее запрашиваемому сектору, не найдено, то ioman переходит к шагу 2.
2. Когда объект не в кэше, он читается с диска. Объект должен быть выбран с диска и помещён лучше всего в то место памяти кэша, которая еще не содержит ничего полезного. IOMan ищет такое место, которое пока ничем не занято. Если такое место найдено, сектор помещается туда. Иначе IOMan переходит к шагу 3.
3. Поскольку тут ничего не остается, кроме как перезаписать уже заполненный кэш, IOMan сделает попытку найти наименее интересную область кэша. Сначала он ищет кэш, который не помечен на запись, и не имеет пользователей. Затем IOMan выберет тот кэш, на который было меньше всего ссылок. Если такой кэш не найден, IOMan будет искать кэш среди помеченного на запись и не имеющего пользователей, с минимальным количеством ссылок. Поскольку кэш помечен на запись, сначала IOMan сбросит его содержимое на диск (медленная операция). После этого запрашиваемый сектор будет записан в найденное место, и вызывающей функции будет возвращен указатель на него. Если IOMan не сможет найти кэш, который не имеет пользователей, то перейдет к шагу 4.
4. Поскольку все места в кэше заняты, IOMan выберет один для переразмещения. Поскольку этот выбор зависит от многих факторов и весьма сложен, используется система указателей. Алгоритм просматривает каждое место в кэше на предмет количества указаний на него - наименьшее количество указывает на кандидата в переразмещение. 50% указаний производится на кэш, помеченный на запись, поскольку запись сектора самая затратная по ресурсам. Другие 35% - указания на переразмещение этого места в кэше. Это не имеет смысла к перераспределению того же самого буфера. Остальные 15% определяются по количеству ссылок на сектор. После того, как выбран лучший кандидат на переразмещение, IOMan перезапишет это место в кэше содержимым нового сектора. Также в стек поиска кэша будет помещен номер сектора и состояние кэша, чтобы во время освобождения сектора старый сектор мог быть восстанвлен. Если и эта операция не удалась, то происходит переход на шаг 5.
5. Отсюда IOMan возвращает нулевой указатель и устанавливает флаг ошибки.

[Функции]

esint8 ioman_init (IOManager *ioman, hwInterface *iface, euint8* bufferarea)
    Эта функция вызывается для инициализации внутреннего состояния I/O менеджера. Она должна быть первой вызываемой функцией для ioman-объекта. Если этого не сделать, то получится непредсказуемое поведение кода. Функция очищает все внутренние переменные и устанавливает их в состояние по умолчанию, а также настраивает область памяти. Здесь есть две возможности - если Вы передадите нулевой указатель bufferarea, вызываемая функция будет использовать статическую переменную размером в 512 * IOMAN NUMBUFFERS, иначе подразумевается, что Вы самостоятельно выделили память, и будет использоваться предоставленный Вами указатель на эту память.

void ioman_reset (IOManager *ioman)
    Эта функция вызывается из функции ioman_init, она делает сброс всех переменных.

esint8 ioman_pop (IOManager *ioman,euint16 bufplace)
    Эта функция выбирает установки (номер сектора, регистр статуса и использования) из стека и помещает их в обратно в основные регистры. В случае успеха возвращается 0, и в случае ошибки, либо когда в стеке нет элементов для pop, возвращается -1.

esint8 ioman_push (IOManager *ioman,euint16 bufplace)
    Эта функция проталкивает установки кэша в стек кэша, оставляя в целости данные основных регистров кэша. В случае успеха возвращается 0, и в случае ошибки, когда в стеке нет места для push, возвращается -1.

esint8 ioman_readSector (IOManager *ioman, euint32 address, euint8* buf)
    Эта функция производит реальное чтение данных из аппаратуры. Только эта функция вызывается при readBuf(), и здесь может быть реализована политика попыток повторного чтения при ошибке. Эта функция корректно передает поток ошибок на вышележащий уровень. Все вызовы этой функции из iomanager проверяются на возвращаемое значение, и информация об ошибках передается наверх. Параметр address указывает на данные относительно начала диска. Никакие предположения по поводу буфера не делаются, это может быть диапазон памяти кэша, либо область памяти, выделенная пользователем. В случае успеха возвращается 0, и в случае ошибки возвращается -1.

esint8 ioman_writeSector (IOManager *ioman, euint32 address, euint8* buf)
    Эта функция производит реальную запись данных в аппаратуру. Только эта функция вызывается при writeBuf(), и здесь может быть реализована политика попыток повторной записи при ошибке. Эта функция корректно передает поток ошибок на вышележащий уровень. Все вызовы этой функции из iomanager проверяются на возвращаемое значение, и информация об ошибках передается наверх. Параметр address указывает на данные относительно начала диска. Никакие предположения по поводу буфера не делаются, это может быть диапазон памяти кэша, либо область памяти, выделенная пользователем. В случае успеха возвращается 0, и в случае ошибки возвращается -1.

euint8* ioman_getSector (IOManager *ioman,euint32 address, euint8 mode)
    Эта функция наиболее часто вызывают из вышележащих подпрограмм библиотеки. Она предоставляет указатель на память, содержащую адрес номера сектора. Есть несколько режимов, которые Вы можете выбрать или комбинировать (операцией ИЛИ).
    IOM_MODE_READONLY   Этот атрибут говорит ioman, что нужен буфер только для чтения. Это не означает, что Вам разрешают записывать в этот буфер - если Вы так поступите, то получите непредсказуемое поведение. Вы не можете комбинировать эту опцию с IOM_MODE_READWRITE.
    IOM_MODE_READWRITE  Этот атрибут говорит ioman, что нужен буфер не только для чтения, но и для записи. Когда Вы освободите сектор, он будет записан на диск. Это необязательно случится немедленно, хотя Вы можете вызвать функцию ioman_flushRange(). Вы не можете комбинировать эту опцию с IOM_MODE_READONLY.
    IOM_MODE_EXP_REQ    Эта опция говорит ioman, что запрос является исключительным, например - запрос вряд ли случится снова. Библиотека добавляет эти флаги к опциям, когда запрашивается bootrecord, чтобы препятствовать получение запросу высокой оценки, что препятствует удалению этих данных из кэша.

esint8 ioman_releaseSector (IOManager *ioman, euint8* buf)
    Эта функция говорит ioman, что Вы окончили работу с некоторыми элементами кэша. Если забыть вызвать эту функцию, то можно получить deadlock в менеджерах IOMan.

esint8 ioman_directSectorRead (IOManager *ioman,euint32 address, euint8* buf)
    Это вариант обычного получения сектора. Иногда Вам нужен сектор с диска, но все, что Вам нужно сделать с ним - это положить его напрямую в буфер пользователя. Было бы глупо вызвать кэширование сектора, если есть внешнее пространство, доступное для данных сектора. Эта функция получает адрес сектора диска и помещает данные сектора в память, указываемую buf. Если сектор доступен из кэша, он будет просто скопирован функцией memCpy() в буфер пользователя.

esint8 ioman_directSectorWrite (IOManager *ioman, euint32 address, euint8* buf)
    Эта функция базируется на той же философии, что и ioman directSectorRead(), однако вопреки названию, данные проходят через слой кэширования. Если имеется неиспользуемая область кэша (или сектор в области кэша), буфер пользователя копируется в это место и остается там, пока это место не понадобится для других целей, либо пока не будет принудительно сделан flush.

esint8 ioman_flushRange (IOManager *ioman, euint32 address_low, euint32 address_high)
    Эта функция используется для запроса к ioman сбросить в указанном диапазоне все сектора на диск. Например, Вы можете захотеть сделать flush частного диапазона Вашей файловой системы, без ненужного беспокойства для других частей. Диапазон address_low < = n = > address_high. Само собой, будут записаны только те сектора, которые помечены на запись.

esint8 ioman_flushAll (IOManager *ioman)
    Эта функция заставит ioman сделать flush out всех частей кэша, помеченных на запись. Если у них не будет никаких пользователей, то они потеряют свою пометку записи.

[Ссылки]

1. Сайт EFSL.
2. IAR EW ARM: работа с файловой системой FAT на карточках SD/MMC.
3. IAR EW ARM: как сделать USB Mass Storage Device на основе MMC/SD.
4. Библиотека EFSL.