4. Программирование в системе GEOM

4.1. Ggate

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

4.2. Класс GEOM

Класс GEOM выполняет преобразования данных. Эти преобразования могут быть скомпонованы друг с другом в виде дерева. Экземпляр класса GEOM называют geom.

В каждом классе GEOM есть несколько ''методов класса'', которые вызываются когда экземпляра класса нет в наличии (или же они не привязаны к конкретному экземпляру класса).

Также определены функции событий GEOM, которые копируются в экземпляр geom.

Поле .geom в структуре g_class — это список (LIST) экземпляров geom, реализованных из класса.

Эти функции вызываются из g_event потока ядра.

4.3. Softc

''softc'' — это устаревший термин для ''приватных данных драйвера'' (''driver private data''). Название вероятней всего происходит от устаревшего термина ''software control block''. В системе GEOM softc это структура (точнее: указатель на структуру) которая может быть присоединена к экземпляру geom и может содержать приватные данные экземпляра. У большинства классов GEOM есть следующие члены:

Структура softc содержит состояние экземпляра geom. У каждого экземпляра есть свой softc.

4.4. Метаданные

Формат метаданных в той или иной мере зависит от конкретного класса, но обязан начинаться с:

Подразумевается, что классы geom знают как обращаться с метаданными с идентификаторами версий ниже, чем их собственные.

Метаданные размещаются в последнем секторе поставщика geom (поэтому обязаны целиком умещаться в нем).

(Все это зависит от реализации, но весь существующий код работает подобно описанному и поддерживается библиотеками.)

4.5. Маркирование/создание экземпляра geom

Последовательность событий следующая:

Вот так происходит создание/маркирование нового экземпляра geom:

(Приведенная выше последовательность событий зависит от конкретной реализации, но весь существующий код работает подобно описанному и поддерживается библиотеками.)

4.6. Структура команд geom

Вспомогательная библиотека geom_CLASSNAME.so экспортирует структуру class_commands, которая является массивом элементов struct g_command. Эти команды одинакового формата и выглядят следующим образом:

  команда [-опции] имя_geom [другие]

Общими командами являются:

Общие опции:

Некоторые операции, к примеру маркирование метаданными и разрушение метаданных могут быть выполнены из пространства пользовательских процессов. Для этого, структура g_command содержит поле gc_func, которое может быть установлено на функцию (в том-же .so), которая будет вызвана для обработки команды. В случае, когда gc_func равно NULL, команда будет передана модулю ядра: функции .ctlreq класса GEOM.

4.7. Экземпляры geom

У экземпляров классов GEOM есть внутренние данные, которые хранятся в структурах softc, а также есть некоторые функции, посредством которых они реагируют на внешние события.

Функции событий:

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

Из вышеупомянутых, наиболее важной и выполняющей полезную работу функцией является .start(), которая вызывается всякий раз, когда поставщику geom, управляемому экземпляром класса, приходит запрос BIO.

4.8. Потоки выполнения системы geom

Системой GEOM в ядре ОС создаются и используются три потока выполнения (kernel threads):

Когда пользовательский процесс запрашивает ''прочитать данные X по смещению Y файла'', происходит следующее:

За информацией о том, как данные передаются в структуре bio между экземплярами geom, смотрите g_bio(9) (обратите внимание на использование полей bio_parent и bio_children).

Важный момент в том, что НЕЛЬЗЯ ДОПУСКАТЬ БЛОКИРОВОК В ПОТОКАХ G_UP И G_DOWN. Вот неполный перечень того, что нельзя делать в этих потоках:

Это ограничение на код GEOM призвано избежать от ''засорения'' пути запроса ввода/вывода, так как блокировки обычно не имеют четких временных границ, и нет гарантий на занимаемое время (также на то есть и другие технические причины). Это также значит, что в вышеупомянутых потоках сколь-нибудь сложные операции выполнить нельзя, например: любое сложное преобразование требует выделения памяти. К счастью решение есть: создание дополнительных ядерных потоков.

4.9. Ядерные потоки выполнения, предназначенные для использования в коде geom

Ядерные потоки выполнения создаются функцией kthread_create(9), в своем поведении они схожи с потоками, созданными в пространстве пользовательских процессов, но есть одно отличие: они не могут известить вызвавший их поток о своем завершении; по завершению — необходимо вызывать kthread_exit(9)

В коде GEOM обычное назначение этих потоков — разгрузить поток g_down (функцию .start() ) от обработки запросов. Эти потоки подобны ''обработчикам событий'' (''event handlers''): у них есть очередь событий (которая наполняется событиями от разных функций из разных потоков; очередь необходимо защищать мьютексом), события из очереди выбираются одно за другим и обрабатываются в большом блоке switch().

Основное преимущество использования отдельного потока, который обрабатывает запросы ввода/вывода, то, что он может блокироваться по мере необходимости. Это, несомненно, привлекательно, но должно быть хорошо обдумано. Блокирование — хорошо и удобно, но может существенно снизить производительность преобразований данных в системе GEOM. Особо требовательные к производительности классы могут делать всю работу в функции .start(), уделяя особое внимание ошибкам при работе с памятью.

Еще одно преимущество потока ''обработчика событий'' это сериализация всех запросов и ответов, приходящих с разных потоков geom в один поток. Это также удобно, но может быть медленным. В большинстве случаев, обработка запросов функцией .done() может быть оставлена потоку g_up.

У мьютексов в ядре FreeBSD (mutex(9)) есть одно различие с их аналогами из пространства пользовательских процессов — во время удержания мьютекса в коде не должно быть блокировки. Если в коде необходимо блокирование, то лучше использовать sx(9). С другой стороны, если вся ваша работа выполняется в одном потоке, вы можете обойтись вообще без мьютексов.

Этот, и другие документы, могут быть скачаны с ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

По вопросам, связанным с FreeBSD, прочитайте документацию прежде чем писать в <questions@FreeBSD.org>.
По вопросам, связанным с этой документацией, пишите <doc@FreeBSD.org>.
По вопросам, связанным с русским переводом документации, пишите в рассылку <frdp@FreeBSD.org.ua>.
Информация по подписке на эту рассылку находится на сайте проекта перевода.