Продолжить начатую ранее тему устройства GEOM я хотел бы с таких понятий как tasting, orphanization и spoiling. Для пользователя системы наиболее интересным наверно покажется первый термин. Tasting - это процесс автоматической настройки geom объектов. Этот процесс инициируется подсистемой GEOM каждый раз, когда создаётся новый GEOM класс или провайдер. С точки зрения алгоритма работы эти два случая немного различаются, так tasting при создании нового провайдера выглядит примерно так:
Рассмотрим пример возможного сценария работы tasting при обнаружении жесткого диска:
Про tasting сказать вроде больше нечего, перейдём к orphanization. Это обратный tasting процесс, во время которого провайдеры уничтожаются. В переводе orphanization означает "осиротение" и это не просто так. Например, когда вы отключаете жесткий диск, драйвер диска посылает классу DISK команду на уничтожение. Тот, в свою очередь, инициирует процесс "осиротения" и все объекты geom, связанные с провайдером диска через консьюмеров "информируются" о том, что они стали "сиротами". Им передаётся код ошибки, который будет возвращён пользователю при любых попытка взаимодействия с осиротевшими провайдерами. Т.е. если вы попробуете записать данные на такой провайдер, то получите ошибку. Информирование выполняется через вызов базового для всех классов метода orphan, в реализации которого разработчик может предусмотреть действия, необходимые для завершения работы объекта. Ну и заканчивается этот процесс самоуничтожением всех осиротевших провайдеров и объектов. Если не углубляться в особенности реализации, то можно сказать, что "оно" всё делает "само". Само находит что ему нужно во время tasting, само уничтожается во время orphanization :)
Теперь рассмотрим spoiling. Этот механизм предназначен для защиты от устаревших метаданных. Стоит понимать, что защащются здесь не метаданные, а находящийся в памяти geom объект. Так как система оперирует с данными в памяти, важно чтобы они были синхронизированы с метаданными на диске.
Ранее я рассказывал о счётчиках доступа, которые по-совместительству служат для ограничения доступа. Этими же счётчиками подсистема GEOM пользуется для активации механизма spoiling. Когда кто-либо обращается к провайдеру, он явно или неявно вызывает функцию g_access, в которой указывается желаемый режим доступа. Например, вы записываете что-то на диск, для этого вам нужно получить "разрешение" на запись, при этом счётчик w увеличивается. Как только вы прекращаете запись - счётчик уменьшается. Переход состояния счётчика w из нуля в значение большее нуля вызывает механизм spoiling. При этом все консьюмеры этого провайдера предупреждаются о том, что кто-то открыл провайдера для записи. Никто не знает, что вы туда будете записывать, но если geom объекту это важно (там могут быть его метаданные), то он примет информацию к сведению. Обычно "принять к сведению" означает, что geom объект самоуничтожится (так же, как он это делает во время orphanization). Но стоит вам завершить запись, чтобы значение счётчика w перешло в ноль, будет иницирован процесс re-tasting по алгоритму, используемому при создании нового провайдера.
Таким образом срабатывает защита от устаревших метаданных. Если вы измените метаданные, или же просто произведёте запись не связанную с ними, то GEOM класс, использующий провайдера, заново обнаружит свои метаданные на нём и автоматически сконфигурируется. Если же вы удалите или повредите метаданные, то spoiling уничтожит geom объект, использующий провайдер. Жизненный пример на эту тему - gpart vs. bsdlabel:
Ещё один пример, эксплуатирующий эти особенности GEOM - это запуск механизма re-tasting в ручную. Хотя, честно говоря, мне он ниразу не понадобился, но в списках рассылки вчтречаются люди, которым это нужно. Делается это просто, нужно всего лишь "открыть" провайдер для записи и "закрыть" его, например так:
Это инициирует re-tasting и GEOM классы заново "обследуют" этот провайдер.
На этом пока всё. На счёт продолжения не уверен, но, возможно, будет ещё одна заметка.
- Ядро проходит в цикле по списку известных ему GEOM классов.
- Для каждого из них вызывается метод класса taste, который уже в зависимости от реализации класса может создать новый geom объект, либо посчитать этот новый провайдер неподходящим для использования классом.
- Ядро проходит в цикле по списку известных ему GEOM классов.
- Для каждого класса просматривается каждый существующий geom объект и все его провайдеры, которые передаются на проверку в метод taste для нового класса. А тот уже решает, подходит ему этот провайдер или нет.
Рассмотрим пример возможного сценария работы tasting при обнаружении жесткого диска:
- Драйвер жесткого диска создаёт geom объект ada0 класса DISK, тот в свою очередь создаёт провайдер ada0.
- Создание провайдера инициирует tasting. Последовательность может отличаться, но мы предположим такое развитие событий - вызывается метод taste класса DEV. Этот класс служит для создания файлов устройств в файловой системе devfs. Т.е. это его мы можем поблагодарить за создание /dev/ada0 (стоит заметить, что этот класс не создаёт своих провайдеров).
- На этом процесс tasting не заканчивается. Предположим, что в списке классов, которые знает ядро, следующим стоит класс PART. Ядро вызывает метод taste этого класса, он считывает секторы этого провайдера, в которых могут находиться таблицы разделов, и проверяет есть ли они там. Если есть, то создаются новые провайдеры, в соответствии с таблицей разделов. Это, в свою очередь, опять же инициирует tasting.
Про tasting сказать вроде больше нечего, перейдём к orphanization. Это обратный tasting процесс, во время которого провайдеры уничтожаются. В переводе orphanization означает "осиротение" и это не просто так. Например, когда вы отключаете жесткий диск, драйвер диска посылает классу DISK команду на уничтожение. Тот, в свою очередь, инициирует процесс "осиротения" и все объекты geom, связанные с провайдером диска через консьюмеров "информируются" о том, что они стали "сиротами". Им передаётся код ошибки, который будет возвращён пользователю при любых попытка взаимодействия с осиротевшими провайдерами. Т.е. если вы попробуете записать данные на такой провайдер, то получите ошибку. Информирование выполняется через вызов базового для всех классов метода orphan, в реализации которого разработчик может предусмотреть действия, необходимые для завершения работы объекта. Ну и заканчивается этот процесс самоуничтожением всех осиротевших провайдеров и объектов. Если не углубляться в особенности реализации, то можно сказать, что "оно" всё делает "само". Само находит что ему нужно во время tasting, само уничтожается во время orphanization :)
Теперь рассмотрим spoiling. Этот механизм предназначен для защиты от устаревших метаданных. Стоит понимать, что защащются здесь не метаданные, а находящийся в памяти geom объект. Так как система оперирует с данными в памяти, важно чтобы они были синхронизированы с метаданными на диске.
Ранее я рассказывал о счётчиках доступа, которые по-совместительству служат для ограничения доступа. Этими же счётчиками подсистема GEOM пользуется для активации механизма spoiling. Когда кто-либо обращается к провайдеру, он явно или неявно вызывает функцию g_access, в которой указывается желаемый режим доступа. Например, вы записываете что-то на диск, для этого вам нужно получить "разрешение" на запись, при этом счётчик w увеличивается. Как только вы прекращаете запись - счётчик уменьшается. Переход состояния счётчика w из нуля в значение большее нуля вызывает механизм spoiling. При этом все консьюмеры этого провайдера предупреждаются о том, что кто-то открыл провайдера для записи. Никто не знает, что вы туда будете записывать, но если geom объекту это важно (там могут быть его метаданные), то он примет информацию к сведению. Обычно "принять к сведению" означает, что geom объект самоуничтожится (так же, как он это делает во время orphanization). Но стоит вам завершить запись, чтобы значение счётчика w перешло в ноль, будет иницирован процесс re-tasting по алгоритму, используемому при создании нового провайдера.
Таким образом срабатывает защита от устаревших метаданных. Если вы измените метаданные, или же просто произведёте запись не связанную с ними, то GEOM класс, использующий провайдера, заново обнаружит свои метаданные на нём и автоматически сконфигурируется. Если же вы удалите или повредите метаданные, то spoiling уничтожит geom объект, использующий провайдер. Жизненный пример на эту тему - gpart vs. bsdlabel:
# gpart show md0 md0s1 => 9 204786 md0 MBR (100M) 9 204786 1 freebsd (100M) => 0 204786 md0s1 BSD (100M) 0 51200 1 freebsd-ufs (25M) 51200 51200 2 freebsd-ufs (25M) 102400 51200 4 freebsd-ufs (25M) 153600 51186 - free - (25M) # bsdlabel -w md0s1 # gpart show md0 md0s1 => 9 204786 md0 MBR (100M) 9 204786 1 freebsd (100M) => 0 204786 md0s1 BSD (100M) 0 16 - free - (8.0K) 16 204770 1 !0 (100M)В первом выводе gpart show есть два geom объекта класса PART. md0 - представляет таблицу разделов MBR, а md0s1 - является партицией MBR, провайдером объекта md0, и, в то же время, содержит разметку BSD. Выполнив bsdlabel -w мы выполнили запись на раздел md0s1, изменив метаданные объекта md0s1. При этом, в момент вызова команды bsdlabel она открыла провайдера md0s1 для записи, что инициировало spoiling - самоуничтожение объекта md0s1 и всех его партиций. После завершения записи на провайдере md0s1 снова были найдены метаданные разметки BSD, но на этот раз там один раздел.
Ещё один пример, эксплуатирующий эти особенности GEOM - это запуск механизма re-tasting в ручную. Хотя, честно говоря, мне он ниразу не понадобился, но в списках рассылки вчтречаются люди, которым это нужно. Делается это просто, нужно всего лишь "открыть" провайдер для записи и "закрыть" его, например так:
# true > /dev/ada2
Это инициирует re-tasting и GEOM классы заново "обследуют" этот провайдер.
На этом пока всё. На счёт продолжения не уверен, но, возможно, будет ещё одна заметка.
Спасибо! Хорошая статья. А нет ли желания написать подробнее про внутренности и механизмы работы GEOM?
ОтветитьУдалитьДаже не знаю.. Мне кажется, если пытаться описать подробно, то в формате блога это будет похоже на пересказывание кусков кода с комментированием.
ОтветитьУдалитьИначе, это будет уже глава для книги, а на такую работу у меня нет времени :)