Объединенные файловые системы
Объединенные файловые системы
Взгляните повнимательнее на уже знакомый нам рисунок.
Пространство имен путей в QNX/Neutrino.
Обратите внимание, что ответственными за префикс «/» объявили себя как файловая система fs-qnx4, так и администратор процессов. Это нормально, и беспокоиться тут не о чем. Мало того, иногда это оказывается очень даже неплохой идеей. Рассмотрим один такой случай.
Файловые системы с перекрытием
Предположим, что у вас очень медленное сетевое соединение, и вы смонтировали поверх него сетевую файловую систему. Вы замечаете, что некоторый файлы используются достаточно часто, и хотели бы, чтобы эти файлы неким волшебным способом «кешировались» на вашей машине, но увы и ах, проектировщики сетевой файловой системы это почему-то не предусмотрели. И тогда вы решаете самостоятельно написать кеширующую файловую систему (назовем ее, например, fs-cache) и поместить ее поверх сетевой файловой системы. Вот как это будет смотреться с позиции клиента:
Обе файловые системы, fs-nfs (сетевая файловая система) и ваша кэшированная файловая система (fs-cache) регистрируются под одним и тем же префиксом, «/nfs» уже упомянули выше, в QNX/Neutrino это нормально и абсолютно законно.
Предположим, что ваша система только что стартовала, и в вашей кэшированной файловой системе еще ничего нет. Клиентская программа пробует открыть какой-нибудь файл — скажем /nfs/home/rk/abc.txt. Ваша кэшированная файловая система находится «перед» сетевой файловой системой (я потом покажу вам, как это сделать, когда мы будем обсуждать реализацию администратора ресурса).
Клиентский вызов open() выполняет свои обычные действия:
1. Спрашивает администратор процессов: «К кому обратиться по поводу файла /nfs/home/rk/abc.txt?»
2. Получает ответ от администратора процессов: «Поговори сначала с fs-cache, а потом с fs-nfs».
Обратите внимание, что здесь администратор процессов возвращает две четверки ND/PID/CHID/handle — одну для файловой системы fs-cache и одну для файловой системы fs-nfs. Это критично.
Далее функция open() делает следующее:
1. Направляет сообщение файловой системе fs-cache. «Я бы хотел открыть файл /nfs/home/rk/abc.txt на чтение, пожалуйста.»
2. Получает ответ от файловой системы fs-cache: «Сожалею, но я никогда о таком не слышала.»
Здесь становится ясно, что с администратором файловой системы fs-cache клиентской функции open() не повезло. Файл не существует! Однако, вызов open() знает, что он получил список из двух четверок ND/PID/CHID/handle, и поэтому пробует второй вариант:
1. Направляет сообщение файловой системе fs-nfs: «Я бы хотел открыть файл /nfs/home/rk/abc.txt на чтение, пожалуйста.»
2. От файловой системы приходит ответ: «Запросто, никаких проблем!»
Теперь, после того как у функции open() есть EOK («никаких проблем»), она возвращает дескриптор файла. Все дальнейшие операции клиент выполняет непосредственно с администратором сетевой файловой системы fs-nfs.
Имя пути разрешается только один раз — во время вызова функции open(). Это означает, что как только мы успешно открыли нужный администратор ресурса, все дальнейшие вызовы, работающие с дескрипторами файлов, будут идти через него.
Так когда же вступает в игру наша кеширующая файловая система fs-cache? Ну, допустим, пользователь закончил считывание файла (файл теперь загружен в текстовый редактор). Когда файл понадобится сохранить, произойдет та же самая последовательность действий, но возникнет один любопытный поворот:
1. Сообщение администратору процессов: «С кем я должен переговорить насчет файла /nfs/home/rk/abc.txt?»
2. Ответ администратора процессов: «Поговори сначала с fs-cache, а затем с fs-nfs».
3. Сообщение fs-cache: «Мне хотелось бы открыть файл /nfs/home/rk/abc.txt на запись, пожалуйста».
4. Ответ от fs-cache: «Запросто, нет проблем».
Обратите внимание на то, что на 3 этапе мы открыли файл на запись, а не на чтение, как в первый раз. Поэтому не удивительно, что fs-cache на этот раз разрешает эту операцию (этап 4).
Еще более интересные события происходят, когда мы повторно пытаемся прочитать этот файл:
1. Сообщение администратору процессов: «С кем я должен переговорить насчет файла /nfs/home/rk/abc.txt?»
2. Ответ администратора процессов: «Поговори сначала с fs-cache, а затем с fs-nfs».
3. Сообщение fs-cache: «Мне хотелось бы открыть файл /nfs/home/rk/abc.txt на чтение, пожалуйста».
4. Ответ от fs-cache: «Запросто, нет проблем».
Да-да, на этот раз fs-cache обработала запрос на чтение!
Мы опустили несколько деталей, но для восприятия базовых идей они не так важны. Очевидно, кеширующая файловая система должна предусматривать некоторый способ отправки данных по сети на «реальный» носитель. Она также должна уметь перед отправкой данных клиенту проверять, не изменился ли файл (чтобы клиент не получил устаревшие данные). К тому же, кеширующая файловая система вполне могла бы сама обработать первый запрос на чтение, загрузив данные из сетевой файловой системы в свой кэш. И так далее.
Объединенные файловые системы (UFS — Unioned File Systems) и объединенные точки монтирования (UMP — Unioned Mount Points)
Дабы не путать понятия, сделаем небольшой экскурс в терминологию. Основное различие между объединенной файловой системой (UFS) и объединенной точкой монтирования (UMP) заключается в том, что UFS ориентирована на файлы, а UMP — на точки монтирования. В вышеупомянутой кеширующей файловой системе у нас была UFS, потому что оба администратора могли получить доступ к файлу вне зависимости от глубины его размещения файла в дереве каталогов. Давайте для примера рассмотрим другой администратор ресурса (назовем его «foobar»), отвечающий за путь «/nfs/other». В UFS-системе процесс fs-cache был бы способен кэшировать файлы и оттуда тоже, присоединившись к «/nfs». В случае с UMP, что принято в QNX/Neutrino по умолчанию, поскольку там все основано на соответствии самого длинного префикса, запросы смог бы обрабатывать только администратор foobar.