- Add translation of geom-class article
PR: docs/130742 Submitted by: Taras Korenko <ds@ukrhub.net>
This commit is contained in:
parent
832840e4cd
commit
9b34b39c11
Notes:
svn2git
2020-12-08 03:00:23 +00:00
svn path=/head/; revision=34809
3 changed files with 840 additions and 0 deletions
ru_RU.KOI8-R/articles
|
@ -25,6 +25,7 @@ SUBDIR+= fbsd-from-scratch
|
|||
SUBDIR+= fonts
|
||||
SUBDIR+= formatting-media
|
||||
SUBDIR+= freebsd-questions
|
||||
SUBDIR+= geom-class
|
||||
SUBDIR+= hats
|
||||
SUBDIR+= hubs
|
||||
SUBDIR+= ipsec-must
|
||||
|
|
19
ru_RU.KOI8-R/articles/geom-class/Makefile
Normal file
19
ru_RU.KOI8-R/articles/geom-class/Makefile
Normal file
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
# Article: Writing a GEOM Class
|
||||
|
||||
DOC?= article
|
||||
|
||||
FORMATS?= html
|
||||
WITH_ARTICLE_TOC?= YES
|
||||
|
||||
INSTALL_COMPRESSED?= gz
|
||||
INSTALL_ONLY_COMPRESSED?=
|
||||
|
||||
SRCS= article.sgml
|
||||
|
||||
URL_RELPREFIX?= ../../../..
|
||||
DOC_PREFIX?= ${.CURDIR}/../../..
|
||||
|
||||
.include "${DOC_PREFIX}/share/mk/doc.project.mk"
|
820
ru_RU.KOI8-R/articles/geom-class/article.sgml
Normal file
820
ru_RU.KOI8-R/articles/geom-class/article.sgml
Normal file
|
@ -0,0 +1,820 @@
|
|||
<!DOCTYPE article PUBLIC "-//FreeBSD//DTD DocBook V4.1-Based Extension//EN" [
|
||||
<!--
|
||||
The FreeBSD Russian Documentation Project
|
||||
$FreeBSD$
|
||||
$FreeBSDru$
|
||||
Original Revision: 1.8
|
||||
-->
|
||||
<!ENTITY % articles.ent PUBLIC "-//FreeBSD//ENTITIES DocBook FreeBSD Articles Entity Set//EN">
|
||||
%articles.ent;
|
||||
]>
|
||||
<!-- Перевод: Тарас Коренко -->
|
||||
|
||||
<article lang="ru">
|
||||
<title>Создание класса GEOM</title>
|
||||
|
||||
<articleinfo>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Ivan</firstname>
|
||||
<surname>Voras</surname>
|
||||
<affiliation>
|
||||
<address><email>ivoras@FreeBSD.org</email>
|
||||
</address>
|
||||
</affiliation>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<pubdate>$FreeBSD$</pubdate>
|
||||
|
||||
<legalnotice id="trademarks" role="trademarks">
|
||||
&tm-attrib.freebsd;
|
||||
&tm-attrib.cvsup;
|
||||
&tm-attrib.intel;
|
||||
&tm-attrib.xfree86;
|
||||
&tm-attrib.general;
|
||||
</legalnotice>
|
||||
|
||||
<abstract>
|
||||
<para>Эта статья документирует некоторые начальные выкладки
|
||||
в разработке GEOM-классов, а также модулей ядра в общем.
|
||||
Предполагается, что читатель близко знаком с программированием
|
||||
на Си в контексте пространства пользовательских процессов
|
||||
(userland).</para>
|
||||
</abstract>
|
||||
</articleinfo>
|
||||
|
||||
<sect1 id="intro">
|
||||
<title>Вступление</title>
|
||||
|
||||
<sect2 id="intro-docs">
|
||||
<title>Документация</title>
|
||||
|
||||
<para>Документация по программированию для ядра скудная, это одна из
|
||||
немногих областей программирования, где почти нет хороших учебных
|
||||
пособий, и совет <quote>читай исходники!</quote> —
|
||||
сохраняет свою справедливость. Однако, существует несколько
|
||||
статей и книг разной актуальности, которые рекомендуются к изучению
|
||||
перед тем, как начать программировать:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><ulink url="&url.books.developers-handbook;/index.html">
|
||||
Руководство FreeBSD для разработчиков</ulink> — часть
|
||||
Проекта Документации FreeBSD, ничего специфичного
|
||||
о программировании ядра в нем нет, зато есть немного общей
|
||||
полезной информации.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><ulink url="&url.books.arch-handbook;/index.html">
|
||||
Руководство по Архитектуре FreeBSD</ulink> — также
|
||||
является частью Проекта Документации FreeBSD, содержит описания
|
||||
некоторых низкоуровневых средств и процедур. Уделите внимание
|
||||
разделу номер 13 —
|
||||
<ulink url="&url.books.arch-handbook;/driverbasics.html">
|
||||
Написание драйверов устройств для FreeBSD</ulink>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Несколько интересных статей об устройстве ядра
|
||||
можно найти на сайте
|
||||
<ulink url="http://www.freebsddiary.com">FreeBSD
|
||||
Diary</ulink>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Страницы из раздела номер 9 системного справочника,
|
||||
содержат важную документацию по функциям ядра.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Страница справочника &man.geom.4;, а также <ulink
|
||||
url="http://phk.freebsd.dk/pubs/">слайды Пола-Хеннинга Кампа
|
||||
</ulink> — общее представление о подсистеме GEOM.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Страницы справочника &man.g.bio.9;, &man.g.event.9;,
|
||||
&man.g.data.9;, &man.g.geom.9;, &man.g.provider.9;,
|
||||
&man.g.consumer.9;, &man.g.access.9;, а также другие,
|
||||
связанные с вышеупомянутыми и раскрывающие специфический
|
||||
функционал подсистемы GEOM.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Страница справочника &man.style.9; — документирует
|
||||
соглашения о стиле оформления кода, которые обязаны быть соблюдены
|
||||
если вы планируете передать ваш код в CVS-древо FreeBSD.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="prelim">
|
||||
<title>Подготовка</title>
|
||||
|
||||
<para>Для того, чтоб заниматься разработками для ядра, желательно
|
||||
иметь два отдельных компьютера. Один из них предназначен для среды
|
||||
разработки и исходных кодов, а второй — для запуска тестов
|
||||
отлаживаемого кода. Второму компьютеру для работы достаточно
|
||||
иметь возможность выполнять начальную загрузку по сети и монтирование
|
||||
файловых систем по сети. В этой ситуации, если отлаживаемый код
|
||||
содержит ошибки и вызовет аварийную остановку системы, то это не
|
||||
повлечет порчу или утерю исходного кода
|
||||
<!-- (and other <quote>live</quote> data). -->.
|
||||
Второму компьютеру даже не потребуется иметь свой монитор,
|
||||
достаточно будет соединения асинхронных портов кабелем RS-232 или
|
||||
соединения при помощи KVM-устройства.</para>
|
||||
|
||||
<para>Но так как далеко не у каждого есть два или более компьютеров
|
||||
под рукой, есть пара способов подготовить иную <quote>живую</quote>
|
||||
систему для разработки кода для ядра. Один из них — это
|
||||
разработка в <ulink url="http://www.vmware.com/">VMWare</ulink>
|
||||
или <ulink url="http://www.qemu.org/">QEmu</ulink> виртуальной машине
|
||||
(это лучшее из доступного, после, конечно-же, выделенного для тестов
|
||||
компьютера).</para>
|
||||
|
||||
<sect2 id="prelim-system">
|
||||
<title>Настройка системы для разработки</title>
|
||||
|
||||
<para>Прежде всего необходимо иметь в ядре поддержку
|
||||
<option>INVARIANTS</option>. Добавьте следующие строки в
|
||||
файл конфигурации ядра:</para>
|
||||
|
||||
<programlisting>options INVARIANT_SUPPORT
|
||||
options INVARIANTS</programlisting>
|
||||
|
||||
<para>Для большей информативности при отладке включите поддержку
|
||||
WITNESS, которая будет предупреждать вас в случае возникновения
|
||||
взаимоблокировок:</para>
|
||||
|
||||
<programlisting>options WITNESS_SUPPORT
|
||||
options WITNESS
|
||||
</programlisting>
|
||||
|
||||
<para>Также включите отладочные символы, если планируете
|
||||
выполнять отладку по дампам аварийных отказов</para>
|
||||
|
||||
<programlisting> makeoptions DEBUG=-g</programlisting>
|
||||
|
||||
<para>Установка отладочного ядра обычным способом (<command>make
|
||||
installkernel</command>) не даст привычного результата:
|
||||
файл ядра будет называться <filename>kernel.debug</filename>
|
||||
и будет находиться в
|
||||
<filename>/usr/obj/usr/src/sys/KERNELNAME/</filename>.
|
||||
Для удобства, отладочное ядро необходимо скопировать в
|
||||
<filename>/boot/kernel/</filename>.</para>
|
||||
|
||||
<para>Также удобно иметь включенный отладчик ядра, так вы сможете
|
||||
исследовать паники сразу-же после их возникновения. Для включения
|
||||
отладчика добавьте следующие строки в файл конфигурации ядра:</para>
|
||||
|
||||
<programlisting>options KDB
|
||||
options DDB
|
||||
options KDB_TRACE</programlisting>
|
||||
|
||||
<para>Для автоматического запуска отладчика ядра после возникновения
|
||||
паники может понадобиться установить переменную sysctl:</para>
|
||||
|
||||
<programlisting> debug.debugger_on_panic=1</programlisting>
|
||||
|
||||
<para>Паники системы будут происходить, поэтому уделите внимание
|
||||
кэшу файловой системы. Обычно, при включенном механизме
|
||||
softupdates, последняя версия файла может быть утеряна если
|
||||
паника произошла раньше сбрасывания кэша на устройство хранения.
|
||||
Выключение механизма softupdates (посредством монтирования файловой
|
||||
системы с опцией <quote>sync</quote>) значительно сказывается на
|
||||
производительности и, опять-же, не гарантирует целосности
|
||||
данных. Как компромисс, можно сократить задержки сбрасывания
|
||||
кэша механизма softupdates. Есть три переменных sysctl, значения
|
||||
которых необходимо изменить (лучше всего — прописав их в
|
||||
<filename>/etc/sysctl.conf</filename>):</para>
|
||||
|
||||
<programlisting>kern.filedelay=5
|
||||
kern.dirdelay=4
|
||||
kern.metadelay=3</programlisting>
|
||||
|
||||
<para>Значения этих переменных — секунды.</para>
|
||||
|
||||
<para>Для отладки паник ядра необходимы дампы памяти.
|
||||
Так как паника ядра может <quote>сломать</quote> файловую
|
||||
систему, дамп сначала сохраняется в <quote>сырой</quote>
|
||||
раздел. Обычно, это своп-раздел. Поэтому, размер своп-раздела
|
||||
должен быть не меньше размера ОЗУ компьютера. При последующей
|
||||
загрузке дамп копируется в обычный файл. Это происходит сразу-же
|
||||
после проверки и монтирования файловых систем, но перед
|
||||
активированием раздела свопа. Такое поведение контроллируется
|
||||
следующими переменными <filename>/etc/rc.conf</filename>:</para>
|
||||
|
||||
<programlisting>dumpdev="/dev/ad0s4b"
|
||||
dumpdir="/usr/core" </programlisting>
|
||||
|
||||
<para>Переменная <varname>dumpdev</varname> указывает на раздел
|
||||
подкачки, а <varname>dumpdir</varname> сообщает системе куда
|
||||
перемещать дамп ядра при следующей загрузке.</para>
|
||||
|
||||
<para>Сохранение дампа ядра — процесс медленный, и, если
|
||||
у вашего компьютера много оперативной памяти (>256M) и если
|
||||
паники случаются часто, то ожидание сохранения дампов может начать
|
||||
раздражать (вспомним, что над дампом происходит две операции:
|
||||
сохранение в своп-файл и перемещение на файловую систему).
|
||||
В таком случае может оказаться удобным ограничивание объема
|
||||
используемой системой памяти путем установки переменной в
|
||||
<filename>/boot/loader.conf</filename>:</para>
|
||||
|
||||
<programlisting> hw.physmem="256M"</programlisting>
|
||||
|
||||
<para>Если паники случаются часто и размер файловых систем большой
|
||||
(или же вы просто не доверяете softupdates и фоновой проверке
|
||||
файловых систем), рекомендуется отключить фоновую проверку файловых
|
||||
систем посредством установки переменной в
|
||||
<filename>/etc/rc.conf</filename>:</para>
|
||||
|
||||
<programlisting> background_fsck="NO"</programlisting>
|
||||
|
||||
<para>В этом случае файловые системы будут проверяться только при
|
||||
необходимости. Также заметьте, что в случае использования фоновой
|
||||
проверки, новая паника может случиться в то время, когда
|
||||
проверяются диски. Другими словами, наиболее безопасный способ
|
||||
— не иметь много локальных файловых систем, а использовать
|
||||
второй компьютер в качестве NFS-сервера.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="prelim-starting">
|
||||
<title>Начало проекта</title>
|
||||
|
||||
<para>Для написания нового класса GEOM необходимо создать поддиректорию
|
||||
в любой доступной пользователю директории. Совсем не обязательно,
|
||||
чтоб ваш модуль изначально размещался в
|
||||
<filename>/usr/src</filename>.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="prelim-makefile">
|
||||
<title>Makefile</title>
|
||||
|
||||
<para>Правилом хорошего тона является создание
|
||||
<filename>Makefile</filename>-ов для каждого нетривиального
|
||||
проекта, примером которого конечно-же является создание
|
||||
модулей ядра.</para>
|
||||
|
||||
<para>Создание <filename>Makefile</filename> — дело
|
||||
не сложное благодаря исчерпывающему набору вспомогательных средств,
|
||||
предоставляемых системой. В кратце, вот как должен выглядеть
|
||||
<filename>Makefile</filename> для модуля ядра:</para>
|
||||
|
||||
<programlisting>SRCS=g_journal.c
|
||||
KMOD=geom_journal
|
||||
|
||||
.include <bsd.kmod.mk></programlisting>
|
||||
|
||||
<para>Этот <filename>Makefile</filename> (с измененными именами
|
||||
файлов) подойдет к любому модулю ядра. Класс GEOM может размещаться
|
||||
в одном единственном модуле ядра. Если для сборки вашего модуля
|
||||
требуется больше, чем один файл, то перечислите их имена,
|
||||
разделенные пробельными символами, в переменной <envar>SRCS</envar>.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="kernelprog">
|
||||
<title>Программирование в ядре FreeBSD</title>
|
||||
|
||||
<sect2 id="kernelprog-memalloc">
|
||||
<title>Выделение памяти</title>
|
||||
|
||||
<para>Прочитайте &man.malloc.9; — выделение памяти лишь
|
||||
немного отличается от своего эквивалента, используемого в пространстве
|
||||
пользовательских процессов (userland). Наиболее приметно то, что
|
||||
<function>malloc</function>() и <function>free</function>()
|
||||
принимают дополнительные параметры, которые описаны в странице
|
||||
справочника.</para>
|
||||
|
||||
<para>Тип <quote>malloc_type</quote> необходимо объявить в секции
|
||||
деклараций файла с ихсодным кодом, например:</para>
|
||||
|
||||
<programlisting> static MALLOC_DEFINE(M_GJOURNAL, "gjournal data", "GEOM_JOURNAL Data");</programlisting>
|
||||
|
||||
<para>Для того, чтобы можно было использовать этот макрос,
|
||||
необходимо включить следующие заголовочные файлы:
|
||||
<filename>sys/param.h</filename>,
|
||||
<filename>sys/kernel.h</filename> и
|
||||
<filename>sys/malloc.h</filename></para>
|
||||
|
||||
<para>Существует еще один механизм выделения памяти — UMA
|
||||
(Universal Memory Allocator), описанный в &man.uma.9;.
|
||||
Это специфический метод, преимущественно предназначенный для
|
||||
быстрого выделения памяти под списки, состоящие из элементов
|
||||
одинакового размера (например, динамические массивы структур).</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="kernelprog-lists">
|
||||
<title>Очередя и списки</title>
|
||||
|
||||
<para>Ознакомьтесь с &man.queue.3;
|
||||
Во множестве случаев вам необходимо будет организовывать и
|
||||
управлять такой структурой данных, как списки. К счастью, эта
|
||||
структура данных реализована несколькими способами в виде макросов
|
||||
на Си, а также включена в систему. Наиболее гибкий и часто
|
||||
употребляемый тип списка — TAILQ. Этот тип списка также один
|
||||
из наиболее требовательных к памяти (его элементы - с двойными
|
||||
связями), а также — наиболее медленный (однако счет идет
|
||||
на несколько инструкций ЦПУ, поэтому последнее утверждение не следует
|
||||
воспринимать в серьез).</para>
|
||||
|
||||
<para>Если важна скорость получения данных, то возьмите на вооружение
|
||||
&man.tree.3; и &man.hashinit.9;.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="kernelprog-bios">
|
||||
<title>BIOs</title>
|
||||
|
||||
<para>Структура <structname>bio</structname> используется для всех
|
||||
операций ввода/вывода, касающихся GEOM. Она содержит
|
||||
информацию о том, какое устройство ('поставщик geom') должно ответить
|
||||
на запрос, тип запроса, смещение, длину и указатель на буффер,
|
||||
а также набор <quote>определенных пользователем</quote> флагов и
|
||||
полей
|
||||
<!-- , которые могут помочь осуществить various hacks-->.
|
||||
</para>
|
||||
|
||||
<para>Важным моментом является то, что <structname>bio</structname>
|
||||
обрабатываются асинхронно. Это значит, что во многих частях кода
|
||||
нет аналога к &man.read.2; и &man.write.2; функциям из пространства
|
||||
пользовательских процессов, которые не возвращают управление пока
|
||||
не выполнится системный вызов. Скорее, по завершении обработки
|
||||
запроса (или в случае ошибки при обработке) как извещение вызывается
|
||||
определенная пользователем функция.</para>
|
||||
|
||||
<para>Асинхронная модель программирования в чем-то сложней,
|
||||
нежели чаще используемая императивная модель, используемая в
|
||||
пространстве пользовательских процессов; в любом случае, привыкание
|
||||
займет некоторое время. В некоторых случаях могут быть использованы
|
||||
вспомогательные функции <function>g_write_data</function>() и
|
||||
<function>g_read_data</function>(), но <emphasis>далеко не
|
||||
всегда</emphasis>. В частности, эти функции не могут использоваться
|
||||
когда захвачен мьютекс; например, мьютекс GEOM-топологии или
|
||||
внутренний мьютекс, удерживаемый в ходе выполнения
|
||||
<function>.start</function>() или <function>.stop</function>().</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="geom">
|
||||
<title>Программирование в системе GEOM</title>
|
||||
|
||||
<sect2 id="geom-ggate">
|
||||
<title>Ggate</title>
|
||||
|
||||
<para>Если максимальная производительность не требуется,
|
||||
то более простой способ совершать преобразования данныx —
|
||||
это выполнять их в пространстве пользовательских процессов
|
||||
посредством ggate (GEOM gate). К недостаткам следует отнести
|
||||
невозможность простого переноса кода в ядро.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="geom-class">
|
||||
<title>Класс GEOM</title>
|
||||
|
||||
<para>Класс GEOM выполняет преобразования данных. Эти преобразования
|
||||
могут быть скомпонованы друг с другом в виде дерева. Экземпляр
|
||||
класса GEOM называют <emphasis>geom</emphasis>.</para>
|
||||
|
||||
<para>В каждом классе GEOM есть несколько <quote>методов класса</quote>,
|
||||
которые вызываются когда экземпляра класса нет в наличии (или же
|
||||
они не привязаны к конкретному экземпляру класса).</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><function>.init</function> вызывается тогда, когда
|
||||
системе GEOM становится известно о классе GEOM (например,
|
||||
когда загружается модуль ядра).</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><function>.fini</function> будет вызван в случае отказа
|
||||
GEOM системы от класса (например, при выгрузке модуля).</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><function>.taste</function> вызывается, когда в
|
||||
системе появляется новый класс или поставщик geom
|
||||
(<quote>provider</quote>). Если соответствие найдено, то эта
|
||||
функция обычно создает и запускает экземпляр geom.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><function>.destroy_geom</function> вызывается при
|
||||
необходимости разрушить экземпляр geom.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><function>.ctlconf</function> будет вызван, когда
|
||||
пользователь запросит изменение конфигурации существующего
|
||||
экземпляра geom</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Также определены функции событий GEOM, которые копируются
|
||||
в экземпляр geom.</para>
|
||||
|
||||
<para>Поле <function>.geom</function> в структуре
|
||||
<structname>g_class</structname> — это список (LIST) экземпляров
|
||||
geom, реализованных из класса.</para>
|
||||
|
||||
<para>Эти функции вызываются из g_event потока ядра.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="geom-softc">
|
||||
<title>Softc</title>
|
||||
|
||||
<para><quote>softc</quote> — это устаревший термин
|
||||
для <quote>приватных данных драйвера</quote> (<quote>driver
|
||||
private data</quote>). Название вероятней всего происходит от
|
||||
устаревшего термина <quote>software control block</quote>.
|
||||
В системе GEOM softc это структура (точнее: указатель на
|
||||
структуру) которая может быть присоединена к экземпляру geom
|
||||
и может содержать приватные данные экземпляра. У большинства
|
||||
классов GEOM есть следующие члены:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><varname>struct g_provider *provider</varname> :
|
||||
<quote>поставщик geom</quote> предоставляемый данным экземпляром
|
||||
geom</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><varname>uint16_t n_disks</varname> : Количество
|
||||
потребителей geom (<quote>consumer</quote>), обслуживаемых данным
|
||||
экземпляром geom</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><varname>struct g_consumer **disks</varname> :
|
||||
Массив <varname>struct g_consumer*</varname>. (Невозможно
|
||||
обойтись одинарным указателем, потому что система GEOM создает
|
||||
для нас структуры struct g_consumer*)</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Структура <structname>softc</structname> содержит состояние
|
||||
экземпляра geom. У каждого экземпляра есть свой softc.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="geom-metadata">
|
||||
<title>Метаданные</title>
|
||||
|
||||
<para>Формат метаданных в той или иной мере зависит от конкретного
|
||||
класса, но <emphasis>обязан</emphasis> начинаться с:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>16-байтного буффера для подписи — строки с
|
||||
завершающим нулем (обычно это имя класса)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>uint32 идентификатора версии</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Подразумевается, что классы geom знают как обращаться с
|
||||
метаданными с идентификаторами версий ниже, чем их собственные.</para>
|
||||
|
||||
<para>Метаданные размещаются в последнем секторе поставщика geom
|
||||
(поэтому обязаны целиком умещаться в нем).</para>
|
||||
|
||||
<para>(Все это зависит от реализации, но весь существующий код работает
|
||||
подобно описанному и поддерживается библиотеками.)</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="geom-creating">
|
||||
<title>Маркирование/создание экземпляра geom</title>
|
||||
|
||||
<para>Последовательность событий следующая:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>пользователь запускает служебную программу &man.geom.8;</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>программа решает каким классом geom ей прийдется
|
||||
управлять и ищет библиотеку
|
||||
<filename>geom_<replaceable>CLASSNAME</replaceable>.so</filename>
|
||||
(которая обычно находится в <filename>/lib/geom</filename>).</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>она открывает библиотеку при помощи &man.dlopen.3;,
|
||||
извлекает вспомогательные функции и определения параметров
|
||||
командной строки.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Вот так происходит создание/маркирование нового экземпляра
|
||||
geom:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>&man.geom.8; ищет команду в аргументах командной
|
||||
строки (обычно это <option>label</option>) и вызывает
|
||||
вспомогательную функцию.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Вспомогательная функция проверяет параметры и
|
||||
собирает метаданные, которые записываются во все вовлеченные
|
||||
поставщики geom.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Это <quote>повреждает (spoil)</quote> существующие
|
||||
экземпляры geom (если они были) и порождает новый виток
|
||||
<quote>тестирования</quote> поставщиков geom. Целевой класс geom
|
||||
опознает метаданные и активирует экземпляр geom.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>(Приведенная выше последовательность событий зависит от
|
||||
конкретной реализации, но весь существующий код работает
|
||||
подобно описанному и поддерживается библиотеками.)</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="geom-command">
|
||||
<title>Структура команд geom</title>
|
||||
|
||||
<para>Вспомогательная библиотека <filename>geom_CLASSNAME.so</filename>
|
||||
экспортирует структуру <structname>class_commands</structname>,
|
||||
которая является массивом элементов
|
||||
<structname>struct g_command</structname>. Эти команды одинакового
|
||||
формата и выглядят следующим образом:</para>
|
||||
|
||||
<programlisting> команда [-опции] имя_geom [другие]</programlisting>
|
||||
|
||||
<para>Общими командами являются:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>label — записать метаданные в устройства,
|
||||
чтобы они могли быть опознаны в процессе тестирования
|
||||
и использованы в соответствующих экземплярах geom</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>destroy — разрушить метаданные, за которым
|
||||
последует разрушение экземпляров geom</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Общие опции:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><literal>-v</literal> : детальный вывод</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>-f</literal> : принудить</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Некоторые операции, к примеру маркирование метаданными и
|
||||
разрушение метаданных могут быть выполнены из пространства
|
||||
пользовательских процессов. Для этого, структура
|
||||
<structname>g_command</structname> содержит поле
|
||||
<varname>gc_func</varname>, которое может быть установлено на
|
||||
функцию (в том-же <filename>.so</filename>), которая будет вызвана
|
||||
для обработки команды. В случае, когда <varname>gc_func</varname>
|
||||
равно NULL, команда будет передана модулю ядра: функции
|
||||
<function>.ctlreq</function> класса GEOM.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="geom-geoms">
|
||||
<title>Экземпляры geom</title>
|
||||
|
||||
<para>У экземпляров классов GEOM есть внутренние данные, которые
|
||||
хранятся в структурах softc, а также есть некоторые функции,
|
||||
посредством которых они реагируют на внешние события.</para>
|
||||
|
||||
<para>Функции событий:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><function>.access</function> : просчитывает
|
||||
права доступа (чтение/запись/исключительный доступ)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><function>.dumpconf</function> : возвращает
|
||||
информацию о экземпляре geom; формат XML</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><function>.orphan</function> : вызывается, когда
|
||||
отсоединяется любой из низлежащих поставщиков geom</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><function>.spoiled</function> : вызывается, когда
|
||||
производится запись в низлежащий поставщик geom</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><function>.start</function> : обрабатывает ввод/вывод</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Эти функции вызываются из ядерного потока
|
||||
<function>g_down</function> и в этом контексте не может быть
|
||||
блокировок (поищите определение <quote>блокировка</quote> в других
|
||||
источниках), что немного ограничивает свободу действий, но
|
||||
способствует быстроте обработки.</para>
|
||||
|
||||
<para>Из вышеупомянутых, наиболее важной и выполняющей полезную работу
|
||||
функцией является <function>.start</function>(), которая вызывается
|
||||
всякий раз, когда поставщику geom, управляемому экземпляром класса,
|
||||
приходит запрос BIO.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="geom-threads">
|
||||
<title>Потоки выполнения системы geom</title>
|
||||
|
||||
<para>Системой GEOM в ядре ОС создаются и используются три
|
||||
потока выполнения (kernel threads):</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><literal>g_down</literal> : Обрабатывает запросы,
|
||||
приходящие от высокоуровневых сущностей (таких, как запросы из
|
||||
пространства пользовательских процессов) на пути к физическим
|
||||
устройствам</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>g_up</literal> : Обрабатывает ответы от
|
||||
драйверов устройств на запросы, выполненные высокоуровневыми
|
||||
сущностями</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>g_event</literal> : Отрабатывает в остальных
|
||||
случаях, как-то создание экземпляра geom, просчитывание прав
|
||||
доступа, события <quote>повреждения</quote> и т.п.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Когда пользовательский процесс запрашивает <quote>прочитать
|
||||
данные X по смещению Y файла</quote>, происходит следующее:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Файловая система преобразует запрос в экземпляр
|
||||
структуры bio и передает его системе GEOM. Файловая система
|
||||
<quote>знает</quote>, что экземпляр geom должен обработать запрос,
|
||||
так как файловые системы размещаются непосредственно
|
||||
над экземпляром geom.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Запрос завершается вызовом функции
|
||||
<function>.start</function>() в потоке g_down и достигает
|
||||
верхнего экземпляра geom.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Верхний экземпляр geom (например, это секционировщик
|
||||
разделов (partition slicer)) определяет, что запрос должен быть
|
||||
переадресован нижестоящему экземпляру geom (к примеру, драйверу
|
||||
диска). Вышестоящий экземпляр geom создает копию запроса bio
|
||||
(запросы bio <emphasis>ВСЕГДА</emphasis> копируются при передаче
|
||||
между экземплярами geom при помощи
|
||||
<function>g_clone_bio</function>()!),
|
||||
изменяет поля смещения и целевого поставщика geom и запускает
|
||||
на обработку копию при помощи функции
|
||||
<function>g_io_request</function>()</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Драйвер диска также получает запрос bio, как вызов
|
||||
функции <function>.start</function>() в потоке
|
||||
<literal>g_down</literal>. Драйвер обращается к контроллеру диска,
|
||||
получает блок данных и вызывает функцию
|
||||
<function>g_io_deliver</function>() используя копию запроса bio
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Теперь, извещение о завершении bio
|
||||
<quote>всплывает</quote> в потоке <literal>g_up</literal>.
|
||||
Сначала в потоке <literal>g_up</literal> вызывается функция
|
||||
<function>.done</function>() секционировщика разделов,
|
||||
последний использует полученную информацию, разрушает
|
||||
клонированный экземпляр структуры bio посредством
|
||||
<function>g_destroy_bio</function>() и вызывает
|
||||
<function>g_io_deliver</function>() используя
|
||||
первоначальный запрос</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Файловая система получает данные и передает их
|
||||
пользовательскому процессу</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>За информацией о том, как данные передаются в структуре
|
||||
<structname>bio</structname> между экземплярами geom,
|
||||
смотрите &man.g.bio.9; (обратите внимание на использование полей
|
||||
<varname>bio_parent</varname> и <varname>bio_children</varname>).
|
||||
</para>
|
||||
|
||||
<para>Важный момент в том, что <emphasis>НЕЛЬЗЯ ДОПУСКАТЬ БЛОКИРОВОК
|
||||
В ПОТОКАХ G_UP И G_DOWN</emphasis>. Вот неполный перечень того,
|
||||
что нельзя делать в этих потоках:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Вызывать функции <function>msleep</function>() или
|
||||
<function>tsleep</function>().</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Использовать функции
|
||||
<function>g_write_data</function>() и
|
||||
<function>g_read_data</function>(), так как они блокируются
|
||||
в момент обмена данными с потребителями geom.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Ожидать ввод/вывод.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Вызывать &man.malloc.9; и
|
||||
<function>uma_zalloc</function>() с установленным флагом
|
||||
<varname>M_WAITOK</varname>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Использовать &man.sx.9; <!-- и другие sleepable locks. -->
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Это ограничение на код GEOM призвано избежать от
|
||||
<quote>засорения</quote> пути запроса ввода/вывода, так как
|
||||
блокировки обычно не имеют четких временных границ, и нет гарантий
|
||||
на занимаемое время (также на то есть и другие технические причины).
|
||||
Это также значит, что в вышеупомянутых потоках сколь-нибудь сложные
|
||||
операции выполнить нельзя, например: любое сложное преобразование
|
||||
требует выделения памяти. К счастью решение есть: создание
|
||||
дополнительных ядерных потоков.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="geom-kernelthreads">
|
||||
<title>Ядерные потоки выполнения, предназначенные для использования
|
||||
в коде geom</title>
|
||||
|
||||
<para>Ядерные потоки выполнения создаются функцией
|
||||
&man.kthread.create.9;, в своем поведении они схожи с потоками,
|
||||
созданными в пространстве пользовательских процессов, но есть одно
|
||||
отличие: они не могут известить вызвавший их поток о своем завершении;
|
||||
по завершению — необходимо вызывать &man.kthread.exit.9;</para>
|
||||
|
||||
<para>В коде GEOM обычное назначение этих потоков — разгрузить
|
||||
поток <literal>g_down</literal>
|
||||
(функцию <function>.start</function>() ) от обработки запросов. Эти
|
||||
потоки подобны <quote>обработчикам событий</quote>
|
||||
(<quote>event handlers</quote>):
|
||||
у них есть очередь событий (которая наполняется событиями от разных
|
||||
функций из разных потоков; очередь необходимо защищать мьютексом),
|
||||
события из очереди выбираются одно за другим и обрабатываются
|
||||
в большом блоке <literal>switch</literal>().</para>
|
||||
|
||||
<para>Основное преимущество использования отдельного потока, который
|
||||
обрабатывает запросы ввода/вывода, то, что он может блокироваться по
|
||||
мере необходимости. Это, несомненно, привлекательно, но должно быть
|
||||
хорошо обдумано. Блокирование — хорошо и удобно, но может
|
||||
существенно снизить поизводительность преобразований данных в
|
||||
системе GEOM. Особо требовательные к производительности классы
|
||||
могут делать всю работу в функции <function>.start</function>(),
|
||||
уделяя особое внимание ошибкам при работе с памятью.</para>
|
||||
|
||||
<para>Еще одно преимущество потока <quote>обработчика событий</quote>
|
||||
это сериализация всех запростов и ответов, приходящих с разных
|
||||
потоков geom в один поток. Это также удобно, но может быть медленным.
|
||||
В большинстве случаев, обработка запросов функцией
|
||||
<function>.done</function>() может быть оставлена потоку
|
||||
<literal>g_up</literal>.</para>
|
||||
|
||||
<para>У мьютексов в ядре FreeBSD (&man.mutex.9;) есть одно различие
|
||||
с их аналогами из пространства пользовательских процессов —
|
||||
во время удержания мьютекса в коде не должно быть блокировки. Если
|
||||
в коде необходимо блокирование, то лучше исплользовать &man.sx.9;.
|
||||
С другой стороны, если вся ваша работа выполняется в одном потоке,
|
||||
вы можете обойтись вообще без мьютексов.</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
</article>
|
||||
|
Loading…
Reference in a new issue