aboutsummaryrefslogtreecommitdiff
path: root/documentation/content/ru
diff options
context:
space:
mode:
Diffstat (limited to 'documentation/content/ru')
-rw-r--r--documentation/content/ru/articles/linux-emulation/_index.adoc973
-rw-r--r--documentation/content/ru/articles/linux-emulation/_index.po4415
-rw-r--r--documentation/content/ru/articles/vm-design/_index.adoc49
-rw-r--r--documentation/content/ru/articles/vm-design/_index.po1356
-rw-r--r--documentation/content/ru/books/arch-handbook/_index.adoc57
-rw-r--r--documentation/content/ru/books/arch-handbook/_index.po71
-rw-r--r--documentation/content/ru/books/arch-handbook/bibliography/_index.adoc51
-rw-r--r--documentation/content/ru/books/arch-handbook/bibliography/_index.po45
-rw-r--r--documentation/content/ru/books/arch-handbook/book.adoc49
-rw-r--r--documentation/content/ru/books/arch-handbook/book.po71
-rw-r--r--documentation/content/ru/books/arch-handbook/boot/_index.adoc1351
-rw-r--r--documentation/content/ru/books/arch-handbook/boot/_index.po4415
-rw-r--r--documentation/content/ru/books/arch-handbook/driverbasics/_index.adoc347
-rw-r--r--documentation/content/ru/books/arch-handbook/driverbasics/_index.po867
-rw-r--r--documentation/content/ru/books/arch-handbook/isa/_index.adoc1121
-rw-r--r--documentation/content/ru/books/arch-handbook/isa/_index.po4237
-rw-r--r--documentation/content/ru/books/arch-handbook/jail/_index.adoc529
-rw-r--r--documentation/content/ru/books/arch-handbook/jail/_index.po1452
-rw-r--r--documentation/content/ru/books/arch-handbook/kobj/_index.adoc259
-rw-r--r--documentation/content/ru/books/arch-handbook/kobj/_index.po622
-rw-r--r--documentation/content/ru/books/arch-handbook/locking/_index.adoc145
-rw-r--r--documentation/content/ru/books/arch-handbook/locking/_index.po390
-rw-r--r--documentation/content/ru/books/arch-handbook/mac/_index.adoc5080
-rw-r--r--documentation/content/ru/books/arch-handbook/mac/_index.po8639
-rw-r--r--documentation/content/ru/books/arch-handbook/newbus/_index.adoc213
-rw-r--r--documentation/content/ru/books/arch-handbook/newbus/_index.po711
-rw-r--r--documentation/content/ru/books/arch-handbook/parti.adoc13
-rw-r--r--documentation/content/ru/books/arch-handbook/parti.po31
-rw-r--r--documentation/content/ru/books/arch-handbook/partii.adoc12
-rw-r--r--documentation/content/ru/books/arch-handbook/partii.po31
-rw-r--r--documentation/content/ru/books/arch-handbook/partiii.adoc12
-rw-r--r--documentation/content/ru/books/arch-handbook/partiii.po31
-rw-r--r--documentation/content/ru/books/arch-handbook/pccard/_index.adoc209
-rw-r--r--documentation/content/ru/books/arch-handbook/pccard/_index.po710
-rw-r--r--documentation/content/ru/books/arch-handbook/pci/_index.adoc424
-rw-r--r--documentation/content/ru/books/arch-handbook/pci/_index.po1071
-rw-r--r--documentation/content/ru/books/arch-handbook/scsi/_index.adoc1367
-rw-r--r--documentation/content/ru/books/arch-handbook/scsi/_index.po4280
-rw-r--r--documentation/content/ru/books/arch-handbook/smp/_index.adoc360
-rw-r--r--documentation/content/ru/books/arch-handbook/smp/_index.po1983
-rw-r--r--documentation/content/ru/books/arch-handbook/sound/_index.adoc351
-rw-r--r--documentation/content/ru/books/arch-handbook/sound/_index.po1125
-rw-r--r--documentation/content/ru/books/arch-handbook/sysinit/_index.adoc165
-rw-r--r--documentation/content/ru/books/arch-handbook/sysinit/_index.po393
-rw-r--r--documentation/content/ru/books/arch-handbook/usb/_index.adoc186
-rw-r--r--documentation/content/ru/books/arch-handbook/usb/_index.po1200
-rw-r--r--documentation/content/ru/books/arch-handbook/vm/_index.adoc127
-rw-r--r--documentation/content/ru/books/arch-handbook/vm/_index.po551
-rw-r--r--documentation/content/ru/books/developers-handbook/_index.adoc253
-rw-r--r--documentation/content/ru/books/developers-handbook/_index.po70
-rw-r--r--documentation/content/ru/books/developers-handbook/bibliography/_index.adoc62
-rw-r--r--documentation/content/ru/books/developers-handbook/bibliography/_index.po92
-rw-r--r--documentation/content/ru/books/developers-handbook/book.adoc84
-rw-r--r--documentation/content/ru/books/developers-handbook/book.po70
-rw-r--r--documentation/content/ru/books/developers-handbook/introduction/_index.adoc86
-rw-r--r--documentation/content/ru/books/developers-handbook/introduction/_index.po185
-rw-r--r--documentation/content/ru/books/developers-handbook/ipv6/_index.adoc687
-rw-r--r--documentation/content/ru/books/developers-handbook/ipv6/_index.po2818
-rw-r--r--documentation/content/ru/books/developers-handbook/kernelbuild/_index.adoc94
-rw-r--r--documentation/content/ru/books/developers-handbook/kernelbuild/_index.po140
-rw-r--r--documentation/content/ru/books/developers-handbook/kerneldebug/_index.adoc773
-rw-r--r--documentation/content/ru/books/developers-handbook/kerneldebug/_index.po2243
-rw-r--r--documentation/content/ru/books/developers-handbook/l10n/_index.adoc228
-rw-r--r--documentation/content/ru/books/developers-handbook/l10n/_index.po659
-rw-r--r--documentation/content/ru/books/developers-handbook/parti.adoc12
-rw-r--r--documentation/content/ru/books/developers-handbook/parti.po31
-rw-r--r--documentation/content/ru/books/developers-handbook/partii.adoc12
-rw-r--r--documentation/content/ru/books/developers-handbook/partii.po31
-rw-r--r--documentation/content/ru/books/developers-handbook/partiii.adoc12
-rw-r--r--documentation/content/ru/books/developers-handbook/partiii.po31
-rw-r--r--documentation/content/ru/books/developers-handbook/partiv.adoc12
-rw-r--r--documentation/content/ru/books/developers-handbook/partiv.po31
-rw-r--r--documentation/content/ru/books/developers-handbook/partv.adoc12
-rw-r--r--documentation/content/ru/books/developers-handbook/partv.po31
-rw-r--r--documentation/content/ru/books/developers-handbook/policies/_index.adoc148
-rw-r--r--documentation/content/ru/books/developers-handbook/policies/_index.po523
-rw-r--r--documentation/content/ru/books/developers-handbook/secure/_index.adoc230
-rw-r--r--documentation/content/ru/books/developers-handbook/secure/_index.po824
-rw-r--r--documentation/content/ru/books/developers-handbook/sockets/_index.adoc909
-rw-r--r--documentation/content/ru/books/developers-handbook/sockets/_index.po3021
-rw-r--r--documentation/content/ru/books/developers-handbook/testing/_index.adoc187
-rw-r--r--documentation/content/ru/books/developers-handbook/testing/_index.po802
-rw-r--r--documentation/content/ru/books/developers-handbook/tools/_index.adoc1428
-rw-r--r--documentation/content/ru/books/developers-handbook/tools/_index.po4483
-rw-r--r--documentation/content/ru/books/developers-handbook/x86/_index.adoc3864
-rw-r--r--documentation/content/ru/books/developers-handbook/x86/_index.po11160
86 files changed, 88205 insertions, 280 deletions
diff --git a/documentation/content/ru/articles/linux-emulation/_index.adoc b/documentation/content/ru/articles/linux-emulation/_index.adoc
new file mode 100644
index 0000000000..9d60209b00
--- /dev/null
+++ b/documentation/content/ru/articles/linux-emulation/_index.adoc
@@ -0,0 +1,973 @@
+---
+authors:
+ -
+ author: 'Roman Divacky'
+ email: rdivacky@FreeBSD.org
+description: 'Техническое описание внутреннего устройства слоя эмуляции Linux в FreeBSD'
+tags: ["Emulation", "Linuxulator", "kernel", "FreeBSD"]
+title: 'Эмуляция Linux® в FreeBSD'
+trademarks: ["freebsd", "ibm", "adobe", "netbsd", "realnetworks", "oracle", "linux", "sun", "general"]
+---
+
+= Эмуляция Linux(R) в FreeBSD
+:doctype: article
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:source-highlighter: rouge
+:experimental:
+:images-path: articles/linux-emulation/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+:imagesdir: ../../../images/{images-path}
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[.abstract-title]
+Аннотация
+
+Эта магистерская диссертация посвящена обновлению слоя эмуляции Linux(R) (так называемого _Linuxulator_). Задача состояла в обновлении слоя для соответствия функциональности Linux(R) 2.6. В качестве эталонной реализации было выбрано ядро Linux(R) 2.6.16. Концепция основана на реализации NetBSD. Большая часть работы была выполнена летом 2006 года в рамках программы Google Summer of Code для студентов. Основное внимание уделялось добавлению поддержки _NPTL_ (новой библиотеки потоков POSIX(R)) в слой эмуляции, включая _TLS_ (локальное хранилище потоков), _фьютексы (futex)_ (быстрые мьютексы в пользовательском пространстве), _PID mangling_ и некоторые другие второстепенные аспекты. В процессе было выявлено и исправлено множество мелких проблем. Моя работа была интегрирована в основной репозиторий исходного кода FreeBSD и войдет в предстоящий релиз 7.0R. Мы, команда разработчиков эмуляции, работаем над тем, чтобы сделать эмуляцию Linux(R) 2.6 стандартным слоем эмуляции в FreeBSD.
+
+'''
+
+toc::[]
+
+[[intro]]
+== Введение
+
+В последние несколько лет операционные системы с открытым исходным кодом на основе UNIX(R) начали широко использоваться на серверных и клиентских машинах. Среди этих операционных систем я хотел бы выделить две: FreeBSD — за наследие BSD, проверенную временем кодобазу и множество интересных возможностей, и Linux(R) — за широкую пользовательскую базу, активное сообщество разработчиков и поддержку крупных компаний. FreeBSD чаще используется на серверных машинах, выполняющих сложные сетевые задачи, и реже — на настольных компьютерах обычных пользователей. В то время как Linux(R) также применяется на серверах, он гораздо популярнее среди домашних пользователей. Это приводит к ситуации, когда для Linux(R) доступно множество проприетарных программ, которые не поддерживают FreeBSD.
+
+Естественно, возникает необходимость в возможности запуска Linux(R) бинарников в системе FreeBSD, и именно этому посвящена данная работа: эмуляции ядра Linux(R) в операционной системе FreeBSD.
+
+Летом 2006 года компания Google Inc. спонсировала проект, направленный на расширение слоя эмуляции Linux(R) (так называемого Linuxulator) в FreeBSD для включения возможностей Linux(R) 2.6. Данная диссертация написана в рамках этого проекта.
+
+[[inside]]
+== Взгляд изнутри...
+
+В этом разделе мы рассмотрим каждую из рассматриваемых операционных систем. Как они работают с системными вызовами, фреймами прерываний и другими низкоуровневыми аспектами. Также мы опишем, как они интерпретируют общие примитивы UNIX(R), такие как PID, потоки и т. д. В третьем подразделе мы поговорим о том, как в целом может быть реализована эмуляция UNIX(R) на UNIX(R).
+
+[[what-is-unix]]
+=== Что такое UNIX(R)
+
+UNIX(R) — это операционная система с долгой историей, которая повлияла почти на все остальные операционные системы, используемые в настоящее время. Начиная с 1960-х годов, её разработка продолжается и по сей день (хотя в разных проектах). Вскоре развитие UNIX(R) разделилось на два основных направления: семейства BSD и System III/V. Они взаимно влияли друг на друга, формируя общий стандарт UNIX(R). Среди вклада, возникшего в BSD, можно назвать виртуальную память, сетевой стек TCP/IP, FFS и многие другие. Ветка System V внесла свой вклад в примитивы межпроцессного взаимодействия SysV, копирование при записи и т. д. Самого UNIX(R) больше не существует, но его идеи были использованы многими другими операционными системами по всему миру, образовав так называемые UNIX(R)-подобные операционные системы. В наши дни наиболее влиятельными из них являются Linux(R), Solaris и, возможно (в некоторой степени), FreeBSD. Существуют корпоративные производные UNIX(R) (AIX, HP-UX и т. д.), но они всё больше мигрируют на упомянутые системы. Давайте подведём итог типичным характеристикам UNIX(R).
+
+[[tech-details]]
+=== Технические детали
+
+Каждая запущенная программа представляет собой процесс, который отражает состояние вычислений. Выполняющийся процесс разделяется между пространством ядра и пользовательским пространством. Некоторые операции могут выполняться только из пространства ядра (например, работа с оборудованием), но процесс должен проводить большую часть своего времени в пользовательском пространстве. Ядро — это место, где происходит управление процессами, оборудованием и низкоуровневыми деталями. Ядро предоставляет стандартный унифицированный UNIX(R) API для пользовательского пространства. Наиболее важные из них рассмотрены ниже.
+
+[[kern-proc-comm]]
+==== Обмен данными между ядром и пользовательским процессом
+
+Общий API UNIX(R) определяет системный вызов как способ передачи команд из пользовательского процесса ядру. Наиболее распространённая реализация использует либо прерывание, либо специализированную инструкцию (например, инструкции `SYSENTER`/`SYSCALL` для ia32). Системные вызовы определяются по номеру. Например, в FreeBSD системный вызов номер 85 — это man:swapon[2], а номер 132 — man:mkfifo[2]. Некоторые системные вызовы требуют параметров, которые передаются из пользовательского пространства в пространство ядра различными способами (зависит от реализации). Системные вызовы являются синхронными.
+
+Еще один возможный способ взаимодействия — использование _прерывания_. Прерывания происходят асинхронно после возникновения определенного события (деление на ноль, ошибка страницы и т.д.). Прерывание может быть прозрачным для процесса (ошибка страницы) или привести к реакции, например, отправке _сигнала_ (деление на ноль).
+
+[[proc-proc-comm]]
+==== Обмен данными между процессами
+
+Существуют другие API (System V IPC, разделяемая память и т.д.), но наиболее важным API являются сигналы. Сигналы отправляются процессами или ядром и принимаются процессами. Некоторые сигналы могут быть проигнорированы или обработаны пользовательской процедурой, другие приводят к предопределённому действию, которое нельзя изменить или игнорировать.
+
+[[proc-mgmt]]
+==== Управление процессами
+
+Процессы ядра обрабатываются первыми в системе (так называемый init). Каждый запущенный процесс может создать свою идентичную копию, используя системный вызов man:fork[2]. Были введены некоторые немного изменённые версии этого системного вызова, но базовая семантика остаётся той же. Каждый запущенный процесс может превратиться в другой процесс, используя системный вызов man:exec[3]. Были введены некоторые модификации этого системного вызова, но все они служат одной и той же базовой цели. Процессы завершают своё существование, вызывая системный вызов man:exit[2]. Каждый процесс идентифицируется уникальным номером, называемым PID. У каждого процесса есть определённый родитель (идентифицируемый его PID).
+
+[[thread-mgmt]]
+==== Управление потоками
+
+Традиционный UNIX(R) не определяет никакого API или реализации для потоков, в то время как POSIX(R) определяет свой API для потоков, но реализация остается неопределенной. Традиционно существовало два способа реализации потоков: обработка их как отдельных процессов (потоки 1:1) или обертывание всей группы потоков в один процесс с управлением потоками в пользовательском пространстве (потоки 1:N). Сравнение основных особенностей каждого подхода:
+
+Потоки 1:1
+
+- тяжеловесные потоки
+- планирование не может быть изменено пользователем (частично смягчено
+ благодаря POSIX(R) API)
++ нет необходимости в обёртке системных вызовов
++ может использовать несколько процессоров
+
+Потоки 1:N
+
++ легковесные потоки
++ планирование может быть легко изменено пользователем
+- Системные вызовы должны быть обернуты
+- не может использовать более одного CPU
+
+[[what-is-freebsd]]
+=== Что такое FreeBSD?
+
+Проект FreeBSD — одна из старейших операционных систем с открытым исходным кодом, доступных для повседневного использования. Она является прямым потомком оригинальной UNIX(R), поэтому можно утверждать, что это настоящая UNIX(R), хотя проблемы с лицензированием не позволяют этого сделать. Начало проекта относится к началу 1990-х годов, когда группа пользователей BSD создала набор исправлений для операционной системы 386BSD. На основе этого набора возникла новая операционная система под названием FreeBSD, получившая своё имя благодаря либеральной лицензии. Другая группа создала операционную систему NetBSD с другими целями. Мы сосредоточимся на FreeBSD.
+
+FreeBSD — это современная операционная система на основе UNIX(R), обладающая всеми возможностями UNIX(R). Вытесняющая многозадачность, многопользовательские функции, сетевые возможности TCP/IP, защита памяти, поддержка симметричной многопроцессорности, виртуальная память с объединёнными VM и кэшем буфера — всё это присутствует. Одной из интересных и чрезвычайно полезных особенностей является возможность эмуляции других UNIX(R)-подобных операционных систем. По состоянию на декабрь 2006 года и разработку 7-CURRENT поддерживаются следующие функции эмуляции:
+
+* Совместимость FreeBSD/i386 на FreeBSD/amd64
+* FreeBSD/i386 эмуляция на FreeBSD/ia64
+* Эмуляция Linux(R) операционной системы Linux(R) на FreeBSD
+* NDIS-эмуляция интерфейса сетевых драйверов Windows
+* NetBSD-эмуляция операционной системы NetBSD
+* Поддержка PECoff для исполняемых файлов FreeBSD в формате PECoff
+* Эмуляция SVR4 System V revision 4 UNIX(R)
+
+Активно разрабатываемые эмуляции — это слой Linux(R) и различные слои FreeBSD-on-FreeBSD. Остальные в настоящее время не должны работать корректно или быть пригодными к использованию.
+
+[[freebsd-tech-details]]
+==== Технические детали
+
+FreeBSD — это традиционный вариант UNIX(R) в смысле разделения выполнения процессов на две части: выполнение в пространстве ядра и выполнение в пространстве пользователя. Существует два типа входа процесса в ядро: системный вызов (syscall) и ловушка (trap). Возврат только один. В последующих разделах мы опишем три входа/выхода в/из ядра. Всё описание относится к архитектуре i386, так как Linuxulator существует только там, но концепция схожа на других архитектурах. Информация была взята из [1] и исходного кода.
+
+[[freebsd-sys-entries]]
+===== Системные записи
+
+В FreeBSD существует абстракция, называемая загрузчиком классов исполнения, которая является прослойкой в системном вызове man:execve[2]. Она использует структуру `sysentvec`, описывающую ABI исполняемого файла. Эта структура содержит такие элементы, как таблицу преобразования errno, таблицу преобразования сигналов, различные функции для обработки системных вызовов (исправление стека, создание дампов памяти и т.д.). Каждый ABI, который ядро FreeBSD поддерживает, должен определять эту структуру, так как она используется в дальнейшем в коде обработки системных вызовов и в некоторых других местах. Системные вызовы обрабатываются обработчиками прерываний, где можно одновременно получить доступ как к пространству ядра, так и к пользовательскому пространству.
+
+[[freebsd-syscalls]]
+===== Системные вызовы
+
+Системные вызовы в FreeBSD выполняются путем прерывания `0x80` с установленным в регистре `%eax` номером нужного системного вызова и аргументами, переданными через стек.
+
+Когда процесс вызывает прерывание `0x80`, срабатывает обработчик системного вызова `int0x80` (определённый в [.filename]#sys/i386/i386/exception.s#), который подготавливает аргументы (т.е. копирует их в стек) для вызова функции на языке C man:syscall[2] (определённой в [.filename]#sys/i386/i386/trap.c#), обрабатывающей переданный фрейм прерывания. Обработка включает подготовку системного вызова (в зависимости от записи `sysvec`), определение разрядности системного вызова (32-битный или 64-битный, что влияет на размер параметров), после чего параметры копируются, включая сам системный вызов. Затем выполняется фактическая функция системного вызова с обработкой кода возврата (особые случаи для ошибок `ERESTART` и `EJUSTRETURN`). В завершение планируется вызов `userret()`, возвращающий процесс в пользовательское пространство. Параметры для фактического обработчика системного вызова передаются в виде аргументов `struct thread *td`, `struct syscall args *`, где второй параметр является указателем на скопированную структуру параметров.
+
+[[freebsd-traps]]
+===== Ловушки (trap)
+
+Обработка ловушек в FreeBSD аналогична обработке системных вызовов. При возникновении ловушки вызывается обработчик на ассемблере. Он выбирается между `alltraps`, `alltraps` с сохранением регистров или `calltrap` в зависимости от типа ловушки. Этот обработчик подготавливает аргументы для вызова функции на языке C `trap()` (определена в [.filename]#sys/i386/i386/trap.c#), которая затем обрабатывает произошедшую ловушку. После обработки она может отправить сигнал процессу и/или вернуться в пользовательское пространство с помощью `userret()`.
+
+[[freebsd-exits]]
+===== Выходы
+
+Выход из ядра в пользовательское пространство происходит с использованием ассемблерной процедуры `doreti`, независимо от того, было ли ядро вызвано через ловушку или через системный вызов. Это восстанавливает состояние программы из стека и возвращает управление в пользовательское пространство.
+
+[[freebsd-unix-primitives]]
+===== Примитивы UNIX(R)
+
+Операционная система FreeBSD придерживается традиционной схемы UNIX(R), где каждый процесс имеет уникальный идентификационный номер, так называемый _PID_ (Идентификатор Процесса). Номера PID выделяются либо линейно, либо случайным образом в диапазоне от `0` до `PID_MAX`. Распределение номеров PID осуществляется с помощью линейного поиска в пространстве PID. Каждый поток в процессе получает тот же номер PID в результате вызова man:getpid[2].
+
+В настоящее время в FreeBSD существует два способа реализации потоков. Первый способ — это M:N потоки, за которым следует модель потоков 1:1. По умолчанию используется библиотека M:N (`libpthread`), но во время выполнения можно переключиться на потоки 1:1 (`libthr`). Планируется в ближайшее время перейти на библиотеку 1:1 по умолчанию. Хотя обе библиотеки используют одни и те же примитивы ядра, доступ к ним осуществляется через разные API. Библиотека M:N использует семейство системных вызовов `kse_*`, тогда как библиотека 1:1 использует семейство `thr_*`. Из-за этого отсутствует общая концепция идентификатора потока, разделяемая между ядром и пользовательским пространством. Конечно, обе библиотеки реализуют API идентификатора потока pthread. У каждого потока ядра (как описано в `struct thread`) есть идентификатор td tid, но он недоступен напрямую из пользовательского пространства и служит исключительно нуждам ядра. Он также используется в библиотеке потоков 1:1 в качестве идентификатора потока pthread, но обработка этого идентификатора внутренняя для библиотеки и не может быть использована напрямую.
+
+Как упоминалось ранее, в FreeBSD существуют две реализации потоков. Библиотека M:N разделяет работу между пространством ядра и пользовательским пространством. Поток — это сущность, которая планируется в ядре, но может представлять различное количество пользовательских потоков. M пользовательских потоков отображаются на N потоков ядра, что позволяет экономить ресурсы, сохраняя при этом возможность использовать преимущества многопроцессорного параллелизма. Дополнительную информацию о реализации можно получить из man-страницы или [1]. Библиотека 1:1 напрямую отображает пользовательский поток на поток ядра, что значительно упрощает схему. Ни одна из этих реализаций не включает механизм справедливости (такой механизм был реализован, но недавно удалён, поскольку вызывал серьёзное замедление и усложнял работу с кодом).
+
+[[what-is-linux]]
+=== Что такое Linux(R)
+
+Linux(R) — это UNIX(R)-подобное ядро, изначально разработанное Линусом Торвальдсом, а сейчас развиваемое множеством программистов по всему миру. От своих скромных начал до сегодняшнего дня, при широкой поддержке таких компаний, как IBM или Google, Linux(R) ассоциируется с быстрым темпом разработки, полной поддержкой оборудования и моделью организации по принципу "доброжелательного диктатора".
+
+Разработка Linux(R) началась в 1991 году как любительский проект в Университете Хельсинки, Финляндия. С тех пор она приобрела все черты современной ОС, подобной UNIX(R): многопроцессорность, поддержка многопользовательского режима, виртуальная память, сетевое взаимодействие — в общем, всё необходимое. Также присутствуют высокоуровневые функции, такие как виртуализация и т. д.
+
+В 2006 году Linux(R), похоже, был наиболее широко используемой открытой операционной системой с поддержкой независимых поставщиков программного обеспечения, таких как Oracle, RealNetworks, Adobe и других. Большая часть коммерческого программного обеспечения, распространяемого для Linux(R), доступна только в бинарном виде, поэтому перекомпиляция для других операционных систем невозможна.
+
+Большая часть разработки Linux(R) происходит в системе контроля версий Git. Git — это распределённая система, поэтому нет централизованного источника кода Linux(R), но некоторые ветви считаются основными и официальными. Схема нумерации версий, используемая в Linux(R), состоит из четырёх чисел: A.B.C.D. В настоящее время разработка ведётся в ветке 2.6.C.D, где C обозначает мажорную версию, в которую добавляются или изменяются функции, а D — минорную версию, предназначенную только для исправления ошибок.
+
+Дополнительную информацию можно получить из [3].
+
+[[linux-tech-details]]
+==== Технические детали
+
+Linux(R) следует традиционной схеме UNIX(R), разделяя выполнение процесса на две части: ядро и пользовательское пространство. Ядро может быть вызвано двумя способами: через ловушку (trap) или через системный вызов. Возврат осуществляется только одним способом. Далее описание относится к Linux(R) 2.6 на архитектуре i386(TM). Эта информация взята из [2].
+
+[[linux-syscalls]]
+===== Системные вызовы
+
+Системные вызовы в Linux(R) выполняются (в пользовательском пространстве) с использованием макросов `syscallX`, где X заменяется числом, представляющим количество параметров данного системного вызова. Этот макрос преобразуется в код, который загружает регистр `%eax` номером системного вызова и выполняет прерывание `0x80`. После этого вызывается возврат из системного вызова, который преобразует отрицательные значения возврата в положительные значения `errno` и устанавливает `res` в `-1` в случае ошибки. При вызове прерывания `0x80` процесс переходит в ядро в обработчик ловушки системного вызова. Эта процедура сохраняет все регистры в стеке и вызывает выбранную точку входа системного вызова. Обратите внимание, что соглашение о вызовах Linux(R) предполагает передачу параметров системного вызова через регистры, как показано здесь:
+
+. параметр -> `%ebx`
+. параметр -> `%ecx`
+. параметр -> `%edx`
+. параметр -> `%esi`
+. параметр -> `%edi`
+. параметр -> `%ebp`
+
+Существуют некоторые исключения из этого правила, где Linux(R) использует другие соглашения о вызовах (наиболее примечателен системный вызов `clone`).
+
+[[linux-traps]]
+===== Ловушки (trap)
+
+Обработчики ловушек представлены в файле [.filename]#arch/i386/kernel/traps.c#, а большинство этих обработчиков находятся в [.filename]#arch/i386/kernel/entry.S#, где происходит обработка ловушек.
+
+[[linux-exits]]
+===== Выходы
+
+Возврат из системного вызова обрабатывается функцией `syscall man:exit[3]`, которая проверяет, есть ли у процесса незавершённые задачи, затем проверяет, использовались ли селекторы, предоставленные пользователем. Если это произошло, применяется исправление стека, и, наконец, регистры восстанавливаются из стека, а процесс возвращается в пользовательское пространство.
+
+[[linux-unix-primitives]]
+===== Примитивы UNIX(R)
+
+В версии 2.6 операционная система Linux(R) переопределила некоторые традиционные примитивы UNIX(R), в частности PID, TID и поток. PID определяется не как уникальный для каждого процесса, поэтому для некоторых процессов (потоков) man:getppid[2] возвращает одинаковое значение. Уникальная идентификация процесса обеспечивается TID. Это связано с тем, что _NPTL_ (New POSIX(R) Thread Library) определяет потоки как обычные процессы (так называемая модель 1:1). Создание нового процесса в Linux(R) 2.6 происходит с использованием системного вызова `clone` (варианты fork перереализованы с его использованием). Этот системный вызов clone определяет набор флагов, которые влияют на поведение процесса клонирования в отношении реализации потоков. Семантика немного размыта, так как нет единого флага, указывающего системному вызову создать поток.
+
+Реализованные флаги клонирования:
+
+* `CLONE_VM` - процессы разделяют общее адресное пространство
+* `CLONE_FS` — совместно использовать umask, текущий рабочий каталог и пространство имён
+* `CLONE_FILES` - совместно использовать открытые файлы
+* `CLONE_SIGHAND` - разделять обработчики сигналов и заблокированные сигналы
+* `CLONE_PARENT` - использовать один процесс к качестве родительского
+* `CLONE_THREAD` — быть потоком (дальнейшие пояснения ниже)
+* `CLONE_NEWNS` - новое пространство имен
+* `CLONE_SYSVSEM` - совместное использование структур отмены SysV
+* `CLONE_SETTLS` - настройка TLS по указанному адресу
+* `CLONE_PARENT_SETTID` - установить TID в родителе
+* `CLONE_CHILD_CLEARTID` - очистить TID в дочернем процессе
+* `CLONE_CHILD_SETTID` - установить TID в дочернем процессе
+
+`CLONE_PARENT` устанавливает реального родителя в родителя вызывающего процесса. Это полезно для потоков, потому что если поток A создаёт поток B, мы хотим, чтобы поток B был привязан к родителю всей группы потоков. `CLONE_THREAD` делает то же самое, что `CLONE_PARENT`, `CLONE_VM` и `CLONE_SIGHAND`, перезаписывает PID, чтобы он совпадал с PID вызывающего процесса, устанавливает сигнал завершения в "нет" и входит в группу потоков. `CLONE_SETTLS` настраивает записи GDT для обработки TLS. Набор флагов `CLONE_*_*TID` устанавливает/сбрасывает предоставленный пользователем адрес в TID или 0.
+
+Как видно, `CLONE_THREAD` выполняет большую часть работы и не очень хорошо вписывается в схему. Первоначальный замысел неясен (даже для авторов, согласно комментариям в коде), но я думаю, изначально был один флаг для потоков, который затем был разделён на множество других флагов, но это разделение так и не было завершено. Также непонятно, для чего нужно это разделение, так как glibc не использует его, и только ручное использование clone позволяет программисту получить доступ к этим возможностям.
+
+Для непоточных программ PID и TID совпадают. Для поточных программ первый поток имеет одинаковые PID и TID, а каждый созданный поток разделяет тот же PID и получает уникальный TID (поскольку передается `CLONE_THREAD`), также родительский процесс общий для всех процессов, образующих эту поточную программу.
+
+Код, реализующий man:pthread_create[3] в NPTL, определяет флаги clone следующим образом:
+
+[.programlisting]
+....
+int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL
+
+ | CLONE_SETTLS | CLONE_PARENT_SETTID
+
+| CLONE_CHILD_CLEARTID | CLONE_SYSVSEM
+#if __ASSUME_NO_CLONE_DETACHED == 0
+
+| CLONE_DETACHED
+#endif
+
+| 0);
+....
+
+`CLONE_SIGNAL` определен как
+
+[.programlisting]
+....
+#define CLONE_SIGNAL (CLONE_SIGHAND | CLONE_THREAD)
+....
+
+последний 0 означает, что сигнал не отправляется при завершении любого из потоков.
+
+[[what-is-emu]]
+=== Что такое эмуляция
+
+Согласно словарному определению, эмуляция — это способность программы или устройства имитировать другую программу или устройство. Это достигается за счёт предоставления той же реакции на заданный стимул, что и у эмулируемого объекта. На практике в мире программного обеспечения в основном встречаются три типа эмуляции — программа, используемая для эмуляции машины (QEMU, различные эмуляторы игровых консолей и т.д.), программная эмуляция аппаратного обеспечения (эмуляторы OpenGL, эмуляция блоков плавающей запятой и т.д.) и эмуляция операционной системы (либо в ядре операционной системы, либо в виде программы пользовательского пространства).
+
+Эмуляция обычно используется в тех случаях, когда применение оригинального компонента невозможно или нецелесообразно. Например, может возникнуть необходимость использовать программу, разработанную для другой операционной системы. В такой ситуации на помощь приходит эмуляция. Иногда эмуляция — единственный возможный вариант, например, когда необходимое аппаратное устройство ещё не существует или уже не выпускается. Такое часто происходит при переносе операционной системы на новую (ещё не существующую) платформу. Иногда эмуляция просто экономически выгоднее.
+
+С точки зрения реализации, существует два основных подхода к эмуляции. Вы можете либо эмулировать всё целиком — принимать возможные входные данные исходного объекта, поддерживать внутреннее состояние и выдавать корректные выходные данные на основе состояния и/или входных данных. Такой вид эмуляции не требует каких-либо специальных условий и, в принципе, может быть реализован где угодно для любого устройства/программы. Недостаток в том, что реализация такой эмуляции довольно сложна, трудоёмка и подвержена ошибкам. В некоторых случаях можно использовать более простой подход. Представьте, что вы хотите эмулировать принтер, печатающий слева направо, на принтере, который печатает справа налево. Очевидно, что нет необходимости в сложном слое эмуляции — достаточно просто перевернуть печатаемый текст. Иногда эмулирующая среда очень похожа на эмулируемую, и тогда достаточно тонкого слоя преобразования для обеспечения полностью рабочей эмуляции! Как видите, такой подход гораздо менее требователен к реализации, а значит, менее трудоёмок и подвержен ошибкам, чем предыдущий. Однако необходимое условие — две среды должны быть достаточно схожи. Третий подход сочетает в себе два предыдущих. Чаще всего объекты не предоставляют одинаковые возможности, поэтому в случае эмуляции более мощного объекта на менее мощном приходится эмулировать отсутствующие функции с помощью полной эмуляции, описанной выше.
+
+Эта магистерская диссертация посвящена эмуляции UNIX(R) на UNIX(R), что является именно тем случаем, когда достаточно тонкого слоя трансляции для обеспечения полной эмуляции. API UNIX(R) состоит из набора системных вызовов, которые обычно самодостаточны и не влияют на глобальное состояние ядра.
+
+Существует несколько системных вызовов, которые влияют на внутреннее состояние, но это можно решить, предоставив некоторые структуры, поддерживающие дополнительное состояние.
+
+Эмуляция не бывает идеальной, и в эмуляторах часто чего-то не хватает, но обычно это не вызывает серьёзных проблем. Представьте эмулятор игровой приставки, который эмулирует всё, кроме звука. Без сомнения, игры остаются играбельными, и эмулятором можно пользоваться. Возможно, это не так комфортно, как оригинальная приставка, но это приемлемый компромисс между ценой и удобством.
+
+То же самое касается UNIX(R) API. Большинство программ могут работать с очень ограниченным набором системных вызовов. Эти вызовы, как правило, являются самыми старыми (man:read[2]/man:write[2], семейство man:fork[2], обработка man:signal[3], man:exit[3], API man:socket[2]), поэтому их легко эмулировать, поскольку их семантика одинакова во всех современных UNIX(R)-подобных системах.
+
+[[freebsd-emulation]]
+== Эмуляция
+
+=== Как работает эмуляция в FreeBSD
+
+Как упоминалось ранее, FreeBSD поддерживает выполнение бинарных файлов из нескольких других UNIX(R)-подобных систем. Это возможно благодаря наличию в FreeBSD абстракции, называемой загрузчик классов исполнения. Он интегрируется в системный вызов man:execve[2], поэтому когда man:execve[2] собирается выполнить бинарный файл, он анализирует его тип.
+
+В FreeBSD существуют два основных типа исполняемых файлов. Текстовые скрипты, подобные shell-скриптам, которые идентифицируются по первым двум символам `#!`, и обычные (как правило, _ELF_) бинарные файлы, представляющие собой скомпилированные исполняемые объекты. Подавляющее большинство (можно сказать, все) исполняемых файлов в FreeBSD относятся к типу ELF. Файлы ELF содержат заголовок, который определяет ABI операционной системы для данного ELF-файла. Считывая эту информацию, операционная система может точно определить, к какому типу относится данный исполняемый файл.
+
+Каждый ABI ОС должен быть зарегистрирован в ядре FreeBSD. Это относится и к родному ABI ОС FreeBSD. Таким образом, когда man:execve[2] выполняет двоичный файл, он перебирает список зарегистрированных API, и когда находит подходящий, начинает использовать информацию, содержащуюся в описании ABI ОС (его таблицу системных вызовов, таблицу преобразования `errno` и т.д.). Таким образом, каждый раз, когда процесс вызывает системный вызов, он использует свой собственный набор системных вызовов вместо какого-либо глобального. Это обеспечивает очень элегантный и простой способ поддержки выполнения различных двоичных форматов.
+
+Природа эмуляции различных ОС (а также некоторых других подсистем) привела разработчиков к внедрению механизма обработчиков событий. В ядре существует множество мест, где вызывается список обработчиков событий. Каждая подсистема может зарегистрировать обработчик событий, и они вызываются соответствующим образом. Например, при завершении процесса вызывается обработчик, который может выполнить необходимую очистку для подсистемы.
+
+Те простые средства предоставляют практически всё необходимое для инфраструктуры эмуляции, и, по сути, это единственное, что требуется для реализации слоя эмуляции Linux(R).
+
+[[freebsd-common-primitives]]
+=== Общие примитивы в ядре FreeBSD
+
+Для работы слоев эмуляции требуется некоторая поддержка со стороны операционной системы. Я расскажу о некоторых поддерживаемых примитивах в операционной системе FreeBSD.
+
+[[freebsd-locking-primitives]]
+==== Примитивы синхронизации
+
+Добавил: `{attilio}`
+
+Примитивы синхронизации FreeBSD основаны на идее предоставления достаточно большого количества различных примитивов таким образом, чтобы для каждой конкретной подходящей ситуации можно было использовать наилучший.
+
+На высоком уровне можно выделить три вида примитивов синхронизации в ядре FreeBSD:
+
+* атомарные операции и барьеры памяти
+* блокировки
+* барьеры планирования
+
+Ниже приведены описания для 3 семейств. Для каждой блокировки рекомендуется ознакомиться с соответствующей справочной страницей (где это возможно), чтобы получить более подробные объяснения.
+
+[[freebsd-atomic-op]]
+===== Атомарные операции и барьеры памяти
+
+Атомарные операции реализуются через набор функций, выполняющих простые арифметические действия над операндами в памяти атомарным образом по отношению к внешним событиям (прерываниям, вытеснению и т. д.). Атомарные операции могут гарантировать атомарность только для небольших типов данных (порядка величины типа `.long` в архитектуре C), поэтому их следует редко использовать напрямую в конечном коде, разве что для очень простых операций (например, установки флага в битовой карте). На самом деле довольно просто и часто можно допустить семантическую ошибку, полагаясь только на атомарные операции (обычно называемые lock-less). Ядро FreeBSD предоставляет способ выполнения атомарных операций в сочетании с барьерами памяти. Барьеры памяти гарантируют, что атомарная операция произойдет в определенном порядке относительно других обращений к памяти. Например, если нам нужно, чтобы атомарная операция выполнилась только после завершения всех ожидающих операций записи (с точки зрения переупорядочивания буферов инструкций), нам необходимо явно использовать барьер памяти вместе с этой атомарной операцией. Таким образом, легко понять, почему барьеры памяти играют ключевую роль в построении высокоуровневых блокировок (таких как refcounts, мьютексы и т. д.). Для подробного объяснения атомарных операций обратитесь к man:atomic[9]. Однако важно отметить, что атомарные операции (и барьеры памяти тоже) в идеале должны использоваться только для построения фронтенд-блокировок (например, мьютексов).
+
+[[freebsd-refcounts]]
+===== Счетчики ссылок (refcount)
+
+Счетчики ссылок (refcounts) — это интерфейсы для работы с подсчетом ссылок. Они реализованы с использованием атомарных операций и предназначены для случаев, когда счетчик ссылок — это единственное, что требует защиты, поэтому даже такие механизмы, как спин-мьютекс, не рекомендуются. Использование интерфейса refcount для структур, где уже применяется мьютекс, часто является ошибкой, так как, вероятно, следует защитить счетчик ссылок в рамках уже существующих защищенных участков кода. В настоящее время man-страница, посвященная refcount, отсутствует; для обзора существующего API обратитесь к [.filename]#sys/refcount.h#.
+
+[[freebsd-locks]]
+===== Блокировки
+
+Ядро FreeBSD имеет множество классов блокировок. Каждая блокировка определяется некоторыми уникальными свойствами, но, вероятно, наиболее важным является событие, связанное с конкурирующими владельцами (или, другими словами, поведение потоков, неспособных захватить блокировку). Схема блокировок FreeBSD предлагает три различных поведения для конкурирующих потоков:
+
+. вращающиеся
+. блокирующие
+. спящие
+
+[NOTE]
+====
+номера приведены не случайно
+====
+
+[[freebsd-spinlocks]]
+===== Вращающиеся блокировки
+
+Спин-блокировки позволяют ожидающим потокам продолжать работу (вращаться), пока они не смогут захватить блокировку. Важным аспектом является ситуация, когда поток соревнуется за спин-блокировку и не вытесняется. Поскольку ядро FreeBSD является вытесняющим, это подвергает спин-блокировки риску взаимоблокировок, которые можно устранить только отключением прерываний на время их удержания. По этой и другим причинам (таким как отсутствие поддержки распространения приоритетов, неэффективность схем балансировки нагрузки между CPU и т.д.), спин-блокировки предназначены для защиты очень небольших участков кода или, в идеале, не должны использоваться вовсе, если это не требуется явно (об этом далее).
+
+[[freebsd-blocking]]
+===== Блокирующие
+
+Блокирующие блокировки позволяют ожидающим потокам быть выгруженными и заблокированными до тех пор, пока владелец блокировки не освободит её и не разбудит один или несколько конкурентов. Чтобы избежать проблем с голоданием, блокирующие блокировки передают приоритет от ожидающих к владельцу. Блокирующие блокировки должны быть реализованы через интерфейс турникета и предназначены для наиболее частого использования в ядре, если нет особых условий.
+
+[[freebsd-sleeping]]
+===== Спящие
+
+Спящие блокировки (с ожиданием) позволяют ожидающим потокам быть вытесненными и заснуть до тех пор, пока держатель блокировки не освободит её и не разбудит один или несколько ожидающих. Поскольку блокировки с ожиданием предназначены для защиты больших участков кода и обработки асинхронных событий, они не поддерживают распространение приоритетов. Они должны быть реализованы через интерфейс man:sleepqueue[9].
+
+Порядок захвата блокировок очень важен, не только из-за возможности взаимоблокировки при обратном порядке захвата, но и потому, что захват блокировок должен следовать определённым правилам, связанным с их природой. Если взглянуть на таблицу выше, практическое правило заключается в том, что если поток удерживает блокировку уровня n (где уровень — это число, указанное рядом с типом блокировки), ему запрещено захватывать блокировки более высоких уровней, так как это нарушит заданную семантику пути. Например, если поток удерживает блокирующую блокировку (уровень 2), ему разрешено захватывать спин-блокировку (уровень 1), но не спящую блокировку (уровень 3), поскольку блокирующие блокировки предназначены для защиты более коротких путей, чем спящие блокировки (однако эти правила не касаются атомарных операций или барьеров планирования).
+
+Вот список блокировок с соответствующими типами поведения:
+
+* spin mutex – вращающийся режим – man:mutex[9]
+* sleep mutex – блокирующий режим – man:mutex[9]
+* pool mutex – блокирующий режим – man:mtx[pool]
+* Семейство функций sleep – спящий режим – man:sleep[9] pause tsleep msleep msleep_spin msleep_rw msleep_sx
+* condvar – спящий режим – man:condvar[9]
+* rwlock – блокирующий режим – man:rwlock[9]
+* sxlock – спящий режим – man:sx[9]
+* lockmgr – спящий режим – man:lockmgr[9]
+* семафоры – спящий режим – man:sema[9]
+
+Среди этих блокировок только мьютексы, sxlock, rwlock и lockmgr предназначены для обработки рекурсии, но в настоящее время рекурсия поддерживается только мьютексами и lockmgr.
+
+[[freebsd-scheduling]]
+===== Барьеры планирования
+
+Барьеры планирования предназначены для управления планированием потоков. Они в основном состоят из трех различных заглушек:
+
+* критические секции (и вытеснение)
+* sched_bind
+* sched_pin
+
+Как правило, их следует использовать только в определённом контексте, и даже если они часто могут заменять блокировки, их следует избегать, поскольку они не позволяют диагностировать простые потенциальные проблемы с помощью инструментов отладки блокировок (например, man:witness[4]).
+
+[[freebsd-critical]]
+===== Критические секции
+
+В ядре FreeBSD была реализована вытесняющая многозадачность в основном для работы с потоками обработки прерываний. Фактически, чтобы избежать высокой задержки прерываний, потоки с приоритетом разделения времени могут быть вытеснены потоками обработки прерываний (таким образом, им не нужно ждать планирования, как это предусмотрено в обычном случае). Однако вытеснение также вводит новые точки гонки, которые необходимо обрабатывать. Часто для борьбы с вытеснением проще всего полностью отключить его. Критическая секция определяет участок кода (ограниченный парой функций man:critical_enter[9] и man:critical_exit[9]), где гарантируется отсутствие вытеснения (пока защищённый код не будет полностью выполнен). Это часто может эффективно заменить блокировку, но должно использоваться осторожно, чтобы не потерять все преимущества, которые даёт вытеснение.
+
+[[freebsd-schedpin]]
+===== sched_pin/sched_unpin
+
+Еще один способ работы с вытеснением — это интерфейс `sched_pin()`. Если участок кода заключен между функциями `sched_pin()` и `sched_unpin()`, гарантируется, что соответствующий поток, даже если он может быть вытеснен, всегда будет выполняться на том же CPU. Закрепление очень эффективно в частном случае, когда нам необходимо обращаться к данным, привязанным к определенным CPU, и мы предполагаем, что другие потоки не изменят эти данные. Последнее условие делает критическую секцию избыточно строгим условием для нашего кода.
+
+[[freebsd-schedbind]]
+===== sched_bind/sched_unbind
+
+`sched_bind` — это API, используемый для привязки потока к определённому CPU на всё время выполнения кода, пока вызов функции `sched_unbind` не отменит эту привязку. Эта функция играет ключевую роль в ситуациях, когда нельзя доверять текущему состоянию CPU (например, на самых ранних этапах загрузки), так как требуется избежать миграции потока на неактивные CPU. Поскольку `sched_bind` и `sched_unbind` работают с внутренними структурами планировщика, их использование должно быть заключено в захват/освобождение `sched_lock`.
+
+[[freebsd-proc]]
+==== Структура proc
+
+Различные уровни эмуляции иногда требуют дополнительных данных для каждого процесса. Можно управлять отдельными структурами (списком, деревом и т.д.), содержащими эти данные для каждого процесса, но это может быть медленно и потреблять много памяти. Чтобы решить эту проблему, структура `proc` в FreeBSD содержит `p_emuldata` — указатель типа void на данные, специфичные для уровня эмуляции. Эта запись `proc` защищена мьютексом proc.
+
+Структура `proc` в FreeBSD содержит элемент `p_sysent`, который идентифицирует, под какой ABI работает данный процесс. Фактически, это указатель на упомянутый выше `sysentvec`. Таким образом, сравнивая этот указатель с адресом, по которому хранится структура `sysentvec` для данной ABI, мы можем эффективно определить, принадлежит ли процесс нашему эмуляционному слою. Код обычно выглядит следующим образом:
+
+[.programlisting]
+....
+if (__predict_true(p->p_sysent != &elf_Linux(R)_sysvec))
+ return;
+....
+
+Как видите, мы эффективно используем модификатор `__predict_true`, чтобы свести наиболее распространённый случай (процесс FreeBSD) к простой операции возврата, сохраняя высокую производительность. Этот код следует преобразовать в макрос, поскольку в настоящее время он не очень гибкий, например, мы не поддерживаем эмуляцию Linux(R)64, а также процессы Linux(R) в формате A.OUT на архитектуре i386.
+
+[[freebsd-vfs]]
+==== VFS
+
+Подсистема VFS в FreeBSD очень сложна, но слой эмуляции Linux(R) использует лишь небольшую её часть через чётко определённый API. Она может работать как с vnode, так и с файловыми дескрипторами. Vnode представляет собой виртуальный vnode, то есть представление узла в VFS. Другое представление — это файловый дескриптор, который представляет открытый файл с точки зрения процесса. Файловый дескриптор может представлять сокет или обычный файл. Файловый дескриптор содержит указатель на свой vnode. Более одного файлового дескриптора могут указывать на один и тот же vnode.
+
+[[freebsd-namei]]
+===== namei
+
+Функция man:namei[9] является центральной точкой входа для поиска и преобразования путей. Она проходит по пути шаг за шагом от начальной до конечной точки, используя функцию поиска, которая является внутренней для VFS. Системный вызов man:namei[9] может обрабатывать символьные ссылки, абсолютные и относительные пути. Когда путь ищется с помощью man:namei[9], он заносится в кэш имён. Это поведение можно отключить. Данная функция используется повсеместно в ядре, и её производительность крайне важна.
+
+[[freebsd-vn]]
+===== vn_fullpath
+
+Функция man:vn_fullpath[9] предпринимает максимальные усилия для обхода кэша имён VFS и возвращает путь для заданного (заблокированного) vnode. Этот процесс ненадёжен, но в большинстве типичных случаев работает корректно. Ненадёжность обусловлена тем, что функция опирается на кэш VFS (она не обходит структуры на носителе), не работает с жёсткими ссылками и т.д. Данная процедура используется в нескольких местах Linuxulator.
+
+[[freebsd-vnode]]
+===== Операции с vnode
+
+* `fgetvp` - по заданным потоку и номеру файлового дескриптора возвращает связанный vnode
+* man:vn_lock[9] - блокирует vnode
+* `vn_unlock` - разблокирует vnode
+* man:VOP_READDIR[9] - читает каталог, на который ссылается vnode
+* man:VOP_GETATTR[9] - получает атрибуты файла или каталога, на который ссылается vnode
+* man:VOP_LOOKUP[9] - выполняет поиск пути к заданному каталогу
+* man:VOP_OPEN[9] - открывает файл, на который ссылается vnode
+* man:VOP_CLOSE[9] - закрывает файл, на который ссылается vnode
+* man:vput[9] - уменьшает счетчик использования для vnode и разблокирует его
+* man:vrele[9] - уменьшает счетчик использования для vnode
+* man:vref[9] - увеличивает счетчик использования для vnode
+
+[[freebsd-file-handler]]
+===== Операции обработчика файлов (handler)
+
+* `fget` - для заданного потока и номера файлового дескриптора возвращает связанный обработчик файла и делает на него ссылку
+* `fdrop` - освобождает ссылку на обработчик файлов
+* `fhold` - ссылается на обработчик файла
+
+[[md]]
+== Слой эмуляции Linux(R) - машинно-зависимая часть
+
+В этом разделе рассматривается реализация слоя эмуляции Linux(R) в операционной системе FreeBSD. Сначала описывается машинно-зависимая часть, рассказывающая о том, как и где реализовано взаимодействие между пользовательским пространством и ядром. Рассматриваются системные вызовы, сигналы, ptrace, ловушки и исправление стека. Эта часть посвящена архитектуре i386, но написана в общем виде, поэтому другие архитектуры не должны сильно отличаться. Следующая часть — машинно-независимая часть Linuxulator. Этот раздел охватывает только i386 и обработку ELF. A.OUT устарел и не поддерживается.
+
+[[syscall-handling]]
+=== Обработка системных вызовов
+
+Обработка системных вызовов в основном реализована в файле [.filename]#linux_sysvec.c#, который покрывает большинство процедур, указанных в структуре `sysentvec`. Когда процесс Linux(R), выполняющийся на FreeBSD, делает системный вызов, общая процедура обработки системных вызовов вызывает linux prepsyscall для ABI Linux(R).
+
+[[linux-prepsyscall]]
+==== Linux(R) prepsyscall
+
+Linux(R) передает аргументы системных вызовов через регистры (поэтому на i386 ограничено 6 параметрами), тогда как FreeBSD использует стек. Подпрограмма Linux(R) `prepsyscall` должна копировать параметры из регистров в стек. Порядок регистров следующий: `%ebx`, `%ecx`, `%edx`, `%esi`, `%edi`, `%ebp`. Однако это верно только для _большинства_ системных вызовов. Некоторые (особенно `clone`) используют другой порядок, но это, к счастью, легко исправить, добавив фиктивный параметр в прототип `linux_clone`.
+
+[[syscall-writing]]
+==== Как писать системные вызовы
+
+Каждый системный вызов, реализованный в Linuxulator, должен иметь свой прототип с различными флагами в [.filename]#syscalls.master#. Формат файла следующий:
+
+[.programlisting]
+....
+...
+ AUE_FORK STD { int linux_fork(void); }
+...
+ AUE_CLOSE NOPROTO { int close(int fd); }
+...
+....
+
+Первый столбец представляет номер системного вызова. Второй столбец предназначен для поддержки аудита. Третий столбец обозначает тип системного вызова. Он может быть `STD`, `OBSOL`, `NOPROTO` или `UNIMPL`. `STD` — это стандартный системный вызов с полным прототипом и реализацией. `OBSOL` означает устаревший вызов и определяет только прототип. `NOPROTO` означает, что системный вызов реализован в другом месте, поэтому не требует добавления префикса ABI и т.д. `UNIMPL` означает, что системный вызов будет заменён на `nosys` (системный вызов, который просто выводит сообщение о том, что вызов не реализован, и возвращает `ENOSYS`).
+
+Из файла [.filename]#syscalls.master# скрипт генерирует три файла: [.filename]#linux_syscall.h#, [.filename]#linux_proto.h# и [.filename]#linux_sysent.c#. Файл [.filename]#linux_syscall.h# содержит определения имен системных вызовов и их числовых значений, например:
+
+[.programlisting]
+....
+...
+#define LINUX_SYS_linux_fork 2
+...
+#define LINUX_SYS_close 6
+...
+....
+
+[.filename]#linux_proto.h# содержит определения структур аргументов для каждого системного вызова, например:
+
+[.programlisting]
+....
+struct linux_fork_args {
+ register_t dummy;
+};
+....
+
+И, наконец, [.filename]#linux_sysent.c# содержит структуру, описывающую таблицу системных вызовов, используемую для фактической диспетчеризации системного вызова, например:
+
+[.programlisting]
+....
+{ 0, (sy_call_t *)linux_fork, AUE_FORK, NULL, 0, 0 }, /* 2 = linux_fork */
+{ AS(close_args), (sy_call_t *)close, AUE_CLOSE, NULL, 0, 0 }, /* 6 = close */
+....
+
+Как видно, `linux_fork` реализован в самом Linuxulator, поэтому определение имеет тип `STD` и не имеет аргументов, что демонстрируется структурой-заглушкой. С другой стороны, `close` — это просто псевдоним для настоящего FreeBSD man:close[2], поэтому у него нет связанной структуры аргументов Linux, и в системной таблице вызовов он не имеет префикса linux, так как вызывает настоящий man:close[2] в ядре.
+
+[[dummy-syscalls]]
+==== Нереализованные системные вызовы
+
+Слой эмуляции Linux(R) не является полным, так как некоторые системные вызовы реализованы неправильно, а некоторые не реализованы вовсе. В слое эмуляции используется механизм для пометки нереализованных системных вызовов с помощью макроса `DUMMY`. Эти заглушки находятся в файле [.filename]#linux_dummy.c# в форме `DUMMY(syscall);`, которые затем преобразуются в различные вспомогательные файлы системных вызовов, а их реализация сводится к выводу сообщения о том, что данный системный вызов не реализован. Прототип `UNIMPL` не используется, потому что мы хотим иметь возможность идентифицировать имя вызванного системного вызова, чтобы понимать, какие системные вызовы более важны для реализации.
+
+[[signal-handling]]
+=== Обработка сигналов
+
+Обработка сигналов обычно выполняется в ядре FreeBSD для всех вариантов бинарной совместимости с помощью вызова уровня, зависящего от совместимости. Слой совместимости Linux(R) определяет для этой цели процедуру `linux_sendsig`.
+
+[[linux-sendsig]]
+==== Linux(R) sendsig
+
+Эта процедура сначала проверяет, установлен ли сигнал с флагом `SA_SIGINFO`, в таком случае она вызывает процедуру `linux_rt_sendsig` вместо текущей. Далее она выделяет (или повторно использует уже существующий) контекст обработчика сигнала, затем формирует список аргументов для обработчика сигнала. Она преобразует номер сигнала на основе таблицы преобразования сигналов, назначает обработчик, преобразует sigset. Затем она сохраняет контекст для процедуры `sigreturn` (различные регистры, преобразованный номер trap и маску сигналов). Наконец, она копирует контекст сигнала в пользовательское пространство и подготавливает контекст для фактического выполнения обработчика сигнала.
+
+[[linux-rt-sendsig]]
+==== linux_rt_sendsig
+
+Эта процедура аналогична `linux_sendsig`, только подготовка контекста сигнала отличается. Она добавляет `siginfo`, `ucontext` и некоторые части POSIX(R). Стоит рассмотреть возможность объединения этих двух функций с выгодой в виде меньшего дублирования кода и, возможно, даже более быстрого выполнения.
+
+[[linux-sigreturn]]
+==== linux_sigreturn
+
+Этот системный вызов используется для возврата из обработчика сигнала. Он выполняет некоторые проверки безопасности и восстанавливает исходный контекст процесса. Также он разблокирует сигнал в маске сигналов процесса.
+
+[[ptrace]]
+=== Ptrace
+
+Многие производные UNIX(R) реализуют системный вызов man:ptrace[2] для обеспечения различных функций отслеживания и отладки. Этот механизм позволяет трассирующему процессу получать различную информацию о трассируемом процессе, такую как дампы регистров, любую память из адресного пространства процесса и т.д., а также трассировать процесс, например, пошагово выполнять инструкции или между системными вызовами (сисколлами и ловушками). man:ptrace[2] также позволяет устанавливать различную информацию в трассируемом процессе (регистры и т.д.). man:ptrace[2] является стандартом для UNIX(R), реализованным в большинстве UNIX(R)-систем по всему миру.
+
+Эмуляция Linux(R) в FreeBSD реализует механизм man:ptrace[2] в файле [.filename]#linux_ptrace.c#. Функции для преобразования регистров между Linux(R) и FreeBSD и фактический системный вызов эмуляции man:ptrace[2]. Системный вызов представляет собой длинный блок switch, который реализует свой аналог в FreeBSD для каждой команды man:ptrace[2]. Команды man:ptrace[2] в основном одинаковы между Linux(R) и FreeBSD, поэтому обычно требуется лишь небольшая модификация. Например, `PT_GETREGS` в Linux(R) работает с непосредственными данными, в то время как FreeBSD использует указатель на данные, поэтому после выполнения (нативного) системного вызова man:ptrace[2] необходимо выполнить copyout для сохранения семантики Linux(R).
+
+Реализация man:ptrace[2] в Linuxulator имеет известные недостатки. Наблюдались паники при использовании `strace` (который является потребителем man:ptrace[2]) в среде Linuxulator. Также `PT_SYSCALL` не реализован.
+
+[[traps]]
+=== Ловушки (trap)
+
+Всякий раз, когда процесс Linux(R), выполняющийся в слое эмуляции, вызывает прерывание (trap), само прерывание обрабатывается прозрачно, за исключением преобразования прерывания. Linux(R) и FreeBSD расходятся во мнениях относительно того, что является прерыванием, поэтому этот вопрос решается здесь. Код на самом деле очень короткий:
+
+[.programlisting]
+....
+static int
+translate_traps(int signal, int trap_code)
+{
+
+ if (signal != SIGBUS)
+ return signal;
+
+ switch (trap_code) {
+
+ case T_PROTFLT:
+ case T_TSSFLT:
+ case T_DOUBLEFLT:
+ case T_PAGEFLT:
+ return SIGSEGV;
+
+ default:
+ return signal;
+ }
+}
+....
+
+[[stack-fixup]]
+=== Исправление стека
+
+Динамический редактор связей RTLD ожидает так называемые AUX-теги на стеке во время выполнения `execve`, поэтому необходимо выполнить исправление, чтобы это обеспечить. Конечно, каждая система RTLD отличается, поэтому уровень эмуляции должен предоставлять собственную процедуру исправления стека. Linuxulator делает именно это. Функция `elf_linux_fixup` просто копирует AUX-теги на стек и корректирует стек пользовательского процесса, чтобы он указывал сразу после этих тегов. Таким образом, RTLD работает умным способом.
+
+[[aout-support]]
+=== Поддержка A.OUT
+
+Эмуляционный слой Linux(R) на i386 также поддерживает бинарные файлы Linux(R) в формате A.OUT. Почти всё, что описано в предыдущих разделах, должно быть реализовано для поддержки A.OUT (кроме перевода ловушек и отправки сигналов). Поддержка бинарных файлов A.OUT больше не поддерживается, в частности, эмуляция 2.6 с ними не работает, но это не вызывает никаких проблем, так как linux-base в портах, вероятно, вообще не поддерживает бинарные файлы A.OUT. Эта поддержка, скорее всего, будет удалена в будущем. Большая часть кода, необходимого для загрузки бинарных файлов Linux(R) A.OUT, находится в файле [.filename]#imgact_linux.c#.
+
+[[mi]]
+== Слой эмуляции Linux(R) - машино-независимая часть
+
+В этом разделе рассматривается машинно-независимая часть Linuxulator. Он охватывает инфраструктуру эмуляции, необходимую для эмуляции Linux(R) 2.6, реализацию thread local storage (TLS) (на i386) и фьютексы. Затем мы кратко обсуждаем некоторые системные вызовы.
+
+[[nptl-desc]]
+=== Описание NPTL
+
+Одним из основных направлений прогресса в разработке Linux(R) 2.6 стала поддержка потоков. До версии 2.6 поддержка потоков в Linux(R) реализовывалась в библиотеке linuxthreads. Эта библиотека представляла собой частичную реализацию потоков POSIX(R). Потоки создавались как отдельные процессы с использованием системного вызова `clone`, что позволяло им разделять адресное пространство (и другие ресурсы). Основными недостатками такого подхода были разные PID для каждого потока, некорректная обработка сигналов (с точки зрения pthreads) и т.д. Кроме того, производительность оставляла желать лучшего (использование сигналов `SIGUSR` для синхронизации потоков, потребление ресурсов ядра и т.п.), поэтому для решения этих проблем была разработана новая система потоков под названием NPTL.
+
+Библиотека NPTL была сосредоточена на двух вещах, но появилась третья, поэтому её обычно считают частью NPTL. Этими двумя вещами были встраивание потоков в структуру процесса и фьютекс. Дополнительной третьей вещью стал TLS, который не требуется напрямую NPTL, но вся пользовательская библиотека NPTL зависит от него. Эти улучшения привели к значительному росту производительности и соответствию стандартам. В настоящее время NPTL является стандартной библиотекой потоков в системах Linux(R).
+
+Реализация Linuxulator в FreeBSD подходит к NPTL в трёх основных направлениях: TLS, фьютекс и изменение PID, что предназначено для эмуляции потоков Linux(R). В следующих разделах описывается каждое из этих направлений.
+
+[[linux26-emu]]
+=== Инфраструктура эмуляции Linux(R) 2.6
+
+Эти разделы посвящены тому, как управляются потоки Linux(R) и как мы моделируем это в FreeBSD.
+
+[[linux26-runtime]]
+==== Определение эмуляции 2.6 во время выполнения
+
+Слой эмуляции Linux(R) в FreeBSD поддерживает динамическую настройку эмулируемой версии. Это выполняется с помощью man:sysctl[8], а именно `compat.linux.osrelease`. Установка этого man:sysctl[8] влияет на поведение слоя эмуляции во время выполнения. При установке значения 2.6.x устанавливается переменная `linux_use_linux26`, а при установке другого значения она остаётся сброшенной. Эта переменная (а также аналогичные переменные для каждой клетки) определяет, используется ли в коде инфраструктура 2.6 (в основном, преобразование PID). Настройка версии применяется глобально для всей системы и влияет на все процессы Linux(R). Не следует изменять man:sysctl[8] во время выполнения любого бинарного файла Linux(R), так как это может привести к проблемам.
+
+[[linux-proc-thread]]
+==== Идентификаторы процессов и потоков Linux(R)
+
+Семантика потоков в Linux(R) немного запутанная и использует совершенно другую терминологию по сравнению с FreeBSD. Процесс в Linux(R) состоит из `struct task`, включающей два поля идентификаторов — PID и TGID. PID — это _не_ идентификатор процесса, а идентификатор потока. TGID идентифицирует группу потоков, другими словами, процесс. Для однопоточного процесса PID равен TGID.
+
+Поток в NPTL — это обычный процесс, у которого TGID не равен PID и есть групповой лидер, отличный от него самого (и, конечно, общая виртуальная память и т.д.). Все остальное происходит так же, как и с обычным процессом. Нет разделения общего состояния на внешнюю структуру, как в FreeBSD. Это создает некоторое дублирование информации и возможную несогласованность данных. Ядро Linux(R), похоже, использует информацию о задаче -> группе в одних местах и информацию о задаче в других, что не очень последовательно и выглядит небезопасно с точки зрения возможных ошибок.
+
+Каждый поток NPTL создается вызовом системного вызова `clone` с определенным набором флагов (подробнее в следующем подразделе). NPTL реализует строгую модель потоков 1:1.
+
+В FreeBSD мы эмулируем потоки NPTL с помощью обычных процессов FreeBSD, которые разделяют виртуальную память и т.д., а гимнастика с PID просто имитируется в специфической для эмуляции структуре, прикреплённой к процессу. Структура, прикреплённая к процессу, выглядит следующим образом:
+
+[.programlisting]
+....
+struct linux_emuldata {
+ pid_t pid;
+
+ int *child_set_tid; /* in clone(): Child.s TID to set on clone */
+ int *child_clear_tid;/* in clone(): Child.s TID to clear on exit */
+
+ struct linux_emuldata_shared *shared;
+
+ int pdeath_signal; /* parent death signal */
+
+ LIST_ENTRY(linux_emuldata) threads; /* list of linux threads */
+};
+....
+
+PID используется для идентификации процесса FreeBSD, к которому присоединена эта структура. `child_se_tid` и `child_clear_tid` используются для копирования адреса TID при завершении и создании процесса. Указатель `shared` указывает на структуру, разделяемую между потоками. Переменная `pdeath_signal` определяет сигнал завершения родительского процесса, а указатель `threads` используется для связывания этой структуры со списком потоков. Структура `linux_emuldata_shared` выглядит следующим образом:
+
+[.programlisting]
+....
+struct linux_emuldata_shared {
+
+ int refs;
+
+ pid_t group_pid;
+
+ LIST_HEAD(, linux_emuldata) threads; /* head of list of linux threads */
+};
+....
+
+`refs` — это счётчик ссылок, используемый для определения момента, когда можно освободить структуру, чтобы избежать утечек памяти. `group_pid` служит для идентификации PID (= TGID) всего процесса (= группы потоков). Указатель `threads` является головой списка потоков в процессе.
+
+Структуру `linux_emuldata` можно получить из процесса с помощью `em_find`. Прототип функции выглядит следующим образом:
+
+[.programlisting]
+....
+struct linux_emuldata *em_find(struct proc *, int locked);
+....
+
+Здесь `proc` — это процесс, из которого мы хотим получить структуру `emuldata`, а параметр `locked` определяет, нужно ли блокировать. Допустимые значения — `EMUL_DOLOCK` и `EMUL_DOUNLOCK`. Подробнее о блокировке позже.
+
+[[pid-mangling]]
+==== Преобразование PID
+
+Поскольку между FreeBSD и Linux(R) существуют различия в представлении идентификатора процесса (PID) и идентификатора потока (TID), нам необходимо преобразовывать эти понятия. Это достигается за счёт модификации PID. Это означает, что мы изменяем представление о PID (=TGID) и TID (=PID) между ядром и пользовательским пространством. Основное правило заключается в следующем: в ядре (в Linuxulator) `PID = PID`, а `TGID = shared -> group_pid`; для пользовательского пространства мы представляем `PID = shared -> group_pid` и `TID = proc -> p_pid`. Член `PID` в структуре `linux_emuldata` является FreeBSD PID.
+
+Вышесказанное в основном влияет на системные вызовы getpid, getppid, gettid. В случаях, где мы используем PID/TGID соответственно. При копировании TID в `child_clear_tid` и `child_set_tid` мы копируем FreeBSD PID.
+
+[[clone-syscall]]
+==== Системный вызов clone
+
+`clone` — это системный вызов, с помощью которого создаются потоки в Linux(R). Прототип системного вызова выглядит следующим образом:
+
+[.programlisting]
+....
+int linux_clone(l_int flags, void *stack, void *parent_tidptr, int dummy,
+void * child_tidptr);
+....
+
+Параметр `flags` указывает системному вызову, как именно процессы должны быть клонированы. Как описано выше, Linux(R) может создавать процессы, разделяющие различные ресурсы независимо, например, два процесса могут разделять файловые дескрипторы, но не виртуальную память и т.д. Последний байт параметра `flags` является сигналом завершения для вновь созданного процесса. Параметр `stack`, если он не `NULL`, указывает, где находится стек потока, а если он `NULL`, предполагается копирование при записи стека вызывающего процесса (т.е. делать то же, что делает обычная функция man:fork[2]). Параметр `parent_tidptr` используется как адрес для копирования PID процесса (т.е. идентификатора потока) после того, как процесс достаточно инициализирован, но ещё не готов к выполнению. Параметр `dummy` присутствует из-за очень странного соглашения о вызовах этого системного вызова на i386. Он использует регистры напрямую и не позволяет компилятору делать это, что приводит к необходимости использования фиктивного системного вызова. Параметр `child_tidptr` используется как адрес для копирования PID после завершения ветвления процесса и при его завершении.
+
+Системный вызов продолжает выполнение, устанавливая соответствующие флаги в зависимости от переданных аргументов. Например, `CLONE_VM` преобразуется в RFMEM (общее адресное пространство) и т.д. Единственная тонкость здесь — это `CLONE_FS` и `CLONE_FILES`, поскольку FreeBSD не позволяет устанавливать их отдельно, поэтому мы эмулируем это, не устанавливая RFFDG (копирование таблицы файловых дескрипторов и другой информации о файловой системе), если задан любой из этих флагов. Это не вызывает проблем, так как эти флаги всегда устанавливаются вместе. После установки флагов процесс создаётся с помощью внутренней процедуры `fork1`, при этом процесс настраивается так, чтобы не помещаться в очередь выполнения (т.е. не становиться исполняемым). После завершения ветвления мы, при необходимости, изменяем родителя для нового процесса, чтобы эмулировать семантику `CLONE_PARENT`. Следующий шаг — создание данных эмуляции. Потоки в Linux(R) не отправляют сигналы своим родителям, поэтому мы устанавливаем сигнал завершения в 0, чтобы отключить эту возможность. Затем выполняется настройка `child_set_tid` и `child_clear_tid`, что активирует соответствующую функциональность далее в коде. На этом этапе мы копируем PID по адресу, указанному в `parent_tidptr`. Установка стека процесса выполняется простой перезаписью регистра `%esp` (`%rsp` на amd64) в кадре потока. Далее настраивается TLS для нового процесса. После этого может быть эмулирована семантика man:vfork[2], и, наконец, новый процесс помещается в очередь выполнения, а его PID возвращается родительскому процессу через возвращаемое значение `clone`.
+
+Системный вызов `clone` способен и фактически используется для эмуляции классических системных вызовов man:fork[2] и man:vfork[2]. Более новые версии glibc в случае ядра 2.6 используют `clone` для реализации системных вызовов man:fork[2] и man:vfork[2].
+
+[[locking]]
+==== Блокировка
+
+Блокировка реализована на уровне подсистем, поскольку не ожидается высокой конкуренции за эти ресурсы. Существует две блокировки: `emul_lock`, используемая для защиты манипуляций с `linux_emuldata`, и `emul_shared_lock`, используемая для манипуляций с `linux_emuldata_shared`. `emul_lock` представляет собой неспящий блокирующий мьютекс, в то время как `emul_shared_lock` — это спящий блокирующий `sx_lock`. Благодаря блокировке на уровне подсистем мы можем объединять некоторые блокировки, поэтому em_find предлагает доступ без блокировки.
+
+[[tls]]
+=== TLS
+
+Этот раздел посвящён TLS, также известному как локальное хранилище потока.
+
+[[trheading-intro]]
+==== Введение в многопоточность
+
+В компьютерных науках потоки (threads) — это сущности внутри процесса, которые могут планироваться независимо друг от друга. Потоки в процессе разделяют общие данные процесса (например, файловые дескрипторы), но также имеют свой собственный стек для своих данных. Иногда возникает необходимость в данных, специфичных для конкретного потока, но доступных на уровне процесса. Например, имя выполняемого потока или что-то подобное. Традиционный API для работы с потоками в UNIX® — pthreads — предоставляет способ сделать это через функции `man:pthread_key_create[3]`, `man:pthread_setspecific[3]` и `man:pthread_getspecific[3]`, где поток может создать ключ к локальным данным потока и использовать `man:pthread_getspecific[3]` или `man:pthread_getspecific[3]` для управления этими данными. Легко заметить, что это не самый удобный способ. Поэтому различные разработчики компиляторов C/C++ предложили более удобный метод. Они ввели новое ключевое слово `thread`, которое указывает, что переменная является специфичной для потока. Также был разработан новый метод доступа к таким переменным (по крайней мере, на архитектуре i386). Метод pthreads обычно реализуется в пользовательском пространстве в виде простой таблицы поиска. Производительность такого решения не очень высока. Новый метод использует (на i386) сегментные регистры для адресации области, где хранится TLS (Thread-Local Storage), так что фактический доступ к переменной потока сводится к добавлению сегментного регистра к адресу, таким образом обращаясь через него. Сегментные регистры, обычно `%gs` и `%fs`, действуют как селекторы сегментов. Каждый поток имеет свою собственную область, где хранятся локальные данные потока, и сегмент должен загружаться при каждом переключении контекста. Этот метод очень быстрый и используется практически повсеместно в мире UNIX® на архитектуре i386. И FreeBSD, и Linux® реализуют этот подход, и он даёт очень хорошие результаты. Единственный недостаток — необходимость перезагружать сегмент при каждом переключении контекста, что может замедлять переключения. FreeBSD пытается минимизировать эти накладные расходы, используя только 1 дескриптор сегмента, в то время как Linux® использует 3. Интересно, что почти ничто не использует больше 1 дескриптора (только Wine, кажется, использует 2), поэтому Linux® платит эту необязательную цену при переключении контекстов.
+
+[[i386-segs]]
+==== Сегменты на i386
+
+Архитектура i386 реализует так называемые сегменты. Сегмент — это описание области памяти. Он включает базовый адрес (начало) области памяти, её конец (границу), тип, защиту и т.д. Доступ к памяти, описываемой сегментом, может осуществляться с использованием регистров селекторов сегментов (`%cs`, `%ds`, `%ss`, `%es`, `%fs`, `%gs`). Например, предположим, что у нас есть сегмент с базовым адресом 0x1234 и длиной, а также следующий код:
+
+[.programlisting]
+....
+mov %edx,%gs:0x10
+....
+
+Это загрузит содержимое регистра `%edx` в ячейку памяти по адресу 0x1244. Некоторые сегментные регистры имеют специальное назначение, например, `%cs` используется для сегмента кода, а `%ss` — для сегмента стека, но `%fs` и `%gs` обычно не используются. Сегменты хранятся либо в глобальной таблице GDT, либо в локальной таблице LDT. Доступ к LDT осуществляется через запись в GDT. LDT может хранить больше типов сегментов. LDT может быть отдельной для каждого процесса. Обе таблицы определяют до 8191 записей.
+
+[[linux-i386]]
+==== Реализация на Linux(R) i386
+
+Существует два основных способа настройки TLS в Linux(R). Он может быть настроен при клонировании процесса с использованием системного вызова `clone` или с помощью вызова `set_thread_area`. Когда процесс передает флаг `CLONE_SETTLS` в `clone`, ядро ожидает, что память, на которую указывает регистр `%esi`, будет содержать пользовательское представление сегмента в Linux(R), которое преобразуется в машинное представление сегмента и загружается в слот GDT. Слот GDT может быть указан номером или можно использовать -1, что означает, что система сама должна выбрать первый свободный слот. На практике подавляющее большинство программ используют только одну запись TLS и не заботятся о номере записи. Мы используем это в эмуляции и фактически зависим от этого.
+
+[[tls-emu]]
+==== Эмуляция Linux(R) TLS
+
+[[tls-i386]]
+===== i386
+
+Загрузка TLS для текущего потока происходит путем вызова `set_thread_area`, тогда как загрузка TLS для второго процесса в `clone` выполняется в отдельном блоке в `clone`. Эти две функции очень похожи. Единственное различие заключается в фактической загрузке сегмента GDT, которая происходит при следующем переключении контекста для вновь созданного процесса, в то время как `set_thread_area` должен загрузить его напрямую. Код в основном делает следующее. Он копирует дескриптор сегмента в формате Linux(R) из пользовательского пространства. Код проверяет номер дескриптора, но поскольку он различается между FreeBSD и Linux(R), мы немного имитируем его. Мы поддерживаем только индексы 6, 3 и -1. Число 6 — это оригинальный номер Linux(R), 3 — оригинальный номер FreeBSD, а -1 означает авто-выбор. Затем мы устанавливаем номер дескриптора на константу 3 и копируем его обратно в пользовательское пространство. Мы полагаемся на то, что процесс в пользовательском пространстве использует номер из дескриптора, но это работает в большинстве случаев (никогда не встречалось ситуации, когда это не срабатывало), так как процесс в пользовательском пространстве обычно передает 1. Затем мы преобразуем дескриптор из формата Linux(R) в машинно-зависимую форму (т.е. независимую от операционной системы) и копируем его в дескриптор сегмента, определенный FreeBSD. Наконец, мы можем загрузить его. Мы назначаем дескриптор PCB потока (блок управления процессом) и загружаем сегмент `%gs` с помощью `load_gs`. Эта загрузка должна выполняться в критической секции, чтобы ничто не могло нас прервать. Случай `CLONE_SETTLS` работает точно так же, только загрузка с помощью `load_gs` не выполняется. Сегмент, используемый для этого (сегмент номер 3), разделяется между процессами FreeBSD и Linux(R), поэтому слой эмуляции Linux(R) не добавляет накладных расходов по сравнению с обычным FreeBSD.
+
+[[tls-amd64]]
+===== amd64
+
+Реализация amd64 аналогична реализации i386, но изначально не использовался 32-битный дескриптор сегмента для этой цели (поэтому даже нативные пользователи 32-битного TLS не работали), поэтому нам пришлось добавить такой сегмент и реализовать его загрузку при каждом переключении контекста (когда установлен флаг, сигнализирующий о использовании 32-битного режима). Кроме этого, загрузка TLS точно такая же, только номера сегментов отличаются, а формат дескриптора и загрузка немного различаются.
+
+[[futexes]]
+=== Фьютексы
+
+[[sync-intro]]
+==== Введение в синхронизацию
+
+Потокам требуется некоторая синхронизация, и POSIX(R) предоставляет несколько её видов: мьютексы для взаимного исключения, блокировки чтения-записи для взаимного исключения с преобладанием операций чтения над записями и условные переменные для сигнализации об изменении состояния. Интересно отметить, что в API потоков POSIX(R) отсутствует поддержка семафоров. Реализации этих механизмов синхронизации сильно зависят от типа поддержки потоков, которая у нас есть. В чистой модели 1:M (пользовательское пространство) реализация может быть выполнена исключительно в пользовательском пространстве и, следовательно, быть очень быстрой (условные переменные, вероятно, будут реализованы с использованием сигналов, т.е. не быстро) и простой. В модели 1:1 ситуация также довольно ясна — потоки должны синхронизироваться с использованием средств ядра (что очень медленно, поскольку требуется выполнение системного вызова). Смешанный сценарий M:N просто комбинирует первый и второй подходы или полагается исключительно на ядро. Синхронизация потоков является важной частью программирования с использованием потоков, и её производительность может значительно влиять на итоговую программу. Недавние тесты в операционной системе FreeBSD показали, что улучшенная реализация `sx_lock` дала 40% прироста скорости в _ZFS_ (где активно используются блокировки sx), это внутренние механизмы ядра, но это наглядно демонстрирует, насколько важна производительность примитивов синхронизации.
+
+Многопоточные программы должны быть написаны с минимальной конкуренцией за блокировки. В противном случае, вместо выполнения полезной работы поток просто ожидает блокировку. В результате, наиболее хорошо написанные многопоточные программы демонстрируют низкую конкуренцию за блокировки.
+
+[[futex-intro]]
+==== Введение в фьютексы
+
+Linux(R) реализует 1:1 потоковую модель, то есть использует примитивы синхронизации в ядре. Как упоминалось ранее, хорошо написанные многопоточные программы имеют низкую конкуренцию за блокировки. Таким образом, типичная последовательность может выполняться как два атомарных увеличения/уменьшения счётчика ссылок мьютекса, что очень быстро, как показано в следующем примере:
+
+[.programlisting]
+....
+pthread_mutex_lock(&mutex);
+...
+pthread_mutex_unlock(&mutex);
+....
+
+1:1 threading вынуждает нас выполнять два системных вызова для этих вызовов мьютекса, что очень медленно.
+
+Решение, реализованное в Linux(R) 2.6, называется фьютексы. Фьютексы выполняют проверку на конкуренцию в пользовательском пространстве и вызывают примитивы ядра только в случае конкуренции. Таким образом, типичный случай обходится без вмешательства ядра. Это обеспечивает достаточно быструю и гибкую реализацию примитивов синхронизации.
+
+[[futex-api]]
+==== API фьютексов
+
+Системный вызов futex выглядит следующим образом:
+
+[.programlisting]
+....
+int futex(void *uaddr, int op, int val, struct timespec *timeout, void *uaddr2, int val3);
+....
+
+В этом примере `uaddr` — это адрес мьютекса в пользовательском пространстве, `op` — операция, которую мы собираемся выполнить, а остальные параметры имеют значение, зависящее от конкретной операции.
+
+Фьютексы реализуют следующие операции:
+
+* `FUTEX_WAIT`
+* `FUTEX_WAKE`
+* `FUTEX_FD`
+* `FUTEX_REQUEUE`
+* `FUTEX_CMP_REQUEUE`
+* `FUTEX_WAKE_OP`
+
+[[futex-wait]]
+===== FUTEX_WAIT
+
+Эта операция проверяет, что по адресу `uaddr` записано значение `val`. Если нет, возвращается `EWOULDBLOCK`, в противном случае поток ставится в очередь на фьютекс и приостанавливается. Если аргумент `timeout` не равен нулю, он задает максимальное время ожидания, в противном случае ожидание бесконечно.
+
+[[futex-wake]]
+===== FUTEX_WAKE
+
+Эта операция захватывает фьютекс по адресу `uaddr` и пробуждает первые `val` фьютексов, ожидающих в очереди на этом фьютексе.
+
+[[futex-fd]]
+===== FUTEX_FD
+
+Эта операция связывает файловый дескриптор с заданным фьютексом.
+
+[[futex-requeue]]
+===== FUTEX_REQUEUE
+
+Эта операция берет `val` потоков, ожидающих на фьютексе по адресу `uaddr`, пробуждает их и берет следующие `val2` потоков, перемещая их в очередь фьютекса по адресу `uaddr2`.
+
+[[futex-cmp-requeue]]
+===== FUTEX_CMP_REQUEUE
+
+Эта операция делает то же самое, что и `FUTEX_REQUEUE`, но сначала проверяет, что `val3` равно `val`.
+
+[[futex-wake-op]]
+===== FUTEX_WAKE_OP
+
+Эта операция выполняет атомарную операцию над `val3` (которая содержит закодированное другое значение) и `uaddr`. Затем она пробуждает `val` потоков на фьютексе по адресу `uaddr`, и если атомарная операция вернула положительное число, пробуждает `val2` потоков на фьютексе по адресу `uaddr2`.
+
+Операции, реализованные в `FUTEX_WAKE_OP`:
+
+* `FUTEX_OP_SET`
+* `FUTEX_OP_ADD`
+* `FUTEX_OP_OR`
+* `FUTEX_OP_AND`
+* `FUTEX_OP_XOR`
+
+[NOTE]
+====
+В прототипе системного вызова futex отсутствует параметр `val2`. Значение `val2` берётся из параметра `struct timespec *timeout` для операций `FUTEX_REQUEUE`, `FUTEX_CMP_REQUEUE` и `FUTEX_WAKE_OP`.
+====
+
+[[futex-emu]]
+==== Эмуляция фьютексов в FreeBSD
+
+Эмуляция futex в FreeBSD взята из NetBSD и дополнительно расширена нами. Она размещена в файлах `linux_futex.c` и [.filename]#linux_futex.h#. Структура `futex` выглядит следующим образом:
+
+[.programlisting]
+....
+struct futex {
+ void *f_uaddr;
+ int f_refcount;
+
+ LIST_ENTRY(futex) f_list;
+
+ TAILQ_HEAD(lf_waiting_paroc, waiting_proc) f_waiting_proc;
+};
+....
+
+И структура `waiting_proc` выглядит следующим образом:
+
+[.programlisting]
+....
+struct waiting_proc {
+
+ struct thread *wp_t;
+
+ struct futex *wp_new_futex;
+
+ TAILQ_ENTRY(waiting_proc) wp_list;
+};
+....
+
+[[futex-get]]
+===== futex_get / futex_put
+
+Фьютекс получается с помощью функции `futex_get`, которая выполняет поиск в линейном списке фьютексов и возвращает найденный или создает новый. При освобождении фьютекса после использования вызывается функция `futex_put`, которая уменьшает счетчик ссылок фьютекса, и если счетчик достигает нуля, фьютекс освобождается.
+
+[[futex-sleep]]
+===== futex_sleep
+
+Когда фьютекс ставит поток в очередь на ожидание, он создает структуру `working_proc` и помещает эту структуру в список внутри структуры futex, после чего просто выполняет man:tsleep[9] для приостановки потока. Ожидание может быть ограничено по времени. После возврата из man:tsleep[9] (поток был разбужен или истекло время ожидания) структура `working_proc` удаляется из списка и уничтожается. Все это выполняется в функции `futex_sleep`. Если мы были разбужены с помощью `futex_wake`, у нас установлен `wp_new_futex`, поэтому мы ожидаем на нем. Таким образом, фактическое перемещение выполняется в этой функции.
+
+[[futex-wake-2]]
+===== futex_wake
+
+Пробуждение потока, ожидающего на фьютексе, выполняется в функции `futex_wake`. Сначала в этой функции мы имитируем странное поведение Linux(R), где пробуждаются N потоков для всех операций, за исключением того, что операции REQUEUE выполняются на N+1 потоках. Однако обычно это не имеет значения, так как мы пробуждаем все потоки. Далее в функции в цикле мы пробуждаем n потоков, после чего проверяем, есть ли новый фьютекс для перестановки. Если есть, мы переставляем до n2 потоков на новый futex. Это взаимодействует с `futex_sleep`.
+
+[[futex-wake-op-2]]
+===== futex_wake_op
+
+Операция `FUTEX_WAKE_OP` довольно сложна. Сначала мы получаем два фьютекса по адресам `uaddr` и `uaddr2`, затем выполняем атомарную операцию с использованием `val3` и `uaddr2`. После этого пробуждаются `val` ожидающих на первом фьютексе, и если условие атомарной операции выполняется, мы пробуждаем `val2` (т.е. `timeout`) ожидающих на втором фьютексе.
+
+[[futex-atomic-op]]
+===== Атомарная операция на фьютексе
+
+Атомарная операция принимает два параметра `encoded_op` и `uaddr`. Закодированная операция включает саму операцию, сравниваемое значение, аргумент операции и аргумент сравнения. Псевдокод операции выглядит следующим образом:
+
+[.programlisting]
+....
+oldval = *uaddr2
+*uaddr2 = oldval OP oparg
+....
+
+И это выполняется атомарно. Сначала происходит копирование числа по адресу `uaddr`, а затем выполняется операция. Код обрабатывает ошибки страниц, и если ошибки не происходит, `oldval` сравнивается с аргументом `cmparg` с помощью компаратора cmp.
+
+[[futex-locking]]
+===== Блокировка фьютекса
+
+Реализация фьютексов использует два списка блокировок для защиты `sx_lock` и глобальных блокировок (либо Giant, либо другой `sx_lock`). Каждая операция выполняется заблокированной от начала до самого конца.
+
+[[syscall-impl]]
+=== Реализация различных системных вызовов
+
+В этом разделе я опишу несколько менее значимых системных вызовов, которые стоит упомянуть, потому что их реализация неочевидна или эти вызовы представляют интерес с другой точки зрения.
+
+[[syscall-at]]
+==== *семейство системных вызовов at
+
+Во время разработки ядра Linux(R) 2.6.16 были добавлены *at-системные вызовы. Эти системные вызовы (например, `openat`) работают точно так же, как их аналоги без at, за исключением параметра `dirfd`. Этот параметр определяет местоположение файла, над которым выполняется системный вызов. Если параметр `filename` является абсолютным, `dirfd` игнорируется, но если путь к файлу относительный, `dirfd` вступает в игру. Параметр `dirfd` представляет собой каталог, относительно которого проверяется относительный путь. Параметр `dirfd` является файловым дескриптором некоторого каталога или `AT_FDCWD`. Например, системный вызов `openat` может выглядеть следующим образом:
+
+[.programlisting]
+....
+file descriptor 123 = /tmp/foo/, current working directory = /tmp/
+
+openat(123, /tmp/bah\, flags, mode) /* opens /tmp/bah */
+openat(123, bah\, flags, mode) /* opens /tmp/foo/bah */
+openat(AT_FDWCWD, bah\, flags, mode) /* opens /tmp/bah */
+openat(stdio, bah\, flags, mode) /* returns error because stdio is not a directory */
+....
+
+Эта инфраструктура необходима для избежания состояний гонки при открытии файлов вне рабочего каталога. Представьте, что процесс состоит из двух потоков, потока A и потока B. Поток A выполняет `open(./tmp/foo/bah., flags, mode)`, и перед возвратом управления он вытесняется, и начинает выполняться поток B. Поток B не учитывает потребности потока A и переименовывает или удаляет [.filename]#/tmp/foo/#. Возникает состояние гонки. Чтобы избежать этого, мы можем открыть [.filename]#/tmp/foo# и использовать его как `dirfd` для системного вызова `openat`. Это также позволяет пользователю реализовать рабочие каталоги для каждого потока.
+
+Семейство *at системных вызовов Linux(R) включает: `linux_openat`, `linux_mkdirat`, `linux_mknodat`, `linux_fchownat`, `linux_futimesat`, `linux_fstatat64`, `linux_unlinkat`, `linux_renameat`, `linux_linkat`, `linux_symlinkat`, `linux_readlinkat`, `linux_fchmodat` и `linux_faccessat`. Все они реализованы с использованием модифицированной функции man:namei[9] и простого слоя обёртки.
+
+[[implementation]]
+===== Реализация
+
+Реализация выполнена путем изменения функции man:namei[9] (описанной выше) для приема дополнительного параметра `dirfd` в структуре `nameidata`, который указывает начальную точку для поиска пути вместо использования текущей рабочей директории каждый раз. Преобразование `dirfd` из номера файлового дескриптора в vnode выполняется в нативных *at-системных вызовах. Когда `dirfd` равен `AT_FDCWD`, запись `dvp` в структуре `nameidata` имеет значение `NULL`, но если `dirfd` представляет другой номер, мы получаем файл по этому дескриптору, проверяем его валидность и, если к нему прикреплен vnode, получаем этот vnode. Затем проверяем, является ли этот vnode директорией. В самой функции man:namei[9] мы просто заменяем vnode `dvp` на переменную `dp` в функции man:namei[9], которая определяет начальную точку. Функция man:namei[9] используется не напрямую, а через цепочку различных функций на разных уровнях. Например, `openat` работает следующим образом:
+
+[.programlisting]
+....
+openat() --> kern_openat() --> vn_open() -> namei()
+....
+
+По этой причине `kern_open` и `vn_open` должны быть изменены для включения дополнительного параметра `dirfd`. Слой совместимости для них не создаётся, так как пользователей этих функций немного и их можно легко адаптировать. Данная общая реализация позволяет FreeBSD реализовать свои собственные *at-системные вызовы. Это обсуждается в настоящее время.
+
+[[ioctl]]
+==== Ioctl
+
+Интерфейс ioctl довольно хрупок из-за своей обобщённости. Необходимо учитывать, что устройства в Linux(R) и FreeBSD различаются, поэтому требуется особая осторожность для корректной работы эмуляции ioctl. Обработка ioctl реализована в файле [.filename]#linux_ioctl.c#, где определена функция `linux_ioctl`. Эта функция просто перебирает наборы обработчиков ioctl, чтобы найти обработчик, реализующий данную команду. Системный вызов ioctl имеет три параметра: файловый дескриптор, команду и аргумент. Команда представляет собой 16-битное число, которое теоретически делится на старшие 8 бит, определяющие класс команды ioctl, и младшие 8 бит, которые являются конкретной командой в данном наборе. Эмуляция использует это разделение. Реализованы обработчики для каждого набора, такие как `sound_handler` или `disk_handler`. Каждый обработчик имеет определённые максимальную и минимальную команды, которые используются для выбора нужного обработчика. Существуют небольшие проблемы с этим подходом, поскольку Linux(R) не всегда последовательно использует разделение на наборы, поэтому иногда ioctls для другого набора оказываются внутри набора, к которому они не должны принадлежать (например, SCSI generic ioctls внутри набора cdrom и т.д.). В настоящее время FreeBSD реализует не так много ioctls Linux(R) (по сравнению с NetBSD, например), но планируется перенести их из NetBSD. Тенденция такова, что ioctls Linux(R) используются даже в родных драйверах FreeBSD для упрощения портирования приложений.
+
+[[debugging]]
+==== Отладка
+
+Каждый системный вызов должен поддерживать отладку. Для этой цели мы вводим небольшую инфраструктуру. У нас есть механизм `ldebug`, который определяет, нужно ли отлаживать данный системный вызов (настраивается через `sysctl`). Для вывода сообщений используются макросы `LMSG` и `ARGS`. Они применяются для форматирования строк вывода с целью создания единообразных отладочных сообщений.
+
+[[conclusion]]
+== Заключение
+
+[[results]]
+=== Результаты
+
+По состоянию на апрель 2007 года уровень эмуляции Linux(R) способен достаточно хорошо эмулировать ядро Linux(R) 2.6.16. Оставшиеся проблемы касаются фьютексов, незавершённого семейства системных вызовов *at, проблематичной доставки сигналов, отсутствия `epoll` и `inotify`, а также, вероятно, некоторых ошибок, которые мы ещё не обнаружили. Несмотря на это, мы способны запускать практически все программы Linux(R), включённые в Коллекцию портов FreeBSD, с Fedora Core 4 на ядре 2.6.16, а также есть некоторые предварительные сообщения об успешной работе с Fedora Core 6 на ядре 2.6.16. Недавно был добавлен linux_base Fedora Core 6, что позволило провести дополнительные тестирования уровня эмуляции и дало нам больше подсказок, куда следует направить усилия для реализации недостающих функций.
+
+Мы можем запускать наиболее популярные приложения, такие как package:www/linux-firefox[], package:net-im/skype[], и некоторые игры из Коллекции портов. Некоторые программы демонстрируют некорректное поведение при эмуляции 2.6, но это в настоящее время исследуется, и, надеемся, скоро будет исправлено. Единственное крупное приложение, которое, как известно, не работает, — это Linux(R) Java(TM) Development Kit, и это связано с требованием функции `epoll`, которая не имеет прямого отношения к ядру Linux(R) 2.6.
+
+Мы надеемся включить эмуляцию 2.6.16 по умолчанию через некоторое время после выхода FreeBSD 7.0, по крайней мере, чтобы открыть части эмуляции 2.6 для более широкого тестирования. Как только это будет сделано, мы сможем перейти на Fedora Core 6 linux_base, что является конечной целью.
+
+[[future-work]]
+=== Будущие работы
+
+Будущая работа должна быть сосредоточена на исправлении оставшихся проблем с фьютексами, реализации оставшихся системных вызовов семейства *at, исправлении доставки сигналов и, возможно, реализации механизмов `epoll` и `inotify`.
+
+Мы надеемся вскоре добиться безупречной работы наиболее важных программ, чтобы можно было по умолчанию переключиться на эмуляцию 2.6 и сделать Fedora Core 6 базовой версией linux_base, поскольку используемая в настоящее время Fedora Core 4 больше не поддерживается.
+
+Другая возможная цель — поделиться нашим кодом с NetBSD и DragonflyBSD. NetBSD имеет некоторую поддержку эмуляции 2.6, но она далека от завершения и не была должным образом протестирована. DragonflyBSD выразила некоторую заинтересованность в переносе улучшений версии 2.6.
+
+В целом, по мере развития Linux(R) мы хотели бы идти в ногу с их разработкой, реализуя новые системные вызовы. В первую очередь на ум приходит `splice`. Некоторые уже реализованные системные вызовы также неоптимальны, например `mremap` и другие. Также можно внести некоторые улучшения производительности, такие как более детальная блокировка и другие.
+
+[[team]]
+=== Команда
+
+Я сотрудничал в этом проекте с (в алфавитном порядке):
+
+* `{jhb}`
+* `{kib}`
+* Emmanuel Dreyfus
+* Scot Hetzel
+* `{jkim}`
+* `{netchild}`
+* `{ssouhlal}`
+* Li Xiao
+* `{davidxu}`
+
+Я хотел бы поблагодарить всех этих людей за их советы, рецензирование кода и общую поддержку.
+
+[[literatures]]
+== Литература
+
+. Marshall Kirk McKusick - George V. Neville-Neil. Design and Implementation of the FreeBSD operating system. Addison-Wesley, 2005 год.
+. https://tldp.org[https://tldp.org]
+. https://www.kernel.org[https://www.kernel.org]
diff --git a/documentation/content/ru/articles/linux-emulation/_index.po b/documentation/content/ru/articles/linux-emulation/_index.po
new file mode 100644
index 0000000000..0296103bc0
--- /dev/null
+++ b/documentation/content/ru/articles/linux-emulation/_index.po
@@ -0,0 +1,4415 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-13 23:41+0300\n"
+"PO-Revision-Date: 2025-10-03 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/articleslinux-emulation_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1
+#, no-wrap
+msgid "A technical description about the internals of the Linux emulation layer in FreeBSD"
+msgstr "Техническое описание внутреннего устройства слоя эмуляции Linux в FreeBSD"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1
+#, no-wrap
+msgid "Linux® emulation in FreeBSD"
+msgstr "Эмуляция Linux® в FreeBSD"
+
+#. type: Title =
+#: documentation/content/en/articles/linux-emulation/_index.adoc:11
+#, no-wrap
+msgid "Linux(R) emulation in FreeBSD"
+msgstr "Эмуляция Linux(R) в FreeBSD"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:44
+msgid "Abstract"
+msgstr "Аннотация"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:54
+msgid ""
+"This masters thesis deals with updating the Linux(R) emulation layer (the so "
+"called _Linuxulator_). The task was to update the layer to match the "
+"functionality of Linux(R) 2.6. As a reference implementation, the Linux(R) "
+"2.6.16 kernel was chosen. The concept is loosely based on the NetBSD "
+"implementation. Most of the work was done in the summer of 2006 as a part "
+"of the Google Summer of Code students program. The focus was on bringing "
+"the _NPTL_ (new POSIX(R) thread library) support into the emulation layer, "
+"including _TLS_ (thread local storage), _futexes_ (fast user space mutexes), "
+"_PID mangling_, and some other minor things. Many small problems were "
+"identified and fixed in the process. My work was integrated into the main "
+"FreeBSD source repository and will be shipped in the upcoming 7.0R release. "
+"We, the emulation development team, are working on making the Linux(R) 2.6 "
+"emulation the default emulation layer in FreeBSD."
+msgstr ""
+"Эта магистерская диссертация посвящена обновлению слоя эмуляции Linux(R) "
+"(так называемого _Linuxulator_). Задача состояла в обновлении слоя для "
+"соответствия функциональности Linux(R) 2.6. В качестве эталонной реализации "
+"было выбрано ядро Linux(R) 2.6.16. Концепция основана на реализации NetBSD. "
+"Большая часть работы была выполнена летом 2006 года в рамках программы "
+"Google Summer of Code для студентов. Основное внимание уделялось добавлению "
+"поддержки _NPTL_ (новой библиотеки потоков POSIX(R)) в слой эмуляции, "
+"включая _TLS_ (локальное хранилище потоков), _фьютексы (futex)_ (быстрые "
+"мьютексы в пользовательском пространстве), _PID mangling_ и некоторые другие "
+"второстепенные аспекты. В процессе было выявлено и исправлено множество "
+"мелких проблем. Моя работа была интегрирована в основной репозиторий "
+"исходного кода FreeBSD и войдет в предстоящий релиз 7.0R. Мы, команда "
+"разработчиков эмуляции, работаем над тем, чтобы сделать эмуляцию Linux(R) "
+"2.6 стандартным слоем эмуляции в FreeBSD."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:56
+msgid "'''"
+msgstr "'''"
+
+#. type: Title ==
+#: documentation/content/en/articles/linux-emulation/_index.adoc:60
+#, no-wrap
+msgid "Introduction"
+msgstr "Введение"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:67
+msgid ""
+"In the last few years the open source UNIX(R) based operating systems "
+"started to be widely deployed on server and client machines. Among these "
+"operating systems I would like to point out two: FreeBSD, for its BSD "
+"heritage, time proven code base and many interesting features and Linux(R) "
+"for its wide user base, enthusiastic open developer community and support "
+"from large companies. FreeBSD tends to be used on server class machines "
+"serving heavy duty networking tasks with less usage on desktop class "
+"machines for ordinary users. While Linux(R) has the same usage on servers, "
+"but it is used much more by home based users. This leads to a situation "
+"where there are many binary only programs available for Linux(R) that lack "
+"support for FreeBSD."
+msgstr ""
+"В последние несколько лет операционные системы с открытым исходным кодом на "
+"основе UNIX(R) начали широко использоваться на серверных и клиентских "
+"машинах. Среди этих операционных систем я хотел бы выделить две: FreeBSD — "
+"за наследие BSD, проверенную временем кодобазу и множество интересных "
+"возможностей, и Linux(R) — за широкую пользовательскую базу, активное "
+"сообщество разработчиков и поддержку крупных компаний. FreeBSD чаще "
+"используется на серверных машинах, выполняющих сложные сетевые задачи, и "
+"реже — на настольных компьютерах обычных пользователей. В то время как "
+"Linux(R) также применяется на серверах, он гораздо популярнее среди домашних "
+"пользователей. Это приводит к ситуации, когда для Linux(R) доступно "
+"множество проприетарных программ, которые не поддерживают FreeBSD."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:69
+msgid ""
+"Naturally, a need for the ability to run Linux(R) binaries on a FreeBSD "
+"system arises and this is what this thesis deals with: the emulation of the "
+"Linux(R) kernel in the FreeBSD operating system."
+msgstr ""
+"Естественно, возникает необходимость в возможности запуска Linux(R) "
+"бинарников в системе FreeBSD, и именно этому посвящена данная работа: "
+"эмуляции ядра Linux(R) в операционной системе FreeBSD."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:72
+msgid ""
+"During the Summer of 2006 Google Inc. sponsored a project which focused on "
+"extending the Linux(R) emulation layer (the so called Linuxulator) in "
+"FreeBSD to include Linux(R) 2.6 facilities. This thesis is written as a "
+"part of this project."
+msgstr ""
+"Летом 2006 года компания Google Inc. спонсировала проект, направленный на "
+"расширение слоя эмуляции Linux(R) (так называемого Linuxulator) в FreeBSD "
+"для включения возможностей Linux(R) 2.6. Данная диссертация написана в "
+"рамках этого проекта."
+
+#. type: Title ==
+#: documentation/content/en/articles/linux-emulation/_index.adoc:74
+#, no-wrap
+msgid "A look inside..."
+msgstr "Взгляд изнутри..."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:80
+msgid ""
+"In this section we are going to describe every operating system in "
+"question. How they deal with syscalls, trapframes etc., all the low-level "
+"stuff. We also describe the way they understand common UNIX(R) primitives "
+"like what a PID is, what a thread is, etc. In the third subsection we talk "
+"about how UNIX(R) on UNIX(R) emulation could be done in general."
+msgstr ""
+"В этом разделе мы рассмотрим каждую из рассматриваемых операционных систем. "
+"Как они работают с системными вызовами, фреймами прерываний и другими "
+"низкоуровневыми аспектами. Также мы опишем, как они интерпретируют общие "
+"примитивы UNIX(R), такие как PID, потоки и т. д. В третьем подразделе мы "
+"поговорим о том, как в целом может быть реализована эмуляция UNIX(R) на "
+"UNIX(R)."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:82
+#, no-wrap
+msgid "What is UNIX(R)"
+msgstr "Что такое UNIX(R)"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:94
+msgid ""
+"UNIX(R) is an operating system with a long history that has influenced "
+"almost every other operating system currently in use. Starting in the "
+"1960s, its development continues to this day (although in different "
+"projects). UNIX(R) development soon forked into two main ways: the BSDs and "
+"System III/V families. They mutually influenced themselves by growing a "
+"common UNIX(R) standard. Among the contributions originated in BSD we can "
+"name virtual memory, TCP/IP networking, FFS, and many others. The System V "
+"branch contributed to SysV interprocess communication primitives, copy-on-"
+"write, etc. UNIX(R) itself does not exist any more but its ideas have been "
+"used by many other operating systems world wide thus forming the so called "
+"UNIX(R)-like operating systems. These days the most influential ones are "
+"Linux(R), Solaris, and possibly (to some extent) FreeBSD. There are in-"
+"company UNIX(R) derivatives (AIX, HP-UX etc.), but these have been more and "
+"more migrated to the aforementioned systems. Let us summarize typical "
+"UNIX(R) characteristics."
+msgstr ""
+"UNIX(R) — это операционная система с долгой историей, которая повлияла почти "
+"на все остальные операционные системы, используемые в настоящее время. "
+"Начиная с 1960-х годов, её разработка продолжается и по сей день (хотя в "
+"разных проектах). Вскоре развитие UNIX(R) разделилось на два основных "
+"направления: семейства BSD и System III/V. Они взаимно влияли друг на друга, "
+"формируя общий стандарт UNIX(R). Среди вклада, возникшего в BSD, можно "
+"назвать виртуальную память, сетевой стек TCP/IP, FFS и многие другие. Ветка "
+"System V внесла свой вклад в примитивы межпроцессного взаимодействия SysV, "
+"копирование при записи и т. д. Самого UNIX(R) больше не существует, но его "
+"идеи были использованы многими другими операционными системами по всему "
+"миру, образовав так называемые UNIX(R)-подобные операционные системы. В наши "
+"дни наиболее влиятельными из них являются Linux(R), Solaris и, возможно (в "
+"некоторой степени), FreeBSD. Существуют корпоративные производные UNIX(R) "
+"(AIX, HP-UX и т. д.), но они всё больше мигрируют на упомянутые системы. "
+"Давайте подведём итог типичным характеристикам UNIX(R)."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:96
+#: documentation/content/en/articles/linux-emulation/_index.adoc:187
+#: documentation/content/en/articles/linux-emulation/_index.adoc:279
+#, no-wrap
+msgid "Technical details"
+msgstr "Технические детали"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:104
+msgid ""
+"Every running program constitutes a process that represents a state of the "
+"computation. Running process is divided between kernel-space and user-"
+"space. Some operations can be done only from kernel space (dealing with "
+"hardware etc.), but the process should spend most of its lifetime in the "
+"user space. The kernel is where the management of the processes, hardware, "
+"and low-level details take place. The kernel provides a standard unified "
+"UNIX(R) API to the user space. The most important ones are covered below."
+msgstr ""
+"Каждая запущенная программа представляет собой процесс, который отражает "
+"состояние вычислений. Выполняющийся процесс разделяется между пространством "
+"ядра и пользовательским пространством. Некоторые операции могут выполняться "
+"только из пространства ядра (например, работа с оборудованием), но процесс "
+"должен проводить большую часть своего времени в пользовательском "
+"пространстве. Ядро — это место, где происходит управление процессами, "
+"оборудованием и низкоуровневыми деталями. Ядро предоставляет стандартный "
+"унифицированный UNIX(R) API для пользовательского пространства. Наиболее "
+"важные из них рассмотрены ниже."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:106
+#, no-wrap
+msgid "Communication between kernel and user space process"
+msgstr "Обмен данными между ядром и пользовательским процессом"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:114
+msgid ""
+"Common UNIX(R) API defines a syscall as a way to issue commands from a user "
+"space process to the kernel. The most common implementation is either by "
+"using an interrupt or specialized instruction (think of `SYSENTER`/`SYSCALL` "
+"instructions for ia32). Syscalls are defined by a number. For example in "
+"FreeBSD, the syscall number 85 is the man:swapon[2] syscall and the syscall "
+"number 132 is man:mkfifo[2]. Some syscalls need parameters, which are "
+"passed from the user-space to the kernel-space in various ways "
+"(implementation dependent). Syscalls are synchronous."
+msgstr ""
+"Общий API UNIX(R) определяет системный вызов как способ передачи команд из "
+"пользовательского процесса ядру. Наиболее распространённая реализация "
+"использует либо прерывание, либо специализированную инструкцию (например, "
+"инструкции `SYSENTER`/`SYSCALL` для ia32). Системные вызовы определяются по "
+"номеру. Например, в FreeBSD системный вызов номер 85 — это man:swapon[2], а "
+"номер 132 — man:mkfifo[2]. Некоторые системные вызовы требуют параметров, "
+"которые передаются из пользовательского пространства в пространство ядра "
+"различными способами (зависит от реализации). Системные вызовы являются "
+"синхронными."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:118
+msgid ""
+"Another possible way to communicate is by using a _trap_. Traps occur "
+"asynchronously after some event occurs (division by zero, page fault etc.). "
+"A trap can be transparent for a process (page fault) or can result in a "
+"reaction like sending a _signal_ (division by zero)."
+msgstr ""
+"Еще один возможный способ взаимодействия — использование _прерывания_. "
+"Прерывания происходят асинхронно после возникновения определенного события "
+"(деление на ноль, ошибка страницы и т.д.). Прерывание может быть прозрачным "
+"для процесса (ошибка страницы) или привести к реакции, например, отправке "
+"_сигнала_ (деление на ноль)."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:120
+#, no-wrap
+msgid "Communication between processes"
+msgstr "Обмен данными между процессами"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:125
+msgid ""
+"There are other APIs (System V IPC, shared memory etc.) but the single most "
+"important API is signal. Signals are sent by processes or by the kernel and "
+"received by processes. Some signals can be ignored or handled by a user "
+"supplied routine, some result in a predefined action that cannot be altered "
+"or ignored."
+msgstr ""
+"Существуют другие API (System V IPC, разделяемая память и т.д.), но наиболее "
+"важным API являются сигналы. Сигналы отправляются процессами или ядром и "
+"принимаются процессами. Некоторые сигналы могут быть проигнорированы или "
+"обработаны пользовательской процедурой, другие приводят к предопределённому "
+"действию, которое нельзя изменить или игнорировать."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:127
+#, no-wrap
+msgid "Process management"
+msgstr "Управление процессами"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:137
+msgid ""
+"Kernel instances are processed first in the system (so called init). Every "
+"running process can create its identical copy using the man:fork[2] "
+"syscall. Some slightly modified versions of this syscall were introduced "
+"but the basic semantic is the same. Every running process can morph into "
+"some other process using the man:exec[3] syscall. Some modifications of "
+"this syscall were introduced but all serve the same basic purpose. "
+"Processes end their lives by calling the man:exit[2] syscall. Every process "
+"is identified by a unique number called PID. Every process has a defined "
+"parent (identified by its PID)."
+msgstr ""
+"Процессы ядра обрабатываются первыми в системе (так называемый init). Каждый "
+"запущенный процесс может создать свою идентичную копию, используя системный "
+"вызов man:fork[2]. Были введены некоторые немного изменённые версии этого "
+"системного вызова, но базовая семантика остаётся той же. Каждый запущенный "
+"процесс может превратиться в другой процесс, используя системный вызов "
+"man:exec[3]. Были введены некоторые модификации этого системного вызова, но "
+"все они служат одной и той же базовой цели. Процессы завершают своё "
+"существование, вызывая системный вызов man:exit[2]. Каждый процесс "
+"идентифицируется уникальным номером, называемым PID. У каждого процесса есть "
+"определённый родитель (идентифицируемый его PID)."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:139
+#, no-wrap
+msgid "Thread management"
+msgstr "Управление потоками"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:145
+msgid ""
+"Traditional UNIX(R) does not define any API nor implementation for "
+"threading, while POSIX(R) defines its threading API but the implementation "
+"is undefined. Traditionally there were two ways of implementing threads. "
+"Handling them as separate processes (1:1 threading) or envelope the whole "
+"thread group in one process and managing the threading in userspace (1:N "
+"threading). Comparing main features of each approach:"
+msgstr ""
+"Традиционный UNIX(R) не определяет никакого API или реализации для потоков, "
+"в то время как POSIX(R) определяет свой API для потоков, но реализация "
+"остается неопределенной. Традиционно существовало два способа реализации "
+"потоков: обработка их как отдельных процессов (потоки 1:1) или обертывание "
+"всей группы потоков в один процесс с управлением потоками в пользовательском "
+"пространстве (потоки 1:N). Сравнение основных особенностей каждого подхода:"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:147
+msgid "1:1 threading"
+msgstr "Потоки 1:1"
+
+#. type: Bullet: '- '
+#: documentation/content/en/articles/linux-emulation/_index.adoc:149
+msgid "heavyweight threads"
+msgstr "тяжеловесные потоки"
+
+#. type: Bullet: '- '
+#: documentation/content/en/articles/linux-emulation/_index.adoc:150
+msgid ""
+"the scheduling cannot be altered by the user (slightly mitigated by the "
+"POSIX(R) API)"
+msgstr ""
+"планирование не может быть изменено пользователем (частично смягчено "
+"благодаря POSIX(R) API)"
+
+#. type: Bullet: '+ '
+#: documentation/content/en/articles/linux-emulation/_index.adoc:151
+msgid "no syscall wrapping necessary"
+msgstr "нет необходимости в обёртке системных вызовов"
+
+#. type: Bullet: '+ '
+#: documentation/content/en/articles/linux-emulation/_index.adoc:152
+msgid "can utilize multiple CPUs"
+msgstr "может использовать несколько процессоров"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:154
+msgid "1:N threading"
+msgstr "Потоки 1:N"
+
+#. type: Bullet: '+ '
+#: documentation/content/en/articles/linux-emulation/_index.adoc:156
+msgid "lightweight threads"
+msgstr "легковесные потоки"
+
+#. type: Bullet: '+ '
+#: documentation/content/en/articles/linux-emulation/_index.adoc:157
+msgid "scheduling can be easily altered by the user"
+msgstr "планирование может быть легко изменено пользователем"
+
+#. type: Bullet: '- '
+#: documentation/content/en/articles/linux-emulation/_index.adoc:158
+msgid "syscalls must be wrapped"
+msgstr "Системные вызовы должны быть обернуты"
+
+#. type: Bullet: '- '
+#: documentation/content/en/articles/linux-emulation/_index.adoc:159
+msgid "cannot utilize more than one CPU"
+msgstr "не может использовать более одного CPU"
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:161
+#, no-wrap
+msgid "What is FreeBSD?"
+msgstr "Что такое FreeBSD?"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:169
+msgid ""
+"The FreeBSD project is one of the oldest open source operating systems "
+"currently available for daily use. It is a direct descendant of the genuine "
+"UNIX(R) so it could be claimed that it is a true UNIX(R) although licensing "
+"issues do not permit that. The start of the project dates back to the early "
+"1990's when a crew of fellow BSD users patched the 386BSD operating system. "
+"Based on this patchkit a new operating system arose named FreeBSD for its "
+"liberal license. Another group created the NetBSD operating system with "
+"different goals in mind. We will focus on FreeBSD."
+msgstr ""
+"Проект FreeBSD — одна из старейших операционных систем с открытым исходным "
+"кодом, доступных для повседневного использования. Она является прямым "
+"потомком оригинальной UNIX(R), поэтому можно утверждать, что это настоящая "
+"UNIX(R), хотя проблемы с лицензированием не позволяют этого сделать. Начало "
+"проекта относится к началу 1990-х годов, когда группа пользователей BSD "
+"создала набор исправлений для операционной системы 386BSD. На основе этого "
+"набора возникла новая операционная система под названием FreeBSD, получившая "
+"своё имя благодаря либеральной лицензии. Другая группа создала операционную "
+"систему NetBSD с другими целями. Мы сосредоточимся на FreeBSD."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:174
+msgid ""
+"FreeBSD is a modern UNIX(R)-based operating system with all the features of "
+"UNIX(R). Preemptive multitasking, multiuser facilities, TCP/IP networking, "
+"memory protection, symmetric multiprocessing support, virtual memory with "
+"merged VM and buffer cache, they are all there. One of the interesting and "
+"extremely useful features is the ability to emulate other UNIX(R)-like "
+"operating systems. As of December 2006 and 7-CURRENT development, the "
+"following emulation functionalities are supported:"
+msgstr ""
+"FreeBSD — это современная операционная система на основе UNIX(R), обладающая "
+"всеми возможностями UNIX(R). Вытесняющая многозадачность, "
+"многопользовательские функции, сетевые возможности TCP/IP, защита памяти, "
+"поддержка симметричной многопроцессорности, виртуальная память с "
+"объединёнными VM и кэшем буфера — всё это присутствует. Одной из интересных "
+"и чрезвычайно полезных особенностей является возможность эмуляции других "
+"UNIX(R)-подобных операционных систем. По состоянию на декабрь 2006 года и "
+"разработку 7-CURRENT поддерживаются следующие функции эмуляции:"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:176
+msgid "FreeBSD/i386 emulation on FreeBSD/amd64"
+msgstr "Совместимость FreeBSD/i386 на FreeBSD/amd64"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:177
+msgid "FreeBSD/i386 emulation on FreeBSD/ia64"
+msgstr "FreeBSD/i386 эмуляция на FreeBSD/ia64"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:178
+msgid "Linux(R)-emulation of Linux(R) operating system on FreeBSD"
+msgstr "Эмуляция Linux(R) операционной системы Linux(R) на FreeBSD"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:179
+msgid "NDIS-emulation of Windows networking drivers interface"
+msgstr "NDIS-эмуляция интерфейса сетевых драйверов Windows"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:180
+msgid "NetBSD-emulation of NetBSD operating system"
+msgstr "NetBSD-эмуляция операционной системы NetBSD"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:181
+msgid "PECoff-support for PECoff FreeBSD executables"
+msgstr "Поддержка PECoff для исполняемых файлов FreeBSD в формате PECoff"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:182
+msgid "SVR4-emulation of System V revision 4 UNIX(R)"
+msgstr "Эмуляция SVR4 System V revision 4 UNIX(R)"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:185
+msgid ""
+"Actively developed emulations are the Linux(R) layer and various FreeBSD-on-"
+"FreeBSD layers. Others are not supposed to work properly nor be usable "
+"these days."
+msgstr ""
+"Активно разрабатываемые эмуляции — это слой Linux(R) и различные слои "
+"FreeBSD-on-FreeBSD. Остальные в настоящее время не должны работать корректно "
+"или быть пригодными к использованию."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:195
+msgid ""
+"FreeBSD is traditional flavor of UNIX(R) in the sense of dividing the run of "
+"processes into two halves: kernel space and user space run. There are two "
+"types of process entry to the kernel: a syscall and a trap. There is only "
+"one way to return. In the subsequent sections we will describe the three "
+"gates to/from the kernel. The whole description applies to the i386 "
+"architecture as the Linuxulator only exists there but the concept is similar "
+"on other architectures. The information was taken from [1] and the source "
+"code."
+msgstr ""
+"FreeBSD — это традиционный вариант UNIX(R) в смысле разделения выполнения "
+"процессов на две части: выполнение в пространстве ядра и выполнение в "
+"пространстве пользователя. Существует два типа входа процесса в ядро: "
+"системный вызов (syscall) и ловушка (trap). Возврат только один. В "
+"последующих разделах мы опишем три входа/выхода в/из ядра. Всё описание "
+"относится к архитектуре i386, так как Linuxulator существует только там, но "
+"концепция схожа на других архитектурах. Информация была взята из [1] и "
+"исходного кода."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:197
+#, no-wrap
+msgid "System entries"
+msgstr "Системные записи"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:204
+msgid ""
+"FreeBSD has an abstraction called an execution class loader, which is a "
+"wedge into the man:execve[2] syscall. This employs a structure `sysentvec`, "
+"which describes an executable ABI. It contains things like errno "
+"translation table, signal translation table, various functions to serve "
+"syscall needs (stack fixup, coredumping, etc.). Every ABI the FreeBSD "
+"kernel wants to support must define this structure, as it is used later in "
+"the syscall processing code and at some other places. System entries are "
+"handled by trap handlers, where we can access both the kernel-space and the "
+"user-space at once."
+msgstr ""
+"В FreeBSD существует абстракция, называемая загрузчиком классов исполнения, "
+"которая является прослойкой в системном вызове man:execve[2]. Она использует "
+"структуру `sysentvec`, описывающую ABI исполняемого файла. Эта структура "
+"содержит такие элементы, как таблицу преобразования errno, таблицу "
+"преобразования сигналов, различные функции для обработки системных вызовов "
+"(исправление стека, создание дампов памяти и т.д.). Каждый ABI, который ядро "
+"FreeBSD поддерживает, должен определять эту структуру, так как она "
+"используется в дальнейшем в коде обработки системных вызовов и в некоторых "
+"других местах. Системные вызовы обрабатываются обработчиками прерываний, где "
+"можно одновременно получить доступ как к пространству ядра, так и к "
+"пользовательскому пространству."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:206
+#: documentation/content/en/articles/linux-emulation/_index.adoc:288
+#, no-wrap
+msgid "Syscalls"
+msgstr "Системные вызовы"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:209
+msgid ""
+"Syscalls on FreeBSD are issued by executing interrupt `0x80` with register "
+"`%eax` set to a desired syscall number with arguments passed on the stack."
+msgstr ""
+"Системные вызовы в FreeBSD выполняются путем прерывания `0x80` с "
+"установленным в регистре `%eax` номером нужного системного вызова и "
+"аргументами, переданными через стек."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:215
+msgid ""
+"When a process issues an interrupt `0x80`, the `int0x80` syscall trap "
+"handler is issued (defined in [.filename]#sys/i386/i386/exception.s#), which "
+"prepares arguments (i.e. copies them on to the stack) for a call to a C "
+"function man:syscall[2] (defined in [.filename]#sys/i386/i386/trap.c#), "
+"which processes the passed in trapframe. The processing consists of "
+"preparing the syscall (depending on the `sysvec` entry), determining if the "
+"syscall is 32-bit or 64-bit one (changes size of the parameters), then the "
+"parameters are copied, including the syscall. Next, the actual syscall "
+"function is executed with processing of the return code (special cases for "
+"`ERESTART` and `EJUSTRETURN` errors). Finally an `userret()` is scheduled, "
+"switching the process back to the users-pace. The parameters to the actual "
+"syscall handler are passed in the form of `struct thread *td`, `struct "
+"syscall args *` arguments where the second parameter is a pointer to the "
+"copied in structure of parameters."
+msgstr ""
+"Когда процесс вызывает прерывание `0x80`, срабатывает обработчик системного "
+"вызова `int0x80` (определённый в [.filename]#sys/i386/i386/exception.s#), "
+"который подготавливает аргументы (т.е. копирует их в стек) для вызова "
+"функции на языке C man:syscall[2] (определённой в [.filename]#sys/i386/i386/"
+"trap.c#), обрабатывающей переданный фрейм прерывания. Обработка включает "
+"подготовку системного вызова (в зависимости от записи `sysvec`), определение "
+"разрядности системного вызова (32-битный или 64-битный, что влияет на размер "
+"параметров), после чего параметры копируются, включая сам системный вызов. "
+"Затем выполняется фактическая функция системного вызова с обработкой кода "
+"возврата (особые случаи для ошибок `ERESTART` и `EJUSTRETURN`). В завершение "
+"планируется вызов `userret()`, возвращающий процесс в пользовательское "
+"пространство. Параметры для фактического обработчика системного вызова "
+"передаются в виде аргументов `struct thread *td`, `struct syscall args *`, "
+"где второй параметр является указателем на скопированную структуру "
+"параметров."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:217
+#: documentation/content/en/articles/linux-emulation/_index.adoc:307
+#: documentation/content/en/articles/linux-emulation/_index.adoc:794
+#, no-wrap
+msgid "Traps"
+msgstr "Ловушки (trap)"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:224
+msgid ""
+"Handling of traps in FreeBSD is similar to the handling of syscalls. "
+"Whenever a trap occurs, an assembler handler is called. It is chosen "
+"between alltraps, alltraps with regs pushed or calltrap depending on the "
+"type of the trap. This handler prepares arguments for a call to a C "
+"function `trap()` (defined in [.filename]#sys/i386/i386/trap.c#), which then "
+"processes the occurred trap. After the processing it might send a signal to "
+"the process and/or exit to userland using `userret()`."
+msgstr ""
+"Обработка ловушек в FreeBSD аналогична обработке системных вызовов. При "
+"возникновении ловушки вызывается обработчик на ассемблере. Он выбирается "
+"между `alltraps`, `alltraps` с сохранением регистров или `calltrap` в "
+"зависимости от типа ловушки. Этот обработчик подготавливает аргументы для "
+"вызова функции на языке C `trap()` (определена в [.filename]#sys/i386/i386/"
+"trap.c#), которая затем обрабатывает произошедшую ловушку. После обработки "
+"она может отправить сигнал процессу и/или вернуться в пользовательское "
+"пространство с помощью `userret()`."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:226
+#: documentation/content/en/articles/linux-emulation/_index.adoc:312
+#, no-wrap
+msgid "Exits"
+msgstr "Выходы"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:230
+msgid ""
+"Exits from kernel to userspace happen using the assembler routine `doreti` "
+"regardless of whether the kernel was entered via a trap or via a syscall. "
+"This restores the program status from the stack and returns to the userspace."
+msgstr ""
+"Выход из ядра в пользовательское пространство происходит с использованием "
+"ассемблерной процедуры `doreti`, независимо от того, было ли ядро вызвано "
+"через ловушку или через системный вызов. Это восстанавливает состояние "
+"программы из стека и возвращает управление в пользовательское пространство."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:232
+#: documentation/content/en/articles/linux-emulation/_index.adoc:318
+#, no-wrap
+msgid "UNIX(R) primitives"
+msgstr "Примитивы UNIX(R)"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:238
+msgid ""
+"FreeBSD operating system adheres to the traditional UNIX(R) scheme, where "
+"every process has a unique identification number, the so called _PID_ "
+"(Process ID). PID numbers are allocated either linearly or randomly ranging "
+"from `0` to `PID_MAX`. The allocation of PID numbers is done using linear "
+"searching of PID space. Every thread in a process receives the same PID "
+"number as result of the man:getpid[2] call."
+msgstr ""
+"Операционная система FreeBSD придерживается традиционной схемы UNIX(R), где "
+"каждый процесс имеет уникальный идентификационный номер, так называемый "
+"_PID_ (Идентификатор Процесса). Номера PID выделяются либо линейно, либо "
+"случайным образом в диапазоне от `0` до `PID_MAX`. Распределение номеров PID "
+"осуществляется с помощью линейного поиска в пространстве PID. Каждый поток в "
+"процессе получает тот же номер PID в результате вызова man:getpid[2]."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:249
+msgid ""
+"There are currently two ways to implement threading in FreeBSD. The first "
+"way is M:N threading followed by the 1:1 threading model. The default "
+"library used is M:N threading (`libpthread`) and you can switch at runtime "
+"to 1:1 threading (`libthr`). The plan is to switch to 1:1 library by "
+"default soon. Although those two libraries use the same kernel primitives, "
+"they are accessed through different API(es). The M:N library uses the "
+"`kse_*` family of syscalls while the 1:1 library uses the `thr_*` family of "
+"syscalls. Due to this, there is no general concept of thread ID shared "
+"between kernel and userspace. Of course, both threading libraries implement "
+"the pthread thread ID API. Every kernel thread (as described by `struct "
+"thread`) has td tid identifier but this is not directly accessible from "
+"userland and solely serves the kernel's needs. It is also used for 1:1 "
+"threading library as pthread's thread ID but handling of this is internal to "
+"the library and cannot be relied on."
+msgstr ""
+"В настоящее время в FreeBSD существует два способа реализации потоков. "
+"Первый способ — это M:N потоки, за которым следует модель потоков 1:1. По "
+"умолчанию используется библиотека M:N (`libpthread`), но во время выполнения "
+"можно переключиться на потоки 1:1 (`libthr`). Планируется в ближайшее время "
+"перейти на библиотеку 1:1 по умолчанию. Хотя обе библиотеки используют одни "
+"и те же примитивы ядра, доступ к ним осуществляется через разные API. "
+"Библиотека M:N использует семейство системных вызовов `kse_*`, тогда как "
+"библиотека 1:1 использует семейство `thr_*`. Из-за этого отсутствует общая "
+"концепция идентификатора потока, разделяемая между ядром и пользовательским "
+"пространством. Конечно, обе библиотеки реализуют API идентификатора потока "
+"pthread. У каждого потока ядра (как описано в `struct thread`) есть "
+"идентификатор td tid, но он недоступен напрямую из пользовательского "
+"пространства и служит исключительно нуждам ядра. Он также используется в "
+"библиотеке потоков 1:1 в качестве идентификатора потока pthread, но "
+"обработка этого идентификатора внутренняя для библиотеки и не может быть "
+"использована напрямую."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:257
+msgid ""
+"As stated previously there are two implementations of threading in FreeBSD. "
+"The M:N library divides the work between kernel space and userspace. Thread "
+"is an entity that gets scheduled in the kernel but it can represent various "
+"number of userspace threads. M userspace threads get mapped to N kernel "
+"threads thus saving resources while keeping the ability to exploit "
+"multiprocessor parallelism. Further information about the implementation "
+"can be obtained from the man page or [1]. The 1:1 library directly maps a "
+"userland thread to a kernel thread thus greatly simplifying the scheme. "
+"None of these designs implement a fairness mechanism (such a mechanism was "
+"implemented but it was removed recently because it caused serious slowdown "
+"and made the code more difficult to deal with)."
+msgstr ""
+"Как упоминалось ранее, в FreeBSD существуют две реализации потоков. "
+"Библиотека M:N разделяет работу между пространством ядра и пользовательским "
+"пространством. Поток — это сущность, которая планируется в ядре, но может "
+"представлять различное количество пользовательских потоков. M "
+"пользовательских потоков отображаются на N потоков ядра, что позволяет "
+"экономить ресурсы, сохраняя при этом возможность использовать преимущества "
+"многопроцессорного параллелизма. Дополнительную информацию о реализации "
+"можно получить из man-страницы или [1]. Библиотека 1:1 напрямую отображает "
+"пользовательский поток на поток ядра, что значительно упрощает схему. Ни "
+"одна из этих реализаций не включает механизм справедливости (такой механизм "
+"был реализован, но недавно удалён, поскольку вызывал серьёзное замедление и "
+"усложнял работу с кодом)."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:259
+#, no-wrap
+msgid "What is Linux(R)"
+msgstr "Что такое Linux(R)"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:263
+msgid ""
+"Linux(R) is a UNIX(R)-like kernel originally developed by Linus Torvalds, "
+"and now being contributed to by a massive crowd of programmers all around "
+"the world. From its mere beginnings to today, with wide support from "
+"companies such as IBM or Google, Linux(R) is being associated with its fast "
+"development pace, full hardware support and benevolent dictator model of "
+"organization."
+msgstr ""
+"Linux(R) — это UNIX(R)-подобное ядро, изначально разработанное Линусом "
+"Торвальдсом, а сейчас развиваемое множеством программистов по всему миру. От "
+"своих скромных начал до сегодняшнего дня, при широкой поддержке таких "
+"компаний, как IBM или Google, Linux(R) ассоциируется с быстрым темпом "
+"разработки, полной поддержкой оборудования и моделью организации по принципу "
+"\"доброжелательного диктатора\"."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:267
+msgid ""
+"Linux(R) development started in 1991 as a hobbyist project at University of "
+"Helsinki in Finland. Since then it has obtained all the features of a "
+"modern UNIX(R)-like OS: multiprocessing, multiuser support, virtual memory, "
+"networking, basically everything is there. There are also highly advanced "
+"features like virtualization etc."
+msgstr ""
+"Разработка Linux(R) началась в 1991 году как любительский проект в "
+"Университете Хельсинки, Финляндия. С тех пор она приобрела все черты "
+"современной ОС, подобной UNIX(R): многопроцессорность, поддержка "
+"многопользовательского режима, виртуальная память, сетевое взаимодействие — "
+"в общем, всё необходимое. Также присутствуют высокоуровневые функции, такие "
+"как виртуализация и т. д."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:270
+msgid ""
+"As of 2006 Linux(R) seems to be the most widely used open source operating "
+"system with support from independent software vendors like Oracle, "
+"RealNetworks, Adobe, etc. Most of the commercial software distributed for "
+"Linux(R) can only be obtained in a binary form so recompilation for other "
+"operating systems is impossible."
+msgstr ""
+"В 2006 году Linux(R), похоже, был наиболее широко используемой открытой "
+"операционной системой с поддержкой независимых поставщиков программного "
+"обеспечения, таких как Oracle, RealNetworks, Adobe и других. Большая часть "
+"коммерческого программного обеспечения, распространяемого для Linux(R), "
+"доступна только в бинарном виде, поэтому перекомпиляция для других "
+"операционных систем невозможна."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:275
+msgid ""
+"Most of the Linux(R) development happens in a Git version control system. "
+"Git is a distributed system so there is no central source of the Linux(R) "
+"code, but some branches are considered prominent and official. The version "
+"number scheme implemented by Linux(R) consists of four numbers A.B.C.D. "
+"Currently development happens in 2.6.C.D, where C represents major version, "
+"where new features are added or changed while D is a minor version for "
+"bugfixes only."
+msgstr ""
+"Большая часть разработки Linux(R) происходит в системе контроля версий Git. "
+"Git — это распределённая система, поэтому нет централизованного источника "
+"кода Linux(R), но некоторые ветви считаются основными и официальными. Схема "
+"нумерации версий, используемая в Linux(R), состоит из четырёх чисел: "
+"A.B.C.D. В настоящее время разработка ведётся в ветке 2.6.C.D, где C "
+"обозначает мажорную версию, в которую добавляются или изменяются функции, а "
+"D — минорную версию, предназначенную только для исправления ошибок."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:277
+msgid "More information can be obtained from [3]."
+msgstr "Дополнительную информацию можно получить из [3]."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:286
+msgid ""
+"Linux(R) follows the traditional UNIX(R) scheme of dividing the run of a "
+"process in two halves: the kernel and user space. The kernel can be entered "
+"in two ways: via a trap or via a syscall. The return is handled only in one "
+"way. The further description applies to Linux(R) 2.6 on the i386(TM) "
+"architecture. This information was taken from [2]."
+msgstr ""
+"Linux(R) следует традиционной схеме UNIX(R), разделяя выполнение процесса на "
+"две части: ядро и пользовательское пространство. Ядро может быть вызвано "
+"двумя способами: через ловушку (trap) или через системный вызов. Возврат "
+"осуществляется только одним способом. Далее описание относится к Linux(R) "
+"2.6 на архитектуре i386(TM). Эта информация взята из [2]."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:296
+msgid ""
+"Syscalls in Linux(R) are performed (in userspace) using `syscallX` macros "
+"where X substitutes a number representing the number of parameters of the "
+"given syscall. This macro translates to a code that loads `%eax` register "
+"with a number of the syscall and executes interrupt `0x80`. After this "
+"syscall return is called, which translates negative return values to "
+"positive `errno` values and sets `res` to `-1` in case of an error. "
+"Whenever the interrupt `0x80` is called the process enters the kernel in "
+"system call trap handler. This routine saves all registers on the stack and "
+"calls the selected syscall entry. Note that the Linux(R) calling convention "
+"expects parameters to the syscall to be passed via registers as shown here:"
+msgstr ""
+"Системные вызовы в Linux(R) выполняются (в пользовательском пространстве) с "
+"использованием макросов `syscallX`, где X заменяется числом, представляющим "
+"количество параметров данного системного вызова. Этот макрос преобразуется в "
+"код, который загружает регистр `%eax` номером системного вызова и выполняет "
+"прерывание `0x80`. После этого вызывается возврат из системного вызова, "
+"который преобразует отрицательные значения возврата в положительные значения "
+"`errno` и устанавливает `res` в `-1` в случае ошибки. При вызове прерывания "
+"`0x80` процесс переходит в ядро в обработчик ловушки системного вызова. Эта "
+"процедура сохраняет все регистры в стеке и вызывает выбранную точку входа "
+"системного вызова. Обратите внимание, что соглашение о вызовах Linux(R) "
+"предполагает передачу параметров системного вызова через регистры, как "
+"показано здесь:"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:298
+msgid "parameter -> `%ebx`"
+msgstr "параметр -> `%ebx`"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:299
+msgid "parameter -> `%ecx`"
+msgstr "параметр -> `%ecx`"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:300
+msgid "parameter -> `%edx`"
+msgstr "параметр -> `%edx`"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:301
+msgid "parameter -> `%esi`"
+msgstr "параметр -> `%esi`"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:302
+msgid "parameter -> `%edi`"
+msgstr "параметр -> `%edi`"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:303
+msgid "parameter -> `%ebp`"
+msgstr "параметр -> `%ebp`"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:305
+msgid ""
+"There are some exceptions to this, where Linux(R) uses different calling "
+"convention (most notably the `clone` syscall)."
+msgstr ""
+"Существуют некоторые исключения из этого правила, где Linux(R) использует "
+"другие соглашения о вызовах (наиболее примечателен системный вызов `clone`)."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:310
+msgid ""
+"The trap handlers are introduced in [.filename]#arch/i386/kernel/traps.c# "
+"and most of these handlers live in [.filename]#arch/i386/kernel/entry.S#, "
+"where handling of the traps happens."
+msgstr ""
+"Обработчики ловушек представлены в файле [.filename]#arch/i386/kernel/"
+"traps.c#, а большинство этих обработчиков находятся в [.filename]#arch/i386/"
+"kernel/entry.S#, где происходит обработка ловушек."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:316
+msgid ""
+"Return from the syscall is managed by syscall man:exit[3], which checks for "
+"the process having unfinished work, then checks whether we used user-"
+"supplied selectors. If this happens stack fixing is applied and finally the "
+"registers are restored from the stack and the process returns to the "
+"userspace."
+msgstr ""
+"Возврат из системного вызова обрабатывается функцией `syscall man:exit[3]`, "
+"которая проверяет, есть ли у процесса незавершённые задачи, затем проверяет, "
+"использовались ли селекторы, предоставленные пользователем. Если это "
+"произошло, применяется исправление стека, и, наконец, регистры "
+"восстанавливаются из стека, а процесс возвращается в пользовательское "
+"пространство."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:327
+msgid ""
+"In the 2.6 version, the Linux(R) operating system redefined some of the "
+"traditional UNIX(R) primitives, notably PID, TID and thread. PID is defined "
+"not to be unique for every process, so for some processes (threads) "
+"man:getppid[2] returns the same value. Unique identification of process is "
+"provided by TID. This is because _NPTL_ (New POSIX(R) Thread Library) "
+"defines threads to be normal processes (so called 1:1 threading). Spawning "
+"a new process in Linux(R) 2.6 happens using the `clone` syscall (fork "
+"variants are reimplemented using it). This clone syscall defines a set of "
+"flags that affect behavior of the cloning process regarding thread "
+"implementation. The semantic is a bit fuzzy as there is no single flag "
+"telling the syscall to create a thread."
+msgstr ""
+"В версии 2.6 операционная система Linux(R) переопределила некоторые "
+"традиционные примитивы UNIX(R), в частности PID, TID и поток. PID "
+"определяется не как уникальный для каждого процесса, поэтому для некоторых "
+"процессов (потоков) man:getppid[2] возвращает одинаковое значение. "
+"Уникальная идентификация процесса обеспечивается TID. Это связано с тем, что "
+"_NPTL_ (New POSIX(R) Thread Library) определяет потоки как обычные процессы "
+"(так называемая модель 1:1). Создание нового процесса в Linux(R) 2.6 "
+"происходит с использованием системного вызова `clone` (варианты fork "
+"перереализованы с его использованием). Этот системный вызов clone определяет "
+"набор флагов, которые влияют на поведение процесса клонирования в отношении "
+"реализации потоков. Семантика немного размыта, так как нет единого флага, "
+"указывающего системному вызову создать поток."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:329
+msgid "Implemented clone flags are:"
+msgstr "Реализованные флаги клонирования:"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:331
+msgid "`CLONE_VM` - processes share their memory space"
+msgstr "`CLONE_VM` - процессы разделяют общее адресное пространство"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:332
+msgid "`CLONE_FS` - share umask, cwd and namespace"
+msgstr ""
+"`CLONE_FS` — совместно использовать umask, текущий рабочий каталог и "
+"пространство имён"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:333
+msgid "`CLONE_FILES` - share open files"
+msgstr "`CLONE_FILES` - совместно использовать открытые файлы"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:334
+msgid "`CLONE_SIGHAND` - share signal handlers and blocked signals"
+msgstr ""
+"`CLONE_SIGHAND` - разделять обработчики сигналов и заблокированные сигналы"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:335
+msgid "`CLONE_PARENT` - share parent"
+msgstr "`CLONE_PARENT` - использовать один процесс к качестве родительского"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:336
+msgid "`CLONE_THREAD` - be thread (further explanation below)"
+msgstr "`CLONE_THREAD` — быть потоком (дальнейшие пояснения ниже)"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:337
+msgid "`CLONE_NEWNS` - new namespace"
+msgstr "`CLONE_NEWNS` - новое пространство имен"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:338
+msgid "`CLONE_SYSVSEM` - share SysV undo structures"
+msgstr "`CLONE_SYSVSEM` - совместное использование структур отмены SysV"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:339
+msgid "`CLONE_SETTLS` - setup TLS at supplied address"
+msgstr "`CLONE_SETTLS` - настройка TLS по указанному адресу"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:340
+msgid "`CLONE_PARENT_SETTID` - set TID in the parent"
+msgstr "`CLONE_PARENT_SETTID` - установить TID в родителе"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:341
+msgid "`CLONE_CHILD_CLEARTID` - clear TID in the child"
+msgstr "`CLONE_CHILD_CLEARTID` - очистить TID в дочернем процессе"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:342
+msgid "`CLONE_CHILD_SETTID` - set TID in the child"
+msgstr "`CLONE_CHILD_SETTID` - установить TID в дочернем процессе"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:348
+msgid ""
+"`CLONE_PARENT` sets the real parent to the parent of the caller. This is "
+"useful for threads because if thread A creates thread B we want thread B to "
+"be parented to the parent of the whole thread group. `CLONE_THREAD` does "
+"exactly the same thing as `CLONE_PARENT`, `CLONE_VM` and `CLONE_SIGHAND`, "
+"rewrites PID to be the same as PID of the caller, sets exit signal to be "
+"none and enters the thread group. `CLONE_SETTLS` sets up GDT entries for "
+"TLS handling. The `CLONE_*_*TID` set of flags sets/clears user supplied "
+"address to TID or 0."
+msgstr ""
+"`CLONE_PARENT` устанавливает реального родителя в родителя вызывающего "
+"процесса. Это полезно для потоков, потому что если поток A создаёт поток B, "
+"мы хотим, чтобы поток B был привязан к родителю всей группы потоков. "
+"`CLONE_THREAD` делает то же самое, что `CLONE_PARENT`, `CLONE_VM` и "
+"`CLONE_SIGHAND`, перезаписывает PID, чтобы он совпадал с PID вызывающего "
+"процесса, устанавливает сигнал завершения в \"нет\" и входит в группу "
+"потоков. `CLONE_SETTLS` настраивает записи GDT для обработки TLS. Набор "
+"флагов `CLONE_*_*TID` устанавливает/сбрасывает предоставленный пользователем "
+"адрес в TID или 0."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:352
+msgid ""
+"As you can see the `CLONE_THREAD` does most of the work and does not seem to "
+"fit the scheme very well. The original intention is unclear (even for "
+"authors, according to comments in the code) but I think originally there was "
+"one threading flag, which was then parcelled among many other flags but this "
+"separation was never fully finished. It is also unclear what this partition "
+"is good for as glibc does not use that so only hand-written use of the clone "
+"permits a programmer to access this features."
+msgstr ""
+"Как видно, `CLONE_THREAD` выполняет большую часть работы и не очень хорошо "
+"вписывается в схему. Первоначальный замысел неясен (даже для авторов, "
+"согласно комментариям в коде), но я думаю, изначально был один флаг для "
+"потоков, который затем был разделён на множество других флагов, но это "
+"разделение так и не было завершено. Также непонятно, для чего нужно это "
+"разделение, так как glibc не использует его, и только ручное использование "
+"clone позволяет программисту получить доступ к этим возможностям."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:355
+msgid ""
+"For non-threaded programs the PID and TID are the same. For threaded "
+"programs the first thread PID and TID are the same and every created thread "
+"shares the same PID and gets assigned a unique TID (because `CLONE_THREAD` "
+"is passed in) also parent is shared for all processes forming this threaded "
+"program."
+msgstr ""
+"Для непоточных программ PID и TID совпадают. Для поточных программ первый "
+"поток имеет одинаковые PID и TID, а каждый созданный поток разделяет тот же "
+"PID и получает уникальный TID (поскольку передается `CLONE_THREAD`), также "
+"родительский процесс общий для всех процессов, образующих эту поточную "
+"программу."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/linux-emulation/_index.adoc:357
+msgid ""
+"The code that implements man:pthread_create[3] in NPTL defines the clone "
+"flags like this:"
+msgstr ""
+"Код, реализующий man:pthread_create[3] в NPTL, определяет флаги clone "
+"следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:361
+#, no-wrap
+msgid "int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL\n"
+msgstr "int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:363
+#, no-wrap
+msgid " | CLONE_SETTLS | CLONE_PARENT_SETTID\n"
+msgstr " | CLONE_SETTLS | CLONE_PARENT_SETTID\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:366
+#, no-wrap
+msgid ""
+"| CLONE_CHILD_CLEARTID | CLONE_SYSVSEM\n"
+"#if __ASSUME_NO_CLONE_DETACHED == 0\n"
+msgstr ""
+"| CLONE_CHILD_CLEARTID | CLONE_SYSVSEM\n"
+"#if __ASSUME_NO_CLONE_DETACHED == 0\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:369
+#, no-wrap
+msgid ""
+"| CLONE_DETACHED\n"
+"#endif\n"
+msgstr ""
+"| CLONE_DETACHED\n"
+"#endif\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:371
+#, no-wrap
+msgid "| 0);\n"
+msgstr "| 0);\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:374
+msgid "The `CLONE_SIGNAL` is defined like"
+msgstr "`CLONE_SIGNAL` определен как"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:378
+#, no-wrap
+msgid "#define CLONE_SIGNAL (CLONE_SIGHAND | CLONE_THREAD)\n"
+msgstr "#define CLONE_SIGNAL (CLONE_SIGHAND | CLONE_THREAD)\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:381
+msgid "the last 0 means no signal is sent when any of the threads exits."
+msgstr ""
+"последний 0 означает, что сигнал не отправляется при завершении любого из "
+"потоков."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:383
+#, no-wrap
+msgid "What is emulation"
+msgstr "Что такое эмуляция"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:388
+msgid ""
+"According to a dictionary definition, emulation is the ability of a program "
+"or device to imitate another program or device. This is achieved by "
+"providing the same reaction to a given stimulus as the emulated object. In "
+"practice, the software world mostly sees three types of emulation - a "
+"program used to emulate a machine (QEMU, various game console emulators "
+"etc.), software emulation of a hardware facility (OpenGL emulators, floating "
+"point units emulation etc.) and operating system emulation (either in kernel "
+"of the operating system or as a userspace program)."
+msgstr ""
+"Согласно словарному определению, эмуляция — это способность программы или "
+"устройства имитировать другую программу или устройство. Это достигается за "
+"счёт предоставления той же реакции на заданный стимул, что и у эмулируемого "
+"объекта. На практике в мире программного обеспечения в основном встречаются "
+"три типа эмуляции — программа, используемая для эмуляции машины (QEMU, "
+"различные эмуляторы игровых консолей и т.д.), программная эмуляция "
+"аппаратного обеспечения (эмуляторы OpenGL, эмуляция блоков плавающей запятой "
+"и т.д.) и эмуляция операционной системы (либо в ядре операционной системы, "
+"либо в виде программы пользовательского пространства)."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:395
+msgid ""
+"Emulation is usually used in a place, where using the original component is "
+"not feasible nor possible at all. For example someone might want to use a "
+"program developed for a different operating system than they use. Then "
+"emulation comes in handy. Sometimes there is no other way but to use "
+"emulation - e.g. when the hardware device you try to use does not exist (yet/"
+"anymore) then there is no other way but emulation. This happens often when "
+"porting an operating system to a new (non-existent) platform. Sometimes it "
+"is just cheaper to emulate."
+msgstr ""
+"Эмуляция обычно используется в тех случаях, когда применение оригинального "
+"компонента невозможно или нецелесообразно. Например, может возникнуть "
+"необходимость использовать программу, разработанную для другой операционной "
+"системы. В такой ситуации на помощь приходит эмуляция. Иногда эмуляция — "
+"единственный возможный вариант, например, когда необходимое аппаратное "
+"устройство ещё не существует или уже не выпускается. Такое часто происходит "
+"при переносе операционной системы на новую (ещё не существующую) платформу. "
+"Иногда эмуляция просто экономически выгоднее."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:407
+msgid ""
+"Looking from an implementation point of view, there are two main approaches "
+"to the implementation of emulation. You can either emulate the whole thing "
+"- accepting possible inputs of the original object, maintaining inner state "
+"and emitting correct output based on the state and/or input. This kind of "
+"emulation does not require any special conditions and basically can be "
+"implemented anywhere for any device/program. The drawback is that "
+"implementing such emulation is quite difficult, time-consuming and error-"
+"prone. In some cases we can use a simpler approach. Imagine you want to "
+"emulate a printer that prints from left to right on a printer that prints "
+"from right to left. It is obvious that there is no need for a complex "
+"emulation layer but simply reversing of the printed text is sufficient. "
+"Sometimes the emulating environment is very similar to the emulated one so "
+"just a thin layer of some translation is necessary to provide fully working "
+"emulation! As you can see this is much less demanding to implement, so less "
+"time-consuming and error-prone than the previous approach. But the "
+"necessary condition is that the two environments must be similar enough. "
+"The third approach combines the two previous. Most of the time the objects "
+"do not provide the same capabilities so in a case of emulating the more "
+"powerful one on the less powerful we have to emulate the missing features "
+"with full emulation described above."
+msgstr ""
+"С точки зрения реализации, существует два основных подхода к эмуляции. Вы "
+"можете либо эмулировать всё целиком — принимать возможные входные данные "
+"исходного объекта, поддерживать внутреннее состояние и выдавать корректные "
+"выходные данные на основе состояния и/или входных данных. Такой вид эмуляции "
+"не требует каких-либо специальных условий и, в принципе, может быть "
+"реализован где угодно для любого устройства/программы. Недостаток в том, что "
+"реализация такой эмуляции довольно сложна, трудоёмка и подвержена ошибкам. В "
+"некоторых случаях можно использовать более простой подход. Представьте, что "
+"вы хотите эмулировать принтер, печатающий слева направо, на принтере, "
+"который печатает справа налево. Очевидно, что нет необходимости в сложном "
+"слое эмуляции — достаточно просто перевернуть печатаемый текст. Иногда "
+"эмулирующая среда очень похожа на эмулируемую, и тогда достаточно тонкого "
+"слоя преобразования для обеспечения полностью рабочей эмуляции! Как видите, "
+"такой подход гораздо менее требователен к реализации, а значит, менее "
+"трудоёмок и подвержен ошибкам, чем предыдущий. Однако необходимое условие — "
+"две среды должны быть достаточно схожи. Третий подход сочетает в себе два "
+"предыдущих. Чаще всего объекты не предоставляют одинаковые возможности, "
+"поэтому в случае эмуляции более мощного объекта на менее мощном приходится "
+"эмулировать отсутствующие функции с помощью полной эмуляции, описанной выше."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:410
+msgid ""
+"This master thesis deals with emulation of UNIX(R) on UNIX(R), which is "
+"exactly the case, where only a thin layer of translation is sufficient to "
+"provide full emulation. The UNIX(R) API consists of a set of syscalls, "
+"which are usually self contained and do not affect some global kernel state."
+msgstr ""
+"Эта магистерская диссертация посвящена эмуляции UNIX(R) на UNIX(R), что "
+"является именно тем случаем, когда достаточно тонкого слоя трансляции для "
+"обеспечения полной эмуляции. API UNIX(R) состоит из набора системных "
+"вызовов, которые обычно самодостаточны и не влияют на глобальное состояние "
+"ядра."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:412
+msgid ""
+"There are a few syscalls that affect inner state but this can be dealt with "
+"by providing some structures that maintain the extra state."
+msgstr ""
+"Существует несколько системных вызовов, которые влияют на внутреннее "
+"состояние, но это можно решить, предоставив некоторые структуры, "
+"поддерживающие дополнительное состояние."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:416
+msgid ""
+"No emulation is perfect and emulations tend to lack some parts but this "
+"usually does not cause any serious drawbacks. Imagine a game console "
+"emulator that emulates everything but music output. No doubt that the games "
+"are playable and one can use the emulator. It might not be that comfortable "
+"as the original game console but its an acceptable compromise between price "
+"and comfort."
+msgstr ""
+"Эмуляция не бывает идеальной, и в эмуляторах часто чего-то не хватает, но "
+"обычно это не вызывает серьёзных проблем. Представьте эмулятор игровой "
+"приставки, который эмулирует всё, кроме звука. Без сомнения, игры остаются "
+"играбельными, и эмулятором можно пользоваться. Возможно, это не так "
+"комфортно, как оригинальная приставка, но это приемлемый компромисс между "
+"ценой и удобством."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:420
+msgid ""
+"The same goes with the UNIX(R) API. Most programs can live with a very "
+"limited set of syscalls working. Those syscalls tend to be the oldest ones "
+"(man:read[2]/man:write[2], man:fork[2] family, man:signal[3] handling, "
+"man:exit[3], man:socket[2] API) hence it is easy to emulate because their "
+"semantics is shared among all UNIX(R)es, which exist todays."
+msgstr ""
+"То же самое касается UNIX(R) API. Большинство программ могут работать с "
+"очень ограниченным набором системных вызовов. Эти вызовы, как правило, "
+"являются самыми старыми (man:read[2]/man:write[2], семейство man:fork[2], "
+"обработка man:signal[3], man:exit[3], API man:socket[2]), поэтому их легко "
+"эмулировать, поскольку их семантика одинакова во всех современных UNIX(R)-"
+"подобных системах."
+
+#. type: Title ==
+#: documentation/content/en/articles/linux-emulation/_index.adoc:422
+#, no-wrap
+msgid "Emulation"
+msgstr "Эмуляция"
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:424
+#, no-wrap
+msgid "How emulation works in FreeBSD"
+msgstr "Как работает эмуляция в FreeBSD"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:429
+msgid ""
+"As stated earlier, FreeBSD supports running binaries from several other "
+"UNIX(R)es. This works because FreeBSD has an abstraction called the "
+"execution class loader. This wedges into the man:execve[2] syscall, so when "
+"man:execve[2] is about to execute a binary it examines its type."
+msgstr ""
+"Как упоминалось ранее, FreeBSD поддерживает выполнение бинарных файлов из "
+"нескольких других UNIX(R)-подобных систем. Это возможно благодаря наличию в "
+"FreeBSD абстракции, называемой загрузчик классов исполнения. Он "
+"интегрируется в системный вызов man:execve[2], поэтому когда man:execve[2] "
+"собирается выполнить бинарный файл, он анализирует его тип."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:435
+msgid ""
+"There are basically two types of binaries in FreeBSD. Shell-like text "
+"scripts which are identified by `#!` as their first two characters and "
+"normal (typically _ELF_) binaries, which are a representation of a compiled "
+"executable object. The vast majority (one could say all of them) of "
+"binaries in FreeBSD are from type ELF. ELF files contain a header, which "
+"specifies the OS ABI for this ELF file. By reading this information, the "
+"operating system can accurately determine what type of binary the given file "
+"is."
+msgstr ""
+"В FreeBSD существуют два основных типа исполняемых файлов. Текстовые "
+"скрипты, подобные shell-скриптам, которые идентифицируются по первым двум "
+"символам `#!`, и обычные (как правило, _ELF_) бинарные файлы, представляющие "
+"собой скомпилированные исполняемые объекты. Подавляющее большинство (можно "
+"сказать, все) исполняемых файлов в FreeBSD относятся к типу ELF. Файлы ELF "
+"содержат заголовок, который определяет ABI операционной системы для данного "
+"ELF-файла. Считывая эту информацию, операционная система может точно "
+"определить, к какому типу относится данный исполняемый файл."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:441
+msgid ""
+"Every OS ABI must be registered in the FreeBSD kernel. This applies to the "
+"FreeBSD native OS ABI, as well. So when man:execve[2] executes a binary it "
+"iterates through the list of registered APIs and when it finds the right one "
+"it starts to use the information contained in the OS ABI description (its "
+"syscall table, `errno` translation table, etc.). So every time the process "
+"calls a syscall, it uses its own set of syscalls instead of some global "
+"one. This effectively provides a very elegant and easy way of supporting "
+"execution of various binary formats."
+msgstr ""
+"Каждый ABI ОС должен быть зарегистрирован в ядре FreeBSD. Это относится и к "
+"родному ABI ОС FreeBSD. Таким образом, когда man:execve[2] выполняет "
+"двоичный файл, он перебирает список зарегистрированных API, и когда находит "
+"подходящий, начинает использовать информацию, содержащуюся в описании ABI ОС "
+"(его таблицу системных вызовов, таблицу преобразования `errno` и т.д.). "
+"Таким образом, каждый раз, когда процесс вызывает системный вызов, он "
+"использует свой собственный набор системных вызовов вместо какого-либо "
+"глобального. Это обеспечивает очень элегантный и простой способ поддержки "
+"выполнения различных двоичных форматов."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:446
+msgid ""
+"The nature of emulation of different OSes (and also some other subsystems) "
+"led developers to invite a handler event mechanism. There are various "
+"places in the kernel, where a list of event handlers are called. Every "
+"subsystem can register an event handler and they are called accordingly. "
+"For example, when a process exits there is a handler called that possibly "
+"cleans up whatever the subsystem needs to be cleaned."
+msgstr ""
+"Природа эмуляции различных ОС (а также некоторых других подсистем) привела "
+"разработчиков к внедрению механизма обработчиков событий. В ядре существует "
+"множество мест, где вызывается список обработчиков событий. Каждая "
+"подсистема может зарегистрировать обработчик событий, и они вызываются "
+"соответствующим образом. Например, при завершении процесса вызывается "
+"обработчик, который может выполнить необходимую очистку для подсистемы."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:448
+msgid ""
+"Those simple facilities provide basically everything that is needed for the "
+"emulation infrastructure and in fact these are basically the only things "
+"necessary to implement the Linux(R) emulation layer."
+msgstr ""
+"Те простые средства предоставляют практически всё необходимое для "
+"инфраструктуры эмуляции, и, по сути, это единственное, что требуется для "
+"реализации слоя эмуляции Linux(R)."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:450
+#, no-wrap
+msgid "Common primitives in the FreeBSD kernel"
+msgstr "Общие примитивы в ядре FreeBSD"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:454
+msgid ""
+"Emulation layers need some support from the operating system. I am going to "
+"describe some of the supported primitives in the FreeBSD operating system."
+msgstr ""
+"Для работы слоев эмуляции требуется некоторая поддержка со стороны "
+"операционной системы. Я расскажу о некоторых поддерживаемых примитивах в "
+"операционной системе FreeBSD."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:456
+#, no-wrap
+msgid "Locking primitives"
+msgstr "Примитивы синхронизации"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:459
+msgid "Contributed by: `{attilio}`"
+msgstr "Добавил: `{attilio}`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:461
+msgid ""
+"The FreeBSD synchronization primitive set is based on the idea to supply a "
+"rather huge number of different primitives in a way that the better one can "
+"be used for every particular, appropriate situation."
+msgstr ""
+"Примитивы синхронизации FreeBSD основаны на идее предоставления достаточно "
+"большого количества различных примитивов таким образом, чтобы для каждой "
+"конкретной подходящей ситуации можно было использовать наилучший."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:463
+msgid ""
+"To a high level point of view you can consider three kinds of "
+"synchronization primitives in the FreeBSD kernel:"
+msgstr ""
+"На высоком уровне можно выделить три вида примитивов синхронизации в ядре "
+"FreeBSD:"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:465
+msgid "atomic operations and memory barriers"
+msgstr "атомарные операции и барьеры памяти"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:466
+msgid "locks"
+msgstr "блокировки"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:467
+msgid "scheduling barriers"
+msgstr "барьеры планирования"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:470
+msgid ""
+"Below there are descriptions for the 3 families. For every lock, you should "
+"really check the linked manpage (where possible) for more detailed "
+"explanations."
+msgstr ""
+"Ниже приведены описания для 3 семейств. Для каждой блокировки рекомендуется "
+"ознакомиться с соответствующей справочной страницей (где это возможно), "
+"чтобы получить более подробные объяснения."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:472
+#, no-wrap
+msgid "Atomic operations and memory barriers"
+msgstr "Атомарные операции и барьеры памяти"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:483
+msgid ""
+"Atomic operations are implemented through a set of functions performing "
+"simple arithmetics on memory operands in an atomic way with respect to "
+"external events (interrupts, preemption, etc.). Atomic operations can "
+"guarantee atomicity just on small data types (in the magnitude order of the "
+"`.long.` architecture C data type), so should be rarely used directly in the "
+"end-level code, if not only for very simple operations (like flag setting in "
+"a bitmap, for example). In fact, it is rather simple and common to write "
+"down a wrong semantic based on just atomic operations (usually referred as "
+"lock-less). The FreeBSD kernel offers a way to perform atomic operations in "
+"conjunction with a memory barrier. The memory barriers will guarantee that "
+"an atomic operation will happen following some specified ordering with "
+"respect to other memory accesses. For example, if we need that an atomic "
+"operation happen just after all other pending writes (in terms of "
+"instructions reordering buffers activities) are completed, we need to "
+"explicitly use a memory barrier in conjunction to this atomic operation. So "
+"it is simple to understand why memory barriers play a key role for higher-"
+"level locks building (just as refcounts, mutexes, etc.). For a detailed "
+"explanatory on atomic operations, please refer to man:atomic[9]. It is far, "
+"however, noting that atomic operations (and memory barriers as well) should "
+"ideally only be used for building front-ending locks (as mutexes)."
+msgstr ""
+"Атомарные операции реализуются через набор функций, выполняющих простые "
+"арифметические действия над операндами в памяти атомарным образом по "
+"отношению к внешним событиям (прерываниям, вытеснению и т. д.). Атомарные "
+"операции могут гарантировать атомарность только для небольших типов данных "
+"(порядка величины типа `.long` в архитектуре C), поэтому их следует редко "
+"использовать напрямую в конечном коде, разве что для очень простых операций "
+"(например, установки флага в битовой карте). На самом деле довольно просто и "
+"часто можно допустить семантическую ошибку, полагаясь только на атомарные "
+"операции (обычно называемые lock-less). Ядро FreeBSD предоставляет способ "
+"выполнения атомарных операций в сочетании с барьерами памяти. Барьеры памяти "
+"гарантируют, что атомарная операция произойдет в определенном порядке "
+"относительно других обращений к памяти. Например, если нам нужно, чтобы "
+"атомарная операция выполнилась только после завершения всех ожидающих "
+"операций записи (с точки зрения переупорядочивания буферов инструкций), нам "
+"необходимо явно использовать барьер памяти вместе с этой атомарной "
+"операцией. Таким образом, легко понять, почему барьеры памяти играют "
+"ключевую роль в построении высокоуровневых блокировок (таких как refcounts, "
+"мьютексы и т. д.). Для подробного объяснения атомарных операций обратитесь к "
+"man:atomic[9]. Однако важно отметить, что атомарные операции (и барьеры "
+"памяти тоже) в идеале должны использоваться только для построения фронтенд-"
+"блокировок (например, мьютексов)."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:485
+#, no-wrap
+msgid "Refcounts"
+msgstr "Счетчики ссылок (refcount)"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:491
+msgid ""
+"Refcounts are interfaces for handling reference counters. They are "
+"implemented through atomic operations and are intended to be used just for "
+"cases, where the reference counter is the only one thing to be protected, so "
+"even something like a spin-mutex is deprecated. Using the refcount "
+"interface for structures, where a mutex is already used is often wrong since "
+"we should probably close the reference counter in some already protected "
+"paths. A manpage discussing refcount does not exist currently, just check "
+"[.filename]#sys/refcount.h# for an overview of the existing API."
+msgstr ""
+"Счетчики ссылок (refcounts) — это интерфейсы для работы с подсчетом ссылок. "
+"Они реализованы с использованием атомарных операций и предназначены для "
+"случаев, когда счетчик ссылок — это единственное, что требует защиты, "
+"поэтому даже такие механизмы, как спин-мьютекс, не рекомендуются. "
+"Использование интерфейса refcount для структур, где уже применяется мьютекс, "
+"часто является ошибкой, так как, вероятно, следует защитить счетчик ссылок в "
+"рамках уже существующих защищенных участков кода. В настоящее время man-"
+"страница, посвященная refcount, отсутствует; для обзора существующего API "
+"обратитесь к [.filename]#sys/refcount.h#."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:493
+#, no-wrap
+msgid "Locks"
+msgstr "Блокировки"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:498
+msgid ""
+"FreeBSD kernel has huge classes of locks. Every lock is defined by some "
+"peculiar properties, but probably the most important is the event linked to "
+"contesting holders (or in other terms, the behavior of threads unable to "
+"acquire the lock). FreeBSD's locking scheme presents three different "
+"behaviors for contenders:"
+msgstr ""
+"Ядро FreeBSD имеет множество классов блокировок. Каждая блокировка "
+"определяется некоторыми уникальными свойствами, но, вероятно, наиболее "
+"важным является событие, связанное с конкурирующими владельцами (или, "
+"другими словами, поведение потоков, неспособных захватить блокировку). Схема "
+"блокировок FreeBSD предлагает три различных поведения для конкурирующих "
+"потоков:"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:500
+msgid "spinning"
+msgstr "вращающиеся"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:501
+msgid "blocking"
+msgstr "блокирующие"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:502
+msgid "sleeping"
+msgstr "спящие"
+
+#. type: delimited block = 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:506
+msgid "numbers are not casual"
+msgstr "номера приведены не случайно"
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:509
+#, no-wrap
+msgid "Spinning locks"
+msgstr "Вращающиеся блокировки"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:515
+msgid ""
+"Spin locks let waiters to spin until they cannot acquire the lock. An "
+"important matter do deal with is when a thread contests on a spin lock if it "
+"is not descheduled. Since the FreeBSD kernel is preemptive, this exposes "
+"spin lock at the risk of deadlocks that can be solved just disabling "
+"interrupts while they are acquired. For this and other reasons (like lack "
+"of priority propagation support, poorness in load balancing schemes between "
+"CPUs, etc.), spin locks are intended to protect very small paths of code, or "
+"ideally not to be used at all if not explicitly requested (explained later)."
+msgstr ""
+"Спин-блокировки позволяют ожидающим потокам продолжать работу (вращаться), "
+"пока они не смогут захватить блокировку. Важным аспектом является ситуация, "
+"когда поток соревнуется за спин-блокировку и не вытесняется. Поскольку ядро "
+"FreeBSD является вытесняющим, это подвергает спин-блокировки риску "
+"взаимоблокировок, которые можно устранить только отключением прерываний на "
+"время их удержания. По этой и другим причинам (таким как отсутствие "
+"поддержки распространения приоритетов, неэффективность схем балансировки "
+"нагрузки между CPU и т.д.), спин-блокировки предназначены для защиты очень "
+"небольших участков кода или, в идеале, не должны использоваться вовсе, если "
+"это не требуется явно (об этом далее)."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:517
+#, no-wrap
+msgid "Blocking"
+msgstr "Блокирующие"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:522
+msgid ""
+"Block locks let waiters to be descheduled and blocked until the lock owner "
+"does not drop it and wakes up one or more contenders. To avoid starvation "
+"issues, blocking locks do priority propagation from the waiters to the "
+"owner. Block locks must be implemented through the turnstile interface and "
+"are intended to be the most used kind of locks in the kernel, if no "
+"particular conditions are met."
+msgstr ""
+"Блокирующие блокировки позволяют ожидающим потокам быть выгруженными и "
+"заблокированными до тех пор, пока владелец блокировки не освободит её и не "
+"разбудит один или несколько конкурентов. Чтобы избежать проблем с "
+"голоданием, блокирующие блокировки передают приоритет от ожидающих к "
+"владельцу. Блокирующие блокировки должны быть реализованы через интерфейс "
+"турникета и предназначены для наиболее частого использования в ядре, если "
+"нет особых условий."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:524
+#, no-wrap
+msgid "Sleeping"
+msgstr "Спящие"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:529
+msgid ""
+"Sleep locks let waiters to be descheduled and fall asleep until the lock "
+"holder does not drop it and wakes up one or more waiters. Since sleep locks "
+"are intended to protect large paths of code and to cater asynchronous "
+"events, they do not do any form of priority propagation. They must be "
+"implemented through the man:sleepqueue[9] interface."
+msgstr ""
+"Спящие блокировки (с ожиданием) позволяют ожидающим потокам быть "
+"вытесненными и заснуть до тех пор, пока держатель блокировки не освободит её "
+"и не разбудит один или несколько ожидающих. Поскольку блокировки с ожиданием "
+"предназначены для защиты больших участков кода и обработки асинхронных "
+"событий, они не поддерживают распространение приоритетов. Они должны быть "
+"реализованы через интерфейс man:sleepqueue[9]."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:533
+msgid ""
+"The order used to acquire locks is very important, not only for the "
+"possibility to deadlock due at lock order reversals, but even because lock "
+"acquisition should follow specific rules linked to locks natures. If you "
+"give a look at the table above, the practical rule is that if a thread holds "
+"a lock of level n (where the level is the number listed close to the kind of "
+"lock) it is not allowed to acquire a lock of superior levels, since this "
+"would break the specified semantic for a path. For example, if a thread "
+"holds a block lock (level 2), it is allowed to acquire a spin lock (level 1) "
+"but not a sleep lock (level 3), since block locks are intended to protect "
+"smaller paths than sleep lock (these rules are not about atomic operations "
+"or scheduling barriers, however)."
+msgstr ""
+"Порядок захвата блокировок очень важен, не только из-за возможности "
+"взаимоблокировки при обратном порядке захвата, но и потому, что захват "
+"блокировок должен следовать определённым правилам, связанным с их природой. "
+"Если взглянуть на таблицу выше, практическое правило заключается в том, что "
+"если поток удерживает блокировку уровня n (где уровень — это число, "
+"указанное рядом с типом блокировки), ему запрещено захватывать блокировки "
+"более высоких уровней, так как это нарушит заданную семантику пути. "
+"Например, если поток удерживает блокирующую блокировку (уровень 2), ему "
+"разрешено захватывать спин-блокировку (уровень 1), но не спящую блокировку "
+"(уровень 3), поскольку блокирующие блокировки предназначены для защиты более "
+"коротких путей, чем спящие блокировки (однако эти правила не касаются "
+"атомарных операций или барьеров планирования)."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:535
+msgid "This is a list of lock with their respective behaviors:"
+msgstr "Вот список блокировок с соответствующими типами поведения:"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:537
+msgid "spin mutex - spinning - man:mutex[9]"
+msgstr "spin mutex – вращающийся режим – man:mutex[9]"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:538
+msgid "sleep mutex - blocking - man:mutex[9]"
+msgstr "sleep mutex – блокирующий режим – man:mutex[9]"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:539
+msgid "pool mutex - blocking - man:mtx[pool]"
+msgstr "pool mutex – блокирующий режим – man:mtx[pool]"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:540
+msgid ""
+"sleep family - sleeping - man:sleep[9] pause tsleep msleep msleep spin "
+"msleep rw msleep sx"
+msgstr ""
+"Семейство функций sleep – спящий режим – man:sleep[9] pause tsleep msleep "
+"msleep_spin msleep_rw msleep_sx"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:541
+msgid "condvar - sleeping - man:condvar[9]"
+msgstr "condvar – спящий режим – man:condvar[9]"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:542
+msgid "rwlock - blocking - man:rwlock[9]"
+msgstr "rwlock – блокирующий режим – man:rwlock[9]"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:543
+msgid "sxlock - sleeping - man:sx[9]"
+msgstr "sxlock – спящий режим – man:sx[9]"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:544
+msgid "lockmgr - sleeping - man:lockmgr[9]"
+msgstr "lockmgr – спящий режим – man:lockmgr[9]"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:545
+msgid "semaphores - sleeping - man:sema[9]"
+msgstr "семафоры – спящий режим – man:sema[9]"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:547
+msgid ""
+"Among these locks only mutexes, sxlocks, rwlocks and lockmgrs are intended "
+"to handle recursion, but currently recursion is only supported by mutexes "
+"and lockmgrs."
+msgstr ""
+"Среди этих блокировок только мьютексы, sxlock, rwlock и lockmgr "
+"предназначены для обработки рекурсии, но в настоящее время рекурсия "
+"поддерживается только мьютексами и lockmgr."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:549
+#, no-wrap
+msgid "Scheduling barriers"
+msgstr "Барьеры планирования"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:553
+msgid ""
+"Scheduling barriers are intended to be used to drive scheduling of "
+"threading. They consist mainly of three different stubs:"
+msgstr ""
+"Барьеры планирования предназначены для управления планированием потоков. Они "
+"в основном состоят из трех различных заглушек:"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:555
+msgid "critical sections (and preemption)"
+msgstr "критические секции (и вытеснение)"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:556
+msgid "sched_bind"
+msgstr "sched_bind"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:557
+msgid "sched_pin"
+msgstr "sched_pin"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:559
+msgid ""
+"Generally, these should be used only in a particular context and even if "
+"they can often replace locks, they should be avoided because they do not let "
+"the diagnose of simple eventual problems with locking debugging tools (as "
+"man:witness[4])."
+msgstr ""
+"Как правило, их следует использовать только в определённом контексте, и даже "
+"если они часто могут заменять блокировки, их следует избегать, поскольку они "
+"не позволяют диагностировать простые потенциальные проблемы с помощью "
+"инструментов отладки блокировок (например, man:witness[4])."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:561
+#, no-wrap
+msgid "Critical sections"
+msgstr "Критические секции"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:569
+msgid ""
+"The FreeBSD kernel has been made preemptive basically to deal with interrupt "
+"threads. In fact, to avoid high interrupt latency, time-sharing priority "
+"threads can be preempted by interrupt threads (in this way, they do not need "
+"to wait to be scheduled as the normal path previews). Preemption, however, "
+"introduces new racing points that need to be handled, as well. Often, to "
+"deal with preemption, the simplest thing to do is to completely disable it. "
+"A critical section defines a piece of code (borderlined by the pair of "
+"functions man:critical_enter[9] and man:critical_exit[9], where preemption "
+"is guaranteed to not happen (until the protected code is fully executed). "
+"This can often replace a lock effectively but should be used carefully to "
+"not lose the whole advantage that preemption brings."
+msgstr ""
+"В ядре FreeBSD была реализована вытесняющая многозадачность в основном для "
+"работы с потоками обработки прерываний. Фактически, чтобы избежать высокой "
+"задержки прерываний, потоки с приоритетом разделения времени могут быть "
+"вытеснены потоками обработки прерываний (таким образом, им не нужно ждать "
+"планирования, как это предусмотрено в обычном случае). Однако вытеснение "
+"также вводит новые точки гонки, которые необходимо обрабатывать. Часто для "
+"борьбы с вытеснением проще всего полностью отключить его. Критическая секция "
+"определяет участок кода (ограниченный парой функций man:critical_enter[9] и "
+"man:critical_exit[9]), где гарантируется отсутствие вытеснения (пока "
+"защищённый код не будет полностью выполнен). Это часто может эффективно "
+"заменить блокировку, но должно использоваться осторожно, чтобы не потерять "
+"все преимущества, которые даёт вытеснение."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:571
+#, no-wrap
+msgid "sched_pin/sched_unpin"
+msgstr "sched_pin/sched_unpin"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:577
+msgid ""
+"Another way to deal with preemption is the `sched_pin()` interface. If a "
+"piece of code is closed in the `sched_pin()` and `sched_unpin()` pair of "
+"functions it is guaranteed that the respective thread, even if it can be "
+"preempted, it will always be executed on the same CPU. Pinning is very "
+"effective in the particular case when we have to access at per-cpu datas and "
+"we assume other threads will not change those data. The latter condition "
+"will determine a critical section as a too strong condition for our code."
+msgstr ""
+"Еще один способ работы с вытеснением — это интерфейс `sched_pin()`. Если "
+"участок кода заключен между функциями `sched_pin()` и `sched_unpin()`, "
+"гарантируется, что соответствующий поток, даже если он может быть вытеснен, "
+"всегда будет выполняться на том же CPU. Закрепление очень эффективно в "
+"частном случае, когда нам необходимо обращаться к данным, привязанным к "
+"определенным CPU, и мы предполагаем, что другие потоки не изменят эти "
+"данные. Последнее условие делает критическую секцию избыточно строгим "
+"условием для нашего кода."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:579
+#, no-wrap
+msgid "sched_bind/sched_unbind"
+msgstr "sched_bind/sched_unbind"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:584
+msgid ""
+"`sched_bind` is an API used to bind a thread to a particular CPU for all the "
+"time it executes the code, until a `sched_unbind` function call does not "
+"unbind it. This feature has a key role in situations where you cannot trust "
+"the current state of CPUs (for example, at very early stages of boot), as "
+"you want to avoid your thread to migrate on inactive CPUs. Since "
+"`sched_bind` and `sched_unbind` manipulate internal scheduler structures, "
+"they need to be enclosed in `sched_lock` acquisition/releasing when used."
+msgstr ""
+"`sched_bind` — это API, используемый для привязки потока к определённому CPU "
+"на всё время выполнения кода, пока вызов функции `sched_unbind` не отменит "
+"эту привязку. Эта функция играет ключевую роль в ситуациях, когда нельзя "
+"доверять текущему состоянию CPU (например, на самых ранних этапах загрузки), "
+"так как требуется избежать миграции потока на неактивные CPU. Поскольку "
+"`sched_bind` и `sched_unbind` работают с внутренними структурами "
+"планировщика, их использование должно быть заключено в захват/освобождение "
+"`sched_lock`."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:586
+#, no-wrap
+msgid "Proc structure"
+msgstr "Структура proc"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:592
+msgid ""
+"Various emulation layers sometimes require some additional per-process "
+"data. It can manage separate structures (a list, a tree etc.) containing "
+"these data for every process but this tends to be slow and memory "
+"consuming. To solve this problem the FreeBSD `proc` structure contains "
+"`p_emuldata`, which is a void pointer to some emulation layer specific "
+"data. This `proc` entry is protected by the proc mutex."
+msgstr ""
+"Различные уровни эмуляции иногда требуют дополнительных данных для каждого "
+"процесса. Можно управлять отдельными структурами (списком, деревом и т.д.), "
+"содержащими эти данные для каждого процесса, но это может быть медленно и "
+"потреблять много памяти. Чтобы решить эту проблему, структура `proc` в "
+"FreeBSD содержит `p_emuldata` — указатель типа void на данные, специфичные "
+"для уровня эмуляции. Эта запись `proc` защищена мьютексом proc."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:597
+msgid ""
+"The FreeBSD `proc` structure contains a `p_sysent` entry that identifies, "
+"which ABI this process is running. In fact, it is a pointer to the "
+"`sysentvec` described above. So by comparing this pointer to the address "
+"where the `sysentvec` structure for the given ABI is stored we can "
+"effectively determine whether the process belongs to our emulation layer. "
+"The code typically looks like:"
+msgstr ""
+"Структура `proc` в FreeBSD содержит элемент `p_sysent`, который "
+"идентифицирует, под какой ABI работает данный процесс. Фактически, это "
+"указатель на упомянутый выше `sysentvec`. Таким образом, сравнивая этот "
+"указатель с адресом, по которому хранится структура `sysentvec` для данной "
+"ABI, мы можем эффективно определить, принадлежит ли процесс нашему "
+"эмуляционному слою. Код обычно выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:602
+#, no-wrap
+msgid ""
+"if (__predict_true(p->p_sysent != &elf_Linux(R)_sysvec))\n"
+"\t return;\n"
+msgstr ""
+"if (__predict_true(p->p_sysent != &elf_Linux(R)_sysvec))\n"
+"\t return;\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:606
+msgid ""
+"As you can see, we effectively use the `__predict_true` modifier to collapse "
+"the most common case (FreeBSD process) to a simple return operation thus "
+"preserving high performance. This code should be turned into a macro "
+"because currently it is not very flexible, i.e. we do not support Linux(R)64 "
+"emulation nor A.OUT Linux(R) processes on i386."
+msgstr ""
+"Как видите, мы эффективно используем модификатор `__predict_true`, чтобы "
+"свести наиболее распространённый случай (процесс FreeBSD) к простой операции "
+"возврата, сохраняя высокую производительность. Этот код следует "
+"преобразовать в макрос, поскольку в настоящее время он не очень гибкий, "
+"например, мы не поддерживаем эмуляцию Linux(R)64, а также процессы Linux(R) "
+"в формате A.OUT на архитектуре i386."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:608
+#, no-wrap
+msgid "VFS"
+msgstr "VFS"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:617
+msgid ""
+"The FreeBSD VFS subsystem is very complex but the Linux(R) emulation layer "
+"uses just a small subset via a well defined API. It can either operate on "
+"vnodes or file handlers. Vnode represents a virtual vnode, i.e. "
+"representation of a node in VFS. Another representation is a file handler, "
+"which represents an opened file from the perspective of a process. A file "
+"handler can represent a socket or an ordinary file. A file handler contains "
+"a pointer to its vnode. More then one file handler can point to the same "
+"vnode."
+msgstr ""
+"Подсистема VFS в FreeBSD очень сложна, но слой эмуляции Linux(R) использует "
+"лишь небольшую её часть через чётко определённый API. Она может работать как "
+"с vnode, так и с файловыми дескрипторами. Vnode представляет собой "
+"виртуальный vnode, то есть представление узла в VFS. Другое представление — "
+"это файловый дескриптор, который представляет открытый файл с точки зрения "
+"процесса. Файловый дескриптор может представлять сокет или обычный файл. "
+"Файловый дескриптор содержит указатель на свой vnode. Более одного файлового "
+"дескриптора могут указывать на один и тот же vnode."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:619
+#, no-wrap
+msgid "namei"
+msgstr "namei"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:626
+msgid ""
+"The man:namei[9] routine is a central entry point to pathname lookup and "
+"translation. It traverses the path point by point from the starting point "
+"to the end point using lookup function, which is internal to VFS. The "
+"man:namei[9] syscall can cope with symlinks, absolute and relative paths. "
+"When a path is looked up using man:namei[9] it is inputed to the name cache. "
+"This behavior can be suppressed. This routine is used all over the kernel "
+"and its performance is very critical."
+msgstr ""
+"Функция man:namei[9] является центральной точкой входа для поиска и "
+"преобразования путей. Она проходит по пути шаг за шагом от начальной до "
+"конечной точки, используя функцию поиска, которая является внутренней для "
+"VFS. Системный вызов man:namei[9] может обрабатывать символьные ссылки, "
+"абсолютные и относительные пути. Когда путь ищется с помощью man:namei[9], "
+"он заносится в кэш имён. Это поведение можно отключить. Данная функция "
+"используется повсеместно в ядре, и её производительность крайне важна."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:628
+#, no-wrap
+msgid "vn_fullpath"
+msgstr "vn_fullpath"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:634
+msgid ""
+"The man:vn_fullpath[9] function takes the best effort to traverse VFS name "
+"cache and returns a path for a given (locked) vnode. This process is "
+"unreliable but works just fine for the most common cases. The unreliability "
+"is because it relies on VFS cache (it does not traverse the on medium "
+"structures), it does not work with hardlinks, etc. This routine is used in "
+"several places in the Linuxulator."
+msgstr ""
+"Функция man:vn_fullpath[9] предпринимает максимальные усилия для обхода кэша "
+"имён VFS и возвращает путь для заданного (заблокированного) vnode. Этот "
+"процесс ненадёжен, но в большинстве типичных случаев работает корректно. "
+"Ненадёжность обусловлена тем, что функция опирается на кэш VFS (она не "
+"обходит структуры на носителе), не работает с жёсткими ссылками и т.д. "
+"Данная процедура используется в нескольких местах Linuxulator."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:636
+#, no-wrap
+msgid "Vnode operations"
+msgstr "Операции с vnode"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:639
+msgid ""
+"`fgetvp` - given a thread and a file descriptor number it returns the "
+"associated vnode"
+msgstr ""
+"`fgetvp` - по заданным потоку и номеру файлового дескриптора возвращает "
+"связанный vnode"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:640
+msgid "man:vn_lock[9] - locks a vnode"
+msgstr "man:vn_lock[9] - блокирует vnode"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:641
+msgid "`vn_unlock` - unlocks a vnode"
+msgstr "`vn_unlock` - разблокирует vnode"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:642
+msgid "man:VOP_READDIR[9] - reads a directory referenced by a vnode"
+msgstr "man:VOP_READDIR[9] - читает каталог, на который ссылается vnode"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:643
+msgid ""
+"man:VOP_GETATTR[9] - gets attributes of a file or a directory referenced by "
+"a vnode"
+msgstr ""
+"man:VOP_GETATTR[9] - получает атрибуты файла или каталога, на который "
+"ссылается vnode"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:644
+msgid "man:VOP_LOOKUP[9] - looks up a path to a given directory"
+msgstr "man:VOP_LOOKUP[9] - выполняет поиск пути к заданному каталогу"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:645
+msgid "man:VOP_OPEN[9] - opens a file referenced by a vnode"
+msgstr "man:VOP_OPEN[9] - открывает файл, на который ссылается vnode"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:646
+msgid "man:VOP_CLOSE[9] - closes a file referenced by a vnode"
+msgstr "man:VOP_CLOSE[9] - закрывает файл, на который ссылается vnode"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:647
+msgid "man:vput[9] - decrements the use count for a vnode and unlocks it"
+msgstr ""
+"man:vput[9] - уменьшает счетчик использования для vnode и разблокирует его"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:648
+msgid "man:vrele[9] - decrements the use count for a vnode"
+msgstr "man:vrele[9] - уменьшает счетчик использования для vnode"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:649
+msgid "man:vref[9] - increments the use count for a vnode"
+msgstr "man:vref[9] - увеличивает счетчик использования для vnode"
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:651
+#, no-wrap
+msgid "File handler operations"
+msgstr "Операции обработчика файлов (handler)"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:654
+msgid ""
+"`fget` - given a thread and a file descriptor number it returns associated "
+"file handler and references it"
+msgstr ""
+"`fget` - для заданного потока и номера файлового дескриптора возвращает "
+"связанный обработчик файла и делает на него ссылку"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:655
+msgid "`fdrop` - drops a reference to a file handler"
+msgstr "`fdrop` - освобождает ссылку на обработчик файлов"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:656
+msgid "`fhold` - references a file handler"
+msgstr "`fhold` - ссылается на обработчик файла"
+
+#. type: Title ==
+#: documentation/content/en/articles/linux-emulation/_index.adoc:658
+#, no-wrap
+msgid "Linux(R) emulation layer -MD part"
+msgstr "Слой эмуляции Linux(R) - машинно-зависимая часть"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:666
+msgid ""
+"This section deals with implementation of Linux(R) emulation layer in "
+"FreeBSD operating system. It first describes the machine dependent part "
+"talking about how and where interaction between userland and kernel is "
+"implemented. It talks about syscalls, signals, ptrace, traps, stack fixup. "
+"This part discusses i386 but it is written generally so other architectures "
+"should not differ very much. The next part is the machine independent part "
+"of the Linuxulator. This section only covers i386 and ELF handling. A.OUT "
+"is obsolete and untested."
+msgstr ""
+"В этом разделе рассматривается реализация слоя эмуляции Linux(R) в "
+"операционной системе FreeBSD. Сначала описывается машинно-зависимая часть, "
+"рассказывающая о том, как и где реализовано взаимодействие между "
+"пользовательским пространством и ядром. Рассматриваются системные вызовы, "
+"сигналы, ptrace, ловушки и исправление стека. Эта часть посвящена "
+"архитектуре i386, но написана в общем виде, поэтому другие архитектуры не "
+"должны сильно отличаться. Следующая часть — машинно-независимая часть "
+"Linuxulator. Этот раздел охватывает только i386 и обработку ELF. A.OUT "
+"устарел и не поддерживается."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:668
+#, no-wrap
+msgid "Syscall handling"
+msgstr "Обработка системных вызовов"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:672
+msgid ""
+"Syscall handling is mostly written in [.filename]#linux_sysvec.c#, which "
+"covers most of the routines pointed out in the `sysentvec` structure. When "
+"a Linux(R) process running on FreeBSD issues a syscall, the general syscall "
+"routine calls linux prepsyscall routine for the Linux(R) ABI."
+msgstr ""
+"Обработка системных вызовов в основном реализована в файле "
+"[.filename]#linux_sysvec.c#, который покрывает большинство процедур, "
+"указанных в структуре `sysentvec`. Когда процесс Linux(R), выполняющийся на "
+"FreeBSD, делает системный вызов, общая процедура обработки системных вызовов "
+"вызывает linux prepsyscall для ABI Linux(R)."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:674
+#, no-wrap
+msgid "Linux(R) prepsyscall"
+msgstr "Linux(R) prepsyscall"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:681
+msgid ""
+"Linux(R) passes arguments to syscalls via registers (that is why it is "
+"limited to 6 parameters on i386) while FreeBSD uses the stack. The Linux(R) "
+"prepsyscall routine must copy parameters from registers to the stack. The "
+"order of the registers is: `%ebx`, `%ecx`, `%edx`, `%esi`, `%edi`, `%ebp`. "
+"The catch is that this is true for only _most_ of the syscalls. Some (most "
+"notably `clone`) uses a different order but it is luckily easy to fix by "
+"inserting a dummy parameter in the `linux_clone` prototype."
+msgstr ""
+"Linux(R) передает аргументы системных вызовов через регистры (поэтому на "
+"i386 ограничено 6 параметрами), тогда как FreeBSD использует стек. "
+"Подпрограмма Linux(R) `prepsyscall` должна копировать параметры из регистров "
+"в стек. Порядок регистров следующий: `%ebx`, `%ecx`, `%edx`, `%esi`, "
+"`%edi`, `%ebp`. Однако это верно только для _большинства_ системных "
+"вызовов. Некоторые (особенно `clone`) используют другой порядок, но это, к "
+"счастью, легко исправить, добавив фиктивный параметр в прототип "
+"`linux_clone`."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:683
+#, no-wrap
+msgid "Syscall writing"
+msgstr "Как писать системные вызовы"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:687
+msgid ""
+"Every syscall implemented in the Linuxulator must have its prototype with "
+"various flags in [.filename]#syscalls.master#. The form of the file is:"
+msgstr ""
+"Каждый системный вызов, реализованный в Linuxulator, должен иметь свой "
+"прототип с различными флагами в [.filename]#syscalls.master#. Формат файла "
+"следующий:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:695
+#, no-wrap
+msgid ""
+"...\n"
+"\tAUE_FORK STD\t\t{ int linux_fork(void); }\n"
+"...\n"
+"\tAUE_CLOSE NOPROTO\t{ int close(int fd); }\n"
+"...\n"
+msgstr ""
+"...\n"
+"\tAUE_FORK STD\t\t{ int linux_fork(void); }\n"
+"...\n"
+"\tAUE_CLOSE NOPROTO\t{ int close(int fd); }\n"
+"...\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:705
+msgid ""
+"The first column represents the syscall number. The second column is for "
+"auditing support. The third column represents the syscall type. It is "
+"either `STD`, `OBSOL`, `NOPROTO` and `UNIMPL`. `STD` is a standard syscall "
+"with full prototype and implementation. `OBSOL` is obsolete and defines "
+"just the prototype. `NOPROTO` means that the syscall is implemented "
+"elsewhere so do not prepend ABI prefix, etc. `UNIMPL` means that the "
+"syscall will be substituted with the `nosys` syscall (a syscall just "
+"printing out a message about the syscall not being implemented and returning "
+"`ENOSYS`)."
+msgstr ""
+"Первый столбец представляет номер системного вызова. Второй столбец "
+"предназначен для поддержки аудита. Третий столбец обозначает тип системного "
+"вызова. Он может быть `STD`, `OBSOL`, `NOPROTO` или `UNIMPL`. `STD` — это "
+"стандартный системный вызов с полным прототипом и реализацией. `OBSOL` "
+"означает устаревший вызов и определяет только прототип. `NOPROTO` означает, "
+"что системный вызов реализован в другом месте, поэтому не требует добавления "
+"префикса ABI и т.д. `UNIMPL` означает, что системный вызов будет заменён на "
+"`nosys` (системный вызов, который просто выводит сообщение о том, что вызов "
+"не реализован, и возвращает `ENOSYS`)."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:708
+msgid ""
+"From [.filename]#syscalls.master# a script generates three files: "
+"[.filename]#linux_syscall.h#, [.filename]#linux_proto.h# and "
+"[.filename]#linux_sysent.c#. The [.filename]#linux_syscall.h# contains "
+"definitions of syscall names and their numerical value, e.g.:"
+msgstr ""
+"Из файла [.filename]#syscalls.master# скрипт генерирует три файла: "
+"[.filename]#linux_syscall.h#, [.filename]#linux_proto.h# и "
+"[.filename]#linux_sysent.c#. Файл [.filename]#linux_syscall.h# содержит "
+"определения имен системных вызовов и их числовых значений, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:716
+#, no-wrap
+msgid ""
+"...\n"
+"#define LINUX_SYS_linux_fork 2\n"
+"...\n"
+"#define LINUX_SYS_close 6\n"
+"...\n"
+msgstr ""
+"...\n"
+"#define LINUX_SYS_linux_fork 2\n"
+"...\n"
+"#define LINUX_SYS_close 6\n"
+"...\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:719
+msgid ""
+"The [.filename]#linux_proto.h# contains structure definitions of arguments "
+"to every syscall, e.g.:"
+msgstr ""
+"[.filename]#linux_proto.h# содержит определения структур аргументов для "
+"каждого системного вызова, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:725
+#, no-wrap
+msgid ""
+"struct linux_fork_args {\n"
+" register_t dummy;\n"
+"};\n"
+msgstr ""
+"struct linux_fork_args {\n"
+" register_t dummy;\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:728
+msgid ""
+"And finally, [.filename]#linux_sysent.c# contains structure describing the "
+"system entry table, used to actually dispatch a syscall, e.g.:"
+msgstr ""
+"И, наконец, [.filename]#linux_sysent.c# содержит структуру, описывающую "
+"таблицу системных вызовов, используемую для фактической диспетчеризации "
+"системного вызова, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:733
+#, no-wrap
+msgid ""
+"{ 0, (sy_call_t *)linux_fork, AUE_FORK, NULL, 0, 0 }, /* 2 = linux_fork */\n"
+"{ AS(close_args), (sy_call_t *)close, AUE_CLOSE, NULL, 0, 0 }, /* 6 = close */\n"
+msgstr ""
+"{ 0, (sy_call_t *)linux_fork, AUE_FORK, NULL, 0, 0 }, /* 2 = linux_fork */\n"
+"{ AS(close_args), (sy_call_t *)close, AUE_CLOSE, NULL, 0, 0 }, /* 6 = close */\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:737
+msgid ""
+"As you can see `linux_fork` is implemented in Linuxulator itself so the "
+"definition is of `STD` type and has no argument, which is exhibited by the "
+"dummy argument structure. On the other hand `close` is just an alias for "
+"real FreeBSD man:close[2] so it has no linux arguments structure associated "
+"and in the system entry table it is not prefixed with linux as it calls the "
+"real man:close[2] in the kernel."
+msgstr ""
+"Как видно, `linux_fork` реализован в самом Linuxulator, поэтому определение "
+"имеет тип `STD` и не имеет аргументов, что демонстрируется структурой-"
+"заглушкой. С другой стороны, `close` — это просто псевдоним для настоящего "
+"FreeBSD man:close[2], поэтому у него нет связанной структуры аргументов "
+"Linux, и в системной таблице вызовов он не имеет префикса linux, так как "
+"вызывает настоящий man:close[2] в ядре."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:739
+#, no-wrap
+msgid "Dummy syscalls"
+msgstr "Нереализованные системные вызовы"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:745
+msgid ""
+"The Linux(R) emulation layer is not complete, as some syscalls are not "
+"implemented properly and some are not implemented at all. The emulation "
+"layer employs a facility to mark unimplemented syscalls with the `DUMMY` "
+"macro. These dummy definitions reside in [.filename]#linux_dummy.c# in a "
+"form of `DUMMY(syscall);`, which is then translated to various syscall "
+"auxiliary files and the implementation consists of printing a message saying "
+"that this syscall is not implemented. The `UNIMPL` prototype is not used "
+"because we want to be able to identify the name of the syscall that was "
+"called to know what syscalls are more important to implement."
+msgstr ""
+"Слой эмуляции Linux(R) не является полным, так как некоторые системные "
+"вызовы реализованы неправильно, а некоторые не реализованы вовсе. В слое "
+"эмуляции используется механизм для пометки нереализованных системных вызовов "
+"с помощью макроса `DUMMY`. Эти заглушки находятся в файле "
+"[.filename]#linux_dummy.c# в форме `DUMMY(syscall);`, которые затем "
+"преобразуются в различные вспомогательные файлы системных вызовов, а их "
+"реализация сводится к выводу сообщения о том, что данный системный вызов не "
+"реализован. Прототип `UNIMPL` не используется, потому что мы хотим иметь "
+"возможность идентифицировать имя вызванного системного вызова, чтобы "
+"понимать, какие системные вызовы более важны для реализации."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:747
+#, no-wrap
+msgid "Signal handling"
+msgstr "Обработка сигналов"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:751
+msgid ""
+"Signal handling is done generally in the FreeBSD kernel for all binary "
+"compatibilities with a call to a compat-dependent layer. Linux(R) "
+"compatibility layer defines `linux_sendsig` routine for this purpose."
+msgstr ""
+"Обработка сигналов обычно выполняется в ядре FreeBSD для всех вариантов "
+"бинарной совместимости с помощью вызова уровня, зависящего от совместимости. "
+"Слой совместимости Linux(R) определяет для этой цели процедуру "
+"`linux_sendsig`."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:753
+#, no-wrap
+msgid "Linux(R) sendsig"
+msgstr "Linux(R) sendsig"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:760
+msgid ""
+"This routine first checks whether the signal has been installed with a "
+"`SA_SIGINFO` in which case it calls `linux_rt_sendsig` routine instead. "
+"Furthermore, it allocates (or reuses an already existing) signal handle "
+"context, then it builds a list of arguments for the signal handler. It "
+"translates the signal number based on the signal translation table, assigns "
+"a handler, translates sigset. Then it saves context for the `sigreturn` "
+"routine (various registers, translated trap number and signal mask). "
+"Finally, it copies out the signal context to the userspace and prepares "
+"context for the actual signal handler to run."
+msgstr ""
+"Эта процедура сначала проверяет, установлен ли сигнал с флагом `SA_SIGINFO`, "
+"в таком случае она вызывает процедуру `linux_rt_sendsig` вместо текущей. "
+"Далее она выделяет (или повторно использует уже существующий) контекст "
+"обработчика сигнала, затем формирует список аргументов для обработчика "
+"сигнала. Она преобразует номер сигнала на основе таблицы преобразования "
+"сигналов, назначает обработчик, преобразует sigset. Затем она сохраняет "
+"контекст для процедуры `sigreturn` (различные регистры, преобразованный "
+"номер trap и маску сигналов). Наконец, она копирует контекст сигнала в "
+"пользовательское пространство и подготавливает контекст для фактического "
+"выполнения обработчика сигнала."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:762
+#, no-wrap
+msgid "linux_rt_sendsig"
+msgstr "linux_rt_sendsig"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:767
+msgid ""
+"This routine is similar to `linux_sendsig` just the signal context "
+"preparation is different. It adds `siginfo`, `ucontext`, and some POSIX(R) "
+"parts. It might be worth considering whether those two functions could not "
+"be merged with a benefit of less code duplication and possibly even faster "
+"execution."
+msgstr ""
+"Эта процедура аналогична `linux_sendsig`, только подготовка контекста "
+"сигнала отличается. Она добавляет `siginfo`, `ucontext` и некоторые части "
+"POSIX(R). Стоит рассмотреть возможность объединения этих двух функций с "
+"выгодой в виде меньшего дублирования кода и, возможно, даже более быстрого "
+"выполнения."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:769
+#, no-wrap
+msgid "linux_sigreturn"
+msgstr "linux_sigreturn"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:774
+msgid ""
+"This syscall is used for return from the signal handler. It does some "
+"security checks and restores the original process context. It also unmasks "
+"the signal in process signal mask."
+msgstr ""
+"Этот системный вызов используется для возврата из обработчика сигнала. Он "
+"выполняет некоторые проверки безопасности и восстанавливает исходный "
+"контекст процесса. Также он разблокирует сигнал в маске сигналов процесса."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:776
+#, no-wrap
+msgid "Ptrace"
+msgstr "Ptrace"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:782
+msgid ""
+"Many UNIX(R) derivates implement the man:ptrace[2] syscall to allow various "
+"tracking and debugging features. This facility enables the tracing process "
+"to obtain various information about the traced process, like register dumps, "
+"any memory from the process address space, etc. and also to trace the "
+"process like in stepping an instruction or between system entries (syscalls "
+"and traps). man:ptrace[2] also lets you set various information in the "
+"traced process (registers etc.). man:ptrace[2] is a UNIX(R)-wide standard "
+"implemented in most UNIX(R)es around the world."
+msgstr ""
+"Многие производные UNIX(R) реализуют системный вызов man:ptrace[2] для "
+"обеспечения различных функций отслеживания и отладки. Этот механизм "
+"позволяет трассирующему процессу получать различную информацию о "
+"трассируемом процессе, такую как дампы регистров, любую память из адресного "
+"пространства процесса и т.д., а также трассировать процесс, например, "
+"пошагово выполнять инструкции или между системными вызовами (сисколлами и "
+"ловушками). man:ptrace[2] также позволяет устанавливать различную информацию "
+"в трассируемом процессе (регистры и т.д.). man:ptrace[2] является стандартом "
+"для UNIX(R), реализованным в большинстве UNIX(R)-систем по всему миру."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:788
+msgid ""
+"Linux(R) emulation in FreeBSD implements the man:ptrace[2] facility in "
+"[.filename]#linux_ptrace.c#. The routines for converting registers between "
+"Linux(R) and FreeBSD and the actual man:ptrace[2] syscall emulation "
+"syscall. The syscall is a long switch block that implements its counterpart "
+"in FreeBSD for every man:ptrace[2] command. The man:ptrace[2] commands are "
+"mostly equal between Linux(R) and FreeBSD so usually just a small "
+"modification is needed. For example, `PT_GETREGS` in Linux(R) operates on "
+"direct data while FreeBSD uses a pointer to the data so after performing a "
+"(native) man:ptrace[2] syscall, a copyout must be done to preserve Linux(R) "
+"semantics."
+msgstr ""
+"Эмуляция Linux(R) в FreeBSD реализует механизм man:ptrace[2] в файле "
+"[.filename]#linux_ptrace.c#. Функции для преобразования регистров между "
+"Linux(R) и FreeBSD и фактический системный вызов эмуляции man:ptrace[2]. "
+"Системный вызов представляет собой длинный блок switch, который реализует "
+"свой аналог в FreeBSD для каждой команды man:ptrace[2]. Команды "
+"man:ptrace[2] в основном одинаковы между Linux(R) и FreeBSD, поэтому обычно "
+"требуется лишь небольшая модификация. Например, `PT_GETREGS` в Linux(R) "
+"работает с непосредственными данными, в то время как FreeBSD использует "
+"указатель на данные, поэтому после выполнения (нативного) системного вызова "
+"man:ptrace[2] необходимо выполнить copyout для сохранения семантики Linux(R)."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:792
+msgid ""
+"The man:ptrace[2] implementation in Linuxulator has some known weaknesses. "
+"There have been panics seen when using `strace` (which is a man:ptrace[2] "
+"consumer) in the Linuxulator environment. Also `PT_SYSCALL` is not "
+"implemented."
+msgstr ""
+"Реализация man:ptrace[2] в Linuxulator имеет известные недостатки. "
+"Наблюдались паники при использовании `strace` (который является потребителем "
+"man:ptrace[2]) в среде Linuxulator. Также `PT_SYSCALL` не реализован."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:799
+msgid ""
+"Whenever a Linux(R) process running in the emulation layer traps the trap "
+"itself is handled transparently with the only exception of the trap "
+"translation. Linux(R) and FreeBSD differs in opinion on what a trap is so "
+"this is dealt with here. The code is actually very short:"
+msgstr ""
+"Всякий раз, когда процесс Linux(R), выполняющийся в слое эмуляции, вызывает "
+"прерывание (trap), само прерывание обрабатывается прозрачно, за исключением "
+"преобразования прерывания. Linux(R) и FreeBSD расходятся во мнениях "
+"относительно того, что является прерыванием, поэтому этот вопрос решается "
+"здесь. Код на самом деле очень короткий:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:805
+#, no-wrap
+msgid ""
+"static int\n"
+"translate_traps(int signal, int trap_code)\n"
+"{\n"
+msgstr ""
+"static int\n"
+"translate_traps(int signal, int trap_code)\n"
+"{\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:808
+#, no-wrap
+msgid ""
+" if (signal != SIGBUS)\n"
+" return signal;\n"
+msgstr ""
+" if (signal != SIGBUS)\n"
+" return signal;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:810
+#, no-wrap
+msgid " switch (trap_code) {\n"
+msgstr " switch (trap_code) {\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:816
+#, no-wrap
+msgid ""
+" case T_PROTFLT:\n"
+" case T_TSSFLT:\n"
+" case T_DOUBLEFLT:\n"
+" case T_PAGEFLT:\n"
+" return SIGSEGV;\n"
+msgstr ""
+" case T_PROTFLT:\n"
+" case T_TSSFLT:\n"
+" case T_DOUBLEFLT:\n"
+" case T_PAGEFLT:\n"
+" return SIGSEGV;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:821
+#, no-wrap
+msgid ""
+" default:\n"
+" return signal;\n"
+" }\n"
+"}\n"
+msgstr ""
+" default:\n"
+" return signal;\n"
+" }\n"
+"}\n"
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:824
+#, no-wrap
+msgid "Stack fixup"
+msgstr "Исправление стека"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:831
+msgid ""
+"The RTLD run-time link-editor expects so called AUX tags on stack during an "
+"`execve` so a fixup must be done to ensure this. Of course, every RTLD "
+"system is different so the emulation layer must provide its own stack fixup "
+"routine to do this. So does Linuxulator. The `elf_linux_fixup` simply "
+"copies out AUX tags to the stack and adjusts the stack of the user space "
+"process to point right after those tags. So RTLD works in a smart way."
+msgstr ""
+"Динамический редактор связей RTLD ожидает так называемые AUX-теги на стеке "
+"во время выполнения `execve`, поэтому необходимо выполнить исправление, "
+"чтобы это обеспечить. Конечно, каждая система RTLD отличается, поэтому "
+"уровень эмуляции должен предоставлять собственную процедуру исправления "
+"стека. Linuxulator делает именно это. Функция `elf_linux_fixup` просто "
+"копирует AUX-теги на стек и корректирует стек пользовательского процесса, "
+"чтобы он указывал сразу после этих тегов. Таким образом, RTLD работает умным "
+"способом."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:833
+#, no-wrap
+msgid "A.OUT support"
+msgstr "Поддержка A.OUT"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:840
+msgid ""
+"The Linux(R) emulation layer on i386 also supports Linux(R) A.OUT binaries. "
+"Pretty much everything described in the previous sections must be "
+"implemented for A.OUT support (beside traps translation and signals "
+"sending). The support for A.OUT binaries is no longer maintained, "
+"especially the 2.6 emulation does not work with it but this does not cause "
+"any problem, as the linux-base in ports probably do not support A.OUT "
+"binaries at all. This support will probably be removed in future. Most of "
+"the stuff necessary for loading Linux(R) A.OUT binaries is in "
+"[.filename]#imgact_linux.c# file."
+msgstr ""
+"Эмуляционный слой Linux(R) на i386 также поддерживает бинарные файлы "
+"Linux(R) в формате A.OUT. Почти всё, что описано в предыдущих разделах, "
+"должно быть реализовано для поддержки A.OUT (кроме перевода ловушек и "
+"отправки сигналов). Поддержка бинарных файлов A.OUT больше не "
+"поддерживается, в частности, эмуляция 2.6 с ними не работает, но это не "
+"вызывает никаких проблем, так как linux-base в портах, вероятно, вообще не "
+"поддерживает бинарные файлы A.OUT. Эта поддержка, скорее всего, будет "
+"удалена в будущем. Большая часть кода, необходимого для загрузки бинарных "
+"файлов Linux(R) A.OUT, находится в файле [.filename]#imgact_linux.c#."
+
+#. type: Title ==
+#: documentation/content/en/articles/linux-emulation/_index.adoc:842
+#, no-wrap
+msgid "Linux(R) emulation layer -MI part"
+msgstr "Слой эмуляции Linux(R) - машино-независимая часть"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:847
+msgid ""
+"This section talks about machine independent part of the Linuxulator. It "
+"covers the emulation infrastructure needed for Linux(R) 2.6 emulation, the "
+"thread local storage (TLS) implementation (on i386) and futexes. Then we "
+"talk briefly about some syscalls."
+msgstr ""
+"В этом разделе рассматривается машинно-независимая часть Linuxulator. Он "
+"охватывает инфраструктуру эмуляции, необходимую для эмуляции Linux(R) 2.6, "
+"реализацию thread local storage (TLS) (на i386) и фьютексы. Затем мы кратко "
+"обсуждаем некоторые системные вызовы."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:849
+#, no-wrap
+msgid "Description of NPTL"
+msgstr "Описание NPTL"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:857
+msgid ""
+"One of the major areas of progress in development of Linux(R) 2.6 was "
+"threading. Prior to 2.6, the Linux(R) threading support was implemented in "
+"the linuxthreads library. The library was a partial implementation of "
+"POSIX(R) threading. The threading was implemented using separate processes "
+"for each thread using the `clone` syscall to let them share the address "
+"space (and other things). The main weaknesses of this approach was that "
+"every thread had a different PID, signal handling was broken (from the "
+"pthreads perspective), etc. Also the performance was not very good (use of "
+"`SIGUSR` signals for threads synchronization, kernel resource consumption, "
+"etc.) so to overcome these problems a new threading system was developed and "
+"named NPTL."
+msgstr ""
+"Одним из основных направлений прогресса в разработке Linux(R) 2.6 стала "
+"поддержка потоков. До версии 2.6 поддержка потоков в Linux(R) "
+"реализовывалась в библиотеке linuxthreads. Эта библиотека представляла собой "
+"частичную реализацию потоков POSIX(R). Потоки создавались как отдельные "
+"процессы с использованием системного вызова `clone`, что позволяло им "
+"разделять адресное пространство (и другие ресурсы). Основными недостатками "
+"такого подхода были разные PID для каждого потока, некорректная обработка "
+"сигналов (с точки зрения pthreads) и т.д. Кроме того, производительность "
+"оставляла желать лучшего (использование сигналов `SIGUSR` для синхронизации "
+"потоков, потребление ресурсов ядра и т.п.), поэтому для решения этих проблем "
+"была разработана новая система потоков под названием NPTL."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:863
+msgid ""
+"The NPTL library focused on two things but a third thing came along so it is "
+"usually considered a part of NPTL. Those two things were embedding of "
+"threads into a process structure and futexes. The additional third thing "
+"was TLS, which is not directly required by NPTL but the whole NPTL userland "
+"library depends on it. Those improvements yielded in much improved "
+"performance and standards conformance. NPTL is a standard threading library "
+"in Linux(R) systems these days."
+msgstr ""
+"Библиотека NPTL была сосредоточена на двух вещах, но появилась третья, "
+"поэтому её обычно считают частью NPTL. Этими двумя вещами были встраивание "
+"потоков в структуру процесса и фьютекс. Дополнительной третьей вещью стал "
+"TLS, который не требуется напрямую NPTL, но вся пользовательская библиотека "
+"NPTL зависит от него. Эти улучшения привели к значительному росту "
+"производительности и соответствию стандартам. В настоящее время NPTL "
+"является стандартной библиотекой потоков в системах Linux(R)."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:867
+msgid ""
+"The FreeBSD Linuxulator implementation approaches the NPTL in three main "
+"areas. The TLS, futexes and PID mangling, which is meant to simulate the "
+"Linux(R) threads. Further sections describe each of these areas."
+msgstr ""
+"Реализация Linuxulator в FreeBSD подходит к NPTL в трёх основных "
+"направлениях: TLS, фьютекс и изменение PID, что предназначено для эмуляции "
+"потоков Linux(R). В следующих разделах описывается каждое из этих "
+"направлений."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:869
+#, no-wrap
+msgid "Linux(R) 2.6 emulation infrastructure"
+msgstr "Инфраструктура эмуляции Linux(R) 2.6"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:872
+msgid ""
+"These sections deal with the way Linux(R) threads are managed and how we "
+"simulate that in FreeBSD."
+msgstr ""
+"Эти разделы посвящены тому, как управляются потоки Linux(R) и как мы "
+"моделируем это в FreeBSD."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:874
+#, no-wrap
+msgid "Runtime determining of 2.6 emulation"
+msgstr "Определение эмуляции 2.6 во время выполнения"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:883
+msgid ""
+"The Linux(R) emulation layer in FreeBSD supports runtime setting of the "
+"emulated version. This is done via man:sysctl[8], namely "
+"`compat.linux.osrelease`. Setting this man:sysctl[8] affects runtime "
+"behavior of the emulation layer. When set to 2.6.x it sets the value of "
+"`linux_use_linux26` while setting to something else keeps it unset. This "
+"variable (plus per-prison variables of the very same kind) determines "
+"whether 2.6 infrastructure (mainly PID mangling) is used in the code or "
+"not. The version setting is done system-wide and this affects all Linux(R) "
+"processes. The man:sysctl[8] should not be changed when running any "
+"Linux(R) binary as it might harm things."
+msgstr ""
+"Слой эмуляции Linux(R) в FreeBSD поддерживает динамическую настройку "
+"эмулируемой версии. Это выполняется с помощью man:sysctl[8], а именно "
+"`compat.linux.osrelease`. Установка этого man:sysctl[8] влияет на поведение "
+"слоя эмуляции во время выполнения. При установке значения 2.6.x "
+"устанавливается переменная `linux_use_linux26`, а при установке другого "
+"значения она остаётся сброшенной. Эта переменная (а также аналогичные "
+"переменные для каждой клетки) определяет, используется ли в коде "
+"инфраструктура 2.6 (в основном, преобразование PID). Настройка версии "
+"применяется глобально для всей системы и влияет на все процессы Linux(R). Не "
+"следует изменять man:sysctl[8] во время выполнения любого бинарного файла "
+"Linux(R), так как это может привести к проблемам."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:885
+#, no-wrap
+msgid "Linux(R) processes and thread identifiers"
+msgstr "Идентификаторы процессов и потоков Linux(R)"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:892
+msgid ""
+"The semantics of Linux(R) threading are a little confusing and uses entirely "
+"different nomenclature to FreeBSD. A process in Linux(R) consists of a "
+"`struct task` embedding two identifier fields - PID and TGID. PID is _not_ "
+"a process ID but it is a thread ID. The TGID identifies a thread group in "
+"other words a process. For single-threaded process the PID equals the TGID."
+msgstr ""
+"Семантика потоков в Linux(R) немного запутанная и использует совершенно "
+"другую терминологию по сравнению с FreeBSD. Процесс в Linux(R) состоит из "
+"`struct task`, включающей два поля идентификаторов — PID и TGID. PID — это "
+"_не_ идентификатор процесса, а идентификатор потока. TGID идентифицирует "
+"группу потоков, другими словами, процесс. Для однопоточного процесса PID "
+"равен TGID."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:898
+msgid ""
+"The thread in NPTL is just an ordinary process that happens to have TGID not "
+"equal to PID and have a group leader not equal to itself (and shared VM etc. "
+"of course). Everything else happens in the same way as to an ordinary "
+"process. There is no separation of a shared status to some external "
+"structure like in FreeBSD. This creates some duplication of information and "
+"possible data inconsistency. The Linux(R) kernel seems to use task -> group "
+"information in some places and task information elsewhere and it is really "
+"not very consistent and looks error-prone."
+msgstr ""
+"Поток в NPTL — это обычный процесс, у которого TGID не равен PID и есть "
+"групповой лидер, отличный от него самого (и, конечно, общая виртуальная "
+"память и т.д.). Все остальное происходит так же, как и с обычным процессом. "
+"Нет разделения общего состояния на внешнюю структуру, как в FreeBSD. Это "
+"создает некоторое дублирование информации и возможную несогласованность "
+"данных. Ядро Linux(R), похоже, использует информацию о задаче -> группе в "
+"одних местах и информацию о задаче в других, что не очень последовательно и "
+"выглядит небезопасно с точки зрения возможных ошибок."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:901
+msgid ""
+"Every NPTL thread is created by a call to the `clone` syscall with a "
+"specific set of flags (more in the next subsection). The NPTL implements "
+"strict 1:1 threading."
+msgstr ""
+"Каждый поток NPTL создается вызовом системного вызова `clone` с определенным "
+"набором флагов (подробнее в следующем подразделе). NPTL реализует строгую "
+"модель потоков 1:1."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:903
+msgid ""
+"In FreeBSD we emulate NPTL threads with ordinary FreeBSD processes that "
+"share VM space, etc. and the PID gymnastic is just mimicked in the emulation "
+"specific structure attached to the process. The structure attached to the "
+"process looks like:"
+msgstr ""
+"В FreeBSD мы эмулируем потоки NPTL с помощью обычных процессов FreeBSD, "
+"которые разделяют виртуальную память и т.д., а гимнастика с PID просто "
+"имитируется в специфической для эмуляции структуре, прикреплённой к "
+"процессу. Структура, прикреплённая к процессу, выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:908
+#, no-wrap
+msgid ""
+"struct linux_emuldata {\n"
+" pid_t pid;\n"
+msgstr ""
+"struct linux_emuldata {\n"
+" pid_t pid;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:911
+#, no-wrap
+msgid ""
+" int *child_set_tid; /* in clone(): Child.s TID to set on clone */\n"
+" int *child_clear_tid;/* in clone(): Child.s TID to clear on exit */\n"
+msgstr ""
+" int *child_set_tid; /* in clone(): Child.s TID to set on clone */\n"
+" int *child_clear_tid;/* in clone(): Child.s TID to clear on exit */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:913
+#, no-wrap
+msgid " struct linux_emuldata_shared *shared;\n"
+msgstr " struct linux_emuldata_shared *shared;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:915
+#, no-wrap
+msgid " int pdeath_signal; /* parent death signal */\n"
+msgstr " int pdeath_signal; /* parent death signal */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:918
+#, no-wrap
+msgid ""
+" LIST_ENTRY(linux_emuldata) threads; /* list of linux threads */\n"
+"};\n"
+msgstr ""
+" LIST_ENTRY(linux_emuldata) threads; /* list of linux threads */\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:925
+msgid ""
+"The PID is used to identify the FreeBSD process that attaches this "
+"structure. The `child_se_tid` and `child_clear_tid` are used for TID "
+"address copyout when a process exits and is created. The `shared` pointer "
+"points to a structure shared among threads. The `pdeath_signal` variable "
+"identifies the parent death signal and the `threads` pointer is used to link "
+"this structure to the list of threads. The `linux_emuldata_shared` "
+"structure looks like:"
+msgstr ""
+"PID используется для идентификации процесса FreeBSD, к которому присоединена "
+"эта структура. `child_se_tid` и `child_clear_tid` используются для "
+"копирования адреса TID при завершении и создании процесса. Указатель "
+"`shared` указывает на структуру, разделяемую между потоками. Переменная "
+"`pdeath_signal` определяет сигнал завершения родительского процесса, а "
+"указатель `threads` используется для связывания этой структуры со списком "
+"потоков. Структура `linux_emuldata_shared` выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:929
+#, no-wrap
+msgid "struct linux_emuldata_shared {\n"
+msgstr "struct linux_emuldata_shared {\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:931
+#, no-wrap
+msgid " int refs;\n"
+msgstr " int refs;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:933
+#, no-wrap
+msgid " pid_t group_pid;\n"
+msgstr " pid_t group_pid;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:936
+#, no-wrap
+msgid ""
+" LIST_HEAD(, linux_emuldata) threads; /* head of list of linux threads */\n"
+"};\n"
+msgstr ""
+" LIST_HEAD(, linux_emuldata) threads; /* head of list of linux threads */\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:941
+msgid ""
+"The `refs` is a reference counter being used to determine when we can free "
+"the structure to avoid memory leaks. The `group_pid` is to identify PID ( = "
+"TGID) of the whole process ( = thread group). The `threads` pointer is the "
+"head of the list of threads in the process."
+msgstr ""
+"`refs` — это счётчик ссылок, используемый для определения момента, когда "
+"можно освободить структуру, чтобы избежать утечек памяти. `group_pid` служит "
+"для идентификации PID (= TGID) всего процесса (= группы потоков). Указатель "
+"`threads` является головой списка потоков в процессе."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:944
+msgid ""
+"The `linux_emuldata` structure can be obtained from the process using "
+"`em_find`. The prototype of the function is:"
+msgstr ""
+"Структуру `linux_emuldata` можно получить из процесса с помощью `em_find`. "
+"Прототип функции выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:948
+#, no-wrap
+msgid "struct linux_emuldata *em_find(struct proc *, int locked);\n"
+msgstr "struct linux_emuldata *em_find(struct proc *, int locked);\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:953
+msgid ""
+"Here, `proc` is the process we want the emuldata structure from and the "
+"locked parameter determines whether we want to lock or not. The accepted "
+"values are `EMUL_DOLOCK` and `EMUL_DOUNLOCK`. More about locking later."
+msgstr ""
+"Здесь `proc` — это процесс, из которого мы хотим получить структуру "
+"`emuldata`, а параметр `locked` определяет, нужно ли блокировать. Допустимые "
+"значения — `EMUL_DOLOCK` и `EMUL_DOUNLOCK`. Подробнее о блокировке позже."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:955
+#, no-wrap
+msgid "PID mangling"
+msgstr "Преобразование PID"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:962
+msgid ""
+"As there is a difference in view as what to the idea of a process ID and "
+"thread ID is between FreeBSD and Linux(R) we have to translate the view "
+"somehow. We do it by PID mangling. This means that we fake what a PID "
+"(=TGID) and TID (=PID) is between kernel and userland. The rule of thumb is "
+"that in kernel (in Linuxulator) PID = PID and TGID = shared -> group pid and "
+"to userland we present `PID = shared -> group_pid` and `TID = proc -> "
+"p_pid`. The PID member of `linux_emuldata structure` is a FreeBSD PID."
+msgstr ""
+"Поскольку между FreeBSD и Linux(R) существуют различия в представлении "
+"идентификатора процесса (PID) и идентификатора потока (TID), нам необходимо "
+"преобразовывать эти понятия. Это достигается за счёт модификации PID. Это "
+"означает, что мы изменяем представление о PID (=TGID) и TID (=PID) между "
+"ядром и пользовательским пространством. Основное правило заключается в "
+"следующем: в ядре (в Linuxulator) `PID = PID`, а `TGID = shared -> "
+"group_pid`; для пользовательского пространства мы представляем `PID = shared "
+"-> group_pid` и `TID = proc -> p_pid`. Член `PID` в структуре "
+"`linux_emuldata` является FreeBSD PID."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:966
+msgid ""
+"The above affects mainly getpid, getppid, gettid syscalls. Where we use PID/"
+"TGID respectively. In copyout of TIDs in `child_clear_tid` and "
+"`child_set_tid` we copy out FreeBSD PID."
+msgstr ""
+"Вышесказанное в основном влияет на системные вызовы getpid, getppid, gettid. "
+"В случаях, где мы используем PID/TGID соответственно. При копировании TID в "
+"`child_clear_tid` и `child_set_tid` мы копируем FreeBSD PID."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:968
+#, no-wrap
+msgid "Clone syscall"
+msgstr "Системный вызов clone"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:972
+msgid ""
+"The `clone` syscall is the way threads are created in Linux(R). The syscall "
+"prototype looks like this:"
+msgstr ""
+"`clone` — это системный вызов, с помощью которого создаются потоки в "
+"Linux(R). Прототип системного вызова выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:977
+#, no-wrap
+msgid ""
+"int linux_clone(l_int flags, void *stack, void *parent_tidptr, int dummy,\n"
+"void * child_tidptr);\n"
+msgstr ""
+"int linux_clone(l_int flags, void *stack, void *parent_tidptr, int dummy,\n"
+"void * child_tidptr);\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:987
+msgid ""
+"The `flags` parameter tells the syscall how exactly the processes should be "
+"cloned. As described above, Linux(R) can create processes sharing various "
+"things independently, for example two processes can share file descriptors "
+"but not VM, etc. Last byte of the `flags` parameter is the exit signal of "
+"the newly created process. The `stack` parameter if non-`NULL` tells, where "
+"the thread stack is and if it is `NULL` we are supposed to copy-on-write the "
+"calling process stack (i.e. do what normal man:fork[2] routine does). The "
+"`parent_tidptr` parameter is used as an address for copying out process PID "
+"(i.e. thread id) once the process is sufficiently instantiated but is not "
+"runnable yet. The `dummy` parameter is here because of the very strange "
+"calling convention of this syscall on i386. It uses the registers directly "
+"and does not let the compiler do it what results in the need of a dummy "
+"syscall. The `child_tidptr` parameter is used as an address for copying out "
+"PID once the process has finished forking and when the process exits."
+msgstr ""
+"Параметр `flags` указывает системному вызову, как именно процессы должны "
+"быть клонированы. Как описано выше, Linux(R) может создавать процессы, "
+"разделяющие различные ресурсы независимо, например, два процесса могут "
+"разделять файловые дескрипторы, но не виртуальную память и т.д. Последний "
+"байт параметра `flags` является сигналом завершения для вновь созданного "
+"процесса. Параметр `stack`, если он не `NULL`, указывает, где находится стек "
+"потока, а если он `NULL`, предполагается копирование при записи стека "
+"вызывающего процесса (т.е. делать то же, что делает обычная функция "
+"man:fork[2]). Параметр `parent_tidptr` используется как адрес для "
+"копирования PID процесса (т.е. идентификатора потока) после того, как "
+"процесс достаточно инициализирован, но ещё не готов к выполнению. Параметр "
+"`dummy` присутствует из-за очень странного соглашения о вызовах этого "
+"системного вызова на i386. Он использует регистры напрямую и не позволяет "
+"компилятору делать это, что приводит к необходимости использования "
+"фиктивного системного вызова. Параметр `child_tidptr` используется как адрес "
+"для копирования PID после завершения ветвления процесса и при его завершении."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1001
+msgid ""
+"The syscall itself proceeds by setting corresponding flags depending on the "
+"flags passed in. For example, `CLONE_VM` maps to RFMEM (sharing of VM), "
+"etc. The only nit here is `CLONE_FS` and `CLONE_FILES` because FreeBSD does "
+"not allow setting this separately so we fake it by not setting RFFDG "
+"(copying of fd table and other fs information) if either of these is "
+"defined. This does not cause any problems, because those flags are always "
+"set together. After setting the flags the process is forked using the "
+"internal `fork1` routine, the process is instrumented not to be put on a run "
+"queue, i.e. not to be set runnable. After the forking is done we possibly "
+"reparent the newly created process to emulate `CLONE_PARENT` semantics. "
+"Next part is creating the emulation data. Threads in Linux(R) does not "
+"signal their parents so we set exit signal to be 0 to disable this. After "
+"that setting of `child_set_tid` and `child_clear_tid` is performed enabling "
+"the functionality later in the code. At this point we copy out the PID to "
+"the address specified by `parent_tidptr`. The setting of process stack is "
+"done by simply rewriting thread frame `%esp` register (`%rsp` on amd64). "
+"Next part is setting up TLS for the newly created process. After this "
+"man:vfork[2] semantics might be emulated and finally the newly created "
+"process is put on a run queue and copying out its PID to the parent process "
+"via `clone` return value is done."
+msgstr ""
+"Системный вызов продолжает выполнение, устанавливая соответствующие флаги в "
+"зависимости от переданных аргументов. Например, `CLONE_VM` преобразуется в "
+"RFMEM (общее адресное пространство) и т.д. Единственная тонкость здесь — это "
+"`CLONE_FS` и `CLONE_FILES`, поскольку FreeBSD не позволяет устанавливать их "
+"отдельно, поэтому мы эмулируем это, не устанавливая RFFDG (копирование "
+"таблицы файловых дескрипторов и другой информации о файловой системе), если "
+"задан любой из этих флагов. Это не вызывает проблем, так как эти флаги "
+"всегда устанавливаются вместе. После установки флагов процесс создаётся с "
+"помощью внутренней процедуры `fork1`, при этом процесс настраивается так, "
+"чтобы не помещаться в очередь выполнения (т.е. не становиться исполняемым). "
+"После завершения ветвления мы, при необходимости, изменяем родителя для "
+"нового процесса, чтобы эмулировать семантику `CLONE_PARENT`. Следующий шаг — "
+"создание данных эмуляции. Потоки в Linux(R) не отправляют сигналы своим "
+"родителям, поэтому мы устанавливаем сигнал завершения в 0, чтобы отключить "
+"эту возможность. Затем выполняется настройка `child_set_tid` и "
+"`child_clear_tid`, что активирует соответствующую функциональность далее в "
+"коде. На этом этапе мы копируем PID по адресу, указанному в `parent_tidptr`. "
+"Установка стека процесса выполняется простой перезаписью регистра `%esp` "
+"(`%rsp` на amd64) в кадре потока. Далее настраивается TLS для нового "
+"процесса. После этого может быть эмулирована семантика man:vfork[2], и, "
+"наконец, новый процесс помещается в очередь выполнения, а его PID "
+"возвращается родительскому процессу через возвращаемое значение `clone`."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1004
+msgid ""
+"The `clone` syscall is able and in fact is used for emulating classic "
+"man:fork[2] and man:vfork[2] syscalls. Newer glibc in a case of 2.6 kernel "
+"uses `clone` to implement man:fork[2] and man:vfork[2] syscalls."
+msgstr ""
+"Системный вызов `clone` способен и фактически используется для эмуляции "
+"классических системных вызовов man:fork[2] и man:vfork[2]. Более новые "
+"версии glibc в случае ядра 2.6 используют `clone` для реализации системных "
+"вызовов man:fork[2] и man:vfork[2]."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1006
+#, no-wrap
+msgid "Locking"
+msgstr "Блокировка"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1012
+msgid ""
+"The locking is implemented to be per-subsystem because we do not expect a "
+"lot of contention on these. There are two locks: `emul_lock` used to "
+"protect manipulating of `linux_emuldata` and `emul_shared_lock` used to "
+"manipulate `linux_emuldata_shared`. The `emul_lock` is a nonsleepable "
+"blocking mutex while `emul_shared_lock` is a sleepable blocking `sx_lock`. "
+"Due to of the per-subsystem locking we can coalesce some locks and that is "
+"why the em find offers the non-locking access."
+msgstr ""
+"Блокировка реализована на уровне подсистем, поскольку не ожидается высокой "
+"конкуренции за эти ресурсы. Существует две блокировки: `emul_lock`, "
+"используемая для защиты манипуляций с `linux_emuldata`, и "
+"`emul_shared_lock`, используемая для манипуляций с `linux_emuldata_shared`. "
+"`emul_lock` представляет собой неспящий блокирующий мьютекс, в то время как "
+"`emul_shared_lock` — это спящий блокирующий `sx_lock`. Благодаря блокировке "
+"на уровне подсистем мы можем объединять некоторые блокировки, поэтому "
+"em_find предлагает доступ без блокировки."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1014
+#, no-wrap
+msgid "TLS"
+msgstr "TLS"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1017
+msgid "This section deals with TLS also known as thread local storage."
+msgstr ""
+"Этот раздел посвящён TLS, также известному как локальное хранилище потока."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1019
+#, no-wrap
+msgid "Introduction to threading"
+msgstr "Введение в многопоточность"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1040
+msgid ""
+"Threads in computer science are entities within a process that can be "
+"scheduled independently from each other. The threads in the process share "
+"process wide data (file descriptors, etc.) but also have their own stack for "
+"their own data. Sometimes there is a need for process-wide data specific to "
+"a given thread. Imagine a name of the thread in execution or something like "
+"that. The traditional UNIX(R) threading API, pthreads provides a way to do "
+"it via man:pthread_key_create[3], man:pthread_setspecific[3] and "
+"man:pthread_getspecific[3] where a thread can create a key to the thread "
+"local data and using man:pthread_getspecific[3] or "
+"man:pthread_getspecific[3] to manipulate those data. You can easily see "
+"that this is not the most comfortable way this could be accomplished. So "
+"various producers of C/C++ compilers introduced a better way. They defined "
+"a new modifier keyword thread that specifies that a variable is thread "
+"specific. A new method of accessing such variables was developed as well "
+"(at least on i386). The pthreads method tends to be implemented in "
+"userspace as a trivial lookup table. The performance of such a solution is "
+"not very good. So the new method uses (on i386) segment registers to "
+"address a segment, where TLS area is stored so the actual accessing of a "
+"thread variable is just appending the segment register to the address thus "
+"addressing via it. The segment registers are usually `%gs` and `%fs` acting "
+"like segment selectors. Every thread has its own area where the thread "
+"local data are stored and the segment must be loaded on every context "
+"switch. This method is very fast and used almost exclusively in the whole "
+"i386 UNIX(R) world. Both FreeBSD and Linux(R) implement this approach and "
+"it yields very good results. The only drawback is the need to reload the "
+"segment on every context switch which can slowdown context switches. "
+"FreeBSD tries to avoid this overhead by using only 1 segment descriptor for "
+"this while Linux(R) uses 3. Interesting thing is that almost nothing uses "
+"more than 1 descriptor (only Wine seems to use 2) so Linux(R) pays this "
+"unnecessary price for context switches."
+msgstr ""
+"В компьютерных науках потоки (threads) — это сущности внутри процесса, "
+"которые могут планироваться независимо друг от друга. Потоки в процессе "
+"разделяют общие данные процесса (например, файловые дескрипторы), но также "
+"имеют свой собственный стек для своих данных. Иногда возникает необходимость "
+"в данных, специфичных для конкретного потока, но доступных на уровне "
+"процесса. Например, имя выполняемого потока или что-то подобное. "
+"Традиционный API для работы с потоками в UNIX® — pthreads — предоставляет "
+"способ сделать это через функции `man:pthread_key_create[3]`, "
+"`man:pthread_setspecific[3]` и `man:pthread_getspecific[3]`, где поток может "
+"создать ключ к локальным данным потока и использовать "
+"`man:pthread_getspecific[3]` или `man:pthread_getspecific[3]` для управления "
+"этими данными. Легко заметить, что это не самый удобный способ. Поэтому "
+"различные разработчики компиляторов C/C++ предложили более удобный метод. "
+"Они ввели новое ключевое слово `thread`, которое указывает, что переменная "
+"является специфичной для потока. Также был разработан новый метод доступа к "
+"таким переменным (по крайней мере, на архитектуре i386). Метод pthreads "
+"обычно реализуется в пользовательском пространстве в виде простой таблицы "
+"поиска. Производительность такого решения не очень высока. Новый метод "
+"использует (на i386) сегментные регистры для адресации области, где хранится "
+"TLS (Thread-Local Storage), так что фактический доступ к переменной потока "
+"сводится к добавлению сегментного регистра к адресу, таким образом обращаясь "
+"через него. Сегментные регистры, обычно `%gs` и `%fs`, действуют как "
+"селекторы сегментов. Каждый поток имеет свою собственную область, где "
+"хранятся локальные данные потока, и сегмент должен загружаться при каждом "
+"переключении контекста. Этот метод очень быстрый и используется практически "
+"повсеместно в мире UNIX® на архитектуре i386. И FreeBSD, и Linux® реализуют "
+"этот подход, и он даёт очень хорошие результаты. Единственный недостаток — "
+"необходимость перезагружать сегмент при каждом переключении контекста, что "
+"может замедлять переключения. FreeBSD пытается минимизировать эти накладные "
+"расходы, используя только 1 дескриптор сегмента, в то время как Linux® "
+"использует 3. Интересно, что почти ничто не использует больше 1 дескриптора "
+"(только Wine, кажется, использует 2), поэтому Linux® платит эту "
+"необязательную цену при переключении контекстов."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1042
+#, no-wrap
+msgid "Segments on i386"
+msgstr "Сегменты на i386"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1049
+msgid ""
+"The i386 architecture implements the so called segments. A segment is a "
+"description of an area of memory. The base address (bottom) of the memory "
+"area, the end of it (ceiling), type, protection, etc. The memory described "
+"by a segment can be accessed using segment selector registers (`%cs`, `%ds`, "
+"`%ss`, `%es`, `%fs`, `%gs`). For example let us suppose we have a segment "
+"which base address is 0x1234 and length and this code:"
+msgstr ""
+"Архитектура i386 реализует так называемые сегменты. Сегмент — это описание "
+"области памяти. Он включает базовый адрес (начало) области памяти, её конец "
+"(границу), тип, защиту и т.д. Доступ к памяти, описываемой сегментом, может "
+"осуществляться с использованием регистров селекторов сегментов (`%cs`, "
+"`%ds`, `%ss`, `%es`, `%fs`, `%gs`). Например, предположим, что у нас есть "
+"сегмент с базовым адресом 0x1234 и длиной, а также следующий код:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1053
+#, no-wrap
+msgid "mov %edx,%gs:0x10\n"
+msgstr "mov %edx,%gs:0x10\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1062
+msgid ""
+"This will load the content of the `%edx` register into memory location "
+"0x1244. Some segment registers have a special use, for example `%cs` is "
+"used for code segment and `%ss` is used for stack segment but `%fs` and "
+"`%gs` are generally unused. Segments are either stored in a global GDT "
+"table or in a local LDT table. LDT is accessed via an entry in the GDT. "
+"The LDT can store more types of segments. LDT can be per process. Both "
+"tables define up to 8191 entries."
+msgstr ""
+"Это загрузит содержимое регистра `%edx` в ячейку памяти по адресу 0x1244. "
+"Некоторые сегментные регистры имеют специальное назначение, например, `%cs` "
+"используется для сегмента кода, а `%ss` — для сегмента стека, но `%fs` и "
+"`%gs` обычно не используются. Сегменты хранятся либо в глобальной таблице "
+"GDT, либо в локальной таблице LDT. Доступ к LDT осуществляется через запись "
+"в GDT. LDT может хранить больше типов сегментов. LDT может быть отдельной "
+"для каждого процесса. Обе таблицы определяют до 8191 записей."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1064
+#, no-wrap
+msgid "Implementation on Linux(R) i386"
+msgstr "Реализация на Linux(R) i386"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1072
+msgid ""
+"There are two main ways of setting up TLS in Linux(R). It can be set when "
+"cloning a process using the `clone` syscall or it can call "
+"`set_thread_area`. When a process passes `CLONE_SETTLS` flag to `clone`, "
+"the kernel expects the memory pointed to by the `%esi` register a Linux(R) "
+"user space representation of a segment, which gets translated to the machine "
+"representation of a segment and loaded into a GDT slot. The GDT slot can be "
+"specified with a number or -1 can be used meaning that the system itself "
+"should choose the first free slot. In practice, the vast majority of "
+"programs use only one TLS entry and does not care about the number of the "
+"entry. We exploit this in the emulation and in fact depend on it."
+msgstr ""
+"Существует два основных способа настройки TLS в Linux(R). Он может быть "
+"настроен при клонировании процесса с использованием системного вызова "
+"`clone` или с помощью вызова `set_thread_area`. Когда процесс передает флаг "
+"`CLONE_SETTLS` в `clone`, ядро ожидает, что память, на которую указывает "
+"регистр `%esi`, будет содержать пользовательское представление сегмента в "
+"Linux(R), которое преобразуется в машинное представление сегмента и "
+"загружается в слот GDT. Слот GDT может быть указан номером или можно "
+"использовать -1, что означает, что система сама должна выбрать первый "
+"свободный слот. На практике подавляющее большинство программ используют "
+"только одну запись TLS и не заботятся о номере записи. Мы используем это в "
+"эмуляции и фактически зависим от этого."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1074
+#, no-wrap
+msgid "Emulation of Linux(R) TLS"
+msgstr "Эмуляция Linux(R) TLS"
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1077
+#, no-wrap
+msgid "i386"
+msgstr "i386"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1095
+msgid ""
+"Loading of TLS for the current thread happens by calling `set_thread_area` "
+"while loading TLS for a second process in `clone` is done in the separate "
+"block in `clone`. Those two functions are very similar. The only "
+"difference being the actual loading of the GDT segment, which happens on the "
+"next context switch for the newly created process while `set_thread_area` "
+"must load this directly. The code basically does this. It copies the "
+"Linux(R) form segment descriptor from the userland. The code checks for the "
+"number of the descriptor but because this differs between FreeBSD and "
+"Linux(R) we fake it a little. We only support indexes of 6, 3 and -1. The "
+"6 is genuine Linux(R) number, 3 is genuine FreeBSD one and -1 means "
+"autoselection. Then we set the descriptor number to constant 3 and copy out "
+"this to the userspace. We rely on the userspace process using the number "
+"from the descriptor but this works most of the time (have never seen a case "
+"where this did not work) as the userspace process typically passes in 1. "
+"Then we convert the descriptor from the Linux(R) form to a machine dependant "
+"form (i.e. operating system independent form) and copy this to the FreeBSD "
+"defined segment descriptor. Finally we can load it. We assign the "
+"descriptor to threads PCB (process control block) and load the `%gs` segment "
+"using `load_gs`. This loading must be done in a critical section so that "
+"nothing can interrupt us. The `CLONE_SETTLS` case works exactly like this "
+"just the loading using `load_gs` is not performed. The segment used for "
+"this (segment number 3) is shared for this use between FreeBSD processes and "
+"Linux(R) processes so the Linux(R) emulation layer does not add any overhead "
+"over plain FreeBSD."
+msgstr ""
+"Загрузка TLS для текущего потока происходит путем вызова `set_thread_area`, "
+"тогда как загрузка TLS для второго процесса в `clone` выполняется в "
+"отдельном блоке в `clone`. Эти две функции очень похожи. Единственное "
+"различие заключается в фактической загрузке сегмента GDT, которая происходит "
+"при следующем переключении контекста для вновь созданного процесса, в то "
+"время как `set_thread_area` должен загрузить его напрямую. Код в основном "
+"делает следующее. Он копирует дескриптор сегмента в формате Linux(R) из "
+"пользовательского пространства. Код проверяет номер дескриптора, но "
+"поскольку он различается между FreeBSD и Linux(R), мы немного имитируем его. "
+"Мы поддерживаем только индексы 6, 3 и -1. Число 6 — это оригинальный номер "
+"Linux(R), 3 — оригинальный номер FreeBSD, а -1 означает авто-выбор. Затем мы "
+"устанавливаем номер дескриптора на константу 3 и копируем его обратно в "
+"пользовательское пространство. Мы полагаемся на то, что процесс в "
+"пользовательском пространстве использует номер из дескриптора, но это "
+"работает в большинстве случаев (никогда не встречалось ситуации, когда это "
+"не срабатывало), так как процесс в пользовательском пространстве обычно "
+"передает 1. Затем мы преобразуем дескриптор из формата Linux(R) в машинно-"
+"зависимую форму (т.е. независимую от операционной системы) и копируем его в "
+"дескриптор сегмента, определенный FreeBSD. Наконец, мы можем загрузить его. "
+"Мы назначаем дескриптор PCB потока (блок управления процессом) и загружаем "
+"сегмент `%gs` с помощью `load_gs`. Эта загрузка должна выполняться в "
+"критической секции, чтобы ничто не могло нас прервать. Случай `CLONE_SETTLS` "
+"работает точно так же, только загрузка с помощью `load_gs` не выполняется. "
+"Сегмент, используемый для этого (сегмент номер 3), разделяется между "
+"процессами FreeBSD и Linux(R), поэтому слой эмуляции Linux(R) не добавляет "
+"накладных расходов по сравнению с обычным FreeBSD."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1097
+#, no-wrap
+msgid "amd64"
+msgstr "amd64"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1101
+msgid ""
+"The amd64 implementation is similar to the i386 one but there was initially "
+"no 32bit segment descriptor used for this purpose (hence not even native "
+"32bit TLS users worked) so we had to add such a segment and implement its "
+"loading on every context switch (when a flag signaling use of 32bit is "
+"set). Apart from this the TLS loading is exactly the same just the segment "
+"numbers are different and the descriptor format and the loading differs "
+"slightly."
+msgstr ""
+"Реализация amd64 аналогична реализации i386, но изначально не использовался "
+"32-битный дескриптор сегмента для этой цели (поэтому даже нативные "
+"пользователи 32-битного TLS не работали), поэтому нам пришлось добавить "
+"такой сегмент и реализовать его загрузку при каждом переключении контекста "
+"(когда установлен флаг, сигнализирующий о использовании 32-битного режима). "
+"Кроме этого, загрузка TLS точно такая же, только номера сегментов "
+"отличаются, а формат дескриптора и загрузка немного различаются."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1103
+#, no-wrap
+msgid "Futexes"
+msgstr "Фьютексы"
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1106
+#, no-wrap
+msgid "Introduction to synchronization"
+msgstr "Введение в синхронизацию"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1116
+msgid ""
+"Threads need some kind of synchronization and POSIX(R) provides some of "
+"them: mutexes for mutual exclusion, read-write locks for mutual exclusion "
+"with biased ratio of reads and writes and condition variables for signaling "
+"a status change. It is interesting to note that POSIX(R) threading API "
+"lacks support for semaphores. Those synchronization routines "
+"implementations are heavily dependant on the type threading support we "
+"have. In pure 1:M (userspace) model the implementation can be solely done "
+"in userspace and thus be very fast (the condition variables will probably "
+"end up being implemented using signals, i.e. not fast) and simple. In 1:1 "
+"model, the situation is also quite clear - the threads must be synchronized "
+"using kernel facilities (which is very slow because a syscall must be "
+"performed). The mixed M:N scenario just combines the first and second "
+"approach or rely solely on kernel. Threads synchronization is a vital part "
+"of thread-enabled programming and its performance can affect resulting "
+"program a lot. Recent benchmarks on FreeBSD operating system showed that an "
+"improved sx_lock implementation yielded 40% speedup in _ZFS_ (a heavy sx "
+"user), this is in-kernel stuff but it shows clearly how important the "
+"performance of synchronization primitives is."
+msgstr ""
+"Потокам требуется некоторая синхронизация, и POSIX(R) предоставляет "
+"несколько её видов: мьютексы для взаимного исключения, блокировки чтения-"
+"записи для взаимного исключения с преобладанием операций чтения над записями "
+"и условные переменные для сигнализации об изменении состояния. Интересно "
+"отметить, что в API потоков POSIX(R) отсутствует поддержка семафоров. "
+"Реализации этих механизмов синхронизации сильно зависят от типа поддержки "
+"потоков, которая у нас есть. В чистой модели 1:M (пользовательское "
+"пространство) реализация может быть выполнена исключительно в "
+"пользовательском пространстве и, следовательно, быть очень быстрой (условные "
+"переменные, вероятно, будут реализованы с использованием сигналов, т.е. не "
+"быстро) и простой. В модели 1:1 ситуация также довольно ясна — потоки должны "
+"синхронизироваться с использованием средств ядра (что очень медленно, "
+"поскольку требуется выполнение системного вызова). Смешанный сценарий M:N "
+"просто комбинирует первый и второй подходы или полагается исключительно на "
+"ядро. Синхронизация потоков является важной частью программирования с "
+"использованием потоков, и её производительность может значительно влиять на "
+"итоговую программу. Недавние тесты в операционной системе FreeBSD показали, "
+"что улучшенная реализация `sx_lock` дала 40% прироста скорости в _ZFS_ (где "
+"активно используются блокировки sx), это внутренние механизмы ядра, но это "
+"наглядно демонстрирует, насколько важна производительность примитивов "
+"синхронизации."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1120
+msgid ""
+"Threaded programs should be written with as little contention on locks as "
+"possible. Otherwise, instead of doing useful work the thread just waits on "
+"a lock. As a result of this, the most well written threaded programs show "
+"little locks contention."
+msgstr ""
+"Многопоточные программы должны быть написаны с минимальной конкуренцией за "
+"блокировки. В противном случае, вместо выполнения полезной работы поток "
+"просто ожидает блокировку. В результате, наиболее хорошо написанные "
+"многопоточные программы демонстрируют низкую конкуренцию за блокировки."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1122
+#, no-wrap
+msgid "Futexes introduction"
+msgstr "Введение в фьютексы"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1127
+msgid ""
+"Linux(R) implements 1:1 threading, i.e. it has to use in-kernel "
+"synchronization primitives. As stated earlier, well written threaded "
+"programs have little lock contention. So a typical sequence could be "
+"performed as two atomic increase/decrease mutex reference counter, which is "
+"very fast, as presented by the following example:"
+msgstr ""
+"Linux(R) реализует 1:1 потоковую модель, то есть использует примитивы "
+"синхронизации в ядре. Как упоминалось ранее, хорошо написанные многопоточные "
+"программы имеют низкую конкуренцию за блокировки. Таким образом, типичная "
+"последовательность может выполняться как два атомарных увеличения/уменьшения "
+"счётчика ссылок мьютекса, что очень быстро, как показано в следующем примере:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1133
+#, no-wrap
+msgid ""
+"pthread_mutex_lock(&mutex);\n"
+"...\n"
+"pthread_mutex_unlock(&mutex);\n"
+msgstr ""
+"pthread_mutex_lock(&mutex);\n"
+"...\n"
+"pthread_mutex_unlock(&mutex);\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1136
+msgid ""
+"1:1 threading forces us to perform two syscalls for those mutex calls, which "
+"is very slow."
+msgstr ""
+"1:1 threading вынуждает нас выполнять два системных вызова для этих вызовов "
+"мьютекса, что очень медленно."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1141
+msgid ""
+"The solution Linux(R) 2.6 implements is called futexes. Futexes implement "
+"the check for contention in userspace and call kernel primitives only in a "
+"case of contention. Thus the typical case takes place without any kernel "
+"intervention. This yields reasonably fast and flexible synchronization "
+"primitives implementation."
+msgstr ""
+"Решение, реализованное в Linux(R) 2.6, называется фьютексы. Фьютексы "
+"выполняют проверку на конкуренцию в пользовательском пространстве и вызывают "
+"примитивы ядра только в случае конкуренции. Таким образом, типичный случай "
+"обходится без вмешательства ядра. Это обеспечивает достаточно быструю и "
+"гибкую реализацию примитивов синхронизации."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1143
+#, no-wrap
+msgid "Futex API"
+msgstr "API фьютексов"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1146
+msgid "The futex syscall looks like this:"
+msgstr "Системный вызов futex выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1150
+#, no-wrap
+msgid "int futex(void *uaddr, int op, int val, struct timespec *timeout, void *uaddr2, int val3);\n"
+msgstr "int futex(void *uaddr, int op, int val, struct timespec *timeout, void *uaddr2, int val3);\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1153
+msgid ""
+"In this example `uaddr` is an address of the mutex in userspace, `op` is an "
+"operation we are about to perform and the other parameters have per-"
+"operation meaning."
+msgstr ""
+"В этом примере `uaddr` — это адрес мьютекса в пользовательском пространстве, "
+"`op` — операция, которую мы собираемся выполнить, а остальные параметры "
+"имеют значение, зависящее от конкретной операции."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1155
+msgid "Futexes implement the following operations:"
+msgstr "Фьютексы реализуют следующие операции:"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1157
+msgid "`FUTEX_WAIT`"
+msgstr "`FUTEX_WAIT`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1158
+msgid "`FUTEX_WAKE`"
+msgstr "`FUTEX_WAKE`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1159
+msgid "`FUTEX_FD`"
+msgstr "`FUTEX_FD`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1160
+msgid "`FUTEX_REQUEUE`"
+msgstr "`FUTEX_REQUEUE`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1161
+msgid "`FUTEX_CMP_REQUEUE`"
+msgstr "`FUTEX_CMP_REQUEUE`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1162
+msgid "`FUTEX_WAKE_OP`"
+msgstr "`FUTEX_WAKE_OP`"
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1164
+#, no-wrap
+msgid "FUTEX_WAIT"
+msgstr "FUTEX_WAIT"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1169
+msgid ""
+"This operation verifies that on address `uaddr` the value `val` is written. "
+"If not, `EWOULDBLOCK` is returned, otherwise the thread is queued on the "
+"futex and gets suspended. If the argument `timeout` is non-zero it "
+"specifies the maximum time for the sleeping, otherwise the sleeping is "
+"infinite."
+msgstr ""
+"Эта операция проверяет, что по адресу `uaddr` записано значение `val`. Если "
+"нет, возвращается `EWOULDBLOCK`, в противном случае поток ставится в очередь "
+"на фьютекс и приостанавливается. Если аргумент `timeout` не равен нулю, он "
+"задает максимальное время ожидания, в противном случае ожидание бесконечно."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1171
+#, no-wrap
+msgid "FUTEX_WAKE"
+msgstr "FUTEX_WAKE"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1174
+msgid ""
+"This operation takes a futex at `uaddr` and wakes up `val` first futexes "
+"queued on this futex."
+msgstr ""
+"Эта операция захватывает фьютекс по адресу `uaddr` и пробуждает первые `val` "
+"фьютексов, ожидающих в очереди на этом фьютексе."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1176
+#, no-wrap
+msgid "FUTEX_FD"
+msgstr "FUTEX_FD"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1179
+msgid "This operations associates a file descriptor with a given futex."
+msgstr "Эта операция связывает файловый дескриптор с заданным фьютексом."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1181
+#, no-wrap
+msgid "FUTEX_REQUEUE"
+msgstr "FUTEX_REQUEUE"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1184
+msgid ""
+"This operation takes `val` threads queued on futex at `uaddr`, wakes them "
+"up, and takes `val2` next threads and requeues them on futex at `uaddr2`."
+msgstr ""
+"Эта операция берет `val` потоков, ожидающих на фьютексе по адресу `uaddr`, "
+"пробуждает их и берет следующие `val2` потоков, перемещая их в очередь "
+"фьютекса по адресу `uaddr2`."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1186
+#, no-wrap
+msgid "FUTEX_CMP_REQUEUE"
+msgstr "FUTEX_CMP_REQUEUE"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1189
+msgid ""
+"This operation does the same as `FUTEX_REQUEUE` but it checks that `val3` "
+"equals to `val` first."
+msgstr ""
+"Эта операция делает то же самое, что и `FUTEX_REQUEUE`, но сначала "
+"проверяет, что `val3` равно `val`."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1191
+#, no-wrap
+msgid "FUTEX_WAKE_OP"
+msgstr "FUTEX_WAKE_OP"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1195
+msgid ""
+"This operation performs an atomic operation on `val3` (which contains coded "
+"some other value) and `uaddr`. Then it wakes up `val` threads on futex at "
+"`uaddr` and if the atomic operation returned a positive number it wakes up "
+"`val2` threads on futex at `uaddr2`."
+msgstr ""
+"Эта операция выполняет атомарную операцию над `val3` (которая содержит "
+"закодированное другое значение) и `uaddr`. Затем она пробуждает `val` "
+"потоков на фьютексе по адресу `uaddr`, и если атомарная операция вернула "
+"положительное число, пробуждает `val2` потоков на фьютексе по адресу "
+"`uaddr2`."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1197
+msgid "The operations implemented in `FUTEX_WAKE_OP`:"
+msgstr "Операции, реализованные в `FUTEX_WAKE_OP`:"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1199
+msgid "`FUTEX_OP_SET`"
+msgstr "`FUTEX_OP_SET`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1200
+msgid "`FUTEX_OP_ADD`"
+msgstr "`FUTEX_OP_ADD`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1201
+msgid "`FUTEX_OP_OR`"
+msgstr "`FUTEX_OP_OR`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1202
+msgid "`FUTEX_OP_AND`"
+msgstr "`FUTEX_OP_AND`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1203
+msgid "`FUTEX_OP_XOR`"
+msgstr "`FUTEX_OP_XOR`"
+
+#. type: delimited block = 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1208
+msgid ""
+"There is no `val2` parameter in the futex prototype. The `val2` is taken "
+"from the `struct timespec *timeout` parameter for operations "
+"`FUTEX_REQUEUE`, `FUTEX_CMP_REQUEUE` and `FUTEX_WAKE_OP`."
+msgstr ""
+"В прототипе системного вызова futex отсутствует параметр `val2`. Значение "
+"`val2` берётся из параметра `struct timespec *timeout` для операций "
+"`FUTEX_REQUEUE`, `FUTEX_CMP_REQUEUE` и `FUTEX_WAKE_OP`."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1211
+#, no-wrap
+msgid "Futex emulation in FreeBSD"
+msgstr "Эмуляция фьютексов в FreeBSD"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1216
+msgid ""
+"The futex emulation in FreeBSD is taken from NetBSD and further extended by "
+"us. It is placed in `linux_futex.c` and [.filename]#linux_futex.h# files. "
+"The `futex` structure looks like:"
+msgstr ""
+"Эмуляция futex в FreeBSD взята из NetBSD и дополнительно расширена нами. Она "
+"размещена в файлах `linux_futex.c` и [.filename]#linux_futex.h#. Структура "
+"`futex` выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1222
+#, no-wrap
+msgid ""
+"struct futex {\n"
+" void *f_uaddr;\n"
+" int f_refcount;\n"
+msgstr ""
+"struct futex {\n"
+" void *f_uaddr;\n"
+" int f_refcount;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1224
+#, no-wrap
+msgid " LIST_ENTRY(futex) f_list;\n"
+msgstr " LIST_ENTRY(futex) f_list;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1227
+#, no-wrap
+msgid ""
+" TAILQ_HEAD(lf_waiting_paroc, waiting_proc) f_waiting_proc;\n"
+"};\n"
+msgstr ""
+" TAILQ_HEAD(lf_waiting_paroc, waiting_proc) f_waiting_proc;\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1230
+msgid "And the structure `waiting_proc` is:"
+msgstr "И структура `waiting_proc` выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1234
+#, no-wrap
+msgid "struct waiting_proc {\n"
+msgstr "struct waiting_proc {\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1236
+#, no-wrap
+msgid " struct thread *wp_t;\n"
+msgstr " struct thread *wp_t;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1238
+#, no-wrap
+msgid " struct futex *wp_new_futex;\n"
+msgstr " struct futex *wp_new_futex;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1241
+#, no-wrap
+msgid ""
+" TAILQ_ENTRY(waiting_proc) wp_list;\n"
+"};\n"
+msgstr ""
+" TAILQ_ENTRY(waiting_proc) wp_list;\n"
+"};\n"
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1244
+#, no-wrap
+msgid "futex_get / futex_put"
+msgstr "futex_get / futex_put"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1248
+msgid ""
+"A futex is obtained using the `futex_get` function, which searches a linear "
+"list of futexes and returns the found one or creates a new futex. When "
+"releasing a futex from the use we call the `futex_put` function, which "
+"decreases a reference counter of the futex and if the refcount reaches zero "
+"it is released."
+msgstr ""
+"Фьютекс получается с помощью функции `futex_get`, которая выполняет поиск в "
+"линейном списке фьютексов и возвращает найденный или создает новый. При "
+"освобождении фьютекса после использования вызывается функция `futex_put`, "
+"которая уменьшает счетчик ссылок фьютекса, и если счетчик достигает нуля, "
+"фьютекс освобождается."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1250
+#, no-wrap
+msgid "futex_sleep"
+msgstr "futex_sleep"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1258
+msgid ""
+"When a futex queues a thread for sleeping it creates a `working_proc` "
+"structure and puts this structure to the list inside the futex structure "
+"then it just performs a man:tsleep[9] to suspend the thread. The sleep can "
+"be timed out. After man:tsleep[9] returns (the thread was woken up or it "
+"timed out) the `working_proc` structure is removed from the list and is "
+"destroyed. All this is done in the `futex_sleep` function. If we got woken "
+"up from `futex_wake` we have `wp_new_futex` set so we sleep on it. This way "
+"the actual requeueing is done in this function."
+msgstr ""
+"Когда фьютекс ставит поток в очередь на ожидание, он создает структуру "
+"`working_proc` и помещает эту структуру в список внутри структуры futex, "
+"после чего просто выполняет man:tsleep[9] для приостановки потока. Ожидание "
+"может быть ограничено по времени. После возврата из man:tsleep[9] (поток был "
+"разбужен или истекло время ожидания) структура `working_proc` удаляется из "
+"списка и уничтожается. Все это выполняется в функции `futex_sleep`. Если мы "
+"были разбужены с помощью `futex_wake`, у нас установлен `wp_new_futex`, "
+"поэтому мы ожидаем на нем. Таким образом, фактическое перемещение "
+"выполняется в этой функции."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1260
+#, no-wrap
+msgid "futex_wake"
+msgstr "futex_wake"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1268
+msgid ""
+"Waking up a thread sleeping on a futex is performed in the `futex_wake` "
+"function. First in this function we mimic the strange Linux(R) behavior, "
+"where it wakes up N threads for all operations, the only exception is that "
+"the REQUEUE operations are performed on N+1 threads. But this usually does "
+"not make any difference as we are waking up all threads. Next in the "
+"function in the loop we wake up n threads, after this we check if there is a "
+"new futex for requeueing. If so, we requeue up to n2 threads on the new "
+"futex. This cooperates with `futex_sleep`."
+msgstr ""
+"Пробуждение потока, ожидающего на фьютексе, выполняется в функции "
+"`futex_wake`. Сначала в этой функции мы имитируем странное поведение "
+"Linux(R), где пробуждаются N потоков для всех операций, за исключением того, "
+"что операции REQUEUE выполняются на N+1 потоках. Однако обычно это не имеет "
+"значения, так как мы пробуждаем все потоки. Далее в функции в цикле мы "
+"пробуждаем n потоков, после чего проверяем, есть ли новый фьютекс для "
+"перестановки. Если есть, мы переставляем до n2 потоков на новый futex. Это "
+"взаимодействует с `futex_sleep`."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1270
+#, no-wrap
+msgid "futex_wake_op"
+msgstr "futex_wake_op"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1275
+msgid ""
+"The `FUTEX_WAKE_OP` operation is quite complicated. First we obtain two "
+"futexes at addresses `uaddr` and `uaddr2` then we perform the atomic "
+"operation using `val3` and `uaddr2`. Then `val` waiters on the first futex "
+"is woken up and if the atomic operation condition holds we wake up `val2` "
+"(i.e. `timeout`) waiter on the second futex."
+msgstr ""
+"Операция `FUTEX_WAKE_OP` довольно сложна. Сначала мы получаем два фьютекса "
+"по адресам `uaddr` и `uaddr2`, затем выполняем атомарную операцию с "
+"использованием `val3` и `uaddr2`. После этого пробуждаются `val` ожидающих "
+"на первом фьютексе, и если условие атомарной операции выполняется, мы "
+"пробуждаем `val2` (т.е. `timeout`) ожидающих на втором фьютексе."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1277
+#, no-wrap
+msgid "futex atomic operation"
+msgstr "Атомарная операция на фьютексе"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1282
+msgid ""
+"The atomic operation takes two parameters `encoded_op` and `uaddr`. The "
+"encoded operation encodes the operation itself, comparing value, operation "
+"argument, and comparing argument. The pseudocode for the operation is like "
+"this one:"
+msgstr ""
+"Атомарная операция принимает два параметра `encoded_op` и `uaddr`. "
+"Закодированная операция включает саму операцию, сравниваемое значение, "
+"аргумент операции и аргумент сравнения. Псевдокод операции выглядит "
+"следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1287
+#, no-wrap
+msgid ""
+"oldval = *uaddr2\n"
+"*uaddr2 = oldval OP oparg\n"
+msgstr ""
+"oldval = *uaddr2\n"
+"*uaddr2 = oldval OP oparg\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1291
+msgid ""
+"And this is done atomically. First a copying in of the number at `uaddr` is "
+"performed and the operation is done. The code handles page faults and if no "
+"page fault occurs `oldval` is compared to `cmparg` argument with cmp "
+"comparator."
+msgstr ""
+"И это выполняется атомарно. Сначала происходит копирование числа по адресу "
+"`uaddr`, а затем выполняется операция. Код обрабатывает ошибки страниц, и "
+"если ошибки не происходит, `oldval` сравнивается с аргументом `cmparg` с "
+"помощью компаратора cmp."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1293
+#, no-wrap
+msgid "Futex locking"
+msgstr "Блокировка фьютекса"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1297
+msgid ""
+"Futex implementation uses two lock lists protecting `sx_lock` and global "
+"locks (either Giant or another `sx_lock`). Every operation is performed "
+"locked from the start to the very end."
+msgstr ""
+"Реализация фьютексов использует два списка блокировок для защиты `sx_lock` и "
+"глобальных блокировок (либо Giant, либо другой `sx_lock`). Каждая операция "
+"выполняется заблокированной от начала до самого конца."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1299
+#, no-wrap
+msgid "Various syscalls implementation"
+msgstr "Реализация различных системных вызовов"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1302
+msgid ""
+"In this section I am going to describe some smaller syscalls that are worth "
+"mentioning because their implementation is not obvious or those syscalls are "
+"interesting from other point of view."
+msgstr ""
+"В этом разделе я опишу несколько менее значимых системных вызовов, которые "
+"стоит упомянуть, потому что их реализация неочевидна или эти вызовы "
+"представляют интерес с другой точки зрения."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1304
+#, no-wrap
+msgid "*at family of syscalls"
+msgstr "*семейство системных вызовов at"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1313
+msgid ""
+"During development of Linux(R) 2.6.16 kernel, the *at syscalls were added. "
+"Those syscalls (`openat` for example) work exactly like their at-less "
+"counterparts with the slight exception of the `dirfd` parameter. This "
+"parameter changes where the given file, on which the syscall is to be "
+"performed, is. When the `filename` parameter is absolute `dirfd` is ignored "
+"but when the path to the file is relative, it comes to the play. The "
+"`dirfd` parameter is a directory relative to which the relative pathname is "
+"checked. The `dirfd` parameter is a file descriptor of some directory or "
+"`AT_FDCWD`. So for example the `openat` syscall can be like this:"
+msgstr ""
+"Во время разработки ядра Linux(R) 2.6.16 были добавлены *at-системные "
+"вызовы. Эти системные вызовы (например, `openat`) работают точно так же, как "
+"их аналоги без at, за исключением параметра `dirfd`. Этот параметр "
+"определяет местоположение файла, над которым выполняется системный вызов. "
+"Если параметр `filename` является абсолютным, `dirfd` игнорируется, но если "
+"путь к файлу относительный, `dirfd` вступает в игру. Параметр `dirfd` "
+"представляет собой каталог, относительно которого проверяется относительный "
+"путь. Параметр `dirfd` является файловым дескриптором некоторого каталога "
+"или `AT_FDCWD`. Например, системный вызов `openat` может выглядеть следующим "
+"образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1317
+#, no-wrap
+msgid "file descriptor 123 = /tmp/foo/, current working directory = /tmp/\n"
+msgstr "file descriptor 123 = /tmp/foo/, current working directory = /tmp/\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1322
+#, no-wrap
+msgid ""
+"openat(123, /tmp/bah\\, flags, mode)\t/* opens /tmp/bah */\n"
+"openat(123, bah\\, flags, mode)\t\t/* opens /tmp/foo/bah */\n"
+"openat(AT_FDWCWD, bah\\, flags, mode)\t/* opens /tmp/bah */\n"
+"openat(stdio, bah\\, flags, mode)\t/* returns error because stdio is not a directory */\n"
+msgstr ""
+"openat(123, /tmp/bah\\, flags, mode)\t/* opens /tmp/bah */\n"
+"openat(123, bah\\, flags, mode)\t\t/* opens /tmp/foo/bah */\n"
+"openat(AT_FDWCWD, bah\\, flags, mode)\t/* opens /tmp/bah */\n"
+"openat(stdio, bah\\, flags, mode)\t/* returns error because stdio is not a directory */\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1331
+msgid ""
+"This infrastructure is necessary to avoid races when opening files outside "
+"the working directory. Imagine that a process consists of two threads, "
+"thread A and thread B. Thread A issues `open(./tmp/foo/bah., flags, mode)` "
+"and before returning it gets preempted and thread B runs. Thread B does not "
+"care about the needs of thread A and renames or removes [.filename]#/tmp/foo/"
+"#. We got a race. To avoid this we can open [.filename]#/tmp/foo# and use "
+"it as `dirfd` for `openat` syscall. This also enables user to implement per-"
+"thread working directories."
+msgstr ""
+"Эта инфраструктура необходима для избежания состояний гонки при открытии "
+"файлов вне рабочего каталога. Представьте, что процесс состоит из двух "
+"потоков, потока A и потока B. Поток A выполняет `open(./tmp/foo/bah., flags, "
+"mode)`, и перед возвратом управления он вытесняется, и начинает выполняться "
+"поток B. Поток B не учитывает потребности потока A и переименовывает или "
+"удаляет [.filename]#/tmp/foo/#. Возникает состояние гонки. Чтобы избежать "
+"этого, мы можем открыть [.filename]#/tmp/foo# и использовать его как `dirfd` "
+"для системного вызова `openat`. Это также позволяет пользователю реализовать "
+"рабочие каталоги для каждого потока."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1334
+msgid ""
+"Linux(R) family of *at syscalls contains: `linux_openat`, `linux_mkdirat`, "
+"`linux_mknodat`, `linux_fchownat`, `linux_futimesat`, `linux_fstatat64`, "
+"`linux_unlinkat`, `linux_renameat`, `linux_linkat`, `linux_symlinkat`, "
+"`linux_readlinkat`, `linux_fchmodat` and `linux_faccessat`. All these are "
+"implemented using the modified man:namei[9] routine and simple wrapping "
+"layer."
+msgstr ""
+"Семейство *at системных вызовов Linux(R) включает: `linux_openat`, "
+"`linux_mkdirat`, `linux_mknodat`, `linux_fchownat`, `linux_futimesat`, "
+"`linux_fstatat64`, `linux_unlinkat`, `linux_renameat`, `linux_linkat`, "
+"`linux_symlinkat`, `linux_readlinkat`, `linux_fchmodat` и `linux_faccessat`. "
+"Все они реализованы с использованием модифицированной функции man:namei[9] и "
+"простого слоя обёртки."
+
+#. type: Title =====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1336
+#, no-wrap
+msgid "Implementation"
+msgstr "Реализация"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1344
+msgid ""
+"The implementation is done by altering the man:namei[9] routine (described "
+"above) to take additional parameter `dirfd` in its `nameidata` structure, "
+"which specifies the starting point of the pathname lookup instead of using "
+"the current working directory every time. The resolution of `dirfd` from "
+"file descriptor number to a vnode is done in native *at syscalls. When "
+"`dirfd` is `AT_FDCWD` the `dvp` entry in `nameidata` structure is `NULL` but "
+"when `dirfd` is a different number we obtain a file for this file "
+"descriptor, check whether this file is valid and if there is vnode attached "
+"to it then we get a vnode. Then we check this vnode for being a directory. "
+"In the actual man:namei[9] routine we simply substitute the `dvp` vnode for "
+"`dp` variable in the man:namei[9] function, which determines the starting "
+"point. The man:namei[9] is not used directly but via a trace of different "
+"functions on various levels. For example the `openat` goes like this:"
+msgstr ""
+"Реализация выполнена путем изменения функции man:namei[9] (описанной выше) "
+"для приема дополнительного параметра `dirfd` в структуре `nameidata`, "
+"который указывает начальную точку для поиска пути вместо использования "
+"текущей рабочей директории каждый раз. Преобразование `dirfd` из номера "
+"файлового дескриптора в vnode выполняется в нативных *at-системных вызовах. "
+"Когда `dirfd` равен `AT_FDCWD`, запись `dvp` в структуре `nameidata` имеет "
+"значение `NULL`, но если `dirfd` представляет другой номер, мы получаем файл "
+"по этому дескриптору, проверяем его валидность и, если к нему прикреплен "
+"vnode, получаем этот vnode. Затем проверяем, является ли этот vnode "
+"директорией. В самой функции man:namei[9] мы просто заменяем vnode `dvp` на "
+"переменную `dp` в функции man:namei[9], которая определяет начальную точку. "
+"Функция man:namei[9] используется не напрямую, а через цепочку различных "
+"функций на разных уровнях. Например, `openat` работает следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1348
+#, no-wrap
+msgid "openat() --> kern_openat() --> vn_open() -> namei()\n"
+msgstr "openat() --> kern_openat() --> vn_open() -> namei()\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1354
+msgid ""
+"For this reason `kern_open` and `vn_open` must be altered to incorporate the "
+"additional `dirfd` parameter. No compat layer is created for those because "
+"there are not many users of this and the users can be easily converted. "
+"This general implementation enables FreeBSD to implement their own *at "
+"syscalls. This is being discussed right now."
+msgstr ""
+"По этой причине `kern_open` и `vn_open` должны быть изменены для включения "
+"дополнительного параметра `dirfd`. Слой совместимости для них не создаётся, "
+"так как пользователей этих функций немного и их можно легко адаптировать. "
+"Данная общая реализация позволяет FreeBSD реализовать свои собственные *at-"
+"системные вызовы. Это обсуждается в настоящее время."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1356
+#, no-wrap
+msgid "Ioctl"
+msgstr "Ioctl"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1370
+msgid ""
+"The ioctl interface is quite fragile due to its generality. We have to bear "
+"in mind that devices differ between Linux(R) and FreeBSD so some care must "
+"be applied to do ioctl emulation work right. The ioctl handling is "
+"implemented in [.filename]#linux_ioctl.c#, where `linux_ioctl` function is "
+"defined. This function simply iterates over sets of ioctl handlers to find "
+"a handler that implements a given command. The ioctl syscall has three "
+"parameters, the file descriptor, command and an argument. The command is a "
+"16-bit number, which in theory is divided into high 8 bits determining class "
+"of the ioctl command and low 8 bits, which are the actual command within the "
+"given set. The emulation takes advantage of this division. We implement "
+"handlers for each set, like `sound_handler` or `disk_handler`. Each handler "
+"has a maximum command and a minimum command defined, which is used for "
+"determining what handler is used. There are slight problems with this "
+"approach because Linux(R) does not use the set division consistently so "
+"sometimes ioctls for a different set are inside a set they should not belong "
+"to (SCSI generic ioctls inside cdrom set, etc.). FreeBSD currently does not "
+"implement many Linux(R) ioctls (compared to NetBSD, for example) but the "
+"plan is to port those from NetBSD. The trend is to use Linux(R) ioctls even "
+"in the native FreeBSD drivers because of the easy porting of applications."
+msgstr ""
+"Интерфейс ioctl довольно хрупок из-за своей обобщённости. Необходимо "
+"учитывать, что устройства в Linux(R) и FreeBSD различаются, поэтому "
+"требуется особая осторожность для корректной работы эмуляции ioctl. "
+"Обработка ioctl реализована в файле [.filename]#linux_ioctl.c#, где "
+"определена функция `linux_ioctl`. Эта функция просто перебирает наборы "
+"обработчиков ioctl, чтобы найти обработчик, реализующий данную команду. "
+"Системный вызов ioctl имеет три параметра: файловый дескриптор, команду и "
+"аргумент. Команда представляет собой 16-битное число, которое теоретически "
+"делится на старшие 8 бит, определяющие класс команды ioctl, и младшие 8 бит, "
+"которые являются конкретной командой в данном наборе. Эмуляция использует "
+"это разделение. Реализованы обработчики для каждого набора, такие как "
+"`sound_handler` или `disk_handler`. Каждый обработчик имеет определённые "
+"максимальную и минимальную команды, которые используются для выбора нужного "
+"обработчика. Существуют небольшие проблемы с этим подходом, поскольку "
+"Linux(R) не всегда последовательно использует разделение на наборы, поэтому "
+"иногда ioctls для другого набора оказываются внутри набора, к которому они "
+"не должны принадлежать (например, SCSI generic ioctls внутри набора cdrom и "
+"т.д.). В настоящее время FreeBSD реализует не так много ioctls Linux(R) (по "
+"сравнению с NetBSD, например), но планируется перенести их из NetBSD. "
+"Тенденция такова, что ioctls Linux(R) используются даже в родных драйверах "
+"FreeBSD для упрощения портирования приложений."
+
+#. type: Title ====
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1372
+#, no-wrap
+msgid "Debugging"
+msgstr "Отладка"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1379
+msgid ""
+"Every syscall should be debuggable. For this purpose we introduce a small "
+"infrastructure. We have the ldebug facility, which tells whether a given "
+"syscall should be debugged (settable via a sysctl). For printing we have "
+"LMSG and ARGS macros. Those are used for altering a printable string for "
+"uniform debugging messages."
+msgstr ""
+"Каждый системный вызов должен поддерживать отладку. Для этой цели мы вводим "
+"небольшую инфраструктуру. У нас есть механизм `ldebug`, который определяет, "
+"нужно ли отлаживать данный системный вызов (настраивается через `sysctl`). "
+"Для вывода сообщений используются макросы `LMSG` и `ARGS`. Они применяются "
+"для форматирования строк вывода с целью создания единообразных отладочных "
+"сообщений."
+
+#. type: Title ==
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1381
+#, no-wrap
+msgid "Conclusion"
+msgstr "Заключение"
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1384
+#, no-wrap
+msgid "Results"
+msgstr "Результаты"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1390
+msgid ""
+"As of April 2007 the Linux(R) emulation layer is capable of emulating the "
+"Linux(R) 2.6.16 kernel quite well. The remaining problems concern futexes, "
+"unfinished *at family of syscalls, problematic signals delivery, missing "
+"`epoll` and `inotify` and probably some bugs we have not discovered yet. "
+"Despite this we are capable of running basically all the Linux(R) programs "
+"included in FreeBSD Ports Collection with Fedora Core 4 at 2.6.16 and there "
+"are some rudimentary reports of success with Fedora Core 6 at 2.6.16. The "
+"Fedora Core 6 linux_base was recently committed enabling some further "
+"testing of the emulation layer and giving us some more hints where we should "
+"put our effort in implementing missing stuff."
+msgstr ""
+"По состоянию на апрель 2007 года уровень эмуляции Linux(R) способен "
+"достаточно хорошо эмулировать ядро Linux(R) 2.6.16. Оставшиеся проблемы "
+"касаются фьютексов, незавершённого семейства системных вызовов *at, "
+"проблематичной доставки сигналов, отсутствия `epoll` и `inotify`, а также, "
+"вероятно, некоторых ошибок, которые мы ещё не обнаружили. Несмотря на это, "
+"мы способны запускать практически все программы Linux(R), включённые в "
+"Коллекцию портов FreeBSD, с Fedora Core 4 на ядре 2.6.16, а также есть "
+"некоторые предварительные сообщения об успешной работе с Fedora Core 6 на "
+"ядре 2.6.16. Недавно был добавлен linux_base Fedora Core 6, что позволило "
+"провести дополнительные тестирования уровня эмуляции и дало нам больше "
+"подсказок, куда следует направить усилия для реализации недостающих функций."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1394
+msgid ""
+"We are able to run the most used applications like package:www/linux-"
+"firefox[], package:net-im/skype[] and some games from the Ports Collection. "
+"Some of the programs exhibit bad behavior under 2.6 emulation but this is "
+"currently under investigation and hopefully will be fixed soon. The only "
+"big application that is known not to work is the Linux(R) Java(TM) "
+"Development Kit and this is because of the requirement of `epoll` facility "
+"which is not directly related to the Linux(R) kernel 2.6."
+msgstr ""
+"Мы можем запускать наиболее популярные приложения, такие как package:www/"
+"linux-firefox[], package:net-im/skype[], и некоторые игры из Коллекции "
+"портов. Некоторые программы демонстрируют некорректное поведение при "
+"эмуляции 2.6, но это в настоящее время исследуется, и, надеемся, скоро будет "
+"исправлено. Единственное крупное приложение, которое, как известно, не "
+"работает, — это Linux(R) Java(TM) Development Kit, и это связано с "
+"требованием функции `epoll`, которая не имеет прямого отношения к ядру "
+"Linux(R) 2.6."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1397
+msgid ""
+"We hope to enable 2.6.16 emulation by default some time after FreeBSD 7.0 is "
+"released at least to expose the 2.6 emulation parts for some wider testing. "
+"Once this is done we can switch to Fedora Core 6 linux_base, which is the "
+"ultimate plan."
+msgstr ""
+"Мы надеемся включить эмуляцию 2.6.16 по умолчанию через некоторое время "
+"после выхода FreeBSD 7.0, по крайней мере, чтобы открыть части эмуляции 2.6 "
+"для более широкого тестирования. Как только это будет сделано, мы сможем "
+"перейти на Fedora Core 6 linux_base, что является конечной целью."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1399
+#, no-wrap
+msgid "Future work"
+msgstr "Будущие работы"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1402
+msgid ""
+"Future work should focus on fixing the remaining issues with futexes, "
+"implement the rest of the *at family of syscalls, fix the signal delivery "
+"and possibly implement the `epoll` and `inotify` facilities."
+msgstr ""
+"Будущая работа должна быть сосредоточена на исправлении оставшихся проблем с "
+"фьютексами, реализации оставшихся системных вызовов семейства *at, "
+"исправлении доставки сигналов и, возможно, реализации механизмов `epoll` и "
+"`inotify`."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1404
+msgid ""
+"We hope to be able to run the most important programs flawlessly soon, so we "
+"will be able to switch to the 2.6 emulation by default and make the Fedora "
+"Core 6 the default linux_base because our currently used Fedora Core 4 is "
+"not supported any more."
+msgstr ""
+"Мы надеемся вскоре добиться безупречной работы наиболее важных программ, "
+"чтобы можно было по умолчанию переключиться на эмуляцию 2.6 и сделать Fedora "
+"Core 6 базовой версией linux_base, поскольку используемая в настоящее время "
+"Fedora Core 4 больше не поддерживается."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1408
+msgid ""
+"The other possible goal is to share our code with NetBSD and DragonflyBSD. "
+"NetBSD has some support for 2.6 emulation but its far from finished and not "
+"really tested. DragonflyBSD has expressed some interest in porting the 2.6 "
+"improvements."
+msgstr ""
+"Другая возможная цель — поделиться нашим кодом с NetBSD и DragonflyBSD. "
+"NetBSD имеет некоторую поддержку эмуляции 2.6, но она далека от завершения и "
+"не была должным образом протестирована. DragonflyBSD выразила некоторую "
+"заинтересованность в переносе улучшений версии 2.6."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1413
+msgid ""
+"Generally, as Linux(R) develops we would like to keep up with their "
+"development, implementing newly added syscalls. Splice comes to mind "
+"first. Some already implemented syscalls are also suboptimal, for example "
+"`mremap` and others. Some performance improvements can also be made, finer "
+"grained locking and others."
+msgstr ""
+"В целом, по мере развития Linux(R) мы хотели бы идти в ногу с их "
+"разработкой, реализуя новые системные вызовы. В первую очередь на ум "
+"приходит `splice`. Некоторые уже реализованные системные вызовы также "
+"неоптимальны, например `mremap` и другие. Также можно внести некоторые "
+"улучшения производительности, такие как более детальная блокировка и другие."
+
+#. type: Title ===
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1415
+#, no-wrap
+msgid "Team"
+msgstr "Команда"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1418
+msgid "I cooperated on this project with (in alphabetical order):"
+msgstr "Я сотрудничал в этом проекте с (в алфавитном порядке):"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1420
+msgid "`{jhb}`"
+msgstr "`{jhb}`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1421
+msgid "`{kib}`"
+msgstr "`{kib}`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1422
+msgid "Emmanuel Dreyfus"
+msgstr "Emmanuel Dreyfus"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1423
+msgid "Scot Hetzel"
+msgstr "Scot Hetzel"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1424
+msgid "`{jkim}`"
+msgstr "`{jkim}`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1425
+msgid "`{netchild}`"
+msgstr "`{netchild}`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1426
+msgid "`{ssouhlal}`"
+msgstr "`{ssouhlal}`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1427
+msgid "Li Xiao"
+msgstr "Li Xiao"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1428
+msgid "`{davidxu}`"
+msgstr "`{davidxu}`"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1430
+msgid ""
+"I would like to thank all those people for their advice, code reviews and "
+"general support."
+msgstr ""
+"Я хотел бы поблагодарить всех этих людей за их советы, рецензирование кода и "
+"общую поддержку."
+
+#. type: Title ==
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1432
+#, no-wrap
+msgid "Literatures"
+msgstr "Литература"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1435
+msgid ""
+"Marshall Kirk McKusick - George V. Nevile-Neil. Design and Implementation of "
+"the FreeBSD operating system. Addison-Wesley, 2005."
+msgstr ""
+"Marshall Kirk McKusick - George V. Neville-Neil. Design and Implementation "
+"of the FreeBSD operating system. Addison-Wesley, 2005 год."
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1436
+msgid "https://tldp.org[https://tldp.org]"
+msgstr "https://tldp.org[https://tldp.org]"
+
+#. type: Plain text
+#: documentation/content/en/articles/linux-emulation/_index.adoc:1436
+msgid "https://www.kernel.org[https://www.kernel.org]"
+msgstr "https://www.kernel.org[https://www.kernel.org]"
diff --git a/documentation/content/ru/articles/vm-design/_index.adoc b/documentation/content/ru/articles/vm-design/_index.adoc
index 99af966d20..e0847dea68 100644
--- a/documentation/content/ru/articles/vm-design/_index.adoc
+++ b/documentation/content/ru/articles/vm-design/_index.adoc
@@ -1,9 +1,12 @@
---
-title: Элементы архитектуры системы виртуальной памяти во FreeBSD
authors:
- - author: Matthew Dillon
+ -
+ author: 'Matthew Dillon'
email: dillon@apollo.backplane.com
-trademarks: ["freebsd", "linux", "microsoft", "opengroup", "general"]
+description: 'Простое и понятное описание архитектуры системы виртуальной памяти FreeBSD'
+tags: ["Design", "virtual machine", "FreeBSD"]
+title: 'Элементы архитектуры системы виртуальной памяти во FreeBSD'
+trademarks: ["freebsd", "linux", "microsoft", "opengroup", "daemon-news", "general"]
---
= Элементы архитектуры системы виртуальной памяти во FreeBSD
@@ -37,10 +40,17 @@ ifndef::env-beastie[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
+[NOTE]
+====
+Этот документ устарел, и некоторые разделы больше не соответствуют текущему состоянию системы виртуальной памяти. Он сохранён в исторических целях и может быть обновлён в будущем.
+====
+
[.abstract-title]
Аннотация
-Название статьи говорит лишь о том, что я попытаюсь описать в целом VM-систему понятным языком. Последний год я сосредоточил усилия в работе над несколькими основными подсистемами ядра FreeBSD, среди которых подсистемы VM и подкачки были самыми интересными, а NFS оказалась "необходимой рутиной". Я переписал лишь малую часть кода. Что касается VM, то я единственным большим обновлением, которое я сделал, является переделка подсистемы подкачки. Основная часть моей работы заключалась в зачистке и поддержке кода, с единственной заметной переделкой кода и без значительной переделки алгоритмов в VM-подсистеме. В основном теоретическая база работы VM-подсистемы осталась неизменной, а большинство благодарностей за современных нововведения за последние несколько лет принадлежат John Dyson и David Greenman. Не являясь историком, как Керк, я не буду пытаться связать различные возможности системы с именами, потому что обязательно ошибусь.
+Matthew Dillon <dillon@apollo.backplane.com>
+
+Это название — просто замысловатый способ сказать, что я попытаюсь описать всю систему виртуальной памяти (VM) целиком, по возможности так, чтобы это было понятно каждому.В течение последнего года я сосредоточился на нескольких основных подсистемах ядра FreeBSD. Наиболее интересными из них стали подсистемы VM и подкачки (Swap), тогда как работа с NFS оказалась, скорее, «необходимой рутиной». Я переписал лишь небольшие части кода. В области VM моей единственной крупной переработкой стала подсистема подкачки. В основном моя работа заключалась в очистке и поддержке кода, с умеренными правками и без серьёзных изменений алгоритмов в подсистеме VM. Теоретическая основа VM-подсистемы осталась неизменной, и львиная доля заслуг в её модернизации за последние годы принадлежит Джону Дайсону и Дэвиду Гринману. Я не историк, в отличие от Кирка, поэтому не стану приписывать различные функции конкретным людям — всё равно где-нибудь ошибусь.
'''
@@ -69,7 +79,7 @@ toc::[]
FreeBSD управляет всем этим при помощи многоуровневой модели VM-объектов. Исходный файл с двоичной программой переносится на самый нижний уровень объектов VM. Уровень страниц, копируемых при записи, находится выше него, и хранит те страницы, которые были скопированы из исходного файла. Если программа модифицирует страницы данных, относящиеся к исходному файлу, то система VM обнаруживает это и переносит копию этой страницы на более высокий уровень. Когда процесс разветвляется, добавляются новые уровни VM-объектов. Это можно показать на простом примере. Функция `fork()` является общей операцией для всех систем *BSD, так что в этом примере будет рассматриваться программа, которая запускается, а затем разветвляется. Когда процесс запускается, VM-система создает некоторый уровень объектов, обозначим его A:
-image::fig1.png[Рисунок]
+image::fig1.png["Рисунок"]
A соответствует файлу-по необходимости страницы памяти могут высвобождаться и подгружаться с носителя файла. Подгрузка с диска может потребоваться программе, однако на самом деле мы не хотим, чтобы она записывалась обратно в файл. Поэтому VM-система создает второй уровень, B, который физически поддерживается дисковым пространством подкачки:
@@ -140,20 +150,13 @@ FreeBSD использует несколько очередей страниц
Большой процент ошибок доступа к страницам, относится к ошибкам при заполнении нулями. Вы можете обычно видеть это, просматривая вывод команды `vmstat -s`. Это происходит, когда процесс обращается к страницам в своей области BSS. Область BSS предполагается изначально заполненной нулями, но VM-система не заботится о выделении памяти до тех пор, пока процесс реально к ней не обратится. При возникновении ошибки VM-система должна не только выделить новую страницу, но и заполнить ее нулями. Для оптимизации операции по заполнению нулями в системе VM имеется возможность предварительно обнулять страницы и помечать их, и запрашивать уже обнуленные страницы при возникновении ошибок заполнения нулями. Предварительное заполнение нулями происходит, когда CPU простаивает, однако количество страниц, которые система заранее заполняет нулями, ограничено, для того, чтобы не переполнить кэши памяти. Это прекрасный пример добавления сложности в VM-систему ради оптимизации критического пути.
-[[pre-table-optimizations]]
+[[page-table-optimizations]]
== Оптимизация таблицы страниц
Оптимизация таблицы страниц составляет самую содержательную часть архитектуры VM во FreeBSD и она проявляется при появлении нагрузки при значительном использовании `mmap()`. Я думаю, что это на самом деле особенность работы большинства BSD-систем, хотя я не уверен, когда это проявилось впервые. Есть два основных подхода к оптимизации. Первый заключается в том, что аппаратные таблицы страниц не содержат постоянного состояния, а вместо этого могут быть сброшены в любой момент с малыми накладными расходами. Второй подход состоит в том, что каждая активная таблица страниц в системе имеет управляющую структуру `pv_entry`, которая связана в структуру `vm_page`. FreeBSD может просто просматривать эти отображения, которые существуют, когда как в Linux должны проверяться все таблицы страниц, которые _могут_ содержать нужное отображение, что в некоторых ситуация дает увеличение сложности O(n^2). Из-за того, что FreeBSD стремится выбрать наиболее подходящую к повторному использованию или сбросу в область подкачки страницу, когда ощущается нехватка памяти, система дает лучшую производительность при нагрузке. Однако во FreeBSD требуется тонкая настройка ядра для соответствия ситуациям с большим совместно используемым адресным пространством, которые могут случиться в системе, обслуживающей сервер телеконференций, потому что структуры `pv_entry` могут оказаться исчерпанными.
И в Linux, и во FreeBSD требуются доработки в этой области. FreeBSD пытается максимизировать преимущества от потенциально редко применяемой модели активного отображения (к примеру, не всем процессам нужно отображать все страницы динамической библиотеки), когда как Linux пытается упростить свои алгоритмы. FreeBSD имеет здесь общее преимущество в производительности за счет использования дополнительной памяти, но FreeBSD выглядит хуже в случае, когда большой файл совместно используется сотнями процессов. Linux, с другой стороны, выглядит хуже в случае, когда много процессов частично используют одну и ту же динамическую библиотеку, а также работает неоптимально при попытке определить, может ли страница повторно использоваться, или нет.
-[[page-coloring-optimizations]]
-== Подгонка страниц
-
-Мы закончим рассмотрением метода оптимизации подгонкой страниц. Подгонка является методом оптимизации, разработанным для того, чтобы доступ в последовательные страницы виртуальной памяти максимально использовал кэш процессора. В далеком прошлом (то есть больше 10 лет назад) процессорные кэши предпочитали отображать виртуальную память, а не физическую. Это приводило к огромному количеству проблем, включая необходимость очистки кэша в некоторых случаях при каждом переключении контекста и проблемы с замещением данных в кэше. В современных процессорах кэши отображают физическую память именно для решения этих проблем. Это означает, что две соседние страницы в адресном пространстве процессов могут не соответствовать двух соседним страницам в кэше. Фактически, если вы об этом не позаботились, то соседние страницы в виртуальной памяти могут использовать ту же самую страницу в кэше процессора-это приводит к сбросу кэшируемых данных и снижению производительности CPU. Это так даже с множественными ассоциативными кэшами (хотя здесь эффект несколько сглажен).
-
-Код выделения памяти во FreeBSD выполняет оптимизацию с применением подгонки страниц, означающую то, что код выделения памяти будет пытаться найти свободные страницы, которые являются последовательными с точки зрения кэша. Например, если страница 16 физической памяти назначается странице 0 виртуальной памяти процесса, а в кэш помещается 4 страницы, то код подгонки страниц не будет назначать страницу 20 физической памяти странице 1 виртуальной памяти процесса. Вместо этого будет назначена страница 21 физической памяти. Код подгонки страниц попытается избежать назначение страницы 20, потому что такое отображение перекрывается в той же самой памяти кэша как страница 16, и приведет к неоптимальному кэшированию. Как вы можете предположить, такой код значительно добавляет сложности в подсистему выделения памяти VM, но результат стоит того. Подгонка страниц делает память VM предсказуемой, как и обычная физическая память, относительно производительности кэша.
-
[[conclusion]]
== Заключение
@@ -201,23 +204,3 @@ FreeBSD 3.X использует "последовательный список
Но во FreeBSD имеется проблема масштабирования, которой нет в Linux, потому что имеется ограниченное число структур `pv_entry`, и это приводит к возникновению проблем при большом объеме совместно используемых данных. В этом случае у вас может возникнуть нехватка структур `pv_entry`, даже если свободной памяти хватает. Это может быть достаточно легко исправлено увеличением количества структур `pv_entry` при настройке, но на самом деле нам нужно найти лучший способ делать это.
Что касается использования памяти под таблицу страниц против схемы с `pv_entry`: Linux использует "постоянные" таблицы страниц, которые не сбрасываются, но ему не нужны `pv_entry` для каждого потенциально отображаемого pte. FreeBSD использует "сбрасываемые" таблицы страниц, но для каждого реально отображаемого pte добавляется структура `pv_entry`. Я думаю, что использование памяти будет примерно одинакова, тем более что у FreeBSD есть алгоритмическое преимущество, заключающееся в способности сбрасывать таблицы страниц с очень малыми накладными расходами.
-
-=== Наконец, в разделе о подгонке страниц хорошо бы было иметь краткое описание того, что это значит. Я не совсем это понял.
-
-Знаете ли вы, как работает аппаратный кэш памяти L1? Объясняю: Представьте машину с 16МБ основной памяти и только со 128К памяти кэша L1. В общем, этот кэш работает так, что каждый блок по 128К основной памяти использует _те же самые_ 128К кэша. Если вы обращаетесь к основной памяти по смещению 0, а затем к основной памяти по смещению 128К, вы перезаписываете данные кэша, прочтенные по смещению 0!
-
-Я очень сильно все упрощаю. То, что я только что описал, называется "напрямую отображаемым" аппаратным кэшем памяти. Большинство современных кэшей являются так называемыми 2-сторонними множественными ассоциативными или 4-сторонними множественными ассоциативными кэшами. Множественная ассоциативность позволяет вам обращаться к вплоть до N различным областям памяти, которые используют одну и ту же память кэша без уничтожения ранее помещенных в кэш данных. Но только N.
-
-Так что если у меня имеется 4-сторонний ассоциативный кэш, я могу обратиться к памяти по смещению 0, смещению 128К, 256К и смещению 384K, затем снова обратиться к памяти по смещению 0 и получу ее из кэша L1. Однако, если после этого я обращусь к памяти по смещению 512К, один из ранее помещенных в кэш объектов данных будет из кэша удален.
-
-Это чрезвычайно важно... для большинства обращений к памяти процессора _чрезвычайно_ важно, чтобы данные находились в кэше L1, так как кэш L1 работает на тактовой частоте работы процессора. В случае, если данных в кэше L1 не обнаруживается, и они ищутся в кэше L2 или в основной памяти, процессор будет простаивать, или, скорее, сидеть, сложив ручки, в ожидании окончания чтения из основной памяти, хотя за это время можно было выполнить _сотни_ операций. Основная память (динамическое ОЗУ, которое установлено в компьютере) работает по сравнению со скоростью работы ядра современных процессоров __медленно__.
-
-Хорошо, а теперь рассмотрим подгонку страниц: Все современные кэши памяти являются так называемыми _физическими_ кэшами. Они кэшируют адреса физической памяти, а не виртуальной. Это позволяет кэшу не принимать во внимание переключение контекстов процессов, что очень важно.
-
-Но в мире UNIX(R) вы работаете с виртуальными адресными пространствами, а не с физическими. Любая программа, вами написанная, имеет дело с виртуальным адресным пространством, ей предоставленным. Реальные _физические_ страницы, соответствующие виртуальному адресному пространству, не обязательно расположены физически последовательно! На самом деле у вас могут оказаться две страницы, которые в адресном пространстве процессов являются граничащими, но располагающимися по смещению 0 и по смещению 128К в _физической_ памяти.
-
-Обычно программа полагает, что две граничащие страницы будут кэшироваться оптимально. То есть вы можете обращаться к объектам данных в обеих страницах без замещений в кэше данных друг друга. Но это имеет место, если только физические страницы, соответствующие виртуальному адресному пространству, располагаются рядом (в такой мере, что попадают в кэш).
-
-Это именно то, что выполняет подгонка. Вместо того, чтобы назначать _случайные_ физические страницы виртуальным адресам, что может привести к неоптимальной работе кэша, при подгонке страниц виртуальным адресам назначаются _примерно подходящие по порядку_ физические страницы. Таким образом, программы могут писаться в предположении, что характеристики низлежащего аппаратного кэша для виртуального адресного пространства будут такими же, как если бы программа работала непосредственно в физическом адресном пространстве.
-
-Заметьте, что я сказал "примерно" подходящие, а не просто "последовательные". С точки зрения напрямую отображаемого кэша в 128К, физический адрес 0 одинаков с физическим адресом 128К. Так что две граничащие страницы в вашем виртуальном адресном пространстве могут располагаться по смещению 128К и 132К физической памяти, но могут легко находиться по смещению 128К и по смещению 4К физической памяти, и иметь те же самые характеристики работы кэша. Так что при подгонке _не нужно_ назначать в действительности последовательные страницы физической памяти последовательным страницам виртуальной памяти, достаточно просто добиться расположения страниц по соседству друг с другом с точки зрения работы кэша.
diff --git a/documentation/content/ru/articles/vm-design/_index.po b/documentation/content/ru/articles/vm-design/_index.po
new file mode 100644
index 0000000000..8dda5aa52d
--- /dev/null
+++ b/documentation/content/ru/articles/vm-design/_index.po
@@ -0,0 +1,1356 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:25+0300\n"
+"PO-Revision-Date: 2025-07-05 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/articlesvm-design_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/articles/vm-design/_index.adoc:1
+#, no-wrap
+msgid "An easy to follow description of the design of the FreeBSD virtual memory system"
+msgstr "Простое и понятное описание архитектуры системы виртуальной памяти FreeBSD"
+
+#. type: Title =
+#: documentation/content/en/articles/vm-design/_index.adoc:1
+#: documentation/content/en/articles/vm-design/_index.adoc:11
+#, no-wrap
+msgid "Design elements of the FreeBSD VM system"
+msgstr "Элементы архитектуры системы виртуальной памяти во FreeBSD"
+
+#. type: delimited block = 4
+#: documentation/content/en/articles/vm-design/_index.adoc:46
+msgid ""
+"This document is outdated and some sections do not accurately describe the "
+"current state of the VM system. It is retained for historical purposes and "
+"may be updated over time."
+msgstr ""
+"Этот документ устарел, и некоторые разделы больше не соответствуют текущему "
+"состоянию системы виртуальной памяти. Он сохранён в исторических целях и "
+"может быть обновлён в будущем."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:50
+msgid "Abstract"
+msgstr "Аннотация"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:52
+msgid "Matthew Dillon <dillon@apollo.backplane.com>"
+msgstr "Matthew Dillon <dillon@apollo.backplane.com>"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:59
+msgid ""
+"The title is really just a fancy way of saying that I am going to attempt to "
+"describe the whole VM enchilada, hopefully in a way that everyone can "
+"follow. For the last year I have concentrated on a number of major kernel "
+"subsystems within FreeBSD, with the VM and Swap subsystems being the most "
+"interesting and NFS being \"a necessary chore\". I rewrote only small "
+"portions of the code. In the VM arena the only major rewrite I have done is "
+"to the swap subsystem. Most of my work was cleanup and maintenance, with "
+"only moderate code rewriting and no major algorithmic adjustments within the "
+"VM subsystem. The bulk of the VM subsystem's theoretical base remains "
+"unchanged and a lot of the credit for the modernization effort in the last "
+"few years belongs to John Dyson and David Greenman. Not being a historian "
+"like Kirk I will not attempt to tag all the various features with peoples "
+"names, since I will invariably get it wrong."
+msgstr ""
+"Это название — просто замысловатый способ сказать, что я попытаюсь описать "
+"всю систему виртуальной памяти (VM) целиком, по возможности так, чтобы это "
+"было понятно каждому.В течение последнего года я сосредоточился на "
+"нескольких основных подсистемах ядра FreeBSD. Наиболее интересными из них "
+"стали подсистемы VM и подкачки (Swap), тогда как работа с NFS оказалась, "
+"скорее, «необходимой рутиной». Я переписал лишь небольшие части кода. В "
+"области VM моей единственной крупной переработкой стала подсистема подкачки. "
+"В основном моя работа заключалась в очистке и поддержке кода, с умеренными "
+"правками и без серьёзных изменений алгоритмов в подсистеме VM. Теоретическая "
+"основа VM-подсистемы осталась неизменной, и львиная доля заслуг в её "
+"модернизации за последние годы принадлежит Джону Дайсону и Дэвиду Гринману. "
+"Я не историк, в отличие от Кирка, поэтому не стану приписывать различные "
+"функции конкретным людям — всё равно где-нибудь ошибусь."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:61
+msgid "'''"
+msgstr "'''"
+
+#. type: Title ==
+#: documentation/content/en/articles/vm-design/_index.adoc:65
+#, no-wrap
+msgid "Introduction"
+msgstr "Введение"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:81
+msgid ""
+"Before moving along to the actual design let's spend a little time on the "
+"necessity of maintaining and modernizing any long-living codebase. In the "
+"programming world, algorithms tend to be more important than code and it is "
+"precisely due to BSD's academic roots that a great deal of attention was "
+"paid to algorithm design from the beginning. More attention paid to the "
+"design generally leads to a clean and flexible codebase that can be fairly "
+"easily modified, extended, or replaced over time. While BSD is considered "
+"an \"old\" operating system by some people, those of us who work on it tend "
+"to view it more as a \"mature\" codebase which has various components "
+"modified, extended, or replaced with modern code. It has evolved, and "
+"FreeBSD is at the bleeding edge no matter how old some of the code might "
+"be. This is an important distinction to make and one that is unfortunately "
+"lost to many people. The biggest error a programmer can make is to not "
+"learn from history, and this is precisely the error that many other modern "
+"operating systems have made. Windows NT(R) is the best example of this, and "
+"the consequences have been dire. Linux also makes this mistake to some "
+"degree-enough that we BSD folk can make small jokes about it every once in a "
+"while, anyway. Linux's problem is simply one of a lack of experience and "
+"history to compare ideas against, a problem that is easily and rapidly being "
+"addressed by the Linux community in the same way it has been addressed in "
+"the BSD community-by continuous code development. The Windows NT(R) folk, "
+"on the other hand, repeatedly make the same mistakes solved by UNIX(R) "
+"decades ago and then spend years fixing them. Over and over again. They "
+"have a severe case of \"not designed here\" and \"we are always right "
+"because our marketing department says so\". I have little tolerance for "
+"anyone who cannot learn from history."
+msgstr ""
+"Перед тем, как перейти непосредственно к существующей архитектуре, потратим "
+"немного времени на рассмотрение вопроса о необходимости поддержки и "
+"модернизации любого длительно живущего кода. В мире программирования "
+"алгоритмы становятся более важными, чем код, и именно из-за академических "
+"корней BSD изначально большое внимание уделялось проработке алгоритмов. "
+"Внимание, уделенное архитектуре, в общем отражается на ясности и гибкости "
+"кода, который может быть достаточно легко изменен, расширен или с течением "
+"времени заменен. Хотя некоторые считают BSD \"старой\" операционной "
+"системой, те их нас, кто работает над ней, видят ее скорее системой со "
+"\"зрелым\" кодом с различными компонентами, которые были заменены, расширены "
+"или изменены современным кодом. Он развивается, и FreeBSD остается передовой "
+"системой, вне зависимости от того, насколько старой может быть часть кода. "
+"Это важное отличие, которое, к сожалению, не всеми понимается. Самой большой "
+"ошибкой, которую может допустить программист, является игнорирование "
+"истории, и это именно та ошибка, которую сделали многие другие современные "
+"операционные системы. Самым ярки примером здесь является Windows NT(R), и "
+"последствия ужасны. Linux также в некоторой степени совершил эту ошибку-"
+"достаточно, чтобы мы, люди BSD, по крайней мере по разу отпустили по этому "
+"поводу шутку. Проблема Linux заключается просто в отсутствии опыта и истории "
+"для сравнения идей, проблема, которая легко и быстро решается сообществом "
+"Linux точно так же, как она решается в сообществе BSD-постоянной работой над "
+"кодом. Разработчики Windows NT(R), с другой стороны, постоянно совершают те "
+"же самые ошибки, что были решены в UNIX(R) десятки лет назад, а затем тратят "
+"годы на их устранение. Снова и снова. Есть несколько случаев \"проработка "
+"архитектуры отсутствует\" и \"мы всегда правы, потому что так говорит наш "
+"отдел продаж\". Я плохо переношу тех, кого не учит история."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:86
+msgid ""
+"Much of the apparent complexity of the FreeBSD design, especially in the VM/"
+"Swap subsystem, is a direct result of having to solve serious performance "
+"issues that occur under various conditions. These issues are not due to bad "
+"algorithmic design but instead rise from environmental factors. In any "
+"direct comparison between platforms, these issues become most apparent when "
+"system resources begin to get stressed. As I describe FreeBSD's VM/Swap "
+"subsystem the reader should always keep two points in mind:"
+msgstr ""
+"Большинство очевидной сложности архитектуры FreeBSD, особенно в подсистеме "
+"VM/Swap, является прямым следствием того, что она решает серьезные проблемы "
+"с производительностью, которые проявляются при различных условиях. Эти "
+"проблемы вызваны не плохой проработкой алгоритмов, а возникают из окружающих "
+"факторов. В любом прямом сравнении между платформами эти проблемы "
+"проявляются, когда системные ресурсы начинают истощаться. Так как я описываю "
+"подсистему VM/Swap во FreeBSD, то читатель должен всегда иметь в виду два "
+"обстоятельства:"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:88
+msgid ""
+"The most important aspect of performance design is what is known as "
+"\"Optimizing the Critical Path\". It is often the case that performance "
+"optimizations add a little bloat to the code to make the critical path "
+"perform better."
+msgstr ""
+"Самым важным аспектом при проектировании производительности является то, что "
+"называется \"оптимизацией критического маршрута\". Часто случается, что "
+"оптимизация производительности дает прирост объема кода ради того, чтобы "
+"критический маршрут работал быстрее."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:89
+msgid ""
+"A solid, generalized design outperforms a heavily-optimized design over the "
+"long run. While a generalized design may end up being slower than an heavily-"
+"optimized design when they are first implemented, the generalized design "
+"tends to be easier to adapt to changing conditions and the heavily-optimized "
+"design winds up having to be thrown away."
+msgstr ""
+"Четкость общей архитектуры оказывается лучше сильно оптимизированной "
+"архитектуры с течением времени. Когда как обобщенная архитектура может быть "
+"медленнее, чем оптимизированная архитектура, при первой реализации, при "
+"обобщенной архитектуре легче подстраиваться под изменяющиеся условия и "
+"чрезмерно оптимизированная архитектура оказывается непригодной."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:93
+msgid ""
+"Any codebase that will survive and be maintainable for years must therefore "
+"be designed properly from the beginning even if it costs some performance. "
+"Twenty years ago people were still arguing that programming in assembly was "
+"better than programming in a high-level language because it produced code "
+"that was ten times as fast. Today, the fallibility of that argument is "
+"obvious - as are the parallels to algorithmic design and code generalization."
+msgstr ""
+"Любой код, который должен выжить и поддаваться поддержке годы, должен "
+"поэтому быть тщательно продуман с самого начала, даже если это стоит потери "
+"производительности. Двадцать лет назад были те, кто отстаивал преимущество "
+"программирования на языке ассемблера перед программированием на языке "
+"высокого уровня, потому что первый генерировал в десять раз более быстрый "
+"код. В наши дни ошибочность этого аргумента очевидна - можно провести "
+"параллели с построением алгоритмов и обобщением кода."
+
+#. type: Title ==
+#: documentation/content/en/articles/vm-design/_index.adoc:95
+#, no-wrap
+msgid "VM Objects"
+msgstr "Объекты VM"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:105
+msgid ""
+"The best way to begin describing the FreeBSD VM system is to look at it from "
+"the perspective of a user-level process. Each user process sees a single, "
+"private, contiguous VM address space containing several types of memory "
+"objects. These objects have various characteristics. Program code and "
+"program data are effectively a single memory-mapped file (the binary file "
+"being run), but program code is read-only while program data is copy-on-"
+"write. Program BSS is just memory allocated and filled with zeros on "
+"demand, called demand zero page fill. Arbitrary files can be memory-mapped "
+"into the address space as well, which is how the shared library mechanism "
+"works. Such mappings can require modifications to remain private to the "
+"process making them. The fork system call adds an entirely new dimension to "
+"the VM management problem on top of the complexity already given."
+msgstr ""
+"Лучше всего начать описание VM-системы FreeBSD с попытки взглянуть на нее с "
+"точки зрения пользовательского процесса. Каждый пользовательский процесс "
+"имеет единое, принадлежащее только ему и неразрывное адресное пространство "
+"VM, содержащее несколько типов объектов памяти. Эти объекты имеют различные "
+"характеристики. Код программы и ее данные являются единым файлом, "
+"отображаемым в память (это выполняющийся двоичный файл), однако код "
+"программы доступен только для чтения, когда как данные программы размещаются "
+"в режиме копирования-при-записи. BSS программы представляет собой всего лишь "
+"выделенную область памяти, заполненную, если это требовалось, нулями, что "
+"называется обнулением страниц памяти по требованию. Отдельные файлы могут "
+"также отображаться в адресное пространство, именно так работают динамические "
+"библиотеки. Такие отображения требуют изменений, чтобы оставаться "
+"принадлежащими процессу, который их выполнил. Системный вызов fork добавляет "
+"переводит проблему управления VM полностью в новую плоскость, вдобавок к уже "
+"имеющимся сложностям."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:111
+msgid ""
+"A program binary data page (which is a basic copy-on-write page) illustrates "
+"the complexity. A program binary contains a preinitialized data section "
+"which is initially mapped directly from the program file. When a program is "
+"loaded into a process's VM space, this area is initially memory-mapped and "
+"backed by the program binary itself, allowing the VM system to free/reuse "
+"the page and later load it back in from the binary. The moment a process "
+"modifies this data, however, the VM system must make a private copy of the "
+"page for that process. Since the private copy has been modified, the VM "
+"system may no longer free it, because there is no longer any way to restore "
+"it later on."
+msgstr ""
+"Иллюстрирует сложность страница данных двоичной программы (которая является "
+"страницей копируемой-при-записи). Двоичная программа содержит секцию "
+"предварительно инициализированных данных, которая первоначально отображается "
+"непосредственно из файла программы. Когда программа загружается в Vm-"
+"пространство процесса, эта область сначала отображается в память и "
+"поддерживается бинарным файлом программы, позволяя VM-системе освобождать/"
+"повторно использовать страницу, а потом загружать ее снова из бинарного "
+"файла. Однако в момент, когда процесс изменяет эти данные, VM-система должна "
+"сделать копию страницы, принадлежащую только этому процессу. Так как эта "
+"копия была изменена, то VM-система не может больше освобождать эту страницу, "
+"так как впоследствии ее невозможно будет восстановить."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:118
+msgid ""
+"You will notice immediately that what was originally a simple file mapping "
+"has become much more complex. Data may be modified on a page-by-page basis "
+"whereas the file mapping encompasses many pages at once. The complexity "
+"further increases when a process forks. When a process forks, the result is "
+"two processes-each with their own private address spaces, including any "
+"modifications made by the original process prior to the call to `fork()`. "
+"It would be silly for the VM system to make a complete copy of the data at "
+"the time of the `fork()` because it is quite possible that at least one of "
+"the two processes will only need to read from that page from then on, "
+"allowing the original page to continue to be used. What was a private page "
+"is made copy-on-write again, since each process (parent and child) expects "
+"their own personal post-fork modifications to remain private to themselves "
+"and not affect the other."
+msgstr ""
+"Вы тут же заметите, что то, что сначала было простым отображением файла в "
+"память, становится гораздо более сложным предметом. Данные могут "
+"модифицироваться постранично, когда как отображение файла выполняется для "
+"многих страниц за раз. Сложность еще более увеличивается, когда процесс "
+"выполняет вызов fork. При этом порождаются два процесса-каждый со с "
+"собственным адресным пространством, включающим все изменения, выполненные "
+"исходным процессом до вызова функции `fork()`. Было бы глупо для VM-системы "
+"делать полную копию данных во время вызова `fork()`, так как весьма "
+"вероятно, что один из двух процессов будет нужен только для чтения из той "
+"страницы, что позволяет использование исходной страницы. То, что было "
+"страницей, принадлежащей только процессу, сделается снова страницей, "
+"копируемой при записи, так как каждый из процессов (и родитель, и потомок) "
+"полагают, что их собственные изменения после разветвления будут принадлежать "
+"только им, и не затронут родственный процесс."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:127
+msgid ""
+"FreeBSD manages all of this with a layered VM Object model. The original "
+"binary program file winds up being the lowest VM Object layer. A copy-on-"
+"write layer is pushed on top of that to hold those pages which had to be "
+"copied from the original file. If the program modifies a data page "
+"belonging to the original file the VM system takes a fault and makes a copy "
+"of the page in the higher layer. When a process forks, additional VM Object "
+"layers are pushed on. This might make a little more sense with a fairly "
+"basic example. A `fork()` is a common operation for any *BSD system, so "
+"this example will consider a program that starts up, and forks. When the "
+"process starts, the VM system creates an object layer, let's call this A:"
+msgstr ""
+"FreeBSD управляет всем этим при помощи многоуровневой модели VM-объектов. "
+"Исходный файл с двоичной программой переносится на самый нижний уровень "
+"объектов VM. Уровень страниц, копируемых при записи, находится выше него, и "
+"хранит те страницы, которые были скопированы из исходного файла. Если "
+"программа модифицирует страницы данных, относящиеся к исходному файлу, то "
+"система VM обнаруживает это и переносит копию этой страницы на более высокий "
+"уровень. Когда процесс разветвляется, добавляются новые уровни VM-объектов. "
+"Это можно показать на простом примере. Функция `fork()` является общей "
+"операцией для всех систем *BSD, так что в этом примере будет рассматриваться "
+"программа, которая запускается, а затем разветвляется. Когда процесс "
+"запускается, VM-система создает некоторый уровень объектов, обозначим его A:"
+
+#. type: Positional ($1) AttributeList argument for macro 'image'
+#: documentation/content/en/articles/vm-design/_index.adoc:128
+#, no-wrap
+msgid "A picture"
+msgstr "Рисунок"
+
+#. type: Target for macro image
+#: documentation/content/en/articles/vm-design/_index.adoc:128
+#, no-wrap
+msgid "fig1.png"
+msgstr "fig1.png"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:133
+msgid ""
+"A represents the file-pages may be paged in and out of the file's physical "
+"media as necessary. Paging in from the disk is reasonable for a program, "
+"but we really do not want to page back out and overwrite the executable. "
+"The VM system therefore creates a second layer, B, that will be physically "
+"backed by swap space:"
+msgstr ""
+"A соответствует файлу-по необходимости страницы памяти могут высвобождаться "
+"и подгружаться с носителя файла. Подгрузка с диска может потребоваться "
+"программе, однако на самом деле мы не хотим, чтобы она записывалась обратно "
+"в файл. Поэтому VM-система создает второй уровень, B, который физически "
+"поддерживается дисковым пространством подкачки:"
+
+#. type: Target for macro image
+#: documentation/content/en/articles/vm-design/_index.adoc:134
+#, no-wrap
+msgid "fig2.png"
+msgstr "fig2.png"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:139
+msgid ""
+"On the first write to a page after this, a new page is created in B, and its "
+"contents are initialized from A. All pages in B can be paged in or out to a "
+"swap device. When the program forks, the VM system creates two new object "
+"layers-C1 for the parent, and C2 for the child-that rest on top of B:"
+msgstr ""
+"При первой записи в страницу после выполнения этой операции, в B создается "
+"новая страница, содержимое которой берется из A. Все страницы в B могут "
+"сбрасываться и считываться из устройства подкачки. Когда программа ветвится, "
+"VM-система создает два новых уровня объектов-C1 для порождающего процесса и "
+"C2 для порожденного-они располагаются поверх B:"
+
+#. type: Target for macro image
+#: documentation/content/en/articles/vm-design/_index.adoc:140
+#, no-wrap
+msgid "fig3.png"
+msgstr "fig3.png"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:151
+msgid ""
+"In this case, let's say a page in B is modified by the original parent "
+"process. The process will take a copy-on-write fault and duplicate the page "
+"in C1, leaving the original page in B untouched. Now, let's say the same "
+"page in B is modified by the child process. The process will take a copy-on-"
+"write fault and duplicate the page in C2. The original page in B is now "
+"completely hidden since both C1 and C2 have a copy and B could theoretically "
+"be destroyed if it does not represent a \"real\" file; however, this sort of "
+"optimization is not trivial to make because it is so fine-grained. FreeBSD "
+"does not make this optimization. Now, suppose (as is often the case) that "
+"the child process does an `exec()`. Its current address space is usually "
+"replaced by a new address space representing a new file. In this case, the "
+"C2 layer is destroyed:"
+msgstr ""
+"В этом случае, допустим, что страница в B была изменена начальным "
+"родительским процессом. В процессе возникнет ситуация копирования при записи "
+"и страница скопируется в C1, при этом исходная страница останется в B "
+"нетронутой. Теперь допустим, что та же самая страница в B изменяется "
+"порожденным процессом. В процессе возникнет ситуация копирования при записи "
+"и страница скопируется в C2. Исходная страница в B теперь полностью скрыта, "
+"так как и C1, и C2 имеют копии, а B теоретически может быть уничтожена, если "
+"она не представляет собой \"реального\" файла). Однако такую оптимизацию не "
+"так уж просто осуществить, потому что она делается на уровне мелких единиц. "
+"Во FreeBSD такая оптимизация не выполняется. Теперь положим (а это часто "
+"случается), что порожденный процесс выполняет вызов `exec()`. Его текущее "
+"адресное пространство обычно заменяется новым адресным пространством, "
+"представляющим новый файл. В этом случае уровень C2 уничтожается:"
+
+#. type: Target for macro image
+#: documentation/content/en/articles/vm-design/_index.adoc:152
+#, no-wrap
+msgid "fig4.png"
+msgstr "fig4.png"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:158
+msgid ""
+"In this case, the number of children of B drops to one, and all accesses to "
+"B now go through C1. This means that B and C1 can be collapsed together. "
+"Any pages in B that also exist in C1 are deleted from B during the "
+"collapse. Thus, even though the optimization in the previous step could not "
+"be made, we can recover the dead pages when either of the processes exit or "
+"`exec()`."
+msgstr ""
+"В этом случае количество потомков B становится равным одному и все обращения "
+"к B теперь выполняются через C1. Это означает, что B и C1 могут быть "
+"объединены. Все страницы в B, которые также существуют и в C1, во время "
+"объединения из B удаляются. Таким образом, хотя оптимизация на предыдущем "
+"шаге может не делаться, мы можем восстановить мертвые страницы при окончании "
+"работы процессов или при вызове `exec()`."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:165
+msgid ""
+"This model creates a number of potential problems. The first is that you "
+"can wind up with a relatively deep stack of layered VM Objects which can "
+"cost scanning time and memory when you take a fault. Deep layering can "
+"occur when processes fork and then fork again (either parent or child). The "
+"second problem is that you can wind up with dead, inaccessible pages deep in "
+"the stack of VM Objects. In our last example if both the parent and child "
+"processes modify the same page, they both get their own private copies of "
+"the page and the original page in B is no longer accessible by anyone. That "
+"page in B can be freed."
+msgstr ""
+"Такая модель создает некоторое количество потенциальных проблем. Первая, с "
+"которой вы можете столкнуться, заключается в сравнительно большой "
+"последовательности уровней объектов VM, на сканирование которых тратится "
+"время и память. Большое количество уровней может возникнуть, когда процессы "
+"разветвляются, а затем разветвляются еще раз (как порожденные, так и "
+"порождающие). Вторая проблема заключается в том, что вы можете столкнуться с "
+"мертвыми, недоступными страницами глубоко в иерархии объектов VM. В нашем "
+"последнем примере если как родитель, так и потомок изменяют одну и ту же "
+"страницу, они оба получают собственные копии страницы, а исходная страница в "
+"B становится никому не доступной. такая страница в B может быть высвобождена."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:176
+msgid ""
+"FreeBSD solves the deep layering problem with a special optimization called "
+"the \"All Shadowed Case\". This case occurs if either C1 or C2 take "
+"sufficient COW faults to completely shadow all pages in B. Lets say that C1 "
+"achieves this. C1 can now bypass B entirely, so rather then have C1->B->A "
+"and C2->B->A we now have C1->A and C2->B->A. But look what also happened-"
+"now B has only one reference (C2), so we can collapse B and C2 together. "
+"The end result is that B is deleted entirely and we have C1->A and C2->A. "
+"It is often the case that B will contain a large number of pages and neither "
+"C1 nor C2 will be able to completely overshadow it. If we fork again and "
+"create a set of D layers, however, it is much more likely that one of the D "
+"layers will eventually be able to completely overshadow the much smaller "
+"dataset represented by C1 or C2. The same optimization will work at any "
+"point in the graph and the grand result of this is that even on a heavily "
+"forked machine VM Object stacks tend to not get much deeper then 4. This is "
+"true of both the parent and the children and true whether the parent is "
+"doing the forking or whether the children cascade forks."
+msgstr ""
+"FreeBSD решает проблему с глубиной вложенности с помощью приема оптимизации, "
+"который называется \"All Shadowed Case\". Этот случай возникает, если в C1 "
+"либо C2 возникает столько случаев копирования страниц при записи, что они "
+"полностью закрывают все страницы в B. Допустим, что такое произошло в C1. C1 "
+"может теперь полностью заменить B, так что вместо цепочек C1->B->A и C2->B-"
+">A мы теперь имеем цепочки C1->A и C2->B->A. Но посмотрите, что получается-"
+"теперь B имеет только одну ссылку (C2), так что мы можем объединить B и C2. "
+"В конечном итоге B будет полностью удален и мы имеем цепочки C1->A и C2->A. "
+"Часто B будет содержать большое количество страниц, и ни C1, ни C2 не смогут "
+"полностью их заменить. Если мы снова породим процесс и создадим набор "
+"уровней D, при этом, однако, более вероятно, что один из уровней D "
+"постепенно сможет полностью заместить гораздо меньший набор данных, "
+"представленный C1 и C2. Та же самая оптимизация будет работать в любой точке "
+"графа и главным результатом этого является то, что даже на сильно "
+"загруженной машине с множеством порождаемых процессов стеки объектов VM не "
+"часто бывают глубже четырех уровней. Это так как для порождающего, так и для "
+"порожденного процессов, и остается в силе как в случае, когда ветвление "
+"делает родитель, так и в случае, когда ветвление выполняет потомок."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:180
+msgid ""
+"The dead page problem still exists in the case where C1 or C2 do not "
+"completely overshadow B. Due to our other optimizations this case does not "
+"represent much of a problem and we simply allow the pages to be dead. If "
+"the system runs low on memory it will swap them out, eating a little swap, "
+"but that is it."
+msgstr ""
+"Проблема с мертвой страницей все еще имеет место, когда C1 или C2 не "
+"полностью перекрывают B. Из-за других применяемых нами методов оптимизации "
+"этот случай не представляет большой проблемы и мы просто позволяем таким "
+"страницам существовать. Если система испытывает нехватку оперативной памяти, "
+"она выполняет их выгрузку в область подкачки, что занимает некоторое "
+"пространство в области подкачки, но это все."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:184
+msgid ""
+"The advantage to the VM Object model is that `fork()` is extremely fast, "
+"since no real data copying need take place. The disadvantage is that you "
+"can build a relatively complex VM Object layering that slows page fault "
+"handling down a little, and you spend memory managing the VM Object "
+"structures. The optimizations FreeBSD makes proves to reduce the problems "
+"enough that they can be ignored, leaving no real disadvantage."
+msgstr ""
+"Преимущество модели VM-объектов заключается в очень быстром выполнении "
+"функции `fork()`, так как при этом не выполняется реального копирования "
+"данных. Минусом этого подхода является то, что вы можете построить "
+"сравнительно сложную иерархию объектов VM, которая несколько замедляет "
+"обработку ситуаций отсутствия страниц памяти, и к тому же тратится память на "
+"управление структурами объектов VM. Приемы оптимизации, применяемые во "
+"FreeBSD, позволяют снизить значимость этих проблем до степени, когда их "
+"можно без особых потерь игнорировать."
+
+#. type: Title ==
+#: documentation/content/en/articles/vm-design/_index.adoc:186
+#, no-wrap
+msgid "SWAP Layers"
+msgstr "Уровни области подкачки"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:194
+msgid ""
+"Private data pages are initially either copy-on-write or zero-fill pages. "
+"When a change, and therefore a copy, is made, the original backing object "
+"(usually a file) can no longer be used to save a copy of the page when the "
+"VM system needs to reuse it for other purposes. This is where SWAP comes "
+"in. SWAP is allocated to create backing store for memory that does not "
+"otherwise have it. FreeBSD allocates the swap management structure for a VM "
+"Object only when it is actually needed. However, the swap management "
+"structure has had problems historically:"
+msgstr ""
+"Страницы с собственными данными первоначально являются страницами, "
+"копируемыми при записи или заполняемыми нулями. Когда выполняется изменение, "
+"и, соответственно, копирование, начальное хранилище объекта (обычно файл) не "
+"может больше использоваться для хранения копии страницы, когда VM-системе "
+"нужно использовать ее повторно для других целей. В этот момент на помощь "
+"приходит область подкачки. Область подкачки выделяется для организации "
+"хранилища памяти, которая иначе не может быть доступна. FreeBSD создает "
+"структуру управления подкачкой для объекта VM, только когда это "
+"действительно нужно. Однако структура управления подкачкой исторически имела "
+"некоторые проблемы:"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:196
+msgid ""
+"Under FreeBSD 3.X the swap management structure preallocates an array that "
+"encompasses the entire object requiring swap backing store-even if only a "
+"few pages of that object are swap-backed. This creates a kernel memory "
+"fragmentation problem when large objects are mapped, or processes with large "
+"runsizes (RSS) fork."
+msgstr ""
+"Во FreeBSD 3.X в структуре управления областью подкачки предварительно "
+"выделяется массив, который представляет целый объект, требующий хранения в "
+"области подкачки-даже если только несколько страниц этого объекта хранятся в "
+"области подкачки. Это создает проблему фрагментации памяти ядра в случае, "
+"когда в память отображаются большие объекты или когда ветвятся процессы, "
+"занимающие большой объем памяти при работе (RSS)."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:197
+msgid ""
+"Also, to keep track of swap space, a \"list of holes\" is kept in kernel "
+"memory, and this tends to get severely fragmented as well. Since the \"list "
+"of holes\" is a linear list, the swap allocation and freeing performance is "
+"a non-optimal O(n)-per-page."
+msgstr ""
+"Также для отслеживания памяти подкачки в памяти ядра поддерживается \"список "
+"дыр\", и он также несколько фрагментирован. Так как \"список дыр\" является "
+"последовательным списком, то производительность при распределении и "
+"высвобождении памяти в области подкачки неоптимально и ее сложность зависит "
+"от количества страниц как O(n)."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:198
+msgid ""
+"It requires kernel memory allocations to take place during the swap freeing "
+"process, and that creates low memory deadlock problems."
+msgstr ""
+"Также в процессе высвобождения памяти в области подкачки требуется выделение "
+"памяти в ядре, и это приводит к проблемам блокировки при недостатке памяти."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:199
+msgid ""
+"The problem is further exacerbated by holes created due to the interleaving "
+"algorithm."
+msgstr ""
+"Проблема еще более обостряется из-за дыр, создаваемых по чередующемуся "
+"алгоритму."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:200
+msgid ""
+"Also, the swap block map can become fragmented fairly easily resulting in "
+"non-contiguous allocations."
+msgstr ""
+"Кроме того, список распределения блоков в области подкачки легко оказывается "
+"фрагментированным, что приводит к распределению непоследовательных областей."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:201
+msgid ""
+"Kernel memory must also be allocated on the fly for additional swap "
+"management structures when a swapout occurs."
+msgstr ""
+"Память ядра также должна распределяться по ходу работы для дополнительных "
+"структур по управлению областью подкачки при выгрузке страниц памяти в эту "
+"область."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:204
+msgid ""
+"It is evident from that list that there was plenty of room for improvement. "
+"For FreeBSD 4.X, I completely rewrote the swap subsystem:"
+msgstr ""
+"Очевидно, что мест для усовершенствований предостаточно. Во FreeBSD 4.X "
+"подсистема управления областью подкачки была полностью переписана мною:"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:206
+msgid ""
+"Swap management structures are allocated through a hash table rather than a "
+"linear array giving them a fixed allocation size and much finer granularity."
+msgstr ""
+"Структуры управления областью подкачки распределяются при помощи хэш-"
+"таблицы, а не через линейный массив, что дает им фиксированный размер при "
+"распределении и работу с гораздо меньшими структурами."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:207
+msgid ""
+"Rather then using a linearly linked list to keep track of swap space "
+"reservations, it now uses a bitmap of swap blocks arranged in a radix tree "
+"structure with free-space hinting in the radix node structures. This "
+"effectively makes swap allocation and freeing an O(1) operation."
+msgstr ""
+"Вместо того, чтобы использовать однонаправленный связный список для "
+"отслеживания выделения пространства в области подкачки, теперь используется "
+"побитовая карта блоков области подкачки, выполненная в основном в виде "
+"древовидной структуры с информацией о свободном пространстве, находящейся в "
+"узлах структур. Это приводит к тому, что выделение и высвобождение памяти в "
+"области подкачки становится операцией сложности O(1)."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:208
+msgid ""
+"The entire radix tree bitmap is also preallocated to avoid having to "
+"allocate kernel memory during critical low memory swapping operations. After "
+"all, the system tends to swap when it is low on memory so we should avoid "
+"allocating kernel memory at such times to avoid potential deadlocks."
+msgstr ""
+"Все дерево также распределяется заранее для того, чтобы избежать "
+"распределения памяти ядра во время операций с областью подкачки при "
+"критически малом объеме свободной памяти. В конце концов, система обращается "
+"к области подкачки при нехватке памяти, так что мы должны избежать "
+"распределения памяти ядра в такие моменты для избежания потенциальных "
+"блокировок."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:209
+msgid ""
+"To reduce fragmentation the radix tree is capable of allocating large "
+"contiguous chunks at once, skipping over smaller fragmented chunks."
+msgstr ""
+"Для уменьшения фрагментации дерево может распределять большой "
+"последовательный кусок за раз, пропуская меньшие фрагментированные области."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:211
+msgid ""
+"I did not take the final step of having an \"allocating hint pointer\" that "
+"would trundle through a portion of swap as allocations were made to further "
+"guarantee contiguous allocations or at least locality of reference, but I "
+"ensured that such an addition could be made."
+msgstr ""
+"Я не сделал последний шаг к заведению \"указателя на распределение\", "
+"который будет передвигаться по участку области подкачки при выделении памяти "
+"для обеспечения в будущем распределения последовательных участков, или по "
+"крайней мере местоположения ссылки, но я убежден, что это может быть сделано."
+
+#. type: Title ==
+#: documentation/content/en/articles/vm-design/_index.adoc:213
+#, no-wrap
+msgid "When to free a page"
+msgstr "Когда освобождать страницу"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:218
+msgid ""
+"Since the VM system uses all available memory for disk caching, there are "
+"usually very few truly-free pages. The VM system depends on being able to "
+"properly choose pages which are not in use to reuse for new allocations. "
+"Selecting the optimal pages to free is possibly the single-most important "
+"function any VM system can perform because if it makes a poor selection, the "
+"VM system may be forced to unnecessarily retrieve pages from disk, seriously "
+"degrading system performance."
+msgstr ""
+"Так как система VM использует всю доступную память для кэширования диска, то "
+"обычно действительно незанятых страниц очень мало. Система VM зависит от "
+"того, как она точно выбирает незанятые страницы для повторного использования "
+"для новых распределений. Оптимальный выбор страниц для высвобождения, "
+"возможно, является самой важной функцией любой VM-системы, из тех, что она "
+"может выполнять, потому что при неправильном выборе система VM вынуждена "
+"будет запрашивать страницы с диска, значительно снижая производительность "
+"всей системы."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:221
+msgid ""
+"How much overhead are we willing to suffer in the critical path to avoid "
+"freeing the wrong page? Each wrong choice we make will cost us hundreds of "
+"thousands of CPU cycles and a noticeable stall of the affected processes, so "
+"we are willing to endure a significant amount of overhead to be sure that "
+"the right page is chosen. This is why FreeBSD tends to outperform other "
+"systems when memory resources become stressed."
+msgstr ""
+"Какую дополнительную нагрузку мы может выделить в критическом пути для "
+"избежания высвобождения не той страницы? Каждый неправильный выбор будет "
+"стоить нам сотни тысяч тактов работы центрального процессора и заметное "
+"замедление работы затронутых процессов, так что мы должны смириться со "
+"значительными издержками для того, чтобы была заведомо выбрана правильная "
+"страница. Вот почему FreeBSD превосходит другие системы в производительности "
+"при нехватке ресурсов памяти."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:224
+msgid ""
+"The free page determination algorithm is built upon a history of the use of "
+"memory pages. To acquire this history, the system takes advantage of a page-"
+"used bit feature that most hardware page tables have."
+msgstr ""
+"Алгоритм определения свободной страницы написан на основе истории "
+"использования страниц памяти. Для получения этой истории система использует "
+"возможности бита использования памяти, которые имеются в большинстве "
+"аппаратных таблицах страниц памяти."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:230
+msgid ""
+"In any case, the page-used bit is cleared and at some later point the VM "
+"system comes across the page again and sees that the page-used bit has been "
+"set. This indicates that the page is still being actively used. If the bit "
+"is still clear it is an indication that the page is not being actively "
+"used. By testing this bit periodically, a use history (in the form of a "
+"counter) for the physical page is developed. When the VM system later needs "
+"to free up some pages, checking this history becomes the cornerstone of "
+"determining the best candidate page to reuse."
+msgstr ""
+"В любом случае, бит использования страницы очищается, и в некоторый более "
+"поздний момент VM-система обращается к странице снова и обнаруживает, что "
+"этот бит установлен. Это указывает на то, что страница активно используется. "
+"Периодически проверяя этот бит, накапливается история использования (в виде "
+"счетчика) физической страницы. Когда позже VM-системе требуется высвободить "
+"некоторые страницы, проверка истории выступает указателем при определении "
+"наиболее вероятной кандидатуры для повторного использования."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:235
+msgid ""
+"For those platforms that do not have this feature, the system actually "
+"emulates a page-used bit. It unmaps or protects a page, forcing a page "
+"fault if the page is accessed again. When the page fault is taken, the "
+"system simply marks the page as having been used and unprotects the page so "
+"that it may be used. While taking such page faults just to determine if a "
+"page is being used appears to be an expensive proposition, it is much less "
+"expensive than reusing the page for some other purpose only to find that a "
+"process needs it back and then have to go to disk."
+msgstr ""
+"Для тех платформ, что не имеют этой возможности, система эмулирует этот бит. "
+"Она снимает отображение или защищает страницу, что приводит к ошибке доступа "
+"к странице, если к странице выполняется повторное обращение. При "
+"возникновении этой ошибки система просто помечает страницу как используемую "
+"и снимает защиту со страницы, так что она может использоваться. Хотя "
+"использование такого приема только для определения использования страницы "
+"весьма накладно, это выгоднее, чем повторно использовать страницу для других "
+"целей и обнаружить, что она снова нужна процессу и подгружать ее с диска."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:245
+msgid ""
+"FreeBSD makes use of several page queues to further refine the selection of "
+"pages to reuse as well as to determine when dirty pages must be flushed to "
+"their backing store. Since page tables are dynamic entities under FreeBSD, "
+"it costs virtually nothing to unmap a page from the address space of any "
+"processes using it. When a page candidate has been chosen based on the page-"
+"use counter, this is precisely what is done. The system must make a "
+"distinction between clean pages which can theoretically be freed up at any "
+"time, and dirty pages which must first be written to their backing store "
+"before being reusable. When a page candidate has been found it is moved to "
+"the inactive queue if it is dirty, or the cache queue if it is clean. A "
+"separate algorithm based on the dirty-to-clean page ratio determines when "
+"dirty pages in the inactive queue must be flushed to disk. Once this is "
+"accomplished, the flushed pages are moved from the inactive queue to the "
+"cache queue. At this point, pages in the cache queue can still be "
+"reactivated by a VM fault at relatively low cost. However, pages in the "
+"cache queue are considered to be \"immediately freeable\" and will be reused "
+"in an LRU (least-recently used) fashion when the system needs to allocate "
+"new memory."
+msgstr ""
+"FreeBSD использует несколько очередей страниц для обновления выбора страниц "
+"для повторного использования, а также для определения того, когда же грязные "
+"страницы должны быть сброшены в хранилище. Так как таблицы страниц во "
+"FreeBSD являются динамическими объектами, практически ничего не стоит "
+"вырезать страницу из адресного пространства любого использующего ее "
+"процесса. После того, как подходящая страница, на основе счетчика "
+"использования, выбрана, именно это и выполняется. Система должна отличать "
+"между чистыми страницами, которые теоретически могут быть высвобождены в "
+"любое время, и грязными страницами, которые сначала должны быть переписаны в "
+"хранилище перед тем, как их можно будет использовать повторно. После "
+"нахождения подходящей страницы она перемещается в неактивную очередь, если "
+"она является грязной, или в очередь кэша, если она чистая. Отдельный "
+"алгоритм, основывающийся на отношении количества грязных страниц к чистым, "
+"определяет, когда грязные страницы в неактивной очереди должны быть сброшены "
+"на диск. Когда это выполнится, сброшенные страницы перемещаются из "
+"неактивной очереди в очередь кэша. В этот момент страницы в очереди кэша "
+"могут быть повторно активизированы VM со сравнительно малыми накладными "
+"расходами. Однако страницы в очереди кэша предполагается \"высвобождать "
+"немедленно\" и повторно использовать в LRU-порядке (меньше всего "
+"используемый), когда системе потребуется выделение дополнительной памяти."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:249
+msgid ""
+"It is important to note that the FreeBSD VM system attempts to separate "
+"clean and dirty pages for the express reason of avoiding unnecessary flushes "
+"of dirty pages (which eats I/O bandwidth), nor does it move pages between "
+"the various page queues gratuitously when the memory subsystem is not being "
+"stressed. This is why you will see some systems with very low cache queue "
+"counts and high active queue counts when doing a `systat -vm` command. As "
+"the VM system becomes more stressed, it makes a greater effort to maintain "
+"the various page queues at the levels determined to be the most effective."
+msgstr ""
+"Стоит отметить, что во FreeBSD VM-система пытается разделить чистые и "
+"грязные страницы во избежание срочной необходимости в ненужных сбросах "
+"грязных страниц (что отражается на пропускной способности ввода/вывода) и не "
+"перемещает беспричинно страницы между разными очередями, когда подсистема "
+"управления памятью не испытывает нехватку ресурсов. Вот почему вы можете "
+"видеть, что при выполнении команды `systat -vm` в некоторых системах "
+"значение счетчика очереди кэша мало, а счетчик активной очереди большой. При "
+"повышении нагрузки на VM-систему она прилагает большие усилия на поддержку "
+"различных очередей страниц в соотношениях, которые являются наиболее "
+"эффективными."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:253
+msgid ""
+"An urban myth has circulated for years that Linux did a better job avoiding "
+"swapouts than FreeBSD, but this in fact is not true. What was actually "
+"occurring was that FreeBSD was proactively paging out unused pages to make "
+"room for more disk cache while Linux was keeping unused pages in core and "
+"leaving less memory available for cache and process pages. I do not know "
+"whether this is still true today."
+msgstr ""
+"Годами ходили современные легенды, что Linux выполняет работу по "
+"предотвращению выгрузки на диск лучше, чем FreeBSD, но это не так. На самом "
+"деле FreeBSD старается сбросить на диск неиспользуемые страницы для "
+"освобождения места под дисковый кэш, когда как Linux хранит неиспользуемые "
+"страницы в памяти и оставляет под кэш и страницы процессов меньше памяти. Я "
+"не знаю, остается ли это правдой на сегодняшний день."
+
+#. type: Title ==
+#: documentation/content/en/articles/vm-design/_index.adoc:255
+#, no-wrap
+msgid "Pre-Faulting and Zeroing Optimizations"
+msgstr "Оптимизация ошибок доступа к страницам и их обнуления"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:265
+msgid ""
+"Taking a VM fault is not expensive if the underlying page is already in core "
+"and can simply be mapped into the process, but it can become expensive if "
+"you take a whole lot of them on a regular basis. A good example of this is "
+"running a program such as man:ls[1] or man:ps[1] over and over again. If "
+"the program binary is mapped into memory but not mapped into the page table, "
+"then all the pages that will be accessed by the program will have to be "
+"faulted in every time the program is run. This is unnecessary when the "
+"pages in question are already in the VM Cache, so FreeBSD will attempt to "
+"pre-populate a process's page tables with those pages that are already in "
+"the VM Cache. One thing that FreeBSD does not yet do is pre-copy-on-write "
+"certain pages on exec. For example, if you run the man:ls[1] program while "
+"running `vmstat 1` you will notice that it always takes a certain number of "
+"page faults, even when you run it over and over again. These are zero-fill "
+"faults, not program code faults (which were pre-faulted in already). Pre-"
+"copying pages on exec or fork is an area that could use more study."
+msgstr ""
+"Полагая, что ошибка доступа к странице памяти в VM не является операцией с "
+"большими накладными расходами, если страница уже находится в основной памяти "
+"и может быть просто отображена в адресное пространство процесса, может "
+"оказаться, что это станет весьма накладно, если их будет оказываться "
+"регулярно много. Хорошим примером этой ситуации является запуск таких "
+"программ, как man:ls[1] или man:ps[1], снова и снова. Если бинарный файл "
+"программы отображен в память, но не отображен в таблицу страниц, то все "
+"страницы, к которым обращалась программа, окажутся недоступными при каждом "
+"запуске программы. Это не так уж необходимо, если эти страницы уже "
+"присутствуют в кэше VM, так что FreeBSD будет пытаться восстанавливать "
+"таблицы страниц процесса из тех страниц, что уже располагаются в VM-кэше. "
+"Однако во FreeBSD пока не выполняется предварительное копирование при записи "
+"определенных страниц при выполнении вызова exec. Например, если вы "
+"запускаете программу man:ls[1] одновременно с работающей `vmstat 1`, то "
+"заметите, что она всегда выдает некоторое количество ошибок доступа к "
+"страницам, даже когда вы запускаете ее снова и снова. Это ошибки заполнения "
+"нулями, а не ошибки кода программы (которые уже были обработаны). "
+"Предварительное копирование страниц при выполнении вызовов exec или fork "
+"находятся в области, требующей более тщательного изучения."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:274
+msgid ""
+"A large percentage of page faults that occur are zero-fill faults. You can "
+"usually see this by observing the `vmstat -s` output. These occur when a "
+"process accesses pages in its BSS area. The BSS area is expected to be "
+"initially zero but the VM system does not bother to allocate any memory at "
+"all until the process actually accesses it. When a fault occurs the VM "
+"system must not only allocate a new page, it must zero it as well. To "
+"optimize the zeroing operation the VM system has the ability to pre-zero "
+"pages and mark them as such, and to request pre-zeroed pages when zero-fill "
+"faults occur. The pre-zeroing occurs whenever the CPU is idle but the "
+"number of pages the system pre-zeros is limited to avoid blowing away the "
+"memory caches. This is an excellent example of adding complexity to the VM "
+"system to optimize the critical path."
+msgstr ""
+"Большой процент ошибок доступа к страницам, относится к ошибкам при "
+"заполнении нулями. Вы можете обычно видеть это, просматривая вывод команды "
+"`vmstat -s`. Это происходит, когда процесс обращается к страницам в своей "
+"области BSS. Область BSS предполагается изначально заполненной нулями, но VM-"
+"система не заботится о выделении памяти до тех пор, пока процесс реально к "
+"ней не обратится. При возникновении ошибки VM-система должна не только "
+"выделить новую страницу, но и заполнить ее нулями. Для оптимизации операции "
+"по заполнению нулями в системе VM имеется возможность предварительно "
+"обнулять страницы и помечать их, и запрашивать уже обнуленные страницы при "
+"возникновении ошибок заполнения нулями. Предварительное заполнение нулями "
+"происходит, когда CPU простаивает, однако количество страниц, которые "
+"система заранее заполняет нулями, ограничено, для того, чтобы не переполнить "
+"кэши памяти. Это прекрасный пример добавления сложности в VM-систему ради "
+"оптимизации критического пути."
+
+#. type: Title ==
+#: documentation/content/en/articles/vm-design/_index.adoc:276
+#, no-wrap
+msgid "Page Table Optimizations"
+msgstr "Оптимизация таблицы страниц"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:286
+msgid ""
+"The page table optimizations make up the most contentious part of the "
+"FreeBSD VM design and they have shown some strain with the advent of serious "
+"use of `mmap()`. I think this is actually a feature of most BSDs though I "
+"am not sure when it was first introduced. There are two major "
+"optimizations. The first is that hardware page tables do not contain "
+"persistent state but instead can be thrown away at any time with only a "
+"minor amount of management overhead. The second is that every active page "
+"table entry in the system has a governing `pv_entry` structure which is tied "
+"into the `vm_page` structure. FreeBSD can simply iterate through those "
+"mappings that are known to exist while Linux must check all page tables that "
+"_might_ contain a specific mapping to see if it does, which can achieve "
+"O(n^2) overhead in certain situations. It is because of this that FreeBSD "
+"tends to make better choices on which pages to reuse or swap when memory is "
+"stressed, giving it better performance under load. However, FreeBSD "
+"requires kernel tuning to accommodate large-shared-address-space situations "
+"such as those that can occur in a news system because it may run out of "
+"`pv_entry` structures."
+msgstr ""
+"Оптимизация таблицы страниц составляет самую содержательную часть "
+"архитектуры VM во FreeBSD и она проявляется при появлении нагрузки при "
+"значительном использовании `mmap()`. Я думаю, что это на самом деле "
+"особенность работы большинства BSD-систем, хотя я не уверен, когда это "
+"проявилось впервые. Есть два основных подхода к оптимизации. Первый "
+"заключается в том, что аппаратные таблицы страниц не содержат постоянного "
+"состояния, а вместо этого могут быть сброшены в любой момент с малыми "
+"накладными расходами. Второй подход состоит в том, что каждая активная "
+"таблица страниц в системе имеет управляющую структуру `pv_entry`, которая "
+"связана в структуру `vm_page`. FreeBSD может просто просматривать эти "
+"отображения, которые существуют, когда как в Linux должны проверяться все "
+"таблицы страниц, которые _могут_ содержать нужное отображение, что в "
+"некоторых ситуация дает увеличение сложности O(n^2). Из-за того, что FreeBSD "
+"стремится выбрать наиболее подходящую к повторному использованию или сбросу "
+"в область подкачки страницу, когда ощущается нехватка памяти, система дает "
+"лучшую производительность при нагрузке. Однако во FreeBSD требуется тонкая "
+"настройка ядра для соответствия ситуациям с большим совместно используемым "
+"адресным пространством, которые могут случиться в системе, обслуживающей "
+"сервер телеконференций, потому что структуры `pv_entry` могут оказаться "
+"исчерпанными."
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:291
+msgid ""
+"Both Linux and FreeBSD need work in this area. FreeBSD is trying to "
+"maximize the advantage of a potentially sparse active-mapping model (not all "
+"processes need to map all pages of a shared library, for example), whereas "
+"Linux is trying to simplify its algorithms. FreeBSD generally has the "
+"performance advantage here at the cost of wasting a little extra memory, but "
+"FreeBSD breaks down in the case where a large file is massively shared "
+"across hundreds of processes. Linux, on the other hand, breaks down in the "
+"case where many processes are sparsely-mapping the same shared library and "
+"also runs non-optimally when trying to determine whether a page can be "
+"reused or not."
+msgstr ""
+"И в Linux, и во FreeBSD требуются доработки в этой области. FreeBSD пытается "
+"максимизировать преимущества от потенциально редко применяемой модели "
+"активного отображения (к примеру, не всем процессам нужно отображать все "
+"страницы динамической библиотеки), когда как Linux пытается упростить свои "
+"алгоритмы. FreeBSD имеет здесь общее преимущество в производительности за "
+"счет использования дополнительной памяти, но FreeBSD выглядит хуже в случае, "
+"когда большой файл совместно используется сотнями процессов. Linux, с другой "
+"стороны, выглядит хуже в случае, когда много процессов частично используют "
+"одну и ту же динамическую библиотеку, а также работает неоптимально при "
+"попытке определить, может ли страница повторно использоваться, или нет."
+
+#. type: Title ==
+#: documentation/content/en/articles/vm-design/_index.adoc:293
+#, no-wrap
+msgid "Conclusion"
+msgstr "Заключение"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:298
+msgid ""
+"Virtual memory in modern operating systems must address a number of "
+"different issues efficiently and for many different usage patterns. The "
+"modular and algorithmic approach that BSD has historically taken allows us "
+"to study and understand the current implementation as well as relatively "
+"cleanly replace large sections of the code. There have been a number of "
+"improvements to the FreeBSD VM system in the last several years, and work is "
+"ongoing."
+msgstr ""
+"Виртуальная память в современных операционных системах должна решать "
+"несколько различных задач эффективно и при разных условиях. Модульный и "
+"алгоритмический подход, которому исторически следует BSD, позволяет нам "
+"изучить и понять существующую реализацию, а также сравнительно легко "
+"изменить большие блоки кода. За несколько последних лет в VM-системе FreeBSD "
+"было сделано некоторое количество усовершенствований, и работа над ними "
+"продолжается."
+
+#. type: Title ==
+#: documentation/content/en/articles/vm-design/_index.adoc:300
+#, no-wrap
+msgid "Bonus QA session by Allen Briggs"
+msgstr "Дополнительный сеанс вопросов и ответов от Аллена Бриггса (Allen Briggs)"
+
+#. type: Title ===
+#: documentation/content/en/articles/vm-design/_index.adoc:302
+#, no-wrap
+msgid "What is the interleaving algorithm that you refer to in your listing of the ills of the FreeBSD 3.X swap arrangements?"
+msgstr "Что это за алгоритм чередования, который вы упоминали в списке недостатков подсистемы управления разделом подкачки во FreeBSD 3.X?"
+
+#. type: .abstract-title
+#: documentation/content/en/articles/vm-design/_index.adoc:308
+msgid ""
+"FreeBSD uses a fixed swap interleave which defaults to 4. This means that "
+"FreeBSD reserves space for four swap areas even if you only have one, two, "
+"or three. Since swap is interleaved the linear address space representing "
+"the \"four swap areas\" will be fragmented if you do not actually have four "
+"swap areas. For example, if you have two swap areas A and B FreeBSD's "
+"address space representation for that swap area will be interleaved in "
+"blocks of 16 pages:"
+msgstr ""
+"FreeBSD использует в области подкачки механизм чередования, с индексом по "
+"умолчанию, равным четырем. Это означает, что FreeBSD резервирует "
+"пространство для четырех областей подкачки, даже если у вас имеется всего "
+"лишь одна, две или три области. Так как в области подкачки имеется "
+"чередование, то линейное адресное пространство, представляющее \"четыре "
+"области подкачки\", будет фрагментироваться, если у вас нет на самом деле "
+"четырех областей подкачки. Например, если у вас две области A и B, то "
+"представление адресного пространства для этой области подкачки во FreeBSD "
+"будет организовано с чередованием блоков из 16 страниц:"
+
+#. type: delimited block . 4
+#: documentation/content/en/articles/vm-design/_index.adoc:311
+#, no-wrap
+msgid "A B C D A B C D A B C D A B C D\n"
+msgstr "A B C D A B C D A B C D A B C D\n"
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:318
+msgid ""
+"FreeBSD 3.X uses a \"sequential list of free regions\" approach to "
+"accounting for the free swap areas. The idea is that large blocks of free "
+"linear space can be represented with a single list node ([.filename]#kern/"
+"subr_rlist.c#). But due to the fragmentation the sequential list winds up "
+"being insanely fragmented. In the above example, completely unused swap "
+"will have A and B shown as \"free\" and C and D shown as \"all allocated\". "
+"Each A-B sequence requires a list node to account for because C and D are "
+"holes, so the list node cannot be combined with the next A-B sequence."
+msgstr ""
+"FreeBSD 3.X использует \"последовательный список свободных областей\" для "
+"управления свободными областями в разделе подкачки. Идея состоит в том, что "
+"большие последовательные блоки свободного пространства могут быть "
+"представлены при помощи узла односвязного списка ([.filename]#kern/"
+"subr_rlist.c#). Но из-за фрагментации последовательный список сам становится "
+"фрагментированным. В примере выше полностью неиспользуемое пространство в A "
+"и B будет показано как \"свободное\", а C и D как \"полностью занятое\". "
+"Каждой последовательности A-B требуется для учета узел списка, потому что C "
+"и D являются дырами, так что узел списка не может быть связан со следующей "
+"последовательностью A-B."
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:320
+msgid ""
+"Why do we interleave our swap space instead of just tack swap areas onto the "
+"end and do something fancier? It is a whole lot easier to allocate linear "
+"swaths of an address space and have the result automatically be interleaved "
+"across multiple disks than it is to try to put that sophistication elsewhere."
+msgstr ""
+"Почему мы организуем чередование в области подкачки вместо того, чтобы "
+"просто объединить области подкачки в одно целое и придумать что-то более "
+"умное? Потому что гораздо легче выделять последовательные полосы адресного "
+"пространства и получать в результате автоматическое чередование между "
+"несколькими дисками, чем пытаться выдумывать сложности в другом месте."
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:325
+msgid ""
+"The fragmentation causes other problems. Being a linear list under 3.X, and "
+"having such a huge amount of inherent fragmentation, allocating and freeing "
+"swap winds up being an O(N) algorithm instead of an O(1) algorithm. "
+"Combined with other factors (heavy swapping) and you start getting into "
+"O(N^2) and O(N^3) levels of overhead, which is bad. The 3.X system may also "
+"need to allocate KVM during a swap operation to create a new list node which "
+"can lead to a deadlock if the system is trying to pageout pages in a low-"
+"memory situation."
+msgstr ""
+"Фрагментация вызывает другие проблемы. Являясь последовательным списком в "
+"3.X и имея такое огромную фрагментацию, выделение и освобождение в области "
+"подкачки становится алгоритмом сложности O(N), а не O(1). Вместе с другими "
+"факторами (частое обращение к области подкачки) вы получаете сложность "
+"уровней O(N^2) и O(N^3), что плохо. В системе 3.X также может потребоваться "
+"выделение KVM во время работы с областью подкачки для создания нового узла "
+"списка, что в условии нехватки памяти может привести к блокировке, если "
+"система попытается сбросить страницы в область подкачки."
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:330
+msgid ""
+"Under 4.X we do not use a sequential list. Instead we use a radix tree and "
+"bitmaps of swap blocks rather than ranged list nodes. We take the hit of "
+"preallocating all the bitmaps required for the entire swap area up front but "
+"it winds up wasting less memory due to the use of a bitmap (one bit per "
+"block) instead of a linked list of nodes. The use of a radix tree instead "
+"of a sequential list gives us nearly O(1) performance no matter how "
+"fragmented the tree becomes."
+msgstr ""
+"В 4.X мы не используем последовательный список. Вместо этого мы используем "
+"базисное дерево и битовые карты блоков области подкачки, а не ограниченный "
+"список узлов. Мы принимаем предварительное выделение всех битовых карт, "
+"требуемых для всей области подкачки, но при этом тратится меньше памяти, "
+"потому что мы используем битовые карты (один бит на блок), а не связанный "
+"список узлов. Использование базисного дерева вместо последовательного списка "
+"дает нам производительность O(1) вне зависимости от фрагментации дерева."
+
+#. type: Title ===
+#: documentation/content/en/articles/vm-design/_index.adoc:331
+#, no-wrap
+msgid "How is the separation of clean and dirty (inactive) pages related to the situation where you see low cache queue counts and high active queue counts in systat -vm? Do the systat stats roll the active and dirty pages together for the active queue count?"
+msgstr "Как разделение чистых и грязных (неактивных) страниц связано с ситуацией, когда вы видите маленький счетчик очереди кэша и большой счетчик активной очереди в выдаче команды systat -vm? Разве системная статистика не считает активные и грязные страницы вместе за счетчик активной очереди?"
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:336
+msgid ""
+"Yes, that is confusing. The relationship is \"goal\" verses \"reality\". "
+"Our goal is to separate the pages but the reality is that if we are not in a "
+"memory crunch, we do not really have to."
+msgstr ""
+"Да, это запутывает. Связь заключается в \"желаемом\" и \"действительном\". "
+"Мы желаем разделить страницы, но реальность такова, что пока у нас нет "
+"проблем с памятью, нам это на самом деле не нужно."
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:338
+msgid ""
+"What this means is that FreeBSD will not try very hard to separate out dirty "
+"pages (inactive queue) from clean pages (cache queue) when the system is not "
+"being stressed, nor will it try to deactivate pages (active queue -> "
+"inactive queue) when the system is not being stressed, even if they are not "
+"being used."
+msgstr ""
+"Это означает, что FreeBSD не будет очень сильно стараться над отделением "
+"грязных страниц (неактивная очередь) от чистых страниц (очередь кэша), когда "
+"система не находится под нагрузкой, и не будет деактивировать страницы "
+"(активная очередь -> неактивная очередь), когда система не нагружена, даже "
+"если они не используются."
+
+#. type: Title ===
+#: documentation/content/en/articles/vm-design/_index.adoc:339
+#, no-wrap
+msgid "In man:ls[1] the / vmstat 1 example, would not some of the page faults be data page faults (COW from executable file to private page)? I.e., I would expect the page faults to be some zero-fill and some program data. Or are you implying that FreeBSD does do pre-COW for the program data?"
+msgstr "В примере с / vmstat 1 могут ли некоторые ошибки доступа к странице быть ошибками страниц данных (COW из выполнимого файла в приватные страницы)? То есть я полагаю, что ошибки доступа к страницам являются частично ошибками при заполнении нулями, а частично данных программы. Или вы гарантируете, что FreeBSD выполняет предварительно COW для данных программы?"
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:345
+msgid ""
+"A COW fault can be either zero-fill or program-data. The mechanism is the "
+"same either way because the backing program-data is almost certainly already "
+"in the cache. I am indeed lumping the two together. FreeBSD does not pre-"
+"COW program data or zero-fill, but it _does_ pre-map pages that exist in its "
+"cache."
+msgstr ""
+"Ошибка COW может быть ошибкой при заполнении нулями или данных программы. "
+"Механизм в любом случае один и тот же, потому что хранилище данных программы "
+"уже в кэше. Я на самом деле не рад ни тому, ни другому. FreeBSD не выполняет "
+"предварительное COW данных программы и заполнение нулями, но она _выполняет_ "
+"предварительно отображение страниц, которые имеются в ее кэше."
+
+#. type: Title ===
+#: documentation/content/en/articles/vm-design/_index.adoc:346
+#, no-wrap
+msgid "In your section on page table optimizations, can you give a little more detail about pv_entry and vm_page (or should vm_page be vm_pmap-as in 4.4, cf. pp. 180-181 of McKusick, Bostic, Karel, Quarterman)? Specifically, what kind of operation/reaction would require scanning the mappings?"
+msgstr "В вашем разделе об оптимизации таблицы страниц, не могли бы вы более подробно рассказать о pv_entry и vm_page (или vm_page должна быть vm_pmap-как в 4.4, cf. pp. 180-181 of McKusick, Bostic, Karel, Quarterman)? А именно какое действие/реакцию должно потребоваться для сканирования отображений?"
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:350
+msgid ""
+"A `vm_page` represents an (object,index#) tuple. A `pv_entry` represents a "
+"hardware page table entry (pte). If you have five processes sharing the "
+"same physical page, and three of those processes's page tables actually map "
+"the page, that page will be represented by a single `vm_page` structure and "
+"three `pv_entry` structures."
+msgstr ""
+"`vm_page` представляет собой пару (object,index#). `pv_entry` является "
+"записью из аппаратной таблицы страниц (pte). Если у вас имеется пять "
+"процессов, совместно использующих одну и ту же физическую страницу, и в трех "
+"таблицах страниц этих процессов на самом деле отображается страница, то "
+"страница будет представляться одной структурой `vm_page` и тремя структурами "
+"`pv_entry`."
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:353
+msgid ""
+"`pv_entry` structures only represent pages mapped by the MMU (one `pv_entry` "
+"represents one pte). This means that when we need to remove all hardware "
+"references to a `vm_page` (to reuse the page for something else, page it "
+"out, clear it, dirty it, and so forth) we can simply scan the linked list of "
+"pv_entry's associated with that vm_page to remove or modify the pte's from "
+"their page tables."
+msgstr ""
+"Структуры `pv_entry` представляют страницы, отображаемые MMU (одна структура "
+"`pv_entry` соответствует одной pte). Это означает, что, когда нам нужно "
+"убрать все аппаратные ссылки на `vm_page` (для того, чтобы повторно "
+"использовать страницу для чего-то еще, выгрузить ее, очистить, пометить как "
+"грязную и так далее), мы можем просто просмотреть связный список структур "
+"`pv_entry`, связанных с этой `vm_page`, для того, чтобы удалить или изменить "
+"pte из их таблиц страниц."
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:360
+msgid ""
+"Under Linux there is no such linked list. To remove all the hardware page "
+"table mappings for a `vm_page` linux must index into every VM object that "
+"_might_ have mapped the page. For example, if you have 50 processes all "
+"mapping the same shared library and want to get rid of page X in that "
+"library, you need to index into the page table for each of those 50 "
+"processes even if only 10 of them have actually mapped the page. So Linux "
+"is trading off the simplicity of its design against performance. Many VM "
+"algorithms which are O(1) or (small N) under FreeBSD wind up being O(N), "
+"O(N^2), or worse under Linux. Since the pte's representing a particular "
+"page in an object tend to be at the same offset in all the page tables they "
+"are mapped in, reducing the number of accesses into the page tables at the "
+"same pte offset will often avoid blowing away the L1 cache line for that "
+"offset, which can lead to better performance."
+msgstr ""
+"В Linux нет такого связного списка. Для того, чтобы удалить все отображения "
+"аппаратной таблицы страниц для `vm_page`, linux должен пройти по индексу "
+"каждого объекта VM, который _может_ отображать страницу. К примеру, если у "
+"вас имеется 50 процессов, которые все отображают ту же самую динамическую "
+"библиотеку и хотите избавиться от страницы X в этой библиотеке, то вам нужно "
+"пройтись по индексу всей таблицы страниц для каждого из этих 50 процессов, "
+"даже если только 10 из них на самом деле отображают страницу. Так что Linux "
+"использует простоту подхода за счет производительности. Многие алгоритмы VM, "
+"которые имеют сложность O(1) или (N малое) во FreeBSD, в Linux приобретают "
+"сложность O(N), O(N^2) или хуже. Так как pte, представляющий конкретную "
+"страницу в объекте, скорее всего, будет с тем же смещением во всех таблицах "
+"страниц, в которых они отображаются, то уменьшение количества обращений в "
+"таблицы страниц по тому же самому смещению часто позволяет избежать "
+"разрастания кэша L1 для этого смещения, что приводит к улучшению "
+"производительности."
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:362
+msgid ""
+"FreeBSD has added complexity (the `pv_entry` scheme) to increase performance "
+"(to limit page table accesses to _only_ those pte's that need to be "
+"modified)."
+msgstr ""
+"Во FreeBSD введены дополнительные сложности (схема с `pv_entry`) для "
+"увеличения производительности (уменьшая количество обращений _только_ к тем "
+"pte, которые нужно модифицировать)."
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:366
+msgid ""
+"But FreeBSD has a scaling problem that Linux does not in that there are a "
+"limited number of `pv_entry` structures and this causes problems when you "
+"have massive sharing of data. In this case you may run out of `pv_entry` "
+"structures even though there is plenty of free memory available. This can "
+"be fixed easily enough by bumping up the number of `pv_entry` structures in "
+"the kernel config, but we really need to find a better way to do it."
+msgstr ""
+"Но во FreeBSD имеется проблема масштабирования, которой нет в Linux, потому "
+"что имеется ограниченное число структур `pv_entry`, и это приводит к "
+"возникновению проблем при большом объеме совместно используемых данных. В "
+"этом случае у вас может возникнуть нехватка структур `pv_entry`, даже если "
+"свободной памяти хватает. Это может быть достаточно легко исправлено "
+"увеличением количества структур `pv_entry` при настройке, но на самом деле "
+"нам нужно найти лучший способ делать это."
+
+#. type: Plain text
+#: documentation/content/en/articles/vm-design/_index.adoc:369
+msgid ""
+"In regards to the memory overhead of a page table verses the `pv_entry` "
+"scheme: Linux uses \"permanent\" page tables that are not throw away, but "
+"does not need a `pv_entry` for each potentially mapped pte. FreeBSD uses "
+"\"throw away\" page tables but adds in a `pv_entry` structure for each "
+"actually-mapped pte. I think memory utilization winds up being about the "
+"same, giving FreeBSD an algorithmic advantage with its ability to throw away "
+"page tables at will with very low overhead."
+msgstr ""
+"Что касается использования памяти под таблицу страниц против схемы с "
+"`pv_entry`: Linux использует \"постоянные\" таблицы страниц, которые не "
+"сбрасываются, но ему не нужны `pv_entry` для каждого потенциально "
+"отображаемого pte. FreeBSD использует \"сбрасываемые\" таблицы страниц, но "
+"для каждого реально отображаемого pte добавляется структура `pv_entry`. Я "
+"думаю, что использование памяти будет примерно одинакова, тем более что у "
+"FreeBSD есть алгоритмическое преимущество, заключающееся в способности "
+"сбрасывать таблицы страниц с очень малыми накладными расходами."
diff --git a/documentation/content/ru/books/arch-handbook/_index.adoc b/documentation/content/ru/books/arch-handbook/_index.adoc
new file mode 100644
index 0000000000..7417861a9b
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/_index.adoc
@@ -0,0 +1,57 @@
+---
+add_single_page_link: true
+authors:
+ -
+ author: 'The FreeBSD Documentation Project'
+bookOrder: 50
+copyright: '2000-2006, 2012-2023 The FreeBSD Documentation Project'
+description: 'Для разработчиков систем FreeBSD. В этой книге рассматриваются архитектурные особенности многих важных подсистем ядра FreeBSD'
+next: books/arch-handbook/parti
+params:
+ path: /books/arch-handbook/
+showBookMenu: true
+tags: ["Arch Handbook", "FreeBSD"]
+title: 'Руководство по архитектуре FreeBSD'
+trademarks: ["freebsd", "apple", "microsoft", "unix", "general"]
+weight: 0
+---
+
+= Руководство по архитектуре FreeBSD
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[.abstract-title]
+Аннотация
+
+Добро пожаловать в Руководство по архитектуре FreeBSD. Это руководство находится _в стадии разработки_ и создаётся усилиями многих участников. Многие разделы пока не написаны, а существующие могут требовать обновления. Если вы хотите помочь в работе над этим проектом, напишите на электронную почту списка рассылки {freebsd-doc}.
+
+Актуальная версия этого документа всегда доступна на https://www.FreeBSD.org/[официальном веб-сервере FreeBSD]. Его также можно загрузить в различных форматах и с разными вариантами сжатия с https://download.freebsd.org/doc/[сервера загрузок FreeBSD] или одного из многочисленных зеркал extref:{handbook}mirrors/[mirror sites, mirrors].
+
+'''
diff --git a/documentation/content/ru/books/arch-handbook/_index.po b/documentation/content/ru/books/arch-handbook/_index.po
new file mode 100644
index 0000000000..980f503404
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/_index.po
@@ -0,0 +1,71 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-08-16 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbook_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/arch-handbook/_index.adoc:1
+#, no-wrap
+msgid "For FreeBSD system developers. This book covers the architectural details of many important FreeBSD kernel subsystems"
+msgstr "Для разработчиков систем FreeBSD. В этой книге рассматриваются архитектурные особенности многих важных подсистем ядра FreeBSD"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/_index.adoc:18
+#, no-wrap
+msgid "FreeBSD Architecture Handbook"
+msgstr "Руководство по архитектуре FreeBSD"
+
+#. type: .abstract-title
+#: documentation/content/en/books/arch-handbook/_index.adoc:51
+msgid "Abstract"
+msgstr "Аннотация"
+
+#. type: .abstract-title
+#: documentation/content/en/books/arch-handbook/_index.adoc:53
+msgid ""
+"Welcome to the FreeBSD Architecture Handbook. This manual is a _work in "
+"progress_ and is the work of many individuals. Many sections do not yet "
+"exist and some of those that do exist need to be updated. If you are "
+"interested in helping with this project, send email to the {freebsd-doc}."
+msgstr ""
+"Добро пожаловать в Руководство по архитектуре FreeBSD. Это руководство "
+"находится _в стадии разработки_ и создаётся усилиями многих участников. "
+"Многие разделы пока не написаны, а существующие могут требовать обновления. "
+"Если вы хотите помочь в работе над этим проектом, напишите на электронную "
+"почту списка рассылки {freebsd-doc}."
+
+#. type: .abstract-title
+#: documentation/content/en/books/arch-handbook/_index.adoc:55
+msgid ""
+"The latest version of this document is always available from the "
+"link:https://www.FreeBSD.org/[FreeBSD World Wide Web server]. It may also be "
+"downloaded in a variety of formats and compression options from the https://"
+"download.freebsd.org/doc/[FreeBSD download server] or one of the numerous "
+"extref:{handbook}[mirror sites, mirrors]."
+msgstr ""
+"Актуальная версия этого документа всегда доступна на https://www.FreeBSD.org/"
+"[официальном веб-сервере FreeBSD]. Его также можно загрузить в различных "
+"форматах и с разными вариантами сжатия с https://download.freebsd.org/doc/"
+"[сервера загрузок FreeBSD] или одного из многочисленных зеркал extref:"
+"{handbook}mirrors/[mirror sites, mirrors]."
+
+#. type: .abstract-title
+#: documentation/content/en/books/arch-handbook/_index.adoc:56
+msgid "'''"
+msgstr "'''"
diff --git a/documentation/content/ru/books/arch-handbook/bibliography/_index.adoc b/documentation/content/ru/books/arch-handbook/bibliography/_index.adoc
new file mode 100644
index 0000000000..96ff388686
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/bibliography/_index.adoc
@@ -0,0 +1,51 @@
+---
+description: 'Библиография Руководства по архитектуре FreeBSD'
+params:
+ path: /books/arch-handbook/bibliography/
+prev: books/arch-handbook/partiii
+showBookMenu: true
+tags: ["bibliography", "Arch Handbook", "FreeBSD"]
+title: Библиография
+weight: 20
+---
+
+[appendix]
+[[bibliography]]
+= Библиография
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: A
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[1] _Marshall Kirk McKusick, Keith Bostic, Michael J Karels, and John S Quarterman._ Copyright © 1996 Addison-Wesley Publishing Company, Inc.. 0-201-54979-4. Издано Addison-Wesley Publishing Company, Inc.. The Design and Implementation of the 4.4 BSD Operating System. 1-2.
diff --git a/documentation/content/ru/books/arch-handbook/bibliography/_index.po b/documentation/content/ru/books/arch-handbook/bibliography/_index.po
new file mode 100644
index 0000000000..c5253e7d57
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/bibliography/_index.po
@@ -0,0 +1,45 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-07-12 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookbibliography_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/arch-handbook/bibliography/_index.adoc:1
+#, no-wrap
+msgid "Bibliography of the FreeBSD Architecture Handbook"
+msgstr "Библиография Руководства по архитектуре FreeBSD"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/bibliography/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/bibliography/_index.adoc:14
+#, no-wrap
+msgid "Bibliography"
+msgstr "Библиография"
+
+#. type: appendix
+#: documentation/content/en/books/arch-handbook/bibliography/_index.adoc:51
+msgid ""
+"[1] _Marshall Kirk McKusick, Keith Bostic, Michael J Karels, and John S "
+"Quarterman._ Copyright © 1996 Addison-Wesley Publishing Company, Inc.. "
+"0-201-54979-4. Addison-Wesley Publishing Company, Inc.. The Design and "
+"Implementation of the 4.4 BSD Operating System. 1-2."
+msgstr ""
+"[1] _Marshall Kirk McKusick, Keith Bostic, Michael J Karels, and John S "
+"Quarterman._ Copyright © 1996 Addison-Wesley Publishing Company, Inc.. "
+"0-201-54979-4. Издано Addison-Wesley Publishing Company, Inc.. The Design "
+"and Implementation of the 4.4 BSD Operating System. 1-2."
diff --git a/documentation/content/ru/books/arch-handbook/book.adoc b/documentation/content/ru/books/arch-handbook/book.adoc
index 02d82d2ae2..22f9cfba0b 100644
--- a/documentation/content/ru/books/arch-handbook/book.adoc
+++ b/documentation/content/ru/books/arch-handbook/book.adoc
@@ -1,14 +1,16 @@
---
-title: FreeBSD Architecture Handbook
-authors:
- - author: The FreeBSD Documentation Project
-copyright: 2000-2006, 2012-2013 The FreeBSD Documentation Project
-description: FreeBSD Architecture Handbook
-trademarks: ["freebsd", "apple", "microsoft", "unix", "general"]
+add_split_page_link: true
+authors:
+ -
+ author: 'The FreeBSD Documentation Project'
+copyright: '2000-2006, 2012-2023 The FreeBSD Documentation Project'
+description: 'Для разработчиков систем FreeBSD. В этой книге рассматриваются архитектурные особенности многих важных подсистем ядра FreeBSD'
tags: ["Arch Handbook", "FreeBSD"]
+title: 'Руководство по архитектуре FreeBSD'
+trademarks: ["freebsd", "apple", "microsoft", "unix", "general"]
---
-= FreeBSD Architecture Handbook
+= Руководство по архитектуре FreeBSD
:doctype: book
:toc: macro
:toclevels: 2
@@ -20,7 +22,6 @@ tags: ["Arch Handbook", "FreeBSD"]
:experimental:
:book: true
:pdf: false
-:images-path: books/arch-handbook/
ifdef::env-beastie[]
ifdef::backend-html5[]
@@ -44,15 +45,39 @@ ifndef::env-beastie[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
+[.abstract-title]
+Аннотация
+
+Добро пожаловать в Руководство по архитектуре FreeBSD. Это руководство находится _в стадии разработки_ и создаётся усилиями многих участников. Многие разделы пока не написаны, а существующие могут требовать обновления. Если вы хотите помочь в работе над этим проектом, напишите на электронную почту списка рассылки {freebsd-doc}.
+
+Актуальная версия этого документа всегда доступна на https://www.FreeBSD.org/[официальном веб-сервере FreeBSD]. Его также можно загрузить в различных форматах и с разными вариантами сжатия с https://download.freebsd.org/doc/[сервера загрузок FreeBSD] или одного из многочисленных зеркал extref:{handbook}mirrors/[mirror sites, mirrors].
+
'''
toc::[]
// Section one
-
-include::{chapters-path}locking/chapter.adoc[leveloffset=+1]
+include::{chapters-path}parti.adoc[]
+include::{chapters-path}boot/_index.adoc[leveloffset=+1]
+include::{chapters-path}locking/_index.adoc[leveloffset=+1]
+include::{chapters-path}kobj/_index.adoc[leveloffset=+1]
+include::{chapters-path}jail/_index.adoc[leveloffset=+1]
+include::{chapters-path}sysinit/_index.adoc[leveloffset=+1]]
+include::{chapters-path}mac/_index.adoc[leveloffset=+1]
+include::{chapters-path}vm/_index.adoc[leveloffset=+1]
+include::{chapters-path}smp/_index.adoc[leveloffset=+1]
// Section two
+include::{chapters-path}partii.adoc[]
+include::{chapters-path}driverbasics/_index.adoc[leveloffset=+1]
+include::{chapters-path}isa/_index.adoc[leveloffset=+1]
+include::{chapters-path}pci/_index.adoc[leveloffset=+1]
+include::{chapters-path}scsi/_index.adoc[leveloffset=+1]
+include::{chapters-path}usb/_index.adoc[leveloffset=+1]
+include::{chapters-path}newbus/_index.adoc[leveloffset=+1]
+include::{chapters-path}sound/_index.adoc[leveloffset=+1]
+include::{chapters-path}pccard/_index.adoc[leveloffset=+1]
-include::{chapters-path}driverbasics/chapter.adoc[leveloffset=+1]
-include::{chapters-path}sound/chapter.adoc[leveloffset=+1]
+// Section three
+include::{chapters-path}partiii.adoc[]
+include::{chapters-path}bibliography/_index.adoc[leveloffset=+1]
diff --git a/documentation/content/ru/books/arch-handbook/book.po b/documentation/content/ru/books/arch-handbook/book.po
new file mode 100644
index 0000000000..a099efa2f7
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/book.po
@@ -0,0 +1,71 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-08-16 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookbook/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/arch-handbook/book.adoc:1
+#, no-wrap
+msgid "For FreeBSD system developers. This book covers the architectural details of many important FreeBSD kernel subsystems"
+msgstr "Для разработчиков систем FreeBSD. В этой книге рассматриваются архитектурные особенности многих важных подсистем ядра FreeBSD"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/book.adoc:1
+#: documentation/content/en/books/arch-handbook/book.adoc:12
+#, no-wrap
+msgid "FreeBSD Architecture Handbook"
+msgstr "Руководство по архитектуре FreeBSD"
+
+#. type: .abstract-title
+#: documentation/content/en/books/arch-handbook/book.adoc:49
+msgid "Abstract"
+msgstr "Аннотация"
+
+#. type: .abstract-title
+#: documentation/content/en/books/arch-handbook/book.adoc:51
+msgid ""
+"Welcome to the FreeBSD Architecture Handbook. This manual is a _work in "
+"progress_ and is the work of many individuals. Many sections do not yet "
+"exist and some of those that do exist need to be updated. If you are "
+"interested in helping with this project, send email to the {freebsd-doc}."
+msgstr ""
+"Добро пожаловать в Руководство по архитектуре FreeBSD. Это руководство "
+"находится _в стадии разработки_ и создаётся усилиями многих участников. "
+"Многие разделы пока не написаны, а существующие могут требовать обновления. "
+"Если вы хотите помочь в работе над этим проектом, напишите на электронную "
+"почту списка рассылки {freebsd-doc}."
+
+#. type: .abstract-title
+#: documentation/content/en/books/arch-handbook/book.adoc:53
+msgid ""
+"The latest version of this document is always available from the "
+"link:https://www.FreeBSD.org/[FreeBSD World Wide Web server]. It may also be "
+"downloaded in a variety of formats and compression options from the https://"
+"download.freebsd.org/doc/[FreeBSD download server] or one of the numerous "
+"extref:{handbook}[mirror sites, mirrors]."
+msgstr ""
+"Актуальная версия этого документа всегда доступна на https://www.FreeBSD.org/"
+"[официальном веб-сервере FreeBSD]. Его также можно загрузить в различных "
+"форматах и с разными вариантами сжатия с https://download.freebsd.org/doc/"
+"[сервера загрузок FreeBSD] или одного из многочисленных зеркал extref:"
+"{handbook}mirrors/[mirror sites, mirrors]."
+
+#. type: .abstract-title
+#: documentation/content/en/books/arch-handbook/book.adoc:55
+msgid "'''"
+msgstr "'''"
diff --git a/documentation/content/ru/books/arch-handbook/boot/_index.adoc b/documentation/content/ru/books/arch-handbook/boot/_index.adoc
new file mode 100644
index 0000000000..5b356b42ac
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/boot/_index.adoc
@@ -0,0 +1,1351 @@
+---
+description: 'Начальная загрузка и инициализация ядра'
+next: books/arch-handbook/locking
+params:
+ path: /books/arch-handbook/boot/
+prev: books/arch-handbook/parti
+showBookMenu: true
+tags: ["boot", "BIOS", "kernel", "MBR", "FreeBSD"]
+title: 'Глава 1. Начальная загрузка и инициализация ядра'
+weight: 2
+---
+
+[[boot]]
+= Начальная загрузка и инициализация ядра
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 1
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[boot-synopsis]]
+== Обзор
+
+Эта глава представляет собой обзор процессов загрузки и инициализации системы, начиная с POST в BIOS (микропрограмме) и заканчивая созданием первого пользовательского процесса. Поскольку начальные этапы загрузки системы сильно зависят от архитектуры, в качестве примера используется архитектура IA-32. Однако архитектуры AMD64 и ARM64 гораздо важнее и интереснее, и их следует рассмотреть в ближайшем будущем в соответствии с темой этого документа.
+
+Процесс загрузки FreeBSD может быть удивительно сложным. После передачи управления от BIOS необходимо выполнить значительный объем низкоуровневой настройки перед загрузкой и выполнением ядра. Эта настройка должна быть выполнена простым и гибким способом, предоставляя пользователю широкие возможности для настройки и адаптации.
+
+[[boot-overview]]
+== Обзор
+
+Процесс загрузки — это операция, крайне зависимая от оборудования. Не только для каждой архитектуры компьютера должен быть написан код, но также могут существовать различные типы загрузки в рамках одной архитектуры. Например, список файлов в каталоге [.filename]#stand# показывает большое количество кода, зависящего от архитектуры. Для каждой из поддерживаемых архитектур существует отдельный каталог. FreeBSD поддерживает стандарт загрузки CSM (Compatibility Support Module). Таким образом, CSM поддерживается (как с GPT, так и с MBR разметкой), а также загрузка через UEFI (GPT полностью поддерживается, MBR — в основном). Также поддерживается загрузка файлов с ext2fs, MSDOS, UFS и ZFS. FreeBSD поддерживает функцию загрузочного окружения ZFS, которая позволяет основной ОС передавать детали о том, что загружать, выходящие за рамки простого раздела, как это было возможно ранее. Однако в наши дни UEFI более актуален, чем CSM. В следующем примере показана загрузка компьютера x86 с жёсткого диска с MBR-разметкой, где используется мультизагрузчик FreeBSD [.filename]#boot0#, сохранённый в самом первом секторе. Этот загрузочный код запускает трёхэтапный процесс загрузки FreeBSD.
+
+Ключ к пониманию этого процесса заключается в том, что он состоит из последовательных стадий возрастающей сложности. Эти стадии — [.filename]#boot1#, [.filename]#boot2# и [.filename]#loader# (подробнее см. man:boot[8]). Система загрузки выполняет каждую стадию последовательно. Последняя стадия, [.filename]#loader#, отвечает за загрузку ядра FreeBSD. Каждая стадия рассматривается в следующих разделах.
+
+Вот пример вывода, сгенерированного на различных этапах загрузки. Фактический вывод может отличаться в зависимости от машины:
+
+[.informaltable]
+[cols="20%,80%", frame="none"]
+|===
+
+|*Компонент FreeBSD*
+|*Вывод (может отличаться)*
+
+|`boot0`
+a|
+
+[source,bash]
+....
+F1 FreeBSD
+F2 BSD
+F5 Disk 2
+....
+
+|`boot2` footnote:[Это приглашение появится, если пользователь нажмет клавишу сразу после выбора ОС для загрузки на этапе boot0.]
+a|
+
+[source,bash]
+....
+>>FreeBSD/x86 BOOT
+Default: 0:ad(0p4)/boot/loader
+boot:
+....
+
+|[.filename]#loader#
+a|
+
+[source,bash]
+....
+BTX loader 1.00 BTX version is 1.02
+Consoles: internal video/keyboard
+BIOS drive C: is disk0
+BIOS 639kB/2096064kB available memory
+
+FreeBSD/x86 bootstrap loader, Revision 1.1
+Console internal video/keyboard
+(root@releng1.nyi.freebsd.org, Fri Apr 9 04:04:45 UTC 2021)
+Loading /boot/defaults/loader.conf
+/boot/kernel/kernel text=0xed9008 data=0x117d28+0x176650 syms=[0x8+0x137988+0x8+0x1515f8]
+....
+
+|ядро системы
+a|
+
+[source,bash]
+....
+Copyright (c) 1992-2021 The FreeBSD Project.
+Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
+ The Regents of the University of California. All rights reserved.
+FreeBSD is a registered trademark of The FreeBSD Foundation.
+FreeBSD 13.0-RELEASE 0 releng/13.0-n244733-ea31abc261f: Fri Apr 9 04:04:45 UTC 2021
+ root@releng1.nyi.freebsd.org:/usr/obj/usr/src/i386.i386/sys/GENERIC i386
+FreeBSD clang version 11.0.1 (git@github.com:llvm/llvm-project.git llvmorg-11.0.1-0-g43ff75f2c3fe)
+....
+
+|===
+
+[[boot-bios]]
+== BIOS
+
+При включении компьютера регистры процессора устанавливаются в некоторые предопределённые значения. Один из регистров — это регистр _указателя команд_, и его значение после включения питания чётко определено: это 32-битное значение `0xfffffff0`. Регистр указателя команд (также известный как Счётчик Команд) указывает на код, который должен быть выполнен процессором. Ещё один важный регистр — это 32-битный управляющий регистр `cr0`, и его значение сразу после перезагрузки равно `0`. Один из битов ``cr0``, бит PE (Protection Enabled, Защита Включена), указывает, работает ли процессор в 32-битном защищённом режиме или 16-битном реальном режиме. Поскольку этот бит сброшен при загрузке, процессор запускается в 16-битном реальном режиме. Реальный режим означает, среди прочего, что линейные и физические адреса идентичны. Причина, по которой процессор не запускается сразу в 32-битном защищённом режиме, — это обратная совместимость. В частности, процесс загрузки зависит от услуг, предоставляемых BIOS, а сам BIOS работает в устаревшем 16-битном коде.
+
+Значение `0xfffffff0` немного меньше 4 ГБ, поэтому, если в машине нет 4 ГБ физической памяти, оно не может указывать на действительный адрес памяти. Аппаратное обеспечение компьютера преобразует этот адрес так, чтобы он указывал на блок памяти BIOS.
+
+BIOS (Basic Input Output System) — это микросхема на материнской плате, которая содержит относительно небольшой объем памяти только для чтения (ROM). Эта память включает различные низкоуровневые процедуры, специфичные для оборудования, поставляемого с материнской платой. Процессор сначала переходит по адресу 0xfffffff0, который фактически находится в памяти BIOS. Обычно по этому адресу содержится инструкция перехода к процедурам POST BIOS.
+
+POST (Power On Self Test) — это набор процедур, включающих проверку памяти, проверку системной шины и другую низкоуровневую инициализацию, чтобы процессор мог правильно настроить компьютер. Важным этапом на этой стадии является определение загрузочного устройства. Современные реализации BIOS позволяют выбирать загрузочное устройство, обеспечивая загрузку с дискеты, CD-ROM, жесткого диска или других устройств.
+
+Самым последним действием в POST является инструкция `INT 0x19`. Обработчик `INT 0x19` считывает 512 байт из первого сектора загрузочного устройства в память по адресу `0x7c00`. Термин _первый сектор_ происходит из архитектуры жёстких дисков, где магнитная пластина разделена на множество цилиндрических дорожек. Дорожки нумеруются, и каждая дорожка разделена на несколько (обычно 64) секторов. Нумерация дорожек начинается с 0, но нумерация секторов начинается с 1. Дорожка 0 находится на внешней стороне магнитной пластины, а сектор 1, первый сектор, имеет особое назначение. Он также называется MBR (Master Boot Record) или Главная Загрузочная Запись. Остальные секторы на первой дорожке не используются.
+
+Этот сектор является нашей точкой входа в последовательность загрузки. Как мы увидим, этот сектор содержит копию нашей программы [.filename]#boot0#. BIOS выполняет переход по адресу `0x7c00`, и она начинает выполняться.
+
+[[boot-boot0]]
+== Главная загрузочная запись (`boot0`)
+
+После получения управления от BIOS по адресу памяти `0x7c00` начинает выполняться [.filename]#boot0#. Это первый код, который управляется FreeBSD. Задача [.filename]#boot0# довольно проста: просканировать таблицу разделов и позволить пользователю выбрать, с какого раздела загружаться. Таблица разделов — это специальная стандартная структура данных, встроенная в MBR (а значит, и в [.filename]#boot0#), которая описывает четыре стандартных PC-раздела. [.filename]#boot0# находится в файловой системе как [.filename]#/boot/boot0#. Это небольшой файл размером 512 байт, и именно его процедура установки FreeBSD записывает в MBR жёсткого диска, если во время установки была выбрана опция "bootmanager". Действительно, [.filename]#boot0# _и есть_ MBR.
+
+Как упоминалось ранее, мы вызываем прерывание BIOS `INT 0x19` для загрузки MBR ([.filename]#boot0#) в память по адресу `0x7c00`. Исходный файл для [.filename]#boot0# можно найти в [.filename]#stand/i386/boot0/boot0.S# — это впечатляющий фрагмент кода, написанный Робертом Нордье.
+
+Особая структура, начинающаяся со смещения `0x1be` в MBR, называется _таблицей разделов_. Она содержит четыре записи по 16 байт каждая, называемые _записями разделов_, которые определяют, как разделён жёсткий диск, или, в терминологии FreeBSD, нарезан. Один из этих 16 байт указывает, является ли раздел (срез) загрузочным или нет. Ровно одна запись должна быть с этом установленным флагом, иначе код [.filename]#boot0# откажется продолжать работу.
+
+Запись о разделе содержит следующие поля:
+
+* 1-байтовый тип файловой системы
+* 1-байтовый флаг загрузки (`bootable`)
+* 6-байтовый дескриптор в формате CHS
+* 8-байтовый дескриптор в формате LBA
+
+Дескриптор записи раздела содержит информацию о том, где именно раздел расположен на диске. Оба дескриптора, LBA и CHS, описывают одну и ту же информацию, но разными способами: LBA (Logical Block Addressing) содержит начальный сектор раздела и его длину, тогда как CHS (Cylinder Head Sector) содержит координаты первого и последнего секторов раздела. Таблица разделов завершается специальной сигнатурой `0xaa55`.
+
+MBR должен помещаться в 512 байт, один сектор диска. Эта программа использует низкоуровневые «трюки», такие как использование побочных эффектов определённых инструкций и повторное использование значений регистров из предыдущих операций, чтобы максимально эффективно использовать минимально возможное количество инструкций. Также необходимо соблюдать осторожность при работе с таблицей разделов, которая встроена в сам MBR. По этим причинам будьте очень внимательны при изменении [.filename]#boot0.S#.
+
+Обратите внимание, что исходный файл [.filename]#boot0.S# ассемблируется "как есть": инструкции переводятся одна за одной в бинарный код без дополнительной информации (например, без формата файла ELF). Такой низкоуровневый контроль достигается на этапе компоновки с помощью специальных флагов, передаваемых компоновщику. Например, текстовая секция программы располагается по адресу `0x600`. На практике это означает, что [.filename]#boot0# должен быть загружен в память по адресу `0x600` для корректной работы.
+
+Стоит взглянуть на [.filename]#Makefile# для [.filename]#boot0# ([.filename]#stand/i386/boot0/Makefile#), так как он определяет некоторые аспекты поведения [.filename]#boot0# во время выполнения. Например, если для ввода-вывода используется терминал, подключённый к последовательному порту (COM1), необходимо определить макрос `SIO` (`-DSIO`). `-DPXE` включает загрузку через PXE при нажатии kbd:[F6]. Кроме того, программа определяет набор _флагов_, которые позволяют дополнительно настроить её поведение. Всё это проиллюстрировано в [.filename]#Makefile#. Например, обратите внимание на директивы компоновщика, которые предписывают ему начинать секцию текста с адреса `0x600` и создавать выходной файл "как есть" (удаляя любое форматирование файла):
+
+[.programlisting]
+....
+ BOOT_BOOT0_ORG?=0x600
+ ORG=${BOOT_BOOT0_ORG}
+....
+
+.[.filename]#stand/i386/boot0/Makefile# [[boot-boot0-makefile-as-is]]
+Приступим к изучению MBR, или [.filename]#boot0#, начиная с точки входа.
+
+[NOTE]
+====
+В некоторые инструкции были внесены изменения для лучшего изложения. Например, некоторые макросы раскрыты, а некоторые проверки макросов опущены, когда результат проверки известен. Это относится ко всем приведённым примерам кода.
+====
+
+[.programlisting]
+....
+start:
+ cld # String ops inc
+ xorw %ax,%ax # Zero
+ movw %ax,%es # Address
+ movw %ax,%ds # data
+ movw %ax,%ss # Set up
+ movw $LOAD,%sp # stack
+....
+
+.[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-entrypoint]]
+Этот первый блок кода является точкой входа программы. Именно сюда BIOS передаёт управление. Сначала он гарантирует, что строковые операции автоматически увеличивают указатели операндов (инструкция `cld`) footnote:[В случае сомнений мы отсылаем читателя к официальным руководствам Intel, где описана точная семантика каждой инструкции: .]. Затем, не делая предположений о состоянии сегментных регистров, он их инициализирует. Наконец, он устанавливает регистр указателя стека (`%sp`) в ($LOAD = адрес `0x7c00`), чтобы обеспечить работоспособный стек.
+
+Следующий блок отвечает за перемещение и последующий переход к перемещенному коду.
+
+[.programlisting]
+....
+ movw %sp,%si # Source
+ movw $start,%di # Destination
+ movw $0x100,%cx # Word count
+ rep # Relocate
+ movsw # code
+ movw %di,%bp # Address variables
+ movb $0x8,%cl # Words to clear
+ rep # Zero
+ stosw # them
+ incb -0xe(%di) # Set the S field to 1
+ jmp main-LOAD+ORIGIN # Jump to relocated code
+....
+
+.[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-relocation]]
+Так как [.filename]#boot0# загружается BIOS по адресу `0x7C00`, он копирует себя по адресу `0x600` и передаёт управление туда (напомним, что он был слинкован для выполнения по адресу `0x600`). Исходный адрес, `0x7c00`, копируется в регистр `%si`. Конечный адрес, `0x600`, — в регистр `%di`. Количество слов для копирования, `256` (размер программы = 512 байт), копируется в регистр `%cx`. Далее инструкция `rep` повторяет следующую за ней инструкцию, то есть `movsw`, количество раз, указанное в регистре `%cx`. Инструкция `movsw` копирует слово, на которое указывает `%si`, по адресу, на который указывает `%di`. Это повторяется ещё 255 раз. При каждом повторении оба регистра, исходный и конечный, `%si` и `%di`, увеличиваются на единицу. Таким образом, по завершении копирования 256 слов (512 байт), `%di` имеет значение `0x600`+`512`= `0x800`, а `%si` — значение `0x7c00`+`512`= `0x7e00`; таким образом, мы завершили _перемещение_ кода. С момента последнего обновления этого документа инструкции копирования в коде изменились, поэтому вместо movsb и stosb были введены movsw и stosw, которые копируют 2 байта (1 слово) за одну итерацию.
+
+Затем регистр назначения `%di` копируется в `%bp`. `%bp` получает значение `0x800`. Значение `8` копируется в `%cl` для подготовки новой строковой операции (как в предыдущей `movsw`). Теперь `stosw` выполняется 8 раз. Эта инструкция копирует значение `0` по адресу, на который указывает регистр назначения (`%di`, то есть `0x800`), и увеличивает его. Это повторяется ещё 7 раз, так что `%di` в итоге получает значение `0x810`. Фактически это очищает диапазон адресов `0x800`-`0x80f`. Этот диапазон используется как (фиктивная) таблица разделов для записи MBR обратно на диск. Наконец, полю сектора для CHS-адресации этого фиктивного раздела присваивается значение 1, и выполняется переход к основной функции из перемещённого кода. Обратите внимание, что до этого перехода к перемещённому коду любые ссылки на абсолютные адреса избегались.
+
+Следующий блок кода проверяет, следует ли использовать номер диска, предоставленный BIOS, или тот, что хранится в [.filename]#boot0#.
+
+[.programlisting]
+....
+main:
+ testb $SETDRV,_FLAGS(%bp) # Set drive number?
+#ifndef CHECK_DRIVE /* disable drive checks */
+ jz save_curdrive # no, use the default
+#else
+ jnz disable_update # Yes
+ testb %dl,%dl # Drive number valid?
+ js save_curdrive # Possibly (0x80 set)
+#endif
+....
+
+.[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-drivenumber]]
+Этот код проверяет бит `SETDRV` (`0x20`) в переменной _flags_. Напомним, что регистр `%bp` указывает на адрес `0x800`, поэтому проверка выполняется для переменной _flags_ по адресу `0x800`-`69`= `0x7bb`. Это пример типа изменений, которые можно внести в [.filename]#boot0#. Флаг `SETDRV` не установлен по умолчанию, но его можно задать в [.filename]#Makefile#. Если он установлен, используется номер диска, сохранённый в MBR, вместо предоставленного BIOS. Мы предполагаем значения по умолчанию и то, что BIOS предоставил корректный номер диска, поэтому переходим к `save_curdrive`.
+
+Следующий блок сохраняет номер диска, предоставленный BIOS, и вызывает `putn` для вывода новой строки на экран.
+
+[.programlisting]
+....
+save_curdrive:
+ movb %dl, (%bp) # Save drive number
+ pushw %dx # Also in the stack
+#ifdef TEST /* test code, print internal bios drive */
+ rolb $1, %dl
+ movw $drive, %si
+ call putkey
+#endif
+ callw putn # Print a newline
+....
+
+.[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-savedrivenumber]]
+Обратите внимание, что мы предполагаем, что `TEST` не определён, поэтому условный код в нём не собирается и не появится в нашем исполняемом файле [.filename]#boot0#.
+
+Следующий блок реализует фактическое сканирование таблицы разделов. Он выводит на экран тип раздела для каждой из четырёх записей в таблице разделов. Каждый тип сравнивается со списком известных файловых систем операционных систем. Примерами распознаваемых типов разделов являются NTFS (Windows(R), ID 0x7), `ext2fs` (Linux(R), ID 0x83) и, конечно же, `ffs`/`ufs2` (FreeBSD, ID 0xa5). Реализация довольно проста.
+
+[.programlisting]
+....
+ movw $(partbl+0x4),%bx # Partition table (+4)
+ xorw %dx,%dx # Item number
+
+read_entry:
+ movb %ch,-0x4(%bx) # Zero active flag (ch == 0)
+ btw %dx,_FLAGS(%bp) # Entry enabled?
+ jnc next_entry # No
+ movb (%bx),%al # Load type
+ test %al, %al # skip empty partition
+ jz next_entry
+ movw $bootable_ids,%di # Lookup tables
+ movb $(TLEN+1),%cl # Number of entries
+ repne # Locate
+ scasb # type
+ addw $(TLEN-1), %di # Adjust
+ movb (%di),%cl # Partition
+ addw %cx,%di # description
+ callw putx # Display it
+
+next_entry:
+ incw %dx # Next item
+ addb $0x10,%bl # Next entry
+ jnc read_entry # Till done
+....
+
+.[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-partition-scan]]
+Важно отметить, что флаг активности для каждой записи сбрасывается, поэтому после сканирования _ни одна_ запись о разделе не активна в нашей копии [.filename]#boot0# в памяти. Позже флаг активности будет установлен для выбранного раздела. Это гарантирует, что только один активный раздел существует, если пользователь решит записать изменения обратно на диск.
+
+Следующий блок проверяет наличие других дисков. При запуске BIOS записывает количество дисков, присутствующих в компьютере, по адресу `0x475`. Если есть другие диски, [.filename]#boot0# выводит текущий диск на экран. Пользователь может позже дать команду [.filename]#boot0# просканировать разделы на другом диске.
+
+[.programlisting]
+....
+ popw %ax # Drive number
+ subb $0x80-0x1,%al # Does next
+ cmpb NHRDRV,%al # drive exist? (from BIOS?)
+ jb print_drive # Yes
+ decw %ax # Already drive 0?
+ jz print_prompt # Yes
+....
+
+.[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-test-drives]]
+Мы предполагаем, что присутствует только один диск, поэтому переход к `print_drive` не выполняется. Также мы предполагаем, что ничего необычного не произошло, поэтому переходим к `print_prompt`.
+
+Следующий блок просто выводит приглашение с последующим вариантом по умолчанию:
+
+[.programlisting]
+....
+print_prompt:
+ movw $prompt,%si # Display
+ callw putstr # prompt
+ movb _OPT(%bp),%dl # Display
+ decw %si # default
+ callw putkey # key
+ jmp start_input # Skip beep
+....
+
+.[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-prompt]]
+Наконец, выполняется переход к `start_input`, где используются сервисы BIOS для запуска таймера и чтения пользовательского ввода с клавиатуры; если таймер истекает, будет выбран вариант по умолчанию:
+
+[.programlisting]
+....
+start_input:
+ xorb %ah,%ah # BIOS: Get
+ int $0x1a # system time
+ movw %dx,%di # Ticks when
+ addw _TICKS(%bp),%di # timeout
+read_key:
+ movb $0x1,%ah # BIOS: Check
+ int $0x16 # for keypress
+ jnz got_key # Have input
+ xorb %ah,%ah # BIOS: int 0x1a, 00
+ int $0x1a # get system time
+ cmpw %di,%dx # Timeout?
+ jb read_key # No
+....
+
+.[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-start-input]]
+Прерывание запрашивается с номером `0x1a` и аргументом `0` в регистре `%ah`. BIOS имеет предопределённый набор сервисов, запрашиваемых приложениями как программно-генерируемые прерывания через инструкцию `int`, с получением аргументов в регистрах (в данном случае, `%ah`). Здесь, в частности, запрашивается количество тиков часов с момента последней полуночи; это значение вычисляется BIOS через RTC (Real Time Clock). Эти часы могут быть настроены на работу с частотой от 2 Гц до 8192 Гц. BIOS устанавливает их на 18,2 Гц при запуске. Когда запрос выполнен, 32-битный результат возвращается BIOS в регистрах `%cx` и `%dx` (младшие байты в `%dx`). Этот результат (часть `%dx`) копируется в регистр `%di`, и к `%di` добавляется значение переменной `TICKS`. Эта переменная находится в [.filename]#boot0# по смещению `_TICKS` (отрицательное значение) от регистра `%bp` (который, напомним, указывает на `0x800`). Значение этой переменной по умолчанию — `0xb6` (182 в десятичной системе). Идея заключается в том, что [.filename]#boot0# постоянно запрашивает время у BIOS, и когда значение, возвращённое в регистре `%dx`, становится больше значения, хранящегося в `%di`, время истекает и будет сделан выбор по умолчанию. Поскольку RTC тикает 18,2 раза в секунду, это условие выполнится через 10 секунд (это поведение по умолчанию можно изменить в [.filename]#Makefile#). До истечения этого времени [.filename]#boot0# непрерывно опрашивает BIOS на предмет ввода пользователя; это делается через `int 0x16`, аргумент `1` в `%ah`.
+
+Была нажата клавиша или истекло время, последующий код проверяет выбор. В зависимости от выбора, регистр `%si` устанавливается так, чтобы указывать на соответствующую запись раздела в таблице разделов. Этот новый выбор переопределяет предыдущий выбор по умолчанию. Действительно, он становится новым значением по умолчанию. Наконец, устанавливается флаг ACTIVE выбранного раздела. Если это было разрешено при компиляции, версия [.filename]#boot0# в памяти с этими изменёнными значениями записывается обратно в MBR на диске. Мы оставляем детали этой реализации читателю.
+
+Мы завершаем наше изучение последним блоком кода из программы [.filename]#boot0#:
+
+[.programlisting]
+....
+ movw $LOAD,%bx # Address for read
+ movb $0x2,%ah # Read sector
+ callw intx13 # from disk
+ jc beep # If error
+ cmpw $MAGIC,0x1fe(%bx) # Bootable?
+ jne beep # No
+ pushw %si # Save ptr to selected part.
+ callw putn # Leave some space
+ popw %si # Restore, next stage uses it
+ jmp *%bx # Invoke bootstrap
+....
+
+.[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-check-bootable]]
+Вспомним, что `%si` указывает на выбранную запись раздела. Эта запись сообщает нам, где начинается раздел на диске. Мы предполагаем, конечно, что выбранный раздел действительно является срезом FreeBSD.
+
+[NOTE]
+====
+Отныне мы будем отдавать предпочтение использованию технически более точного термина "слайс" вместо "раздел".
+====
+
+Буфер передачи установлен в `0x7c00` (регистр `%bx`), и запрос на чтение первого сектора слайса FreeBSD выполняется вызовом `intx13`. Мы предполагаем, что всё прошло успешно, поэтому переход к `beep` не выполняется. В частности, новый прочитанный сектор должен заканчиваться магической последовательностью `0xaa55`. Наконец, значение в `%si` (указатель на выбранную таблицу разделов) сохраняется для использования на следующем этапе, и выполняется переход по адресу `0x7c00`, где начинается выполнение нашего следующего этапа (только что прочитанного блока).
+
+[[boot-boot1]]
+== Этап `boot1`
+
+До сих пор мы прошли следующую последовательность:
+
+* BIOS выполнил первоначальную инициализацию оборудования, включая POST. MBR ([.filename]#boot0#) был загружен по адресу `0x7c00` из абсолютного сектора один с диска. Управление выполнением было передано по этому адресу.
+* [.filename]#boot0# переместил себя по адресу, по которому он был скомпонован для выполнения (`0x600`), после чего выполнил переход для продолжения выполнения в соответствующем месте. В завершение, [.filename]#boot0# загрузил первый сектор диска из раздела FreeBSD по адресу `0x7c00`. Управление выполнением было передано по этому адресу.
+
+[.filename]#boot1# — это следующий шаг в последовательности загрузки. Это первая из трех стадий загрузки. Обратите внимание, что до сих пор мы работали исключительно с секторами диска. Действительно, BIOS загружает самый первый сектор, а [.filename]#boot0# загружает первый сектор раздела FreeBSD. Обе загрузки происходят по адресу `0x7c00`. Мы можем концептуально представлять эти секторы диска как содержащие файлы [.filename]#boot0# и [.filename]#boot1#, соответственно, но на самом деле это не совсем верно для [.filename]#boot1#. Строго говоря, в отличие от [.filename]#boot0#, [.filename]#boot1# не является частью загрузочных блоков footnote:[Файл /boot/boot1 существует, но он не записывается в начало раздела FreeBSD. Вместо этого он объединяется с boot2, формируя файл boot, который записывается в начало раздела FreeBSD и считывается во время загрузки.]. Вместо этого, единый полноценный файл [.filename]#boot# ([.filename]#/boot/boot#) в итоге записывается на диск. Этот файл представляет собой комбинацию [.filename]#boot1#, [.filename]#boot2# и `Boot Extender` (или BTX). Этот единый файл превышает размер одного сектора (больше 512 байт). К счастью, [.filename]#boot1# занимает _ровно_ первые 512 байт этого файла, поэтому, когда [.filename]#boot0# загружает первый сектор раздела FreeBSD (512 байт), он фактически загружает [.filename]#boot1# и передает ему управление.
+
+Основная задача [.filename]#boot1# — загрузить следующий этап загрузки. Этот следующий этап несколько сложнее. Он состоит из сервера под названием "Boot Extender" (BTX) и клиента под названием [.filename]#boot2#. Как мы увидим, последний этап загрузки, [.filename]#loader#, также является клиентом сервера BTX.
+
+Давайте теперь подробно рассмотрим, что именно делает [.filename]#boot1#, начиная, как мы это делали для [.filename]#boot0#, с точки входа:
+
+[.programlisting]
+....
+start:
+ jmp main
+....
+
+.[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-entry]]
+Точка входа `start` просто переходит через специальную область данных к метке `main`, которая, в свою очередь, выглядит следующим образом:
+
+[.programlisting]
+....
+main:
+ cld # String ops inc
+ xor %cx,%cx # Zero
+ mov %cx,%es # Address
+ mov %cx,%ds # data
+ mov %cx,%ss # Set up
+ mov $start,%sp # stack
+ mov %sp,%si # Source
+ mov $MEM_REL,%di # Destination
+ incb %ch # Word count
+ rep # Copy
+ movsw # code
+....
+
+.[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-main]]
+Как и [.filename]#boot0#, этот код перемещает [.filename]#boot1#, на этот раз по адресу `0x700`. Однако, в отличие от [.filename]#boot0#, он не переходит туда. [.filename]#boot1# скомпонован для выполнения по адресу `0x7c00`, фактически там, куда он был изначально загружен. Причина этого перемещения будет рассмотрена далее.
+
+Далее идет цикл, который ищет слайс FreeBSD. Хотя [.filename]#boot0# загрузил [.filename]#boot1# из слайса FreeBSD, ему не была передана информация об этом footnote:[На самом деле мы передали указатель на адрес слайса в регистре %si. Однако boot1 не предполагает, что он был загружен boot0 (возможно, его загрузил другой MBR и не передал эту информацию), поэтому он ничего не предполагает.], поэтому [.filename]#boot1# должен повторно просканировать таблицу разделов, чтобы найти начало слайса FreeBSD. Для этого он перечитывает MBR:
+
+[.programlisting]
+....
+ mov $part4,%si # Partition
+ cmpb $0x80,%dl # Hard drive?
+ jb main.4 # No
+ movb $0x1,%dh # Block count
+ callw nread # Read MBR
+....
+
+.[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-find-freebsd]]
+В приведённом выше коде регистр `%dl` содержит информацию о загрузочном устройстве. Эти данные передаются BIOS и сохраняются MBR. Числа `0x80` и выше указывают на то, что мы имеем дело с жёстким диском, поэтому вызывается `nread`, где считывается MBR. Аргументы для `nread` передаются через `%si` и `%dh`. Адрес памяти по метке `part4` копируется в `%si`. Этот адрес памяти содержит "фальшивый раздел", который будет использован `nread`. Ниже приведены данные фальшивого раздела:
+
+[.programlisting]
+....
+ part4:
+ .byte 0x80, 0x00, 0x01, 0x00
+ .byte 0xa5, 0xfe, 0xff, 0xff
+ .byte 0x00, 0x00, 0x00, 0x00
+ .byte 0x50, 0xc3, 0x00, 0x00
+....
+
+.[.filename]#stand/i386/boot2/boot1.S# [[boot-boot2-make-fake-partition]]
+В частности, LBA для этой фиктивной раздела жестко закодирован как ноль. Это используется как аргумент для BIOS при чтении абсолютного сектора один с жесткого диска. Альтернативно, может использоваться адресация CHS. В этом случае, фиктивный раздел содержит цилиндр 0, головку 0 и сектор 1, что эквивалентно абсолютному сектору один.
+
+Продолжим, рассмотрев `nread`:
+
+[.programlisting]
+....
+nread:
+ mov $MEM_BUF,%bx # Transfer buffer
+ mov 0x8(%si),%ax # Get
+ mov 0xa(%si),%cx # LBA
+ push %cs # Read from
+ callw xread.1 # disk
+ jnc return # If success, return
+....
+
+.[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-nread]]
+Напомним, что `%si` указывает на поддельный раздел. Слово footnote:[В контексте 16-битного реального режима слово — это 2 байта.] по смещению `0x8` копируется в регистр `%ax`, а слово по смещению `0xa` — в `%cx`. BIOS интерпретирует их как младшее 4-байтовое значение, обозначающее LBA для чтения (старшие четыре байта предполагаются нулевыми). Регистр `%bx` содержит адрес памяти, куда будет загружен MBR. Инструкция, помещающая `%cs` в стек, очень интересна. В данном контексте она ничего не делает. Однако, как мы скоро увидим, [.filename]#boot2# в сочетании с сервером BTX также использует `xread.1`. Этот механизм будет рассмотрен в следующем разделе.
+
+Код в `xread.1` далее вызывает функцию `read`, которая фактически обращается к BIOS с запросом на чтение сектора диска:
+
+[.programlisting]
+....
+xread.1:
+ pushl $0x0 # absolute
+ push %cx # block
+ push %ax # number
+ push %es # Address of
+ push %bx # transfer buffer
+ xor %ax,%ax # Number of
+ movb %dh,%al # blocks to
+ push %ax # transfer
+ push $0x10 # Size of packet
+ mov %sp,%bp # Packet pointer
+ callw read # Read from disk
+ lea 0x10(%bp),%sp # Clear stack
+ lret # To far caller
+....
+
+.[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-xread1]]
+Обратите внимание на длинную инструкцию возврата в конце этого блока. Эта инструкция извлекает регистр `%cs`, помещённый в стек `nread`, и возвращает управление. В конце `nread` также возвращает управление.
+
+С загрузкой MBR в память начинается фактический цикл поиска слайса FreeBSD:
+
+[.programlisting]
+....
+ mov $0x1,%cx # Two passes
+main.1:
+ mov $MEM_BUF+PRT_OFF,%si # Partition table
+ movb $0x1,%dh # Partition
+main.2:
+ cmpb $PRT_BSD,0x4(%si) # Our partition type?
+ jne main.3 # No
+ jcxz main.5 # If second pass
+ testb $0x80,(%si) # Active?
+ jnz main.5 # Yes
+main.3:
+ add $0x10,%si # Next entry
+ incb %dh # Partition
+ cmpb $0x1+PRT_NUM,%dh # In table?
+ jb main.2 # Yes
+ dec %cx # Do two
+ jcxz main.1 # passes
+....
+
+.[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-find-part]]
+Если обнаружен слайс FreeBSD, выполнение продолжается на метке `main.5`. Обратите внимание, что при обнаружении слайса FreeBSD `%si` указывает на соответствующую запись в таблице разделов, а `%dh` содержит номер раздела. Мы предполагаем, что слайс FreeBSD найден, поэтому продолжаем выполнение на метке `main.5`:
+
+[.programlisting]
+....
+main.5:
+ mov %dx,MEM_ARG # Save args
+ movb $NSECT,%dh # Sector count
+ callw nread # Read disk
+ mov $MEM_BTX,%bx # BTX
+ mov 0xa(%bx),%si # Get BTX length and set
+ add %bx,%si # %si to start of boot2.bin
+ mov $MEM_USR+SIZ_PAG*2,%di # Client page 2
+ mov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx # Byte
+ sub %si,%cx # count
+ rep # Relocate
+ movsb # client
+....
+
+.[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-main5]]
+Напомним, что в данный момент регистр `%si` указывает на запись среза FreeBSD в таблице разделов MBR, поэтому вызов `nread` фактически прочитает секторы в начале этого раздела. Аргумент, переданный в регистре `%dh`, указывает `nread` прочитать 16 секторов диска. Напомним, что первые 512 байт, или первый сектор слайса FreeBSD, совпадает с программой [.filename]#boot1#. Также напомним, что файл, записанный в начало слайса FreeBSD, это не [.filename]#/boot/boot1#, а [.filename]#/boot/boot#. Давайте посмотрим на размер этих файлов в файловой системе:
+
+[source, bash]
+....
+-r--r--r-- 1 root wheel 512B Jan 8 00:15 /boot/boot0
+-r--r--r-- 1 root wheel 512B Jan 8 00:15 /boot/boot1
+-r--r--r-- 1 root wheel 7.5K Jan 8 00:15 /boot/boot2
+-r--r--r-- 1 root wheel 8.0K Jan 8 00:15 /boot/boot
+....
+
+Оба файла [.filename]#boot0# и [.filename]#boot1# имеют размер 512 байт каждый, поэтому они занимают _ровно_ один сектор диска. [.filename]#boot2# значительно больше, так как содержит как сервер BTX, так и клиент [.filename]#boot2#. Наконец, файл под названием просто [.filename]#boot# на 512 байт больше, чем [.filename]#boot2#. Этот файл представляет собой объединение [.filename]#boot1# и [.filename]#boot2#. Как уже отмечалось, [.filename]#boot0# записывается в самый первый сектор диска (MBR), а [.filename]#boot# записывается в первый сектор раздела FreeBSD; [.filename]#boot1# и [.filename]#boot2# _не_ записываются на диск. Команда, используемая для объединения [.filename]#boot1# и [.filename]#boot2# в единый файл [.filename]#boot#, выглядит просто как `cat boot1 boot2 > boot`.
+
+Итак, [.filename]#boot1# занимает ровно первые 512 байт [.filename]#boot#, и, поскольку [.filename]#boot# записывается в первый сектор слайса FreeBSD, [.filename]#boot1# полностью помещается в этот первый сектор. Когда `nread` читает первые 16 секторов слайса FreeBSD, он фактически читает весь файл [.filename]#boot# footnote:[512*16=8192 байта, ровно размер boot]. Более подробно о том, как [.filename]#boot# формируется из [.filename]#boot1# и [.filename]#boot2#, мы увидим в следующем разделе.
+
+Напомним, что `nread` использует адрес памяти `0x8c00` в качестве буфера передачи для хранения прочитанных секторов. Этот адрес выбран не случайно. Действительно, поскольку [.filename]#boot1# принадлежит первым 512 байтам, он оказывается в диапазоне адресов `0x8c00`-`0x8dff`. Следующие 512 байт (диапазон `0x8e00`-`0x8fff`) используются для хранения _bsdlabel_ footnote:[Исторически известной как disklabel. Если вам когда-либо было интересно, где FreeBSD хранит эту информацию, она находится в этой области — см. man:bsdlabel[8]].
+
+Начиная с адреса `0x9000` находится начало сервера BTX, и сразу за ним следует клиент [.filename]#boot2#. Сервер BTX действует как ядро и выполняется в защищённом режиме с наивысшим уровнем привилегий. В отличие от этого, клиенты BTX (например, [.filename]#boot2#) выполняются в пользовательском режиме. Мы увидим, как это реализовано, в следующем разделе. Код после вызова `nread` находит начало [.filename]#boot2# в буфере памяти и копирует его по адресу `0xc000`. Это связано с тем, что сервер BTX размещает [.filename]#boot2# для выполнения в сегменте, начинающемся с `0xa000`. Мы подробно рассмотрим это в следующем разделе.
+
+Последний блок кода в [.filename]#boot1# разрешает доступ к памяти выше 1MB footnote:[Это необходимо по историческим причинам. Заинтересованные читатели могут обратиться к .] и завершается переходом к начальной точке сервера BTX:
+
+[.programlisting]
+....
+seta20:
+ cli # Disable interrupts
+seta20.1:
+ dec %cx # Timeout?
+ jz seta20.3 # Yes
+
+ inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.1 # Yes
+ movb $0xd1,%al # Command: Write
+ outb %al,$0x64 # output port
+seta20.2:
+ inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.2 # Yes
+ movb $0xdf,%al # Enable
+ outb %al,$0x60 # A20
+seta20.3:
+ sti # Enable interrupts
+ jmp 0x9010 # Start BTX
+....
+
+.[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-seta20]]
+Обратите внимание, что непосредственно перед переходом прерывания включаются.
+
+[[btx-server]]
+== Сервер BTX
+
+Далее в нашей последовательности загрузки идёт сервер BTX. Давайте быстро вспомним, как мы сюда попали:
+
+* BIOS загружает абсолютный сектор один (MBR или [.filename]#boot0#) по адресу `0x7c00` и переходит туда.
+* [.filename]#boot0# перемещает себя по адресу `0x600`, по которому он был слинкован для выполнения, и переходит туда. Затем он читает первый сектор среза FreeBSD (который содержит [.filename]#boot1#) в адрес `0x7c00` и переходит туда.
+* [.filename]#boot1# загружает первые 16 секторов среза FreeBSD по адресу `0x8c00`. Эти 16 секторов, или 8192 байта, представляют собой весь файл [.filename]#boot#. Файл является объединением [.filename]#boot1# и [.filename]#boot2#. [.filename]#boot2#, в свою очередь, содержит сервер BTX и клиент [.filename]#boot2#. Наконец, выполняется переход по адресу `0x9010`, точке входа сервера BTX.
+
+Прежде чем изучать сервер BTX подробно, давайте рассмотрим, как создается единый, всеобъемлющий файл [.filename]#boot#. Способ сборки [.filename]#boot# определен в его [.filename]#Makefile# ([.filename]#stand/i386/boot2/Makefile#). Рассмотрим правило, которое создает файл [.filename]#boot#:
+
+[.programlisting]
+....
+ boot: boot1 boot2
+ cat boot1 boot2 > boot
+....
+
+.[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot]]
+Это говорит нам, что [.filename]#boot1# и [.filename]#boot2# необходимы, и правило просто объединяет их для создания одного файла с именем [.filename]#boot#. Правила для создания [.filename]#boot1# также довольно просты:
+
+[.programlisting]
+....
+ boot1: boot1.out
+ ${OBJCOPY} -S -O binary boot1.out ${.TARGET}
+
+ boot1.out: boot1.o
+ ${LD} ${LD_FLAGS} -e start --defsym ORG=${ORG1} -T ${LDSCRIPT} -o ${.TARGET} boot1.o
+....
+
+.[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot1]]
+Для применения правила создания [.filename]#boot1# необходимо собрать [.filename]#boot1.out#. Это, в свою очередь, зависит от наличия [.filename]#boot1.o#. Последний файл является результатом ассемблирования нашего знакомого [.filename]#boot1.S# без компоновки. Теперь применяется правило создания [.filename]#boot1.out#. Оно указывает, что [.filename]#boot1.o# должен быть скомпонован с точкой входа `start` и начальным адресом `0x7c00`. Наконец, [.filename]#boot1# создается из [.filename]#boot1.out# применением соответствующего правила. Это команда [.filename]#objcopy#, применяемая к [.filename]#boot1.out#. Обратите внимание на флаги, передаваемые [.filename]#objcopy#: `-S` указывает на удаление всей информации о перемещении и символов; `-O binary` указывает формат вывода, то есть простой, неформатированный двоичный файл.
+
+Имея [.filename]#boot1#, давайте посмотрим, как устроен [.filename]#boot2#:
+
+[.programlisting]
+....
+ boot2: boot2.ld
+ @set -- `ls -l ${.ALLSRC}`; x=$$((${BOOT2SIZE}-$$5)); \
+ echo "$$x bytes available"; test $$x -ge 0
+ ${DD} if=${.ALLSRC} of=${.TARGET} bs=${BOOT2SIZE} conv=sync
+
+ boot2.ld: boot2.ldr boot2.bin ${BTXKERN}
+ btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \
+ -o ${.TARGET} -P 1 boot2.bin
+
+ boot2.ldr:
+ ${DD} if=/dev/zero of=${.TARGET} bs=512 count=1
+
+ boot2.bin: boot2.out
+ ${OBJCOPY} -S -O binary boot2.out ${.TARGET}
+
+ boot2.out: ${BTXCRT} boot2.o sio.o ashldi3.o
+ ${LD} ${LD_FLAGS} --defsym ORG=${ORG2} -T ${LDSCRIPT} -o ${.TARGET} ${.ALLSRC}
+
+ boot2.h: boot1.out
+ ${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \
+ { x = $$1 - ORG1; \
+ printf("#define XREADORG %#x\n", REL1 + x) }' \
+ ORG1=`printf "%d" ${ORG1}` \
+ REL1=`printf "%d" ${REL1}` > ${.TARGET}
+....
+
+.[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot2]]
+Механизм сборки [.filename]#boot2# гораздо сложнее. Отметим наиболее важные моменты. Список зависимостей выглядит следующим образом:
+
+[.programlisting]
+....
+ boot2: boot2.ld
+ boot2.ld: boot2.ldr boot2.bin ${BTXDIR}
+ boot2.bin: boot2.out
+ boot2.out: ${BTXDIR} boot2.o sio.o ashldi3.o
+ boot2.h: boot1.out
+....
+
+.[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot2-more]]
+Отметим, что изначально файл заголовка [.filename]#boot2.h# отсутствует, но его создание зависит от [.filename]#boot1.out#, который у нас уже есть. Правило его создания немного лаконично, но важно то, что результат, [.filename]#boot2.h#, выглядит примерно так:
+
+[.programlisting]
+....
+#define XREADORG 0x725
+....
+
+.[.filename]#stand/i386/boot2/boot2.h# [[boot-boot1-make-boot2h]]
+Напомним, что [.filename]#boot1# был перемещён (т.е. скопирован из `0x7c00` в `0x700`). Это перемещение теперь обретает смысл, потому что, как мы увидим, сервер BTX освобождает часть памяти, включая область, куда [.filename]#boot1# был изначально загружен. Однако серверу BTX необходим доступ к функции `xread` из [.filename]#boot1#; согласно выводу [.filename]#boot2.h#, эта функция находится по адресу `0x725`. Действительно, сервер BTX использует функцию `xread` из перемещённого кода [.filename]#boot1#. Теперь эта функция доступна из клиента [.filename]#boot2#.
+
+Следующее правило указывает компоновщику на необходимость связать различные файлы ([.filename]#ashldi3.o#, [.filename]#boot2.o# и [.filename]#sio.o#). Обратите внимание, что выходной файл [.filename]#boot2.out# компонуется для выполнения по адресу `0x2000` (${ORG2}). Напомним, что [.filename]#boot2# будет выполняться в пользовательском режиме внутри специального пользовательского сегмента, созданного сервером BTX. Этот сегмент начинается с адреса `0xa000`. Также помните, что часть [.filename]#boot2# в [.filename]#boot# была скопирована по адресу `0xc000`, то есть со смещением `0x2000` от начала пользовательского сегмента, поэтому [.filename]#boot2# будет работать корректно при передаче управления на него. Далее, [.filename]#boot2.bin# создается из [.filename]#boot2.out# путем удаления символов и информации о формате; boot2.bin представляет собой _сырой_ бинарный файл. Теперь обратите внимание, что файл [.filename]#boot2.ldr# создается как 512-байтный файл, заполненный нулями. Это пространство зарезервировано для bsdlabel.
+
+Теперь, когда у нас есть файлы [.filename]#boot1#, [.filename]#boot2.bin# и [.filename]#boot2.ldr#, осталось только добавить сервер BTX перед созданием универсального файла [.filename]#boot#. Сервер BTX находится в [.filename]#stand/i386/btx/btx#; у него есть собственный [.filename]#Makefile# со своим набором правил для сборки. Важно отметить, что он также компилируется как _сырой_ бинарный файл и линкуется для выполнения по адресу `0x9000`. Подробности можно найти в [.filename]#stand/i386/btx/btx/Makefile#.
+
+Имея файлы, составляющие программу [.filename]#boot#, последним шагом является их _объединение_. Это выполняется специальной программой под названием [.filename]#btxld# (исходный код расположен в [.filename]#/usr/src/usr.sbin/btxld#). Некоторые аргументы этой программы включают имя выходного файла ([.filename]#boot#), его точку входа (`0x2000`) и формат файла (бинарный). Различные файлы окончательно объединяются этой утилитой в файл [.filename]#boot#, который состоит из [.filename]#boot1#, [.filename]#boot2#, `bsdlabel` и сервера BTX. Этот файл, занимающий ровно 16 секторов или 8192 байта, записывается в начало раздела FreeBSD во время установки. Теперь перейдем к изучению программы сервера BTX.
+
+Сервер BTX подготавливает простое окружение и переключается из 16-битного реального режима в 32-битный защищённый режим, непосредственно перед передачей управления клиенту. Это включает инициализацию и обновление следующих структур данных:
+
+* Изменяет `Таблицу Векторов Прерываний (IVT)`. IVT предоставляет обработчики исключений и прерываний для кода в Реальном Режиме.
+* Создается `Таблица дескрипторов прерываний (IDT)`. В ней предусмотрены записи для исключений процессора, аппаратных прерываний, двух системных вызовов и интерфейса V86. IDT предоставляет обработчики исключений и прерываний для кода в защищенном режиме.
+* Создается `Сегмент состояния задачи (TSS)`. Это необходимо, потому что процессор работает на _наименее_ привилегированном уровне при выполнении клиента ([.filename]#boot2#), но на _наиболее_ привилегированном уровне при выполнении сервера BTX.
+* Устанавливается GDT (Глобальная Таблица Дескрипторов). Создаются записи (дескрипторы) для кода и данных супервизора, кода и данных пользователя, а также кода и данных реального режима. footnote:[Код и данные реального режима необходимы при переключении обратно в реальный режим из защищённого режима, как указано в руководствах Intel.]
+
+Приступим к изучению фактической реализации. Напомним, что [.filename]#boot1# выполнил переход на адрес `0x9010` — точку входа сервера BTX. Прежде чем изучать выполнение программы там, обратите внимание, что сервер BTX имеет специальный заголовок в диапазоне адресов `0x9000-0x900f`, непосредственно перед точкой входа. Этот заголовок определён следующим образом:
+
+[.programlisting]
+....
+start: # Start of code
+/*
+ * BTX header.
+ */
+btx_hdr: .byte 0xeb # Machine ID
+ .byte 0xe # Header size
+ .ascii "BTX" # Magic
+ .byte 0x1 # Major version
+ .byte 0x2 # Minor version
+ .byte BTX_FLAGS # Flags
+ .word PAG_CNT-MEM_ORG>>0xc # Paging control
+ .word break-start # Text size
+ .long 0x0 # Entry address
+....
+
+.[.filename]#stand/i386/btx/btx/btx.S# [[btx-header]]
+Обратите внимание, что первые два байта — это `0xeb` и `0xe`. В архитектуре IA-32 эти два байта интерпретируются как относительный переход за заголовок к точке входа, поэтому теоретически [.filename]#boot1# мог бы перейти сюда (адрес `0x9000`) вместо адреса `0x9010`. Обратите внимание, что последнее поле в заголовке BTX — это указатель на точку входа клиента ([.filename]#boot2#)b2. Это поле исправляется во время компоновки.
+
+Сразу после заголовка следует точка входа сервера BTX:
+
+[.programlisting]
+....
+/*
+ * Initialization routine.
+ */
+init: cli # Disable interrupts
+ xor %ax,%ax # Zero/segment
+ mov %ax,%ss # Set up
+ mov $MEM_ESP0,%sp # stack
+ mov %ax,%es # Address
+ mov %ax,%ds # data
+ pushl $0x2 # Clear
+ popfl # flags
+....
+
+.[.filename]#stand/i386/btx/btx/btx.S# [[btx-init]]
+Этот код отключает прерывания, устанавливает рабочий стек (начиная с адреса `0x1800`) и очищает флаги в регистре EFLAGS. Обратите внимание, что инструкция `popfl` извлекает двойное слово (4 байта) из стека и помещает его в регистр EFLAGS. Поскольку извлекаемое значение фактически равно `2`, регистр EFLAGS эффективно очищается (IA-32 требует, чтобы бит 2 регистра EFLAGS всегда был равен 1).
+
+Следующий блок кода очищает (устанавливает в `0`) диапазон памяти `0x5e00-0x8fff`. В этом диапазоне будут созданы различные структуры данных:
+
+[.programlisting]
+....
+/*
+ * Initialize memory.
+ */
+ mov $MEM_IDT,%di # Memory to initialize
+ mov $(MEM_ORG-MEM_IDT)/2,%cx # Words to zero
+ rep # Zero-fill
+ stosw # memory
+....
+
+.[.filename]#stand/i386/btx/btx/btx.S# [[btx-clear-mem]]
+Напомним, что [.filename]#boot1# изначально загружался по адресу `0x7c00`, поэтому при такой инициализации памяти эта копия фактически исчезла. Однако также напомним, что [.filename]#boot1# был перемещён на адрес `0x700`, поэтому _эта_ копия всё ещё находится в памяти, и сервер BTX будет её использовать.
+
+Далее обновляется таблица векторов прерываний (IVT) в реальном режиме. IVT представляет собой массив пар сегмент/смещение для обработчиков исключений и прерываний. BIOS обычно сопоставляет аппаратные прерывания с векторами прерываний `0x8`–`0xf` и `0x70`–`0x77`, но, как будет показано, программируемый контроллер прерываний 8259A, микросхема, управляющая фактическим сопоставлением аппаратных прерываний с векторами прерываний, программируется для переназначения этих векторов прерываний с `0x8`–`0xf` на `0x20`–`0x27` и с `0x70`–`0x77` на `0x28`–`0x2f`. Таким образом, обработчики прерываний предоставляются для векторов прерываний `0x20`–`0x2f`. Причина, по которой обработчики, предоставляемые BIOS, не используются напрямую, заключается в том, что они работают в 16-битном реальном режиме, но не в 32-битном защищённом режиме. Вскоре будет выполнен переход в 32-битный защищённый режим. Однако сервер BTX настраивает механизм для эффективного использования обработчиков, предоставляемых BIOS:
+
+[.programlisting]
+....
+/*
+ * Update real mode IDT for reflecting hardware interrupts.
+ */
+ mov $intr20,%bx # Address first handler
+ mov $0x10,%cx # Number of handlers
+ mov $0x20*4,%di # First real mode IDT entry
+init.0: mov %bx,(%di) # Store IP
+ inc %di # Address next
+ inc %di # entry
+ stosw # Store CS
+ add $4,%bx # Next handler
+ loop init.0 # Next IRQ
+....
+
+.[.filename]#stand/i386/btx/btx/btx.S# [[btx-ivt]]
+Следующий блок создает IDT (таблицу дескрипторов прерываний). IDT в защищенном режиме аналогична IVT в реальном режиме. То есть, IDT описывает различные обработчики исключений и прерываний, используемые, когда процессор работает в защищенном режиме. По сути, она также состоит из массива пар сегмент/смещение, хотя структура несколько сложнее, поскольку сегменты в защищенном режиме отличаются от реального режима, и применяются различные механизмы защиты:
+
+[.programlisting]
+....
+/*
+ * Create IDT.
+ */
+ mov $MEM_IDT,%di # IDT's address
+ mov $idtctl,%si # Control string
+init.1: lodsb # Get entry
+ cbw # count
+ xchg %ax,%cx # as word
+ jcxz init.4 # If done
+ lodsb # Get segment
+ xchg %ax,%dx # P:DPL:type
+ lodsw # Get control
+ xchg %ax,%bx # set
+ lodsw # Get handler offset
+ mov $SEL_SCODE,%dh # Segment selector
+init.2: shr %bx # Handle this int?
+ jnc init.3 # No
+ mov %ax,(%di) # Set handler offset
+ mov %dh,0x2(%di) # and selector
+ mov %dl,0x5(%di) # Set P:DPL:type
+ add $0x4,%ax # Next handler
+init.3: lea 0x8(%di),%di # Next entry
+ loop init.2 # Till set done
+ jmp init.1 # Continue
+....
+
+.[.filename]#stand/i386/btx/btx/btx.S# [[btx-idt]]
+Каждая запись в `IDT` имеет длину 8 байт. Помимо информации о сегменте/смещении, они также описывают тип сегмента, уровень привилегий и присутствует ли сегмент в памяти. Структура организована так, что векторы прерываний от `0` до `0xf` (исключения) обрабатываются функцией `intx00`; вектор `0x10` (также исключение) обрабатывается `intx10`; аппаратные прерывания, которые позже настраиваются начиная с вектора `0x20` и до вектора `0x2f`, обрабатываются функцией `intx20`. Наконец, вектор прерывания `0x30`, используемый для системных вызовов, обрабатывается `intx30`, а векторы `0x31` и `0x32` обрабатываются `intx31`. Необходимо отметить, что только дескрипторы для векторов прерываний `0x30`, `0x31` и `0x32` имеют уровень привилегий 3, такой же, как у клиента [.filename]#boot2#, что означает, что клиент может выполнить программно-генерируемое прерывание к этим векторам через инструкцию `int` без ошибки (это способ, которым [.filename]#boot2# использует сервисы, предоставляемые сервером BTX). Также обратите внимание, что _только_ программно-генерируемые прерывания защищены от кода, выполняющегося на более низких уровнях привилегий. Аппаратно-генерируемые прерывания и исключения, генерируемые процессором, _всегда_ обрабатываются корректно, независимо от фактических привилегий.
+
+Следующий шаг — инициализация TSS (сегмента состояния задачи). TSS — это аппаратная функция, которая помогает операционной системе или исполнительному ПО реализовать многозадачность через абстракцию процессов. Архитектура IA-32 требует создания и использования _как минимум_ одного TSS, если используются механизмы многозадачности или определены различные уровни привилегий. Поскольку клиент [.filename]#boot2# выполняется на уровне привилегий 3, а сервер BTX работает на уровне привилегий 0, необходимо определить TSS:
+
+[.programlisting]
+....
+/*
+ * Initialize TSS.
+ */
+init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0
+ movb $SEL_SDATA,TSS_SS0(%di) # Set SS0
+ movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base
+....
+
+.[.filename]#stand/i386/btx/btx/btx.S# [[btx-tss]]
+Обратите внимание, что в TSS указано значение для указателя стека и сегмента стека уровня привилегий 0. Это необходимо, потому что если прерывание или исключение получено во время выполнения [.filename]#boot2# на уровне привилегий 3, процессор автоматически переключается на уровень привилегий 0, поэтому требуется новый рабочий стек. Наконец, полю базового адреса карты ввода-вывода TSS присваивается значение, которое представляет собой 16-битное смещение от начала TSS до битовой карты разрешений ввода-вывода и битовой карты перенаправления прерываний.
+
+После создания IDT и TSS процессор готов к переходу в защищённый режим. Это выполняется в следующем блоке:
+
+[.programlisting]
+....
+/*
+ * Bring up the system.
+ */
+ mov $0x2820,%bx # Set protected mode
+ callw setpic # IRQ offsets
+ lidt idtdesc # Set IDT
+ lgdt gdtdesc # Set GDT
+ mov %cr0,%eax # Switch to protected
+ inc %ax # mode
+ mov %eax,%cr0 #
+ ljmp $SEL_SCODE,$init.8 # To 32-bit code
+ .code32
+init.8: xorl %ecx,%ecx # Zero
+ movb $SEL_SDATA,%cl # To 32-bit
+ movw %cx,%ss # stack
+....
+
+.[.filename]#stand/i386/btx/btx/btx.S# [[btx-prot]]
+Сначала вызывается `setpic` для программирования 8259A PIC (программируемого контроллера прерываний). Этот чип подключен к нескольким источникам аппаратных прерываний. При получении прерывания от устройства он сигнализирует процессору соответствующим вектором прерывания. Это можно настроить так, чтобы определенные прерывания были связаны с конкретными векторами прерываний, как объяснялось ранее. Затем регистры IDTR (Interrupt Descriptor Table Register) и GDTR (Global Descriptor Table Register) загружаются инструкциями `lidt` и `lgdt` соответственно. Эти регистры загружаются базовым адресом и предельным адресом для IDT и GDT. Следующие три инструкции устанавливают бит Protection Enable (PE) в регистре `%cr0`. Это фактически переключает процессор в 32-битный защищенный режим. Затем выполняется дальний переход на `init.8` с использованием селектора сегмента SEL_SCODE, который выбирает сегмент кода супервизора (Supervisor Code Segment). После этого перехода процессор фактически работает на уровне CPL 0 — наиболее привилегированном уровне. Наконец, для стека выбирается сегмент данных супервизора (Supervisor Data Segment) путем присвоения селектора сегмента SEL_SDATA регистру `%ss`. Этот сегмент данных также имеет уровень привилегий `0`.
+
+Наш последний блок кода отвечает за загрузку TR (Регистра Задач) с селектором сегмента для TSS, который мы создали ранее, и настройку окружения пользовательского режима перед передачей управления исполнения клиенту [.filename]#boot2#.
+
+[.programlisting]
+....
+/*
+ * Launch user task.
+ */
+ movb $SEL_TSS,%cl # Set task
+ ltr %cx # register
+ movl $MEM_USR,%edx # User base address
+ movzwl %ss:BDA_MEM,%eax # Get free memory
+ shll $0xa,%eax # To bytes
+ subl $ARGSPACE,%eax # Less arg space
+ subl %edx,%eax # Less base
+ movb $SEL_UDATA,%cl # User data selector
+ pushl %ecx # Set SS
+ pushl %eax # Set ESP
+ push $0x202 # Set flags (IF set)
+ push $SEL_UCODE # Set CS
+ pushl btx_hdr+0xc # Set EIP
+ pushl %ecx # Set GS
+ pushl %ecx # Set FS
+ pushl %ecx # Set DS
+ pushl %ecx # Set ES
+ pushl %edx # Set EAX
+ movb $0x7,%cl # Set remaining
+init.9: push $0x0 # general
+ loop init.9 # registers
+#ifdef BTX_SERIAL
+ call sio_init # setup the serial console
+#endif
+ popa # and initialize
+ popl %es # Initialize
+ popl %ds # user
+ popl %fs # segment
+ popl %gs # registers
+ iret # To user mode
+....
+
+.[.filename]#stand/i386/btx/btx/btx.S# [[btx-end]]
+Обратите внимание, что среда клиента включает селектор сегмента стека и указатель стека (регистры `%ss` и `%esp`). Действительно, как только TR загружается соответствующим селектором сегмента стека (инструкция `ltr`), указатель стека вычисляется и помещается в стек вместе с селектором сегмента стека. Затем значение `0x202` помещается в стек; это значение, которое EFLAGS получит при передаче управления клиенту. Также в стек помещаются селектор сегмента кода пользовательского режима и точка входа клиента. Напомним, что эта точка входа прописывается в заголовке BTX во время компоновки. Наконец, селекторы сегментов (хранящиеся в регистре `%ecx`) для регистров сегментов `%gs, %fs, %ds и %es` помещаются в стек вместе со значением из `%edx` (`0xa000`). Примите во внимание эти значения, помещенные в стек (они скоро будут извлечены). Затем значения для оставшихся регистров общего назначения также помещаются в стек (обратите внимание на цикл `loop`, который помещает значение `0` семь раз). Теперь начнётся извлечение значений из стека. Сначала инструкция `popa` извлекает из стека последние семь помещённых значений. Они сохраняются в регистрах общего назначения в порядке `%edi, %esi, %ebp, %ebx, %edx, %ecx, %eax`. Затем различные селекторы сегментов, помещённые в стек, извлекаются в соответствующие регистры сегментов. В стеке остаются ещё пять значений. Они извлекаются при выполнении инструкции `iret`. Эта инструкция сначала извлекает значение, которое было помещено из заголовка BTX. Это значение является указателем на точку входа [.filename]#boot2#. Оно помещается в регистр `%eip` — регистр указателя инструкций. Затем селектор сегмента кода пользователя извлекается и копируется в регистр `%cs`. Помните, что уровень привилегий этого сегмента — 3, наименее привилегированный уровень. Это означает, что мы должны предоставить значения для стека этого уровня привилегий. Именно поэтому процессор, помимо дальнейшего извлечения значения для регистра EFLAGS, выполняет ещё два извлечения из стека. Эти значения попадают в указатель стека (`%esp`) и сегмент стека (`%ss`). Теперь выполнение продолжается с точки входа ``boot0``.
+
+Важно отметить, как определяется сегмент пользовательского кода. _Базовый адрес_ этого сегмента установлен на `0xa000`. Это означает, что адреса памяти кода являются _относительными_ к адресу 0xa000; если код, который выполняется, извлекается из адреса `0x2000`, _фактический_ адрес в памяти будет `0xa000+0x2000=0xc000`.
+
+[[boot2]]
+== Этап загрузки boot2
+
+`boot2` определяет важную структуру, `struct bootinfo`. Эта структура инициализируется `boot2` и передается загрузчику, а затем ядру. Некоторые узлы этой структуры устанавливаются `boot2`, остальные — загрузчиком. Эта структура, среди прочей информации, содержит имя файла ядра, геометрию жесткого диска в BIOS, номер диска в BIOS для загрузочного устройства, доступную физическую память, указатель `envp` и т.д. Ее определение выглядит так:
+
+[.programlisting]
+....
+/usr/include/machine/bootinfo.h:
+struct bootinfo {
+ u_int32_t bi_version;
+ u_int32_t bi_kernelname; /* represents a char * */
+ u_int32_t bi_nfs_diskless; /* struct nfs_diskless * */
+ /* End of fields that are always present. */
+#define bi_endcommon bi_n_bios_used
+ u_int32_t bi_n_bios_used;
+ u_int32_t bi_bios_geom[N_BIOS_GEOM];
+ u_int32_t bi_size;
+ u_int8_t bi_memsizes_valid;
+ u_int8_t bi_bios_dev; /* bootdev BIOS unit number */
+ u_int8_t bi_pad[2];
+ u_int32_t bi_basemem;
+ u_int32_t bi_extmem;
+ u_int32_t bi_symtab; /* struct symtab * */
+ u_int32_t bi_esymtab; /* struct symtab * */
+ /* Items below only from advanced bootloader */
+ u_int32_t bi_kernend; /* end of kernel space */
+ u_int32_t bi_envp; /* environment */
+ u_int32_t bi_modulep; /* preloaded modules */
+};
+....
+
+`boot2` входит в бесконечный цикл, ожидая ввода пользователя, затем вызывает `load()`. Если пользователь ничего не нажимает, цикл прерывается по таймауту, и `load()` загружает файл по умолчанию ([.filename]#/boot/loader#). Функции `ino_t lookup(char *filename)` и `int xfsread(ino_t inode, void *buf, size_t nbyte)` используются для чтения содержимого файла в память. [.filename]#/boot/loader# — это ELF-бинарный файл, но с заголовком ELF, перед которым добавлена структура `struct exec` из [.filename]#a.out#. `load()` анализирует ELF-заголовок загрузчика, загружает содержимое [.filename]#/boot/loader# в память и передаёт управление на точку входа загрузчика:
+
+[.programlisting]
+....
+stand/i386/boot2/boot2.c:
+ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
+ MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),
+ 0, 0, 0, VTOP(&bootinfo));
+....
+
+[[boot-loader]]
+== Этап загрузчика (loader)
+
+Загрузчик также является клиентом BTX. Я не буду подробно описывать его здесь, существует исчерпывающая man-страница, написанная Майком Смитом: man:loader[8]. Основные механизмы и BTX были рассмотрены выше.
+
+Основная задача загрузчика — загрузить ядро. Когда ядро загружено в память, загрузчик вызывает его:
+
+[.programlisting]
+....
+stand/common/boot.c:
+ /* Call the exec handler from the loader matching the kernel */
+ file_formats[fp->f_loader]->l_exec(fp);
+....
+
+[[boot-kernel]]
+== Инициализация ядра
+
+Давайте рассмотрим команду, которая компонует ядро. Это поможет определить точное местоположение, где загрузчик передает выполнение ядру. Это местоположение является фактической точкой входа ядра. Данная команда теперь исключена из [.filename]#sys/conf/Makefile.i386#. Интересующее нас содержимое можно найти в [.filename]#/usr/obj/usr/src/i386.i386/sys/GENERIC/#.
+
+[.programlisting]
+....
+/usr/obj/usr/src/i386.i386/sys/GENERIC/kernel.meta:
+ld -m elf_i386_fbsd -Bdynamic -T /usr/src/sys/conf/ldscript.i386 --build-id=sha1 --no-warn-mismatch \
+--warn-common --export-dynamic --dynamic-linker /red/herring -X -o kernel locore.o
+<lots of kernel .o files>
+....
+
+Вот несколько интересных наблюдений. Во-первых, ядро представляет собой динамически связанный бинарный файл ELF, но динамический компоновщик для ядра — это [.filename]#/red/herring#, что явно является фиктивным файлом. Во-вторых, взглянув на файл [.filename]#sys/conf/ldscript.i386#, можно понять, какие параметры ld используются при компиляции ядра. Читая первые несколько строк, видим, что строка
+
+[.programlisting]
+....
+sys/conf/ldscript.i386:
+ENTRY(btext)
+....
+
+говорит, что точка входа ядра — это символ `btext`. Этот символ определён в [.filename]#locore.s#:
+
+[.programlisting]
+....
+sys/i386/i386/locore.s:
+ .text
+/**********************************************************************
+ *
+ * This is where the bootblocks start us, set the ball rolling...
+ *
+ */
+NON_GPROF_ENTRY(btext)
+....
+
+Сначала регистр EFLAGS устанавливается в предопределённое значение 0x00000002. Затем инициализируются все сегментные регистры:
+
+[.programlisting]
+....
+sys/i386/i386/locore.s:
+/* Don't trust what the BIOS gives for eflags. */
+ pushl $PSL_KERNEL
+ popfl
+
+/*
+ * Don't trust what the BIOS gives for %fs and %gs. Trust the bootstrap
+ * to set %cs, %ds, %es and %ss.
+ */
+ mov %ds, %ax
+ mov %ax, %fs
+ mov %ax, %gs
+....
+
+btext вызывает подпрограммы `recover_bootinfo()` и `identify_cpu()`, которые также определены в [.filename]#locore.s#. Вот описание их функций:
+
+[.informaltable]
+[cols="1,1", frame="none"]
+|===
+
+|`recover_bootinfo`
+|Эта процедура разбирает параметры, переданные ядру при загрузке.
+Ядро могло быть загружено тремя способами: загрузчиком (как описано выше), старыми загрузочными блоками диска или по старой процедуре загрузки без диска.
+Эта функция определяет метод загрузки и сохраняет структуру `struct bootinfo` в памяти ядра.
+
+|`identify_cpu`
+|Эта функция пытается определить, на каком процессоре она выполняется, сохраняя найденное значение в переменной `_cpu`.
+|===
+
+Следующие шаги включают активацию VME, если процессор поддерживает эту функцию:
+
+[.programlisting]
+....
+sys/i386/i386/mpboot.s:
+ testl $CPUID_VME,%edx
+ jz 3f
+ orl $CR4_VME,%eax
+3: movl %eax,%cr4
+....
+
+Затем, включение подкачки:
+
+[.programlisting]
+....
+sys/i386/i386/mpboot.s:
+/* Now enable paging */
+ movl IdlePTD_nopae, %eax
+ movl %eax,%cr3 /* load ptd addr into mmu */
+ movl %cr0,%eax /* get control word */
+ orl $CR0_PE|CR0_PG,%eax /* enable paging */
+ movl %eax,%cr0 /* and let's page NOW! */
+....
+
+Следующие три строки кода необходимы, потому что была установлена подкачка, поэтому требуется переход для продолжения выполнения в виртуализированном адресном пространстве:
+
+[.programlisting]
+....
+sys/i386/i386/mpboot.s:
+ pushl $mp_begin /* jump to high mem */
+ ret
+
+/* now running relocated at KERNBASE where the system is linked to run */
+mp_begin: /* now running relocated at KERNBASE */
+....
+
+Функция `init386()` вызывается с указателем на первую свободную физическую страницу, после чего следует вызов `mi_startup()`. `init386` — это архитектурно-зависимая функция инициализации, а `mi_startup()` — архитектурно-независимая (префикс 'mi_' означает Machine Independent, то есть «независимая от машины»). Ядро никогда не возвращается из `mi_startup()`, и, вызывая её, завершает загрузку:
+
+[.programlisting]
+....
+sys/i386/i386/locore.s:
+ pushl physfree /* value of first for init386(first) */
+ call init386 /* wire 386 chip for unix operation */
+ addl $4,%esp
+ movl %eax,%esp /* Switch to true top of stack. */
+ call mi_startup /* autoconfiguration, mountroot etc */
+ /* NOTREACHED */
+....
+
+=== `init386()`
+
+`init386()` определена в [.filename]#sys/i386/i386/machdep.c# и выполняет низкоуровневую инициализацию, специфичную для чипа i386. Переход в защищённый режим был выполнен загрузчиком. Загрузчик создал самую первую задачу, в которой ядро продолжает работать. Прежде чем рассматривать код, рассмотрим задачи, которые процессор должен выполнить для инициализации выполнения в защищённом режиме:
+
+* Инициализировать настраиваемые параметры ядра, переданные из загрузочной программы.
+* Подготовить GDT.
+* Подготовить IDT.
+* Инициализировать системную консоль.
+* Инициализировать DDB, если он скомпилирован в ядро.
+* Инициализировать TSS.
+* Подготовить LDT.
+* Настройка pcb для thread0.
+
+`init386()` инициализирует настраиваемые параметры, переданные из bootstrap, устанавливая указатель окружения (envp) и вызывая `init_param1()`. Указатель envp был передан из loader в структуре `bootinfo`:
+
+[.programlisting]
+....
+sys/i386/i386/machdep.c:
+ /* Init basic tunables, hz etc */
+ init_param1();
+....
+
+`init_param1()` определена в [.filename]#sys/kern/subr_param.c#. Этот файл содержит ряд sysctl, а также две функции, `init_param1()` и `init_param2()`, которые вызываются из `init386()`:
+
+[.programlisting]
+....
+sys/kern/subr_param.c:
+ hz = -1;
+ TUNABLE_INT_FETCH("kern.hz", &hz);
+ if (hz == -1)
+ hz = vm_guest > VM_GUEST_NO ? HZ_VM : HZ;
+....
+
+`TUNABLE_<typename>_FETCH` используется для получения значения из окружения:
+
+[.programlisting]
+....
+/usr/src/sys/sys/kernel.h:
+#define TUNABLE_INT_FETCH(path, var) getenv_int((path), (var))
+....
+
+Sysctl `kern.hz` представляет собой такт системных часов. Кроме того, эти параметры sysctl устанавливаются функцией `init_param1()`: `kern.maxswzone, kern.maxbcache, kern.maxtsiz, kern.dfldsiz, kern.maxdsiz, kern.dflssiz, kern.maxssiz, kern.sgrowsiz`.
+
+Затем `init386()` подготавливает Глобальную Таблицу Дескрипторов
+(GDT). Каждая задача на x86 выполняется в своем собственном виртуальном
+адресном пространстве, и это пространство адресуется парой
+сегмент:смещение. Например, если текущая инструкция, которую должен
+выполнить процессор, находится по адресу CS:EIP, то линейный виртуальный
+адрес этой инструкции будет "виртуальный адрес кодового сегмента CS" +
+EIP. Для удобства сегменты начинаются с виртуального адреса 0 и
+заканчиваются на границе 4 ГБ. Таким образом, линейный виртуальный адрес
+инструкции в данном примере будет просто значением EIP. Сегментные регистры,
+такие как CS, DS и другие, являются селекторами, то есть индексами в GDT
+(если быть более точным, индекс — это не сам селектор, а поле INDEX в
+селекторе). GDT в FreeBSD содержит дескрипторы для 15 селекторов на каждый
+CPU:
+
+[.programlisting]
+....
+sys/i386/i386/machdep.c:
+union descriptor gdt0[NGDT]; /* initial global descriptor table */
+union descriptor *gdt = gdt0; /* global descriptor table */
+
+sys/x86/include/segments.h:
+/*
+ * Entries in the Global Descriptor Table (GDT)
+ */
+#define GNULL_SEL 0 /* Null Descriptor */
+#define GPRIV_SEL 1 /* SMP Per-Processor Private Data */
+#define GUFS_SEL 2 /* User %fs Descriptor (order critical: 1) */
+#define GUGS_SEL 3 /* User %gs Descriptor (order critical: 2) */
+#define GCODE_SEL 4 /* Kernel Code Descriptor (order critical: 1) */
+#define GDATA_SEL 5 /* Kernel Data Descriptor (order critical: 2) */
+#define GUCODE_SEL 6 /* User Code Descriptor (order critical: 3) */
+#define GUDATA_SEL 7 /* User Data Descriptor (order critical: 4) */
+#define GBIOSLOWMEM_SEL 8 /* BIOS low memory access (must be entry 8) */
+#define GPROC0_SEL 9 /* Task state process slot zero and up */
+#define GLDT_SEL 10 /* Default User LDT */
+#define GUSERLDT_SEL 11 /* User LDT */
+#define GPANIC_SEL 12 /* Task state to consider panic from */
+#define GBIOSCODE32_SEL 13 /* BIOS interface (32bit Code) */
+#define GBIOSCODE16_SEL 14 /* BIOS interface (16bit Code) */
+#define GBIOSDATA_SEL 15 /* BIOS interface (Data) */
+#define GBIOSUTIL_SEL 16 /* BIOS interface (Utility) */
+#define GBIOSARGS_SEL 17 /* BIOS interface (Arguments) */
+#define GNDIS_SEL 18 /* For the NDIS layer */
+#define NGDT 19
+....
+
+Обратите внимание, что эти `#defines` не являются самими селекторами, а лишь полем `INDEX` селектора, поэтому они точно соответствуют индексам GDT. Например, реальный селектор для кода ядра (`GCODE_SEL`) имеет значение `0x20`.
+
+Следующий шаг — инициализация таблицы дескрипторов прерываний (IDT). Эта таблица используется процессором при возникновении программного или аппаратного прерывания. Например, чтобы выполнить системный вызов, пользовательское приложение использует инструкцию `INT 0x80`. Это программное прерывание, поэтому аппаратное обеспечение процессора ищет запись с индексом 0x80 в IDT. Эта запись указывает на процедуру обработки данного прерывания, в данном конкретном случае это будет шлюз системных вызовов ядра. IDT может содержать максимум 256 (0x100) записей. Ядро выделяет NIDT записей для IDT, где NIDT — это максимум (256):
+
+[.programlisting]
+....
+sys/i386/i386/machdep.c:
+static struct gate_descriptor idt0[NIDT];
+struct gate_descriptor *idt = &idt0[0]; /* interrupt descriptor table */
+....
+
+Для каждого прерывания устанавливается соответствующий обработчик. Также настраивается шлюз системного вызова для `INT 0x80`:
+
+[.programlisting]
+....
+sys/i386/i386/machdep.c:
+ setidt(IDT_SYSCALL, &IDTVEC(int0x80_syscall),
+ SDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL));
+....
+
+Итак, когда пользовательское приложение выполняет инструкцию `INT 0x80`, управление передаётся функции `_Xint0x80_syscall`, которая находится в сегменте кода ядра и будет выполнена с привилегиями супервизора.
+
+Консоль и DDB инициализируются:
+
+[.programlisting]
+....
+sys/i386/i386/machdep.c:
+ cninit();
+/* skipped */
+ kdb_init();
+#ifdef KDB
+ if (boothowto & RB_KDB)
+ kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger");
+#endif
+....
+
+Сегмент состояния задачи (TSS) — это еще одна структура защищенного режима x86, используемая оборудованием для хранения информации о задаче при переключении задач.
+
+Локальная таблица дескрипторов (LDT) используется для ссылки на код и данные пользовательского пространства. Определено несколько селекторов, указывающих на LDT, включая шлюзы системных вызовов, а также селекторы кода и данных пользователя:
+
+[.programlisting]
+....
+sys/x86/include/segments.h:
+#define LSYS5CALLS_SEL 0 /* forced by intel BCS */
+#define LSYS5SIGR_SEL 1
+#define LUCODE_SEL 3
+#define LUDATA_SEL 5
+#define NLDT (LUDATA_SEL + 1)
+....
+
+Далее инициализируется структура Блока Управления Процессом (`struct pcb`) для proc0. proc0 — это структура `struct proc`, описывающая процесс ядра. Она всегда присутствует во время работы ядра, поэтому связана с thread0:
+
+[.programlisting]
+....
+sys/i386/i386/machdep.c:
+register_t
+init386(int first)
+{
+ /* ... skipped ... */
+
+ proc_linkup0(&proc0, &thread0);
+ /* ... skipped ... */
+}
+....
+
+Структура `struct pcb` является частью структуры proc. Она определена в [.filename]#/usr/include/machine/pcb.h# и содержит информацию процесса, специфичную для архитектуры i386, такую как значения регистров.
+
+=== `mi_startup()`
+
+Эта функция выполняет сортировку пузырьком всех объектов инициализации системы, а затем вызывает вход каждого объекта по очереди:
+
+[.programlisting]
+....
+sys/kern/init_main.c:
+ for (sipp = sysinit; sipp < sysinit_end; sipp++) {
+
+ /* ... skipped ... */
+
+ /* Call function */
+ (*((*sipp)->func))((*sipp)->udata);
+ /* ... skipped ... */
+ }
+....
+
+Хотя фреймворк sysinit описан в link:/books/developers-handbook[Руководстве разработчика], я рассмотрю его внутреннее устройство.
+
+Каждый объект инициализации системы (объект sysinit) создается путем вызова макроса SYSINIT(). Возьмем, к примеру, объект sysinit `announce`. Этот объект выводит сообщение об авторских правах:
+
+[.programlisting]
+....
+sys/kern/init_main.c:
+static void
+print_caddr_t(void *data __unused)
+{
+ printf("%s", (char *)data);
+}
+/* ... skipped ... */
+SYSINIT(announce, SI_SUB_COPYRIGHT, SI_ORDER_FIRST, print_caddr_t, copyright);
+....
+
+Идентификатор подсистемы для этого объекта — SI_SUB_COPYRIGHT (0x0800001). Таким образом, сообщение об авторских правах будет выведено первым, сразу после инициализации консоли.
+
+Давайте рассмотрим, что именно делает макрос `SYSINIT()`. Он раскрывается в макрос `C_SYSINIT()`. Макрос `C_SYSINIT()` затем раскрывается в статическое объявление структуры `struct sysinit` с вызовом другого макроса `DATA_SET`:
+
+[.programlisting]
+....
+/usr/include/sys/kernel.h:
+ #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \
+ static struct sysinit uniquifier ## _sys_init = { \ subsystem, \
+ order, \ func, \ (ident) \ }; \ DATA_WSET(sysinit_set,uniquifier ##
+ _sys_init);
+
+#define SYSINIT(uniquifier, subsystem, order, func, ident) \
+ C_SYSINIT(uniquifier, subsystem, order, \
+ (sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)(ident))
+....
+
+Макрос `DATA_SET()` раскрывается в `_MAKE_SET()`, и именно в этом макросе скрыта вся магия инициализации системы:
+
+[.programlisting]
+....
+/usr/include/linker_set.h:
+#define TEXT_SET(set, sym) _MAKE_SET(set, sym)
+#define DATA_SET(set, sym) _MAKE_SET(set, sym)
+....
+
+После выполнения этих макросов в ядре были созданы различные разделы, включая `set.sysinit_set`. Запустив objdump для бинарного файла ядра, можно заметить наличие таких небольших разделов:
+
+[source, bash]
+....
+% llvm-objdump -h /kernel
+Sections:
+Idx Name Size VMA Type
+ 10 set_sysctl_set 000021d4 01827078 DATA
+ 16 set_kbddriver_set 00000010 0182a4d0 DATA
+ 20 set_scterm_set 0000000c 0182c75c DATA
+ 21 set_cons_set 00000014 0182c768 DATA
+ 33 set_scrndr_set 00000024 0182c828 DATA
+ 41 set_sysinit_set 000014d8 018fabb0 DATA
+....
+
+Это содержимое экрана показывает, что размер раздела set.sysinit_set составляет 0x14d8 байт, поэтому `0x14d8/sizeof(void *)` объектов sysinit скомпилировано в ядро. Другие разделы, такие как `set.sysctl_set`, представляют другие наборы компоновщика.
+
+Определяя переменную типа `struct sysinit`, содержимое раздела `set.sysinit_set` будет "собрано" в эту переменную:
+
+[.programlisting]
+....
+sys/kern/init_main.c:
+ SET_DECLARE(sysinit_set, struct sysinit);
+....
+
+`struct sysinit` определена следующим образом:
+
+[.programlisting]
+....
+sys/sys/kernel.h:
+ struct sysinit {
+ enum sysinit_sub_id subsystem; /* subsystem identifier*/
+ enum sysinit_elem_order order; /* init order within subsystem*/
+ sysinit_cfunc_t func; /* function */
+ const void *udata; /* multiplexer/argument */
+};
+....
+
+Возвращаясь к обсуждению `mi_startup()`, теперь должно быть понятно, как организованы объекты sysinit. Функция `mi_startup()` сортирует их и вызывает каждый. Самый последний объект — это системный планировщик:
+
+[.programlisting]
+....
+/usr/include/sys/kernel.h:
+enum sysinit_sub_id {
+ SI_SUB_DUMMY = 0x0000000, /* not executed; for linker*/
+ SI_SUB_DONE = 0x0000001, /* processed*/
+ SI_SUB_TUNABLES = 0x0700000, /* establish tunable values */
+ SI_SUB_COPYRIGHT = 0x0800001, /* first use of console*/
+...
+ SI_SUB_LAST = 0xfffffff /* final initialization */
+};
+....
+
+Системный планировщик sysinit определен в файле [.filename]#sys/vm/vm_glue.c#, а точка входа для этого объекта — `scheduler()`. Эта функция фактически представляет собой бесконечный цикл и описывает процесс с PID 0, известный как процесс swapper. Структура thread0, упомянутая ранее, используется для его описания.
+
+Первый пользовательский процесс, называемый _init_, создаётся объектом sysinit `init`:
+
+[.programlisting]
+....
+sys/kern/init_main.c:
+static void
+create_init(const void *udata __unused)
+{
+ struct fork_req fr;
+ struct ucred *newcred, *oldcred;
+ struct thread *td;
+ int error;
+
+ bzero(&fr, sizeof(fr));
+ fr.fr_flags = RFFDG | RFPROC | RFSTOPPED;
+ fr.fr_procp = &initproc;
+ error = fork1(&thread0, &fr);
+ if (error)
+ panic("cannot fork init: %d\n", error);
+ KASSERT(initproc->p_pid == 1, ("create_init: initproc->p_pid != 1"));
+ /* divorce init's credentials from the kernel's */
+ newcred = crget();
+ sx_xlock(&proctree_lock);
+ PROC_LOCK(initproc);
+ initproc->p_flag |= P_SYSTEM | P_INMEM;
+ initproc->p_treeflag |= P_TREE_REAPER;
+ oldcred = initproc->p_ucred;
+ crcopy(newcred, oldcred);
+#ifdef MAC
+ mac_cred_create_init(newcred);
+#endif
+#ifdef AUDIT
+ audit_cred_proc1(newcred);
+#endif
+ proc_set_cred(initproc, newcred);
+ td = FIRST_THREAD_IN_PROC(initproc);
+ crcowfree(td);
+ td->td_realucred = crcowget(initproc->p_ucred);
+ td->td_ucred = td->td_realucred;
+ PROC_UNLOCK(initproc);
+ sx_xunlock(&proctree_lock);
+ crfree(oldcred);
+ cpu_fork_kthread_handler(FIRST_THREAD_IN_PROC(initproc), start_init, NULL);
+}
+SYSINIT(init, SI_SUB_CREATE_INIT, SI_ORDER_FIRST, create_init, NULL);
+....
+
+Функция `create_init()` выделяет новый процесс, вызывая `fork1()`, но не помечает его как готовый к выполнению. Когда этот новый процесс будет запланирован для выполнения планировщиком, будет вызвана функция `start_init()`. Эта функция определена в [.filename]#init_main.c#. Она пытается загрузить и выполнить бинарный файл [.filename]#init#, сначала проверяя [.filename]#/sbin/init#, затем [.filename]#/sbin/oinit#, [.filename]#/sbin/init.bak# и, наконец, [.filename]#/rescue/init#:
+
+[.programlisting]
+....
+sys/kern/init_main.c:
+static char init_path[MAXPATHLEN] =
+#ifdef INIT_PATH
+ __XSTRING(INIT_PATH);
+#else
+ "/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init";
+#endif
+....
diff --git a/documentation/content/ru/books/arch-handbook/boot/_index.po b/documentation/content/ru/books/arch-handbook/boot/_index.po
new file mode 100644
index 0000000000..171dbcf2b3
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/boot/_index.po
@@ -0,0 +1,4415 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-07-02 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookboot_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:14
+#, no-wrap
+msgid "Bootstrapping and Kernel Initialization"
+msgstr "Начальная загрузка и инициализация ядра"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1
+#, no-wrap
+msgid "Chapter 1. Bootstrapping and Kernel Initialization"
+msgstr "Глава 1. Начальная загрузка и инициализация ядра"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:52
+#, no-wrap
+msgid "Synopsis"
+msgstr "Обзор"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:57
+msgid ""
+"This chapter is an overview of the boot and system initialization processes, "
+"starting from the BIOS (firmware) POST, to the first user process creation. "
+"Since the initial steps of system startup are very architecture dependent, "
+"the IA-32 architecture is used as an example. But the AMD64 and ARM64 "
+"architectures are much more important and compelling examples and should be "
+"explained in the near future according to the topic of this document."
+msgstr ""
+"Эта глава представляет собой обзор процессов загрузки и инициализации "
+"системы, начиная с POST в BIOS (микропрограмме) и заканчивая созданием "
+"первого пользовательского процесса. Поскольку начальные этапы загрузки "
+"системы сильно зависят от архитектуры, в качестве примера используется "
+"архитектура IA-32. Однако архитектуры AMD64 и ARM64 гораздо важнее и "
+"интереснее, и их следует рассмотреть в ближайшем будущем в соответствии с "
+"темой этого документа."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:61
+msgid ""
+"The FreeBSD boot process can be surprisingly complex. After control is "
+"passed from the BIOS, a considerable amount of low-level configuration must "
+"be done before the kernel can be loaded and executed. This setup must be "
+"done in a simple and flexible manner, allowing the user a great deal of "
+"customization possibilities."
+msgstr ""
+"Процесс загрузки FreeBSD может быть удивительно сложным. После передачи "
+"управления от BIOS необходимо выполнить значительный объем низкоуровневой "
+"настройки перед загрузкой и выполнением ядра. Эта настройка должна быть "
+"выполнена простым и гибким способом, предоставляя пользователю широкие "
+"возможности для настройки и адаптации."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:63
+#, no-wrap
+msgid "Overview"
+msgstr "Обзор"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:76
+msgid ""
+"The boot process is an extremely machine-dependent activity. Not only must "
+"code be written for every computer architecture, but there may also be "
+"multiple types of booting on the same architecture. For example, a "
+"directory listing of [.filename]#stand# reveals a great amount of "
+"architecture-dependent code. There is a directory for each of the various "
+"supported architectures. FreeBSD supports the CSM boot standard "
+"(Compatibility Support Module). So CSM is supported (with both GPT and MBR "
+"partitioning support) and UEFI booting (GPT is totally supported, MBR is "
+"mostly supported). It also supports loading files from ext2fs, MSDOS, UFS "
+"and ZFS. FreeBSD also supports the boot environment feature of ZFS which "
+"allows the HOST OS to communicate details about what to boot that go beyond "
+"a simple partition as was possible in the past. But UEFI is more relevant "
+"than the CSM these days. The example that follows shows booting an x86 "
+"computer from an MBR-partitioned hard drive with the FreeBSD "
+"[.filename]#boot0# multi-boot loader stored in the very first sector. That "
+"boot code starts the FreeBSD three-stage boot process."
+msgstr ""
+"Процесс загрузки — это операция, крайне зависимая от оборудования. Не только "
+"для каждой архитектуры компьютера должен быть написан код, но также могут "
+"существовать различные типы загрузки в рамках одной архитектуры. Например, "
+"список файлов в каталоге [.filename]#stand# показывает большое количество "
+"кода, зависящего от архитектуры. Для каждой из поддерживаемых архитектур "
+"существует отдельный каталог. FreeBSD поддерживает стандарт загрузки CSM "
+"(Compatibility Support Module). Таким образом, CSM поддерживается (как с "
+"GPT, так и с MBR разметкой), а также загрузка через UEFI (GPT полностью "
+"поддерживается, MBR — в основном). Также поддерживается загрузка файлов с "
+"ext2fs, MSDOS, UFS и ZFS. FreeBSD поддерживает функцию загрузочного "
+"окружения ZFS, которая позволяет основной ОС передавать детали о том, что "
+"загружать, выходящие за рамки простого раздела, как это было возможно ранее. "
+"Однако в наши дни UEFI более актуален, чем CSM. В следующем примере показана "
+"загрузка компьютера x86 с жёсткого диска с MBR-разметкой, где используется "
+"мультизагрузчик FreeBSD [.filename]#boot0#, сохранённый в самом первом "
+"секторе. Этот загрузочный код запускает трёхэтапный процесс загрузки FreeBSD."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:82
+msgid ""
+"The key to understanding this process is that it is a series of stages of "
+"increasing complexity. These stages are [.filename]#boot1#, "
+"[.filename]#boot2#, and [.filename]#loader# (see man:boot[8] for more "
+"detail). The boot system executes each stage in sequence. The last stage, "
+"[.filename]#loader#, is responsible for loading the FreeBSD kernel. Each "
+"stage is examined in the following sections."
+msgstr ""
+"Ключ к пониманию этого процесса заключается в том, что он состоит из "
+"последовательных стадий возрастающей сложности. Эти стадии — "
+"[.filename]#boot1#, [.filename]#boot2# и [.filename]#loader# (подробнее см. "
+"man:boot[8]). Система загрузки выполняет каждую стадию последовательно. "
+"Последняя стадия, [.filename]#loader#, отвечает за загрузку ядра FreeBSD. "
+"Каждая стадия рассматривается в следующих разделах."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:85
+msgid ""
+"Here is an example of the output generated by the different boot stages. "
+"Actual output may differ from machine to machine:"
+msgstr ""
+"Вот пример вывода, сгенерированного на различных этапах загрузки. "
+"Фактический вывод может отличаться в зависимости от машины:"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:91
+#, no-wrap
+msgid "*FreeBSD Component*"
+msgstr "*Компонент FreeBSD*"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:93
+#, no-wrap
+msgid "*Output (may vary)*"
+msgstr "*Вывод (может отличаться)*"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:94
+#, no-wrap
+msgid "`boot0`"
+msgstr "`boot0`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:103
+#, no-wrap
+msgid ""
+"[source,bash]\n"
+"....\n"
+"F1 FreeBSD\n"
+"F2 BSD\n"
+"F5 Disk 2\n"
+"...."
+msgstr ""
+"[source,bash]\n"
+"....\n"
+"F1 FreeBSD\n"
+"F2 BSD\n"
+"F5 Disk 2\n"
+"...."
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:104
+#, no-wrap
+msgid "`boot2` footnote:[This prompt will appear if the user presses a key just after selecting an OS to boot at the boot0 stage.]"
+msgstr "`boot2` footnote:[Это приглашение появится, если пользователь нажмет клавишу сразу после выбора ОС для загрузки на этапе boot0.]"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:113
+#, no-wrap
+msgid ""
+"[source,bash]\n"
+"....\n"
+">>FreeBSD/x86 BOOT\n"
+"Default: 0:ad(0p4)/boot/loader\n"
+"boot:\n"
+"...."
+msgstr ""
+"[source,bash]\n"
+"....\n"
+">>FreeBSD/x86 BOOT\n"
+"Default: 0:ad(0p4)/boot/loader\n"
+"boot:\n"
+"...."
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:114
+#, no-wrap
+msgid "[.filename]#loader#"
+msgstr "[.filename]#loader#"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:130
+#, no-wrap
+msgid ""
+"[source,bash]\n"
+"....\n"
+"BTX loader 1.00 BTX version is 1.02\n"
+"Consoles: internal video/keyboard\n"
+"BIOS drive C: is disk0\n"
+"BIOS 639kB/2096064kB available memory\n"
+"\n"
+"FreeBSD/x86 bootstrap loader, Revision 1.1\n"
+"Console internal video/keyboard\n"
+"(root@releng1.nyi.freebsd.org, Fri Apr 9 04:04:45 UTC 2021)\n"
+"Loading /boot/defaults/loader.conf\n"
+"/boot/kernel/kernel text=0xed9008 data=0x117d28+0x176650 syms=[0x8+0x137988+0x8+0x1515f8]\n"
+"...."
+msgstr ""
+"[source,bash]\n"
+"....\n"
+"BTX loader 1.00 BTX version is 1.02\n"
+"Consoles: internal video/keyboard\n"
+"BIOS drive C: is disk0\n"
+"BIOS 639kB/2096064kB available memory\n"
+"\n"
+"FreeBSD/x86 bootstrap loader, Revision 1.1\n"
+"Console internal video/keyboard\n"
+"(root@releng1.nyi.freebsd.org, Fri Apr 9 04:04:45 UTC 2021)\n"
+"Loading /boot/defaults/loader.conf\n"
+"/boot/kernel/kernel text=0xed9008 data=0x117d28+0x176650 syms=[0x8+0x137988+0x8+0x1515f8]\n"
+"...."
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:131
+#, no-wrap
+msgid "kernel"
+msgstr "ядро системы"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:144
+#, no-wrap
+msgid ""
+"[source,bash]\n"
+"....\n"
+"Copyright (c) 1992-2021 The FreeBSD Project.\n"
+"Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994\n"
+" The Regents of the University of California. All rights reserved.\n"
+"FreeBSD is a registered trademark of The FreeBSD Foundation.\n"
+"FreeBSD 13.0-RELEASE 0 releng/13.0-n244733-ea31abc261f: Fri Apr 9 04:04:45 UTC 2021\n"
+" root@releng1.nyi.freebsd.org:/usr/obj/usr/src/i386.i386/sys/GENERIC i386\n"
+"FreeBSD clang version 11.0.1 (git@github.com:llvm/llvm-project.git llvmorg-11.0.1-0-g43ff75f2c3fe)\n"
+"...."
+msgstr ""
+"[source,bash]\n"
+"....\n"
+"Copyright (c) 1992-2021 The FreeBSD Project.\n"
+"Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994\n"
+" The Regents of the University of California. All rights reserved.\n"
+"FreeBSD is a registered trademark of The FreeBSD Foundation.\n"
+"FreeBSD 13.0-RELEASE 0 releng/13.0-n244733-ea31abc261f: Fri Apr 9 04:04:45 UTC 2021\n"
+" root@releng1.nyi.freebsd.org:/usr/obj/usr/src/i386.i386/sys/GENERIC i386\n"
+"FreeBSD clang version 11.0.1 (git@github.com:llvm/llvm-project.git llvmorg-11.0.1-0-g43ff75f2c3fe)\n"
+"...."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:147
+#, no-wrap
+msgid "The BIOS"
+msgstr "BIOS"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:158
+msgid ""
+"When the computer powers on, the processor's registers are set to some "
+"predefined values. One of the registers is the _instruction pointer_ "
+"register, and its value after a power on is well defined: it is a 32-bit "
+"value of `0xfffffff0`. The instruction pointer register (also known as the "
+"Program Counter) points to code to be executed by the processor. Another "
+"important register is the `cr0` 32-bit control register, and its value just "
+"after a reboot is `0`. One of ``cr0``'s bits, the PE (Protection Enabled) "
+"bit, indicates whether the processor is running in 32-bit protected mode or "
+"16-bit real mode. Since this bit is cleared at boot time, the processor "
+"boots in 16-bit real mode. Real mode means, among other things, that linear "
+"and physical addresses are identical. The reason for the processor not to "
+"start immediately in 32-bit protected mode is backwards compatibility. In "
+"particular, the boot process relies on the services provided by the BIOS, "
+"and the BIOS itself works in legacy, 16-bit code."
+msgstr ""
+"При включении компьютера регистры процессора устанавливаются в некоторые "
+"предопределённые значения. Один из регистров — это регистр _указателя "
+"команд_, и его значение после включения питания чётко определено: это 32-"
+"битное значение `0xfffffff0`. Регистр указателя команд (также известный как "
+"Счётчик Команд) указывает на код, который должен быть выполнен процессором. "
+"Ещё один важный регистр — это 32-битный управляющий регистр `cr0`, и его "
+"значение сразу после перезагрузки равно `0`. Один из битов ``cr0``, бит PE "
+"(Protection Enabled, Защита Включена), указывает, работает ли процессор в 32-"
+"битном защищённом режиме или 16-битном реальном режиме. Поскольку этот бит "
+"сброшен при загрузке, процессор запускается в 16-битном реальном режиме. "
+"Реальный режим означает, среди прочего, что линейные и физические адреса "
+"идентичны. Причина, по которой процессор не запускается сразу в 32-битном "
+"защищённом режиме, — это обратная совместимость. В частности, процесс "
+"загрузки зависит от услуг, предоставляемых BIOS, а сам BIOS работает в "
+"устаревшем 16-битном коде."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:161
+msgid ""
+"The value of `0xfffffff0` is slightly less than 4 GB, so unless the machine "
+"has 4 GB of physical memory, it cannot point to a valid memory address. The "
+"computer's hardware translates this address so that it points to a BIOS "
+"memory block."
+msgstr ""
+"Значение `0xfffffff0` немного меньше 4 ГБ, поэтому, если в машине нет 4 ГБ "
+"физической памяти, оно не может указывать на действительный адрес памяти. "
+"Аппаратное обеспечение компьютера преобразует этот адрес так, чтобы он "
+"указывал на блок памяти BIOS."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:166
+msgid ""
+"The BIOS (Basic Input Output System) is a chip on the motherboard that has a "
+"relatively small amount of read-only memory (ROM). This memory contains "
+"various low-level routines that are specific to the hardware supplied with "
+"the motherboard. The processor will first jump to the address 0xfffffff0, "
+"which really resides in the BIOS's memory. Usually this address contains a "
+"jump instruction to the BIOS's POST routines."
+msgstr ""
+"BIOS (Basic Input Output System) — это микросхема на материнской плате, "
+"которая содержит относительно небольшой объем памяти только для чтения "
+"(ROM). Эта память включает различные низкоуровневые процедуры, специфичные "
+"для оборудования, поставляемого с материнской платой. Процессор сначала "
+"переходит по адресу 0xfffffff0, который фактически находится в памяти BIOS. "
+"Обычно по этому адресу содержится инструкция перехода к процедурам POST BIOS."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:170
+msgid ""
+"The POST (Power On Self Test) is a set of routines including the memory "
+"check, system bus check, and other low-level initialization so the CPU can "
+"set up the computer properly. The important step of this stage is "
+"determining the boot device. Modern BIOS implementations permit the "
+"selection of a boot device, allowing booting from a floppy, CD-ROM, hard "
+"disk, or other devices."
+msgstr ""
+"POST (Power On Self Test) — это набор процедур, включающих проверку памяти, "
+"проверку системной шины и другую низкоуровневую инициализацию, чтобы "
+"процессор мог правильно настроить компьютер. Важным этапом на этой стадии "
+"является определение загрузочного устройства. Современные реализации BIOS "
+"позволяют выбирать загрузочное устройство, обеспечивая загрузку с дискеты, "
+"CD-ROM, жесткого диска или других устройств."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:179
+msgid ""
+"The very last thing in the POST is the `INT 0x19` instruction. The `INT "
+"0x19` handler reads 512 bytes from the first sector of boot device into the "
+"memory at address `0x7c00`. The term _first sector_ originates from hard "
+"drive architecture, where the magnetic plate is divided into a number of "
+"cylindrical tracks. Tracks are numbered, and every track is divided into a "
+"number (usually 64) of sectors. Track numbers start at 0, but sector "
+"numbers start from 1. Track 0 is the outermost on the magnetic plate, and "
+"sector 1, the first sector, has a special purpose. It is also called the "
+"MBR, or Master Boot Record. The remaining sectors on the first track are "
+"never used."
+msgstr ""
+"Самым последним действием в POST является инструкция `INT 0x19`. Обработчик "
+"`INT 0x19` считывает 512 байт из первого сектора загрузочного устройства в "
+"память по адресу `0x7c00`. Термин _первый сектор_ происходит из архитектуры "
+"жёстких дисков, где магнитная пластина разделена на множество цилиндрических "
+"дорожек. Дорожки нумеруются, и каждая дорожка разделена на несколько (обычно "
+"64) секторов. Нумерация дорожек начинается с 0, но нумерация секторов "
+"начинается с 1. Дорожка 0 находится на внешней стороне магнитной пластины, а "
+"сектор 1, первый сектор, имеет особое назначение. Он также называется MBR "
+"(Master Boot Record) или Главная Загрузочная Запись. Остальные секторы на "
+"первой дорожке не используются."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:183
+msgid ""
+"This sector is our boot-sequence starting point. As we will see, this "
+"sector contains a copy of our [.filename]#boot0# program. A jump is made by "
+"the BIOS to address `0x7c00` so it starts executing."
+msgstr ""
+"Этот сектор является нашей точкой входа в последовательность загрузки. Как "
+"мы увидим, этот сектор содержит копию нашей программы [.filename]#boot0#. "
+"BIOS выполняет переход по адресу `0x7c00`, и она начинает выполняться."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:185
+#, no-wrap
+msgid "The Master Boot Record (`boot0`)"
+msgstr "Главная загрузочная запись (`boot0`)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:194
+msgid ""
+"After control is received from the BIOS at memory address `0x7c00`, "
+"[.filename]#boot0# starts executing. It is the first piece of code under "
+"FreeBSD control. The task of [.filename]#boot0# is quite simple: scan the "
+"partition table and let the user choose which partition to boot from. The "
+"Partition Table is a special, standard data structure embedded in the MBR "
+"(hence embedded in [.filename]#boot0#) describing the four standard PC "
+"\"partitions\". [.filename]#boot0# resides in the filesystem as "
+"[.filename]#/boot/boot0#. It is a small 512-byte file, and it is exactly "
+"what FreeBSD's installation procedure wrote to the hard disk's MBR if you "
+"chose the \"bootmanager\" option at installation time. Indeed, "
+"[.filename]#boot0# _is_ the MBR."
+msgstr ""
+"После получения управления от BIOS по адресу памяти `0x7c00` начинает "
+"выполняться [.filename]#boot0#. Это первый код, который управляется FreeBSD. "
+"Задача [.filename]#boot0# довольно проста: просканировать таблицу разделов и "
+"позволить пользователю выбрать, с какого раздела загружаться. Таблица "
+"разделов — это специальная стандартная структура данных, встроенная в MBR (а "
+"значит, и в [.filename]#boot0#), которая описывает четыре стандартных PC-"
+"раздела. [.filename]#boot0# находится в файловой системе как [.filename]#/"
+"boot/boot0#. Это небольшой файл размером 512 байт, и именно его процедура "
+"установки FreeBSD записывает в MBR жёсткого диска, если во время установки "
+"была выбрана опция \"bootmanager\". Действительно, [.filename]#boot0# _и "
+"есть_ MBR."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:197
+msgid ""
+"As mentioned previously, we're calling the BIOS `INT 0x19` to load the MBR "
+"([.filename]#boot0#) into memory at address `0x7c00`. The source file for "
+"[.filename]#boot0# can be found in [.filename]#stand/i386/boot0/boot0.S# - "
+"which is an awesome piece of code written by Robert Nordier."
+msgstr ""
+"Как упоминалось ранее, мы вызываем прерывание BIOS `INT 0x19` для загрузки "
+"MBR ([.filename]#boot0#) в память по адресу `0x7c00`. Исходный файл для "
+"[.filename]#boot0# можно найти в [.filename]#stand/i386/boot0/boot0.S# — это "
+"впечатляющий фрагмент кода, написанный Робертом Нордье."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:202
+msgid ""
+"A special structure starting from offset `0x1be` in the MBR is called the "
+"_partition table_. It has four records of 16 bytes each, called _partition "
+"records_, which represent how the hard disk is partitioned, or, in FreeBSD's "
+"terminology, sliced. One byte of those 16 says whether a partition (slice) "
+"is bootable or not. Exactly one record must have that flag set, otherwise "
+"[.filename]#boot0#'s code will refuse to proceed."
+msgstr ""
+"Особая структура, начинающаяся со смещения `0x1be` в MBR, называется "
+"_таблицей разделов_. Она содержит четыре записи по 16 байт каждая, "
+"называемые _записями разделов_, которые определяют, как разделён жёсткий "
+"диск, или, в терминологии FreeBSD, нарезан. Один из этих 16 байт указывает, "
+"является ли раздел (срез) загрузочным или нет. Ровно одна запись должна быть "
+"с этом установленным флагом, иначе код [.filename]#boot0# откажется "
+"продолжать работу."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:204
+msgid "A partition record has the following fields:"
+msgstr "Запись о разделе содержит следующие поля:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:206
+msgid "the 1-byte filesystem type"
+msgstr "1-байтовый тип файловой системы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:207
+msgid "the 1-byte bootable flag"
+msgstr "1-байтовый флаг загрузки (`bootable`)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:208
+msgid "the 6 byte descriptor in CHS format"
+msgstr "6-байтовый дескриптор в формате CHS"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:209
+msgid "the 8 byte descriptor in LBA format"
+msgstr "8-байтовый дескриптор в формате LBA"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:213
+msgid ""
+"A partition record descriptor contains information about where exactly the "
+"partition resides on the drive. Both descriptors, LBA and CHS, describe the "
+"same information, but in different ways: LBA (Logical Block Addressing) has "
+"the starting sector for the partition and the partition's length, while CHS "
+"(Cylinder Head Sector) has coordinates for the first and last sectors of the "
+"partition. The partition table ends with the special signature `0xaa55`."
+msgstr ""
+"Дескриптор записи раздела содержит информацию о том, где именно раздел "
+"расположен на диске. Оба дескриптора, LBA и CHS, описывают одну и ту же "
+"информацию, но разными способами: LBA (Logical Block Addressing) содержит "
+"начальный сектор раздела и его длину, тогда как CHS (Cylinder Head Sector) "
+"содержит координаты первого и последнего секторов раздела. Таблица разделов "
+"завершается специальной сигнатурой `0xaa55`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:218
+msgid ""
+"The MBR must fit into 512 bytes, a single disk sector. This program uses "
+"low-level \"tricks\" like taking advantage of the side effects of certain "
+"instructions and reusing register values from previous operations to make "
+"the most out of the fewest possible instructions. Care must also be taken "
+"when handling the partition table, which is embedded in the MBR itself. For "
+"these reasons, be very careful when modifying [.filename]#boot0.S#."
+msgstr ""
+"MBR должен помещаться в 512 байт, один сектор диска. Эта программа "
+"использует низкоуровневые «трюки», такие как использование побочных эффектов "
+"определённых инструкций и повторное использование значений регистров из "
+"предыдущих операций, чтобы максимально эффективно использовать минимально "
+"возможное количество инструкций. Также необходимо соблюдать осторожность при "
+"работе с таблицей разделов, которая встроена в сам MBR. По этим причинам "
+"будьте очень внимательны при изменении [.filename]#boot0.S#."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:223
+msgid ""
+"Note that the [.filename]#boot0.S# source file is assembled \"as is\": "
+"instructions are translated one by one to binary, with no additional "
+"information (no ELF file format, for example). This kind of low-level "
+"control is achieved at link time through special control flags passed to the "
+"linker. For example, the text section of the program is set to be located "
+"at address `0x600`. In practice this means that [.filename]#boot0# must be "
+"loaded to memory address `0x600` in order to function properly."
+msgstr ""
+"Обратите внимание, что исходный файл [.filename]#boot0.S# ассемблируется "
+"\"как есть\": инструкции переводятся одна за одной в бинарный код без "
+"дополнительной информации (например, без формата файла ELF). Такой "
+"низкоуровневый контроль достигается на этапе компоновки с помощью "
+"специальных флагов, передаваемых компоновщику. Например, текстовая секция "
+"программы располагается по адресу `0x600`. На практике это означает, что "
+"[.filename]#boot0# должен быть загружен в память по адресу `0x600` для "
+"корректной работы."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:230
+msgid ""
+"It is worth looking at the [.filename]#Makefile# for [.filename]#boot0# "
+"([.filename]#stand/i386/boot0/Makefile#), as it defines some of the run-time "
+"behavior of [.filename]#boot0#. For instance, if a terminal connected to "
+"the serial port (COM1) is used for I/O, the macro `SIO` must be defined (`-"
+"DSIO`). `-DPXE` enables boot through PXE by pressing kbd:[F6]. "
+"Additionally, the program defines a set of _flags_ that allow further "
+"modification of its behavior. All of this is illustrated in the "
+"[.filename]#Makefile#. For example, look at the linker directives which "
+"command the linker to start the text section at address `0x600`, and to "
+"build the output file \"as is\" (strip out any file formatting):"
+msgstr ""
+"Стоит взглянуть на [.filename]#Makefile# для [.filename]#boot0# "
+"([.filename]#stand/i386/boot0/Makefile#), так как он определяет некоторые "
+"аспекты поведения [.filename]#boot0# во время выполнения. Например, если для "
+"ввода-вывода используется терминал, подключённый к последовательному порту "
+"(COM1), необходимо определить макрос `SIO` (`-DSIO`). `-DPXE` включает "
+"загрузку через PXE при нажатии kbd:[F6]. Кроме того, программа определяет "
+"набор _флагов_, которые позволяют дополнительно настроить её поведение. Всё "
+"это проиллюстрировано в [.filename]#Makefile#. Например, обратите внимание "
+"на директивы компоновщика, которые предписывают ему начинать секцию текста с "
+"адреса `0x600` и создавать выходной файл \"как есть\" (удаляя любое "
+"форматирование файла):"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:235
+#, no-wrap
+msgid ""
+" BOOT_BOOT0_ORG?=0x600\n"
+" ORG=${BOOT_BOOT0_ORG}\n"
+msgstr ""
+" BOOT_BOOT0_ORG?=0x600\n"
+" ORG=${BOOT_BOOT0_ORG}\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:237
+#, no-wrap
+msgid "[.filename]#stand/i386/boot0/Makefile# [[boot-boot0-makefile-as-is]]"
+msgstr "[.filename]#stand/i386/boot0/Makefile# [[boot-boot0-makefile-as-is]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:239
+msgid ""
+"Let us now start our study of the MBR, or [.filename]#boot0#, starting where "
+"execution begins."
+msgstr ""
+"Приступим к изучению MBR, или [.filename]#boot0#, начиная с точки входа."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:245
+msgid ""
+"Some modifications have been made to some instructions in favor of better "
+"exposition. For example, some macros are expanded, and some macro tests are "
+"omitted when the result of the test is known. This applies to all of the "
+"code examples shown."
+msgstr ""
+"В некоторые инструкции были внесены изменения для лучшего изложения. "
+"Например, некоторые макросы раскрыты, а некоторые проверки макросов опущены, "
+"когда результат проверки известен. Это относится ко всем приведённым "
+"примерам кода."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:256
+#, no-wrap
+msgid ""
+"start:\n"
+" cld\t\t\t# String ops inc\n"
+" xorw %ax,%ax\t\t# Zero\n"
+" movw %ax,%es\t\t# Address\n"
+" movw %ax,%ds\t\t# data\n"
+" movw %ax,%ss\t\t# Set up\n"
+" movw $LOAD,%sp\t\t# stack\n"
+msgstr ""
+"start:\n"
+" cld\t\t\t# String ops inc\n"
+" xorw %ax,%ax\t\t# Zero\n"
+" movw %ax,%es\t\t# Address\n"
+" movw %ax,%ds\t\t# data\n"
+" movw %ax,%ss\t\t# Set up\n"
+" movw $LOAD,%sp\t\t# stack\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:258
+#, no-wrap
+msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-entrypoint]]"
+msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-entrypoint]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:264
+msgid ""
+"This first block of code is the entry point of the program. It is where the "
+"BIOS transfers control. First, it makes sure that the string operations "
+"autoincrement its pointer operands (the `cld` instruction) footnote:[When in "
+"doubt, we refer the reader to the official Intel manuals, which describe the "
+"exact semantics for each instruction: .]. Then, as it makes no assumption "
+"about the state of the segment registers, it initializes them. Finally, it "
+"sets the stack pointer register (`%sp`) to ($LOAD = address `0x7c00`), so we "
+"have a working stack."
+msgstr ""
+"Этот первый блок кода является точкой входа программы. Именно сюда BIOS "
+"передаёт управление. Сначала он гарантирует, что строковые операции "
+"автоматически увеличивают указатели операндов (инструкция `cld`) footnote:[В "
+"случае сомнений мы отсылаем читателя к официальным руководствам Intel, где "
+"описана точная семантика каждой инструкции: .]. Затем, не делая "
+"предположений о состоянии сегментных регистров, он их инициализирует. "
+"Наконец, он устанавливает регистр указателя стека (`%sp`) в ($LOAD = адрес "
+"`0x7c00`), чтобы обеспечить работоспособный стек."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:266
+msgid ""
+"The next block is responsible for the relocation and subsequent jump to the "
+"relocated code."
+msgstr ""
+"Следующий блок отвечает за перемещение и последующий переход к перемещенному "
+"коду."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:280
+#, no-wrap
+msgid ""
+" movw %sp,%si # Source\n"
+" movw $start,%di\t\t# Destination\n"
+" movw $0x100,%cx\t\t# Word count\n"
+" rep\t\t\t# Relocate\n"
+" movsw\t\t\t# code\n"
+" movw %di,%bp\t\t# Address variables\n"
+" movb $0x8,%cl\t\t# Words to clear\n"
+" rep\t\t\t# Zero\n"
+" stosw\t\t\t# them\n"
+" incb -0xe(%di)\t\t# Set the S field to 1\n"
+" jmp main-LOAD+ORIGIN\t# Jump to relocated code\n"
+msgstr ""
+" movw %sp,%si # Source\n"
+" movw $start,%di\t\t# Destination\n"
+" movw $0x100,%cx\t\t# Word count\n"
+" rep\t\t\t# Relocate\n"
+" movsw\t\t\t# code\n"
+" movw %di,%bp\t\t# Address variables\n"
+" movb $0x8,%cl\t\t# Words to clear\n"
+" rep\t\t\t# Zero\n"
+" stosw\t\t\t# them\n"
+" incb -0xe(%di)\t\t# Set the S field to 1\n"
+" jmp main-LOAD+ORIGIN\t# Jump to relocated code\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:282
+#, no-wrap
+msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-relocation]]"
+msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-relocation]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:293
+msgid ""
+"As [.filename]#boot0# is loaded by the BIOS to address `0x7C00`, it copies "
+"itself to address `0x600` and then transfers control there (recall that it "
+"was linked to execute at address `0x600`). The source address, `0x7c00`, is "
+"copied to register `%si`. The destination address, `0x600`, to register "
+"`%di`. The number of words to copy, `256` (the program's size = 512 bytes), "
+"is copied to register `%cx`. Next, the `rep` instruction repeats the "
+"instruction that follows, that is, `movsw`, the number of times dictated by "
+"the `%cx` register. The `movsw` instruction copies the word pointed to by "
+"`%si` to the address pointed to by `%di`. This is repeated another 255 "
+"times. On each repetition, both the source and destination registers, `%si` "
+"and `%di`, are incremented by one. Thus, upon completion of the 256-word "
+"(512-byte) copy, `%di` has the value `0x600`+`512`= `0x800`, and `%si` has "
+"the value `0x7c00`+`512`= `0x7e00`; we have thus completed the code "
+"_relocation_. Since the last update of this document, the copy instructions "
+"have changed in the code, so instead of the movsb and stosb, movsw and stosw "
+"have been introduced, which copy 2 bytes(1 word) in one iteration."
+msgstr ""
+"Так как [.filename]#boot0# загружается BIOS по адресу `0x7C00`, он копирует "
+"себя по адресу `0x600` и передаёт управление туда (напомним, что он был "
+"слинкован для выполнения по адресу `0x600`). Исходный адрес, `0x7c00`, "
+"копируется в регистр `%si`. Конечный адрес, `0x600`, — в регистр `%di`. "
+"Количество слов для копирования, `256` (размер программы = 512 байт), "
+"копируется в регистр `%cx`. Далее инструкция `rep` повторяет следующую за "
+"ней инструкцию, то есть `movsw`, количество раз, указанное в регистре `%cx`. "
+"Инструкция `movsw` копирует слово, на которое указывает `%si`, по адресу, на "
+"который указывает `%di`. Это повторяется ещё 255 раз. При каждом повторении "
+"оба регистра, исходный и конечный, `%si` и `%di`, увеличиваются на единицу. "
+"Таким образом, по завершении копирования 256 слов (512 байт), `%di` имеет "
+"значение `0x600`+`512`= `0x800`, а `%si` — значение `0x7c00`+`512`= "
+"`0x7e00`; таким образом, мы завершили _перемещение_ кода. С момента "
+"последнего обновления этого документа инструкции копирования в коде "
+"изменились, поэтому вместо movsb и stosb были введены movsw и stosw, которые "
+"копируют 2 байта (1 слово) за одну итерацию."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:304
+msgid ""
+"Next, the destination register `%di` is copied to `%bp`. `%bp` gets the "
+"value `0x800`. The value `8` is copied to `%cl` in preparation for a new "
+"string operation (like our previous `movsw`). Now, `stosw` is executed 8 "
+"times. This instruction copies a `0` value to the address pointed to by the "
+"destination register (`%di`, which is `0x800`), and increments it. This is "
+"repeated another 7 times, so `%di` ends up with value `0x810`. Effectively, "
+"this clears the address range `0x800`-`0x80f`. This range is used as a "
+"(fake) partition table for writing the MBR back to disk. Finally, the "
+"sector field for the CHS addressing of this fake partition is given the "
+"value 1 and a jump is made to the main function from the relocated code. "
+"Note that until this jump to the relocated code, any reference to an "
+"absolute address was avoided."
+msgstr ""
+"Затем регистр назначения `%di` копируется в `%bp`. `%bp` получает значение "
+"`0x800`. Значение `8` копируется в `%cl` для подготовки новой строковой "
+"операции (как в предыдущей `movsw`). Теперь `stosw` выполняется 8 раз. Эта "
+"инструкция копирует значение `0` по адресу, на который указывает регистр "
+"назначения (`%di`, то есть `0x800`), и увеличивает его. Это повторяется ещё "
+"7 раз, так что `%di` в итоге получает значение `0x810`. Фактически это "
+"очищает диапазон адресов `0x800`-`0x80f`. Этот диапазон используется как "
+"(фиктивная) таблица разделов для записи MBR обратно на диск. Наконец, полю "
+"сектора для CHS-адресации этого фиктивного раздела присваивается значение 1, "
+"и выполняется переход к основной функции из перемещённого кода. Обратите "
+"внимание, что до этого перехода к перемещённому коду любые ссылки на "
+"абсолютные адреса избегались."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:306
+msgid ""
+"The following code block tests whether the drive number provided by the BIOS "
+"should be used, or the one stored in [.filename]#boot0#."
+msgstr ""
+"Следующий блок кода проверяет, следует ли использовать номер диска, "
+"предоставленный BIOS, или тот, что хранится в [.filename]#boot0#."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:318
+#, no-wrap
+msgid ""
+"main:\n"
+" testb $SETDRV,_FLAGS(%bp)\t# Set drive number?\n"
+"#ifndef CHECK_DRIVE\t/* disable drive checks */\n"
+" jz save_curdrive\t\t# no, use the default\n"
+"#else\n"
+" jnz disable_update\t# Yes\n"
+" testb %dl,%dl\t\t# Drive number valid?\n"
+" js save_curdrive\t\t# Possibly (0x80 set)\n"
+"#endif\n"
+msgstr ""
+"main:\n"
+" testb $SETDRV,_FLAGS(%bp)\t# Set drive number?\n"
+"#ifndef CHECK_DRIVE\t/* disable drive checks */\n"
+" jz save_curdrive\t\t# no, use the default\n"
+"#else\n"
+" jnz disable_update\t# Yes\n"
+" testb %dl,%dl\t\t# Drive number valid?\n"
+" js save_curdrive\t\t# Possibly (0x80 set)\n"
+"#endif\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:320
+#, no-wrap
+msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-drivenumber]]"
+msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-drivenumber]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:327
+msgid ""
+"This code tests the `SETDRV` bit (`0x20`) in the _flags_ variable. Recall "
+"that register `%bp` points to address location `0x800`, so the test is done "
+"to the _flags_ variable at address `0x800`-`69`= `0x7bb`. This is an "
+"example of the type of modifications that can be done to "
+"[.filename]#boot0#. The `SETDRV` flag is not set by default, but it can be "
+"set in the [.filename]#Makefile#. When set, the drive number stored in the "
+"MBR is used instead of the one provided by the BIOS. We assume the "
+"defaults, and that the BIOS provided a valid drive number, so we jump to "
+"`save_curdrive`."
+msgstr ""
+"Этот код проверяет бит `SETDRV` (`0x20`) в переменной _flags_. Напомним, что "
+"регистр `%bp` указывает на адрес `0x800`, поэтому проверка выполняется для "
+"переменной _flags_ по адресу `0x800`-`69`= `0x7bb`. Это пример типа "
+"изменений, которые можно внести в [.filename]#boot0#. Флаг `SETDRV` не "
+"установлен по умолчанию, но его можно задать в [.filename]#Makefile#. Если "
+"он установлен, используется номер диска, сохранённый в MBR, вместо "
+"предоставленного BIOS. Мы предполагаем значения по умолчанию и то, что BIOS "
+"предоставил корректный номер диска, поэтому переходим к `save_curdrive`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:329
+msgid ""
+"The next block saves the drive number provided by the BIOS, and calls `putn` "
+"to print a new line on the screen."
+msgstr ""
+"Следующий блок сохраняет номер диска, предоставленный BIOS, и вызывает "
+"`putn` для вывода новой строки на экран."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:341
+#, no-wrap
+msgid ""
+"save_curdrive:\n"
+" movb %dl, (%bp)\t\t# Save drive number\n"
+" pushw %dx\t\t\t# Also in the stack\n"
+"#ifdef\tTEST\t/* test code, print internal bios drive */\n"
+" rolb $1, %dl\n"
+" movw $drive, %si\n"
+" call putkey\n"
+"#endif\n"
+" callw putn\t\t# Print a newline\n"
+msgstr ""
+"save_curdrive:\n"
+" movb %dl, (%bp)\t\t# Save drive number\n"
+" pushw %dx\t\t\t# Also in the stack\n"
+"#ifdef\tTEST\t/* test code, print internal bios drive */\n"
+" rolb $1, %dl\n"
+" movw $drive, %si\n"
+" call putkey\n"
+"#endif\n"
+" callw putn\t\t# Print a newline\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:343
+#, no-wrap
+msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-savedrivenumber]]"
+msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-savedrivenumber]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:345
+msgid ""
+"Note that we assume `TEST` is not defined, so the conditional code in it is "
+"not assembled and will not appear in our executable [.filename]#boot0#."
+msgstr ""
+"Обратите внимание, что мы предполагаем, что `TEST` не определён, поэтому "
+"условный код в нём не собирается и не появится в нашем исполняемом файле "
+"[.filename]#boot0#."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:351
+msgid ""
+"Our next block implements the actual scanning of the partition table. It "
+"prints to the screen the partition type for each of the four entries in the "
+"partition table. It compares each type with a list of well-known operating "
+"system file systems. Examples of recognized partition types are NTFS "
+"(Windows(R), ID 0x7), `ext2fs` (Linux(R), ID 0x83), and, of course, `ffs`/"
+"`ufs2` (FreeBSD, ID 0xa5). The implementation is fairly simple."
+msgstr ""
+"Следующий блок реализует фактическое сканирование таблицы разделов. Он "
+"выводит на экран тип раздела для каждой из четырёх записей в таблице "
+"разделов. Каждый тип сравнивается со списком известных файловых систем "
+"операционных систем. Примерами распознаваемых типов разделов являются NTFS "
+"(Windows(R), ID 0x7), `ext2fs` (Linux(R), ID 0x83) и, конечно же, `ffs`/"
+"`ufs2` (FreeBSD, ID 0xa5). Реализация довольно проста."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:356
+#, no-wrap
+msgid ""
+" movw $(partbl+0x4),%bx\t# Partition table (+4)\n"
+" xorw %dx,%dx\t\t# Item number\n"
+msgstr ""
+" movw $(partbl+0x4),%bx\t# Partition table (+4)\n"
+" xorw %dx,%dx\t\t# Item number\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:372
+#, no-wrap
+msgid ""
+"read_entry:\n"
+" movb %ch,-0x4(%bx)\t# Zero active flag (ch == 0)\n"
+" btw %dx,_FLAGS(%bp)\t# Entry enabled?\n"
+" jnc next_entry\t\t# No\n"
+" movb (%bx),%al\t\t# Load type\n"
+" test %al, %al\t\t# skip empty partition\n"
+" jz next_entry\n"
+" movw $bootable_ids,%di\t# Lookup tables\n"
+" movb $(TLEN+1),%cl\t# Number of entries\n"
+" repne\t\t\t# Locate\n"
+" scasb\t\t\t# type\n"
+" addw $(TLEN-1), %di\t# Adjust\n"
+" movb (%di),%cl\t\t# Partition\n"
+" addw %cx,%di\t\t# description\n"
+" callw putx\t\t# Display it\n"
+msgstr ""
+"read_entry:\n"
+" movb %ch,-0x4(%bx)\t# Zero active flag (ch == 0)\n"
+" btw %dx,_FLAGS(%bp)\t# Entry enabled?\n"
+" jnc next_entry\t\t# No\n"
+" movb (%bx),%al\t\t# Load type\n"
+" test %al, %al\t\t# skip empty partition\n"
+" jz next_entry\n"
+" movw $bootable_ids,%di\t# Lookup tables\n"
+" movb $(TLEN+1),%cl\t# Number of entries\n"
+" repne\t\t\t# Locate\n"
+" scasb\t\t\t# type\n"
+" addw $(TLEN-1), %di\t# Adjust\n"
+" movb (%di),%cl\t\t# Partition\n"
+" addw %cx,%di\t\t# description\n"
+" callw putx\t\t# Display it\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:377
+#, no-wrap
+msgid ""
+"next_entry:\n"
+" incw %dx\t\t\t# Next item\n"
+" addb $0x10,%bl\t\t# Next entry\n"
+" jnc read_entry\t\t# Till done\n"
+msgstr ""
+"next_entry:\n"
+" incw %dx\t\t\t# Next item\n"
+" addb $0x10,%bl\t\t# Next entry\n"
+" jnc read_entry\t\t# Till done\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:379
+#, no-wrap
+msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-partition-scan]]"
+msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-partition-scan]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:383
+msgid ""
+"It is important to note that the active flag for each entry is cleared, so "
+"after the scanning, _no_ partition entry is active in our memory copy of "
+"[.filename]#boot0#. Later, the active flag will be set for the selected "
+"partition. This ensures that only one active partition exists if the user "
+"chooses to write the changes back to disk."
+msgstr ""
+"Важно отметить, что флаг активности для каждой записи сбрасывается, поэтому "
+"после сканирования _ни одна_ запись о разделе не активна в нашей копии "
+"[.filename]#boot0# в памяти. Позже флаг активности будет установлен для "
+"выбранного раздела. Это гарантирует, что только один активный раздел "
+"существует, если пользователь решит записать изменения обратно на диск."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:388
+msgid ""
+"The next block tests for other drives. At startup, the BIOS writes the "
+"number of drives present in the computer to address `0x475`. If there are "
+"any other drives present, [.filename]#boot0# prints the current drive to "
+"screen. The user may command [.filename]#boot0# to scan partitions on "
+"another drive later."
+msgstr ""
+"Следующий блок проверяет наличие других дисков. При запуске BIOS записывает "
+"количество дисков, присутствующих в компьютере, по адресу `0x475`. Если есть "
+"другие диски, [.filename]#boot0# выводит текущий диск на экран. Пользователь "
+"может позже дать команду [.filename]#boot0# просканировать разделы на другом "
+"диске."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:397
+#, no-wrap
+msgid ""
+" popw %ax\t\t\t# Drive number\n"
+" subb $0x80-0x1,%al\t\t# Does next\n"
+" cmpb NHRDRV,%al\t\t# drive exist? (from BIOS?)\n"
+" jb print_drive\t\t# Yes\n"
+" decw %ax\t\t\t# Already drive 0?\n"
+" jz print_prompt\t\t# Yes\n"
+msgstr ""
+" popw %ax\t\t\t# Drive number\n"
+" subb $0x80-0x1,%al\t\t# Does next\n"
+" cmpb NHRDRV,%al\t\t# drive exist? (from BIOS?)\n"
+" jb print_drive\t\t# Yes\n"
+" decw %ax\t\t\t# Already drive 0?\n"
+" jz print_prompt\t\t# Yes\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:399
+#, no-wrap
+msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-test-drives]]"
+msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-test-drives]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:402
+msgid ""
+"We make the assumption that a single drive is present, so the jump to "
+"`print_drive` is not performed. We also assume nothing strange happened, so "
+"we jump to `print_prompt`."
+msgstr ""
+"Мы предполагаем, что присутствует только один диск, поэтому переход к "
+"`print_drive` не выполняется. Также мы предполагаем, что ничего необычного "
+"не произошло, поэтому переходим к `print_prompt`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:404
+msgid ""
+"This next block just prints out a prompt followed by the default option:"
+msgstr ""
+"Следующий блок просто выводит приглашение с последующим вариантом по "
+"умолчанию:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:414
+#, no-wrap
+msgid ""
+"print_prompt:\n"
+" movw $prompt,%si\t\t# Display\n"
+" callw putstr\t\t# prompt\n"
+" movb _OPT(%bp),%dl\t# Display\n"
+" decw %si\t\t\t# default\n"
+" callw putkey\t\t# key\n"
+" jmp start_input\t\t# Skip beep\n"
+msgstr ""
+"print_prompt:\n"
+" movw $prompt,%si\t\t# Display\n"
+" callw putstr\t\t# prompt\n"
+" movb _OPT(%bp),%dl\t# Display\n"
+" decw %si\t\t\t# default\n"
+" callw putkey\t\t# key\n"
+" jmp start_input\t\t# Skip beep\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:416
+#, no-wrap
+msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-prompt]]"
+msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-prompt]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:418
+msgid ""
+"Finally, a jump is performed to `start_input`, where the BIOS services are "
+"used to start a timer and for reading user input from the keyboard; if the "
+"timer expires, the default option will be selected:"
+msgstr ""
+"Наконец, выполняется переход к `start_input`, где используются сервисы BIOS "
+"для запуска таймера и чтения пользовательского ввода с клавиатуры; если "
+"таймер истекает, будет выбран вариант по умолчанию:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:434
+#, no-wrap
+msgid ""
+"start_input:\n"
+" xorb %ah,%ah\t\t# BIOS: Get\n"
+" int $0x1a\t\t\t# system time\n"
+" movw %dx,%di\t\t# Ticks when\n"
+" addw _TICKS(%bp),%di\t# timeout\n"
+"read_key:\n"
+" movb $0x1,%ah\t\t# BIOS: Check\n"
+" int $0x16\t\t\t# for keypress\n"
+" jnz got_key\t\t# Have input\n"
+" xorb %ah,%ah\t\t# BIOS: int 0x1a, 00\n"
+" int $0x1a\t\t\t# get system time\n"
+" cmpw %di,%dx\t\t# Timeout?\n"
+" jb read_key\t\t# No\n"
+msgstr ""
+"start_input:\n"
+" xorb %ah,%ah\t\t# BIOS: Get\n"
+" int $0x1a\t\t\t# system time\n"
+" movw %dx,%di\t\t# Ticks when\n"
+" addw _TICKS(%bp),%di\t# timeout\n"
+"read_key:\n"
+" movb $0x1,%ah\t\t# BIOS: Check\n"
+" int $0x16\t\t\t# for keypress\n"
+" jnz got_key\t\t# Have input\n"
+" xorb %ah,%ah\t\t# BIOS: int 0x1a, 00\n"
+" int $0x1a\t\t\t# get system time\n"
+" cmpw %di,%dx\t\t# Timeout?\n"
+" jb read_key\t\t# No\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:436
+#, no-wrap
+msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-start-input]]"
+msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-start-input]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:449
+msgid ""
+"An interrupt is requested with number `0x1a` and argument `0` in register "
+"`%ah`. The BIOS has a predefined set of services, requested by applications "
+"as software-generated interrupts through the `int` instruction and receiving "
+"arguments in registers (in this case, `%ah`). Here, particularly, we are "
+"requesting the number of clock ticks since last midnight; this value is "
+"computed by the BIOS through the RTC (Real Time Clock). This clock can be "
+"programmed to work at frequencies ranging from 2 Hz to 8192 Hz. The BIOS "
+"sets it to 18.2 Hz at startup. When the request is satisfied, a 32-bit "
+"result is returned by the BIOS in registers `%cx` and `%dx` (lower bytes in "
+"`%dx`). This result (the `%dx` part) is copied to register `%di`, and the "
+"value of the `TICKS` variable is added to `%di`. This variable resides in "
+"[.filename]#boot0# at offset `_TICKS` (a negative value) from register `%bp` "
+"(which, recall, points to `0x800`). The default value of this variable is "
+"`0xb6` (182 in decimal). Now, the idea is that [.filename]#boot0# "
+"constantly requests the time from the BIOS, and when the value returned in "
+"register `%dx` is greater than the value stored in `%di`, the time is up and "
+"the default selection will be made. Since the RTC ticks 18.2 times per "
+"second, this condition will be met after 10 seconds (this default behavior "
+"can be changed in the [.filename]#Makefile#). Until this time has passed, "
+"[.filename]#boot0# continually asks the BIOS for any user input; this is "
+"done through `int 0x16`, argument `1` in `%ah`."
+msgstr ""
+"Прерывание запрашивается с номером `0x1a` и аргументом `0` в регистре `%ah`. "
+"BIOS имеет предопределённый набор сервисов, запрашиваемых приложениями как "
+"программно-генерируемые прерывания через инструкцию `int`, с получением "
+"аргументов в регистрах (в данном случае, `%ah`). Здесь, в частности, "
+"запрашивается количество тиков часов с момента последней полуночи; это "
+"значение вычисляется BIOS через RTC (Real Time Clock). Эти часы могут быть "
+"настроены на работу с частотой от 2 Гц до 8192 Гц. BIOS устанавливает их на "
+"18,2 Гц при запуске. Когда запрос выполнен, 32-битный результат возвращается "
+"BIOS в регистрах `%cx` и `%dx` (младшие байты в `%dx`). Этот результат "
+"(часть `%dx`) копируется в регистр `%di`, и к `%di` добавляется значение "
+"переменной `TICKS`. Эта переменная находится в [.filename]#boot0# по "
+"смещению `_TICKS` (отрицательное значение) от регистра `%bp` (который, "
+"напомним, указывает на `0x800`). Значение этой переменной по умолчанию — "
+"`0xb6` (182 в десятичной системе). Идея заключается в том, что "
+"[.filename]#boot0# постоянно запрашивает время у BIOS, и когда значение, "
+"возвращённое в регистре `%dx`, становится больше значения, хранящегося в "
+"`%di`, время истекает и будет сделан выбор по умолчанию. Поскольку RTC "
+"тикает 18,2 раза в секунду, это условие выполнится через 10 секунд (это "
+"поведение по умолчанию можно изменить в [.filename]#Makefile#). До истечения "
+"этого времени [.filename]#boot0# непрерывно опрашивает BIOS на предмет ввода "
+"пользователя; это делается через `int 0x16`, аргумент `1` в `%ah`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:457
+msgid ""
+"Whether a key was pressed or the time expired, subsequent code validates the "
+"selection. Based on the selection, the register `%si` is set to point to "
+"the appropriate partition entry in the partition table. This new selection "
+"overrides the previous default one. Indeed, it becomes the new default. "
+"Finally, the ACTIVE flag of the selected partition is set. If it was "
+"enabled at compile time, the in-memory version of [.filename]#boot0# with "
+"these modified values is written back to the MBR on disk. We leave the "
+"details of this implementation to the reader."
+msgstr ""
+"Была нажата клавиша или истекло время, последующий код проверяет выбор. В "
+"зависимости от выбора, регистр `%si` устанавливается так, чтобы указывать на "
+"соответствующую запись раздела в таблице разделов. Этот новый выбор "
+"переопределяет предыдущий выбор по умолчанию. Действительно, он становится "
+"новым значением по умолчанию. Наконец, устанавливается флаг ACTIVE "
+"выбранного раздела. Если это было разрешено при компиляции, версия "
+"[.filename]#boot0# в памяти с этими изменёнными значениями записывается "
+"обратно в MBR на диске. Мы оставляем детали этой реализации читателю."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:459
+msgid ""
+"We now end our study with the last code block from the [.filename]#boot0# "
+"program:"
+msgstr ""
+"Мы завершаем наше изучение последним блоком кода из программы "
+"[.filename]#boot0#:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:472
+#, no-wrap
+msgid ""
+" movw $LOAD,%bx\t\t# Address for read\n"
+" movb $0x2,%ah\t\t# Read sector\n"
+" callw intx13\t\t# from disk\n"
+" jc beep\t\t\t# If error\n"
+" cmpw $MAGIC,0x1fe(%bx)\t# Bootable?\n"
+" jne beep\t\t\t# No\n"
+" pushw %si\t\t\t# Save ptr to selected part.\n"
+" callw putn\t\t# Leave some space\n"
+" popw %si\t\t\t# Restore, next stage uses it\n"
+" jmp *%bx\t\t\t# Invoke bootstrap\n"
+msgstr ""
+" movw $LOAD,%bx\t\t# Address for read\n"
+" movb $0x2,%ah\t\t# Read sector\n"
+" callw intx13\t\t# from disk\n"
+" jc beep\t\t\t# If error\n"
+" cmpw $MAGIC,0x1fe(%bx)\t# Bootable?\n"
+" jne beep\t\t\t# No\n"
+" pushw %si\t\t\t# Save ptr to selected part.\n"
+" callw putn\t\t# Leave some space\n"
+" popw %si\t\t\t# Restore, next stage uses it\n"
+" jmp *%bx\t\t\t# Invoke bootstrap\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:474
+#, no-wrap
+msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-check-bootable]]"
+msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-check-bootable]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:478
+msgid ""
+"Recall that `%si` points to the selected partition entry. This entry tells "
+"us where the partition begins on disk. We assume, of course, that the "
+"partition selected is actually a FreeBSD slice."
+msgstr ""
+"Вспомним, что `%si` указывает на выбранную запись раздела. Эта запись "
+"сообщает нам, где начинается раздел на диске. Мы предполагаем, конечно, что "
+"выбранный раздел действительно является срезом FreeBSD."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:482
+msgid ""
+"From now on, we will favor the use of the technically more accurate term "
+"\"slice\" rather than \"partition\"."
+msgstr ""
+"Отныне мы будем отдавать предпочтение использованию технически более точного "
+"термина \"слайс\" вместо \"раздел\"."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:488
+msgid ""
+"The transfer buffer is set to `0x7c00` (register `%bx`), and a read for the "
+"first sector of the FreeBSD slice is requested by calling `intx13`. We "
+"assume that everything went okay, so a jump to `beep` is not performed. In "
+"particular, the new sector read must end with the magic sequence `0xaa55`. "
+"Finally, the value at `%si` (the pointer to the selected partition table) is "
+"preserved for use by the next stage, and a jump is performed to address "
+"`0x7c00`, where execution of our next stage (the just-read block) is started."
+msgstr ""
+"Буфер передачи установлен в `0x7c00` (регистр `%bx`), и запрос на чтение "
+"первого сектора слайса FreeBSD выполняется вызовом `intx13`. Мы "
+"предполагаем, что всё прошло успешно, поэтому переход к `beep` не "
+"выполняется. В частности, новый прочитанный сектор должен заканчиваться "
+"магической последовательностью `0xaa55`. Наконец, значение в `%si` "
+"(указатель на выбранную таблицу разделов) сохраняется для использования на "
+"следующем этапе, и выполняется переход по адресу `0x7c00`, где начинается "
+"выполнение нашего следующего этапа (только что прочитанного блока)."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:490
+#, no-wrap
+msgid "`boot1` Stage"
+msgstr "Этап `boot1`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:493
+msgid "So far we have gone through the following sequence:"
+msgstr "До сих пор мы прошли следующую последовательность:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:497
+msgid ""
+"The BIOS did some early hardware initialization, including the POST. The "
+"MBR ([.filename]#boot0#) was loaded from absolute disk sector one to address "
+"`0x7c00`. Execution control was passed to that location."
+msgstr ""
+"BIOS выполнил первоначальную инициализацию оборудования, включая POST. MBR "
+"([.filename]#boot0#) был загружен по адресу `0x7c00` из абсолютного сектора "
+"один с диска. Управление выполнением было передано по этому адресу."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:500
+msgid ""
+"[.filename]#boot0# relocated itself to the location it was linked to execute "
+"(`0x600`), followed by a jump to continue execution at the appropriate "
+"place. Finally, [.filename]#boot0# loaded the first disk sector from the "
+"FreeBSD slice to address `0x7c00`. Execution control was passed to that "
+"location."
+msgstr ""
+"[.filename]#boot0# переместил себя по адресу, по которому он был скомпонован "
+"для выполнения (`0x600`), после чего выполнил переход для продолжения "
+"выполнения в соответствующем месте. В завершение, [.filename]#boot0# "
+"загрузил первый сектор диска из раздела FreeBSD по адресу `0x7c00`. "
+"Управление выполнением было передано по этому адресу."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:513
+msgid ""
+"[.filename]#boot1# is the next step in the boot-loading sequence. It is the "
+"first of three boot stages. Note that we have been dealing exclusively with "
+"disk sectors. Indeed, the BIOS loads the absolute first sector, while "
+"[.filename]#boot0# loads the first sector of the FreeBSD slice. Both loads "
+"are to address `0x7c00`. We can conceptually think of these disk sectors as "
+"containing the files [.filename]#boot0# and [.filename]#boot1#, "
+"respectively, but in reality this is not entirely true for "
+"[.filename]#boot1#. Strictly speaking, unlike [.filename]#boot0#, "
+"[.filename]#boot1# is not part of the boot blocks footnote:[There is a file /"
+"boot/boot1, but it is not the written to the beginning of the FreeBSD "
+"slice. Instead, it is concatenated with boot2 to form boot, which is "
+"written to the beginning of the FreeBSD slice and read at boot time.]. "
+"Instead, a single, full-blown file, [.filename]#boot# ([.filename]#/boot/"
+"boot#), is what ultimately is written to disk. This file is a combination "
+"of [.filename]#boot1#, [.filename]#boot2# and the `Boot Extender` (or BTX). "
+"This single file is greater in size than a single sector (greater than 512 "
+"bytes). Fortunately, [.filename]#boot1# occupies _exactly_ the first 512 "
+"bytes of this single file, so when [.filename]#boot0# loads the first sector "
+"of the FreeBSD slice (512 bytes), it is actually loading [.filename]#boot1# "
+"and transferring control to it."
+msgstr ""
+"[.filename]#boot1# — это следующий шаг в последовательности загрузки. Это "
+"первая из трех стадий загрузки. Обратите внимание, что до сих пор мы "
+"работали исключительно с секторами диска. Действительно, BIOS загружает "
+"самый первый сектор, а [.filename]#boot0# загружает первый сектор раздела "
+"FreeBSD. Обе загрузки происходят по адресу `0x7c00`. Мы можем концептуально "
+"представлять эти секторы диска как содержащие файлы [.filename]#boot0# и "
+"[.filename]#boot1#, соответственно, но на самом деле это не совсем верно для "
+"[.filename]#boot1#. Строго говоря, в отличие от [.filename]#boot0#, "
+"[.filename]#boot1# не является частью загрузочных блоков footnote:[Файл /"
+"boot/boot1 существует, но он не записывается в начало раздела FreeBSD. "
+"Вместо этого он объединяется с boot2, формируя файл boot, который "
+"записывается в начало раздела FreeBSD и считывается во время загрузки.]. "
+"Вместо этого, единый полноценный файл [.filename]#boot# ([.filename]#/boot/"
+"boot#) в итоге записывается на диск. Этот файл представляет собой комбинацию "
+"[.filename]#boot1#, [.filename]#boot2# и `Boot Extender` (или BTX). Этот "
+"единый файл превышает размер одного сектора (больше 512 байт). К счастью, "
+"[.filename]#boot1# занимает _ровно_ первые 512 байт этого файла, поэтому, "
+"когда [.filename]#boot0# загружает первый сектор раздела FreeBSD (512 байт), "
+"он фактически загружает [.filename]#boot1# и передает ему управление."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:518
+msgid ""
+"The main task of [.filename]#boot1# is to load the next boot stage. This "
+"next stage is somewhat more complex. It is composed of a server called the "
+"\"Boot Extender\", or BTX, and a client, called [.filename]#boot2#. As we "
+"will see, the last boot stage, [.filename]#loader#, is also a client of the "
+"BTX server."
+msgstr ""
+"Основная задача [.filename]#boot1# — загрузить следующий этап загрузки. Этот "
+"следующий этап несколько сложнее. Он состоит из сервера под названием \"Boot "
+"Extender\" (BTX) и клиента под названием [.filename]#boot2#. Как мы увидим, "
+"последний этап загрузки, [.filename]#loader#, также является клиентом "
+"сервера BTX."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:520
+msgid ""
+"Let us now look in detail at what exactly is done by [.filename]#boot1#, "
+"starting like we did for [.filename]#boot0#, at its entry point:"
+msgstr ""
+"Давайте теперь подробно рассмотрим, что именно делает [.filename]#boot1#, "
+"начиная, как мы это делали для [.filename]#boot0#, с точки входа:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:525
+#, no-wrap
+msgid ""
+"start:\n"
+"\tjmp main\n"
+msgstr ""
+"start:\n"
+"\tjmp main\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:527
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-entry]]"
+msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-entry]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:529
+msgid ""
+"The entry point at `start` simply jumps past a special data area to the "
+"label `main`, which in turn looks like this:"
+msgstr ""
+"Точка входа `start` просто переходит через специальную область данных к "
+"метке `main`, которая, в свою очередь, выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:544
+#, no-wrap
+msgid ""
+"main:\n"
+" cld\t\t\t# String ops inc\n"
+" xor %cx,%cx\t\t# Zero\n"
+" mov %cx,%es\t\t# Address\n"
+" mov %cx,%ds\t\t# data\n"
+" mov %cx,%ss\t\t# Set up\n"
+" mov $start,%sp\t\t# stack\n"
+" mov %sp,%si\t\t# Source\n"
+" mov $MEM_REL,%di\t\t# Destination\n"
+" incb %ch\t\t\t# Word count\n"
+" rep\t\t\t# Copy\n"
+" movsw\t\t\t# code\n"
+msgstr ""
+"main:\n"
+" cld\t\t\t# String ops inc\n"
+" xor %cx,%cx\t\t# Zero\n"
+" mov %cx,%es\t\t# Address\n"
+" mov %cx,%ds\t\t# data\n"
+" mov %cx,%ss\t\t# Set up\n"
+" mov $start,%sp\t\t# stack\n"
+" mov %sp,%si\t\t# Source\n"
+" mov $MEM_REL,%di\t\t# Destination\n"
+" incb %ch\t\t\t# Word count\n"
+" rep\t\t\t# Copy\n"
+" movsw\t\t\t# code\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:546
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-main]]"
+msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-main]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:551
+msgid ""
+"Just like [.filename]#boot0#, this code relocates [.filename]#boot1#, this "
+"time to memory address `0x700`. However, unlike [.filename]#boot0#, it does "
+"not jump there. [.filename]#boot1# is linked to execute at address "
+"`0x7c00`, effectively where it was loaded in the first place. The reason "
+"for this relocation will be discussed shortly."
+msgstr ""
+"Как и [.filename]#boot0#, этот код перемещает [.filename]#boot1#, на этот "
+"раз по адресу `0x700`. Однако, в отличие от [.filename]#boot0#, он не "
+"переходит туда. [.filename]#boot1# скомпонован для выполнения по адресу "
+"`0x7c00`, фактически там, куда он был изначально загружен. Причина этого "
+"перемещения будет рассмотрена далее."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:556
+msgid ""
+"Next comes a loop that looks for the FreeBSD slice. Although "
+"[.filename]#boot0# loaded [.filename]#boot1# from the FreeBSD slice, no "
+"information was passed to it about this footnote:[Actually we did pass a "
+"pointer to the slice entry in register %si. However, boot1 does not assume "
+"that it was loaded by boot0 (perhaps some other MBR loaded it, and did not "
+"pass this information), so it assumes nothing.], so [.filename]#boot1# must "
+"rescan the partition table to find where the FreeBSD slice starts. "
+"Therefore it rereads the MBR:"
+msgstr ""
+"Далее идет цикл, который ищет слайс FreeBSD. Хотя [.filename]#boot0# "
+"загрузил [.filename]#boot1# из слайса FreeBSD, ему не была передана "
+"информация об этом footnote:[На самом деле мы передали указатель на адрес "
+"слайса в регистре %si. Однако boot1 не предполагает, что он был загружен "
+"boot0 (возможно, его загрузил другой MBR и не передал эту информацию), "
+"поэтому он ничего не предполагает.], поэтому [.filename]#boot1# должен "
+"повторно просканировать таблицу разделов, чтобы найти начало слайса FreeBSD. "
+"Для этого он перечитывает MBR:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:564
+#, no-wrap
+msgid ""
+" mov $part4,%si\t\t# Partition\n"
+" cmpb $0x80,%dl\t\t# Hard drive?\n"
+" jb main.4\t\t\t# No\n"
+" movb $0x1,%dh\t\t# Block count\n"
+" callw nread\t\t# Read MBR\n"
+msgstr ""
+" mov $part4,%si\t\t# Partition\n"
+" cmpb $0x80,%dl\t\t# Hard drive?\n"
+" jb main.4\t\t\t# No\n"
+" movb $0x1,%dh\t\t# Block count\n"
+" callw nread\t\t# Read MBR\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:566
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-find-freebsd]]"
+msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-find-freebsd]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:574
+msgid ""
+"In the code above, register `%dl` maintains information about the boot "
+"device. This is passed on by the BIOS and preserved by the MBR. Numbers "
+"`0x80` and greater tells us that we are dealing with a hard drive, so a call "
+"is made to `nread`, where the MBR is read. Arguments to `nread` are passed "
+"through `%si` and `%dh`. The memory address at label `part4` is copied to "
+"`%si`. This memory address holds a \"fake partition\" to be used by "
+"`nread`. The following is the data in the fake partition:"
+msgstr ""
+"В приведённом выше коде регистр `%dl` содержит информацию о загрузочном "
+"устройстве. Эти данные передаются BIOS и сохраняются MBR. Числа `0x80` и "
+"выше указывают на то, что мы имеем дело с жёстким диском, поэтому вызывается "
+"`nread`, где считывается MBR. Аргументы для `nread` передаются через `%si` "
+"и `%dh`. Адрес памяти по метке `part4` копируется в `%si`. Этот адрес "
+"памяти содержит \"фальшивый раздел\", который будет использован `nread`. "
+"Ниже приведены данные фальшивого раздела:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:582
+#, no-wrap
+msgid ""
+" part4:\n"
+"\t.byte 0x80, 0x00, 0x01, 0x00\n"
+"\t.byte 0xa5, 0xfe, 0xff, 0xff\n"
+"\t.byte 0x00, 0x00, 0x00, 0x00\n"
+"\t.byte 0x50, 0xc3, 0x00, 0x00\n"
+msgstr ""
+" part4:\n"
+"\t.byte 0x80, 0x00, 0x01, 0x00\n"
+"\t.byte 0xa5, 0xfe, 0xff, 0xff\n"
+"\t.byte 0x00, 0x00, 0x00, 0x00\n"
+"\t.byte 0x50, 0xc3, 0x00, 0x00\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:584
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot2-make-fake-partition]]"
+msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot2-make-fake-partition]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:589
+msgid ""
+"In particular, the LBA for this fake partition is hardcoded to zero. This "
+"is used as an argument to the BIOS for reading absolute sector one from the "
+"hard drive. Alternatively, CHS addressing could be used. In this case, the "
+"fake partition holds cylinder 0, head 0 and sector 1, which is equivalent to "
+"absolute sector one."
+msgstr ""
+"В частности, LBA для этой фиктивной раздела жестко закодирован как ноль. Это "
+"используется как аргумент для BIOS при чтении абсолютного сектора один с "
+"жесткого диска. Альтернативно, может использоваться адресация CHS. В этом "
+"случае, фиктивный раздел содержит цилиндр 0, головку 0 и сектор 1, что "
+"эквивалентно абсолютному сектору один."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:591
+msgid "Let us now proceed to take a look at `nread`:"
+msgstr "Продолжим, рассмотрев `nread`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:601
+#, no-wrap
+msgid ""
+"nread:\n"
+" mov $MEM_BUF,%bx\t\t# Transfer buffer\n"
+" mov 0x8(%si),%ax\t\t# Get\n"
+" mov 0xa(%si),%cx\t\t# LBA\n"
+" push %cs\t\t\t# Read from\n"
+" callw xread.1\t\t# disk\n"
+" jnc return\t\t# If success, return\n"
+msgstr ""
+"nread:\n"
+" mov $MEM_BUF,%bx\t\t# Transfer buffer\n"
+" mov 0x8(%si),%ax\t\t# Get\n"
+" mov 0xa(%si),%cx\t\t# LBA\n"
+" push %cs\t\t\t# Read from\n"
+" callw xread.1\t\t# disk\n"
+" jnc return\t\t# If success, return\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:603
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-nread]]"
+msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-nread]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:612
+msgid ""
+"Recall that `%si` points to the fake partition. The word footnote:[In the "
+"context of 16-bit real mode, a word is 2 bytes.] at offset `0x8` is copied "
+"to register `%ax` and word at offset `0xa` to `%cx`. They are interpreted "
+"by the BIOS as the lower 4-byte value denoting the LBA to be read (the upper "
+"four bytes are assumed to be zero). Register `%bx` holds the memory address "
+"where the MBR will be loaded. The instruction pushing `%cs` onto the stack "
+"is very interesting. In this context, it accomplishes nothing. However, as "
+"we will see shortly, [.filename]#boot2#, in conjunction with the BTX server, "
+"also uses `xread.1`. This mechanism will be discussed in the next section."
+msgstr ""
+"Напомним, что `%si` указывает на поддельный раздел. Слово footnote:[В "
+"контексте 16-битного реального режима слово — это 2 байта.] по смещению "
+"`0x8` копируется в регистр `%ax`, а слово по смещению `0xa` — в `%cx`. BIOS "
+"интерпретирует их как младшее 4-байтовое значение, обозначающее LBA для "
+"чтения (старшие четыре байта предполагаются нулевыми). Регистр `%bx` "
+"содержит адрес памяти, куда будет загружен MBR. Инструкция, помещающая `%cs` "
+"в стек, очень интересна. В данном контексте она ничего не делает. Однако, "
+"как мы скоро увидим, [.filename]#boot2# в сочетании с сервером BTX также "
+"использует `xread.1`. Этот механизм будет рассмотрен в следующем разделе."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:614
+msgid ""
+"The code at `xread.1` further calls the `read` function, which actually "
+"calls the BIOS asking for the disk sector:"
+msgstr ""
+"Код в `xread.1` далее вызывает функцию `read`, которая фактически обращается "
+"к BIOS с запросом на чтение сектора диска:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:631
+#, no-wrap
+msgid ""
+"xread.1:\n"
+"\tpushl $0x0\t\t# absolute\n"
+"\tpush %cx\t\t# block\n"
+"\tpush %ax\t\t# number\n"
+"\tpush %es\t\t# Address of\n"
+"\tpush %bx\t\t# transfer buffer\n"
+"\txor %ax,%ax\t\t# Number of\n"
+"\tmovb %dh,%al\t\t# blocks to\n"
+"\tpush %ax\t\t# transfer\n"
+"\tpush $0x10\t\t# Size of packet\n"
+"\tmov %sp,%bp\t\t# Packet pointer\n"
+"\tcallw read\t\t# Read from disk\n"
+"\tlea 0x10(%bp),%sp\t# Clear stack\n"
+"\tlret\t\t\t# To far caller\n"
+msgstr ""
+"xread.1:\n"
+"\tpushl $0x0\t\t# absolute\n"
+"\tpush %cx\t\t# block\n"
+"\tpush %ax\t\t# number\n"
+"\tpush %es\t\t# Address of\n"
+"\tpush %bx\t\t# transfer buffer\n"
+"\txor %ax,%ax\t\t# Number of\n"
+"\tmovb %dh,%al\t\t# blocks to\n"
+"\tpush %ax\t\t# transfer\n"
+"\tpush $0x10\t\t# Size of packet\n"
+"\tmov %sp,%bp\t\t# Packet pointer\n"
+"\tcallw read\t\t# Read from disk\n"
+"\tlea 0x10(%bp),%sp\t# Clear stack\n"
+"\tlret\t\t\t# To far caller\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:633
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-xread1]]"
+msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-xread1]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:637
+msgid ""
+"Note the long return instruction at the end of this block. This instruction "
+"pops out the `%cs` register pushed by `nread`, and returns. Finally, "
+"`nread` also returns."
+msgstr ""
+"Обратите внимание на длинную инструкцию возврата в конце этого блока. Эта "
+"инструкция извлекает регистр `%cs`, помещённый в стек `nread`, и возвращает "
+"управление. В конце `nread` также возвращает управление."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:639
+msgid ""
+"With the MBR loaded to memory, the actual loop for searching the FreeBSD "
+"slice begins:"
+msgstr ""
+"С загрузкой MBR в память начинается фактический цикл поиска слайса FreeBSD:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:659
+#, no-wrap
+msgid ""
+"\tmov $0x1,%cx\t\t # Two passes\n"
+"main.1:\n"
+"\tmov $MEM_BUF+PRT_OFF,%si # Partition table\n"
+"\tmovb $0x1,%dh\t\t # Partition\n"
+"main.2:\n"
+"\tcmpb $PRT_BSD,0x4(%si)\t # Our partition type?\n"
+"\tjne main.3\t\t # No\n"
+"\tjcxz main.5\t\t # If second pass\n"
+"\ttestb $0x80,(%si)\t # Active?\n"
+"\tjnz main.5\t\t # Yes\n"
+"main.3:\n"
+"\tadd $0x10,%si\t\t # Next entry\n"
+"\tincb %dh\t\t # Partition\n"
+"\tcmpb $0x1+PRT_NUM,%dh\t\t # In table?\n"
+"\tjb main.2\t\t # Yes\n"
+"\tdec %cx\t\t\t # Do two\n"
+"\tjcxz main.1\t\t # passes\n"
+msgstr ""
+"\tmov $0x1,%cx\t\t # Two passes\n"
+"main.1:\n"
+"\tmov $MEM_BUF+PRT_OFF,%si # Partition table\n"
+"\tmovb $0x1,%dh\t\t # Partition\n"
+"main.2:\n"
+"\tcmpb $PRT_BSD,0x4(%si)\t # Our partition type?\n"
+"\tjne main.3\t\t # No\n"
+"\tjcxz main.5\t\t # If second pass\n"
+"\ttestb $0x80,(%si)\t # Active?\n"
+"\tjnz main.5\t\t # Yes\n"
+"main.3:\n"
+"\tadd $0x10,%si\t\t # Next entry\n"
+"\tincb %dh\t\t # Partition\n"
+"\tcmpb $0x1+PRT_NUM,%dh\t\t # In table?\n"
+"\tjb main.2\t\t # Yes\n"
+"\tdec %cx\t\t\t # Do two\n"
+"\tjcxz main.1\t\t # passes\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:661
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-find-part]]"
+msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-find-part]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:665
+msgid ""
+"If a FreeBSD slice is identified, execution continues at `main.5`. Note "
+"that when a FreeBSD slice is found `%si` points to the appropriate entry in "
+"the partition table, and `%dh` holds the partition number. We assume that a "
+"FreeBSD slice is found, so we continue execution at `main.5`:"
+msgstr ""
+"Если обнаружен слайс FreeBSD, выполнение продолжается на метке `main.5`. "
+"Обратите внимание, что при обнаружении слайса FreeBSD `%si` указывает на "
+"соответствующую запись в таблице разделов, а `%dh` содержит номер раздела. "
+"Мы предполагаем, что слайс FreeBSD найден, поэтому продолжаем выполнение на "
+"метке `main.5`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:680
+#, no-wrap
+msgid ""
+"main.5:\n"
+"\tmov %dx,MEM_ARG\t\t\t # Save args\n"
+"\tmovb $NSECT,%dh\t\t\t # Sector count\n"
+"\tcallw nread\t\t\t # Read disk\n"
+"\tmov $MEM_BTX,%bx\t\t\t # BTX\n"
+"\tmov 0xa(%bx),%si\t\t # Get BTX length and set\n"
+"\tadd %bx,%si\t\t\t # %si to start of boot2.bin\n"
+"\tmov $MEM_USR+SIZ_PAG*2,%di\t\t\t # Client page 2\n"
+"\tmov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx\t\t\t # Byte\n"
+"\tsub %si,%cx\t\t\t # count\n"
+"\trep\t\t\t\t # Relocate\n"
+"\tmovsb\t\t\t\t # client\n"
+msgstr ""
+"main.5:\n"
+"\tmov %dx,MEM_ARG\t\t\t # Save args\n"
+"\tmovb $NSECT,%dh\t\t\t # Sector count\n"
+"\tcallw nread\t\t\t # Read disk\n"
+"\tmov $MEM_BTX,%bx\t\t\t # BTX\n"
+"\tmov 0xa(%bx),%si\t\t # Get BTX length and set\n"
+"\tadd %bx,%si\t\t\t # %si to start of boot2.bin\n"
+"\tmov $MEM_USR+SIZ_PAG*2,%di\t\t\t # Client page 2\n"
+"\tmov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx\t\t\t # Byte\n"
+"\tsub %si,%cx\t\t\t # count\n"
+"\trep\t\t\t\t # Relocate\n"
+"\tmovsb\t\t\t\t # client\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:682
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-main5]]"
+msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-main5]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:688
+msgid ""
+"Recall that at this point, register `%si` points to the FreeBSD slice entry "
+"in the MBR partition table, so a call to `nread` will effectively read "
+"sectors at the beginning of this partition. The argument passed on register "
+"`%dh` tells `nread` to read 16 disk sectors. Recall that the first 512 "
+"bytes, or the first sector of the FreeBSD slice, coincides with the "
+"[.filename]#boot1# program. Also recall that the file written to the "
+"beginning of the FreeBSD slice is not [.filename]#/boot/boot1#, but "
+"[.filename]#/boot/boot#. Let us look at the size of these files in the "
+"filesystem:"
+msgstr ""
+"Напомним, что в данный момент регистр `%si` указывает на запись среза "
+"FreeBSD в таблице разделов MBR, поэтому вызов `nread` фактически прочитает "
+"секторы в начале этого раздела. Аргумент, переданный в регистре `%dh`, "
+"указывает `nread` прочитать 16 секторов диска. Напомним, что первые 512 "
+"байт, или первый сектор слайса FreeBSD, совпадает с программой "
+"[.filename]#boot1#. Также напомним, что файл, записанный в начало слайса "
+"FreeBSD, это не [.filename]#/boot/boot1#, а [.filename]#/boot/boot#. Давайте "
+"посмотрим на размер этих файлов в файловой системе:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:695
+#, no-wrap
+msgid ""
+"-r--r--r-- 1 root wheel 512B Jan 8 00:15 /boot/boot0\n"
+"-r--r--r-- 1 root wheel 512B Jan 8 00:15 /boot/boot1\n"
+"-r--r--r-- 1 root wheel 7.5K Jan 8 00:15 /boot/boot2\n"
+"-r--r--r-- 1 root wheel 8.0K Jan 8 00:15 /boot/boot\n"
+msgstr ""
+"-r--r--r-- 1 root wheel 512B Jan 8 00:15 /boot/boot0\n"
+"-r--r--r-- 1 root wheel 512B Jan 8 00:15 /boot/boot1\n"
+"-r--r--r-- 1 root wheel 7.5K Jan 8 00:15 /boot/boot2\n"
+"-r--r--r-- 1 root wheel 8.0K Jan 8 00:15 /boot/boot\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:703
+msgid ""
+"Both [.filename]#boot0# and [.filename]#boot1# are 512 bytes each, so they "
+"fit _exactly_ in one disk sector. [.filename]#boot2# is much bigger, "
+"holding both the BTX server and the [.filename]#boot2# client. Finally, a "
+"file called simply [.filename]#boot# is 512 bytes larger than "
+"[.filename]#boot2#. This file is a concatenation of [.filename]#boot1# and "
+"[.filename]#boot2#. As already noted, [.filename]#boot0# is the file "
+"written to the absolute first disk sector (the MBR), and [.filename]#boot# "
+"is the file written to the first sector of the FreeBSD slice; "
+"[.filename]#boot1# and [.filename]#boot2# are _not_ written to disk. The "
+"command used to concatenate [.filename]#boot1# and [.filename]#boot2# into a "
+"single [.filename]#boot# is merely `cat boot1 boot2 > boot`."
+msgstr ""
+"Оба файла [.filename]#boot0# и [.filename]#boot1# имеют размер 512 байт "
+"каждый, поэтому они занимают _ровно_ один сектор диска. [.filename]#boot2# "
+"значительно больше, так как содержит как сервер BTX, так и клиент "
+"[.filename]#boot2#. Наконец, файл под названием просто [.filename]#boot# на "
+"512 байт больше, чем [.filename]#boot2#. Этот файл представляет собой "
+"объединение [.filename]#boot1# и [.filename]#boot2#. Как уже отмечалось, "
+"[.filename]#boot0# записывается в самый первый сектор диска (MBR), а "
+"[.filename]#boot# записывается в первый сектор раздела FreeBSD; "
+"[.filename]#boot1# и [.filename]#boot2# _не_ записываются на диск. Команда, "
+"используемая для объединения [.filename]#boot1# и [.filename]#boot2# в "
+"единый файл [.filename]#boot#, выглядит просто как `cat boot1 boot2 > boot`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:707
+msgid ""
+"So [.filename]#boot1# occupies exactly the first 512 bytes of "
+"[.filename]#boot# and, because [.filename]#boot# is written to the first "
+"sector of the FreeBSD slice, [.filename]#boot1# fits exactly in this first "
+"sector. When `nread` reads the first 16 sectors of the FreeBSD slice, it "
+"effectively reads the entire [.filename]#boot# file footnote:[512*16=8192 "
+"bytes, exactly the size of boot]. We will see more details about how "
+"[.filename]#boot# is formed from [.filename]#boot1# and [.filename]#boot2# "
+"in the next section."
+msgstr ""
+"Итак, [.filename]#boot1# занимает ровно первые 512 байт [.filename]#boot#, "
+"и, поскольку [.filename]#boot# записывается в первый сектор слайса FreeBSD, "
+"[.filename]#boot1# полностью помещается в этот первый сектор. Когда `nread` "
+"читает первые 16 секторов слайса FreeBSD, он фактически читает весь файл "
+"[.filename]#boot# footnote:[512*16=8192 байта, ровно размер boot]. Более "
+"подробно о том, как [.filename]#boot# формируется из [.filename]#boot1# и "
+"[.filename]#boot2#, мы увидим в следующем разделе."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:713
+msgid ""
+"Recall that `nread` uses memory address `0x8c00` as the transfer buffer to "
+"hold the sectors read. This address is conveniently chosen. Indeed, "
+"because [.filename]#boot1# belongs to the first 512 bytes, it ends up in the "
+"address range `0x8c00`-`0x8dff`. The 512 bytes that follows (range `0x8e00`-"
+"`0x8fff`) is used to store the _bsdlabel_ footnote:[Historically known as "
+"disklabel. If you ever wondered where FreeBSD stored this information, it "
+"is in this region - see man:bsdlabel[8]]."
+msgstr ""
+"Напомним, что `nread` использует адрес памяти `0x8c00` в качестве буфера "
+"передачи для хранения прочитанных секторов. Этот адрес выбран не случайно. "
+"Действительно, поскольку [.filename]#boot1# принадлежит первым 512 байтам, "
+"он оказывается в диапазоне адресов `0x8c00`-`0x8dff`. Следующие 512 байт "
+"(диапазон `0x8e00`-`0x8fff`) используются для хранения _bsdlabel_ footnote:"
+"[Исторически известной как disklabel. Если вам когда-либо было интересно, "
+"где FreeBSD хранит эту информацию, она находится в этой области — см. "
+"man:bsdlabel[8]]."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:721
+msgid ""
+"Starting at address `0x9000` is the beginning of the BTX server, and "
+"immediately following is the [.filename]#boot2# client. The BTX server acts "
+"as a kernel, and executes in protected mode in the most privileged level. "
+"In contrast, the BTX clients ([.filename]#boot2#, for example), execute in "
+"user mode. We will see how this is accomplished in the next section. The "
+"code after the call to `nread` locates the beginning of [.filename]#boot2# "
+"in the memory buffer, and copies it to memory address `0xc000`. This is "
+"because the BTX server arranges [.filename]#boot2# to execute in a segment "
+"starting at `0xa000`. We explore this in detail in the following section."
+msgstr ""
+"Начиная с адреса `0x9000` находится начало сервера BTX, и сразу за ним "
+"следует клиент [.filename]#boot2#. Сервер BTX действует как ядро и "
+"выполняется в защищённом режиме с наивысшим уровнем привилегий. В отличие от "
+"этого, клиенты BTX (например, [.filename]#boot2#) выполняются в "
+"пользовательском режиме. Мы увидим, как это реализовано, в следующем "
+"разделе. Код после вызова `nread` находит начало [.filename]#boot2# в буфере "
+"памяти и копирует его по адресу `0xc000`. Это связано с тем, что сервер BTX "
+"размещает [.filename]#boot2# для выполнения в сегменте, начинающемся с "
+"`0xa000`. Мы подробно рассмотрим это в следующем разделе."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:724
+msgid ""
+"The last code block of [.filename]#boot1# enables access to memory above 1MB "
+"footnote:[This is necessary for legacy reasons. Interested readers should "
+"see .] and concludes with a jump to the starting point of the BTX server:"
+msgstr ""
+"Последний блок кода в [.filename]#boot1# разрешает доступ к памяти выше 1MB "
+"footnote:[Это необходимо по историческим причинам. Заинтересованные читатели "
+"могут обратиться к .] и завершается переходом к начальной точке сервера BTX:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:732
+#, no-wrap
+msgid ""
+"seta20:\n"
+"\tcli\t\t\t# Disable interrupts\n"
+"seta20.1:\n"
+"\tdec %cx\t\t\t# Timeout?\n"
+"\tjz seta20.3\t\t# Yes\n"
+msgstr ""
+"seta20:\n"
+"\tcli\t\t\t# Disable interrupts\n"
+"seta20.1:\n"
+"\tdec %cx\t\t\t# Timeout?\n"
+"\tjz seta20.3\t\t# Yes\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:747
+#, no-wrap
+msgid ""
+"\tinb $0x64,%al\t\t# Get status\n"
+"\ttestb $0x2,%al\t\t# Busy?\n"
+"\tjnz seta20.1\t\t# Yes\n"
+"\tmovb $0xd1,%al\t\t# Command: Write\n"
+"\toutb %al,$0x64\t\t# output port\n"
+"seta20.2:\n"
+"\tinb $0x64,%al\t\t# Get status\n"
+"\ttestb $0x2,%al\t\t# Busy?\n"
+"\tjnz seta20.2\t\t# Yes\n"
+"\tmovb $0xdf,%al\t\t# Enable\n"
+"\toutb %al,$0x60\t\t# A20\n"
+"seta20.3:\n"
+"\tsti\t\t\t# Enable interrupts\n"
+"\tjmp 0x9010\t\t# Start BTX\n"
+msgstr ""
+"\tinb $0x64,%al\t\t# Get status\n"
+"\ttestb $0x2,%al\t\t# Busy?\n"
+"\tjnz seta20.1\t\t# Yes\n"
+"\tmovb $0xd1,%al\t\t# Command: Write\n"
+"\toutb %al,$0x64\t\t# output port\n"
+"seta20.2:\n"
+"\tinb $0x64,%al\t\t# Get status\n"
+"\ttestb $0x2,%al\t\t# Busy?\n"
+"\tjnz seta20.2\t\t# Yes\n"
+"\tmovb $0xdf,%al\t\t# Enable\n"
+"\toutb %al,$0x60\t\t# A20\n"
+"seta20.3:\n"
+"\tsti\t\t\t# Enable interrupts\n"
+"\tjmp 0x9010\t\t# Start BTX\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:749
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-seta20]]"
+msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-seta20]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:751
+msgid "Note that right before the jump, interrupts are enabled."
+msgstr ""
+"Обратите внимание, что непосредственно перед переходом прерывания включаются."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:753
+#, no-wrap
+msgid "The BTX Server"
+msgstr "Сервер BTX"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:757
+msgid ""
+"Next in our boot sequence is the BTX Server. Let us quickly remember how we "
+"got here:"
+msgstr ""
+"Далее в нашей последовательности загрузки идёт сервер BTX. Давайте быстро "
+"вспомним, как мы сюда попали:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:759
+msgid ""
+"The BIOS loads the absolute sector one (the MBR, or [.filename]#boot0#), to "
+"address `0x7c00` and jumps there."
+msgstr ""
+"BIOS загружает абсолютный сектор один (MBR или [.filename]#boot0#) по адресу "
+"`0x7c00` и переходит туда."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:761
+msgid ""
+"[.filename]#boot0# relocates itself to `0x600`, the address it was linked to "
+"execute, and jumps over there. It then reads the first sector of the "
+"FreeBSD slice (which consists of [.filename]#boot1#) into address `0x7c00` "
+"and jumps over there."
+msgstr ""
+"[.filename]#boot0# перемещает себя по адресу `0x600`, по которому он был "
+"слинкован для выполнения, и переходит туда. Затем он читает первый сектор "
+"среза FreeBSD (который содержит [.filename]#boot1#) в адрес `0x7c00` и "
+"переходит туда."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:766
+msgid ""
+"[.filename]#boot1# loads the first 16 sectors of the FreeBSD slice into "
+"address `0x8c00`. This 16 sectors, or 8192 bytes, is the whole file "
+"[.filename]#boot#. The file is a concatenation of [.filename]#boot1# and "
+"[.filename]#boot2#. [.filename]#boot2#, in turn, contains the BTX server "
+"and the [.filename]#boot2# client. Finally, a jump is made to address "
+"`0x9010`, the entry point of the BTX server."
+msgstr ""
+"[.filename]#boot1# загружает первые 16 секторов среза FreeBSD по адресу "
+"`0x8c00`. Эти 16 секторов, или 8192 байта, представляют собой весь файл "
+"[.filename]#boot#. Файл является объединением [.filename]#boot1# и "
+"[.filename]#boot2#. [.filename]#boot2#, в свою очередь, содержит сервер BTX "
+"и клиент [.filename]#boot2#. Наконец, выполняется переход по адресу "
+"`0x9010`, точке входа сервера BTX."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:770
+msgid ""
+"Before studying the BTX Server in detail, let us further review how the "
+"single, all-in-one [.filename]#boot# file is created. The way "
+"[.filename]#boot# is built is defined in its [.filename]#Makefile# "
+"([.filename]#stand/i386/boot2/Makefile#). Let us look at the rule that "
+"creates the [.filename]#boot# file:"
+msgstr ""
+"Прежде чем изучать сервер BTX подробно, давайте рассмотрим, как создается "
+"единый, всеобъемлющий файл [.filename]#boot#. Способ сборки "
+"[.filename]#boot# определен в его [.filename]#Makefile# ([.filename]#stand/"
+"i386/boot2/Makefile#). Рассмотрим правило, которое создает файл "
+"[.filename]#boot#:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:775
+#, no-wrap
+msgid ""
+" boot: boot1 boot2\n"
+"\tcat boot1 boot2 > boot\n"
+msgstr ""
+" boot: boot1 boot2\n"
+"\tcat boot1 boot2 > boot\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:777
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot]]"
+msgstr "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:780
+msgid ""
+"This tells us that [.filename]#boot1# and [.filename]#boot2# are needed, and "
+"the rule simply concatenates them to produce a single file called "
+"[.filename]#boot#. The rules for creating [.filename]#boot1# are also quite "
+"simple:"
+msgstr ""
+"Это говорит нам, что [.filename]#boot1# и [.filename]#boot2# необходимы, и "
+"правило просто объединяет их для создания одного файла с именем "
+"[.filename]#boot#. Правила для создания [.filename]#boot1# также довольно "
+"просты:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:785
+#, no-wrap
+msgid ""
+" boot1: boot1.out\n"
+"\t${OBJCOPY} -S -O binary boot1.out ${.TARGET}\n"
+msgstr ""
+" boot1: boot1.out\n"
+"\t${OBJCOPY} -S -O binary boot1.out ${.TARGET}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:788
+#, no-wrap
+msgid ""
+" boot1.out: boot1.o\n"
+"\t${LD} ${LD_FLAGS} -e start --defsym ORG=${ORG1} -T ${LDSCRIPT} -o ${.TARGET} boot1.o\n"
+msgstr ""
+" boot1.out: boot1.o\n"
+"\t${LD} ${LD_FLAGS} -e start --defsym ORG=${ORG1} -T ${LDSCRIPT} -o ${.TARGET} boot1.o\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:790
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot1]]"
+msgstr "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot1]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:799
+msgid ""
+"To apply the rule for creating [.filename]#boot1#, [.filename]#boot1.out# "
+"must be resolved. This, in turn, depends on the existence of "
+"[.filename]#boot1.o#. This last file is simply the result of assembling our "
+"familiar [.filename]#boot1.S#, without linking. Now, the rule for creating "
+"[.filename]#boot1.out# is applied. This tells us that [.filename]#boot1.o# "
+"should be linked with `start` as its entry point, and starting at address "
+"`0x7c00`. Finally, [.filename]#boot1# is created from "
+"[.filename]#boot1.out# applying the appropriate rule. This rule is the "
+"[.filename]#objcopy# command applied to [.filename]#boot1.out#. Note the "
+"flags passed to [.filename]#objcopy#: `-S` tells it to strip all relocation "
+"and symbolic information; `-O binary` indicates the output format, that is, "
+"a simple, unformatted binary file."
+msgstr ""
+"Для применения правила создания [.filename]#boot1# необходимо собрать "
+"[.filename]#boot1.out#. Это, в свою очередь, зависит от наличия "
+"[.filename]#boot1.o#. Последний файл является результатом ассемблирования "
+"нашего знакомого [.filename]#boot1.S# без компоновки. Теперь применяется "
+"правило создания [.filename]#boot1.out#. Оно указывает, что "
+"[.filename]#boot1.o# должен быть скомпонован с точкой входа `start` и "
+"начальным адресом `0x7c00`. Наконец, [.filename]#boot1# создается из "
+"[.filename]#boot1.out# применением соответствующего правила. Это команда "
+"[.filename]#objcopy#, применяемая к [.filename]#boot1.out#. Обратите "
+"внимание на флаги, передаваемые [.filename]#objcopy#: `-S` указывает на "
+"удаление всей информации о перемещении и символов; `-O binary` указывает "
+"формат вывода, то есть простой, неформатированный двоичный файл."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:801
+msgid ""
+"Having [.filename]#boot1#, let us take a look at how [.filename]#boot2# is "
+"constructed:"
+msgstr ""
+"Имея [.filename]#boot1#, давайте посмотрим, как устроен [.filename]#boot2#:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:808
+#, no-wrap
+msgid ""
+" boot2: boot2.ld\n"
+"\t@set -- `ls -l ${.ALLSRC}`; x=$$((${BOOT2SIZE}-$$5)); \\\n"
+"\t echo \"$$x bytes available\"; test $$x -ge 0\n"
+"\t${DD} if=${.ALLSRC} of=${.TARGET} bs=${BOOT2SIZE} conv=sync\n"
+msgstr ""
+" boot2: boot2.ld\n"
+"\t@set -- `ls -l ${.ALLSRC}`; x=$$((${BOOT2SIZE}-$$5)); \\\n"
+"\t echo \"$$x bytes available\"; test $$x -ge 0\n"
+"\t${DD} if=${.ALLSRC} of=${.TARGET} bs=${BOOT2SIZE} conv=sync\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:812
+#, no-wrap
+msgid ""
+" boot2.ld: boot2.ldr boot2.bin ${BTXKERN}\n"
+"\tbtxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \\\n"
+"\t -o ${.TARGET} -P 1 boot2.bin\n"
+msgstr ""
+" boot2.ld: boot2.ldr boot2.bin ${BTXKERN}\n"
+"\tbtxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \\\n"
+"\t -o ${.TARGET} -P 1 boot2.bin\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:815
+#, no-wrap
+msgid ""
+" boot2.ldr:\n"
+"\t${DD} if=/dev/zero of=${.TARGET} bs=512 count=1\n"
+msgstr ""
+" boot2.ldr:\n"
+"\t${DD} if=/dev/zero of=${.TARGET} bs=512 count=1\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:818
+#, no-wrap
+msgid ""
+" boot2.bin: boot2.out\n"
+"\t${OBJCOPY} -S -O binary boot2.out ${.TARGET}\n"
+msgstr ""
+" boot2.bin: boot2.out\n"
+"\t${OBJCOPY} -S -O binary boot2.out ${.TARGET}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:821
+#, no-wrap
+msgid ""
+" boot2.out: ${BTXCRT} boot2.o sio.o ashldi3.o\n"
+"\t${LD} ${LD_FLAGS} --defsym ORG=${ORG2} -T ${LDSCRIPT} -o ${.TARGET} ${.ALLSRC}\n"
+msgstr ""
+" boot2.out: ${BTXCRT} boot2.o sio.o ashldi3.o\n"
+"\t${LD} ${LD_FLAGS} --defsym ORG=${ORG2} -T ${LDSCRIPT} -o ${.TARGET} ${.ALLSRC}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:828
+#, no-wrap
+msgid ""
+" boot2.h: boot1.out\n"
+"\t${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \\\n"
+"\t { x = $$1 - ORG1; \\\n"
+"\t printf(\"#define XREADORG %#x\\n\", REL1 + x) }' \\\n"
+"\t ORG1=`printf \"%d\" ${ORG1}` \\\n"
+"\t REL1=`printf \"%d\" ${REL1}` > ${.TARGET}\n"
+msgstr ""
+" boot2.h: boot1.out\n"
+"\t${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \\\n"
+"\t { x = $$1 - ORG1; \\\n"
+"\t printf(\"#define XREADORG %#x\\n\", REL1 + x) }' \\\n"
+"\t ORG1=`printf \"%d\" ${ORG1}` \\\n"
+"\t REL1=`printf \"%d\" ${REL1}` > ${.TARGET}\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:830
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot2]]"
+msgstr "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot2]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:834
+msgid ""
+"The mechanism for building [.filename]#boot2# is far more elaborate. Let us "
+"point out the most relevant facts. The dependency list is as follows:"
+msgstr ""
+"Механизм сборки [.filename]#boot2# гораздо сложнее. Отметим наиболее важные "
+"моменты. Список зависимостей выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:842
+#, no-wrap
+msgid ""
+" boot2: boot2.ld\n"
+" boot2.ld: boot2.ldr boot2.bin ${BTXDIR}\n"
+" boot2.bin: boot2.out\n"
+" boot2.out: ${BTXDIR} boot2.o sio.o ashldi3.o\n"
+" boot2.h: boot1.out\n"
+msgstr ""
+" boot2: boot2.ld\n"
+" boot2.ld: boot2.ldr boot2.bin ${BTXDIR}\n"
+" boot2.bin: boot2.out\n"
+" boot2.out: ${BTXDIR} boot2.o sio.o ashldi3.o\n"
+" boot2.h: boot1.out\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:844
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot2-more]]"
+msgstr "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot2-more]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:847
+msgid ""
+"Note that initially there is no header file [.filename]#boot2.h#, but its "
+"creation depends on [.filename]#boot1.out#, which we already have. The rule "
+"for its creation is a bit terse, but the important thing is that the output, "
+"[.filename]#boot2.h#, is something like this:"
+msgstr ""
+"Отметим, что изначально файл заголовка [.filename]#boot2.h# отсутствует, но "
+"его создание зависит от [.filename]#boot1.out#, который у нас уже есть. "
+"Правило его создания немного лаконично, но важно то, что результат, "
+"[.filename]#boot2.h#, выглядит примерно так:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:851
+#, no-wrap
+msgid "#define XREADORG 0x725\n"
+msgstr "#define XREADORG 0x725\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:853
+#, no-wrap
+msgid "[.filename]#stand/i386/boot2/boot2.h# [[boot-boot1-make-boot2h]]"
+msgstr "[.filename]#stand/i386/boot2/boot2.h# [[boot-boot1-make-boot2h]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:859
+msgid ""
+"Recall that [.filename]#boot1# was relocated (i.e., copied from `0x7c00` to "
+"`0x700`). This relocation will now make sense, because as we will see, the "
+"BTX server reclaims some memory, including the space where "
+"[.filename]#boot1# was originally loaded. However, the BTX server needs "
+"access to [.filename]#boot1#'s `xread` function; this function, according to "
+"the output of [.filename]#boot2.h#, is at location `0x725`. Indeed, the BTX "
+"server uses the `xread` function from [.filename]#boot1#'s relocated code. "
+"This function is now accessible from within the [.filename]#boot2# client."
+msgstr ""
+"Напомним, что [.filename]#boot1# был перемещён (т.е. скопирован из `0x7c00` "
+"в `0x700`). Это перемещение теперь обретает смысл, потому что, как мы "
+"увидим, сервер BTX освобождает часть памяти, включая область, куда "
+"[.filename]#boot1# был изначально загружен. Однако серверу BTX необходим "
+"доступ к функции `xread` из [.filename]#boot1#; согласно выводу "
+"[.filename]#boot2.h#, эта функция находится по адресу `0x725`. "
+"Действительно, сервер BTX использует функцию `xread` из перемещённого кода "
+"[.filename]#boot1#. Теперь эта функция доступна из клиента "
+"[.filename]#boot2#."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:868
+msgid ""
+"The next rule directs the linker to link various files "
+"([.filename]#ashldi3.o#, [.filename]#boot2.o# and [.filename]#sio.o#). Note "
+"that the output file, [.filename]#boot2.out#, is linked to execute at "
+"address `0x2000` (${ORG2}). Recall that [.filename]#boot2# will be executed "
+"in user mode, within a special user segment set up by the BTX server. This "
+"segment starts at `0xa000`. Also, remember that the [.filename]#boot2# "
+"portion of [.filename]#boot# was copied to address `0xc000`, that is, offset "
+"`0x2000` from the start of the user segment, so [.filename]#boot2# will work "
+"properly when we transfer control to it. Next, [.filename]#boot2.bin# is "
+"created from [.filename]#boot2.out# by stripping its symbols and format "
+"information; boot2.bin is a _raw_ binary. Now, note that a file "
+"[.filename]#boot2.ldr# is created as a 512-byte file full of zeros. This "
+"space is reserved for the bsdlabel."
+msgstr ""
+"Следующее правило указывает компоновщику на необходимость связать различные "
+"файлы ([.filename]#ashldi3.o#, [.filename]#boot2.o# и [.filename]#sio.o#). "
+"Обратите внимание, что выходной файл [.filename]#boot2.out# компонуется для "
+"выполнения по адресу `0x2000` (${ORG2}). Напомним, что [.filename]#boot2# "
+"будет выполняться в пользовательском режиме внутри специального "
+"пользовательского сегмента, созданного сервером BTX. Этот сегмент начинается "
+"с адреса `0xa000`. Также помните, что часть [.filename]#boot2# в "
+"[.filename]#boot# была скопирована по адресу `0xc000`, то есть со смещением "
+"`0x2000` от начала пользовательского сегмента, поэтому [.filename]#boot2# "
+"будет работать корректно при передаче управления на него. Далее, "
+"[.filename]#boot2.bin# создается из [.filename]#boot2.out# путем удаления "
+"символов и информации о формате; boot2.bin представляет собой _сырой_ "
+"бинарный файл. Теперь обратите внимание, что файл [.filename]#boot2.ldr# "
+"создается как 512-байтный файл, заполненный нулями. Это пространство "
+"зарезервировано для bsdlabel."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:873
+msgid ""
+"Now that we have files [.filename]#boot1#, [.filename]#boot2.bin# and "
+"[.filename]#boot2.ldr#, only the BTX server is missing before creating the "
+"all-in-one [.filename]#boot# file. The BTX server is located in "
+"[.filename]#stand/i386/btx/btx#; it has its own [.filename]#Makefile# with "
+"its own set of rules for building. The important thing to notice is that it "
+"is also compiled as a _raw_ binary, and that it is linked to execute at "
+"address `0x9000`. The details can be found in [.filename]#stand/i386/btx/"
+"btx/Makefile#."
+msgstr ""
+"Теперь, когда у нас есть файлы [.filename]#boot1#, [.filename]#boot2.bin# и "
+"[.filename]#boot2.ldr#, осталось только добавить сервер BTX перед созданием "
+"универсального файла [.filename]#boot#. Сервер BTX находится в "
+"[.filename]#stand/i386/btx/btx#; у него есть собственный "
+"[.filename]#Makefile# со своим набором правил для сборки. Важно отметить, "
+"что он также компилируется как _сырой_ бинарный файл и линкуется для "
+"выполнения по адресу `0x9000`. Подробности можно найти в [.filename]#stand/"
+"i386/btx/btx/Makefile#."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:880
+msgid ""
+"Having the files that comprise the [.filename]#boot# program, the final step "
+"is to _merge_ them. This is done by a special program called "
+"[.filename]#btxld# (source located in [.filename]#/usr/src/usr.sbin/"
+"btxld#). Some arguments to this program include the name of the output file "
+"([.filename]#boot#), its entry point (`0x2000`) and its file format (raw "
+"binary). The various files are finally merged by this utility into the file "
+"[.filename]#boot#, which consists of [.filename]#boot1#, [.filename]#boot2#, "
+"the `bsdlabel` and the BTX server. This file, which takes exactly 16 "
+"sectors, or 8192 bytes, is what is actually written to the beginning of the "
+"FreeBSD slice during installation. Let us now proceed to study the BTX "
+"server program."
+msgstr ""
+"Имея файлы, составляющие программу [.filename]#boot#, последним шагом "
+"является их _объединение_. Это выполняется специальной программой под "
+"названием [.filename]#btxld# (исходный код расположен в [.filename]#/usr/src/"
+"usr.sbin/btxld#). Некоторые аргументы этой программы включают имя выходного "
+"файла ([.filename]#boot#), его точку входа (`0x2000`) и формат файла "
+"(бинарный). Различные файлы окончательно объединяются этой утилитой в файл "
+"[.filename]#boot#, который состоит из [.filename]#boot1#, "
+"[.filename]#boot2#, `bsdlabel` и сервера BTX. Этот файл, занимающий ровно 16 "
+"секторов или 8192 байта, записывается в начало раздела FreeBSD во время "
+"установки. Теперь перейдем к изучению программы сервера BTX."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:883
+msgid ""
+"The BTX server prepares a simple environment and switches from 16-bit real "
+"mode to 32-bit protected mode, right before passing control to the client. "
+"This includes initializing and updating the following data structures:"
+msgstr ""
+"Сервер BTX подготавливает простое окружение и переключается из 16-битного "
+"реального режима в 32-битный защищённый режим, непосредственно перед "
+"передачей управления клиенту. Это включает инициализацию и обновление "
+"следующих структур данных:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:886
+msgid ""
+"Modifies the `Interrupt Vector Table (IVT)`. The IVT provides exception and "
+"interrupt handlers for Real-Mode code."
+msgstr ""
+"Изменяет `Таблицу Векторов Прерываний (IVT)`. IVT предоставляет обработчики "
+"исключений и прерываний для кода в Реальном Режиме."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:889
+msgid ""
+"The `Interrupt Descriptor Table (IDT)` is created. Entries are provided for "
+"processor exceptions, hardware interrupts, two system calls and V86 "
+"interface. The IDT provides exception and interrupt handlers for Protected-"
+"Mode code."
+msgstr ""
+"Создается `Таблица дескрипторов прерываний (IDT)`. В ней предусмотрены "
+"записи для исключений процессора, аппаратных прерываний, двух системных "
+"вызовов и интерфейса V86. IDT предоставляет обработчики исключений и "
+"прерываний для кода в защищенном режиме."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:891
+msgid ""
+"A `Task-State Segment (TSS)` is created. This is necessary because the "
+"processor works in the _least_ privileged level when executing the client "
+"([.filename]#boot2#), but in the _most_ privileged level when executing the "
+"BTX server."
+msgstr ""
+"Создается `Сегмент состояния задачи (TSS)`. Это необходимо, потому что "
+"процессор работает на _наименее_ привилегированном уровне при выполнении "
+"клиента ([.filename]#boot2#), но на _наиболее_ привилегированном уровне при "
+"выполнении сервера BTX."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:894
+msgid ""
+"The GDT (Global Descriptor Table) is set up. Entries (descriptors) are "
+"provided for supervisor code and data, user code and data, and real-mode "
+"code and data. footnote:[Real-mode code and data are necessary when "
+"switching back to real mode from protected mode, as suggested by the Intel "
+"manuals.]"
+msgstr ""
+"Устанавливается GDT (Глобальная Таблица Дескрипторов). Создаются записи "
+"(дескрипторы) для кода и данных супервизора, кода и данных пользователя, а "
+"также кода и данных реального режима. footnote:[Код и данные реального "
+"режима необходимы при переключении обратно в реальный режим из защищённого "
+"режима, как указано в руководствах Intel.]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:899
+msgid ""
+"Let us now start studying the actual implementation. Recall that "
+"[.filename]#boot1# made a jump to address `0x9010`, the BTX server's entry "
+"point. Before studying program execution there, note that the BTX server "
+"has a special header at address range `0x9000-0x900f`, right before its "
+"entry point. This header is defined as follows:"
+msgstr ""
+"Приступим к изучению фактической реализации. Напомним, что "
+"[.filename]#boot1# выполнил переход на адрес `0x9010` — точку входа сервера "
+"BTX. Прежде чем изучать выполнение программы там, обратите внимание, что "
+"сервер BTX имеет специальный заголовок в диапазоне адресов `0x9000-0x900f`, "
+"непосредственно перед точкой входа. Этот заголовок определён следующим "
+"образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:915
+#, no-wrap
+msgid ""
+"start:\t\t\t\t\t\t# Start of code\n"
+"/*\n"
+" * BTX header.\n"
+" */\n"
+"btx_hdr:\t.byte 0xeb\t\t\t# Machine ID\n"
+"\t\t.byte 0xe\t\t\t# Header size\n"
+"\t\t.ascii \"BTX\"\t\t\t# Magic\n"
+"\t\t.byte 0x1\t\t\t# Major version\n"
+"\t\t.byte 0x2\t\t\t# Minor version\n"
+"\t\t.byte BTX_FLAGS\t\t\t# Flags\n"
+"\t\t.word PAG_CNT-MEM_ORG>>0xc\t# Paging control\n"
+"\t\t.word break-start\t\t# Text size\n"
+"\t\t.long 0x0\t\t\t# Entry address\n"
+msgstr ""
+"start:\t\t\t\t\t\t# Start of code\n"
+"/*\n"
+" * BTX header.\n"
+" */\n"
+"btx_hdr:\t.byte 0xeb\t\t\t# Machine ID\n"
+"\t\t.byte 0xe\t\t\t# Header size\n"
+"\t\t.ascii \"BTX\"\t\t\t# Magic\n"
+"\t\t.byte 0x1\t\t\t# Major version\n"
+"\t\t.byte 0x2\t\t\t# Minor version\n"
+"\t\t.byte BTX_FLAGS\t\t\t# Flags\n"
+"\t\t.word PAG_CNT-MEM_ORG>>0xc\t# Paging control\n"
+"\t\t.word break-start\t\t# Text size\n"
+"\t\t.long 0x0\t\t\t# Entry address\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:917
+#, no-wrap
+msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-header]]"
+msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-header]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:922
+msgid ""
+"Note the first two bytes are `0xeb` and `0xe`. In the IA-32 architecture, "
+"these two bytes are interpreted as a relative jump past the header into the "
+"entry point, so in theory, [.filename]#boot1# could jump here (address "
+"`0x9000`) instead of address `0x9010`. Note that the last field in the BTX "
+"header is a pointer to the client's ([.filename]#boot2#) entry pointb2. "
+"This field is patched at link time."
+msgstr ""
+"Обратите внимание, что первые два байта — это `0xeb` и `0xe`. В архитектуре "
+"IA-32 эти два байта интерпретируются как относительный переход за заголовок "
+"к точке входа, поэтому теоретически [.filename]#boot1# мог бы перейти сюда "
+"(адрес `0x9000`) вместо адреса `0x9010`. Обратите внимание, что последнее "
+"поле в заголовке BTX — это указатель на точку входа клиента "
+"([.filename]#boot2#)b2. Это поле исправляется во время компоновки."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:924
+msgid "Immediately following the header is the BTX server's entry point:"
+msgstr "Сразу после заголовка следует точка входа сервера BTX:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:938
+#, no-wrap
+msgid ""
+"/*\n"
+" * Initialization routine.\n"
+" */\n"
+"init:\t\tcli\t\t\t\t# Disable interrupts\n"
+"\t\txor %ax,%ax\t\t\t# Zero/segment\n"
+"\t\tmov %ax,%ss\t\t\t# Set up\n"
+"\t\tmov $MEM_ESP0,%sp\t\t# stack\n"
+"\t\tmov %ax,%es\t\t\t# Address\n"
+"\t\tmov %ax,%ds\t\t\t# data\n"
+"\t\tpushl $0x2\t\t\t# Clear\n"
+"\t\tpopfl\t\t\t\t# flags\n"
+msgstr ""
+"/*\n"
+" * Initialization routine.\n"
+" */\n"
+"init:\t\tcli\t\t\t\t# Disable interrupts\n"
+"\t\txor %ax,%ax\t\t\t# Zero/segment\n"
+"\t\tmov %ax,%ss\t\t\t# Set up\n"
+"\t\tmov $MEM_ESP0,%sp\t\t# stack\n"
+"\t\tmov %ax,%es\t\t\t# Address\n"
+"\t\tmov %ax,%ds\t\t\t# data\n"
+"\t\tpushl $0x2\t\t\t# Clear\n"
+"\t\tpopfl\t\t\t\t# flags\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:940
+#, no-wrap
+msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-init]]"
+msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-init]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:944
+msgid ""
+"This code disables interrupts, sets up a working stack (starting at address "
+"`0x1800`) and clears the flags in the EFLAGS register. Note that the "
+"`popfl` instruction pops out a doubleword (4 bytes) from the stack and "
+"places it in the EFLAGS register. As the value actually popped is `2`, the "
+"EFLAGS register is effectively cleared (IA-32 requires that bit 2 of the "
+"EFLAGS register always be 1)."
+msgstr ""
+"Этот код отключает прерывания, устанавливает рабочий стек (начиная с адреса "
+"`0x1800`) и очищает флаги в регистре EFLAGS. Обратите внимание, что "
+"инструкция `popfl` извлекает двойное слово (4 байта) из стека и помещает его "
+"в регистр EFLAGS. Поскольку извлекаемое значение фактически равно `2`, "
+"регистр EFLAGS эффективно очищается (IA-32 требует, чтобы бит 2 регистра "
+"EFLAGS всегда был равен 1)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:947
+msgid ""
+"Our next code block clears (sets to `0`) the memory range `0x5e00-0x8fff`. "
+"This range is where the various data structures will be created:"
+msgstr ""
+"Следующий блок кода очищает (устанавливает в `0`) диапазон памяти "
+"`0x5e00-0x8fff`. В этом диапазоне будут созданы различные структуры данных:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:957
+#, no-wrap
+msgid ""
+"/*\n"
+" * Initialize memory.\n"
+" */\n"
+"\t\tmov $MEM_IDT,%di\t\t# Memory to initialize\n"
+"\t\tmov $(MEM_ORG-MEM_IDT)/2,%cx\t# Words to zero\n"
+"\t\trep\t\t\t\t# Zero-fill\n"
+"\t\tstosw\t\t\t\t# memory\n"
+msgstr ""
+"/*\n"
+" * Initialize memory.\n"
+" */\n"
+"\t\tmov $MEM_IDT,%di\t\t# Memory to initialize\n"
+"\t\tmov $(MEM_ORG-MEM_IDT)/2,%cx\t# Words to zero\n"
+"\t\trep\t\t\t\t# Zero-fill\n"
+"\t\tstosw\t\t\t\t# memory\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:959
+#, no-wrap
+msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-clear-mem]]"
+msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-clear-mem]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:962
+msgid ""
+"Recall that [.filename]#boot1# was originally loaded to address `0x7c00`, "
+"so, with this memory initialization, that copy effectively disappeared. "
+"However, also recall that [.filename]#boot1# was relocated to `0x700`, so "
+"_that_ copy is still in memory, and the BTX server will make use of it."
+msgstr ""
+"Напомним, что [.filename]#boot1# изначально загружался по адресу `0x7c00`, "
+"поэтому при такой инициализации памяти эта копия фактически исчезла. Однако "
+"также напомним, что [.filename]#boot1# был перемещён на адрес `0x700`, "
+"поэтому _эта_ копия всё ещё находится в памяти, и сервер BTX будет её "
+"использовать."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:970
+msgid ""
+"Next, the real-mode IVT (Interrupt Vector Table is updated. The IVT is an "
+"array of segment/offset pairs for exception and interrupt handlers. The "
+"BIOS normally maps hardware interrupts to interrupt vectors `0x8` to `0xf` "
+"and `0x70` to `0x77` but, as will be seen, the 8259A Programmable Interrupt "
+"Controller, the chip controlling the actual mapping of hardware interrupts "
+"to interrupt vectors, is programmed to remap these interrupt vectors from "
+"`0x8-0xf` to `0x20-0x27` and from `0x70-0x77` to `0x28-0x2f`. Thus, "
+"interrupt handlers are provided for interrupt vectors `0x20-0x2f`. The "
+"reason the BIOS-provided handlers are not used directly is because they work "
+"in 16-bit real mode, but not 32-bit protected mode. Processor mode will be "
+"switched to 32-bit protected mode shortly. However, the BTX server sets up "
+"a mechanism to effectively use the handlers provided by the BIOS:"
+msgstr ""
+"Далее обновляется таблица векторов прерываний (IVT) в реальном режиме. IVT "
+"представляет собой массив пар сегмент/смещение для обработчиков исключений и "
+"прерываний. BIOS обычно сопоставляет аппаратные прерывания с векторами "
+"прерываний `0x8`–`0xf` и `0x70`–`0x77`, но, как будет показано, "
+"программируемый контроллер прерываний 8259A, микросхема, управляющая "
+"фактическим сопоставлением аппаратных прерываний с векторами прерываний, "
+"программируется для переназначения этих векторов прерываний с `0x8`–`0xf` на "
+"`0x20`–`0x27` и с `0x70`–`0x77` на `0x28`–`0x2f`. Таким образом, обработчики "
+"прерываний предоставляются для векторов прерываний `0x20`–`0x2f`. Причина, "
+"по которой обработчики, предоставляемые BIOS, не используются напрямую, "
+"заключается в том, что они работают в 16-битном реальном режиме, но не в 32-"
+"битном защищённом режиме. Вскоре будет выполнен переход в 32-битный "
+"защищённый режим. Однако сервер BTX настраивает механизм для эффективного "
+"использования обработчиков, предоставляемых BIOS:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:985
+#, no-wrap
+msgid ""
+"/*\n"
+" * Update real mode IDT for reflecting hardware interrupts.\n"
+" */\n"
+"\t\tmov $intr20,%bx\t\t\t# Address first handler\n"
+"\t\tmov $0x10,%cx\t\t\t# Number of handlers\n"
+"\t\tmov $0x20*4,%di\t\t\t# First real mode IDT entry\n"
+"init.0:\t\tmov %bx,(%di)\t\t\t# Store IP\n"
+"\t\tinc %di\t\t\t\t# Address next\n"
+"\t\tinc %di\t\t\t\t# entry\n"
+"\t\tstosw\t\t\t\t# Store CS\n"
+"\t\tadd $4,%bx\t\t\t# Next handler\n"
+"\t\tloop init.0\t\t\t# Next IRQ\n"
+msgstr ""
+"/*\n"
+" * Update real mode IDT for reflecting hardware interrupts.\n"
+" */\n"
+"\t\tmov $intr20,%bx\t\t\t# Address first handler\n"
+"\t\tmov $0x10,%cx\t\t\t# Number of handlers\n"
+"\t\tmov $0x20*4,%di\t\t\t# First real mode IDT entry\n"
+"init.0:\t\tmov %bx,(%di)\t\t\t# Store IP\n"
+"\t\tinc %di\t\t\t\t# Address next\n"
+"\t\tinc %di\t\t\t\t# entry\n"
+"\t\tstosw\t\t\t\t# Store CS\n"
+"\t\tadd $4,%bx\t\t\t# Next handler\n"
+"\t\tloop init.0\t\t\t# Next IRQ\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:987
+#, no-wrap
+msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-ivt]]"
+msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-ivt]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:992
+msgid ""
+"The next block creates the IDT (Interrupt Descriptor Table). The IDT is "
+"analogous, in protected mode, to the IVT in real mode. That is, the IDT "
+"describes the various exception and interrupt handlers used when the "
+"processor is executing in protected mode. In essence, it also consists of "
+"an array of segment/offset pairs, although the structure is somewhat more "
+"complex, because segments in protected mode are different than in real mode, "
+"and various protection mechanisms apply:"
+msgstr ""
+"Следующий блок создает IDT (таблицу дескрипторов прерываний). IDT в "
+"защищенном режиме аналогична IVT в реальном режиме. То есть, IDT описывает "
+"различные обработчики исключений и прерываний, используемые, когда процессор "
+"работает в защищенном режиме. По сути, она также состоит из массива пар "
+"сегмент/смещение, хотя структура несколько сложнее, поскольку сегменты в "
+"защищенном режиме отличаются от реального режима, и применяются различные "
+"механизмы защиты:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1019
+#, no-wrap
+msgid ""
+"/*\n"
+" * Create IDT.\n"
+" */\n"
+"\t\tmov $MEM_IDT,%di\t\t# IDT's address\n"
+"\t\tmov $idtctl,%si\t\t\t# Control string\n"
+"init.1:\t\tlodsb\t\t\t\t# Get entry\n"
+"\t\tcbw\t\t\t\t# count\n"
+"\t\txchg %ax,%cx\t\t\t# as word\n"
+"\t\tjcxz init.4\t\t\t# If done\n"
+"\t\tlodsb\t\t\t\t# Get segment\n"
+"\t\txchg %ax,%dx\t\t\t# P:DPL:type\n"
+"\t\tlodsw\t\t\t\t# Get control\n"
+"\t\txchg %ax,%bx\t\t\t# set\n"
+"\t\tlodsw\t\t\t\t# Get handler offset\n"
+"\t\tmov $SEL_SCODE,%dh\t\t# Segment selector\n"
+"init.2:\t\tshr %bx\t\t\t\t# Handle this int?\n"
+"\t\tjnc init.3\t\t\t# No\n"
+"\t\tmov %ax,(%di)\t\t\t# Set handler offset\n"
+"\t\tmov %dh,0x2(%di)\t\t# and selector\n"
+"\t\tmov %dl,0x5(%di)\t\t# Set P:DPL:type\n"
+"\t\tadd $0x4,%ax\t\t\t# Next handler\n"
+"init.3:\t\tlea 0x8(%di),%di\t\t# Next entry\n"
+"\t\tloop init.2\t\t\t# Till set done\n"
+"\t\tjmp init.1\t\t\t# Continue\n"
+msgstr ""
+"/*\n"
+" * Create IDT.\n"
+" */\n"
+"\t\tmov $MEM_IDT,%di\t\t# IDT's address\n"
+"\t\tmov $idtctl,%si\t\t\t# Control string\n"
+"init.1:\t\tlodsb\t\t\t\t# Get entry\n"
+"\t\tcbw\t\t\t\t# count\n"
+"\t\txchg %ax,%cx\t\t\t# as word\n"
+"\t\tjcxz init.4\t\t\t# If done\n"
+"\t\tlodsb\t\t\t\t# Get segment\n"
+"\t\txchg %ax,%dx\t\t\t# P:DPL:type\n"
+"\t\tlodsw\t\t\t\t# Get control\n"
+"\t\txchg %ax,%bx\t\t\t# set\n"
+"\t\tlodsw\t\t\t\t# Get handler offset\n"
+"\t\tmov $SEL_SCODE,%dh\t\t# Segment selector\n"
+"init.2:\t\tshr %bx\t\t\t\t# Handle this int?\n"
+"\t\tjnc init.3\t\t\t# No\n"
+"\t\tmov %ax,(%di)\t\t\t# Set handler offset\n"
+"\t\tmov %dh,0x2(%di)\t\t# and selector\n"
+"\t\tmov %dl,0x5(%di)\t\t# Set P:DPL:type\n"
+"\t\tadd $0x4,%ax\t\t\t# Next handler\n"
+"init.3:\t\tlea 0x8(%di),%di\t\t# Next entry\n"
+"\t\tloop init.2\t\t\t# Till set done\n"
+"\t\tjmp init.1\t\t\t# Continue\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1021
+#, no-wrap
+msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-idt]]"
+msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-idt]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1029
+msgid ""
+"Each entry in the `IDT` is 8 bytes long. Besides the segment/offset "
+"information, they also describe the segment type, privilege level, and "
+"whether the segment is present in memory or not. The construction is such "
+"that interrupt vectors from `0` to `0xf` (exceptions) are handled by "
+"function `intx00`; vector `0x10` (also an exception) is handled by `intx10`; "
+"hardware interrupts, which are later configured to start at interrupt vector "
+"`0x20` all the way to interrupt vector `0x2f`, are handled by function "
+"`intx20`. Lastly, interrupt vector `0x30`, which is used for system calls, "
+"is handled by `intx30`, and vectors `0x31` and `0x32` are handled by "
+"`intx31`. It must be noted that only descriptors for interrupt vectors "
+"`0x30`, `0x31` and `0x32` are given privilege level 3, the same privilege "
+"level as the [.filename]#boot2# client, which means the client can execute a "
+"software-generated interrupt to this vectors through the `int` instruction "
+"without failing (this is the way [.filename]#boot2# use the services "
+"provided by the BTX server). Also, note that _only_ software-generated "
+"interrupts are protected from code executing in lesser privilege levels. "
+"Hardware-generated interrupts and processor-generated exceptions are "
+"_always_ handled adequately, regardless of the actual privileges involved."
+msgstr ""
+"Каждая запись в `IDT` имеет длину 8 байт. Помимо информации о сегменте/"
+"смещении, они также описывают тип сегмента, уровень привилегий и "
+"присутствует ли сегмент в памяти. Структура организована так, что векторы "
+"прерываний от `0` до `0xf` (исключения) обрабатываются функцией `intx00`; "
+"вектор `0x10` (также исключение) обрабатывается `intx10`; аппаратные "
+"прерывания, которые позже настраиваются начиная с вектора `0x20` и до "
+"вектора `0x2f`, обрабатываются функцией `intx20`. Наконец, вектор прерывания "
+"`0x30`, используемый для системных вызовов, обрабатывается `intx30`, а "
+"векторы `0x31` и `0x32` обрабатываются `intx31`. Необходимо отметить, что "
+"только дескрипторы для векторов прерываний `0x30`, `0x31` и `0x32` имеют "
+"уровень привилегий 3, такой же, как у клиента [.filename]#boot2#, что "
+"означает, что клиент может выполнить программно-генерируемое прерывание к "
+"этим векторам через инструкцию `int` без ошибки (это способ, которым "
+"[.filename]#boot2# использует сервисы, предоставляемые сервером BTX). Также "
+"обратите внимание, что _только_ программно-генерируемые прерывания защищены "
+"от кода, выполняющегося на более низких уровнях привилегий. Аппаратно-"
+"генерируемые прерывания и исключения, генерируемые процессором, _всегда_ "
+"обрабатываются корректно, независимо от фактических привилегий."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1034
+msgid ""
+"The next step is to initialize the TSS (Task-State Segment). The TSS is a "
+"hardware feature that helps the operating system or executive software "
+"implement multitasking functionality through process abstraction. The IA-32 "
+"architecture demands the creation and use of _at least_ one TSS if "
+"multitasking facilities are used or different privilege levels are defined. "
+"Since the [.filename]#boot2# client is executed in privilege level 3, but "
+"the BTX server runs in privilege level 0, a TSS must be defined:"
+msgstr ""
+"Следующий шаг — инициализация TSS (сегмента состояния задачи). TSS — это "
+"аппаратная функция, которая помогает операционной системе или "
+"исполнительному ПО реализовать многозадачность через абстракцию процессов. "
+"Архитектура IA-32 требует создания и использования _как минимум_ одного TSS, "
+"если используются механизмы многозадачности или определены различные уровни "
+"привилегий. Поскольку клиент [.filename]#boot2# выполняется на уровне "
+"привилегий 3, а сервер BTX работает на уровне привилегий 0, необходимо "
+"определить TSS:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1043
+#, no-wrap
+msgid ""
+"/*\n"
+" * Initialize TSS.\n"
+" */\n"
+"init.4:\t\tmovb $_ESP0H,TSS_ESP0+1(%di)\t# Set ESP0\n"
+"\t\tmovb $SEL_SDATA,TSS_SS0(%di)\t# Set SS0\n"
+"\t\tmovb $_TSSIO,TSS_MAP(%di)\t# Set I/O bit map base\n"
+msgstr ""
+"/*\n"
+" * Initialize TSS.\n"
+" */\n"
+"init.4:\t\tmovb $_ESP0H,TSS_ESP0+1(%di)\t# Set ESP0\n"
+"\t\tmovb $SEL_SDATA,TSS_SS0(%di)\t# Set SS0\n"
+"\t\tmovb $_TSSIO,TSS_MAP(%di)\t# Set I/O bit map base\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1045
+#, no-wrap
+msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-tss]]"
+msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-tss]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1049
+msgid ""
+"Note that a value is given for the Privilege Level 0 stack pointer and stack "
+"segment in the TSS. This is needed because, if an interrupt or exception is "
+"received while executing [.filename]#boot2# in Privilege Level 3, a change "
+"to Privilege Level 0 is automatically performed by the processor, so a new "
+"working stack is needed. Finally, the I/O Map Base Address field of the TSS "
+"is given a value, which is a 16-bit offset from the beginning of the TSS to "
+"the I/O Permission Bitmap and the Interrupt Redirection Bitmap."
+msgstr ""
+"Обратите внимание, что в TSS указано значение для указателя стека и сегмента "
+"стека уровня привилегий 0. Это необходимо, потому что если прерывание или "
+"исключение получено во время выполнения [.filename]#boot2# на уровне "
+"привилегий 3, процессор автоматически переключается на уровень привилегий 0, "
+"поэтому требуется новый рабочий стек. Наконец, полю базового адреса карты "
+"ввода-вывода TSS присваивается значение, которое представляет собой 16-"
+"битное смещение от начала TSS до битовой карты разрешений ввода-вывода и "
+"битовой карты перенаправления прерываний."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1052
+msgid ""
+"After the IDT and TSS are created, the processor is ready to switch to "
+"protected mode. This is done in the next block:"
+msgstr ""
+"После создания IDT и TSS процессор готов к переходу в защищённый режим. Это "
+"выполняется в следующем блоке:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1070
+#, no-wrap
+msgid ""
+"/*\n"
+" * Bring up the system.\n"
+" */\n"
+"\t\tmov $0x2820,%bx\t\t\t# Set protected mode\n"
+"\t\tcallw setpic\t\t\t# IRQ offsets\n"
+"\t\tlidt idtdesc\t\t\t# Set IDT\n"
+"\t\tlgdt gdtdesc\t\t\t# Set GDT\n"
+"\t\tmov %cr0,%eax\t\t\t# Switch to protected\n"
+"\t\tinc %ax\t\t\t\t# mode\n"
+"\t\tmov %eax,%cr0\t\t\t#\n"
+"\t\tljmp $SEL_SCODE,$init.8\t\t# To 32-bit code\n"
+"\t\t.code32\n"
+"init.8:\t\txorl %ecx,%ecx\t\t\t# Zero\n"
+"\t\tmovb $SEL_SDATA,%cl\t\t# To 32-bit\n"
+"\t\tmovw %cx,%ss\t\t\t# stack\n"
+msgstr ""
+"/*\n"
+" * Bring up the system.\n"
+" */\n"
+"\t\tmov $0x2820,%bx\t\t\t# Set protected mode\n"
+"\t\tcallw setpic\t\t\t# IRQ offsets\n"
+"\t\tlidt idtdesc\t\t\t# Set IDT\n"
+"\t\tlgdt gdtdesc\t\t\t# Set GDT\n"
+"\t\tmov %cr0,%eax\t\t\t# Switch to protected\n"
+"\t\tinc %ax\t\t\t\t# mode\n"
+"\t\tmov %eax,%cr0\t\t\t#\n"
+"\t\tljmp $SEL_SCODE,$init.8\t\t# To 32-bit code\n"
+"\t\t.code32\n"
+"init.8:\t\txorl %ecx,%ecx\t\t\t# Zero\n"
+"\t\tmovb $SEL_SDATA,%cl\t\t# To 32-bit\n"
+"\t\tmovw %cx,%ss\t\t\t# stack\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1072
+#, no-wrap
+msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-prot]]"
+msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-prot]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1085
+msgid ""
+"First, a call is made to `setpic` to program the 8259A PIC (Programmable "
+"Interrupt Controller). This chip is connected to multiple hardware "
+"interrupt sources. Upon receiving an interrupt from a device, it signals "
+"the processor with the appropriate interrupt vector. This can be customized "
+"so that specific interrupts are associated with specific interrupt vectors, "
+"as explained before. Next, the IDTR (Interrupt Descriptor Table Register) "
+"and GDTR (Global Descriptor Table Register) are loaded with the instructions "
+"`lidt` and `lgdt`, respectively. These registers are loaded with the base "
+"address and limit address for the IDT and GDT. The following three "
+"instructions set the Protection Enable (PE) bit of the `%cr0` register. "
+"This effectively switches the processor to 32-bit protected mode. Next, a "
+"long jump is made to `init.8` using segment selector SEL_SCODE, which "
+"selects the Supervisor Code Segment. The processor is effectively executing "
+"in CPL 0, the most privileged level, after this jump. Finally, the "
+"Supervisor Data Segment is selected for the stack by assigning the segment "
+"selector SEL_SDATA to the `%ss` register. This data segment also has a "
+"privilege level of `0`."
+msgstr ""
+"Сначала вызывается `setpic` для программирования 8259A PIC (программируемого "
+"контроллера прерываний). Этот чип подключен к нескольким источникам "
+"аппаратных прерываний. При получении прерывания от устройства он "
+"сигнализирует процессору соответствующим вектором прерывания. Это можно "
+"настроить так, чтобы определенные прерывания были связаны с конкретными "
+"векторами прерываний, как объяснялось ранее. Затем регистры IDTR (Interrupt "
+"Descriptor Table Register) и GDTR (Global Descriptor Table Register) "
+"загружаются инструкциями `lidt` и `lgdt` соответственно. Эти регистры "
+"загружаются базовым адресом и предельным адресом для IDT и GDT. Следующие "
+"три инструкции устанавливают бит Protection Enable (PE) в регистре `%cr0`. "
+"Это фактически переключает процессор в 32-битный защищенный режим. Затем "
+"выполняется дальний переход на `init.8` с использованием селектора сегмента "
+"SEL_SCODE, который выбирает сегмент кода супервизора (Supervisor Code "
+"Segment). После этого перехода процессор фактически работает на уровне CPL 0 "
+"— наиболее привилегированном уровне. Наконец, для стека выбирается сегмент "
+"данных супервизора (Supervisor Data Segment) путем присвоения селектора "
+"сегмента SEL_SDATA регистру `%ss`. Этот сегмент данных также имеет уровень "
+"привилегий `0`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1087
+msgid ""
+"Our last code block is responsible for loading the TR (Task Register) with "
+"the segment selector for the TSS we created earlier, and setting the User "
+"Mode environment before passing execution control to the [.filename]#boot2# "
+"client."
+msgstr ""
+"Наш последний блок кода отвечает за загрузку TR (Регистра Задач) с "
+"селектором сегмента для TSS, который мы создали ранее, и настройку окружения "
+"пользовательского режима перед передачей управления исполнения клиенту "
+"[.filename]#boot2#."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1123
+#, no-wrap
+msgid ""
+"/*\n"
+" * Launch user task.\n"
+" */\n"
+"\t\tmovb $SEL_TSS,%cl\t\t# Set task\n"
+"\t\tltr %cx\t\t\t\t# register\n"
+"\t\tmovl $MEM_USR,%edx\t\t# User base address\n"
+"\t\tmovzwl %ss:BDA_MEM,%eax\t\t# Get free memory\n"
+"\t\tshll $0xa,%eax\t\t\t# To bytes\n"
+"\t\tsubl $ARGSPACE,%eax\t\t# Less arg space\n"
+"\t\tsubl %edx,%eax\t\t\t# Less base\n"
+"\t\tmovb $SEL_UDATA,%cl\t\t# User data selector\n"
+"\t\tpushl %ecx\t\t\t# Set SS\n"
+"\t\tpushl %eax\t\t\t# Set ESP\n"
+"\t\tpush $0x202\t\t\t# Set flags (IF set)\n"
+"\t\tpush $SEL_UCODE\t\t\t# Set CS\n"
+"\t\tpushl btx_hdr+0xc\t\t# Set EIP\n"
+"\t\tpushl %ecx\t\t\t# Set GS\n"
+"\t\tpushl %ecx\t\t\t# Set FS\n"
+"\t\tpushl %ecx\t\t\t# Set DS\n"
+"\t\tpushl %ecx\t\t\t# Set ES\n"
+"\t\tpushl %edx\t\t\t# Set EAX\n"
+"\t\tmovb $0x7,%cl\t\t\t# Set remaining\n"
+"init.9:\t\tpush $0x0\t\t\t# general\n"
+"\t\tloop init.9\t\t\t# registers\n"
+"#ifdef BTX_SERIAL\n"
+"\t\tcall sio_init\t\t\t# setup the serial console\n"
+"#endif\n"
+"\t\tpopa\t\t\t\t# and initialize\n"
+"\t\tpopl %es\t\t\t# Initialize\n"
+"\t\tpopl %ds\t\t\t# user\n"
+"\t\tpopl %fs\t\t\t# segment\n"
+"\t\tpopl %gs\t\t\t# registers\n"
+"\t\tiret\t\t\t\t# To user mode\n"
+msgstr ""
+"/*\n"
+" * Launch user task.\n"
+" */\n"
+"\t\tmovb $SEL_TSS,%cl\t\t# Set task\n"
+"\t\tltr %cx\t\t\t\t# register\n"
+"\t\tmovl $MEM_USR,%edx\t\t# User base address\n"
+"\t\tmovzwl %ss:BDA_MEM,%eax\t\t# Get free memory\n"
+"\t\tshll $0xa,%eax\t\t\t# To bytes\n"
+"\t\tsubl $ARGSPACE,%eax\t\t# Less arg space\n"
+"\t\tsubl %edx,%eax\t\t\t# Less base\n"
+"\t\tmovb $SEL_UDATA,%cl\t\t# User data selector\n"
+"\t\tpushl %ecx\t\t\t# Set SS\n"
+"\t\tpushl %eax\t\t\t# Set ESP\n"
+"\t\tpush $0x202\t\t\t# Set flags (IF set)\n"
+"\t\tpush $SEL_UCODE\t\t\t# Set CS\n"
+"\t\tpushl btx_hdr+0xc\t\t# Set EIP\n"
+"\t\tpushl %ecx\t\t\t# Set GS\n"
+"\t\tpushl %ecx\t\t\t# Set FS\n"
+"\t\tpushl %ecx\t\t\t# Set DS\n"
+"\t\tpushl %ecx\t\t\t# Set ES\n"
+"\t\tpushl %edx\t\t\t# Set EAX\n"
+"\t\tmovb $0x7,%cl\t\t\t# Set remaining\n"
+"init.9:\t\tpush $0x0\t\t\t# general\n"
+"\t\tloop init.9\t\t\t# registers\n"
+"#ifdef BTX_SERIAL\n"
+"\t\tcall sio_init\t\t\t# setup the serial console\n"
+"#endif\n"
+"\t\tpopa\t\t\t\t# and initialize\n"
+"\t\tpopl %es\t\t\t# Initialize\n"
+"\t\tpopl %ds\t\t\t# user\n"
+"\t\tpopl %fs\t\t\t# segment\n"
+"\t\tpopl %gs\t\t\t# registers\n"
+"\t\tiret\t\t\t\t# To user mode\n"
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1125
+#, no-wrap
+msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-end]]"
+msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-end]]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1149
+msgid ""
+"Note that the client's environment include a stack segment selector and "
+"stack pointer (registers `%ss` and `%esp`). Indeed, once the TR is loaded "
+"with the appropriate stack segment selector (instruction `ltr`), the stack "
+"pointer is calculated and pushed onto the stack along with the stack's "
+"segment selector. Next, the value `0x202` is pushed onto the stack; it is "
+"the value that the EFLAGS will get when control is passed to the client. "
+"Also, the User Mode code segment selector and the client's entry point are "
+"pushed. Recall that this entry point is patched in the BTX header at link "
+"time. Finally, segment selectors (stored in register `%ecx`) for the "
+"segment registers `%gs, %fs, %ds and %es` are pushed onto the stack, along "
+"with the value at `%edx` (`0xa000`). Keep in mind the various values that "
+"have been pushed onto the stack (they will be popped out shortly). Next, "
+"values for the remaining general purpose registers are also pushed onto the "
+"stack (note the `loop` that pushes the value `0` seven times). Now, values "
+"will be started to be popped out of the stack. First, the `popa` "
+"instruction pops out of the stack the latest seven values pushed. They are "
+"stored in the general purpose registers in order `%edi, %esi, %ebp, %ebx, "
+"%edx, %ecx, %eax`. Then, the various segment selectors pushed are popped "
+"into the various segment registers. Five values still remain on the stack. "
+"They are popped when the `iret` instruction is executed. This instruction "
+"first pops the value that was pushed from the BTX header. This value is a "
+"pointer to [.filename]#boot2#'s entry point. It is placed in the register "
+"`%eip`, the instruction pointer register. Next, the segment selector for "
+"the User Code Segment is popped and copied to register `%cs`. Remember that "
+"this segment's privilege level is 3, the least privileged level. This means "
+"that we must provide values for the stack of this privilege level. This is "
+"why the processor, besides further popping the value for the EFLAGS "
+"register, does two more pops out of the stack. These values go to the stack "
+"pointer (`%esp`) and the stack segment (`%ss`). Now, execution continues at "
+"``boot0``'s entry point."
+msgstr ""
+"Обратите внимание, что среда клиента включает селектор сегмента стека и "
+"указатель стека (регистры `%ss` и `%esp`). Действительно, как только TR "
+"загружается соответствующим селектором сегмента стека (инструкция `ltr`), "
+"указатель стека вычисляется и помещается в стек вместе с селектором сегмента "
+"стека. Затем значение `0x202` помещается в стек; это значение, которое "
+"EFLAGS получит при передаче управления клиенту. Также в стек помещаются "
+"селектор сегмента кода пользовательского режима и точка входа клиента. "
+"Напомним, что эта точка входа прописывается в заголовке BTX во время "
+"компоновки. Наконец, селекторы сегментов (хранящиеся в регистре `%ecx`) для "
+"регистров сегментов `%gs, %fs, %ds и %es` помещаются в стек вместе со "
+"значением из `%edx` (`0xa000`). Примите во внимание эти значения, помещенные "
+"в стек (они скоро будут извлечены). Затем значения для оставшихся регистров "
+"общего назначения также помещаются в стек (обратите внимание на цикл `loop`, "
+"который помещает значение `0` семь раз). Теперь начнётся извлечение значений "
+"из стека. Сначала инструкция `popa` извлекает из стека последние семь "
+"помещённых значений. Они сохраняются в регистрах общего назначения в порядке "
+"`%edi, %esi, %ebp, %ebx, %edx, %ecx, %eax`. Затем различные селекторы "
+"сегментов, помещённые в стек, извлекаются в соответствующие регистры "
+"сегментов. В стеке остаются ещё пять значений. Они извлекаются при "
+"выполнении инструкции `iret`. Эта инструкция сначала извлекает значение, "
+"которое было помещено из заголовка BTX. Это значение является указателем на "
+"точку входа [.filename]#boot2#. Оно помещается в регистр `%eip` — регистр "
+"указателя инструкций. Затем селектор сегмента кода пользователя извлекается "
+"и копируется в регистр `%cs`. Помните, что уровень привилегий этого сегмента "
+"— 3, наименее привилегированный уровень. Это означает, что мы должны "
+"предоставить значения для стека этого уровня привилегий. Именно поэтому "
+"процессор, помимо дальнейшего извлечения значения для регистра EFLAGS, "
+"выполняет ещё два извлечения из стека. Эти значения попадают в указатель "
+"стека (`%esp`) и сегмент стека (`%ss`). Теперь выполнение продолжается с "
+"точки входа ``boot0``."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1153
+msgid ""
+"It is important to note how the User Code Segment is defined. This "
+"segment's _base address_ is set to `0xa000`. This means that code memory "
+"addresses are _relative_ to address 0xa000; if code being executed is "
+"fetched from address `0x2000`, the _actual_ memory addressed is "
+"`0xa000+0x2000=0xc000`."
+msgstr ""
+"Важно отметить, как определяется сегмент пользовательского кода. _Базовый "
+"адрес_ этого сегмента установлен на `0xa000`. Это означает, что адреса "
+"памяти кода являются _относительными_ к адресу 0xa000; если код, который "
+"выполняется, извлекается из адреса `0x2000`, _фактический_ адрес в памяти "
+"будет `0xa000+0x2000=0xc000`."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1155
+#, no-wrap
+msgid "boot2 Stage"
+msgstr "Этап загрузки boot2"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1162
+msgid ""
+"`boot2` defines an important structure, `struct bootinfo`. This structure "
+"is initialized by `boot2` and passed to the loader, and then further to the "
+"kernel. Some nodes of this structures are set by `boot2`, the rest by the "
+"loader. This structure, among other information, contains the kernel "
+"filename, BIOS harddisk geometry, BIOS drive number for boot device, "
+"physical memory available, `envp` pointer etc. The definition for it is:"
+msgstr ""
+"`boot2` определяет важную структуру, `struct bootinfo`. Эта структура "
+"инициализируется `boot2` и передается загрузчику, а затем ядру. Некоторые "
+"узлы этой структуры устанавливаются `boot2`, остальные — загрузчиком. Эта "
+"структура, среди прочей информации, содержит имя файла ядра, геометрию "
+"жесткого диска в BIOS, номер диска в BIOS для загрузочного устройства, "
+"доступную физическую память, указатель `envp` и т.д. Ее определение выглядит "
+"так:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1187
+#, no-wrap
+msgid ""
+"/usr/include/machine/bootinfo.h:\n"
+"struct bootinfo {\n"
+"\tu_int32_t\tbi_version;\n"
+"\tu_int32_t\tbi_kernelname;\t\t/* represents a char * */\n"
+"\tu_int32_t\tbi_nfs_diskless;\t/* struct nfs_diskless * */\n"
+"\t\t\t\t/* End of fields that are always present. */\n"
+"#define\tbi_endcommon\tbi_n_bios_used\n"
+"\tu_int32_t\tbi_n_bios_used;\n"
+"\tu_int32_t\tbi_bios_geom[N_BIOS_GEOM];\n"
+"\tu_int32_t\tbi_size;\n"
+"\tu_int8_t\tbi_memsizes_valid;\n"
+"\tu_int8_t\tbi_bios_dev;\t\t/* bootdev BIOS unit number */\n"
+"\tu_int8_t\tbi_pad[2];\n"
+"\tu_int32_t\tbi_basemem;\n"
+"\tu_int32_t\tbi_extmem;\n"
+"\tu_int32_t\tbi_symtab;\t\t/* struct symtab * */\n"
+"\tu_int32_t\tbi_esymtab;\t\t/* struct symtab * */\n"
+"\t\t\t\t/* Items below only from advanced bootloader */\n"
+"\tu_int32_t\tbi_kernend;\t\t/* end of kernel space */\n"
+"\tu_int32_t\tbi_envp;\t\t/* environment */\n"
+"\tu_int32_t\tbi_modulep;\t\t/* preloaded modules */\n"
+"};\n"
+msgstr ""
+"/usr/include/machine/bootinfo.h:\n"
+"struct bootinfo {\n"
+"\tu_int32_t\tbi_version;\n"
+"\tu_int32_t\tbi_kernelname;\t\t/* represents a char * */\n"
+"\tu_int32_t\tbi_nfs_diskless;\t/* struct nfs_diskless * */\n"
+"\t\t\t\t/* End of fields that are always present. */\n"
+"#define\tbi_endcommon\tbi_n_bios_used\n"
+"\tu_int32_t\tbi_n_bios_used;\n"
+"\tu_int32_t\tbi_bios_geom[N_BIOS_GEOM];\n"
+"\tu_int32_t\tbi_size;\n"
+"\tu_int8_t\tbi_memsizes_valid;\n"
+"\tu_int8_t\tbi_bios_dev;\t\t/* bootdev BIOS unit number */\n"
+"\tu_int8_t\tbi_pad[2];\n"
+"\tu_int32_t\tbi_basemem;\n"
+"\tu_int32_t\tbi_extmem;\n"
+"\tu_int32_t\tbi_symtab;\t\t/* struct symtab * */\n"
+"\tu_int32_t\tbi_esymtab;\t\t/* struct symtab * */\n"
+"\t\t\t\t/* Items below only from advanced bootloader */\n"
+"\tu_int32_t\tbi_kernend;\t\t/* end of kernel space */\n"
+"\tu_int32_t\tbi_envp;\t\t/* environment */\n"
+"\tu_int32_t\tbi_modulep;\t\t/* preloaded modules */\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1194
+msgid ""
+"`boot2` enters into an infinite loop waiting for user input, then calls "
+"`load()`. If the user does not press anything, the loop breaks by a "
+"timeout, so `load()` will load the default file ([.filename]#/boot/"
+"loader#). Functions `ino_t lookup(char *filename)` and `int xfsread(ino_t "
+"inode, void *buf, size_t nbyte)` are used to read the content of a file into "
+"memory. [.filename]#/boot/loader# is an ELF binary, but where the ELF "
+"header is prepended with [.filename]#a.out#'s `struct exec` structure. "
+"`load()` scans the loader's ELF header, loading the content of [.filename]#/"
+"boot/loader# into memory, and passing the execution to the loader's entry:"
+msgstr ""
+"`boot2` входит в бесконечный цикл, ожидая ввода пользователя, затем вызывает "
+"`load()`. Если пользователь ничего не нажимает, цикл прерывается по "
+"таймауту, и `load()` загружает файл по умолчанию ([.filename]#/boot/"
+"loader#). Функции `ino_t lookup(char *filename)` и `int xfsread(ino_t inode, "
+"void *buf, size_t nbyte)` используются для чтения содержимого файла в "
+"память. [.filename]#/boot/loader# — это ELF-бинарный файл, но с заголовком "
+"ELF, перед которым добавлена структура `struct exec` из [.filename]#a.out#. "
+"`load()` анализирует ELF-заголовок загрузчика, загружает содержимое "
+"[.filename]#/boot/loader# в память и передаёт управление на точку входа "
+"загрузчика:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1201
+#, no-wrap
+msgid ""
+"stand/i386/boot2/boot2.c:\n"
+" __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),\n"
+"\t MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),\n"
+"\t 0, 0, 0, VTOP(&bootinfo));\n"
+msgstr ""
+"stand/i386/boot2/boot2.c:\n"
+" __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),\n"
+"\t MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),\n"
+"\t 0, 0, 0, VTOP(&bootinfo));\n"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1204
+#, no-wrap
+msgid "loader Stage"
+msgstr "Этап загрузчика (loader)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1209
+msgid ""
+"loader is a BTX client as well. I will not describe it here in detail, "
+"there is a comprehensive man page written by Mike Smith, man:loader[8]. The "
+"underlying mechanisms and BTX were discussed above."
+msgstr ""
+"Загрузчик также является клиентом BTX. Я не буду подробно описывать его "
+"здесь, существует исчерпывающая man-страница, написанная Майком Смитом: "
+"man:loader[8]. Основные механизмы и BTX были рассмотрены выше."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1212
+msgid ""
+"The main task for the loader is to boot the kernel. When the kernel is "
+"loaded into memory, it is being called by the loader:"
+msgstr ""
+"Основная задача загрузчика — загрузить ядро. Когда ядро загружено в память, "
+"загрузчик вызывает его:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1218
+#, no-wrap
+msgid ""
+"stand/common/boot.c:\n"
+" /* Call the exec handler from the loader matching the kernel */\n"
+" file_formats[fp->f_loader]->l_exec(fp);\n"
+msgstr ""
+"stand/common/boot.c:\n"
+" /* Call the exec handler from the loader matching the kernel */\n"
+" file_formats[fp->f_loader]->l_exec(fp);\n"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1221
+#, no-wrap
+msgid "Kernel Initialization"
+msgstr "Инициализация ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1228
+msgid ""
+"Let us take a look at the command that links the kernel. This will help "
+"identify the exact location where the loader passes execution to the "
+"kernel. This location is the kernel's actual entry point. This command is "
+"now excluded from [.filename]#sys/conf/Makefile.i386#. The content that "
+"interests us can be found in [.filename]#/usr/obj/usr/src/i386.i386/sys/"
+"GENERIC/#."
+msgstr ""
+"Давайте рассмотрим команду, которая компонует ядро. Это поможет определить "
+"точное местоположение, где загрузчик передает выполнение ядру. Это "
+"местоположение является фактической точкой входа ядра. Данная команда теперь "
+"исключена из [.filename]#sys/conf/Makefile.i386#. Интересующее нас "
+"содержимое можно найти в [.filename]#/usr/obj/usr/src/i386.i386/sys/GENERIC/"
+"#."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1235
+#, no-wrap
+msgid ""
+"/usr/obj/usr/src/i386.i386/sys/GENERIC/kernel.meta:\n"
+"ld -m elf_i386_fbsd -Bdynamic -T /usr/src/sys/conf/ldscript.i386 --build-id=sha1 --no-warn-mismatch \\\n"
+"--warn-common --export-dynamic --dynamic-linker /red/herring -X -o kernel locore.o\n"
+"<lots of kernel .o files>\n"
+msgstr ""
+"/usr/obj/usr/src/i386.i386/sys/GENERIC/kernel.meta:\n"
+"ld -m elf_i386_fbsd -Bdynamic -T /usr/src/sys/conf/ldscript.i386 --build-id=sha1 --no-warn-mismatch \\\n"
+"--warn-common --export-dynamic --dynamic-linker /red/herring -X -o kernel locore.o\n"
+"<lots of kernel .o files>\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1241
+msgid ""
+"A few interesting things can be seen here. First, the kernel is an ELF "
+"dynamically linked binary, but the dynamic linker for kernel is [.filename]#/"
+"red/herring#, which is definitely a bogus file. Second, taking a look at "
+"the file [.filename]#sys/conf/ldscript.i386# gives an idea about what ld "
+"options are used when compiling a kernel. Reading through the first few "
+"lines, the string"
+msgstr ""
+"Вот несколько интересных наблюдений. Во-первых, ядро представляет собой "
+"динамически связанный бинарный файл ELF, но динамический компоновщик для "
+"ядра — это [.filename]#/red/herring#, что явно является фиктивным файлом. Во-"
+"вторых, взглянув на файл [.filename]#sys/conf/ldscript.i386#, можно понять, "
+"какие параметры ld используются при компиляции ядра. Читая первые несколько "
+"строк, видим, что строка"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1246
+#, no-wrap
+msgid ""
+"sys/conf/ldscript.i386:\n"
+"ENTRY(btext)\n"
+msgstr ""
+"sys/conf/ldscript.i386:\n"
+"ENTRY(btext)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1250
+msgid ""
+"says that a kernel's entry point is the symbol `btext`. This symbol is "
+"defined in [.filename]#locore.s#:"
+msgstr ""
+"говорит, что точка входа ядра — это символ `btext`. Этот символ определён в "
+"[.filename]#locore.s#:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1261
+#, no-wrap
+msgid ""
+"sys/i386/i386/locore.s:\n"
+"\t.text\n"
+"/**********************************************************************\n"
+" *\n"
+" * This is where the bootblocks start us, set the ball rolling...\n"
+" *\n"
+" */\n"
+"NON_GPROF_ENTRY(btext)\n"
+msgstr ""
+"sys/i386/i386/locore.s:\n"
+"\t.text\n"
+"/**********************************************************************\n"
+" *\n"
+" * This is where the bootblocks start us, set the ball rolling...\n"
+" *\n"
+" */\n"
+"NON_GPROF_ENTRY(btext)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1265
+msgid ""
+"First, the register EFLAGS is set to a predefined value of 0x00000002. Then "
+"all the segment registers are initialized:"
+msgstr ""
+"Сначала регистр EFLAGS устанавливается в предопределённое значение "
+"0x00000002. Затем инициализируются все сегментные регистры:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1272
+#, no-wrap
+msgid ""
+"sys/i386/i386/locore.s:\n"
+"/* Don't trust what the BIOS gives for eflags. */\n"
+"\tpushl\t$PSL_KERNEL\n"
+"\tpopfl\n"
+msgstr ""
+"sys/i386/i386/locore.s:\n"
+"/* Don't trust what the BIOS gives for eflags. */\n"
+"\tpushl\t$PSL_KERNEL\n"
+"\tpopfl\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1280
+#, no-wrap
+msgid ""
+"/*\n"
+" * Don't trust what the BIOS gives for %fs and %gs. Trust the bootstrap\n"
+" * to set %cs, %ds, %es and %ss.\n"
+" */\n"
+"\tmov\t%ds, %ax\n"
+"\tmov\t%ax, %fs\n"
+"\tmov\t%ax, %gs\n"
+msgstr ""
+"/*\n"
+" * Don't trust what the BIOS gives for %fs and %gs. Trust the bootstrap\n"
+" * to set %cs, %ds, %es and %ss.\n"
+" */\n"
+"\tmov\t%ds, %ax\n"
+"\tmov\t%ax, %fs\n"
+"\tmov\t%ax, %gs\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1284
+msgid ""
+"btext calls the routines `recover_bootinfo()`, `identify_cpu()`, which are "
+"also defined in [.filename]#locore.s#. Here is a description of what they "
+"do:"
+msgstr ""
+"btext вызывает подпрограммы `recover_bootinfo()` и `identify_cpu()`, которые "
+"также определены в [.filename]#locore.s#. Вот описание их функций:"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1290
+#, no-wrap
+msgid "`recover_bootinfo`"
+msgstr "`recover_bootinfo`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1294
+#, no-wrap
+msgid ""
+"This routine parses the parameters to the kernel passed from the bootstrap.\n"
+"The kernel may have been booted in 3 ways: by the loader, described above, by the old disk boot blocks, or by the old diskless boot procedure.\n"
+"This function determines the booting method, and stores the `struct bootinfo` structure into the kernel memory."
+msgstr ""
+"Эта процедура разбирает параметры, переданные ядру при загрузке.\n"
+"Ядро могло быть загружено тремя способами: загрузчиком (как описано выше), старыми загрузочными блоками диска или по старой процедуре загрузки без диска.\n"
+"Эта функция определяет метод загрузки и сохраняет структуру `struct bootinfo` в памяти ядра."
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1295
+#, no-wrap
+msgid "`identify_cpu`"
+msgstr "`identify_cpu`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1296
+#, no-wrap
+msgid "This function tries to find out what CPU it is running on, storing the value found in a variable `_cpu`."
+msgstr "Эта функция пытается определить, на каком процессоре она выполняется, сохраняя найденное значение в переменной `_cpu`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1299
+msgid "The next steps are enabling VME, if the CPU supports it:"
+msgstr ""
+"Следующие шаги включают активацию VME, если процессор поддерживает эту "
+"функцию:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1307
+#, no-wrap
+msgid ""
+"sys/i386/i386/mpboot.s:\n"
+"\ttestl\t$CPUID_VME,%edx\n"
+"\tjz\t3f\n"
+"\torl\t$CR4_VME,%eax\n"
+"3:\tmovl\t%eax,%cr4\n"
+msgstr ""
+"sys/i386/i386/mpboot.s:\n"
+"\ttestl\t$CPUID_VME,%edx\n"
+"\tjz\t3f\n"
+"\torl\t$CR4_VME,%eax\n"
+"3:\tmovl\t%eax,%cr4\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1310
+msgid "Then, enabling paging:"
+msgstr "Затем, включение подкачки:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1320
+#, no-wrap
+msgid ""
+"sys/i386/i386/mpboot.s:\n"
+"/* Now enable paging */\n"
+"\tmovl\tIdlePTD_nopae, %eax\n"
+"\tmovl\t%eax,%cr3\t\t\t/* load ptd addr into mmu */\n"
+"\tmovl\t%cr0,%eax\t\t\t/* get control word */\n"
+"\torl\t$CR0_PE|CR0_PG,%eax\t\t/* enable paging */\n"
+"\tmovl\t%eax,%cr0\t\t\t/* and let's page NOW! */\n"
+msgstr ""
+"sys/i386/i386/mpboot.s:\n"
+"/* Now enable paging */\n"
+"\tmovl\tIdlePTD_nopae, %eax\n"
+"\tmovl\t%eax,%cr3\t\t\t/* load ptd addr into mmu */\n"
+"\tmovl\t%cr0,%eax\t\t\t/* get control word */\n"
+"\torl\t$CR0_PE|CR0_PG,%eax\t\t/* enable paging */\n"
+"\tmovl\t%eax,%cr0\t\t\t/* and let's page NOW! */\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1323
+msgid ""
+"The next three lines of code are because the paging was set, so the jump is "
+"needed to continue the execution in virtualized address space:"
+msgstr ""
+"Следующие три строки кода необходимы, потому что была установлена подкачка, "
+"поэтому требуется переход для продолжения выполнения в виртуализированном "
+"адресном пространстве:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1329
+#, no-wrap
+msgid ""
+"sys/i386/i386/mpboot.s:\n"
+"\tpushl\t$mp_begin\t\t\t\t/* jump to high mem */\n"
+"\tret\n"
+msgstr ""
+"sys/i386/i386/mpboot.s:\n"
+"\tpushl\t$mp_begin\t\t\t\t/* jump to high mem */\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1332
+#, no-wrap
+msgid ""
+"/* now running relocated at KERNBASE where the system is linked to run */\n"
+"mp_begin:\t/* now running relocated at KERNBASE */\n"
+msgstr ""
+"/* now running relocated at KERNBASE where the system is linked to run */\n"
+"mp_begin:\t/* now running relocated at KERNBASE */\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1337
+msgid ""
+"The function `init386()` is called with a pointer to the first free physical "
+"page, after that `mi_startup()`. `init386` is an architecture dependent "
+"initialization function, and `mi_startup()` is an architecture independent "
+"one (the 'mi_' prefix stands for Machine Independent). The kernel never "
+"returns from `mi_startup()`, and by calling it, the kernel finishes booting:"
+msgstr ""
+"Функция `init386()` вызывается с указателем на первую свободную физическую "
+"страницу, после чего следует вызов `mi_startup()`. `init386` — это "
+"архитектурно-зависимая функция инициализации, а `mi_startup()` — "
+"архитектурно-независимая (префикс 'mi_' означает Machine Independent, то "
+"есть «независимая от машины»). Ядро никогда не возвращается из "
+"`mi_startup()`, и, вызывая её, завершает загрузку:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1347
+#, no-wrap
+msgid ""
+"sys/i386/i386/locore.s:\n"
+"\tpushl\tphysfree\t\t\t/* value of first for init386(first) */\n"
+"\tcall\tinit386\t\t\t\t/* wire 386 chip for unix operation */\n"
+"\taddl\t$4,%esp\n"
+"\tmovl\t%eax,%esp\t\t\t/* Switch to true top of stack. */\n"
+"\tcall\tmi_startup\t\t\t/* autoconfiguration, mountroot etc */\n"
+"\t/* NOTREACHED */\n"
+msgstr ""
+"sys/i386/i386/locore.s:\n"
+"\tpushl\tphysfree\t\t\t/* value of first for init386(first) */\n"
+"\tcall\tinit386\t\t\t\t/* wire 386 chip for unix operation */\n"
+"\taddl\t$4,%esp\n"
+"\tmovl\t%eax,%esp\t\t\t/* Switch to true top of stack. */\n"
+"\tcall\tmi_startup\t\t\t/* autoconfiguration, mountroot etc */\n"
+"\t/* NOTREACHED */\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1349
+#, no-wrap
+msgid "`init386()`"
+msgstr "`init386()`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1354
+msgid ""
+"`init386()` is defined in [.filename]#sys/i386/i386/machdep.c# and performs "
+"low-level initialization specific to the i386 chip. The switch to protected "
+"mode was performed by the loader. The loader has created the very first "
+"task, in which the kernel continues to operate. Before looking at the code, "
+"consider the tasks the processor must complete to initialize protected mode "
+"execution:"
+msgstr ""
+"`init386()` определена в [.filename]#sys/i386/i386/machdep.c# и выполняет "
+"низкоуровневую инициализацию, специфичную для чипа i386. Переход в "
+"защищённый режим был выполнен загрузчиком. Загрузчик создал самую первую "
+"задачу, в которой ядро продолжает работать. Прежде чем рассматривать код, "
+"рассмотрим задачи, которые процессор должен выполнить для инициализации "
+"выполнения в защищённом режиме:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1356
+msgid ""
+"Initialize the kernel tunable parameters, passed from the bootstrapping "
+"program."
+msgstr ""
+"Инициализировать настраиваемые параметры ядра, переданные из загрузочной "
+"программы."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1357
+msgid "Prepare the GDT."
+msgstr "Подготовить GDT."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1358
+msgid "Prepare the IDT."
+msgstr "Подготовить IDT."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1359
+msgid "Initialize the system console."
+msgstr "Инициализировать системную консоль."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1360
+msgid "Initialize the DDB, if it is compiled into kernel."
+msgstr "Инициализировать DDB, если он скомпилирован в ядро."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1361
+msgid "Initialize the TSS."
+msgstr "Инициализировать TSS."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1362
+msgid "Prepare the LDT."
+msgstr "Подготовить LDT."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1363
+msgid "Set up thread0's pcb."
+msgstr "Настройка pcb для thread0."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1366
+msgid ""
+"`init386()` initializes the tunable parameters passed from bootstrap by "
+"setting the environment pointer (envp) and calling `init_param1()`. The "
+"envp pointer has been passed from loader in the `bootinfo` structure:"
+msgstr ""
+"`init386()` инициализирует настраиваемые параметры, переданные из bootstrap, "
+"устанавливая указатель окружения (envp) и вызывая `init_param1()`. Указатель "
+"envp был передан из loader в структуре `bootinfo`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1372
+#, no-wrap
+msgid ""
+"sys/i386/i386/machdep.c:\n"
+"\t/* Init basic tunables, hz etc */\n"
+"\tinit_param1();\n"
+msgstr ""
+"sys/i386/i386/machdep.c:\n"
+"\t/* Init basic tunables, hz etc */\n"
+"\tinit_param1();\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1376
+msgid ""
+"`init_param1()` is defined in [.filename]#sys/kern/subr_param.c#. That file "
+"has a number of sysctls, and two functions, `init_param1()` and "
+"`init_param2()`, that are called from `init386()`:"
+msgstr ""
+"`init_param1()` определена в [.filename]#sys/kern/subr_param.c#. Этот файл "
+"содержит ряд sysctl, а также две функции, `init_param1()` и `init_param2()`, "
+"которые вызываются из `init386()`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1384
+#, no-wrap
+msgid ""
+"sys/kern/subr_param.c:\n"
+"\thz = -1;\n"
+"\tTUNABLE_INT_FETCH(\"kern.hz\", &hz);\n"
+"\tif (hz == -1)\n"
+"\t\thz = vm_guest > VM_GUEST_NO ? HZ_VM : HZ;\n"
+msgstr ""
+"sys/kern/subr_param.c:\n"
+"\thz = -1;\n"
+"\tTUNABLE_INT_FETCH(\"kern.hz\", &hz);\n"
+"\tif (hz == -1)\n"
+"\t\thz = vm_guest > VM_GUEST_NO ? HZ_VM : HZ;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1387
+msgid ""
+"TUNABLE_<typename>_FETCH is used to fetch the value from the environment:"
+msgstr ""
+"`TUNABLE_<typename>_FETCH` используется для получения значения из окружения:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1392
+#, no-wrap
+msgid ""
+"/usr/src/sys/sys/kernel.h:\n"
+"#define\tTUNABLE_INT_FETCH(path, var)\tgetenv_int((path), (var))\n"
+msgstr ""
+"/usr/src/sys/sys/kernel.h:\n"
+"#define\tTUNABLE_INT_FETCH(path, var)\tgetenv_int((path), (var))\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1396
+msgid ""
+"Sysctl `kern.hz` is the system clock tick. Additionally, these sysctls are "
+"set by `init_param1()`: `kern.maxswzone, kern.maxbcache, kern.maxtsiz, "
+"kern.dfldsiz, kern.maxdsiz, kern.dflssiz, kern.maxssiz, kern.sgrowsiz`."
+msgstr ""
+"Sysctl `kern.hz` представляет собой такт системных часов. Кроме того, эти "
+"параметры sysctl устанавливаются функцией `init_param1()`: `kern.maxswzone, "
+"kern.maxbcache, kern.maxtsiz, kern.dfldsiz, kern.maxdsiz, kern.dflssiz, "
+"kern.maxssiz, kern.sgrowsiz`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1404
+msgid ""
+"Then `init386()` prepares the Global Descriptors Table (GDT). Every task on "
+"an x86 is running in its own virtual address space, and this space is "
+"addressed by a segment:offset pair. Say, for instance, the current "
+"instruction to be executed by the processor lies at CS:EIP, then the linear "
+"virtual address for that instruction would be \"the virtual address of code "
+"segment CS\" + EIP. For convenience, segments begin at virtual address 0 "
+"and end at a 4GB boundary. Therefore, the instruction's linear virtual "
+"address for this example would just be the value of EIP. Segment registers "
+"such as CS, DS etc are the selectors, i.e., indexes, into GDT (to be more "
+"precise, an index is not a selector itself, but the INDEX field of a "
+"selector). FreeBSD's GDT holds descriptors for 15 selectors per CPU:"
+msgstr ""
+"Затем `init386()` подготавливает Глобальную Таблицу Дескрипторов (GDT). "
+"Каждая задача на x86 выполняется в своем собственном виртуальном адресном "
+"пространстве, и это пространство адресуется парой сегмент:смещение. "
+"Например, если текущая инструкция, которую должен выполнить процессор, "
+"находится по адресу CS:EIP, то линейный виртуальный адрес этой инструкции "
+"будет \"виртуальный адрес кодового сегмента CS\" + EIP. Для удобства "
+"сегменты начинаются с виртуального адреса 0 и заканчиваются на границе 4 ГБ. "
+"Таким образом, линейный виртуальный адрес инструкции в данном примере будет "
+"просто значением EIP. Сегментные регистры, такие как CS, DS и другие, "
+"являются селекторами, то есть индексами в GDT (если быть более точным, "
+"индекс — это не сам селектор, а поле INDEX в селекторе). GDT в FreeBSD "
+"содержит дескрипторы для 15 селекторов на каждый CPU:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1410
+#, no-wrap
+msgid ""
+"sys/i386/i386/machdep.c:\n"
+"union descriptor gdt0[NGDT];\t/* initial global descriptor table */\n"
+"union descriptor *gdt = gdt0;\t/* global descriptor table */\n"
+msgstr ""
+"sys/i386/i386/machdep.c:\n"
+"union descriptor gdt0[NGDT];\t/* initial global descriptor table */\n"
+"union descriptor *gdt = gdt0;\t/* global descriptor table */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1435
+#, no-wrap
+msgid ""
+"sys/x86/include/segments.h:\n"
+"/*\n"
+" * Entries in the Global Descriptor Table (GDT)\n"
+" */\n"
+"#define\tGNULL_SEL\t0\t/* Null Descriptor */\n"
+"#define\tGPRIV_SEL\t1\t/* SMP Per-Processor Private Data */\n"
+"#define\tGUFS_SEL\t2\t/* User %fs Descriptor (order critical: 1) */\n"
+"#define\tGUGS_SEL\t3\t/* User %gs Descriptor (order critical: 2) */\n"
+"#define\tGCODE_SEL\t4\t/* Kernel Code Descriptor (order critical: 1) */\n"
+"#define\tGDATA_SEL\t5\t/* Kernel Data Descriptor (order critical: 2) */\n"
+"#define\tGUCODE_SEL\t6\t/* User Code Descriptor (order critical: 3) */\n"
+"#define\tGUDATA_SEL\t7\t/* User Data Descriptor (order critical: 4) */\n"
+"#define\tGBIOSLOWMEM_SEL\t8\t/* BIOS low memory access (must be entry 8) */\n"
+"#define\tGPROC0_SEL\t9\t/* Task state process slot zero and up */\n"
+"#define\tGLDT_SEL\t10\t/* Default User LDT */\n"
+"#define\tGUSERLDT_SEL\t11\t/* User LDT */\n"
+"#define\tGPANIC_SEL\t12\t/* Task state to consider panic from */\n"
+"#define\tGBIOSCODE32_SEL\t13\t/* BIOS interface (32bit Code) */\n"
+"#define\tGBIOSCODE16_SEL\t14\t/* BIOS interface (16bit Code) */\n"
+"#define\tGBIOSDATA_SEL\t15\t/* BIOS interface (Data) */\n"
+"#define\tGBIOSUTIL_SEL\t16\t/* BIOS interface (Utility) */\n"
+"#define\tGBIOSARGS_SEL\t17\t/* BIOS interface (Arguments) */\n"
+"#define\tGNDIS_SEL\t18\t/* For the NDIS layer */\n"
+"#define\tNGDT\t\t19\n"
+msgstr ""
+"sys/x86/include/segments.h:\n"
+"/*\n"
+" * Entries in the Global Descriptor Table (GDT)\n"
+" */\n"
+"#define\tGNULL_SEL\t0\t/* Null Descriptor */\n"
+"#define\tGPRIV_SEL\t1\t/* SMP Per-Processor Private Data */\n"
+"#define\tGUFS_SEL\t2\t/* User %fs Descriptor (order critical: 1) */\n"
+"#define\tGUGS_SEL\t3\t/* User %gs Descriptor (order critical: 2) */\n"
+"#define\tGCODE_SEL\t4\t/* Kernel Code Descriptor (order critical: 1) */\n"
+"#define\tGDATA_SEL\t5\t/* Kernel Data Descriptor (order critical: 2) */\n"
+"#define\tGUCODE_SEL\t6\t/* User Code Descriptor (order critical: 3) */\n"
+"#define\tGUDATA_SEL\t7\t/* User Data Descriptor (order critical: 4) */\n"
+"#define\tGBIOSLOWMEM_SEL\t8\t/* BIOS low memory access (must be entry 8) */\n"
+"#define\tGPROC0_SEL\t9\t/* Task state process slot zero and up */\n"
+"#define\tGLDT_SEL\t10\t/* Default User LDT */\n"
+"#define\tGUSERLDT_SEL\t11\t/* User LDT */\n"
+"#define\tGPANIC_SEL\t12\t/* Task state to consider panic from */\n"
+"#define\tGBIOSCODE32_SEL\t13\t/* BIOS interface (32bit Code) */\n"
+"#define\tGBIOSCODE16_SEL\t14\t/* BIOS interface (16bit Code) */\n"
+"#define\tGBIOSDATA_SEL\t15\t/* BIOS interface (Data) */\n"
+"#define\tGBIOSUTIL_SEL\t16\t/* BIOS interface (Utility) */\n"
+"#define\tGBIOSARGS_SEL\t17\t/* BIOS interface (Arguments) */\n"
+"#define\tGNDIS_SEL\t18\t/* For the NDIS layer */\n"
+"#define\tNGDT\t\t19\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1439
+msgid ""
+"Note that those #defines are not selectors themselves, but just a field "
+"INDEX of a selector, so they are exactly the indices of the GDT. for "
+"example, an actual selector for the kernel code (GCODE_SEL) has the value "
+"0x20."
+msgstr ""
+"Обратите внимание, что эти `#defines` не являются самими селекторами, а лишь "
+"полем `INDEX` селектора, поэтому они точно соответствуют индексам GDT. "
+"Например, реальный селектор для кода ядра (`GCODE_SEL`) имеет значение "
+"`0x20`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1447
+msgid ""
+"The next step is to initialize the Interrupt Descriptor Table (IDT). This "
+"table is referenced by the processor when a software or hardware interrupt "
+"occurs. For example, to make a system call, user application issues the "
+"`INT 0x80` instruction. This is a software interrupt, so the processor's "
+"hardware looks up a record with index 0x80 in the IDT. This record points "
+"to the routine that handles this interrupt, in this particular case, this "
+"will be the kernel's syscall gate. The IDT may have a maximum of 256 "
+"(0x100) records. The kernel allocates NIDT records for the IDT, where NIDT "
+"is the maximum (256):"
+msgstr ""
+"Следующий шаг — инициализация таблицы дескрипторов прерываний (IDT). Эта "
+"таблица используется процессором при возникновении программного или "
+"аппаратного прерывания. Например, чтобы выполнить системный вызов, "
+"пользовательское приложение использует инструкцию `INT 0x80`. Это "
+"программное прерывание, поэтому аппаратное обеспечение процессора ищет "
+"запись с индексом 0x80 в IDT. Эта запись указывает на процедуру обработки "
+"данного прерывания, в данном конкретном случае это будет шлюз системных "
+"вызовов ядра. IDT может содержать максимум 256 (0x100) записей. Ядро "
+"выделяет NIDT записей для IDT, где NIDT — это максимум (256):"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1453
+#, no-wrap
+msgid ""
+"sys/i386/i386/machdep.c:\n"
+"static struct gate_descriptor idt0[NIDT];\n"
+"struct gate_descriptor *idt = &idt0[0];\t/* interrupt descriptor table */\n"
+msgstr ""
+"sys/i386/i386/machdep.c:\n"
+"static struct gate_descriptor idt0[NIDT];\n"
+"struct gate_descriptor *idt = &idt0[0];\t/* interrupt descriptor table */\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1457
+msgid ""
+"For each interrupt, an appropriate handler is set. The syscall gate for "
+"`INT 0x80` is set as well:"
+msgstr ""
+"Для каждого прерывания устанавливается соответствующий обработчик. Также "
+"настраивается шлюз системного вызова для `INT 0x80`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1463
+#, no-wrap
+msgid ""
+"sys/i386/i386/machdep.c:\n"
+"\tsetidt(IDT_SYSCALL, &IDTVEC(int0x80_syscall),\n"
+"\t\t\tSDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL));\n"
+msgstr ""
+"sys/i386/i386/machdep.c:\n"
+"\tsetidt(IDT_SYSCALL, &IDTVEC(int0x80_syscall),\n"
+"\t\t\tSDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL));\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1466
+msgid ""
+"So when a userland application issues the `INT 0x80` instruction, control "
+"will transfer to the function `_Xint0x80_syscall`, which is in the kernel "
+"code segment and will be executed with supervisor privileges."
+msgstr ""
+"Итак, когда пользовательское приложение выполняет инструкцию `INT 0x80`, "
+"управление передаётся функции `_Xint0x80_syscall`, которая находится в "
+"сегменте кода ядра и будет выполнена с привилегиями супервизора."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1468
+msgid "Console and DDB are then initialized:"
+msgstr "Консоль и DDB инициализируются:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1479
+#, no-wrap
+msgid ""
+"sys/i386/i386/machdep.c:\n"
+"\tcninit();\n"
+"/* skipped */\n"
+" kdb_init();\n"
+"#ifdef KDB\n"
+"\tif (boothowto & RB_KDB)\n"
+"\t\tkdb_enter(KDB_WHY_BOOTFLAGS, \"Boot flags requested debugger\");\n"
+"#endif\n"
+msgstr ""
+"sys/i386/i386/machdep.c:\n"
+"\tcninit();\n"
+"/* skipped */\n"
+" kdb_init();\n"
+"#ifdef KDB\n"
+"\tif (boothowto & RB_KDB)\n"
+"\t\tkdb_enter(KDB_WHY_BOOTFLAGS, \"Boot flags requested debugger\");\n"
+"#endif\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1482
+msgid ""
+"The Task State Segment is another x86 protected mode structure, the TSS is "
+"used by the hardware to store task information when a task switch occurs."
+msgstr ""
+"Сегмент состояния задачи (TSS) — это еще одна структура защищенного режима "
+"x86, используемая оборудованием для хранения информации о задаче при "
+"переключении задач."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1485
+msgid ""
+"The Local Descriptors Table is used to reference userland code and data. "
+"Several selectors are defined to point to the LDT, they are the system call "
+"gates and the user code and data selectors:"
+msgstr ""
+"Локальная таблица дескрипторов (LDT) используется для ссылки на код и данные "
+"пользовательского пространства. Определено несколько селекторов, указывающих "
+"на LDT, включая шлюзы системных вызовов, а также селекторы кода и данных "
+"пользователя:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1494
+#, no-wrap
+msgid ""
+"sys/x86/include/segments.h:\n"
+"#define\tLSYS5CALLS_SEL\t0\t/* forced by intel BCS */\n"
+"#define\tLSYS5SIGR_SEL\t1\n"
+"#define\tLUCODE_SEL\t3\n"
+"#define\tLUDATA_SEL\t5\n"
+"#define\tNLDT\t\t(LUDATA_SEL + 1)\n"
+msgstr ""
+"sys/x86/include/segments.h:\n"
+"#define\tLSYS5CALLS_SEL\t0\t/* forced by intel BCS */\n"
+"#define\tLSYS5SIGR_SEL\t1\n"
+"#define\tLUCODE_SEL\t3\n"
+"#define\tLUDATA_SEL\t5\n"
+"#define\tNLDT\t\t(LUDATA_SEL + 1)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1499
+msgid ""
+"Next, proc0's Process Control Block (`struct pcb`) structure is "
+"initialized. proc0 is a `struct proc` structure that describes a kernel "
+"process. It is always present while the kernel is running, therefore it is "
+"linked with thread0:"
+msgstr ""
+"Далее инициализируется структура Блока Управления Процессом (`struct pcb`) "
+"для proc0. proc0 — это структура `struct proc`, описывающая процесс ядра. "
+"Она всегда присутствует во время работы ядра, поэтому связана с thread0:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1507
+#, no-wrap
+msgid ""
+"sys/i386/i386/machdep.c:\n"
+"register_t\n"
+"init386(int first)\n"
+"{\n"
+" /* ... skipped ... */\n"
+msgstr ""
+"sys/i386/i386/machdep.c:\n"
+"register_t\n"
+"init386(int first)\n"
+"{\n"
+" /* ... skipped ... */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1511
+#, no-wrap
+msgid ""
+" proc_linkup0(&proc0, &thread0);\n"
+" /* ... skipped ... */\n"
+"}\n"
+msgstr ""
+" proc_linkup0(&proc0, &thread0);\n"
+" /* ... skipped ... */\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1515
+msgid ""
+"The structure `struct pcb` is a part of a proc structure. It is defined in "
+"[.filename]#/usr/include/machine/pcb.h# and has a process's information "
+"specific to the i386 architecture, such as registers values."
+msgstr ""
+"Структура `struct pcb` является частью структуры proc. Она определена в "
+"[.filename]#/usr/include/machine/pcb.h# и содержит информацию процесса, "
+"специфичную для архитектуры i386, такую как значения регистров."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1516
+#, no-wrap
+msgid "`mi_startup()`"
+msgstr "`mi_startup()`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1519
+msgid ""
+"This function performs a bubble sort of all the system initialization "
+"objects and then calls the entry of each object one by one:"
+msgstr ""
+"Эта функция выполняет сортировку пузырьком всех объектов инициализации "
+"системы, а затем вызывает вход каждого объекта по очереди:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1524
+#, no-wrap
+msgid ""
+"sys/kern/init_main.c:\n"
+"\tfor (sipp = sysinit; sipp < sysinit_end; sipp++) {\n"
+msgstr ""
+"sys/kern/init_main.c:\n"
+"\tfor (sipp = sysinit; sipp < sysinit_end; sipp++) {\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1526
+#, no-wrap
+msgid "\t\t/* ... skipped ... */\n"
+msgstr "\t\t/* ... skipped ... */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1531
+#, no-wrap
+msgid ""
+"\t\t/* Call function */\n"
+"\t\t(*((*sipp)->func))((*sipp)->udata);\n"
+"\t\t/* ... skipped ... */\n"
+"\t}\n"
+msgstr ""
+"\t\t/* Call function */\n"
+"\t\t(*((*sipp)->func))((*sipp)->udata);\n"
+"\t\t/* ... skipped ... */\n"
+"\t}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1534
+msgid ""
+"Although the sysinit framework is described in the link:/books/developers-"
+"handbook[Developers' Handbook], I will discuss the internals of it."
+msgstr ""
+"Хотя фреймворк sysinit описан в link:/books/developers-handbook[Руководстве "
+"разработчика], я рассмотрю его внутреннее устройство."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1538
+msgid ""
+"Every system initialization object (sysinit object) is created by calling a "
+"SYSINIT() macro. Let us take as example an `announce` sysinit object. This "
+"object prints the copyright message:"
+msgstr ""
+"Каждый объект инициализации системы (объект sysinit) создается путем вызова "
+"макроса SYSINIT(). Возьмем, к примеру, объект sysinit `announce`. Этот "
+"объект выводит сообщение об авторских правах:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1549
+#, no-wrap
+msgid ""
+"sys/kern/init_main.c:\n"
+"static void\n"
+"print_caddr_t(void *data __unused)\n"
+"{\n"
+"\tprintf(\"%s\", (char *)data);\n"
+"}\n"
+"/* ... skipped ... */\n"
+"SYSINIT(announce, SI_SUB_COPYRIGHT, SI_ORDER_FIRST, print_caddr_t, copyright);\n"
+msgstr ""
+"sys/kern/init_main.c:\n"
+"static void\n"
+"print_caddr_t(void *data __unused)\n"
+"{\n"
+"\tprintf(\"%s\", (char *)data);\n"
+"}\n"
+"/* ... skipped ... */\n"
+"SYSINIT(announce, SI_SUB_COPYRIGHT, SI_ORDER_FIRST, print_caddr_t, copyright);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1553
+msgid ""
+"The subsystem ID for this object is SI_SUB_COPYRIGHT (0x0800001). So, the "
+"copyright message will be printed out first, just after the console "
+"initialization."
+msgstr ""
+"Идентификатор подсистемы для этого объекта — SI_SUB_COPYRIGHT (0x0800001). "
+"Таким образом, сообщение об авторских правах будет выведено первым, сразу "
+"после инициализации консоли."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1557
+msgid ""
+"Let us take a look at what exactly the macro `SYSINIT()` does. It expands "
+"to a `C_SYSINIT()` macro. The `C_SYSINIT()` macro then expands to a static "
+"`struct sysinit` structure declaration with another `DATA_SET` macro call:"
+msgstr ""
+"Давайте рассмотрим, что именно делает макрос `SYSINIT()`. Он раскрывается в "
+"макрос `C_SYSINIT()`. Макрос `C_SYSINIT()` затем раскрывается в статическое "
+"объявление структуры `struct sysinit` с вызовом другого макроса `DATA_SET`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1565
+#, no-wrap
+msgid ""
+"/usr/include/sys/kernel.h:\n"
+" #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \\\n"
+" static struct sysinit uniquifier ## _sys_init = { \\ subsystem, \\\n"
+" order, \\ func, \\ (ident) \\ }; \\ DATA_WSET(sysinit_set,uniquifier ##\n"
+" _sys_init);\n"
+msgstr ""
+"/usr/include/sys/kernel.h:\n"
+" #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \\\n"
+" static struct sysinit uniquifier ## _sys_init = { \\ subsystem, \\\n"
+" order, \\ func, \\ (ident) \\ }; \\ DATA_WSET(sysinit_set,uniquifier ##\n"
+" _sys_init);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1569
+#, no-wrap
+msgid ""
+"#define\tSYSINIT(uniquifier, subsystem, order, func, ident)\t\\\n"
+"\tC_SYSINIT(uniquifier, subsystem, order,\t\t\t\\\n"
+"\t(sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)(ident))\n"
+msgstr ""
+"#define\tSYSINIT(uniquifier, subsystem, order, func, ident)\t\\\n"
+"\tC_SYSINIT(uniquifier, subsystem, order,\t\t\t\\\n"
+"\t(sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)(ident))\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1572
+msgid ""
+"The `DATA_SET()` macro expands to a `_MAKE_SET()`, and that macro is the "
+"point where all the sysinit magic is hidden:"
+msgstr ""
+"Макрос `DATA_SET()` раскрывается в `_MAKE_SET()`, и именно в этом макросе "
+"скрыта вся магия инициализации системы:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1578
+#, no-wrap
+msgid ""
+"/usr/include/linker_set.h:\n"
+"#define TEXT_SET(set, sym) _MAKE_SET(set, sym)\n"
+"#define DATA_SET(set, sym) _MAKE_SET(set, sym)\n"
+msgstr ""
+"/usr/include/linker_set.h:\n"
+"#define TEXT_SET(set, sym) _MAKE_SET(set, sym)\n"
+"#define DATA_SET(set, sym) _MAKE_SET(set, sym)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1582
+msgid ""
+"After executing these macros, various sections were made in the kernel, "
+"including`set.sysinit_set`. Running objdump on a kernel binary, you may "
+"notice the presence of such small sections:"
+msgstr ""
+"После выполнения этих макросов в ядре были созданы различные разделы, "
+"включая `set.sysinit_set`. Запустив objdump для бинарного файла ядра, можно "
+"заметить наличие таких небольших разделов:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1594
+#, no-wrap
+msgid ""
+"% llvm-objdump -h /kernel\n"
+"Sections:\n"
+"Idx Name Size VMA Type\n"
+" 10 set_sysctl_set 000021d4 01827078 DATA\n"
+" 16 set_kbddriver_set 00000010 0182a4d0 DATA\n"
+" 20 set_scterm_set 0000000c 0182c75c DATA\n"
+" 21 set_cons_set 00000014 0182c768 DATA\n"
+" 33 set_scrndr_set 00000024 0182c828 DATA\n"
+" 41 set_sysinit_set 000014d8 018fabb0 DATA\n"
+msgstr ""
+"% llvm-objdump -h /kernel\n"
+"Sections:\n"
+"Idx Name Size VMA Type\n"
+" 10 set_sysctl_set 000021d4 01827078 DATA\n"
+" 16 set_kbddriver_set 00000010 0182a4d0 DATA\n"
+" 20 set_scterm_set 0000000c 0182c75c DATA\n"
+" 21 set_cons_set 00000014 0182c768 DATA\n"
+" 33 set_scrndr_set 00000024 0182c828 DATA\n"
+" 41 set_sysinit_set 000014d8 018fabb0 DATA\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1598
+msgid ""
+"This screen dump shows that the size of set.sysinit_set section is 0x14d8 "
+"bytes, so `0x14d8/sizeof(void *)` sysinit objects are compiled into the "
+"kernel. The other sections such as `set.sysctl_set` represent other linker "
+"sets."
+msgstr ""
+"Это содержимое экрана показывает, что размер раздела set.sysinit_set "
+"составляет 0x14d8 байт, поэтому `0x14d8/sizeof(void *)` объектов sysinit "
+"скомпилировано в ядро. Другие разделы, такие как `set.sysctl_set`, "
+"представляют другие наборы компоновщика."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1600
+msgid ""
+"By defining a variable of type `struct sysinit` the content of "
+"`set.sysinit_set` section will be \"collected\" into that variable:"
+msgstr ""
+"Определяя переменную типа `struct sysinit`, содержимое раздела "
+"`set.sysinit_set` будет \"собрано\" в эту переменную:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1605
+#, no-wrap
+msgid ""
+"sys/kern/init_main.c:\n"
+" SET_DECLARE(sysinit_set, struct sysinit);\n"
+msgstr ""
+"sys/kern/init_main.c:\n"
+" SET_DECLARE(sysinit_set, struct sysinit);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1608
+msgid "The `struct sysinit` is defined as follows:"
+msgstr "`struct sysinit` определена следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1618
+#, no-wrap
+msgid ""
+"sys/sys/kernel.h:\n"
+" struct sysinit {\n"
+"\tenum sysinit_sub_id\tsubsystem;\t/* subsystem identifier*/\n"
+"\tenum sysinit_elem_order\torder;\t\t/* init order within subsystem*/\n"
+"\tsysinit_cfunc_t func;\t\t\t/* function\t\t*/\n"
+"\tconst void\t*udata;\t\t\t/* multiplexer/argument */\n"
+"};\n"
+msgstr ""
+"sys/sys/kernel.h:\n"
+" struct sysinit {\n"
+"\tenum sysinit_sub_id\tsubsystem;\t/* subsystem identifier*/\n"
+"\tenum sysinit_elem_order\torder;\t\t/* init order within subsystem*/\n"
+"\tsysinit_cfunc_t func;\t\t\t/* function\t\t*/\n"
+"\tconst void\t*udata;\t\t\t/* multiplexer/argument */\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1623
+msgid ""
+"Returning to the `mi_startup()` discussion, it is must be clear now, how the "
+"sysinit objects are being organized. The `mi_startup()` function sorts them "
+"and calls each. The very last object is the system scheduler:"
+msgstr ""
+"Возвращаясь к обсуждению `mi_startup()`, теперь должно быть понятно, как "
+"организованы объекты sysinit. Функция `mi_startup()` сортирует их и вызывает "
+"каждый. Самый последний объект — это системный планировщик:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1635
+#, no-wrap
+msgid ""
+"/usr/include/sys/kernel.h:\n"
+"enum sysinit_sub_id {\n"
+"\tSI_SUB_DUMMY\t\t= 0x0000000,\t/* not executed; for linker*/\n"
+"\tSI_SUB_DONE\t\t= 0x0000001,\t/* processed*/\n"
+"\tSI_SUB_TUNABLES\t\t= 0x0700000,\t/* establish tunable values */\n"
+"\tSI_SUB_COPYRIGHT\t= 0x0800001,\t/* first use of console*/\n"
+"...\n"
+"\tSI_SUB_LAST\t\t= 0xfffffff\t/* final initialization */\n"
+"};\n"
+msgstr ""
+"/usr/include/sys/kernel.h:\n"
+"enum sysinit_sub_id {\n"
+"\tSI_SUB_DUMMY\t\t= 0x0000000,\t/* not executed; for linker*/\n"
+"\tSI_SUB_DONE\t\t= 0x0000001,\t/* processed*/\n"
+"\tSI_SUB_TUNABLES\t\t= 0x0700000,\t/* establish tunable values */\n"
+"\tSI_SUB_COPYRIGHT\t= 0x0800001,\t/* first use of console*/\n"
+"...\n"
+"\tSI_SUB_LAST\t\t= 0xfffffff\t/* final initialization */\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1640
+msgid ""
+"The system scheduler sysinit object is defined in the file [.filename]#sys/"
+"vm/vm_glue.c#, and the entry point for that object is `scheduler()`. That "
+"function is actually an infinite loop, and it represents a process with PID "
+"0, the swapper process. The thread0 structure, mentioned before, is used to "
+"describe it."
+msgstr ""
+"Системный планировщик sysinit определен в файле [.filename]#sys/vm/"
+"vm_glue.c#, а точка входа для этого объекта — `scheduler()`. Эта функция "
+"фактически представляет собой бесконечный цикл и описывает процесс с PID 0, "
+"известный как процесс swapper. Структура thread0, упомянутая ранее, "
+"используется для его описания."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1642
+msgid ""
+"The first user process, called _init_, is created by the sysinit object "
+"`init`:"
+msgstr ""
+"Первый пользовательский процесс, называемый _init_, создаётся объектом "
+"sysinit `init`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1653
+#, no-wrap
+msgid ""
+"sys/kern/init_main.c:\n"
+"static void\n"
+"create_init(const void *udata __unused)\n"
+"{\n"
+"\tstruct fork_req fr;\n"
+"\tstruct ucred *newcred, *oldcred;\n"
+"\tstruct thread *td;\n"
+"\tint error;\n"
+msgstr ""
+"sys/kern/init_main.c:\n"
+"static void\n"
+"create_init(const void *udata __unused)\n"
+"{\n"
+"\tstruct fork_req fr;\n"
+"\tstruct ucred *newcred, *oldcred;\n"
+"\tstruct thread *td;\n"
+"\tint error;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1686
+#, no-wrap
+msgid ""
+"\tbzero(&fr, sizeof(fr));\n"
+"\tfr.fr_flags = RFFDG | RFPROC | RFSTOPPED;\n"
+"\tfr.fr_procp = &initproc;\n"
+"\terror = fork1(&thread0, &fr);\n"
+"\tif (error)\n"
+"\t\tpanic(\"cannot fork init: %d\\n\", error);\n"
+"\tKASSERT(initproc->p_pid == 1, (\"create_init: initproc->p_pid != 1\"));\n"
+"\t/* divorce init's credentials from the kernel's */\n"
+"\tnewcred = crget();\n"
+"\tsx_xlock(&proctree_lock);\n"
+"\tPROC_LOCK(initproc);\n"
+"\tinitproc->p_flag |= P_SYSTEM | P_INMEM;\n"
+"\tinitproc->p_treeflag |= P_TREE_REAPER;\n"
+"\toldcred = initproc->p_ucred;\n"
+"\tcrcopy(newcred, oldcred);\n"
+"#ifdef MAC\n"
+"\tmac_cred_create_init(newcred);\n"
+"#endif\n"
+"#ifdef AUDIT\n"
+"\taudit_cred_proc1(newcred);\n"
+"#endif\n"
+"\tproc_set_cred(initproc, newcred);\n"
+"\ttd = FIRST_THREAD_IN_PROC(initproc);\n"
+"\tcrcowfree(td);\n"
+"\ttd->td_realucred = crcowget(initproc->p_ucred);\n"
+"\ttd->td_ucred = td->td_realucred;\n"
+"\tPROC_UNLOCK(initproc);\n"
+"\tsx_xunlock(&proctree_lock);\n"
+"\tcrfree(oldcred);\n"
+"\tcpu_fork_kthread_handler(FIRST_THREAD_IN_PROC(initproc), start_init, NULL);\n"
+"}\n"
+"SYSINIT(init, SI_SUB_CREATE_INIT, SI_ORDER_FIRST, create_init, NULL);\n"
+msgstr ""
+"\tbzero(&fr, sizeof(fr));\n"
+"\tfr.fr_flags = RFFDG | RFPROC | RFSTOPPED;\n"
+"\tfr.fr_procp = &initproc;\n"
+"\terror = fork1(&thread0, &fr);\n"
+"\tif (error)\n"
+"\t\tpanic(\"cannot fork init: %d\\n\", error);\n"
+"\tKASSERT(initproc->p_pid == 1, (\"create_init: initproc->p_pid != 1\"));\n"
+"\t/* divorce init's credentials from the kernel's */\n"
+"\tnewcred = crget();\n"
+"\tsx_xlock(&proctree_lock);\n"
+"\tPROC_LOCK(initproc);\n"
+"\tinitproc->p_flag |= P_SYSTEM | P_INMEM;\n"
+"\tinitproc->p_treeflag |= P_TREE_REAPER;\n"
+"\toldcred = initproc->p_ucred;\n"
+"\tcrcopy(newcred, oldcred);\n"
+"#ifdef MAC\n"
+"\tmac_cred_create_init(newcred);\n"
+"#endif\n"
+"#ifdef AUDIT\n"
+"\taudit_cred_proc1(newcred);\n"
+"#endif\n"
+"\tproc_set_cred(initproc, newcred);\n"
+"\ttd = FIRST_THREAD_IN_PROC(initproc);\n"
+"\tcrcowfree(td);\n"
+"\ttd->td_realucred = crcowget(initproc->p_ucred);\n"
+"\ttd->td_ucred = td->td_realucred;\n"
+"\tPROC_UNLOCK(initproc);\n"
+"\tsx_xunlock(&proctree_lock);\n"
+"\tcrfree(oldcred);\n"
+"\tcpu_fork_kthread_handler(FIRST_THREAD_IN_PROC(initproc), start_init, NULL);\n"
+"}\n"
+"SYSINIT(init, SI_SUB_CREATE_INIT, SI_ORDER_FIRST, create_init, NULL);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1692
+msgid ""
+"The function `create_init()` allocates a new process by calling `fork1()`, "
+"but does not mark it runnable. When this new process is scheduled for "
+"execution by the scheduler, the `start_init()` will be called. That "
+"function is defined in [.filename]#init_main.c#. It tries to load and exec "
+"the [.filename]#init# binary, probing [.filename]#/sbin/init# first, then "
+"[.filename]#/sbin/oinit#, [.filename]#/sbin/init.bak#, and finally "
+"[.filename]#/rescue/init#:"
+msgstr ""
+"Функция `create_init()` выделяет новый процесс, вызывая `fork1()`, но не "
+"помечает его как готовый к выполнению. Когда этот новый процесс будет "
+"запланирован для выполнения планировщиком, будет вызвана функция "
+"`start_init()`. Эта функция определена в [.filename]#init_main.c#. Она "
+"пытается загрузить и выполнить бинарный файл [.filename]#init#, сначала "
+"проверяя [.filename]#/sbin/init#, затем [.filename]#/sbin/oinit#, "
+"[.filename]#/sbin/init.bak# и, наконец, [.filename]#/rescue/init#:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/boot/_index.adoc:1702
+#, no-wrap
+msgid ""
+"sys/kern/init_main.c:\n"
+"static char init_path[MAXPATHLEN] =\n"
+"#ifdef\tINIT_PATH\n"
+" __XSTRING(INIT_PATH);\n"
+"#else\n"
+" \"/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init\";\n"
+"#endif\n"
+msgstr ""
+"sys/kern/init_main.c:\n"
+"static char init_path[MAXPATHLEN] =\n"
+"#ifdef\tINIT_PATH\n"
+" __XSTRING(INIT_PATH);\n"
+"#else\n"
+" \"/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init\";\n"
+"#endif\n"
diff --git a/documentation/content/ru/books/arch-handbook/driverbasics/_index.adoc b/documentation/content/ru/books/arch-handbook/driverbasics/_index.adoc
new file mode 100644
index 0000000000..74129e845c
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/driverbasics/_index.adoc
@@ -0,0 +1,347 @@
+---
+description: 'Написание драйверов устройств для FreeBSD'
+next: books/arch-handbook/isa
+params:
+ path: /books/arch-handbook/driverbasics/
+prev: books/arch-handbook/partii
+showBookMenu: true
+tags: ["writing", "device drivers", "KLD", "FreeBSD"]
+title: 'Глава 9. Написание драйверов устройств для FreeBSD'
+weight: 11
+---
+
+[[driverbasics]]
+= Написание драйверов устройств для FreeBSD
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 9
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[driverbasics-intro]]
+== Введение
+
+В этой главе представлено краткое введение в написание драйверов устройств для FreeBSD. Устройство в данном контексте — это термин, используемый в основном для аппаратных компонентов системы, таких как диски, принтеры или графический дисплей с клавиатурой. Драйвер устройства — это программный компонент операционной системы, который управляет конкретным устройством. Также существуют так называемые псевдоустройства, где драйвер эмулирует поведение устройства программно, без использования какого-либо конкретного аппаратного обеспечения. Драйверы устройств могут быть статически скомпилированы в систему или загружены по требованию через механизм динамической загрузки модулей ядра `kld`.
+
+Большинство устройств в операционной системе, подобной UNIX(R), доступны через специальные файлы устройств, также называемые узлами устройств. Эти файлы обычно расположены в каталоге [.filename]#/dev# в иерархии файловой системы.
+
+Драйверы устройств можно условно разделить на две категории: символьные драйверы и драйверы сетевых устройств.
+
+[[driverbasics-kld]]
+== Динамический загрузчик модулей ядра - KLD
+
+Интерфейс kld позволяет системным администраторам динамически добавлять и удалять функциональность в работающей системе. Это позволяет разработчикам драйверов устройств загружать свои новые изменения в работающее ядро без постоянной перезагрузки для проверки изменений.
+
+Интерфейс kld используется с помощью следующих команд:
+
+* `kldload` - загружает новый модуль ядра
+* `kldunload` — выгружает модуль ядра
+* `kldstat` — выводит список загруженных модулей
+
+Каркасная структура модуля ядра
+
+[.programlisting]
+....
+/*
+ * KLD Skeleton
+ * Inspired by Andrew Reiter's Daemonnews article
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h> /* uprintf */
+#include <sys/errno.h>
+#include <sys/param.h> /* defines used in kernel.h */
+#include <sys/module.h>
+#include <sys/kernel.h> /* types used in module initialization */
+
+/*
+ * Load handler that deals with the loading and unloading of a KLD.
+ */
+
+static int
+skel_loader(struct module *m, int what, void *arg)
+{
+ int err = 0;
+
+ switch (what) {
+ case MOD_LOAD: /* kldload */
+ uprintf("Skeleton KLD loaded.\n");
+ break;
+ case MOD_UNLOAD:
+ uprintf("Skeleton KLD unloaded.\n");
+ break;
+ default:
+ err = EOPNOTSUPP;
+ break;
+ }
+ return(err);
+}
+
+/* Declare this module to the rest of the kernel */
+
+static moduledata_t skel_mod = {
+ "skel",
+ skel_loader,
+ NULL
+};
+
+DECLARE_MODULE(skeleton, skel_mod, SI_SUB_KLD, SI_ORDER_ANY);
+....
+
+=== Makefile
+
+FreeBSD предоставляет системный makefile для упрощения компиляции модуля ядра.
+
+[.programlisting]
+....
+SRCS=skeleton.c
+KMOD=skeleton
+
+.include <bsd.kmod.mk>
+....
+
+Запуск `make` с этим makefile создаст файл [.filename]#skeleton.ko#, который можно загрузить в ядро, набрав:
+
+[source, bash]
+....
+# kldload -v ./skeleton.ko
+....
+
+[[driverbasics-char]]
+== Символьные устройства
+
+Драйвер символьного устройства — это драйвер, который передает данные напрямую между устройством и пользовательским процессом. Это наиболее распространенный тип драйвера устройств, и в дереве исходного кода есть множество простых примеров.
+
+Этот простой пример псевдоустройства запоминает все значения, записанные в него, и может затем воспроизводить их при чтении.
+
+.Пример образца драйвера псевдоустройства Echo для FreeBSD 10.X - 12.X
+[example]
+====
+[.programlisting]
+....
+/*
+ * Simple Echo pseudo-device KLD
+ *
+ * Murray Stokely
+ * Søren (Xride) Straarup
+ * Eitan Adler
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h> /* uprintf */
+#include <sys/param.h> /* defines used in kernel.h */
+#include <sys/module.h>
+#include <sys/kernel.h> /* types used in module initialization */
+#include <sys/conf.h> /* cdevsw struct */
+#include <sys/uio.h> /* uio struct */
+#include <sys/malloc.h>
+
+#define BUFFERSIZE 255
+
+/* Function prototypes */
+static d_open_t echo_open;
+static d_close_t echo_close;
+static d_read_t echo_read;
+static d_write_t echo_write;
+
+/* Character device entry points */
+static struct cdevsw echo_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = echo_open,
+ .d_close = echo_close,
+ .d_read = echo_read,
+ .d_write = echo_write,
+ .d_name = "echo",
+};
+
+struct s_echo {
+ char msg[BUFFERSIZE + 1];
+ int len;
+};
+
+/* vars */
+static struct cdev *echo_dev;
+static struct s_echo *echomsg;
+
+MALLOC_DECLARE(M_ECHOBUF);
+MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "buffer for echo module");
+
+/*
+ * This function is called by the kld[un]load(2) system calls to
+ * determine what actions to take when a module is loaded or unloaded.
+ */
+static int
+echo_loader(struct module *m __unused, int what, void *arg __unused)
+{
+ int error = 0;
+
+ switch (what) {
+ case MOD_LOAD: /* kldload */
+ error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK,
+ &echo_dev,
+ &echo_cdevsw,
+ 0,
+ UID_ROOT,
+ GID_WHEEL,
+ 0600,
+ "echo");
+ if (error != 0)
+ break;
+
+ echomsg = malloc(sizeof(*echomsg), M_ECHOBUF, M_WAITOK |
+ M_ZERO);
+ printf("Echo device loaded.\n");
+ break;
+ case MOD_UNLOAD:
+ destroy_dev(echo_dev);
+ free(echomsg, M_ECHOBUF);
+ printf("Echo device unloaded.\n");
+ break;
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+ return (error);
+}
+
+static int
+echo_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused,
+ struct thread *td __unused)
+{
+ int error = 0;
+
+ uprintf("Opened device \"echo\" successfully.\n");
+ return (error);
+}
+
+static int
+echo_close(struct cdev *dev __unused, int fflag __unused, int devtype __unused,
+ struct thread *td __unused)
+{
+
+ uprintf("Closing device \"echo\".\n");
+ return (0);
+}
+
+/*
+ * The read function just takes the buf that was saved via
+ * echo_write() and returns it to userland for accessing.
+ * uio(9)
+ */
+static int
+echo_read(struct cdev *dev __unused, struct uio *uio, int ioflag __unused)
+{
+ size_t amt;
+ int error;
+
+ /*
+ * How big is this read operation? Either as big as the user wants,
+ * or as big as the remaining data. Note that the 'len' does not
+ * include the trailing null character.
+ */
+ amt = MIN(uio->uio_resid, uio->uio_offset >= echomsg->len + 1 ? 0 :
+ echomsg->len + 1 - uio->uio_offset);
+
+ if ((error = uiomove(echomsg->msg, amt, uio)) != 0)
+ uprintf("uiomove failed!\n");
+
+ return (error);
+}
+
+/*
+ * echo_write takes in a character string and saves it
+ * to buf for later accessing.
+ */
+static int
+echo_write(struct cdev *dev __unused, struct uio *uio, int ioflag __unused)
+{
+ size_t amt;
+ int error;
+
+ /*
+ * We either write from the beginning or are appending -- do
+ * not allow random access.
+ */
+ if (uio->uio_offset != 0 && (uio->uio_offset != echomsg->len))
+ return (EINVAL);
+
+ /* This is a new message, reset length */
+ if (uio->uio_offset == 0)
+ echomsg->len = 0;
+
+ /* Copy the string in from user memory to kernel memory */
+ amt = MIN(uio->uio_resid, (BUFFERSIZE - echomsg->len));
+
+ error = uiomove(echomsg->msg + uio->uio_offset, amt, uio);
+
+ /* Now we need to null terminate and record the length */
+ echomsg->len = uio->uio_offset;
+ echomsg->msg[echomsg->len] = 0;
+
+ if (error != 0)
+ uprintf("Write failed: bad address!\n");
+ return (error);
+}
+
+DEV_MODULE(echo, echo_loader, NULL);
+....
+====
+
+Загрузив этот драйвер, попробуйте:
+
+[source, bash]
+....
+# echo -n "Test Data" > /dev/echo
+# cat /dev/echo
+Opened device "echo" successfully.
+Test Data
+Closing device "echo".
+....
+
+Реальные аппаратные устройства описаны в следующей главе.
+
+[[driverbasics-block]]
+== Блочные устройства (удалены)
+
+Другие системы UNIX(R) могут поддерживать второй тип дисковых устройств, известный как блочные устройства. Блочные устройства — это дисковые устройства, для которых ядро предоставляет кэширование. Это кэширование делает блочные устройства практически непригодными или, по крайней мере, опасно ненадёжными. Кэширование изменяет порядок операций записи, лишая приложение возможности точно знать содержимое диска в любой момент времени.
+
+Это делает невозможным предсказуемое и надежное восстановление после сбоев для структур данных на диске (файловых систем, баз данных и т. д.). Поскольку операции записи могут быть отложены, ядро не может сообщить приложению, какая именно операция записи столкнулась с ошибкой, что усугубляет проблему согласованности.
+
+По этой причине ни одно серьезное приложение не полагается на блочные устройства, и фактически почти все приложения, которые обращаются к дискам напрямую, прилагают значительные усилия, чтобы указать, что следует всегда использовать символьные (или "сырые") устройства. Поскольку реализация псевдонимов для каждого диска (раздела) в виде двух устройств с разной семантикой значительно усложняла соответствующий код ядра, FreeBSD отказалась от поддержки кэшируемых дисковых устройств в рамках модернизации инфраструктуры ввода-вывода для дисков.
+
+[[driverbasics-net]]
+== Драйверы сетевых устройств
+
+Драйверы сетевых устройств не используют узлы устройств для доступа. Их выбор основан на других решениях, принимаемых внутри ядра, и вместо вызова open() использование сетевого устройства обычно осуществляется через системный вызов socket(2).
+
+Для получения дополнительной информации см. ifnet(9), исходный текст loopback-устройства.
diff --git a/documentation/content/ru/books/arch-handbook/driverbasics/_index.po b/documentation/content/ru/books/arch-handbook/driverbasics/_index.po
new file mode 100644
index 0000000000..c02c2b0fa1
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/driverbasics/_index.po
@@ -0,0 +1,867 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-09-23 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookdriverbasics_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:14
+#, no-wrap
+msgid "Writing FreeBSD Device Drivers"
+msgstr "Написание драйверов устройств для FreeBSD"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:1
+#, no-wrap
+msgid "Chapter 9. Writing FreeBSD Device Drivers"
+msgstr "Глава 9. Написание драйверов устройств для FreeBSD"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:52
+#, no-wrap
+msgid "Introduction"
+msgstr "Введение"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:55
+msgid ""
+"This chapter provides a brief introduction to writing device drivers for "
+"FreeBSD. A device in this context is a term used mostly for hardware-related "
+"stuff that belongs to the system, like disks, printers, or a graphics "
+"display with its keyboard. A device driver is the software component of the "
+"operating system that controls a specific device. There are also so-called "
+"pseudo-devices where a device driver emulates the behavior of a device in "
+"software without any particular underlying hardware. Device drivers can be "
+"compiled into the system statically or loaded on demand through the dynamic "
+"kernel linker facility `kld'."
+msgstr ""
+"В этой главе представлено краткое введение в написание драйверов устройств "
+"для FreeBSD. Устройство в данном контексте — это термин, используемый в "
+"основном для аппаратных компонентов системы, таких как диски, принтеры или "
+"графический дисплей с клавиатурой. Драйвер устройства — это программный "
+"компонент операционной системы, который управляет конкретным устройством. "
+"Также существуют так называемые псевдоустройства, где драйвер эмулирует "
+"поведение устройства программно, без использования какого-либо конкретного "
+"аппаратного обеспечения. Драйверы устройств могут быть статически "
+"скомпилированы в систему или загружены по требованию через механизм "
+"динамической загрузки модулей ядра `kld`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:57
+msgid ""
+"Most devices in a UNIX(R)-like operating system are accessed through device-"
+"nodes, sometimes also called special files. These files are usually located "
+"under the directory [.filename]#/dev# in the filesystem hierarchy."
+msgstr ""
+"Большинство устройств в операционной системе, подобной UNIX(R), доступны "
+"через специальные файлы устройств, также называемые узлами устройств. Эти "
+"файлы обычно расположены в каталоге [.filename]#/dev# в иерархии файловой "
+"системы."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:59
+msgid ""
+"Device drivers can roughly be broken down into two categories; character and "
+"network device drivers."
+msgstr ""
+"Драйверы устройств можно условно разделить на две категории: символьные "
+"драйверы и драйверы сетевых устройств."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:61
+#, no-wrap
+msgid "Dynamic Kernel Linker Facility - KLD"
+msgstr "Динамический загрузчик модулей ядра - KLD"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:64
+msgid ""
+"The kld interface allows system administrators to dynamically add and remove "
+"functionality from a running system. This allows device driver writers to "
+"load their new changes into a running kernel without constantly rebooting to "
+"test changes."
+msgstr ""
+"Интерфейс kld позволяет системным администраторам динамически добавлять и "
+"удалять функциональность в работающей системе. Это позволяет разработчикам "
+"драйверов устройств загружать свои новые изменения в работающее ядро без "
+"постоянной перезагрузки для проверки изменений."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:66
+msgid "The kld interface is used through:"
+msgstr "Интерфейс kld используется с помощью следующих команд:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:68
+msgid "`kldload` - loads a new kernel module"
+msgstr "`kldload` - загружает новый модуль ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:69
+msgid "`kldunload` - unloads a kernel module"
+msgstr "`kldunload` — выгружает модуль ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:70
+msgid "`kldstat` - lists loaded modules"
+msgstr "`kldstat` — выводит список загруженных модулей"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:72
+msgid "Skeleton Layout of a kernel module"
+msgstr "Каркасная структура модуля ядра"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:79
+#, no-wrap
+msgid ""
+"/*\n"
+" * KLD Skeleton\n"
+" * Inspired by Andrew Reiter's Daemonnews article\n"
+" */\n"
+msgstr ""
+"/*\n"
+" * KLD Skeleton\n"
+" * Inspired by Andrew Reiter's Daemonnews article\n"
+" */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:86
+#, no-wrap
+msgid ""
+"#include <sys/types.h>\n"
+"#include <sys/systm.h> /* uprintf */\n"
+"#include <sys/errno.h>\n"
+"#include <sys/param.h> /* defines used in kernel.h */\n"
+"#include <sys/module.h>\n"
+"#include <sys/kernel.h> /* types used in module initialization */\n"
+msgstr ""
+"#include <sys/types.h>\n"
+"#include <sys/systm.h> /* uprintf */\n"
+"#include <sys/errno.h>\n"
+"#include <sys/param.h> /* defines used in kernel.h */\n"
+"#include <sys/module.h>\n"
+"#include <sys/kernel.h> /* types used in module initialization */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:90
+#, no-wrap
+msgid ""
+"/*\n"
+" * Load handler that deals with the loading and unloading of a KLD.\n"
+" */\n"
+msgstr ""
+"/*\n"
+" * Load handler that deals with the loading and unloading of a KLD.\n"
+" */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:95
+#, no-wrap
+msgid ""
+"static int\n"
+"skel_loader(struct module *m, int what, void *arg)\n"
+"{\n"
+"\tint err = 0;\n"
+msgstr ""
+"static int\n"
+"skel_loader(struct module *m, int what, void *arg)\n"
+"{\n"
+"\tint err = 0;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:109
+#, no-wrap
+msgid ""
+"\tswitch (what) {\n"
+"\tcase MOD_LOAD: /* kldload */\n"
+"\t\tuprintf(\"Skeleton KLD loaded.\\n\");\n"
+"\t\tbreak;\n"
+"\tcase MOD_UNLOAD:\n"
+"\t\tuprintf(\"Skeleton KLD unloaded.\\n\");\n"
+"\t\tbreak;\n"
+"\tdefault:\n"
+"\t\terr = EOPNOTSUPP;\n"
+"\t\tbreak;\n"
+"\t}\n"
+"\treturn(err);\n"
+"}\n"
+msgstr ""
+"\tswitch (what) {\n"
+"\tcase MOD_LOAD: /* kldload */\n"
+"\t\tuprintf(\"Skeleton KLD loaded.\\n\");\n"
+"\t\tbreak;\n"
+"\tcase MOD_UNLOAD:\n"
+"\t\tuprintf(\"Skeleton KLD unloaded.\\n\");\n"
+"\t\tbreak;\n"
+"\tdefault:\n"
+"\t\terr = EOPNOTSUPP;\n"
+"\t\tbreak;\n"
+"\t}\n"
+"\treturn(err);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:111
+#, no-wrap
+msgid "/* Declare this module to the rest of the kernel */\n"
+msgstr "/* Declare this module to the rest of the kernel */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:117
+#, no-wrap
+msgid ""
+"static moduledata_t skel_mod = {\n"
+"\t\"skel\",\n"
+"\tskel_loader,\n"
+"\tNULL\n"
+"};\n"
+msgstr ""
+"static moduledata_t skel_mod = {\n"
+"\t\"skel\",\n"
+"\tskel_loader,\n"
+"\tNULL\n"
+"};\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:119
+#, no-wrap
+msgid "DECLARE_MODULE(skeleton, skel_mod, SI_SUB_KLD, SI_ORDER_ANY);\n"
+msgstr "DECLARE_MODULE(skeleton, skel_mod, SI_SUB_KLD, SI_ORDER_ANY);\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:121
+#, no-wrap
+msgid "Makefile"
+msgstr "Makefile"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:124
+msgid ""
+"FreeBSD provides a system makefile to simplify compiling a kernel module."
+msgstr ""
+"FreeBSD предоставляет системный makefile для упрощения компиляции модуля "
+"ядра."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:129
+#, no-wrap
+msgid ""
+"SRCS=skeleton.c\n"
+"KMOD=skeleton\n"
+msgstr ""
+"SRCS=skeleton.c\n"
+"KMOD=skeleton\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:131
+#, no-wrap
+msgid ".include <bsd.kmod.mk>\n"
+msgstr ".include <bsd.kmod.mk>\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:134
+msgid ""
+"Running `make` with this makefile will create a file "
+"[.filename]#skeleton.ko# that can be loaded into the kernel by typing:"
+msgstr ""
+"Запуск `make` с этим makefile создаст файл [.filename]#skeleton.ko#, который "
+"можно загрузить в ядро, набрав:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:138
+#, no-wrap
+msgid "# kldload -v ./skeleton.ko\n"
+msgstr "# kldload -v ./skeleton.ko\n"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:141
+#, no-wrap
+msgid "Character Devices"
+msgstr "Символьные устройства"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:144
+msgid ""
+"A character device driver is one that transfers data directly to and from a "
+"user process. This is the most common type of device driver and there are "
+"plenty of simple examples in the source tree."
+msgstr ""
+"Драйвер символьного устройства — это драйвер, который передает данные "
+"напрямую между устройством и пользовательским процессом. Это наиболее "
+"распространенный тип драйвера устройств, и в дереве исходного кода есть "
+"множество простых примеров."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:146
+msgid ""
+"This simple example pseudo-device remembers whatever values are written to "
+"it and can then echo them back when read."
+msgstr ""
+"Этот простой пример псевдоустройства запоминает все значения, записанные в "
+"него, и может затем воспроизводить их при чтении."
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:147
+#, no-wrap
+msgid "Example of a Sample Echo Pseudo-Device Driver for FreeBSD 10.X - 12.X"
+msgstr "Пример образца драйвера псевдоустройства Echo для FreeBSD 10.X - 12.X"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:159
+#, no-wrap
+msgid ""
+"/*\n"
+" * Simple Echo pseudo-device KLD\n"
+" *\n"
+" * Murray Stokely\n"
+" * Søren (Xride) Straarup\n"
+" * Eitan Adler\n"
+" */\n"
+msgstr ""
+"/*\n"
+" * Simple Echo pseudo-device KLD\n"
+" *\n"
+" * Murray Stokely\n"
+" * Søren (Xride) Straarup\n"
+" * Eitan Adler\n"
+" */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:168
+#, no-wrap
+msgid ""
+"#include <sys/types.h>\n"
+"#include <sys/systm.h> /* uprintf */\n"
+"#include <sys/param.h> /* defines used in kernel.h */\n"
+"#include <sys/module.h>\n"
+"#include <sys/kernel.h> /* types used in module initialization */\n"
+"#include <sys/conf.h> /* cdevsw struct */\n"
+"#include <sys/uio.h> /* uio struct */\n"
+"#include <sys/malloc.h>\n"
+msgstr ""
+"#include <sys/types.h>\n"
+"#include <sys/systm.h> /* uprintf */\n"
+"#include <sys/param.h> /* defines used in kernel.h */\n"
+"#include <sys/module.h>\n"
+"#include <sys/kernel.h> /* types used in module initialization */\n"
+"#include <sys/conf.h> /* cdevsw struct */\n"
+"#include <sys/uio.h> /* uio struct */\n"
+"#include <sys/malloc.h>\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:170
+#, no-wrap
+msgid "#define BUFFERSIZE 255\n"
+msgstr "#define BUFFERSIZE 255\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:176
+#, no-wrap
+msgid ""
+"/* Function prototypes */\n"
+"static d_open_t echo_open;\n"
+"static d_close_t echo_close;\n"
+"static d_read_t echo_read;\n"
+"static d_write_t echo_write;\n"
+msgstr ""
+"/* Function prototypes */\n"
+"static d_open_t echo_open;\n"
+"static d_close_t echo_close;\n"
+"static d_read_t echo_read;\n"
+"static d_write_t echo_write;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:186
+#, no-wrap
+msgid ""
+"/* Character device entry points */\n"
+"static struct cdevsw echo_cdevsw = {\n"
+"\t.d_version = D_VERSION,\n"
+"\t.d_open = echo_open,\n"
+"\t.d_close = echo_close,\n"
+"\t.d_read = echo_read,\n"
+"\t.d_write = echo_write,\n"
+"\t.d_name = \"echo\",\n"
+"};\n"
+msgstr ""
+"/* Character device entry points */\n"
+"static struct cdevsw echo_cdevsw = {\n"
+"\t.d_version = D_VERSION,\n"
+"\t.d_open = echo_open,\n"
+"\t.d_close = echo_close,\n"
+"\t.d_read = echo_read,\n"
+"\t.d_write = echo_write,\n"
+"\t.d_name = \"echo\",\n"
+"};\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:191
+#, no-wrap
+msgid ""
+"struct s_echo {\n"
+"\tchar msg[BUFFERSIZE + 1];\n"
+"\tint len;\n"
+"};\n"
+msgstr ""
+"struct s_echo {\n"
+"\tchar msg[BUFFERSIZE + 1];\n"
+"\tint len;\n"
+"};\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:195
+#, no-wrap
+msgid ""
+"/* vars */\n"
+"static struct cdev *echo_dev;\n"
+"static struct s_echo *echomsg;\n"
+msgstr ""
+"/* vars */\n"
+"static struct cdev *echo_dev;\n"
+"static struct s_echo *echomsg;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:198
+#, no-wrap
+msgid ""
+"MALLOC_DECLARE(M_ECHOBUF);\n"
+"MALLOC_DEFINE(M_ECHOBUF, \"echobuffer\", \"buffer for echo module\");\n"
+msgstr ""
+"MALLOC_DECLARE(M_ECHOBUF);\n"
+"MALLOC_DEFINE(M_ECHOBUF, \"echobuffer\", \"buffer for echo module\");\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:207
+#, no-wrap
+msgid ""
+"/*\n"
+" * This function is called by the kld[un]load(2) system calls to\n"
+" * determine what actions to take when a module is loaded or unloaded.\n"
+" */\n"
+"static int\n"
+"echo_loader(struct module *m __unused, int what, void *arg __unused)\n"
+"{\n"
+"\tint error = 0;\n"
+msgstr ""
+"/*\n"
+" * This function is called by the kld[un]load(2) system calls to\n"
+" * determine what actions to take when a module is loaded or unloaded.\n"
+" */\n"
+"static int\n"
+"echo_loader(struct module *m __unused, int what, void *arg __unused)\n"
+"{\n"
+"\tint error = 0;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:220
+#, no-wrap
+msgid ""
+"\tswitch (what) {\n"
+"\tcase MOD_LOAD: /* kldload */\n"
+"\t\terror = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK,\n"
+"\t\t &echo_dev,\n"
+"\t\t &echo_cdevsw,\n"
+"\t\t 0,\n"
+"\t\t UID_ROOT,\n"
+"\t\t GID_WHEEL,\n"
+"\t\t 0600,\n"
+"\t\t \"echo\");\n"
+"\t\tif (error != 0)\n"
+"\t\t\tbreak;\n"
+msgstr ""
+"\tswitch (what) {\n"
+"\tcase MOD_LOAD: /* kldload */\n"
+"\t\terror = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK,\n"
+"\t\t &echo_dev,\n"
+"\t\t &echo_cdevsw,\n"
+"\t\t 0,\n"
+"\t\t UID_ROOT,\n"
+"\t\t GID_WHEEL,\n"
+"\t\t 0600,\n"
+"\t\t \"echo\");\n"
+"\t\tif (error != 0)\n"
+"\t\t\tbreak;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:236
+#, no-wrap
+msgid ""
+"\t\techomsg = malloc(sizeof(*echomsg), M_ECHOBUF, M_WAITOK |\n"
+"\t\t M_ZERO);\n"
+"\t\tprintf(\"Echo device loaded.\\n\");\n"
+"\t\tbreak;\n"
+"\tcase MOD_UNLOAD:\n"
+"\t\tdestroy_dev(echo_dev);\n"
+"\t\tfree(echomsg, M_ECHOBUF);\n"
+"\t\tprintf(\"Echo device unloaded.\\n\");\n"
+"\t\tbreak;\n"
+"\tdefault:\n"
+"\t\terror = EOPNOTSUPP;\n"
+"\t\tbreak;\n"
+"\t}\n"
+"\treturn (error);\n"
+"}\n"
+msgstr ""
+"\t\techomsg = malloc(sizeof(*echomsg), M_ECHOBUF, M_WAITOK |\n"
+"\t\t M_ZERO);\n"
+"\t\tprintf(\"Echo device loaded.\\n\");\n"
+"\t\tbreak;\n"
+"\tcase MOD_UNLOAD:\n"
+"\t\tdestroy_dev(echo_dev);\n"
+"\t\tfree(echomsg, M_ECHOBUF);\n"
+"\t\tprintf(\"Echo device unloaded.\\n\");\n"
+"\t\tbreak;\n"
+"\tdefault:\n"
+"\t\terror = EOPNOTSUPP;\n"
+"\t\tbreak;\n"
+"\t}\n"
+"\treturn (error);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:242
+#, no-wrap
+msgid ""
+"static int\n"
+"echo_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused,\n"
+" struct thread *td __unused)\n"
+"{\n"
+"\tint error = 0;\n"
+msgstr ""
+"static int\n"
+"echo_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused,\n"
+" struct thread *td __unused)\n"
+"{\n"
+"\tint error = 0;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:246
+#, no-wrap
+msgid ""
+"\tuprintf(\"Opened device \\\"echo\\\" successfully.\\n\");\n"
+"\treturn (error);\n"
+"}\n"
+msgstr ""
+"\tuprintf(\"Opened device \\\"echo\\\" successfully.\\n\");\n"
+"\treturn (error);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:251
+#, no-wrap
+msgid ""
+"static int\n"
+"echo_close(struct cdev *dev __unused, int fflag __unused, int devtype __unused,\n"
+" struct thread *td __unused)\n"
+"{\n"
+msgstr ""
+"static int\n"
+"echo_close(struct cdev *dev __unused, int fflag __unused, int devtype __unused,\n"
+" struct thread *td __unused)\n"
+"{\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:255
+#, no-wrap
+msgid ""
+"\tuprintf(\"Closing device \\\"echo\\\".\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+msgstr ""
+"\tuprintf(\"Closing device \\\"echo\\\".\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:266
+#, no-wrap
+msgid ""
+"/*\n"
+" * The read function just takes the buf that was saved via\n"
+" * echo_write() and returns it to userland for accessing.\n"
+" * uio(9)\n"
+" */\n"
+"static int\n"
+"echo_read(struct cdev *dev __unused, struct uio *uio, int ioflag __unused)\n"
+"{\n"
+"\tsize_t amt;\n"
+"\tint error;\n"
+msgstr ""
+"/*\n"
+" * The read function just takes the buf that was saved via\n"
+" * echo_write() and returns it to userland for accessing.\n"
+" * uio(9)\n"
+" */\n"
+"static int\n"
+"echo_read(struct cdev *dev __unused, struct uio *uio, int ioflag __unused)\n"
+"{\n"
+"\tsize_t amt;\n"
+"\tint error;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:274
+#, no-wrap
+msgid ""
+"\t/*\n"
+"\t * How big is this read operation? Either as big as the user wants,\n"
+"\t * or as big as the remaining data. Note that the 'len' does not\n"
+"\t * include the trailing null character.\n"
+"\t */\n"
+"\tamt = MIN(uio->uio_resid, uio->uio_offset >= echomsg->len + 1 ? 0 :\n"
+"\t echomsg->len + 1 - uio->uio_offset);\n"
+msgstr ""
+"\t/*\n"
+"\t * How big is this read operation? Either as big as the user wants,\n"
+"\t * or as big as the remaining data. Note that the 'len' does not\n"
+"\t * include the trailing null character.\n"
+"\t */\n"
+"\tamt = MIN(uio->uio_resid, uio->uio_offset >= echomsg->len + 1 ? 0 :\n"
+"\t echomsg->len + 1 - uio->uio_offset);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:277
+#, no-wrap
+msgid ""
+"\tif ((error = uiomove(echomsg->msg, amt, uio)) != 0)\n"
+"\t\tuprintf(\"uiomove failed!\\n\");\n"
+msgstr ""
+"\tif ((error = uiomove(echomsg->msg, amt, uio)) != 0)\n"
+"\t\tuprintf(\"uiomove failed!\\n\");\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:280
+#, no-wrap
+msgid ""
+"\treturn (error);\n"
+"}\n"
+msgstr ""
+"\treturn (error);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:290
+#, no-wrap
+msgid ""
+"/*\n"
+" * echo_write takes in a character string and saves it\n"
+" * to buf for later accessing.\n"
+" */\n"
+"static int\n"
+"echo_write(struct cdev *dev __unused, struct uio *uio, int ioflag __unused)\n"
+"{\n"
+"\tsize_t amt;\n"
+"\tint error;\n"
+msgstr ""
+"/*\n"
+" * echo_write takes in a character string and saves it\n"
+" * to buf for later accessing.\n"
+" */\n"
+"static int\n"
+"echo_write(struct cdev *dev __unused, struct uio *uio, int ioflag __unused)\n"
+"{\n"
+"\tsize_t amt;\n"
+"\tint error;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:297
+#, no-wrap
+msgid ""
+"\t/*\n"
+"\t * We either write from the beginning or are appending -- do\n"
+"\t * not allow random access.\n"
+"\t */\n"
+"\tif (uio->uio_offset != 0 && (uio->uio_offset != echomsg->len))\n"
+"\t\treturn (EINVAL);\n"
+msgstr ""
+"\t/*\n"
+"\t * We either write from the beginning or are appending -- do\n"
+"\t * not allow random access.\n"
+"\t */\n"
+"\tif (uio->uio_offset != 0 && (uio->uio_offset != echomsg->len))\n"
+"\t\treturn (EINVAL);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:301
+#, no-wrap
+msgid ""
+"\t/* This is a new message, reset length */\n"
+"\tif (uio->uio_offset == 0)\n"
+"\t\techomsg->len = 0;\n"
+msgstr ""
+"\t/* This is a new message, reset length */\n"
+"\tif (uio->uio_offset == 0)\n"
+"\t\techomsg->len = 0;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:304
+#, no-wrap
+msgid ""
+"\t/* Copy the string in from user memory to kernel memory */\n"
+"\tamt = MIN(uio->uio_resid, (BUFFERSIZE - echomsg->len));\n"
+msgstr ""
+"\t/* Copy the string in from user memory to kernel memory */\n"
+"\tamt = MIN(uio->uio_resid, (BUFFERSIZE - echomsg->len));\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:306
+#, no-wrap
+msgid "\terror = uiomove(echomsg->msg + uio->uio_offset, amt, uio);\n"
+msgstr "\terror = uiomove(echomsg->msg + uio->uio_offset, amt, uio);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:310
+#, no-wrap
+msgid ""
+"\t/* Now we need to null terminate and record the length */\n"
+"\techomsg->len = uio->uio_offset;\n"
+"\techomsg->msg[echomsg->len] = 0;\n"
+msgstr ""
+"\t/* Now we need to null terminate and record the length */\n"
+"\techomsg->len = uio->uio_offset;\n"
+"\techomsg->msg[echomsg->len] = 0;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:315
+#, no-wrap
+msgid ""
+"\tif (error != 0)\n"
+"\t\tuprintf(\"Write failed: bad address!\\n\");\n"
+"\treturn (error);\n"
+"}\n"
+msgstr ""
+"\tif (error != 0)\n"
+"\t\tuprintf(\"Write failed: bad address!\\n\");\n"
+"\treturn (error);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:317
+#, no-wrap
+msgid "DEV_MODULE(echo, echo_loader, NULL);\n"
+msgstr "DEV_MODULE(echo, echo_loader, NULL);\n"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:321
+msgid "With this driver loaded try:"
+msgstr "Загрузив этот драйвер, попробуйте:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:329
+#, no-wrap
+msgid ""
+"# echo -n \"Test Data\" > /dev/echo\n"
+"# cat /dev/echo\n"
+"Opened device \"echo\" successfully.\n"
+"Test Data\n"
+"Closing device \"echo\".\n"
+msgstr ""
+"# echo -n \"Test Data\" > /dev/echo\n"
+"# cat /dev/echo\n"
+"Opened device \"echo\" successfully.\n"
+"Test Data\n"
+"Closing device \"echo\".\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:332
+msgid "Real hardware devices are described in the next chapter."
+msgstr "Реальные аппаратные устройства описаны в следующей главе."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:334
+#, no-wrap
+msgid "Block Devices (Are Gone)"
+msgstr "Блочные устройства (удалены)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:337
+msgid ""
+"Other UNIX(R) systems may support a second type of disk device known as "
+"block devices. Block devices are disk devices for which the kernel provides "
+"caching. This caching makes block-devices almost unusable, or at least "
+"dangerously unreliable. The caching will reorder the sequence of write "
+"operations, depriving the application of the ability to know the exact disk "
+"contents at any one instant in time."
+msgstr ""
+"Другие системы UNIX(R) могут поддерживать второй тип дисковых устройств, "
+"известный как блочные устройства. Блочные устройства — это дисковые "
+"устройства, для которых ядро предоставляет кэширование. Это кэширование "
+"делает блочные устройства практически непригодными или, по крайней мере, "
+"опасно ненадёжными. Кэширование изменяет порядок операций записи, лишая "
+"приложение возможности точно знать содержимое диска в любой момент времени."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:339
+msgid ""
+"This makes predictable and reliable crash recovery of on-disk data "
+"structures (filesystems, databases, etc.) impossible. Since writes may be "
+"delayed, there is no way the kernel can report to the application which "
+"particular write operation encountered a write error, this further compounds "
+"the consistency problem."
+msgstr ""
+"Это делает невозможным предсказуемое и надежное восстановление после сбоев "
+"для структур данных на диске (файловых систем, баз данных и т. д.). "
+"Поскольку операции записи могут быть отложены, ядро не может сообщить "
+"приложению, какая именно операция записи столкнулась с ошибкой, что "
+"усугубляет проблему согласованности."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:341
+msgid ""
+"For this reason, no serious applications rely on block devices, and in fact, "
+"almost all applications which access disks directly take great pains to "
+"specify that character (or \"raw\") devices should always be used. As the "
+"implementation of the aliasing of each disk (partition) to two devices with "
+"different semantics significantly complicated the relevant kernel code, "
+"FreeBSD dropped support for cached disk devices as part of the modernization "
+"of the disk I/O infrastructure."
+msgstr ""
+"По этой причине ни одно серьезное приложение не полагается на блочные "
+"устройства, и фактически почти все приложения, которые обращаются к дискам "
+"напрямую, прилагают значительные усилия, чтобы указать, что следует всегда "
+"использовать символьные (или \"сырые\") устройства. Поскольку реализация "
+"псевдонимов для каждого диска (раздела) в виде двух устройств с разной "
+"семантикой значительно усложняла соответствующий код ядра, FreeBSD "
+"отказалась от поддержки кэшируемых дисковых устройств в рамках модернизации "
+"инфраструктуры ввода-вывода для дисков."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:343
+#, no-wrap
+msgid "Network Drivers"
+msgstr "Драйверы сетевых устройств"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:346
+msgid ""
+"Drivers for network devices do not use device nodes in order to be accessed. "
+"Their selection is based on other decisions made inside the kernel and "
+"instead of calling open(), use of a network device is generally introduced "
+"by using the system call socket(2)."
+msgstr ""
+"Драйверы сетевых устройств не используют узлы устройств для доступа. Их "
+"выбор основан на других решениях, принимаемых внутри ядра, и вместо вызова "
+"open() использование сетевого устройства обычно осуществляется через "
+"системный вызов socket(2)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/driverbasics/_index.adoc:347
+msgid "For more information see ifnet(9), the source of the loopback device."
+msgstr ""
+"Для получения дополнительной информации см. ifnet(9), исходный текст "
+"loopback-устройства."
diff --git a/documentation/content/ru/books/arch-handbook/isa/_index.adoc b/documentation/content/ru/books/arch-handbook/isa/_index.adoc
new file mode 100644
index 0000000000..c2fead3747
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/isa/_index.adoc
@@ -0,0 +1,1121 @@
+---
+description: 'Драйверы устройств ISA'
+next: books/arch-handbook/pci
+params:
+ path: /books/arch-handbook/isa/
+prev: books/arch-handbook/driverbasics
+showBookMenu: true
+tags: ["ISA", "device drivers", "FreeBSD"]
+title: 'Глава 10. Драйверы устройств ISA'
+weight: 12
+---
+
+[[isa-driver]]
+= Драйверы устройств ISA
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 10
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[isa-driver-synopsis]]
+== Обзор
+
+Эта глава знакомит с вопросами, относящимся к написанию драйвера устройства ISA. Представленный здесь псевдокод довольно детализирован и напоминает реальный код, но всё же остаётся псевдокодом. Он избегает деталей, не относящихся к теме обсуждения. Реальные примеры можно найти в исходном коде настоящих драйверов. В частности, драйверы `ep` и `aha` являются хорошими источниками информации.
+
+[[isa-driver-basics]]
+== Основная информация
+
+Типичному драйверу ISA могут потребоваться следующие включаемые файлы:
+
+[.programlisting]
+....
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <isa/isavar.h>
+#include <isa/pnpvar.h>
+....
+
+Они описывают особенности, специфичные для подсистемы ISA и обобщенной шины.
+
+Подсистема шины реализована в объектно-ориентированном стиле, её основные структуры доступны через методы, связанные с объектами.
+
+Список методов шины, реализуемых драйвером ISA, аналогичен списку для любой другой шины. Для гипотетического драйвера с именем "xxx" они будут:
+
+* `static void xxx_isa_identify (driver_t *, device_t);` Обычно используется для драйверов шины, а не для драйверов устройств. Однако для устройств ISA этот метод может иметь особое применение: если устройство предоставляет специфический (не PnP) способ автоматического обнаружения устройств, эта процедура может его реализовывать.
+* `static int xxx_isa_probe (device_t dev);` Проверка наличия устройства в известном (или PnP) расположении. Эта процедура также может учитывать автоопределение параметров, специфичных для устройства, в случае частично настроенных устройств.
+* `static int xxx_isa_attach (device_t dev);` Подключение и инициализация устройства.
+* `static int xxx_isa_detach (device_t dev);` Отсоединение устройства перед выгрузкой модуля драйвера.
+* `static int xxx_isa_shutdown (device_t dev);` Выполняет завершение работы устройства перед выключением системы.
+* `static int xxx_isa_suspend (device_t dev);` Приостанавливает устройство перед переходом системы в энергосберегающий режим. Также может прервать переход в энергосберегающий режим.
+* `static int xxx_isa_resume (device_t dev);` Возобновляет активность устройства после возврата из энергосберегающего состояния.
+
+`xxx_isa_probe()` и `xxx_isa_attach()` являются обязательными, остальные процедуры опциональны и зависят от потребностей устройства.
+
+Драйвер связан с системой следующим набором описаний.
+
+[.programlisting]
+....
+ /* table of supported bus methods */
+ static device_method_t xxx_isa_methods[] = {
+ /* list all the bus method functions supported by the driver */
+ /* omit the unsupported methods */
+ DEVMETHOD(device_identify, xxx_isa_identify),
+ DEVMETHOD(device_probe, xxx_isa_probe),
+ DEVMETHOD(device_attach, xxx_isa_attach),
+ DEVMETHOD(device_detach, xxx_isa_detach),
+ DEVMETHOD(device_shutdown, xxx_isa_shutdown),
+ DEVMETHOD(device_suspend, xxx_isa_suspend),
+ DEVMETHOD(device_resume, xxx_isa_resume),
+
+ DEVMETHOD_END
+ };
+
+ static driver_t xxx_isa_driver = {
+ "xxx",
+ xxx_isa_methods,
+ sizeof(struct xxx_softc),
+ };
+
+ static devclass_t xxx_devclass;
+
+ DRIVER_MODULE(xxx, isa, xxx_isa_driver, xxx_devclass,
+ load_function, load_argument);
+....
+
+Здесь структура `xxx_softc` — это специфичная для устройства структура, которая содержит приватные данные драйвера и дескрипторы ресурсов драйвера. Код шины автоматически выделяет один дескриптор softc для каждого устройства по мере необходимости.
+
+Если драйвер реализован в виде загружаемого модуля, то `load_function()` вызывается для выполнения специфичной для драйвера инициализации или очистки при загрузке или выгрузке драйвера, а `load_argument` передаётся в качестве одного из её аргументов. Если драйвер не поддерживает динамическую загрузку (другими словами, он всегда должен быть связан с ядром), то эти значения должны быть установлены в 0, и последнее определение будет выглядеть следующим образом:
+
+[.programlisting]
+....
+ DRIVER_MODULE(xxx, isa, xxx_isa_driver,
+ xxx_devclass, 0, 0);
+....
+
+Если драйвер предназначен для устройства с поддержкой PnP, то должна быть определена таблица поддерживаемых PnP ID. Таблица состоит из списка PnP ID, поддерживаемых этим драйвером, и удобочитаемых описаний типов аппаратного обеспечения и моделей, имеющих эти ID. Это выглядит следующим образом:
+
+[.programlisting]
+....
+ static struct isa_pnp_id xxx_pnp_ids[] = {
+ /* a line for each supported PnP ID */
+ { 0x12345678, "Our device model 1234A" },
+ { 0x12345679, "Our device model 1234B" },
+ { 0, NULL }, /* end of table */
+ };
+....
+
+Если драйвер не поддерживает устройства PnP, ему все равно нужна пустая таблица идентификаторов PnP, например:
+
+[.programlisting]
+....
+ static struct isa_pnp_id xxx_pnp_ids[] = {
+ { 0, NULL }, /* end of table */
+ };
+....
+
+[[isa-driver-device-t]]
+== Указатель `device_t`
+
+`device_t` — это тип указателя на структуру устройства. Здесь мы рассматриваем только методы, представляющие интерес с точки зрения разработчика драйверов устройств. Методы для работы со значениями в структуре устройства следующие:
+
+* `device_t device_get_parent(dev)` Получить родительскую шину устройства.
+* `driver_t device_get_driver(dev)` Получить указатель на структуру его драйвера.
+* `char *device_get_name(dev)` Получить имя драйвера, например `"xxx"` в нашем примере.
+* `int device_get_unit(dev)` Получить номер устройства (устройства нумеруются с 0 для устройств, связанных с каждым драйвером).
+* `char *device_get_nameunit(dev)` Получить имя устройства, включая номер юнита, например, "xxx0", "xxx1" и так далее.
+* `char *device_get_desc(dev)` Получить описание устройства. Обычно оно описывает точную модель устройства в удобочитаемом виде.
+* `device_set_desc(dev, desc)` Установить описание. Это заставляет описание устройства указывать на строку desc, которая не может быть освобождена или изменена после этого.
+* `device_set_desc_copy(dev, desc)` Установить описание. Описание копируется во внутренний динамически выделяемый буфер, поэтому строка desc может быть изменена впоследствии без негативных последствий.
+* `void *device_get_softc(dev)` Получить указатель на дескриптор устройства (структура `xxx_softc`), связанный с данным устройством.
+* `u_int32_t device_get_flags(dev)` Получить флаги, указанные для устройства в файле конфигурации.
+
+Функция для удобства `device_printf(dev, fmt, ...)` может использоваться для вывода сообщений из драйвера устройства. Она автоматически добавляет имя устройства и двоеточие перед сообщением.
+
+Методы device_t реализованы в файле [.filename]#kern/bus_subr.c#.
+
+[[isa-driver-config]]
+== Файл конфигурации и порядок определения и проверки при автоматической настройке
+
+Устройства ISA описываются в файле конфигурации ядра следующим образом:
+
+[.programlisting]
+....
+device xxx0 at isa? port 0x300 irq 10 drq 5
+ iomem 0xd0000 flags 0x1 sensitive
+....
+
+Значения порта, IRQ и т. д. преобразуются в ресурсы, связанные с устройством. Они являются необязательными, в зависимости от потребностей устройства и его способностей к автонастройке. Например, некоторым устройствам вообще не нужен DRQ, а некоторые позволяют драйверу читать настройку IRQ из портов конфигурации устройства. Если в машине несколько шин ISA, точная шина может быть указана в строке конфигурации, например `isa0` или `isa1`, иначе устройство будет искаться на всех шинах ISA.
+
+`sensitive` — это ресурс, указывающий, что данное устройство должно быть проверено перед всеми нечувствительными устройствами. Он поддерживается, но, похоже, не используется ни в одном текущем драйвере.
+
+Для устаревших устройств ISA во многих случаях драйверы всё ещё могут определять параметры конфигурации. Однако каждое устройство, которое необходимо настроить в системе, должно иметь строку конфигурации. Если в системе установлено два устройства одного типа, но для соответствующего драйвера есть только одна строка конфигурации, например:
+[.programlisting]
+....
+device xxx0 at isa?
+....
+ тогда будет настроено только одно устройство.
+
+Однако для устройств, поддерживающих автоматическую идентификацию с помощью Plug-n-Play или какого-либо проприетарного протокола, достаточно одной строки конфигурации для настройки всех устройств в системе, как в примере выше или просто:
+
+[.programlisting]
+....
+device xxx at isa?
+....
+
+Если драйвер поддерживает как автоматически определяемые, так и устаревшие устройства, и оба типа установлены одновременно в одной машине, то достаточно описать в конфигурационном файле только устаревшие устройства. Автоматически определяемые устройства будут добавлены автоматически.
+
+При автоматической настройке шины ISA события происходят в следующем порядке:
+
+Все процедуры идентификации драйверов (включая процедуру идентификации PnP, которая определяет все устройства PnP) вызываются в случайном порядке. Когда они идентифицируют устройства, они добавляют их в список на шине ISA. Обычно процедуры идентификации драйверов связывают свои драйверы с новыми устройствами. Процедура идентификации PnP пока не знает о других драйверах, поэтому не связывает ни один из них с новыми устройствами, которые она добавляет.
+
+Устройства PnP переводятся в режим сна с использованием протокола PnP, чтобы предотвратить их обнаружение как устаревших устройств.
+
+Вызываются процедуры обнаружения для устройств, не поддерживающих PnP, помеченных как `sensitive`. Если процедура обнаружения для устройства завершилась успешно, вызывается процедура присоединения для него.
+
+Вызов процедур обнаружения и присоединения всех устройств, не поддерживающих PNP, выполняется аналогичным образом.
+
+Устройства PnP выводятся из состояния сна и получают запрошенные ресурсы: диапазоны адресов ввода-вывода и памяти, IRQ и DRQ, причем все они не конфликтуют с подключенными устаревшими устройствами.
+
+Затем для каждого устройства PnP вызываются процедуры обнаружения всех присутствующих драйверов ISA. Первый драйвер, который заявит о поддержке устройства, будет присоединен. Возможна ситуация, когда несколько драйверов заявят о поддержке устройства с разным приоритетом; в этом случае побеждает драйвер с наивысшим приоритетом. Процедуры обнаружения должны вызывать `ISA_PNP_PROBE()` для сравнения фактического PnP ID со списком ID, поддерживаемых драйвером, и если ID отсутствует в таблице, возвращать ошибку. Это означает, что абсолютно каждый драйвер, даже те, которые не поддерживают никакие PnP устройства, должны вызывать `ISA_PNP_PROBE()`, хотя бы с пустой таблицей PnP ID, чтобы возвращать ошибку для неизвестных PnP устройств.
+
+Процедура обнаружения возвращает положительное значение (код ошибки) в случае ошибки, ноль или отрицательное значение в случае успеха.
+
+Отрицательные возвращаемые значения используются, когда устройство PnP поддерживает несколько интерфейсов. Например, старый совместимый интерфейс и новый расширенный интерфейс, которые поддерживаются разными драйверами. В этом случае оба драйвера обнаружат устройство. Драйвер, возвращающий большее значение в процедуре обнаружения, получает приоритет (другими словами, драйвер, возвращающий 0, имеет наивысший приоритет, возвращающий -1 — следующий, возвращающий -2 — за ним и так далее). В результате устройства, поддерживающие только старый интерфейс, будут обрабатываться старым драйвером (который должен возвращать -1 из процедуры обнаружения), тогда как устройства, поддерживающие также новый интерфейс, будут обрабатываться новым драйвером (который должен возвращать 0 из процедуры обнаружения). Если несколько драйверов возвращают одинаковое значение, побеждает тот, который был вызван первым. Таким образом, если драйвер возвращает значение 0, он может быть уверен, что выиграл арбитраж приоритетов.
+
+Процедуры идентификации, специфичные для устройства, также могут назначать устройству не драйвер, а класс драйверов. Затем все драйверы в этом классе проверяются на совместимость с устройством, как в случае с PnP. Эта возможность не реализована ни в одном существующем драйвере и далее в этом документе не рассматривается.
+
+Поскольку устройства PnP отключены при проверке устаревших устройств, они не будут присоединены дважды (один раз как устаревшие и один раз как PnP). Однако в случае процедур идентификации, зависящих от устройства, ответственность за то, чтобы одно и то же устройство не было присоединено драйвером дважды (один раз как настроенное пользователем устаревшее и один раз как автоматически идентифицированное), лежит на драйвере.
+
+Еще одно практическое следствие для автоматически определяемых устройств (как PnP, так и специфичных для устройства) заключается в том, что флаги не могут быть переданы им из файла конфигурации ядра. Поэтому они либо не должны использовать флаги вообще, либо использовать флаги из устройства unit 0 для всех автоматически определяемых устройств, либо использовать интерфейс sysctl вместо флагов.
+
+Другие нестандартные конфигурации могут быть реализованы путем прямого доступа к ресурсам конфигурации с использованием функций семейств `resource_query_*()` и `resource_*_value()`. Их реализации находятся в [.filename]#kern/subr_bus.c#. Примеры такого использования есть в старом драйвере диска IDE [.filename]#i386/isa/wd.c#. Однако стандартные методы конфигурации всегда должны быть предпочтительны. Оставьте разбор ресурсов конфигурации коду настройки шины.
+
+[[isa-driver-resources]]
+== Ресурсы
+
+Информация, которую пользователь вводит в файл конфигурации ядра, обрабатывается и передаётся ядру в виде ресурсов конфигурации. Эта информация анализируется кодом конфигурации шины и преобразуется в значение структуры `device_t` и связанные с ней ресурсы шины. Драйверы могут напрямую обращаться к ресурсам конфигурации, используя функции `resource_*` для более сложных случаев конфигурации. Однако, как правило, в этом нет необходимости, и это не рекомендуется, поэтому данный вопрос далее не рассматривается.
+
+Ресурсы шины связаны с каждым устройством. Они идентифицируются по типу и номеру внутри типа. Для шины ISA определены следующие типы:
+
+* _SYS_RES_IRQ_ - номер прерывания
+* _SYS_RES_DRQ_ - номер канала ISA DMA
+* _SYS_RES_MEMORY_ - диапазон памяти устройства, отображенный в системное адресное пространство
+* _SYS_RES_IOPORT_ - диапазон регистров ввода-вывода устройства
+
+Перечисление внутри типов начинается с 0, поэтому если устройство имеет две области памяти, оно будет иметь ресурсы типа `SYS_RES_MEMORY` с номерами 0 и 1. Тип ресурса не связан с типом языка C, все значения ресурсов имеют тип `unsigned long` в языке C и должны быть приведены по мере необходимости. Номера ресурсов не обязательно должны быть последовательными, хотя для ISA они обычно таковыми являются. Допустимые номера ресурсов для устройств ISA:
+
+[.programlisting]
+....
+ IRQ: 0-1
+ DRQ: 0-1
+ MEMORY: 0-3
+ IOPORT: 0-7
+....
+
+Все ресурсы представлены в виде диапазонов с начальным значением и количеством. Для ресурсов IRQ и DRQ количество обычно равно 1. Значения для памяти относятся к физическим адресам.
+
+Три типа действий могут выполняться над ресурсами:
+
+* Установка — set/get
+* Выделение — allocate/release
+* Активация — activate/deactivate
+
+Установка задает диапазон, используемый ресурсом. Выделение резервирует запрошенный диапазон, чтобы никакой другой драйвер не смог его зарезервировать (и проверяет, что никакой другой драйвер уже не зарезервировал этот диапазон). Активация делает ресурс доступным для драйвера, выполняя все необходимые для этого действия (например, для памяти это может быть отображение в виртуальное адресное пространство ядра).
+
+Функции для управления ресурсами:
+
+* `int bus_set_resource(device_t dev, int type, int rid, u_long start, u_long count)`
++
+Устанавливает диапазон для ресурса. Возвращает 0 при успешном выполнении, в противном случае — код ошибки. Обычно эта функция возвращает ошибку только в том случае, если одно из значений `type`, `rid`, `start` или `count` выходит за пределы допустимого диапазона.
+
+** dev - устройство драйвера
+** тип - тип ресурса, SYS_RES_*
+** rid - номер ресурса (ID) в пределах типа
+** начало, количество - диапазон ресурсов
+
+* `int bus_get_resource(device_t dev, int type, int rid, u_long *startp, u_long *countp)`
++
+Получает диапазон ресурса. Возвращает 0 при успехе, код ошибки, если ресурс ещё не определён.
+* `u_long bus_get_resource_start(device_t dev, int type, int rid) и u_long bus_get_resource_count (device_t dev, int type, int rid)`
++
+Удобные функции для получения только начала или количества. Возвращают 0 в случае ошибки, поэтому если начало ресурса может законно содержать 0, невозможно определить, является ли значение 0 или произошла ошибка. К счастью, ни один ресурс ISA для дополнительных драйверов не может иметь начальное значение, равное 0.
+* `void bus_delete_resource(device_t dev, int type, int rid)`
++
+Удаляет ресурс, делает его неопределённым.
+* `struct resource * bus_alloc_resource(device_t dev, int type, int *rid, u_long start, u_long end, u_long count, u_int flags)`
++
+Выделяет ресурс как диапазон значений count, не выделенных никем другим, где-то между start и end. Увы, выравнивание не поддерживается. Если ресурс ещё не был установлен, он автоматически создаётся. Специальные значения start равное 0 и end равное ~0 (все единицы) означают, что должны использоваться фиксированные значения, ранее установленные `bus_set_resource()`: start и count как есть, а end=(start+count). В этом случае, если ресурс не был определён ранее, возвращается ошибка. Хотя rid передаётся по ссылке, он нигде не устанавливается кодом выделения ресурсов шины ISA. Другие шины могут использовать иной подход и изменять его.
+
+Флаги представляют собой битовую карту. Интересные для вызывающей стороны флаги:
+
+* _RF_ACTIVE_ - приводит к автоматической активации ресурса после его выделения.
+* _RF_SHAREABLE_ - ресурс может использоваться одновременно несколькими драйверами.
+* _RF_TIMESHARE_ - ресурс может разделяться по времени несколькими драйверами, т.е. выделяться одновременно многими, но активироваться только одним в любой момент времени.
+* Возвращает 0 при ошибке. Выделенные значения могут быть получены из возвращённого дескриптора с использованием методов `rhand_*()`.
+* `int bus_release_resource(device_t dev, int type, int rid, struct resource *r)`
+* Освобождает ресурс, r — это дескриптор, возвращённый `bus_alloc_resource()`. Возвращает 0 при успехе, код ошибки в противном случае.
+* `int bus_activate_resource(device_t dev, int type, int rid, struct resource *r) int bus_deactivate_resource(device_t dev, int type, int rid, struct resource *r)`
+* Активирует или деактивирует ресурс. Возвращает 0 при успехе, в противном случае — код ошибки. Если ресурс разделяемый и в данный момент активирован другим драйвером, возвращается `EBUSY`.
+* `int bus_setup_intr(device_t dev, struct resource *r, int flags, driver_intr_t *handler, void *arg, void **cookiep) int bus_teardown_intr(device_t dev, struct resource *r, void *cookie)`
+* Связывает или разрывает связь обработчика прерывания с устройством. Возвращает 0 при успехе, код ошибки в противном случае.
+* r - активированный обработчик ресурсов, описывающий IRQ
++
+flags - уровень приоритета прерывания, один из:
+
+** `INTR_TYPE_TTY` - терминалы и другие аналогичные символьные устройства. Для их маскировки используйте `spltty()`.
+** `(INTR_TYPE_TTY | INTR_TYPE_FAST)` - терминальные устройства с малым буфером ввода, критичные к потере данных на входе (например, устаревшие последовательные порты). Для их маскирования используйте `spltty()`.
+** `INTR_TYPE_BIO` - блочные устройства, за исключением тех, что подключены к контроллерам CAM. Для их маскирования используйте `splbio()`.
+** `INTR_TYPE_CAM` - контроллеры шины CAM (Common Access Method). Для их маскирования используйте `splcam()`.
+** `INTR_TYPE_NET` - контроллеры сетевых интерфейсов. Для их маскирования используйте `splimp()`.
+** `INTR_TYPE_MISC` — прочие устройства. Нет другого способа их маскировки, кроме `splhigh()`, который маскирует все прерывания.
+
+Когда обработчик прерывания выполняется, все другие прерывания, соответствующие его уровню приоритета, будут заблокированы. Единственное исключение — уровень MISC, для которого никакие другие прерывания не блокируются и который сам не блокируется другими прерываниями.
+
+* _handler_ - указатель на функцию-обработчик, тип driver_intr_t определён как `void driver_intr_t(void *)`
+* _arg_ - аргумент, передаваемый обработчику для идентификации конкретного устройства. Приводится обработчиком от void* к фактическому типу. Старая конвенция для обработчиков прерываний ISA предполагала использование номера устройства в качестве аргумента, новая (рекомендуемая) конвенция предполагает использование указателя на структуру softc устройства.
+* _cookie[p]_ - значение, полученное из `setup()`, используется для идентификации обработчика при передаче в `teardown()`
+
+Определены несколько методов для работы с обработчиками ресурсов (struct resource *). Вот те из них, которые представляют интерес для разработчиков драйверов устройств:
+
+* `u_long rman_get_start(r) u_long rman_get_end(r)` Получают начало и конец выделенного диапазона ресурсов.
+* `void *rman_get_virtual(r)` Получает виртуальный адрес активированного ресурса памяти.
+
+[[isa-driver-busmem]]
+== Отображение памяти шины
+
+Во многих случаях данные передаются между драйвером и устройством через память. Возможны два варианта:
+
+(а) память расположена на карте устройства
+
+(b) память — это основная память компьютера
+
+В случае (a) драйвер всегда копирует данные между памятью на карте и основной памятью по мере необходимости. Для отображения памяти на карте в виртуальное адресное пространство ядра физический адрес и длина памяти на карте должны быть определены как ресурс `SYS_RES_MEMORY`. Этот ресурс может быть затем выделен и активирован, а его виртуальный адрес получен с помощью `rman_get_virtual()`. Более старые драйверы использовали для этой цели функцию `pmap_mapdev()`, которую больше не следует использовать напрямую. Теперь это один из внутренних шагов активации ресурса.
+
+Большинство ISA-карт имеют память, настроенную на физическое расположение в диапазоне 640 КБ–1 МБ. Некоторые ISA-карты требуют большего диапазона памяти, который должен быть размещён ниже 16 МБ (из-за 24-битного ограничения адресации на шине ISA). В таком случае, если в машине больше памяти, чем начальный адрес памяти устройства (другими словами, они пересекаются), необходимо настроить "дыру" в памяти по диапазону адресов, используемому устройствами. Многие BIOS позволяют настроить "дыру" в памяти размером 1 МБ, начиная с 14 МБ или 15 МБ. FreeBSD корректно обрабатывает "дыры" в памяти, если BIOS правильно их сообщает (эта функция может не работать в старых BIOS).
+
+В случае (b) только адрес данных отправляется на устройство, и устройство использует DMA для фактического доступа к данным в основной памяти. Существуют два ограничения: во-первых, карты ISA могут обращаться только к памяти ниже 16 МБ. Во-вторых, непрерывные страницы в виртуальном адресном пространстве могут не быть непрерывными в физическом адресном пространстве, поэтому устройству может потребоваться выполнять операции scatter/gather. Подсистема шины предоставляет готовые решения для некоторых из этих проблем, остальное должно быть реализовано самими драйверами.
+
+Для выделения памяти DMA используются две структуры: `bus_dma_tag_t` и `bus_dmamap_t`. Тег (`tag`) описывает свойства, необходимые для памяти DMA. Карта (`map`) представляет собой блок памяти, выделенный в соответствии с этими свойствами. С одним тегом может быть связано несколько карт.
+
+Теги организованы в иерархию в виде дерева с наследованием свойств. Дочерний тег наследует все требования родительского тега и может делать их более строгими, но никогда более мягкими.
+
+Обычно создается один корневой тег (без родителя) для каждого устройства. Если для каждого устройства требуется несколько областей памяти с разными требованиями, то для каждой из них может быть создан тег как дочерний по отношению к родительскому тегу.
+
+Теги могут быть использованы для создания карты двумя способами.
+
+Сначала может быть выделен (а затем освобожден) блок непрерывной памяти, соответствующий требованиям тега. Обычно это используется для выделения относительно долгоживущих областей памяти для взаимодействия с устройством. Загрузка такой памяти в карту тривиальна: она всегда рассматривается как один блок в соответствующем диапазоне физической памяти.
+
+Второй момент: произвольная область виртуальной памяти может быть загружена в карту. Каждая страница этой памяти будет проверяться на соответствие требованиям карты. Если она соответствует, то остается на своем исходном месте. Если нет, то выделяется новая соответствующая промежуточная страница (bounce page), которая используется как промежуточное хранилище. При записи данных с несоответствующих исходных страниц они сначала копируются на свои промежуточные страницы, а затем передаются с промежуточных страниц на устройство. При чтении данные поступают с устройства на промежуточные страницы, а затем копируются на свои несоответствующие исходные страницы. Процесс копирования между исходными и промежуточными страницами называется синхронизацией. Обычно это используется для каждой передачи: буфер для каждой передачи загружается, передача выполняется, и буфер выгружается.
+
+Функции, работающие с памятью DMA:
+
+* `int bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, bus_size_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr, bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize, int nsegments, bus_size_t maxsegsz, int flags, bus_dma_tag_t *dmat)`
++
+Создать новый тег. Возвращает 0 при успехе, код ошибки в противном случае.
+
+** _parent_ - родительский тег, или NULL для создания тега верхнего уровня.
+** _alignment_ - требуемое физическое выравнивание области памяти, которая будет выделена для этого тега. Используйте значение 1 для "без специфического выравнивания". Применяется только к будущим вызовам `bus_dmamem_alloc()`, но не `bus_dmamap_create()`.
+** _boundary_ - физическая граница адреса, которую нельзя пересекать при выделении памяти. Используйте значение 0 для обозначения "нет границы". Применяется только к будущим вызовам `bus_dmamem_alloc()`, но не `bus_dmamap_create()`. Должна быть степенью 2. Если память планируется использовать в некаскадном режиме DMA (т.е. адреса DMA будут предоставляться не самим устройством, а контроллером DMA ISA), то граница не должна превышать 64 КБ (64*1024) из-за ограничений аппаратного обеспечения DMA.
+** _lowaddr, highaddr_ - названия немного вводят в заблуждение; эти значения используются для ограничения допустимого диапазона физических адресов, используемых для выделения памяти. Точное значение зависит от предполагаемого будущего использования:
+
+*** Для `bus_dmamem_alloc()` все адреса от 0 до lowaddr-1 считаются разрешёнными, а более высокие — запрещёнными.
+*** Для `bus_dmamap_create()` все адреса вне включительного диапазона [lowaddr; highaddr] считаются доступными. Адреса страниц внутри диапазона передаются в функцию-фильтр, которая определяет, доступны ли они. Если функция-фильтр не предоставлена, то весь диапазон считается недоступным.
+*** Для устройств ISA обычные значения (без функции фильтрации) следующие:
++
+lowaddr = BUS_SPACE_MAXADDR_24BIT
++
+highaddr = BUS_SPACE_MAXADDR
+
+** _filter, filterarg_ - функция фильтра и её аргумент. Если передаётся NULL для filter, то весь диапазон [lowaddr, highaddr] считается недоступным при выполнении `bus_dmamap_create()`. В противном случае физический адрес каждой страницы в диапазоне [lowaddr; highaddr] передаётся в функцию фильтра, которая определяет, доступна ли она. Прототип функции фильтра: `int filterfunc(void *arg, bus_addr_t paddr)`. Функция должна вернуть 0, если страница доступна, и ненулевое значение в противном случае.
+** _maxsize_ - максимальный размер памяти (в байтах), который может быть выделен через этот тег. Если сложно оценить или он может быть произвольно большим, для устройств ISA следует использовать значение `BUS_SPACE_MAXSIZE_24BIT`.
+** _nsegments_ - максимальное количество сегментов scatter-gather, поддерживаемых устройством. Если ограничений нет, следует использовать значение `BUS_SPACE_UNRESTRICTED`. Это значение рекомендуется для родительских тегов, фактические ограничения затем будут указаны для дочерних тегов. Теги с nsegments равным `BUS_SPACE_UNRESTRICTED` не могут использоваться для фактической загрузки отображений, они могут применяться только как родительские теги. Практический предел для nsegments составляет около 250-300, более высокие значения вызовут переполнение стека ядра (аппаратное обеспечение обычно не поддерживает такое большое количество scatter-gather буферов в любом случае).
+** _maxsegsz_ — максимальный размер сегмента scatter-gather, поддерживаемый устройством. Максимальное значение для устройства ISA будет `BUS_SPACE_MAXSIZE_24BIT`.
+** _flags_ - битовая маска флагов. Единственный интересный флаг:
+
+*** _BUS_DMA_ALLOCNOW_ - запрашивает выделение всех потенциально необходимых промежуточных страниц при создании тега.
+
+** _dmat_ - указатель на хранилище для нового возвращаемого тега.
+
+* `int bus_dma_tag_destroy(bus_dma_tag_t dmat)`
++
+Уничтожить тег. Возвращает 0 при успехе, код ошибки в противном случае.
++
+dmat - тег, который должен быть уничтожен.
+* `int bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, bus_dmamap_t *mapp)`
++
+Выделить область непрерывной памяти, описанную тегом. Размер выделяемой памяти соответствует maxsize тега. Возвращает 0 при успехе, иначе код ошибки. Результат всё ещё должен быть загружен с помощью `bus_dmamap_load()` перед использованием для получения физического адреса памяти.
+
+** _dmat_ - тег
+** _vaddr_ - указатель на хранилище для возвращаемого виртуального адреса ядра выделенной области.
+** flags - битовая карта флагов. Единственный интересный флаг:
+
+*** _BUS_DMA_NOWAIT_ - если память недоступна немедленно, вернуть ошибку. Если этот флаг не установлен, то процедуре разрешено ожидать до тех пор, пока память не станет доступной.
+
+** _mapp_ - указатель на хранилище для возвращаемой новой карты.
+
+* `void bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)`
++
+Освободить память, выделенную `bus_dmamem_alloc()`. В настоящее время освобождение памяти, выделенной с ограничениями ISA, не реализовано. В связи с этим рекомендуется сохранять и повторно использовать выделенные области как можно дольше. Не следует без необходимости освобождать область и вскоре снова её выделять. Это не означает, что `bus_dmamem_free()` не следует использовать вовсе: есть надежда, что вскоре она будет реализована должным образом.
+
+** _dmat_ - тег
+** _vaddr_ - виртуальный адрес памяти ядра
+** _map_ - карта памяти (как возвращается из `bus_dmamem_alloc()`)
+
+* `int bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)`
++
+Создать карту для тега, которая будет использоваться в `bus_dmamap_load()` позже. Возвращает 0 при успехе, в противном случае — код ошибки.
+
+** _dmat_ - тег
+** _flags_ - теоретически, битовая карта флагов. Однако пока никакие флаги не определены, поэтому в настоящее время значение всегда будет 0.
+** _mapp_ - указатель на хранилище для новой карты, которая будет возвращена
+
+* `int bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)`
++
+Уничтожить карту. Возвращает 0 при успехе, в противном случае — код ошибки.
+
+** dmat - тег, с которым ассоциирована карта
+** map - карта, подлежащая уничтожению
+
+* `int bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, bus_size_t buflen, bus_dmamap_callback_t *callback, void *callback_arg, int flags)`
++
+Загрузить буфер в карту (карта должна быть предварительно создана с помощью `bus_dmamap_create()` или `bus_dmamem_alloc()`). Все страницы буфера проверяются на соответствие требованиям тега, и для несоответствующих выделяются промежуточные страницы. Создается массив дескрипторов физических сегментов и передается в подпрограмму обратного вызова. Ожидается, что эта подпрограмма обработает его каким-либо образом. Количество промежуточных буферов в системе ограничено, поэтому, если эти буферы требуются, но недоступны немедленно, запрос будет поставлен в очередь, и обратный вызов будет выполнен, когда промежуточные буферы станут доступны. Возвращает 0, если обратный вызов был выполнен немедленно, или `EINPROGRESS`, если запрос был поставлен в очередь для выполнения в будущем. В последнем случае синхронизация с подпрограммой обратного вызова, поставленной в очередь, является обязанностью драйвера.
++
+** _dmat_ - тег
+** _map_ - карта
+** _buf_ - виртуальный адрес буфера в пространстве ядра
+** _buflen_ - длина буфера
+** _callback_, `callback_arg` - функция обратного вызова и её аргумент
++
+Прототип функции обратного вызова: `void callback(void *arg, bus_dma_segment_t *seg, int nseg, int error)`
++
+** _arg_ - то же самое, что и callback_arg, переданный в `bus_dmamap_load()`
+** _seg_ - массив дескрипторов сегментов
+** _nseg_ - количество дескрипторов в массиве
+** _error_ - указание на переполнение номера сегмента: если установлено значение `EFBIG`, значит буфер не поместился в максимальное количество сегментов, разрешённых тегом. В этом случае в массиве будет только разрешённое количество дескрипторов. Обработка этой ситуации зависит от драйвера: в зависимости от желаемой семантики он может либо считать это ошибкой, либо разделить буфер на две части и обработать вторую часть отдельно
++
+Каждая запись в массиве segments содержит поля:
++
+** _ds_addr_ - физический адрес шины сегмента
+** _ds_len_ - длина сегмента
++
+* `void bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)`
++
+выгрузить карту.
++
+** _dmat_ - тег
+** _map_ - загруженная карта
++
+* `void bus_dmamap_sync (bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)`
++
+Синхронизировать загруженный буфер с его промежуточными страницами до и после физической передачи на устройство или с устройства. Это функция, которая выполняет все необходимое копирование данных между исходным буфером и его отображенной версией. Буферы должны быть синхронизированы как до, так и после выполнения передачи.
++
+** _dmat_ - тег
+** _map_ - загруженная карта
+** _op_ - тип операции синхронизации для выполнения:
++
+** `BUS_DMASYNC_PREREAD` - перед чтением с устройства в буфер
+** `BUS_DMASYNC_POSTREAD` - после чтения из устройства в буфер
+** `BUS_DMASYNC_PREWRITE` - перед записью буфера в устройство
+** `BUS_DMASYNC_POSTWRITE` - после записи буфера в устройство
+
+На данный момент PREREAD и POSTWRITE являются пустыми операциями, но это может измениться в будущем, поэтому их нельзя игнорировать в драйвере. Синхронизация не требуется для памяти, полученной из `bus_dmamem_alloc()`.
+
+Перед вызовом функции обратного вызова из `bus_dmamap_load()` массив сегментов сохраняется в стеке. Он предварительно выделяется для максимального количества сегментов, разрешенного тегом. В результате этого практический предел количества сегментов на архитектуре i386 составляет около 250-300 (размер стека ядра — 4 КБ минус размер структуры пользователя, размер элемента массива сегментов — 8 байт, и необходимо оставить некоторое пространство). Поскольку массив выделяется исходя из максимального числа, это значение не должно быть установлено выше, чем действительно необходимо. К счастью, для большинства оборудования максимально поддерживаемое количество сегментов значительно ниже. Но если драйвер должен обрабатывать буферы с очень большим количеством сегментов scatter-gather, он должен делать это по частям: загрузить часть буфера, передать его устройству, загрузить следующую часть буфера и так далее.
+
+Еще одно практическое следствие заключается в том, что количество сегментов может ограничивать размер буфера. Если все страницы в буфере окажутся физически несмежными, то максимальный поддерживаемый размер буфера для такого фрагментированного случая будет равен (nsegments * page_size). Например, если поддерживается максимальное количество сегментов, равное 10, то на i386 максимальный гарантированно поддерживаемый размер буфера составит 40K. Если требуется больший размер, то в драйвере следует использовать специальные приемы.
+
+Если оборудование не поддерживает scatter-gather вообще или драйвер хочет поддерживать некоторый размер буфера, даже если он сильно фрагментирован, то решение состоит в выделении непрерывного буфера в драйвере и использовании его в качестве промежуточного хранилища, если исходный буфер не подходит.
+
+Ниже представлены типичные последовательности вызовов при использовании карты в зависимости от её назначения. Символы -> используются для обозначения последовательности во времени.
+
+Для буфера, который остается практически неизменным в течение всего времени между присоединением и отсоединением устройства:
+
+bus_dmamem_alloc -> bus_dmamap_load -> ...use buffer... -> -> bus_dmamap_unload -> bus_dmamem_free
+
+Для буфера, который часто изменяется и передается извне драйвера:
+[.programlisting]
+....
+ bus_dmamap_create ->
+ -> bus_dmamap_load -> bus_dmamap_sync(PRE...) -> do transfer ->
+ -> bus_dmamap_sync(POST...) -> bus_dmamap_unload ->
+ ...
+ -> bus_dmamap_load -> bus_dmamap_sync(PRE...) -> do transfer ->
+ -> bus_dmamap_sync(POST...) -> bus_dmamap_unload ->
+ -> bus_dmamap_destroy
+....
+
+При загрузке карты, созданной `bus_dmamem_alloc()`, переданные адрес и размер буфера должны быть такими же, как использованные в `bus_dmamem_alloc()`. В этом случае гарантируется, что весь буфер будет отображен как один сегмент (так что обратный вызов может основываться на этом предположении) и запрос будет выполнен немедленно (EINPROGRESS никогда не будет возвращен). Все, что нужно сделать обратному вызову в этом случае, — это сохранить физический адрес.
+
+Типичный пример:
+
+[.programlisting]
+....
+ static void
+ alloc_callback(void *arg, bus_dma_segment_t *seg, int nseg, int error)
+ {
+ *(bus_addr_t *)arg = seg[0].ds_addr;
+ }
+
+ ...
+ int error;
+ struct somedata {
+ ....
+ };
+ struct somedata *vsomedata; /* virtual address */
+ bus_addr_t psomedata; /* physical bus-relative address */
+ bus_dma_tag_t tag_somedata;
+ bus_dmamap_t map_somedata;
+ ...
+
+ error=bus_dma_tag_create(parent_tag, alignment,
+ boundary, lowaddr, highaddr, /*filter*/ NULL, /*filterarg*/ NULL,
+ /*maxsize*/ sizeof(struct somedata), /*nsegments*/ 1,
+ /*maxsegsz*/ sizeof(struct somedata), /*flags*/ 0,
+ &tag_somedata);
+ if(error)
+ return error;
+
+ error = bus_dmamem_alloc(tag_somedata, &vsomedata, /* flags*/ 0,
+ &map_somedata);
+ if(error)
+ return error;
+
+ bus_dmamap_load(tag_somedata, map_somedata, (void *)vsomedata,
+ sizeof (struct somedata), alloc_callback,
+ (void *) &psomedata, /*flags*/0);
+....
+
+Выглядит немного длинно и сложно, но это правильный способ. Практическое следствие таково: если несколько областей памяти выделяются всегда вместе, было бы отличной идеей объединить их все в одну структуру и выделять как единое целое (если ограничения выравнивания и границ позволяют).
+
+При загрузке произвольного буфера в карту, созданную `bus_dmamap_create()`, необходимо принять специальные меры для синхронизации с обратным вызовом, если он будет задержан. Код будет выглядеть следующим образом:
+
+[.programlisting]
+....
+ {
+ int s;
+ int error;
+
+ s = splsoftvm();
+ error = bus_dmamap_load(
+ dmat,
+ dmamap,
+ buffer_ptr,
+ buffer_len,
+ callback,
+ /*callback_arg*/ buffer_descriptor,
+ /*flags*/0);
+ if (error == EINPROGRESS) {
+ /*
+ * Do whatever is needed to ensure synchronization
+ * with callback. Callback is guaranteed not to be started
+ * until we do splx() or tsleep().
+ */
+ }
+ splx(s);
+ }
+....
+
+Два возможных подхода для обработки запросов:
+
+1. Если запросы завершаются путём явной пометки их как выполненных (например, запросы CAM), то было бы проще поместить всю дальнейшую обработку в драйвер обратного вызова, который отмечал бы запрос по его завершении. В этом случае не потребуется много дополнительной синхронизации. По соображениям управления потоком может быть полезно заморозить очередь запросов до завершения этого запроса.
+
+2. Если запросы завершаются при возврате функции (например, классические запросы на чтение или запись для символьных устройств), то в дескрипторе буфера должен быть установлен флаг синхронизации и вызвана функция `tsleep()`. Позже, когда будет вызван обратный вызов, он выполнит свою обработку и проверит этот флаг синхронизации. Если флаг установлен, обратный вызов должен инициировать пробуждение. При таком подходе функция обратного вызова может либо выполнить всю необходимую обработку (как в предыдущем случае), либо просто сохранить массив сегментов в дескрипторе буфера. Затем после завершения обратного вызова вызывающая функция может использовать этот сохранённый массив сегментов и выполнить всю обработку.
+
+[[isa-driver-dma]]
+== DMA
+
+Прямой доступ к памяти (DMA) реализован в шине ISA через контроллер DMA (на самом деле их два, но это несущественная деталь). Чтобы сделать ранние устройства ISA простыми и дешёвыми, логика управления шиной и генерации адресов была сосредоточена в контроллере DMA. К счастью, FreeBSD предоставляет набор функций, которые в основном скрывают раздражающие детали работы контроллера DMA от драйверов устройств.
+
+Самый простой случай — для достаточно интеллектуальных устройств. Например, устройства с bus mastering на PCI могут сами генерировать шинные циклы и адреса памяти. Единственное, что им действительно нужно от контроллера DMA, — это арбитраж шины. Для этой цели они притворяются каскадированными подчинёнными контроллерами DMA. И единственное, что требуется от системного контроллера DMA, — это включить каскадный режим на канале DMA, вызвав следующую функцию при присоединении драйвера:
+
+`void isa_dmacascade(int channel_number)`
+
+Все последующие действия выполняются путем программирования устройства. При отсоединении драйвера нет необходимости вызывать функции, связанные с DMA.
+
+Для более простых устройств всё становится сложнее. Используются следующие функции:
+
+* `int isa_dma_acquire(int chanel_number)`
++
+Зарезервировать канал DMA. Возвращает 0 при успехе или EBUSY, если канал уже зарезервирован этим или другим драйвером. Большинство устройств ISA не способны совместно использовать каналы DMA, поэтому обычно эта функция вызывается при присоединении устройства. Это резервирование стало избыточным с появлением современного интерфейса ресурсов шины, но всё ещё должно использоваться в дополнение к последнему. Если резервирование не использовать, то в дальнейшем другие процедуры DMA вызовут панику ядра.
+* `int isa_dma_release(int chanel_number)`
++
+Освободить ранее зарезервированный канал DMA. На момент освобождения канала не должно быть активных передач (дополнительно устройство не должно пытаться инициировать передачу после освобождения канала).
+* `void isa_dmainit(int chan, u_int bouncebufsize)`
++
+Выделить промежуточный буфер для использования с указанным каналом. Запрашиваемый размер буфера не может превышать 64 КБ. Этот промежуточный буфер будет автоматически использован в дальнейшем, если передаваемый буфер окажется не физически непрерывным, находится вне памяти, доступной шине ISA, или пересекает границу 64 КБ. Если передача всегда будет выполняться из буферов, соответствующих этим условиям (например, выделенных с помощью `bus_dmamem_alloc()` с соответствующими ограничениями), то вызов `isa_dmainit()` не требуется. Однако довольно удобно передавать произвольные данные с использованием контроллера DMA. Промежуточный буфер автоматически решит проблемы в ситуациях, когда данные разбросаны в памяти, и их надо собирать.
++
+** _chan_ - номер канала
+** _bouncebufsize_ - размер промежуточного буфера в байтах
++
+* `void isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan)`
++
+Подготовка к началу передачи DMA. Эта функция должна быть вызвана для настройки контроллера DMA перед фактическим началом передачи на устройстве. Она проверяет, что буфер является непрерывным и попадает в диапазон памяти ISA, если нет, то автоматически используется промежуточный буфер. Если требуется промежуточный буфер, но он не настроен с помощью `isa_dmainit()` или слишком мал для запрошенного размера передачи, система перейдет в состояние паники. В случае запроса на запись с промежуточным буфером данные будут автоматически скопированы в этот буфер.
+* flags - битовая маска, определяющая тип выполняемой операции. Бит направления B_READ и B_WRITE являются взаимоисключающими.
++
+** B_READ - чтение с шины ISA в память
+** B_WRITE - запись из памяти на шину ISA
+** B_RAW - если установлен, то контроллер DMA запомнит буфер и после завершения передачи автоматически переинициализирует себя для повторной передачи того же буфера (конечно, драйвер может изменить данные в буфере перед инициированием следующей передачи на устройстве). Если не установлен, то параметры будут работать только для одной передачи, и перед инициированием следующей передачи снова потребуется вызвать `isa_dmastart()`. Использование B_RAW имеет смысл только если промежуточный буфер не используется.
++
+* addr - виртуальный адрес буфера
+* nbytes - длина буфера. Должна быть меньше или равна 64 КБ. Длина 0 не допускается: контроллер DMA интерпретирует это как 64 КБ, в то время как код ядра поймёт это как 0, что приведёт к непредсказуемым последствиям. Для каналов номер 4 и выше длина должна быть чётной, так как эти каналы передают по 2 байта за раз. В случае нечётной длины последний байт не будет передан.
+* chan - номер канала
+* `void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan)`
++
+Синхронизировать память после того, как устройство сообщает о завершении передачи. Если это была операция чтения с промежуточным буфером, то данные будут скопированы из этого буфера в исходный буфер. Аргументы такие же, как у `isa_dmastart()`. Флаг B_RAW разрешён, но он никак не влияет на `isa_dmadone()`.
+* `int isa_dmastatus(int channel_number)`
++
+Возвращает количество оставшихся для передачи байт в текущей передаче. Если флаг B_READ был установлен в `isa_dmastart()`, возвращаемое значение никогда не будет равно нулю. В конце передачи оно автоматически сбрасывается обратно к длине буфера. Обычное использование — проверка количества оставшихся байт после того, как устройство сигнализирует о завершении передачи. Если количество байт не равно 0, то, вероятно, в передаче произошла ошибка.
+* `int isa_dmastop(int channel_number)`
++
+Прерывает текущую передачу и возвращает количество непереданных байтов.
+
+[[isa-driver-probe]]
+== xxx_isa_probe
+
+Эта функция проверяет наличие устройства. Если драйвер поддерживает автоматическое определение некоторых параметров конфигурации устройства (таких как вектор прерывания или адрес памяти), это автоматическое определение должно выполняться в данной процедуре.
+
+Как и для любой другой шины, если устройство не может быть обнаружено, или обнаружено, но не прошло самопроверку, или возникла другая проблема, то возвращается положительное значение ошибки. Значение `ENXIO` должно возвращаться, если устройство отсутствует. Другие значения ошибок могут означать иные условия. Нулевые или отрицательные значения означают успех. Большинство драйверов возвращают ноль в случае успеха.
+
+Отрицательные возвращаемые значения используются, когда устройство PnP поддерживает несколько интерфейсов. Например, старый совместимый интерфейс и новый расширенный интерфейс, которые поддерживаются разными драйверами. В этом случае оба драйвера обнаружат устройство. Драйвер, который возвращает большее значение в процедуре обнаружения, получает приоритет (другими словами, драйвер, возвращающий 0, имеет наивысший приоритет, возвращающий -1 — следующий, возвращающий -2 — за ним и так далее). В результате устройства, поддерживающие только старый интерфейс, будут обрабатываться старым драйвером (который должен возвращать -1 из процедуры probe), а устройства, поддерживающие также новый интерфейс, будут обрабатываться новым драйвером (который должен возвращать 0 из процедуры обнаружения).
+
+Структура дескриптора устройства `xxx_softc` выделяется системой до вызова процедуры обнаружения. Если процедура обнаружения возвращает ошибку, дескриптор автоматически освобождается системой. Поэтому при возникновении ошибки обнаружения драйвер должен убедиться, что все ресурсы, использованные во время обнаружения, освобождены и ничто не мешает безопасному освобождению дескриптора.Если обнаружение завершается успешно, дескриптор сохраняется системой и позже передаётся в процедуру `xxx_isa_attach()`. Если драйвер возвращает отрицательное значение, он не может быть уверен, что получит наивысший приоритет и его процедура присоединения будет вызвана. Поэтому в этом случае он также должен освободить все ресурсы перед возвратом и, если необходимо, выделить их снова в процедуре присоединения. Когда `xxx_isa_probe()` возвращает 0, освобождение ресурсов перед возвратом также является хорошей практикой, и корректно работающий драйвер должен так поступать. Однако в случаях, когда возникают проблемы с освобождением ресурсов, драйверу разрешается сохранять ресурсы между возвратом 0 из процедуры обнаружения и выполнением процедуры присоединения.
+
+Типичная процедура обнаружения начинается с получения дескриптора устройства и номера устройства:
+
+[.programlisting]
+....
+ struct xxx_softc *sc = device_get_softc(dev);
+ int unit = device_get_unit(dev);
+ int pnperror;
+ int error = 0;
+
+ sc->dev = dev; /* link it back */
+ sc->unit = unit;
+....
+
+Затем проверьте устройства PnP. Проверка осуществляется с помощью таблицы, содержащей список PnP ID, поддерживаемых этим драйвером, и удобочитаемые описания моделей устройств, соответствующих этим ID.
+
+[.programlisting]
+....
+
+ pnperror=ISA_PNP_PROBE(device_get_parent(dev), dev,
+ xxx_pnp_ids); if(pnperror == ENXIO) return ENXIO;
+....
+
+Логика работы `ISA_PNP_PROBE` следующая: если данная карта (устройство) не была обнаружена как PnP, то будет возвращено `ENOENT`. Если она была обнаружена как PnP, но её обнаруженный ID не совпадает ни с одним из ID в таблице, то возвращается `ENXIO`. Наконец, если устройство поддерживает PnP и его ID совпадает с одним из ID в таблице, возвращается `0`, а соответствующее описание из таблицы устанавливается с помощью `device_set_desc()`.
+
+Если драйвер поддерживает только устройства PnP, то условие будет выглядеть следующим образом:
+
+[.programlisting]
+....
+ if(pnperror != 0)
+ return pnperror;
+....
+
+Для драйверов, которые не поддерживают PnP, не требуется специальной обработки, так как они передают пустую таблицу идентификаторов PnP и всегда будут получать ENXIO при вызове на PnP-карте.
+
+Функция обнаружения обычно требует как минимум некоторый минимальный набор ресурсов, например, номер порта ввода-вывода, чтобы найти карту и проверить её. В зависимости от оборудования драйвер может автоматически обнаружить другие необходимые ресурсы. Устройства PnP имеют все ресурсы, предварительно установленные подсистемой PnP, поэтому драйверу не нужно обнаруживать их самостоятельно.
+
+Обычно минимальная информация, необходимая для доступа к устройству, — это номер порта ввода-вывода. Затем некоторые устройства позволяют получить остальную информацию из регистров конфигурации устройства (хотя не все устройства это поддерживают). Поэтому сначала мы пытаемся получить начальное значение порта:
+
+[.programlisting]
+....
+ sc->port0 = bus_get_resource_start(dev,
+ SYS_RES_IOPORT, 0 /*rid*/); if(sc->port0 == 0) return ENXIO;
+....
+
+Базовый адрес порта сохраняется в структуре softc для последующего использования. Если он будет использоваться очень часто, то вызов функции ресурса каждый раз будет неприемлемо медленным. Если мы не получаем порт, мы просто возвращаем ошибку. Некоторые драйверы устройств могут вместо этого быть умнее и попытаться обнаружить все возможные порты, например:
+
+[.programlisting]
+....
+
+ /* table of all possible base I/O port addresses for this device */
+ static struct xxx_allports {
+ u_short port; /* port address */
+ short used; /* flag: if this port is already used by some unit */
+ } xxx_allports = {
+ { 0x300, 0 },
+ { 0x320, 0 },
+ { 0x340, 0 },
+ { 0, 0 } /* end of table */
+ };
+
+ ...
+ int port, i;
+ ...
+
+ port = bus_get_resource_start(dev, SYS_RES_IOPORT, 0 /*rid*/);
+ if(port !=0 ) {
+ for(i=0; xxx_allports[i].port!=0; i++) {
+ if(xxx_allports[i].used || xxx_allports[i].port != port)
+ continue;
+
+ /* found it */
+ xxx_allports[i].used = 1;
+ /* do probe on a known port */
+ return xxx_really_probe(dev, port);
+ }
+ return ENXIO; /* port is unknown or already used */
+ }
+
+ /* we get here only if we need to guess the port */
+ for(i=0; xxx_allports[i].port!=0; i++) {
+ if(xxx_allports[i].used)
+ continue;
+
+ /* mark as used - even if we find nothing at this port
+ * at least we won't probe it in future
+ */
+ xxx_allports[i].used = 1;
+
+ error = xxx_really_probe(dev, xxx_allports[i].port);
+ if(error == 0) /* found a device at that port */
+ return 0;
+ }
+ /* probed all possible addresses, none worked */
+ return ENXIO;
+....
+
+Конечно, обычно для таких вещей следует использовать процедуру `identify()` драйвера. Однако может быть одна веская причина, почему лучше сделать это в `probe()`: если этот обнаружение может привести к сбою другого чувствительного устройства. Процедуры обнаружения упорядочены с учетом флага `sensitive`: чувствительные устройства проверяются первыми, а остальные устройства — позже. Но процедуры `identify()` вызываются до любого обнаружения, поэтому они не учитывают чувствительные устройства и могут вызвать их сбой.
+
+Вот, после того как мы получили начальный порт, необходимо установить количество портов (за исключением устройств PnP), так как в файле конфигурации ядра эта информация отсутствует.
+
+[.programlisting]
+....
+
+ if(pnperror /* only for non-PnP devices */
+ && bus_set_resource(dev, SYS_RES_IOPORT, 0, sc->port0,
+ XXX_PORT_COUNT)<0)
+ return ENXIO;
+....
+
+Наконец, выделите и активируйте часть адресного пространства порта (специальные значения start и end означают "используйте те, что мы установили через ``bus_set_resource()``"):
+
+[.programlisting]
+....
+
+ sc->port0_rid = 0;
+ sc->port0_r = bus_alloc_resource(dev, SYS_RES_IOPORT,
+ &sc->port0_rid,
+ /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);
+
+ if(sc->port0_r == NULL)
+ return ENXIO;
+....
+
+Теперь, имея доступ к регистрам с отображением на порты, мы можем каким-либо образом взаимодействовать с устройством и проверить, реагирует ли оно так, как ожидается. Если этого не происходит, вероятно, по этому адресу находится другое устройство или его там нет вовсе.
+
+Обычно драйверы не настраивают обработчики прерываний до вызова процедуры присоединения. Вместо этого они выполняют проверки в режиме опроса, используя функцию `DELAY()` для таймаута. Процедура проверки никогда не должна зависать навсегда, все ожидания ответа от устройства должны выполняться с таймаутами. Если устройство не отвечает в течение заданного времени, вероятно, оно неисправно или неправильно настроено, и драйвер должен вернуть ошибку. При определении интервала таймаута следует давать устройству дополнительное время для надежности: хотя предполагается, что `DELAY()` задерживает выполнение на одинаковое время на любой машине, существует некоторая погрешность, зависящая от конкретного процессора.
+
+Если процедура проверки действительно хочет убедиться, что прерывания работают, она может также настроить и провести обнаружение прерываний. Однако это не рекомендуется.
+
+[.programlisting]
+....
+
+ /* implemented in some very device-specific way */
+ if(error = xxx_probe_ports(sc))
+ goto bad; /* will deallocate the resources before returning */
+....
+
+Функция `xxx_probe_ports()` также может устанавливать описание устройства в зависимости от конкретной модели обнаруженного устройства. Но если поддерживается только одна модель устройства, это можно сделать и жёстко заданным способом. Конечно, для PnP-устройств поддержка PnP автоматически устанавливает описание из таблицы.
+
+[.programlisting]
+....
+ if(pnperror)
+ device_set_desc(dev, "Our device model 1234");
+....
+
+Затем процедура обнаружения должна либо определить диапазоны всех ресурсов, читая регистры конфигурации устройства, либо убедиться, что они были явно заданы пользователем. Мы рассмотрим это на примере встроенной памяти. Процедура обнаружения должна быть как можно менее навязчивой, поэтому выделение и проверку функциональности остальных ресурсов (кроме портов) лучше оставить для процедуры присоединения.
+
+Адрес памяти может быть указан в конфигурационном файле ядра, а на некоторых устройствах он может быть предварительно настроен в энергонезависимых конфигурационных регистрах. Если доступны оба источника, и они различаются, какой из них следует использовать? Вероятно, если пользователь явно указал адрес в конфигурационном файле ядра, он знает, что делает, и этот адрес должен иметь приоритет. Пример реализации может выглядеть так:
+
+[.programlisting]
+....
+
+ /* try to find out the config address first */
+ sc->mem0_p = bus_get_resource_start(dev, SYS_RES_MEMORY, 0 /*rid*/);
+ if(sc->mem0_p == 0) { /* nope, not specified by user */
+ sc->mem0_p = xxx_read_mem0_from_device_config(sc);
+
+ if(sc->mem0_p == 0)
+ /* can't get it from device config registers either */
+ goto bad;
+ } else {
+ if(xxx_set_mem0_address_on_device(sc) < 0)
+ goto bad; /* device does not support that address */
+ }
+
+ /* just like the port, set the memory size,
+ * for some devices the memory size would not be constant
+ * but should be read from the device configuration registers instead
+ * to accommodate different models of devices. Another option would
+ * be to let the user set the memory size as "msize" configuration
+ * resource which will be automatically handled by the ISA bus.
+ */
+ if(pnperror) { /* only for non-PnP devices */
+ sc->mem0_size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0 /*rid*/);
+ if(sc->mem0_size == 0) /* not specified by user */
+ sc->mem0_size = xxx_read_mem0_size_from_device_config(sc);
+
+ if(sc->mem0_size == 0) {
+ /* suppose this is a very old model of device without
+ * auto-configuration features and the user gave no preference,
+ * so assume the minimalistic case
+ * (of course, the real value will vary with the driver)
+ */
+ sc->mem0_size = 8*1024;
+ }
+
+ if(xxx_set_mem0_size_on_device(sc) < 0)
+ goto bad; /* device does not support that size */
+
+ if(bus_set_resource(dev, SYS_RES_MEMORY, /*rid*/0,
+ sc->mem0_p, sc->mem0_size)<0)
+ goto bad;
+ } else {
+ sc->mem0_size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0 /*rid*/);
+ }
+....
+
+Ресурсы для IRQ и DRQ легко проверить по аналогии.
+
+Если всё прошло успешно, то освободите все ресурсы и верните успешный статус.
+
+[.programlisting]
+....
+ xxx_free_resources(sc);
+ return 0;
+....
+
+Наконец, обработайте проблемные ситуации. Все ресурсы должны быть освобождены перед возвратом. Мы используем тот факт, что перед передачей нам структуры `softc` она обнуляется, поэтому мы можем определить, был ли выделен какой-либо ресурс: если его дескриптор не равен нулю.
+
+[.programlisting]
+....
+ bad:
+
+ xxx_free_resources(sc);
+ if(error)
+ return error;
+ else /* exact error is unknown */
+ return ENXIO;
+....
+
+Вот и всё для процедуры обнаружения. Освобождение ресурсов выполняется из нескольких мест, поэтому оно вынесено в функцию, которая может выглядеть так:
+
+[.programlisting]
+....
+static void
+ xxx_free_resources(sc)
+ struct xxx_softc *sc;
+ {
+ /* check every resource and free if not zero */
+
+ /* interrupt handler */
+ if(sc->intr_r) {
+ bus_teardown_intr(sc->dev, sc->intr_r, sc->intr_cookie);
+ bus_release_resource(sc->dev, SYS_RES_IRQ, sc->intr_rid,
+ sc->intr_r);
+ sc->intr_r = 0;
+ }
+
+ /* all kinds of memory maps we could have allocated */
+ if(sc->data_p) {
+ bus_dmamap_unload(sc->data_tag, sc->data_map);
+ sc->data_p = 0;
+ }
+ if(sc->data) { /* sc->data_map may be legitimately equal to 0 */
+ /* the map will also be freed */
+ bus_dmamem_free(sc->data_tag, sc->data, sc->data_map);
+ sc->data = 0;
+ }
+ if(sc->data_tag) {
+ bus_dma_tag_destroy(sc->data_tag);
+ sc->data_tag = 0;
+ }
+
+ ... free other maps and tags if we have them ...
+
+ if(sc->parent_tag) {
+ bus_dma_tag_destroy(sc->parent_tag);
+ sc->parent_tag = 0;
+ }
+
+ /* release all the bus resources */
+ if(sc->mem0_r) {
+ bus_release_resource(sc->dev, SYS_RES_MEMORY, sc->mem0_rid,
+ sc->mem0_r);
+ sc->mem0_r = 0;
+ }
+ ...
+ if(sc->port0_r) {
+ bus_release_resource(sc->dev, SYS_RES_IOPORT, sc->port0_rid,
+ sc->port0_r);
+ sc->port0_r = 0;
+ }
+ }
+....
+
+[[isa-driver-attach]]
+== xxx_isa_attach
+
+Процедура присоединения фактически подключает драйвер к системе, если процедура обнаружения вернула успех, и система решила подключить этот драйвер. Если процедура обнаружения вернула 0, то процедура присоединения может ожидать, что получит структуру устройства `softc` в неизменном виде, как она была установлена процедурой обнаружения. Также, если обнаружение возвращает 0, она можно ожидать, что процедура присоединения для этого устройства будет вызвана в какой-то момент в будущем. Если процедура обнаружения возвращает отрицательное значение, то драйвер не может делать никаких из этих предположений.
+
+Процедура присоединение возвращает 0 при успешном завершении или код ошибки в противном случае.
+
+Процедура присоединения начинается так же, как и процедура обнаружения, с помещения часто используемых данных в более доступные переменные.
+
+[.programlisting]
+....
+ struct xxx_softc *sc = device_get_softc(dev);
+ int unit = device_get_unit(dev);
+ int error = 0;
+....
+
+Затем выделите и активируйте все необходимые ресурсы. Обычно диапазон портов освобождается перед возвратом из обнаружения, поэтому его необходимо выделить снова. Мы предполагаем, что процедура обнаружения корректно установила все диапазоны ресурсов, а также сохранила их в структуре softc. Если процедура обнаружения оставила некоторые ресурсы выделенными, то их не нужно выделять снова (это будет считаться ошибкой).
+
+[.programlisting]
+....
+ sc->port0_rid = 0;
+ sc->port0_r = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port0_rid,
+ /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);
+
+ if(sc->port0_r == NULL)
+ return ENXIO;
+
+ /* on-board memory */
+ sc->mem0_rid = 0;
+ sc->mem0_r = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem0_rid,
+ /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);
+
+ if(sc->mem0_r == NULL)
+ goto bad;
+
+ /* get its virtual address */
+ sc->mem0_v = rman_get_virtual(sc->mem0_r);
+....
+
+Канал запроса DMA (DRQ) выделяется аналогично. Для его инициализации используйте функции семейства `isa_dma*()`. Например:
+
+`isa_dmacascade(sc->drq0);`
+
+Строка запроса прерывания (IRQ) является особенной. Помимо выделения, обработчик прерывания драйвера должен быть связан с ней. Исторически в старых драйверах ISA аргумент, передаваемый системой обработчику прерывания, был номером устройства. Однако в современных драйверах принято передавать указатель на структуру `softc`. Важная причина этого заключается в том, что когда структуры `softc` выделяются динамически, получение номера устройства из `softc` является простым, в то время как получение `softc` из номера устройства затруднительно. Кроме того, такое соглашение делает драйверы для различных шин более однородными и позволяет им совместно использовать код: каждая шина получает свои собственные процедуры обнаружения, присоединения, отсоединения и другие специфичные для шины функции, в то время как основная часть кода драйвера может быть общей для них.
+
+[.programlisting]
+....
+
+ sc->intr_rid = 0;
+ sc->intr_r = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->intr_rid,
+ /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);
+
+ if(sc->intr_r == NULL)
+ goto bad;
+
+ /*
+ * XXX_INTR_TYPE is supposed to be defined depending on the type of
+ * the driver, for example as INTR_TYPE_CAM for a CAM driver
+ */
+ error = bus_setup_intr(dev, sc->intr_r, XXX_INTR_TYPE,
+ (driver_intr_t *) xxx_intr, (void *) sc, &sc->intr_cookie);
+ if(error)
+ goto bad;
+....
+
+Если устройству необходимо выполнять DMA в основную память, то эта память должна быть выделена, как описано ранее:
+
+[.programlisting]
+....
+ error=bus_dma_tag_create(NULL, /*alignment*/ 4,
+ /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR_24BIT,
+ /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL,
+ /*maxsize*/ BUS_SPACE_MAXSIZE_24BIT,
+ /*nsegments*/ BUS_SPACE_UNRESTRICTED,
+ /*maxsegsz*/ BUS_SPACE_MAXSIZE_24BIT, /*flags*/ 0,
+ &sc->parent_tag);
+ if(error)
+ goto bad;
+
+ /* many things get inherited from the parent tag
+ * sc->data is supposed to point to the structure with the shared data,
+ * for example for a ring buffer it could be:
+ * struct {
+ * u_short rd_pos;
+ * u_short wr_pos;
+ * char bf[XXX_RING_BUFFER_SIZE]
+ * } *data;
+ */
+ error=bus_dma_tag_create(sc->parent_tag, 1,
+ 0, BUS_SPACE_MAXADDR, 0, /*filter*/ NULL, /*filterarg*/ NULL,
+ /*maxsize*/ sizeof(* sc->data), /*nsegments*/ 1,
+ /*maxsegsz*/ sizeof(* sc->data), /*flags*/ 0,
+ &sc->data_tag);
+ if(error)
+ goto bad;
+
+ error = bus_dmamem_alloc(sc->data_tag, &sc->data, /* flags*/ 0,
+ &sc->data_map);
+ if(error)
+ goto bad;
+
+ /* xxx_alloc_callback() just saves the physical address at
+ * the pointer passed as its argument, in this case &sc->data_p.
+ * See details in the section on bus memory mapping.
+ * It can be implemented like:
+ *
+ * static void
+ * xxx_alloc_callback(void *arg, bus_dma_segment_t *seg,
+ * int nseg, int error)
+ * {
+ * *(bus_addr_t *)arg = seg[0].ds_addr;
+ * }
+ */
+ bus_dmamap_load(sc->data_tag, sc->data_map, (void *)sc->data,
+ sizeof (* sc->data), xxx_alloc_callback, (void *) &sc->data_p,
+ /*flags*/0);
+....
+
+После выделения всех необходимых ресурсов устройство должно быть инициализировано. Инициализация может включать проверку работоспособности всех ожидаемых функций.
+
+[.programlisting]
+....
+ if(xxx_initialize(sc) < 0)
+ goto bad;
+....
+
+Подсистема шины автоматически выводит на консоль описание устройства, установленное при проверке. Однако, если драйвер хочет вывести дополнительную информацию об устройстве, он может это сделать, например:
+
+[.programlisting]
+....
+
+ device_printf(dev, "has on-card FIFO buffer of %d bytes\n", sc->fifosize);
+....
+
+Если в процессе выполнения инициализации возникают какие-либо проблемы, рекомендуется выводить сообщения об этих проблемах перед возвратом ошибки.
+
+Последним шагом процедуры присоединения является подключение устройства к его функциональной подсистеме в ядре. Конкретный способ зависит от типа драйвера: символьное устройство, блочное устройство, сетевое устройство, устройство шины CAM SCSI и так далее.
+
+Если всё прошло успешно, вернуть успех.
+
+[.programlisting]
+....
+ error = xxx_attach_subsystem(sc);
+ if(error)
+ goto bad;
+
+ return 0;
+....
+
+Наконец, обработаем проблемные ситуации. Все ресурсы должны быть освобождены перед возвратом ошибки. Мы используем тот факт, что перед передачей структуры `softc` она обнуляется, поэтому мы можем определить, был ли выделен какой-либо ресурс: если его дескриптор ненулевой.
+
+[.programlisting]
+....
+ bad:
+
+ xxx_free_resources(sc);
+ if(error)
+ return error;
+ else /* exact error is unknown */
+ return ENXIO;
+....
+
+Вот и всё для процедуры присоединения.
+
+[[isa-driver-detach]]
+== xxx_isa_detach
+
+Если эта функция присутствует в драйвере и драйвер скомпилирован как загружаемый модуль, то драйвер получает возможность быть выгруженным. Это важная функция, если оборудование поддерживает горячее подключение. Однако шина ISA не поддерживает горячее подключение, поэтому эта функция не особенно важна для устройств ISA. Возможность выгрузки драйвера может быть полезна при его отладке, но во многих случаях установка новой версии драйвера потребуется только после того, как старая версия каким-то образом заблокирует систему и перезагрузка все равно будет необходима, поэтому усилия, затраченные на написание процедуры отсоединения, могут не окупиться. Другой аргумент, что выгрузка позволит обновлять драйверы на рабочей машине, кажется в основном теоретическим. Установка новой версии драйвера — это опасная операция, которую никогда не следует выполнять на рабочей машине (и которая не разрешена, когда система работает в безопасном режиме). Тем не менее, процедура отсоединения может быть предоставлена для полноты.
+
+Процедура отсоединения возвращает 0, если драйвер был успешно отсоединён, или код ошибки в противном случае.
+
+Логика отсоединения является зеркальной по отношению к присоединению. Первое, что нужно сделать, — это отсоединить драйвер от его подсистемы ядра. Если устройство в настоящее время открыто, у драйвера есть два варианта: отказаться от отсоединения или принудительно закрыть устройство и продолжить отсоединение. Выбор зависит от возможности конкретной подсистемы ядра выполнить принудительное закрытие и от предпочтений автора драйвера. Как правило, принудительное закрытие кажется предпочтительным вариантом.
+
+[.programlisting]
+....
+ struct xxx_softc *sc = device_get_softc(dev);
+ int error;
+
+ error = xxx_detach_subsystem(sc);
+ if(error)
+ return error;
+....
+
+Далее драйвер может сбросить аппаратное обеспечение в согласованное состояние. Это включает остановку любых текущих передач, отключение каналов DMA и прерываний, чтобы избежать повреждения памяти устройством. Для большинства драйверов это именно то, что делает процедура выключения, поэтому, если она присутствует в драйвере, мы можем просто вызвать её.
+
+`xxx_isa_shutdown(dev);`
+
+И наконец освободить все ресурсы и вернуть успех.
+
+[.programlisting]
+....
+ xxx_free_resources(sc);
+ return 0;
+....
+
+[[isa-driver-shutdown]]
+== xxx_isa_shutdown
+
+Эта процедура вызывается, когда система собирается быть выключена. Ожидается, что она приведет оборудование в согласованное состояние. Для большинства устройств ISA не требуется никаких специальных действий, поэтому функция не является действительно необходимой, так как устройство будет переинициализировано при перезагрузке в любом случае. Однако некоторые устройства должны быть выключены с помощью специальной процедуры, чтобы убедиться, что они будут правильно обнаружены после мягкой перезагрузки (это особенно актуально для многих устройств с проприетарными протоколами идентификации). В любом случае отключение DMA и прерываний в регистрах устройства и остановка любых текущих передач — это хорошая идея. Точные действия зависят от оборудования, поэтому мы не рассматриваем их здесь подробно.
+
+[[isa-driver-intr]]
+== xxx_intr
+
+Обработчик прерывания вызывается при получении прерывания, которое может быть от данного конкретного устройства. Шина ISA не поддерживает разделение прерываний (за исключением некоторых специальных случаев), поэтому на практике, если вызывается обработчик прерывания, то прерывание почти наверняка поступило от его устройства. Тем не менее, обработчик прерывания должен опросить регистры устройства и убедиться, что прерывание было сгенерировано его устройством. Если нет, он должен просто вернуть управление.
+
+Старая практика для драйверов ISA заключалась в получении номера устройства в качестве аргумента. Это устарело, и новые драйверы получают любой аргумент, указанный для них в процедуре присоединения при вызове `bus_setup_intr()`. Согласно новой практике, это должен быть указатель на структуру softc. Таким образом, обработчик прерываний обычно начинается так:
+
+[.programlisting]
+....
+
+ static void
+ xxx_intr(struct xxx_softc *sc)
+ {
+....
+
+Он выполняется на уровне приоритета прерывания, указанном параметром типа прерывания в `bus_setup_intr()`. Это означает, что все остальные прерывания того же типа, а также все программные прерывания, отключены.
+
+Во избежание гонок это обычно записывается в виде цикла:
+
+[.programlisting]
+....
+
+ while(xxx_interrupt_pending(sc)) {
+ xxx_process_interrupt(sc);
+ xxx_acknowledge_interrupt(sc);
+ }
+....
+
+Обработчик прерывания должен подтвердить прерывание только устройству, но не контроллеру прерываний, система позаботится о последнем.
diff --git a/documentation/content/ru/books/arch-handbook/isa/_index.po b/documentation/content/ru/books/arch-handbook/isa/_index.po
new file mode 100644
index 0000000000..d7ed58ee1c
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/isa/_index.po
@@ -0,0 +1,4237 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-09-01 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookisa_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:14
+#, no-wrap
+msgid "ISA Device Drivers"
+msgstr "Драйверы устройств ISA"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1
+#, no-wrap
+msgid "Chapter 10. ISA Device Drivers"
+msgstr "Глава 10. Драйверы устройств ISA"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:52
+#, no-wrap
+msgid "Synopsis"
+msgstr "Обзор"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:55
+msgid ""
+"This chapter introduces the issues relevant to writing a driver for an ISA "
+"device. The pseudo-code presented here is rather detailed and reminiscent of "
+"the real code but is still only pseudo-code. It avoids the details "
+"irrelevant to the subject of the discussion. The real-life examples can be "
+"found in the source code of real drivers. In particular the drivers `ep` and "
+"`aha` are good sources of information."
+msgstr ""
+"Эта глава знакомит с вопросами, относящимся к написанию драйвера устройства "
+"ISA. Представленный здесь псевдокод довольно детализирован и напоминает "
+"реальный код, но всё же остаётся псевдокодом. Он избегает деталей, не "
+"относящихся к теме обсуждения. Реальные примеры можно найти в исходном коде "
+"настоящих драйверов. В частности, драйверы `ep` и `aha` являются хорошими "
+"источниками информации."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:57
+#, no-wrap
+msgid "Basic Information"
+msgstr "Основная информация"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:60
+msgid "A typical ISA driver would need the following include files:"
+msgstr "Типичному драйверу ISA могут потребоваться следующие включаемые файлы:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:68
+#, no-wrap
+msgid ""
+"#include <sys/module.h>\n"
+"#include <sys/bus.h>\n"
+"#include <machine/bus.h>\n"
+"#include <machine/resource.h>\n"
+"#include <sys/rman.h>\n"
+msgstr ""
+"#include <sys/module.h>\n"
+"#include <sys/bus.h>\n"
+"#include <machine/bus.h>\n"
+"#include <machine/resource.h>\n"
+"#include <sys/rman.h>\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:71
+#, no-wrap
+msgid ""
+"#include <isa/isavar.h>\n"
+"#include <isa/pnpvar.h>\n"
+msgstr ""
+"#include <isa/isavar.h>\n"
+"#include <isa/pnpvar.h>\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:74
+msgid "They describe the things specific to the ISA and generic bus subsystem."
+msgstr ""
+"Они описывают особенности, специфичные для подсистемы ISA и обобщенной шины."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:76
+msgid ""
+"The bus subsystem is implemented in an object-oriented fashion, its main "
+"structures are accessed by associated method functions."
+msgstr ""
+"Подсистема шины реализована в объектно-ориентированном стиле, её основные "
+"структуры доступны через методы, связанные с объектами."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:78
+msgid ""
+"The list of bus methods implemented by an ISA driver is like one for any "
+"other bus. For a hypothetical driver named \"xxx\" they would be:"
+msgstr ""
+"Список методов шины, реализуемых драйвером ISA, аналогичен списку для любой "
+"другой шины. Для гипотетического драйвера с именем \"xxx\" они будут:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:80
+msgid ""
+"`static void xxx_isa_identify (driver_t *, device_t);` Normally used for bus "
+"drivers, not device drivers. But for ISA devices this method may have "
+"special use: if the device provides some device-specific (non-PnP) way to "
+"auto-detect devices this routine may implement it."
+msgstr ""
+"`static void xxx_isa_identify (driver_t *, device_t);` Обычно используется "
+"для драйверов шины, а не для драйверов устройств. Однако для устройств ISA "
+"этот метод может иметь особое применение: если устройство предоставляет "
+"специфический (не PnP) способ автоматического обнаружения устройств, эта "
+"процедура может его реализовывать."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:81
+msgid ""
+"`static int xxx_isa_probe (device_t dev);` Probe for a device at a known (or "
+"PnP) location. This routine can also accommodate device-specific auto-"
+"detection of parameters for partially configured devices."
+msgstr ""
+"`static int xxx_isa_probe (device_t dev);` Проверка наличия устройства в "
+"известном (или PnP) расположении. Эта процедура также может учитывать "
+"автоопределение параметров, специфичных для устройства, в случае частично "
+"настроенных устройств."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:82
+msgid ""
+"`static int xxx_isa_attach (device_t dev);` Attach and initialize device."
+msgstr ""
+"`static int xxx_isa_attach (device_t dev);` Подключение и инициализация "
+"устройства."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:83
+msgid ""
+"`static int xxx_isa_detach (device_t dev);` Detach device before unloading "
+"the driver module."
+msgstr ""
+"`static int xxx_isa_detach (device_t dev);` Отсоединение устройства перед "
+"выгрузкой модуля драйвера."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:84
+msgid ""
+"`static int xxx_isa_shutdown (device_t dev);` Execute shutdown of the device "
+"before system shutdown."
+msgstr ""
+"`static int xxx_isa_shutdown (device_t dev);` Выполняет завершение работы "
+"устройства перед выключением системы."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:85
+msgid ""
+"`static int xxx_isa_suspend (device_t dev);` Suspend the device before the "
+"system goes to the power-save state. May also abort transition to the power-"
+"save state."
+msgstr ""
+"`static int xxx_isa_suspend (device_t dev);` Приостанавливает устройство "
+"перед переходом системы в энергосберегающий режим. Также может прервать "
+"переход в энергосберегающий режим."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:86
+msgid ""
+"`static int xxx_isa_resume (device_t dev);` Resume the device activity after "
+"return from power-save state."
+msgstr ""
+"`static int xxx_isa_resume (device_t dev);` Возобновляет активность "
+"устройства после возврата из энергосберегающего состояния."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:88
+msgid ""
+"`xxx_isa_probe()` and `xxx_isa_attach()` are mandatory, the rest of the "
+"routines are optional, depending on the device's needs."
+msgstr ""
+"`xxx_isa_probe()` и `xxx_isa_attach()` являются обязательными, остальные "
+"процедуры опциональны и зависят от потребностей устройства."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:90
+msgid ""
+"The driver is linked to the system with the following set of descriptions."
+msgstr "Драйвер связан с системой следующим набором описаний."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:104
+#, no-wrap
+msgid ""
+" /* table of supported bus methods */\n"
+" static device_method_t xxx_isa_methods[] = {\n"
+" /* list all the bus method functions supported by the driver */\n"
+" /* omit the unsupported methods */\n"
+" DEVMETHOD(device_identify, xxx_isa_identify),\n"
+" DEVMETHOD(device_probe, xxx_isa_probe),\n"
+" DEVMETHOD(device_attach, xxx_isa_attach),\n"
+" DEVMETHOD(device_detach, xxx_isa_detach),\n"
+" DEVMETHOD(device_shutdown, xxx_isa_shutdown),\n"
+" DEVMETHOD(device_suspend, xxx_isa_suspend),\n"
+" DEVMETHOD(device_resume, xxx_isa_resume),\n"
+msgstr ""
+" /* table of supported bus methods */\n"
+" static device_method_t xxx_isa_methods[] = {\n"
+" /* list all the bus method functions supported by the driver */\n"
+" /* omit the unsupported methods */\n"
+" DEVMETHOD(device_identify, xxx_isa_identify),\n"
+" DEVMETHOD(device_probe, xxx_isa_probe),\n"
+" DEVMETHOD(device_attach, xxx_isa_attach),\n"
+" DEVMETHOD(device_detach, xxx_isa_detach),\n"
+" DEVMETHOD(device_shutdown, xxx_isa_shutdown),\n"
+" DEVMETHOD(device_suspend, xxx_isa_suspend),\n"
+" DEVMETHOD(device_resume, xxx_isa_resume),\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:107
+#, no-wrap
+msgid ""
+"\tDEVMETHOD_END\n"
+" };\n"
+msgstr ""
+"\tDEVMETHOD_END\n"
+" };\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:113
+#, no-wrap
+msgid ""
+" static driver_t xxx_isa_driver = {\n"
+" \"xxx\",\n"
+" xxx_isa_methods,\n"
+" sizeof(struct xxx_softc),\n"
+" };\n"
+msgstr ""
+" static driver_t xxx_isa_driver = {\n"
+" \"xxx\",\n"
+" xxx_isa_methods,\n"
+" sizeof(struct xxx_softc),\n"
+" };\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:115
+#, no-wrap
+msgid " static devclass_t xxx_devclass;\n"
+msgstr " static devclass_t xxx_devclass;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:118
+#, no-wrap
+msgid ""
+" DRIVER_MODULE(xxx, isa, xxx_isa_driver, xxx_devclass,\n"
+" load_function, load_argument);\n"
+msgstr ""
+" DRIVER_MODULE(xxx, isa, xxx_isa_driver, xxx_devclass,\n"
+" load_function, load_argument);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:121
+msgid ""
+"Here struct `xxx_softc` is a device-specific structure that contains private "
+"driver data and descriptors for the driver's resources. The bus code "
+"automatically allocates one softc descriptor per device as needed."
+msgstr ""
+"Здесь структура `xxx_softc` — это специфичная для устройства структура, "
+"которая содержит приватные данные драйвера и дескрипторы ресурсов драйвера. "
+"Код шины автоматически выделяет один дескриптор softc для каждого устройства "
+"по мере необходимости."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:123
+msgid ""
+"If the driver is implemented as a loadable module then `load_function()` is "
+"called to do driver-specific initialization or clean-up when the driver is "
+"loaded or unloaded and load_argument is passed as one of its arguments. If "
+"the driver does not support dynamic loading (in other words it must always "
+"be linked into the kernel) then these values should be set to 0 and the last "
+"definition would look like:"
+msgstr ""
+"Если драйвер реализован в виде загружаемого модуля, то `load_function()` "
+"вызывается для выполнения специфичной для драйвера инициализации или очистки "
+"при загрузке или выгрузке драйвера, а `load_argument` передаётся в качестве "
+"одного из её аргументов. Если драйвер не поддерживает динамическую загрузку "
+"(другими словами, он всегда должен быть связан с ядром), то эти значения "
+"должны быть установлены в 0, и последнее определение будет выглядеть "
+"следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:128
+#, no-wrap
+msgid ""
+" DRIVER_MODULE(xxx, isa, xxx_isa_driver,\n"
+" xxx_devclass, 0, 0);\n"
+msgstr ""
+" DRIVER_MODULE(xxx, isa, xxx_isa_driver,\n"
+" xxx_devclass, 0, 0);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:131
+msgid ""
+"If the driver is for a device which supports PnP then a table of supported "
+"PnP IDs must be defined. The table consists of a list of PnP IDs supported "
+"by this driver and human-readable descriptions of the hardware types and "
+"models having these IDs. It looks like:"
+msgstr ""
+"Если драйвер предназначен для устройства с поддержкой PnP, то должна быть "
+"определена таблица поддерживаемых PnP ID. Таблица состоит из списка PnP ID, "
+"поддерживаемых этим драйвером, и удобочитаемых описаний типов аппаратного "
+"обеспечения и моделей, имеющих эти ID. Это выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:140
+#, no-wrap
+msgid ""
+" static struct isa_pnp_id xxx_pnp_ids[] = {\n"
+" /* a line for each supported PnP ID */\n"
+" { 0x12345678, \"Our device model 1234A\" },\n"
+" { 0x12345679, \"Our device model 1234B\" },\n"
+" { 0, NULL }, /* end of table */\n"
+" };\n"
+msgstr ""
+" static struct isa_pnp_id xxx_pnp_ids[] = {\n"
+" /* a line for each supported PnP ID */\n"
+" { 0x12345678, \"Our device model 1234A\" },\n"
+" { 0x12345679, \"Our device model 1234B\" },\n"
+" { 0, NULL }, /* end of table */\n"
+" };\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:143
+msgid ""
+"If the driver does not support PnP devices it still needs an empty PnP ID "
+"table, like:"
+msgstr ""
+"Если драйвер не поддерживает устройства PnP, ему все равно нужна пустая "
+"таблица идентификаторов PnP, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:149
+#, no-wrap
+msgid ""
+" static struct isa_pnp_id xxx_pnp_ids[] = {\n"
+" { 0, NULL }, /* end of table */\n"
+" };\n"
+msgstr ""
+" static struct isa_pnp_id xxx_pnp_ids[] = {\n"
+" { 0, NULL }, /* end of table */\n"
+" };\n"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:152
+#, no-wrap
+msgid "`device_t` Pointer"
+msgstr "Указатель `device_t`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:155
+msgid ""
+"`device_t` is the pointer type for the device structure. Here we consider "
+"only the methods interesting from the device driver writer's standpoint. The "
+"methods to manipulate values in the device structure are:"
+msgstr ""
+"`device_t` — это тип указателя на структуру устройства. Здесь мы "
+"рассматриваем только методы, представляющие интерес с точки зрения "
+"разработчика драйверов устройств. Методы для работы со значениями в "
+"структуре устройства следующие:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:157
+msgid "`device_t device_get_parent(dev)` Get the parent bus of a device."
+msgstr ""
+"`device_t device_get_parent(dev)` Получить родительскую шину устройства."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:158
+msgid "`driver_t device_get_driver(dev)` Get pointer to its driver structure."
+msgstr ""
+"`driver_t device_get_driver(dev)` Получить указатель на структуру его "
+"драйвера."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:159
+msgid ""
+"`char *device_get_name(dev)` Get the driver name, such as `\"xxx\"` for our "
+"example."
+msgstr ""
+"`char *device_get_name(dev)` Получить имя драйвера, например `\"xxx\"` в "
+"нашем примере."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:160
+msgid ""
+"`int device_get_unit(dev)` Get the unit number (units are numbered from 0 "
+"for the devices associated with each driver)."
+msgstr ""
+"`int device_get_unit(dev)` Получить номер устройства (устройства нумеруются "
+"с 0 для устройств, связанных с каждым драйвером)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:161
+msgid ""
+"`char *device_get_nameunit(dev)` Get the device name including the unit "
+"number, such as \"xxx0\", \"xxx1\" and so on."
+msgstr ""
+"`char *device_get_nameunit(dev)` Получить имя устройства, включая номер "
+"юнита, например, \"xxx0\", \"xxx1\" и так далее."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:162
+msgid ""
+"`char *device_get_desc(dev)` Get the device description. Normally it "
+"describes the exact model of device in human-readable form."
+msgstr ""
+"`char *device_get_desc(dev)` Получить описание устройства. Обычно оно "
+"описывает точную модель устройства в удобочитаемом виде."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:163
+msgid ""
+"`device_set_desc(dev, desc)` Set the description. This makes the device "
+"description point to the string desc which may not be deallocated or changed "
+"after that."
+msgstr ""
+"`device_set_desc(dev, desc)` Установить описание. Это заставляет описание "
+"устройства указывать на строку desc, которая не может быть освобождена или "
+"изменена после этого."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:164
+msgid ""
+"`device_set_desc_copy(dev, desc)` Set the description. The description is "
+"copied into an internal dynamically allocated buffer, so the string desc may "
+"be changed afterwards without adverse effects."
+msgstr ""
+"`device_set_desc_copy(dev, desc)` Установить описание. Описание копируется "
+"во внутренний динамически выделяемый буфер, поэтому строка desc может быть "
+"изменена впоследствии без негативных последствий."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:165
+msgid ""
+"`void *device_get_softc(dev)` Get pointer to the device descriptor (struct "
+"`xxx_softc`) associated with this device."
+msgstr ""
+"`void *device_get_softc(dev)` Получить указатель на дескриптор устройства "
+"(структура `xxx_softc`), связанный с данным устройством."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:166
+msgid ""
+"`u_int32_t device_get_flags(dev)` Get the flags specified for the device in "
+"the configuration file."
+msgstr ""
+"`u_int32_t device_get_flags(dev)` Получить флаги, указанные для устройства в "
+"файле конфигурации."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:168
+msgid ""
+"A convenience function `device_printf(dev, fmt, ...)` may be used to print "
+"the messages from the device driver. It automatically prepends the unitname "
+"and colon to the message."
+msgstr ""
+"Функция для удобства `device_printf(dev, fmt, ...)` может использоваться для "
+"вывода сообщений из драйвера устройства. Она автоматически добавляет имя "
+"устройства и двоеточие перед сообщением."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:170
+msgid ""
+"The device_t methods are implemented in the file [.filename]#kern/"
+"bus_subr.c#."
+msgstr "Методы device_t реализованы в файле [.filename]#kern/bus_subr.c#."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:172
+#, no-wrap
+msgid "Configuration File and the Order of Identifying and Probing During Auto-Configuration"
+msgstr "Файл конфигурации и порядок определения и проверки при автоматической настройке"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:175
+msgid "The ISA devices are described in the kernel configuration file like:"
+msgstr ""
+"Устройства ISA описываются в файле конфигурации ядра следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:180
+#, no-wrap
+msgid ""
+"device xxx0 at isa? port 0x300 irq 10 drq 5\n"
+" iomem 0xd0000 flags 0x1 sensitive\n"
+msgstr ""
+"device xxx0 at isa? port 0x300 irq 10 drq 5\n"
+" iomem 0xd0000 flags 0x1 sensitive\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:183
+msgid ""
+"The values of port, IRQ and so on are converted to the resource values "
+"associated with the device. They are optional, depending on the device's "
+"needs and abilities for auto-configuration. For example, some devices do not "
+"need DRQ at all and some allow the driver to read the IRQ setting from the "
+"device configuration ports. If a machine has multiple ISA buses the exact "
+"bus may be specified in the configuration line, like `isa0` or `isa1`, "
+"otherwise the device would be searched for on all the ISA buses."
+msgstr ""
+"Значения порта, IRQ и т. д. преобразуются в ресурсы, связанные с "
+"устройством. Они являются необязательными, в зависимости от потребностей "
+"устройства и его способностей к автонастройке. Например, некоторым "
+"устройствам вообще не нужен DRQ, а некоторые позволяют драйверу читать "
+"настройку IRQ из портов конфигурации устройства. Если в машине несколько шин "
+"ISA, точная шина может быть указана в строке конфигурации, например `isa0` "
+"или `isa1`, иначе устройство будет искаться на всех шинах ISA."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:185
+msgid ""
+"`sensitive` is a resource requesting that this device must be probed before "
+"all non-sensitive devices. It is supported but does not seem to be used in "
+"any current driver."
+msgstr ""
+"`sensitive` — это ресурс, указывающий, что данное устройство должно быть "
+"проверено перед всеми нечувствительными устройствами. Он поддерживается, но, "
+"похоже, не используется ни в одном текущем драйвере."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:187
+msgid ""
+"For legacy ISA devices in many cases the drivers are still able to detect "
+"the configuration parameters. But each device to be configured in the system "
+"must have a config line. If two devices of some type are installed in the "
+"system but there is only one configuration line for the corresponding "
+"driver, ie:"
+msgstr ""
+"Для устаревших устройств ISA во многих случаях драйверы всё ещё могут "
+"определять параметры конфигурации. Однако каждое устройство, которое "
+"необходимо настроить в системе, должно иметь строку конфигурации. Если в "
+"системе установлено два устройства одного типа, но для соответствующего "
+"драйвера есть только одна строка конфигурации, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:190
+#, no-wrap
+msgid "device xxx0 at isa?\n"
+msgstr "device xxx0 at isa?\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:192
+#, no-wrap
+msgid " then only one device will be configured.\n"
+msgstr " тогда будет настроено только одно устройство.\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:194
+msgid ""
+"But for the devices supporting automatic identification by the means of Plug-"
+"n-Play or some proprietary protocol one configuration line is enough to "
+"configure all the devices in the system, like the one above or just simply:"
+msgstr ""
+"Однако для устройств, поддерживающих автоматическую идентификацию с помощью "
+"Plug-n-Play или какого-либо проприетарного протокола, достаточно одной "
+"строки конфигурации для настройки всех устройств в системе, как в примере "
+"выше или просто:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:198
+#, no-wrap
+msgid "device xxx at isa?\n"
+msgstr "device xxx at isa?\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:201
+msgid ""
+"If a driver supports both auto-identified and legacy devices and both kinds "
+"are installed at once in one machine then it is enough to describe in the "
+"config file the legacy devices only. The auto-identified devices will be "
+"added automatically."
+msgstr ""
+"Если драйвер поддерживает как автоматически определяемые, так и устаревшие "
+"устройства, и оба типа установлены одновременно в одной машине, то "
+"достаточно описать в конфигурационном файле только устаревшие устройства. "
+"Автоматически определяемые устройства будут добавлены автоматически."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:203
+msgid "When an ISA bus is auto-configured the events happen as follows:"
+msgstr ""
+"При автоматической настройке шины ISA события происходят в следующем порядке:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:205
+msgid ""
+"All the drivers' identify routines (including the PnP identify routine which "
+"identifies all the PnP devices) are called in random order. As they identify "
+"the devices they add them to the list on the ISA bus. Normally the drivers' "
+"identify routines associate their drivers with the new devices. The PnP "
+"identify routine does not know about the other drivers yet so it does not "
+"associate any with the new devices it adds."
+msgstr ""
+"Все процедуры идентификации драйверов (включая процедуру идентификации PnP, "
+"которая определяет все устройства PnP) вызываются в случайном порядке. Когда "
+"они идентифицируют устройства, они добавляют их в список на шине ISA. Обычно "
+"процедуры идентификации драйверов связывают свои драйверы с новыми "
+"устройствами. Процедура идентификации PnP пока не знает о других драйверах, "
+"поэтому не связывает ни один из них с новыми устройствами, которые она "
+"добавляет."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:207
+msgid ""
+"The PnP devices are put to sleep using the PnP protocol to prevent them from "
+"being probed as legacy devices."
+msgstr ""
+"Устройства PnP переводятся в режим сна с использованием протокола PnP, чтобы "
+"предотвратить их обнаружение как устаревших устройств."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:209
+msgid ""
+"The probe routines of non-PnP devices marked as `sensitive` are called. If "
+"probe for a device went successfully, the attach routine is called for it."
+msgstr ""
+"Вызываются процедуры обнаружения для устройств, не поддерживающих PnP, "
+"помеченных как `sensitive`. Если процедура обнаружения для устройства "
+"завершилась успешно, вызывается процедура присоединения для него."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:211
+msgid ""
+"The probe and attach routines of all non-PNP devices are called likewise."
+msgstr ""
+"Вызов процедур обнаружения и присоединения всех устройств, не поддерживающих "
+"PNP, выполняется аналогичным образом."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:213
+msgid ""
+"The PnP devices are brought back from the sleep state and assigned the "
+"resources they request: I/O and memory address ranges, IRQs and DRQs, all of "
+"them not conflicting with the attached legacy devices."
+msgstr ""
+"Устройства PnP выводятся из состояния сна и получают запрошенные ресурсы: "
+"диапазоны адресов ввода-вывода и памяти, IRQ и DRQ, причем все они не "
+"конфликтуют с подключенными устаревшими устройствами."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:215
+msgid ""
+"Then for each PnP device the probe routines of all the present ISA drivers "
+"are called. The first one that claims the device gets attached. It is "
+"possible that multiple drivers would claim the device with different "
+"priority; in this case, the highest-priority driver wins. The probe routines "
+"must call `ISA_PNP_PROBE()` to compare the actual PnP ID with the list of "
+"the IDs supported by the driver and if the ID is not in the table return "
+"failure. That means that absolutely every driver, even the ones not "
+"supporting any PnP devices must call `ISA_PNP_PROBE()`, at least with an "
+"empty PnP ID table to return failure on unknown PnP devices."
+msgstr ""
+"Затем для каждого устройства PnP вызываются процедуры обнаружения всех "
+"присутствующих драйверов ISA. Первый драйвер, который заявит о поддержке "
+"устройства, будет присоединен. Возможна ситуация, когда несколько драйверов "
+"заявят о поддержке устройства с разным приоритетом; в этом случае побеждает "
+"драйвер с наивысшим приоритетом. Процедуры обнаружения должны вызывать "
+"`ISA_PNP_PROBE()` для сравнения фактического PnP ID со списком ID, "
+"поддерживаемых драйвером, и если ID отсутствует в таблице, возвращать "
+"ошибку. Это означает, что абсолютно каждый драйвер, даже те, которые не "
+"поддерживают никакие PnP устройства, должны вызывать `ISA_PNP_PROBE()`, хотя "
+"бы с пустой таблицей PnP ID, чтобы возвращать ошибку для неизвестных PnP "
+"устройств."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:217
+msgid ""
+"The probe routine returns a positive value (the error code) on error, zero "
+"or negative value on success."
+msgstr ""
+"Процедура обнаружения возвращает положительное значение (код ошибки) в "
+"случае ошибки, ноль или отрицательное значение в случае успеха."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:219
+msgid ""
+"The negative return values are used when a PnP device supports multiple "
+"interfaces. For example, an older compatibility interface and a newer "
+"advanced interface which are supported by different drivers. Then both "
+"drivers would detect the device. The driver which returns a higher value in "
+"the probe routine takes precedence (in other words, the driver returning 0 "
+"has highest precedence, returning -1 is next, returning -2 is after it and "
+"so on). In result the devices which support only the old interface will be "
+"handled by the old driver (which should return -1 from the probe routine) "
+"while the devices supporting the new interface as well will be handled by "
+"the new driver (which should return 0 from the probe routine). If multiple "
+"drivers return the same value then the one called first wins. So if a driver "
+"returns value 0 it may be sure that it won the priority arbitration."
+msgstr ""
+"Отрицательные возвращаемые значения используются, когда устройство PnP "
+"поддерживает несколько интерфейсов. Например, старый совместимый интерфейс и "
+"новый расширенный интерфейс, которые поддерживаются разными драйверами. В "
+"этом случае оба драйвера обнаружат устройство. Драйвер, возвращающий большее "
+"значение в процедуре обнаружения, получает приоритет (другими словами, "
+"драйвер, возвращающий 0, имеет наивысший приоритет, возвращающий -1 — "
+"следующий, возвращающий -2 — за ним и так далее). В результате устройства, "
+"поддерживающие только старый интерфейс, будут обрабатываться старым "
+"драйвером (который должен возвращать -1 из процедуры обнаружения), тогда как "
+"устройства, поддерживающие также новый интерфейс, будут обрабатываться новым "
+"драйвером (который должен возвращать 0 из процедуры обнаружения). Если "
+"несколько драйверов возвращают одинаковое значение, побеждает тот, который "
+"был вызван первым. Таким образом, если драйвер возвращает значение 0, он "
+"может быть уверен, что выиграл арбитраж приоритетов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:221
+msgid ""
+"The device-specific identify routines can also assign not a driver but a "
+"class of drivers to the device. Then all the drivers in the class are probed "
+"for this device, like the case with PnP. This feature is not implemented in "
+"any existing driver and is not considered further in this document."
+msgstr ""
+"Процедуры идентификации, специфичные для устройства, также могут назначать "
+"устройству не драйвер, а класс драйверов. Затем все драйверы в этом классе "
+"проверяются на совместимость с устройством, как в случае с PnP. Эта "
+"возможность не реализована ни в одном существующем драйвере и далее в этом "
+"документе не рассматривается."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:223
+msgid ""
+"As the PnP devices are disabled when probing the legacy devices they will "
+"not be attached twice (once as legacy and once as PnP). But in case of "
+"device-dependent identify routines it is the responsibility of the driver to "
+"make sure that the same device will not be attached by the driver twice: "
+"once as legacy user-configured and once as auto-identified."
+msgstr ""
+"Поскольку устройства PnP отключены при проверке устаревших устройств, они не "
+"будут присоединены дважды (один раз как устаревшие и один раз как PnP). "
+"Однако в случае процедур идентификации, зависящих от устройства, "
+"ответственность за то, чтобы одно и то же устройство не было присоединено "
+"драйвером дважды (один раз как настроенное пользователем устаревшее и один "
+"раз как автоматически идентифицированное), лежит на драйвере."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:225
+msgid ""
+"Another practical consequence for the auto-identified devices (both PnP and "
+"device-specific) is that the flags can not be passed to them from the kernel "
+"configuration file. So they must either not use the flags at all or use the "
+"flags from the device unit 0 for all the auto-identified devices or use the "
+"sysctl interface instead of flags."
+msgstr ""
+"Еще одно практическое следствие для автоматически определяемых устройств "
+"(как PnP, так и специфичных для устройства) заключается в том, что флаги не "
+"могут быть переданы им из файла конфигурации ядра. Поэтому они либо не "
+"должны использовать флаги вообще, либо использовать флаги из устройства unit "
+"0 для всех автоматически определяемых устройств, либо использовать интерфейс "
+"sysctl вместо флагов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:227
+msgid ""
+"Other unusual configurations may be accommodated by accessing the "
+"configuration resources directly with functions of families "
+"`resource_query_*()` and `resource_*_value()`. Their implementations are "
+"located in [.filename]#kern/subr_bus.c#. The old IDE disk driver "
+"[.filename]#i386/isa/wd.c# contains examples of such use. But the standard "
+"means of configuration must always be preferred. Leave parsing the "
+"configuration resources to the bus configuration code."
+msgstr ""
+"Другие нестандартные конфигурации могут быть реализованы путем прямого "
+"доступа к ресурсам конфигурации с использованием функций семейств "
+"`resource_query_*()` и `resource_*_value()`. Их реализации находятся в "
+"[.filename]#kern/subr_bus.c#. Примеры такого использования есть в старом "
+"драйвере диска IDE [.filename]#i386/isa/wd.c#. Однако стандартные методы "
+"конфигурации всегда должны быть предпочтительны. Оставьте разбор ресурсов "
+"конфигурации коду настройки шины."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:229
+#, no-wrap
+msgid "Resources"
+msgstr "Ресурсы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:232
+msgid ""
+"The information that a user enters into the kernel configuration file is "
+"processed and passed to the kernel as configuration resources. This "
+"information is parsed by the bus configuration code and transformed into a "
+"value of structure device_t and the bus resources associated with it. The "
+"drivers may access the configuration resources directly using functions "
+"`resource_*` for more complex cases of configuration. However, generally "
+"this is neither needed nor recommended, so this issue is not discussed "
+"further here."
+msgstr ""
+"Информация, которую пользователь вводит в файл конфигурации ядра, "
+"обрабатывается и передаётся ядру в виде ресурсов конфигурации. Эта "
+"информация анализируется кодом конфигурации шины и преобразуется в значение "
+"структуры `device_t` и связанные с ней ресурсы шины. Драйверы могут напрямую "
+"обращаться к ресурсам конфигурации, используя функции `resource_*` для более "
+"сложных случаев конфигурации. Однако, как правило, в этом нет необходимости, "
+"и это не рекомендуется, поэтому данный вопрос далее не рассматривается."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:234
+msgid ""
+"The bus resources are associated with each device. They are identified by "
+"type and number within the type. For the ISA bus the following types are "
+"defined:"
+msgstr ""
+"Ресурсы шины связаны с каждым устройством. Они идентифицируются по типу и "
+"номеру внутри типа. Для шины ISA определены следующие типы:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:236
+msgid "_SYS_RES_IRQ_ - interrupt number"
+msgstr "_SYS_RES_IRQ_ - номер прерывания"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:237
+msgid "_SYS_RES_DRQ_ - ISA DMA channel number"
+msgstr "_SYS_RES_DRQ_ - номер канала ISA DMA"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:238
+msgid ""
+"_SYS_RES_MEMORY_ - range of device memory mapped into the system memory space"
+msgstr ""
+"_SYS_RES_MEMORY_ - диапазон памяти устройства, отображенный в системное "
+"адресное пространство"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:239
+msgid "_SYS_RES_IOPORT_ - range of device I/O registers"
+msgstr "_SYS_RES_IOPORT_ - диапазон регистров ввода-вывода устройства"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:241
+msgid ""
+"The enumeration within types starts from 0, so if a device has two memory "
+"regions it would have resources of type `SYS_RES_MEMORY` numbered 0 and 1. "
+"The resource type has nothing to do with the C language type, all the "
+"resource values have the C language type `unsigned long` and must be cast as "
+"necessary. The resource numbers do not have to be contiguous, although for "
+"ISA they normally would be. The permitted resource numbers for ISA devices "
+"are:"
+msgstr ""
+"Перечисление внутри типов начинается с 0, поэтому если устройство имеет две "
+"области памяти, оно будет иметь ресурсы типа `SYS_RES_MEMORY` с номерами 0 и "
+"1. Тип ресурса не связан с типом языка C, все значения ресурсов имеют тип "
+"`unsigned long` в языке C и должны быть приведены по мере необходимости. "
+"Номера ресурсов не обязательно должны быть последовательными, хотя для ISA "
+"они обычно таковыми являются. Допустимые номера ресурсов для устройств ISA:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:248
+#, no-wrap
+msgid ""
+" IRQ: 0-1\n"
+" DRQ: 0-1\n"
+" MEMORY: 0-3\n"
+" IOPORT: 0-7\n"
+msgstr ""
+" IRQ: 0-1\n"
+" DRQ: 0-1\n"
+" MEMORY: 0-3\n"
+" IOPORT: 0-7\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:251
+msgid ""
+"All the resources are represented as ranges, with a start value and count. "
+"For IRQ and DRQ resources the count would normally be equal to 1. The values "
+"for memory refer to the physical addresses."
+msgstr ""
+"Все ресурсы представлены в виде диапазонов с начальным значением и "
+"количеством. Для ресурсов IRQ и DRQ количество обычно равно 1. Значения для "
+"памяти относятся к физическим адресам."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:253
+msgid "Three types of activities can be performed on resources:"
+msgstr "Три типа действий могут выполняться над ресурсами:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:255
+msgid "set/get"
+msgstr "Установка — set/get"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:256
+msgid "allocate/release"
+msgstr "Выделение — allocate/release"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:257
+msgid "activate/deactivate"
+msgstr "Активация — activate/deactivate"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:259
+msgid ""
+"Setting sets the range used by the resource. Allocation reserves the "
+"requested range that no other driver would be able to reserve it (and "
+"checking that no other driver reserved this range already). Activation makes "
+"the resource accessible to the driver by doing whatever is necessary for "
+"that (for example, for memory it would be mapping into the kernel virtual "
+"address space)."
+msgstr ""
+"Установка задает диапазон, используемый ресурсом. Выделение резервирует "
+"запрошенный диапазон, чтобы никакой другой драйвер не смог его "
+"зарезервировать (и проверяет, что никакой другой драйвер уже не "
+"зарезервировал этот диапазон). Активация делает ресурс доступным для "
+"драйвера, выполняя все необходимые для этого действия (например, для памяти "
+"это может быть отображение в виртуальное адресное пространство ядра)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:261
+msgid "The functions to manipulate resources are:"
+msgstr "Функции для управления ресурсами:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:263
+msgid ""
+"`int bus_set_resource(device_t dev, int type, int rid, u_long start, u_long "
+"count)`"
+msgstr ""
+"`int bus_set_resource(device_t dev, int type, int rid, u_long start, u_long "
+"count)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:265
+msgid ""
+"Set a range for a resource. Returns 0 if successful, error code otherwise. "
+"Normally, this function will return an error only if one of `type`, `rid`, "
+"`start` or `count` has a value that falls out of the permitted range."
+msgstr ""
+"Устанавливает диапазон для ресурса. Возвращает 0 при успешном выполнении, в "
+"противном случае — код ошибки. Обычно эта функция возвращает ошибку только в "
+"том случае, если одно из значений `type`, `rid`, `start` или `count` выходит "
+"за пределы допустимого диапазона."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:267
+msgid "dev - driver's device"
+msgstr "dev - устройство драйвера"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:268
+msgid "type - type of resource, SYS_RES_*"
+msgstr "тип - тип ресурса, SYS_RES_*"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:269
+msgid "rid - resource number (ID) within type"
+msgstr "rid - номер ресурса (ID) в пределах типа"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:270
+msgid "start, count - resource range"
+msgstr "начало, количество - диапазон ресурсов"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:272
+msgid ""
+"`int bus_get_resource(device_t dev, int type, int rid, u_long *startp, "
+"u_long *countp)`"
+msgstr ""
+"`int bus_get_resource(device_t dev, int type, int rid, u_long *startp, "
+"u_long *countp)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:274
+msgid ""
+"Get the range of resource. Returns 0 if successful, error code if the "
+"resource is not defined yet."
+msgstr ""
+"Получает диапазон ресурса. Возвращает 0 при успехе, код ошибки, если ресурс "
+"ещё не определён."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:275
+msgid ""
+"`u_long bus_get_resource_start(device_t dev, int type, int rid) u_long "
+"bus_get_resource_count (device_t dev, int type, int rid)`"
+msgstr ""
+"`u_long bus_get_resource_start(device_t dev, int type, int rid) и u_long "
+"bus_get_resource_count (device_t dev, int type, int rid)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:277
+msgid ""
+"Convenience functions to get only the start or count. Return 0 in case of "
+"error, so if the resource start has 0 among the legitimate values it would "
+"be impossible to tell if the value is 0 or an error occurred. Luckily, no "
+"ISA resources for add-on drivers may have a start value equal to 0."
+msgstr ""
+"Удобные функции для получения только начала или количества. Возвращают 0 в "
+"случае ошибки, поэтому если начало ресурса может законно содержать 0, "
+"невозможно определить, является ли значение 0 или произошла ошибка. К "
+"счастью, ни один ресурс ISA для дополнительных драйверов не может иметь "
+"начальное значение, равное 0."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:278
+msgid "`void bus_delete_resource(device_t dev, int type, int rid)`"
+msgstr "`void bus_delete_resource(device_t dev, int type, int rid)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:280
+msgid "Delete a resource, make it undefined."
+msgstr "Удаляет ресурс, делает его неопределённым."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:281
+msgid ""
+"`struct resource * bus_alloc_resource(device_t dev, int type, int *rid, "
+"u_long start, u_long end, u_long count, u_int flags)`"
+msgstr ""
+"`struct resource * bus_alloc_resource(device_t dev, int type, int *rid, "
+"u_long start, u_long end, u_long count, u_int flags)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:283
+msgid ""
+"Allocate a resource as a range of count values not allocated by anyone else, "
+"somewhere between start and end. Alas, alignment is not supported. If the "
+"resource was not set yet it is automatically created. The special values of "
+"start 0 and end ~0 (all ones) means that the fixed values previously set by "
+"`bus_set_resource()` must be used instead: start and count as themselves and "
+"end=(start+count), in this case if the resource was not defined before then "
+"an error is returned. Although rid is passed by reference it is not set "
+"anywhere by the resource allocation code of the ISA bus. (The other buses "
+"may use a different approach and modify it)."
+msgstr ""
+"Выделяет ресурс как диапазон значений count, не выделенных никем другим, где-"
+"то между start и end. Увы, выравнивание не поддерживается. Если ресурс ещё "
+"не был установлен, он автоматически создаётся. Специальные значения start "
+"равное 0 и end равное ~0 (все единицы) означают, что должны использоваться "
+"фиксированные значения, ранее установленные `bus_set_resource()`: start и "
+"count как есть, а end=(start+count). В этом случае, если ресурс не был "
+"определён ранее, возвращается ошибка. Хотя rid передаётся по ссылке, он "
+"нигде не устанавливается кодом выделения ресурсов шины ISA. Другие шины "
+"могут использовать иной подход и изменять его."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:285
+msgid "Flags are a bitmap, the flags interesting for the caller are:"
+msgstr ""
+"Флаги представляют собой битовую карту. Интересные для вызывающей стороны "
+"флаги:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:287
+msgid ""
+"_RF_ACTIVE_ - causes the resource to be automatically activated after "
+"allocation."
+msgstr ""
+"_RF_ACTIVE_ - приводит к автоматической активации ресурса после его "
+"выделения."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:288
+msgid ""
+"_RF_SHAREABLE_ - resource may be shared at the same time by multiple drivers."
+msgstr ""
+"_RF_SHAREABLE_ - ресурс может использоваться одновременно несколькими "
+"драйверами."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:289
+msgid ""
+"_RF_TIMESHARE_ - resource may be time-shared by multiple drivers, i.e., "
+"allocated at the same time by many but activated only by one at any given "
+"moment of time."
+msgstr ""
+"_RF_TIMESHARE_ - ресурс может разделяться по времени несколькими драйверами, "
+"т.е. выделяться одновременно многими, но активироваться только одним в любой "
+"момент времени."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:290
+msgid ""
+"Returns 0 on error. The allocated values may be obtained from the returned "
+"handle using methods `rhand_*()`."
+msgstr ""
+"Возвращает 0 при ошибке. Выделенные значения могут быть получены из "
+"возвращённого дескриптора с использованием методов `rhand_*()`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:291
+msgid ""
+"`int bus_release_resource(device_t dev, int type, int rid, struct resource "
+"*r)`"
+msgstr ""
+"`int bus_release_resource(device_t dev, int type, int rid, struct resource "
+"*r)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:292
+msgid ""
+"Release the resource, r is the handle returned by `bus_alloc_resource()`. "
+"Returns 0 on success, error code otherwise."
+msgstr ""
+"Освобождает ресурс, r — это дескриптор, возвращённый `bus_alloc_resource()`. "
+"Возвращает 0 при успехе, код ошибки в противном случае."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:293
+msgid ""
+"`int bus_activate_resource(device_t dev, int type, int rid, struct resource "
+"*r) int bus_deactivate_resource(device_t dev, int type, int rid, struct "
+"resource *r)`"
+msgstr ""
+"`int bus_activate_resource(device_t dev, int type, int rid, struct resource "
+"*r) int bus_deactivate_resource(device_t dev, int type, int rid, struct "
+"resource *r)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:294
+msgid ""
+"Activate or deactivate resource. Return 0 on success, error code otherwise. "
+"If the resource is time-shared and currently activated by another driver "
+"then `EBUSY` is returned."
+msgstr ""
+"Активирует или деактивирует ресурс. Возвращает 0 при успехе, в противном "
+"случае — код ошибки. Если ресурс разделяемый и в данный момент активирован "
+"другим драйвером, возвращается `EBUSY`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:295
+msgid ""
+"`int bus_setup_intr(device_t dev, struct resource *r, int flags, "
+"driver_intr_t *handler, void *arg, void **cookiep) int "
+"bus_teardown_intr(device_t dev, struct resource *r, void *cookie)`"
+msgstr ""
+"`int bus_setup_intr(device_t dev, struct resource *r, int flags, "
+"driver_intr_t *handler, void *arg, void **cookiep) int "
+"bus_teardown_intr(device_t dev, struct resource *r, void *cookie)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:296
+msgid ""
+"Associate or de-associate the interrupt handler with a device. Return 0 on "
+"success, error code otherwise."
+msgstr ""
+"Связывает или разрывает связь обработчика прерывания с устройством. "
+"Возвращает 0 при успехе, код ошибки в противном случае."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:297
+msgid "r - the activated resource handler describing the IRQ"
+msgstr "r - активированный обработчик ресурсов, описывающий IRQ"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:299
+msgid "flags - the interrupt priority level, one of:"
+msgstr "flags - уровень приоритета прерывания, один из:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:301
+msgid ""
+"`INTR_TYPE_TTY` - terminals and other likewise character-type devices. To "
+"mask them use `spltty()`."
+msgstr ""
+"`INTR_TYPE_TTY` - терминалы и другие аналогичные символьные устройства. Для "
+"их маскировки используйте `spltty()`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:302
+msgid ""
+"`(INTR_TYPE_TTY | INTR_TYPE_FAST)` - terminal type devices with small input "
+"buffer, critical to the data loss on input (such as the old-fashioned serial "
+"ports). To mask them use `spltty()`."
+msgstr ""
+"`(INTR_TYPE_TTY | INTR_TYPE_FAST)` - терминальные устройства с малым буфером "
+"ввода, критичные к потере данных на входе (например, устаревшие "
+"последовательные порты). Для их маскирования используйте `spltty()`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:303
+msgid ""
+"`INTR_TYPE_BIO` - block-type devices, except those on the CAM controllers. "
+"To mask them use `splbio()`."
+msgstr ""
+"`INTR_TYPE_BIO` - блочные устройства, за исключением тех, что подключены к "
+"контроллерам CAM. Для их маскирования используйте `splbio()`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:304
+msgid ""
+"`INTR_TYPE_CAM` - CAM (Common Access Method) bus controllers. To mask them "
+"use `splcam()`."
+msgstr ""
+"`INTR_TYPE_CAM` - контроллеры шины CAM (Common Access Method). Для их "
+"маскирования используйте `splcam()`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:305
+msgid ""
+"`INTR_TYPE_NET` - network interface controllers. To mask them use `splimp()`."
+msgstr ""
+"`INTR_TYPE_NET` - контроллеры сетевых интерфейсов. Для их маскирования "
+"используйте `splimp()`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:306
+msgid ""
+"`INTR_TYPE_MISC` - miscellaneous devices. There is no other way to mask them "
+"than by `splhigh()` which masks all interrupts."
+msgstr ""
+"`INTR_TYPE_MISC` — прочие устройства. Нет другого способа их маскировки, "
+"кроме `splhigh()`, который маскирует все прерывания."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:308
+msgid ""
+"When an interrupt handler executes all the other interrupts matching its "
+"priority level will be masked. The only exception is the MISC level for "
+"which no other interrupts are masked and which is not masked by any other "
+"interrupt."
+msgstr ""
+"Когда обработчик прерывания выполняется, все другие прерывания, "
+"соответствующие его уровню приоритета, будут заблокированы. Единственное "
+"исключение — уровень MISC, для которого никакие другие прерывания не "
+"блокируются и который сам не блокируется другими прерываниями."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:310
+msgid ""
+"_handler_ - pointer to the handler function, the type driver_intr_t is "
+"defined as `void driver_intr_t(void *)`"
+msgstr ""
+"_handler_ - указатель на функцию-обработчик, тип driver_intr_t определён как "
+"`void driver_intr_t(void *)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:311
+msgid ""
+"_arg_ - the argument passed to the handler to identify this particular "
+"device. It is cast from void* to any real type by the handler. The old "
+"convention for the ISA interrupt handlers was to use the unit number as "
+"argument, the new (recommended) convention is using a pointer to the device "
+"softc structure."
+msgstr ""
+"_arg_ - аргумент, передаваемый обработчику для идентификации конкретного "
+"устройства. Приводится обработчиком от void* к фактическому типу. Старая "
+"конвенция для обработчиков прерываний ISA предполагала использование номера "
+"устройства в качестве аргумента, новая (рекомендуемая) конвенция "
+"предполагает использование указателя на структуру softc устройства."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:312
+msgid ""
+"_cookie[p]_ - the value received from `setup()` is used to identify the "
+"handler when passed to `teardown()`"
+msgstr ""
+"_cookie[p]_ - значение, полученное из `setup()`, используется для "
+"идентификации обработчика при передаче в `teardown()`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:314
+msgid ""
+"A number of methods are defined to operate on the resource handlers (struct "
+"resource *). Those of interest to the device driver writers are:"
+msgstr ""
+"Определены несколько методов для работы с обработчиками ресурсов (struct "
+"resource *). Вот те из них, которые представляют интерес для разработчиков "
+"драйверов устройств:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:316
+msgid ""
+"`u_long rman_get_start(r) u_long rman_get_end(r)` Get the start and end of "
+"allocated resource range."
+msgstr ""
+"`u_long rman_get_start(r) u_long rman_get_end(r)` Получают начало и конец "
+"выделенного диапазона ресурсов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:317
+msgid ""
+"`void *rman_get_virtual(r)` Get the virtual address of activated memory "
+"resource."
+msgstr ""
+"`void *rman_get_virtual(r)` Получает виртуальный адрес активированного "
+"ресурса памяти."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:319
+#, no-wrap
+msgid "Bus Memory Mapping"
+msgstr "Отображение памяти шины"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:322
+msgid ""
+"In many cases data is exchanged between the driver and the device through "
+"the memory. Two variants are possible:"
+msgstr ""
+"Во многих случаях данные передаются между драйвером и устройством через "
+"память. Возможны два варианта:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:324
+msgid "(a) memory is located on the device card"
+msgstr "(а) память расположена на карте устройства"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:326
+msgid "(b) memory is the main memory of the computer"
+msgstr "(b) память — это основная память компьютера"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:328
+msgid ""
+"In case (a) the driver always copies the data back and forth between the on-"
+"card memory and the main memory as necessary. To map the on-card memory into "
+"the kernel virtual address space the physical address and length of the on-"
+"card memory must be defined as a `SYS_RES_MEMORY` resource. That resource "
+"can then be allocated and activated, and its virtual address obtained using "
+"`rman_get_virtual()`. The older drivers used the function `pmap_mapdev()` "
+"for this purpose, which should not be used directly any more. Now it is one "
+"of the internal steps of resource activation."
+msgstr ""
+"В случае (a) драйвер всегда копирует данные между памятью на карте и "
+"основной памятью по мере необходимости. Для отображения памяти на карте в "
+"виртуальное адресное пространство ядра физический адрес и длина памяти на "
+"карте должны быть определены как ресурс `SYS_RES_MEMORY`. Этот ресурс может "
+"быть затем выделен и активирован, а его виртуальный адрес получен с помощью "
+"`rman_get_virtual()`. Более старые драйверы использовали для этой цели "
+"функцию `pmap_mapdev()`, которую больше не следует использовать напрямую. "
+"Теперь это один из внутренних шагов активации ресурса."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:330
+msgid ""
+"Most of the ISA cards will have their memory configured for physical "
+"location somewhere in range 640KB-1MB. Some of the ISA cards require larger "
+"memory ranges which should be placed somewhere under 16MB (because of the 24-"
+"bit address limitation on the ISA bus). In that case if the machine has more "
+"memory than the start address of the device memory (in other words, they "
+"overlap) a memory hole must be configured at the address range used by "
+"devices. Many BIOSes allow configuration of a memory hole of 1MB starting at "
+"14MB or 15MB. FreeBSD can handle the memory holes properly if the BIOS "
+"reports them properly (this feature may be broken on old BIOSes)."
+msgstr ""
+"Большинство ISA-карт имеют память, настроенную на физическое расположение в "
+"диапазоне 640 КБ–1 МБ. Некоторые ISA-карты требуют большего диапазона "
+"памяти, который должен быть размещён ниже 16 МБ (из-за 24-битного "
+"ограничения адресации на шине ISA). В таком случае, если в машине больше "
+"памяти, чем начальный адрес памяти устройства (другими словами, они "
+"пересекаются), необходимо настроить \"дыру\" в памяти по диапазону адресов, "
+"используемому устройствами. Многие BIOS позволяют настроить \"дыру\" в "
+"памяти размером 1 МБ, начиная с 14 МБ или 15 МБ. FreeBSD корректно "
+"обрабатывает \"дыры\" в памяти, если BIOS правильно их сообщает (эта функция "
+"может не работать в старых BIOS)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:332
+msgid ""
+"In case (b) just the address of the data is sent to the device, and the "
+"device uses DMA to actually access the data in the main memory. Two "
+"limitations are present: First, ISA cards can only access memory below 16MB. "
+"Second, the contiguous pages in virtual address space may not be contiguous "
+"in physical address space, so the device may have to do scatter/gather "
+"operations. The bus subsystem provides ready solutions for some of these "
+"problems, the rest has to be done by the drivers themselves."
+msgstr ""
+"В случае (b) только адрес данных отправляется на устройство, и устройство "
+"использует DMA для фактического доступа к данным в основной памяти. "
+"Существуют два ограничения: во-первых, карты ISA могут обращаться только к "
+"памяти ниже 16 МБ. Во-вторых, непрерывные страницы в виртуальном адресном "
+"пространстве могут не быть непрерывными в физическом адресном пространстве, "
+"поэтому устройству может потребоваться выполнять операции scatter/gather. "
+"Подсистема шины предоставляет готовые решения для некоторых из этих проблем, "
+"остальное должно быть реализовано самими драйверами."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:334
+msgid ""
+"Two structures are used for DMA memory allocation, `bus_dma_tag_t` and "
+"`bus_dmamap_t`. Tag describes the properties required for the DMA memory. "
+"Map represents a memory block allocated according to these properties. "
+"Multiple maps may be associated with the same tag."
+msgstr ""
+"Для выделения памяти DMA используются две структуры: `bus_dma_tag_t` и "
+"`bus_dmamap_t`. Тег (`tag`) описывает свойства, необходимые для памяти DMA. "
+"Карта (`map`) представляет собой блок памяти, выделенный в соответствии с "
+"этими свойствами. С одним тегом может быть связано несколько карт."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:336
+msgid ""
+"Tags are organized into a tree-like hierarchy with inheritance of the "
+"properties. A child tag inherits all the requirements of its parent tag, and "
+"may make them more strict but never more loose."
+msgstr ""
+"Теги организованы в иерархию в виде дерева с наследованием свойств. Дочерний "
+"тег наследует все требования родительского тега и может делать их более "
+"строгими, но никогда более мягкими."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:338
+msgid ""
+"Normally one top-level tag (with no parent) is created for each device unit. "
+"If multiple memory areas with different requirements are needed for each "
+"device then a tag for each of them may be created as a child of the parent "
+"tag."
+msgstr ""
+"Обычно создается один корневой тег (без родителя) для каждого устройства. "
+"Если для каждого устройства требуется несколько областей памяти с разными "
+"требованиями, то для каждой из них может быть создан тег как дочерний по "
+"отношению к родительскому тегу."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:340
+msgid "The tags can be used to create a map in two ways."
+msgstr "Теги могут быть использованы для создания карты двумя способами."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:342
+msgid ""
+"First, a chunk of contiguous memory conformant with the tag requirements may "
+"be allocated (and later may be freed). This is normally used to allocate "
+"relatively long-living areas of memory for communication with the device. "
+"Loading of such memory into a map is trivial: it is always considered as one "
+"chunk in the appropriate physical memory range."
+msgstr ""
+"Сначала может быть выделен (а затем освобожден) блок непрерывной памяти, "
+"соответствующий требованиям тега. Обычно это используется для выделения "
+"относительно долгоживущих областей памяти для взаимодействия с устройством. "
+"Загрузка такой памяти в карту тривиальна: она всегда рассматривается как "
+"один блок в соответствующем диапазоне физической памяти."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:344
+msgid ""
+"Second, an arbitrary area of virtual memory may be loaded into a map. Each "
+"page of this memory will be checked for conformance to the map requirement. "
+"If it conforms then it is left at its original location. If it is not then a "
+"fresh conformant \"bounce page\" is allocated and used as intermediate "
+"storage. When writing the data from the non-conformant original pages they "
+"will be copied to their bounce pages first and then transferred from the "
+"bounce pages to the device. When reading the data would go from the device "
+"to the bounce pages and then copied to their non-conformant original pages. "
+"The process of copying between the original and bounce pages is called "
+"synchronization. This is normally used on a per-transfer basis: buffer for "
+"each transfer would be loaded, transfer done and buffer unloaded."
+msgstr ""
+"Второй момент: произвольная область виртуальной памяти может быть загружена "
+"в карту. Каждая страница этой памяти будет проверяться на соответствие "
+"требованиям карты. Если она соответствует, то остается на своем исходном "
+"месте. Если нет, то выделяется новая соответствующая промежуточная страница "
+"(bounce page), которая используется как промежуточное хранилище. При записи "
+"данных с несоответствующих исходных страниц они сначала копируются на свои "
+"промежуточные страницы, а затем передаются с промежуточных страниц на "
+"устройство. При чтении данные поступают с устройства на промежуточные "
+"страницы, а затем копируются на свои несоответствующие исходные страницы. "
+"Процесс копирования между исходными и промежуточными страницами называется "
+"синхронизацией. Обычно это используется для каждой передачи: буфер для "
+"каждой передачи загружается, передача выполняется, и буфер выгружается."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:346
+msgid "The functions working on the DMA memory are:"
+msgstr "Функции, работающие с памятью DMA:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:348
+msgid ""
+"`int bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, "
+"bus_size_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr, "
+"bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize, int "
+"nsegments, bus_size_t maxsegsz, int flags, bus_dma_tag_t *dmat)`"
+msgstr ""
+"`int bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, "
+"bus_size_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr, "
+"bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize, int "
+"nsegments, bus_size_t maxsegsz, int flags, bus_dma_tag_t *dmat)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:350
+msgid "Create a new tag. Returns 0 on success, the error code otherwise."
+msgstr ""
+"Создать новый тег. Возвращает 0 при успехе, код ошибки в противном случае."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:352
+msgid "_parent_ - parent tag, or NULL to create a top-level tag."
+msgstr ""
+"_parent_ - родительский тег, или NULL для создания тега верхнего уровня."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:353
+msgid ""
+"_alignment_ - required physical alignment of the memory area to be allocated "
+"for this tag. Use value 1 for \"no specific alignment\". Applies only to the "
+"future `bus_dmamem_alloc()` but not `bus_dmamap_create()` calls."
+msgstr ""
+"_alignment_ - требуемое физическое выравнивание области памяти, которая "
+"будет выделена для этого тега. Используйте значение 1 для \"без "
+"специфического выравнивания\". Применяется только к будущим вызовам "
+"`bus_dmamem_alloc()`, но не `bus_dmamap_create()`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:354
+msgid ""
+"_boundary_ - physical address boundary that must not be crossed when "
+"allocating the memory. Use value 0 for \"no boundary\". Applies only to the "
+"future `bus_dmamem_alloc()` but not `bus_dmamap_create()` calls. Must be "
+"power of 2. If the memory is planned to be used in non-cascaded DMA mode "
+"(i.e., the DMA addresses will be supplied not by the device itself but by "
+"the ISA DMA controller) then the boundary must be no larger than 64KB "
+"(64*1024) due to the limitations of the DMA hardware."
+msgstr ""
+"_boundary_ - физическая граница адреса, которую нельзя пересекать при "
+"выделении памяти. Используйте значение 0 для обозначения \"нет границы\". "
+"Применяется только к будущим вызовам `bus_dmamem_alloc()`, но не "
+"`bus_dmamap_create()`. Должна быть степенью 2. Если память планируется "
+"использовать в некаскадном режиме DMA (т.е. адреса DMA будут предоставляться "
+"не самим устройством, а контроллером DMA ISA), то граница не должна "
+"превышать 64 КБ (64*1024) из-за ограничений аппаратного обеспечения DMA."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:355
+msgid ""
+"_lowaddr, highaddr_ - the names are slightly misleading; these values are "
+"used to limit the permitted range of physical addresses used to allocate the "
+"memory. The exact meaning varies depending on the planned future use:"
+msgstr ""
+"_lowaddr, highaddr_ - названия немного вводят в заблуждение; эти значения "
+"используются для ограничения допустимого диапазона физических адресов, "
+"используемых для выделения памяти. Точное значение зависит от "
+"предполагаемого будущего использования:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:357
+msgid ""
+"For `bus_dmamem_alloc()` all the addresses from 0 to lowaddr-1 are "
+"considered permitted, the higher ones are forbidden."
+msgstr ""
+"Для `bus_dmamem_alloc()` все адреса от 0 до lowaddr-1 считаются "
+"разрешёнными, а более высокие — запрещёнными."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:358
+msgid ""
+"For `bus_dmamap_create()` all the addresses outside the inclusive range "
+"[lowaddr; highaddr] are considered accessible. The addresses of pages inside "
+"the range are passed to the filter function which decides if they are "
+"accessible. If no filter function is supplied then all the range is "
+"considered unaccessible."
+msgstr ""
+"Для `bus_dmamap_create()` все адреса вне включительного диапазона [lowaddr; "
+"highaddr] считаются доступными. Адреса страниц внутри диапазона передаются в "
+"функцию-фильтр, которая определяет, доступны ли они. Если функция-фильтр не "
+"предоставлена, то весь диапазон считается недоступным."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:359
+msgid "For the ISA devices the normal values (with no filter function) are:"
+msgstr "Для устройств ISA обычные значения (без функции фильтрации) следующие:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:361
+msgid "lowaddr = BUS_SPACE_MAXADDR_24BIT"
+msgstr "lowaddr = BUS_SPACE_MAXADDR_24BIT"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:363
+msgid "highaddr = BUS_SPACE_MAXADDR"
+msgstr "highaddr = BUS_SPACE_MAXADDR"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:365
+msgid ""
+"_filter, filterarg_ - the filter function and its argument. If NULL is "
+"passed for filter then the whole range [lowaddr, highaddr] is considered "
+"unaccessible when doing `bus_dmamap_create()`. Otherwise the physical "
+"address of each attempted page in range [lowaddr; highaddr] is passed to the "
+"filter function which decides if it is accessible. The prototype of the "
+"filter function is: `int filterfunc(void *arg, bus_addr_t paddr)`. It must "
+"return 0 if the page is accessible, non-zero otherwise."
+msgstr ""
+"_filter, filterarg_ - функция фильтра и её аргумент. Если передаётся NULL "
+"для filter, то весь диапазон [lowaddr, highaddr] считается недоступным при "
+"выполнении `bus_dmamap_create()`. В противном случае физический адрес каждой "
+"страницы в диапазоне [lowaddr; highaddr] передаётся в функцию фильтра, "
+"которая определяет, доступна ли она. Прототип функции фильтра: `int "
+"filterfunc(void *arg, bus_addr_t paddr)`. Функция должна вернуть 0, если "
+"страница доступна, и ненулевое значение в противном случае."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:366
+msgid ""
+"_maxsize_ - the maximal size of memory (in bytes) that may be allocated "
+"through this tag. In case it is difficult to estimate or could be "
+"arbitrarily big, the value for ISA devices would be "
+"`BUS_SPACE_MAXSIZE_24BIT`."
+msgstr ""
+"_maxsize_ - максимальный размер памяти (в байтах), который может быть "
+"выделен через этот тег. Если сложно оценить или он может быть произвольно "
+"большим, для устройств ISA следует использовать значение "
+"`BUS_SPACE_MAXSIZE_24BIT`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:367
+msgid ""
+"_nsegments_ - maximal number of scatter-gather segments supported by the "
+"device. If unrestricted then the value `BUS_SPACE_UNRESTRICTED` should be "
+"used. This value is recommended for the parent tags, the actual restrictions "
+"would then be specified for the descendant tags. Tags with nsegments equal "
+"to `BUS_SPACE_UNRESTRICTED` may not be used to actually load maps, they may "
+"be used only as parent tags. The practical limit for nsegments seems to be "
+"about 250-300, higher values will cause kernel stack overflow (the hardware "
+"can not normally support that many scatter-gather buffers anyway)."
+msgstr ""
+"_nsegments_ - максимальное количество сегментов scatter-gather, "
+"поддерживаемых устройством. Если ограничений нет, следует использовать "
+"значение `BUS_SPACE_UNRESTRICTED`. Это значение рекомендуется для "
+"родительских тегов, фактические ограничения затем будут указаны для дочерних "
+"тегов. Теги с nsegments равным `BUS_SPACE_UNRESTRICTED` не могут "
+"использоваться для фактической загрузки отображений, они могут применяться "
+"только как родительские теги. Практический предел для nsegments составляет "
+"около 250-300, более высокие значения вызовут переполнение стека ядра "
+"(аппаратное обеспечение обычно не поддерживает такое большое количество "
+"scatter-gather буферов в любом случае)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:368
+msgid ""
+"_maxsegsz_ - maximal size of a scatter-gather segment supported by the "
+"device. The maximal value for ISA device would be `BUS_SPACE_MAXSIZE_24BIT`."
+msgstr ""
+"_maxsegsz_ — максимальный размер сегмента scatter-gather, поддерживаемый "
+"устройством. Максимальное значение для устройства ISA будет "
+"`BUS_SPACE_MAXSIZE_24BIT`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:369
+msgid "_flags_ - a bitmap of flags. The only interesting flag is:"
+msgstr "_flags_ - битовая маска флагов. Единственный интересный флаг:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:371
+msgid ""
+"_BUS_DMA_ALLOCNOW_ - requests to allocate all the potentially needed bounce "
+"pages when creating the tag."
+msgstr ""
+"_BUS_DMA_ALLOCNOW_ - запрашивает выделение всех потенциально необходимых "
+"промежуточных страниц при создании тега."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:373
+msgid "_dmat_ - pointer to the storage for the new tag to be returned."
+msgstr "_dmat_ - указатель на хранилище для нового возвращаемого тега."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:375
+msgid "`int bus_dma_tag_destroy(bus_dma_tag_t dmat)`"
+msgstr "`int bus_dma_tag_destroy(bus_dma_tag_t dmat)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:377
+msgid "Destroy a tag. Returns 0 on success, the error code otherwise."
+msgstr ""
+"Уничтожить тег. Возвращает 0 при успехе, код ошибки в противном случае."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:379
+msgid "dmat - the tag to be destroyed."
+msgstr "dmat - тег, который должен быть уничтожен."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:380
+msgid ""
+"`int bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, "
+"bus_dmamap_t *mapp)`"
+msgstr ""
+"`int bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, "
+"bus_dmamap_t *mapp)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:382
+msgid ""
+"Allocate an area of contiguous memory described by the tag. The size of "
+"memory to be allocated is tag's maxsize. Returns 0 on success, the error "
+"code otherwise. The result still has to be loaded by `bus_dmamap_load()` "
+"before being used to get the physical address of the memory."
+msgstr ""
+"Выделить область непрерывной памяти, описанную тегом. Размер выделяемой "
+"памяти соответствует maxsize тега. Возвращает 0 при успехе, иначе код "
+"ошибки. Результат всё ещё должен быть загружен с помощью `bus_dmamap_load()` "
+"перед использованием для получения физического адреса памяти."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:384
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:396
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:404
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:419
+msgid "_dmat_ - the tag"
+msgstr "_dmat_ - тег"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:385
+msgid ""
+"_vaddr_ - pointer to the storage for the kernel virtual address of the "
+"allocated area to be returned."
+msgstr ""
+"_vaddr_ - указатель на хранилище для возвращаемого виртуального адреса ядра "
+"выделенной области."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:386
+msgid "flags - a bitmap of flags. The only interesting flag is:"
+msgstr "flags - битовая карта флагов. Единственный интересный флаг:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:388
+msgid ""
+"_BUS_DMA_NOWAIT_ - if the memory is not immediately available return the "
+"error. If this flag is not set then the routine is allowed to sleep until "
+"the memory becomes available."
+msgstr ""
+"_BUS_DMA_NOWAIT_ - если память недоступна немедленно, вернуть ошибку. Если "
+"этот флаг не установлен, то процедуре разрешено ожидать до тех пор, пока "
+"память не станет доступной."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:390
+msgid "_mapp_ - pointer to the storage for the new map to be returned."
+msgstr "_mapp_ - указатель на хранилище для возвращаемой новой карты."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:392
+msgid ""
+"`void bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)`"
+msgstr ""
+"`void bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:394
+msgid ""
+"Free the memory allocated by `bus_dmamem_alloc()`. At present, freeing of "
+"the memory allocated with ISA restrictions is not implemented. Due to this "
+"the recommended model of use is to keep and re-use the allocated areas for "
+"as long as possible. Do not lightly free some area and then shortly allocate "
+"it again. That does not mean that `bus_dmamem_free()` should not be used at "
+"all: hopefully it will be properly implemented soon."
+msgstr ""
+"Освободить память, выделенную `bus_dmamem_alloc()`. В настоящее время "
+"освобождение памяти, выделенной с ограничениями ISA, не реализовано. В связи "
+"с этим рекомендуется сохранять и повторно использовать выделенные области "
+"как можно дольше. Не следует без необходимости освобождать область и вскоре "
+"снова её выделять. Это не означает, что `bus_dmamem_free()` не следует "
+"использовать вовсе: есть надежда, что вскоре она будет реализована должным "
+"образом."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:397
+msgid "_vaddr_ - the kernel virtual address of the memory"
+msgstr "_vaddr_ - виртуальный адрес памяти ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:398
+msgid "_map_ - the map of the memory (as returned from `bus_dmamem_alloc()`)"
+msgstr "_map_ - карта памяти (как возвращается из `bus_dmamem_alloc()`)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:400
+msgid ""
+"`int bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)`"
+msgstr ""
+"`int bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:402
+msgid ""
+"Create a map for the tag, to be used in `bus_dmamap_load()` later. Returns 0 "
+"on success, the error code otherwise."
+msgstr ""
+"Создать карту для тега, которая будет использоваться в `bus_dmamap_load()` "
+"позже. Возвращает 0 при успехе, в противном случае — код ошибки."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:405
+msgid ""
+"_flags_ - theoretically, a bit map of flags. But no flags are defined yet, "
+"so at present it will be always 0."
+msgstr ""
+"_flags_ - теоретически, битовая карта флагов. Однако пока никакие флаги не "
+"определены, поэтому в настоящее время значение всегда будет 0."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:406
+msgid "_mapp_ - pointer to the storage for the new map to be returned"
+msgstr ""
+"_mapp_ - указатель на хранилище для новой карты, которая будет возвращена"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:408
+msgid "`int bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)`"
+msgstr "`int bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:410
+msgid "Destroy a map. Returns 0 on success, the error code otherwise."
+msgstr ""
+"Уничтожить карту. Возвращает 0 при успехе, в противном случае — код ошибки."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:412
+msgid "dmat - the tag to which the map is associated"
+msgstr "dmat - тег, с которым ассоциирована карта"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:413
+msgid "map - the map to be destroyed"
+msgstr "map - карта, подлежащая уничтожению"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:415
+msgid ""
+"`int bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, "
+"bus_size_t buflen, bus_dmamap_callback_t *callback, void *callback_arg, int "
+"flags)`"
+msgstr ""
+"`int bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, "
+"bus_size_t buflen, bus_dmamap_callback_t *callback, void *callback_arg, int "
+"flags)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:417
+msgid ""
+"Load a buffer into the map (the map must be previously created by "
+"`bus_dmamap_create()` or `bus_dmamem_alloc()`). All the pages of the buffer "
+"are checked for conformance to the tag requirements and for those not "
+"conformant the bounce pages are allocated. An array of physical segment "
+"descriptors is built and passed to the callback routine. This callback "
+"routine is then expected to handle it in some way. The number of bounce "
+"buffers in the system is limited, so if the bounce buffers are needed but "
+"not immediately available the request will be queued and the callback will "
+"be called when the bounce buffers will become available. Returns 0 if the "
+"callback was executed immediately or `EINPROGRESS` if the request was queued "
+"for future execution. In the latter case the synchronization with queued "
+"callback routine is the responsibility of the driver."
+msgstr ""
+"Загрузить буфер в карту (карта должна быть предварительно создана с помощью "
+"`bus_dmamap_create()` или `bus_dmamem_alloc()`). Все страницы буфера "
+"проверяются на соответствие требованиям тега, и для несоответствующих "
+"выделяются промежуточные страницы. Создается массив дескрипторов физических "
+"сегментов и передается в подпрограмму обратного вызова. Ожидается, что эта "
+"подпрограмма обработает его каким-либо образом. Количество промежуточных "
+"буферов в системе ограничено, поэтому, если эти буферы требуются, но "
+"недоступны немедленно, запрос будет поставлен в очередь, и обратный вызов "
+"будет выполнен, когда промежуточные буферы станут доступны. Возвращает 0, "
+"если обратный вызов был выполнен немедленно, или `EINPROGRESS`, если запрос "
+"был поставлен в очередь для выполнения в будущем. В последнем случае "
+"синхронизация с подпрограммой обратного вызова, поставленной в очередь, "
+"является обязанностью драйвера."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:420
+msgid "_map_ - the map"
+msgstr "_map_ - карта"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:421
+msgid "_buf_ - kernel virtual address of the buffer"
+msgstr "_buf_ - виртуальный адрес буфера в пространстве ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:422
+msgid "_buflen_ - length of the buffer"
+msgstr "_buflen_ - длина буфера"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:423
+msgid "_callback_, `callback_arg` - the callback function and its argument"
+msgstr "_callback_, `callback_arg` - функция обратного вызова и её аргумент"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:425
+msgid ""
+"The prototype of callback function is: `void callback(void *arg, "
+"bus_dma_segment_t *seg, int nseg, int error)`"
+msgstr ""
+"Прототип функции обратного вызова: `void callback(void *arg, "
+"bus_dma_segment_t *seg, int nseg, int error)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:427
+msgid "_arg_ - the same as callback_arg passed to `bus_dmamap_load()`"
+msgstr ""
+"_arg_ - то же самое, что и callback_arg, переданный в `bus_dmamap_load()`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:428
+msgid "_seg_ - array of the segment descriptors"
+msgstr "_seg_ - массив дескрипторов сегментов"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:429
+msgid "_nseg_ - number of descriptors in array"
+msgstr "_nseg_ - количество дескрипторов в массиве"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:430
+msgid ""
+"_error_ - indication of the segment number overflow: if it is set to `EFBIG` "
+"then the buffer did not fit into the maximal number of segments permitted by "
+"the tag. In this case only the permitted number of descriptors will be in "
+"the array. Handling of this situation is up to the driver: depending on the "
+"desired semantics it can either consider this an error or split the buffer "
+"in two and handle the second part separately"
+msgstr ""
+"_error_ - указание на переполнение номера сегмента: если установлено "
+"значение `EFBIG`, значит буфер не поместился в максимальное количество "
+"сегментов, разрешённых тегом. В этом случае в массиве будет только "
+"разрешённое количество дескрипторов. Обработка этой ситуации зависит от "
+"драйвера: в зависимости от желаемой семантики он может либо считать это "
+"ошибкой, либо разделить буфер на две части и обработать вторую часть отдельно"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:432
+msgid "Each entry in the segments array contains the fields:"
+msgstr "Каждая запись в массиве segments содержит поля:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:434
+msgid "_ds_addr_ - physical bus address of the segment"
+msgstr "_ds_addr_ - физический адрес шины сегмента"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:435
+msgid "_ds_len_ - length of the segment"
+msgstr "_ds_len_ - длина сегмента"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:437
+msgid "`void bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)`"
+msgstr "`void bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:439
+msgid "unload the map."
+msgstr "выгрузить карту."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:441
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:448
+msgid "_dmat_ - tag"
+msgstr "_dmat_ - тег"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:442
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:449
+msgid "_map_ - loaded map"
+msgstr "_map_ - загруженная карта"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:444
+msgid ""
+"`void bus_dmamap_sync (bus_dma_tag_t dmat, bus_dmamap_t map, "
+"bus_dmasync_op_t op)`"
+msgstr ""
+"`void bus_dmamap_sync (bus_dma_tag_t dmat, bus_dmamap_t map, "
+"bus_dmasync_op_t op)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:446
+msgid ""
+"Synchronise a loaded buffer with its bounce pages before and after physical "
+"transfer to or from device. This is the function that does all the necessary "
+"copying of data between the original buffer and its mapped version. The "
+"buffers must be synchronized both before and after doing the transfer."
+msgstr ""
+"Синхронизировать загруженный буфер с его промежуточными страницами до и "
+"после физической передачи на устройство или с устройства. Это функция, "
+"которая выполняет все необходимое копирование данных между исходным буфером "
+"и его отображенной версией. Буферы должны быть синхронизированы как до, так "
+"и после выполнения передачи."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:450
+msgid "_op_ - type of synchronization operation to perform:"
+msgstr "_op_ - тип операции синхронизации для выполнения:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:452
+msgid "`BUS_DMASYNC_PREREAD` - before reading from device into buffer"
+msgstr "`BUS_DMASYNC_PREREAD` - перед чтением с устройства в буфер"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:453
+msgid "`BUS_DMASYNC_POSTREAD` - after reading from device into buffer"
+msgstr "`BUS_DMASYNC_POSTREAD` - после чтения из устройства в буфер"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:454
+msgid "`BUS_DMASYNC_PREWRITE` - before writing the buffer to device"
+msgstr "`BUS_DMASYNC_PREWRITE` - перед записью буфера в устройство"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:455
+msgid "`BUS_DMASYNC_POSTWRITE` - after writing the buffer to device"
+msgstr "`BUS_DMASYNC_POSTWRITE` - после записи буфера в устройство"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:457
+msgid ""
+"As of now PREREAD and POSTWRITE are null operations but that may change in "
+"the future, so they must not be ignored in the driver. Synchronization is "
+"not needed for the memory obtained from `bus_dmamem_alloc()`."
+msgstr ""
+"На данный момент PREREAD и POSTWRITE являются пустыми операциями, но это "
+"может измениться в будущем, поэтому их нельзя игнорировать в драйвере. "
+"Синхронизация не требуется для памяти, полученной из `bus_dmamem_alloc()`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:459
+msgid ""
+"Before calling the callback function from `bus_dmamap_load()` the segment "
+"array is stored in the stack. And it gets pre-allocated for the maximal "
+"number of segments allowed by the tag. As a result of this the practical "
+"limit for the number of segments on i386 architecture is about 250-300 (the "
+"kernel stack is 4KB minus the size of the user structure, size of a segment "
+"array entry is 8 bytes, and some space must be left). Since the array is "
+"allocated based on the maximal number this value must not be set higher than "
+"really needed. Fortunately, for most of hardware the maximal supported "
+"number of segments is much lower. But if the driver wants to handle buffers "
+"with a very large number of scatter-gather segments it should do that in "
+"portions: load part of the buffer, transfer it to the device, load next part "
+"of the buffer, and so on."
+msgstr ""
+"Перед вызовом функции обратного вызова из `bus_dmamap_load()` массив "
+"сегментов сохраняется в стеке. Он предварительно выделяется для "
+"максимального количества сегментов, разрешенного тегом. В результате этого "
+"практический предел количества сегментов на архитектуре i386 составляет "
+"около 250-300 (размер стека ядра — 4 КБ минус размер структуры пользователя, "
+"размер элемента массива сегментов — 8 байт, и необходимо оставить некоторое "
+"пространство). Поскольку массив выделяется исходя из максимального числа, "
+"это значение не должно быть установлено выше, чем действительно необходимо. "
+"К счастью, для большинства оборудования максимально поддерживаемое "
+"количество сегментов значительно ниже. Но если драйвер должен обрабатывать "
+"буферы с очень большим количеством сегментов scatter-gather, он должен "
+"делать это по частям: загрузить часть буфера, передать его устройству, "
+"загрузить следующую часть буфера и так далее."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:461
+msgid ""
+"Another practical consequence is that the number of segments may limit the "
+"size of the buffer. If all the pages in the buffer happen to be physically "
+"non-contiguous then the maximal supported buffer size for that fragmented "
+"case would be (nsegments * page_size). For example, if a maximal number of "
+"10 segments is supported then on i386 maximal guaranteed supported buffer "
+"size would be 40K. If a higher size is desired then special tricks should be "
+"used in the driver."
+msgstr ""
+"Еще одно практическое следствие заключается в том, что количество сегментов "
+"может ограничивать размер буфера. Если все страницы в буфере окажутся "
+"физически несмежными, то максимальный поддерживаемый размер буфера для "
+"такого фрагментированного случая будет равен (nsegments * page_size). "
+"Например, если поддерживается максимальное количество сегментов, равное 10, "
+"то на i386 максимальный гарантированно поддерживаемый размер буфера составит "
+"40K. Если требуется больший размер, то в драйвере следует использовать "
+"специальные приемы."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:463
+msgid ""
+"If the hardware does not support scatter-gather at all or the driver wants "
+"to support some buffer size even if it is heavily fragmented then the "
+"solution is to allocate a contiguous buffer in the driver and use it as "
+"intermediate storage if the original buffer does not fit."
+msgstr ""
+"Если оборудование не поддерживает scatter-gather вообще или драйвер хочет "
+"поддерживать некоторый размер буфера, даже если он сильно фрагментирован, то "
+"решение состоит в выделении непрерывного буфера в драйвере и использовании "
+"его в качестве промежуточного хранилища, если исходный буфер не подходит."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:465
+msgid ""
+"Below are the typical call sequences when using a map depend on the use of "
+"the map. The characters -> are used to show the flow of time."
+msgstr ""
+"Ниже представлены типичные последовательности вызовов при использовании "
+"карты в зависимости от её назначения. Символы -> используются для "
+"обозначения последовательности во времени."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:467
+msgid ""
+"For a buffer which stays practically fixed during all the time between "
+"attachment and detachment of a device:"
+msgstr ""
+"Для буфера, который остается практически неизменным в течение всего времени "
+"между присоединением и отсоединением устройства:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:469
+msgid ""
+"bus_dmamem_alloc -> bus_dmamap_load -> ...use buffer... -> -> "
+"bus_dmamap_unload -> bus_dmamem_free"
+msgstr ""
+"bus_dmamem_alloc -> bus_dmamap_load -> ...use buffer... -> -> "
+"bus_dmamap_unload -> bus_dmamem_free"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:471
+msgid ""
+"For a buffer that changes frequently and is passed from outside the driver:"
+msgstr "Для буфера, который часто изменяется и передается извне драйвера:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:480
+#, no-wrap
+msgid ""
+" bus_dmamap_create ->\n"
+" -> bus_dmamap_load -> bus_dmamap_sync(PRE...) -> do transfer ->\n"
+" -> bus_dmamap_sync(POST...) -> bus_dmamap_unload ->\n"
+" ...\n"
+" -> bus_dmamap_load -> bus_dmamap_sync(PRE...) -> do transfer ->\n"
+" -> bus_dmamap_sync(POST...) -> bus_dmamap_unload ->\n"
+" -> bus_dmamap_destroy\n"
+msgstr ""
+" bus_dmamap_create ->\n"
+" -> bus_dmamap_load -> bus_dmamap_sync(PRE...) -> do transfer ->\n"
+" -> bus_dmamap_sync(POST...) -> bus_dmamap_unload ->\n"
+" ...\n"
+" -> bus_dmamap_load -> bus_dmamap_sync(PRE...) -> do transfer ->\n"
+" -> bus_dmamap_sync(POST...) -> bus_dmamap_unload ->\n"
+" -> bus_dmamap_destroy\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:483
+msgid ""
+"When loading a map created by `bus_dmamem_alloc()` the passed address and "
+"size of the buffer must be the same as used in `bus_dmamem_alloc()`. In this "
+"case it is guaranteed that the whole buffer will be mapped as one segment "
+"(so the callback may be based on this assumption) and the request will be "
+"executed immediately (EINPROGRESS will never be returned). All the callback "
+"needs to do in this case is to save the physical address."
+msgstr ""
+"При загрузке карты, созданной `bus_dmamem_alloc()`, переданные адрес и "
+"размер буфера должны быть такими же, как использованные в "
+"`bus_dmamem_alloc()`. В этом случае гарантируется, что весь буфер будет "
+"отображен как один сегмент (так что обратный вызов может основываться на "
+"этом предположении) и запрос будет выполнен немедленно (EINPROGRESS никогда "
+"не будет возвращен). Все, что нужно сделать обратному вызову в этом случае, "
+"— это сохранить физический адрес."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:485
+msgid "A typical example would be:"
+msgstr "Типичный пример:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:493
+#, no-wrap
+msgid ""
+" static void\n"
+" alloc_callback(void *arg, bus_dma_segment_t *seg, int nseg, int error)\n"
+" {\n"
+" *(bus_addr_t *)arg = seg[0].ds_addr;\n"
+" }\n"
+msgstr ""
+" static void\n"
+" alloc_callback(void *arg, bus_dma_segment_t *seg, int nseg, int error)\n"
+" {\n"
+" *(bus_addr_t *)arg = seg[0].ds_addr;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:504
+#, no-wrap
+msgid ""
+" ...\n"
+" int error;\n"
+" struct somedata {\n"
+" ....\n"
+" };\n"
+" struct somedata *vsomedata; /* virtual address */\n"
+" bus_addr_t psomedata; /* physical bus-relative address */\n"
+" bus_dma_tag_t tag_somedata;\n"
+" bus_dmamap_t map_somedata;\n"
+" ...\n"
+msgstr ""
+" ...\n"
+" int error;\n"
+" struct somedata {\n"
+" ....\n"
+" };\n"
+" struct somedata *vsomedata; /* virtual address */\n"
+" bus_addr_t psomedata; /* physical bus-relative address */\n"
+" bus_dma_tag_t tag_somedata;\n"
+" bus_dmamap_t map_somedata;\n"
+" ...\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:512
+#, no-wrap
+msgid ""
+" error=bus_dma_tag_create(parent_tag, alignment,\n"
+" boundary, lowaddr, highaddr, /*filter*/ NULL, /*filterarg*/ NULL,\n"
+" /*maxsize*/ sizeof(struct somedata), /*nsegments*/ 1,\n"
+" /*maxsegsz*/ sizeof(struct somedata), /*flags*/ 0,\n"
+" &tag_somedata);\n"
+" if(error)\n"
+" return error;\n"
+msgstr ""
+" error=bus_dma_tag_create(parent_tag, alignment,\n"
+" boundary, lowaddr, highaddr, /*filter*/ NULL, /*filterarg*/ NULL,\n"
+" /*maxsize*/ sizeof(struct somedata), /*nsegments*/ 1,\n"
+" /*maxsegsz*/ sizeof(struct somedata), /*flags*/ 0,\n"
+" &tag_somedata);\n"
+" if(error)\n"
+" return error;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:517
+#, no-wrap
+msgid ""
+" error = bus_dmamem_alloc(tag_somedata, &vsomedata, /* flags*/ 0,\n"
+" &map_somedata);\n"
+" if(error)\n"
+" return error;\n"
+msgstr ""
+" error = bus_dmamem_alloc(tag_somedata, &vsomedata, /* flags*/ 0,\n"
+" &map_somedata);\n"
+" if(error)\n"
+" return error;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:521
+#, no-wrap
+msgid ""
+" bus_dmamap_load(tag_somedata, map_somedata, (void *)vsomedata,\n"
+" sizeof (struct somedata), alloc_callback,\n"
+" (void *) &psomedata, /*flags*/0);\n"
+msgstr ""
+" bus_dmamap_load(tag_somedata, map_somedata, (void *)vsomedata,\n"
+" sizeof (struct somedata), alloc_callback,\n"
+" (void *) &psomedata, /*flags*/0);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:524
+msgid ""
+"Looks a bit long and complicated but that is the way to do it. The practical "
+"consequence is: if multiple memory areas are allocated always together it "
+"would be a really good idea to combine them all into one structure and "
+"allocate as one (if the alignment and boundary limitations permit)."
+msgstr ""
+"Выглядит немного длинно и сложно, но это правильный способ. Практическое "
+"следствие таково: если несколько областей памяти выделяются всегда вместе, "
+"было бы отличной идеей объединить их все в одну структуру и выделять как "
+"единое целое (если ограничения выравнивания и границ позволяют)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:526
+msgid ""
+"When loading an arbitrary buffer into the map created by "
+"`bus_dmamap_create()` special measures must be taken to synchronize with the "
+"callback in case it would be delayed. The code would look like:"
+msgstr ""
+"При загрузке произвольного буфера в карту, созданную `bus_dmamap_create()`, "
+"необходимо принять специальные меры для синхронизации с обратным вызовом, "
+"если он будет задержан. Код будет выглядеть следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:532
+#, no-wrap
+msgid ""
+" {\n"
+" int s;\n"
+" int error;\n"
+msgstr ""
+" {\n"
+" int s;\n"
+" int error;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:551
+#, no-wrap
+msgid ""
+" s = splsoftvm();\n"
+" error = bus_dmamap_load(\n"
+" dmat,\n"
+" dmamap,\n"
+" buffer_ptr,\n"
+" buffer_len,\n"
+" callback,\n"
+" /*callback_arg*/ buffer_descriptor,\n"
+" /*flags*/0);\n"
+" if (error == EINPROGRESS) {\n"
+" /*\n"
+" * Do whatever is needed to ensure synchronization\n"
+" * with callback. Callback is guaranteed not to be started\n"
+" * until we do splx() or tsleep().\n"
+" */\n"
+" }\n"
+" splx(s);\n"
+" }\n"
+msgstr ""
+" s = splsoftvm();\n"
+" error = bus_dmamap_load(\n"
+" dmat,\n"
+" dmamap,\n"
+" buffer_ptr,\n"
+" buffer_len,\n"
+" callback,\n"
+" /*callback_arg*/ buffer_descriptor,\n"
+" /*flags*/0);\n"
+" if (error == EINPROGRESS) {\n"
+" /*\n"
+" * Do whatever is needed to ensure synchronization\n"
+" * with callback. Callback is guaranteed not to be started\n"
+" * until we do splx() or tsleep().\n"
+" */\n"
+" }\n"
+" splx(s);\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:554
+msgid "Two possible approaches for the processing of requests are:"
+msgstr "Два возможных подхода для обработки запросов:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:556
+msgid ""
+"If requests are completed by marking them explicitly as done (such as the "
+"CAM requests) then it would be simpler to put all the further processing "
+"into the callback driver which would mark the request when it is done. Then "
+"not much extra synchronization is needed. For the flow control reasons it "
+"may be a good idea to freeze the request queue until this request gets "
+"completed."
+msgstr ""
+"Если запросы завершаются путём явной пометки их как выполненных (например, "
+"запросы CAM), то было бы проще поместить всю дальнейшую обработку в драйвер "
+"обратного вызова, который отмечал бы запрос по его завершении. В этом случае "
+"не потребуется много дополнительной синхронизации. По соображениям "
+"управления потоком может быть полезно заморозить очередь запросов до "
+"завершения этого запроса."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:558
+msgid ""
+"If requests are completed when the function returns (such as classic read or "
+"write requests on character devices) then a synchronization flag should be "
+"set in the buffer descriptor and `tsleep()` called. Later when the callback "
+"gets called it will do its processing and check this synchronization flag. "
+"If it is set then the callback should issue a wakeup. In this approach the "
+"callback function could either do all the needed processing (just like the "
+"previous case) or simply save the segments array in the buffer descriptor. "
+"Then after callback completes the calling function could use this saved "
+"segments array and do all the processing."
+msgstr ""
+"Если запросы завершаются при возврате функции (например, классические "
+"запросы на чтение или запись для символьных устройств), то в дескрипторе "
+"буфера должен быть установлен флаг синхронизации и вызвана функция "
+"`tsleep()`. Позже, когда будет вызван обратный вызов, он выполнит свою "
+"обработку и проверит этот флаг синхронизации. Если флаг установлен, обратный "
+"вызов должен инициировать пробуждение. При таком подходе функция обратного "
+"вызова может либо выполнить всю необходимую обработку (как в предыдущем "
+"случае), либо просто сохранить массив сегментов в дескрипторе буфера. Затем "
+"после завершения обратного вызова вызывающая функция может использовать этот "
+"сохранённый массив сегментов и выполнить всю обработку."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:560
+#, no-wrap
+msgid "DMA"
+msgstr "DMA"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:563
+msgid ""
+"The Direct Memory Access (DMA) is implemented in the ISA bus through the DMA "
+"controller (actually, two of them but that is an irrelevant detail). To make "
+"the early ISA devices simple and cheap the logic of the bus control and "
+"address generation was concentrated in the DMA controller. Fortunately, "
+"FreeBSD provides a set of functions that mostly hide the annoying details of "
+"the DMA controller from the device drivers."
+msgstr ""
+"Прямой доступ к памяти (DMA) реализован в шине ISA через контроллер DMA (на "
+"самом деле их два, но это несущественная деталь). Чтобы сделать ранние "
+"устройства ISA простыми и дешёвыми, логика управления шиной и генерации "
+"адресов была сосредоточена в контроллере DMA. К счастью, FreeBSD "
+"предоставляет набор функций, которые в основном скрывают раздражающие детали "
+"работы контроллера DMA от драйверов устройств."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:565
+msgid ""
+"The simplest case is for the fairly intelligent devices. Like the bus master "
+"devices on PCI they can generate the bus cycles and memory addresses all by "
+"themselves. The only thing they really need from the DMA controller is bus "
+"arbitration. So for this purpose they pretend to be cascaded slave DMA "
+"controllers. And the only thing needed from the system DMA controller is to "
+"enable the cascaded mode on a DMA channel by calling the following function "
+"when attaching the driver:"
+msgstr ""
+"Самый простой случай — для достаточно интеллектуальных устройств. Например, "
+"устройства с bus mastering на PCI могут сами генерировать шинные циклы и "
+"адреса памяти. Единственное, что им действительно нужно от контроллера DMA, "
+"— это арбитраж шины. Для этой цели они притворяются каскадированными "
+"подчинёнными контроллерами DMA. И единственное, что требуется от системного "
+"контроллера DMA, — это включить каскадный режим на канале DMA, вызвав "
+"следующую функцию при присоединении драйвера:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:567
+msgid "`void isa_dmacascade(int channel_number)`"
+msgstr "`void isa_dmacascade(int channel_number)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:569
+msgid ""
+"All the further activity is done by programming the device. When detaching "
+"the driver no DMA-related functions need to be called."
+msgstr ""
+"Все последующие действия выполняются путем программирования устройства. При "
+"отсоединении драйвера нет необходимости вызывать функции, связанные с DMA."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:571
+msgid ""
+"For the simpler devices things get more complicated. The functions used are:"
+msgstr ""
+"Для более простых устройств всё становится сложнее. Используются следующие "
+"функции:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:573
+msgid "`int isa_dma_acquire(int chanel_number)`"
+msgstr "`int isa_dma_acquire(int chanel_number)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:575
+msgid ""
+"Reserve a DMA channel. Returns 0 on success or EBUSY if the channel was "
+"already reserved by this or a different driver. Most of the ISA devices are "
+"not able to share DMA channels anyway, so normally this function is called "
+"when attaching a device. This reservation was made redundant by the modern "
+"interface of bus resources but still must be used in addition to the latter. "
+"If not used then later, other DMA routines will panic."
+msgstr ""
+"Зарезервировать канал DMA. Возвращает 0 при успехе или EBUSY, если канал уже "
+"зарезервирован этим или другим драйвером. Большинство устройств ISA не "
+"способны совместно использовать каналы DMA, поэтому обычно эта функция "
+"вызывается при присоединении устройства. Это резервирование стало избыточным "
+"с появлением современного интерфейса ресурсов шины, но всё ещё должно "
+"использоваться в дополнение к последнему. Если резервирование не "
+"использовать, то в дальнейшем другие процедуры DMA вызовут панику ядра."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:576
+msgid "`int isa_dma_release(int chanel_number)`"
+msgstr "`int isa_dma_release(int chanel_number)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:578
+msgid ""
+"Release a previously reserved DMA channel. No transfers must be in progress "
+"when the channel is released (in addition the device must not try to "
+"initiate transfer after the channel is released)."
+msgstr ""
+"Освободить ранее зарезервированный канал DMA. На момент освобождения канала "
+"не должно быть активных передач (дополнительно устройство не должно пытаться "
+"инициировать передачу после освобождения канала)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:579
+msgid "`void isa_dmainit(int chan, u_int bouncebufsize)`"
+msgstr "`void isa_dmainit(int chan, u_int bouncebufsize)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:581
+msgid ""
+"Allocate a bounce buffer for use with the specified channel. The requested "
+"size of the buffer can not exceed 64KB. This bounce buffer will be "
+"automatically used later if a transfer buffer happens to be not physically "
+"contiguous or outside of the memory accessible by the ISA bus or crossing "
+"the 64KB boundary. If the transfers will be always done from buffers which "
+"conform to these conditions (such as those allocated by `bus_dmamem_alloc()` "
+"with proper limitations) then `isa_dmainit()` does not have to be called. "
+"But it is quite convenient to transfer arbitrary data using the DMA "
+"controller. The bounce buffer will automatically care of the scatter-gather "
+"issues."
+msgstr ""
+"Выделить промежуточный буфер для использования с указанным каналом. "
+"Запрашиваемый размер буфера не может превышать 64 КБ. Этот промежуточный "
+"буфер будет автоматически использован в дальнейшем, если передаваемый буфер "
+"окажется не физически непрерывным, находится вне памяти, доступной шине ISA, "
+"или пересекает границу 64 КБ. Если передача всегда будет выполняться из "
+"буферов, соответствующих этим условиям (например, выделенных с помощью "
+"`bus_dmamem_alloc()` с соответствующими ограничениями), то вызов "
+"`isa_dmainit()` не требуется. Однако довольно удобно передавать произвольные "
+"данные с использованием контроллера DMA. Промежуточный буфер автоматически "
+"решит проблемы в ситуациях, когда данные разбросаны в памяти, и их надо "
+"собирать."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:583
+msgid "_chan_ - channel number"
+msgstr "_chan_ - номер канала"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:584
+msgid "_bouncebufsize_ - size of the bounce buffer in bytes"
+msgstr "_bouncebufsize_ - размер промежуточного буфера в байтах"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:586
+msgid "`void isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan)`"
+msgstr "`void isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:588
+msgid ""
+"Prepare to start a DMA transfer. This function must be called to set up the "
+"DMA controller before actually starting transfer on the device. It checks "
+"that the buffer is contiguous and falls into the ISA memory range, if not "
+"then the bounce buffer is automatically used. If bounce buffer is required "
+"but not set up by `isa_dmainit()` or too small for the requested transfer "
+"size then the system will panic. In case of a write request with bounce "
+"buffer the data will be automatically copied to the bounce buffer."
+msgstr ""
+"Подготовка к началу передачи DMA. Эта функция должна быть вызвана для "
+"настройки контроллера DMA перед фактическим началом передачи на устройстве. "
+"Она проверяет, что буфер является непрерывным и попадает в диапазон памяти "
+"ISA, если нет, то автоматически используется промежуточный буфер. Если "
+"требуется промежуточный буфер, но он не настроен с помощью `isa_dmainit()` "
+"или слишком мал для запрошенного размера передачи, система перейдет в "
+"состояние паники. В случае запроса на запись с промежуточным буфером данные "
+"будут автоматически скопированы в этот буфер."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:589
+msgid ""
+"flags - a bitmask determining the type of operation to be done. The "
+"direction bits B_READ and B_WRITE are mutually exclusive."
+msgstr ""
+"flags - битовая маска, определяющая тип выполняемой операции. Бит "
+"направления B_READ и B_WRITE являются взаимоисключающими."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:591
+msgid "B_READ - read from the ISA bus into memory"
+msgstr "B_READ - чтение с шины ISA в память"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:592
+msgid "B_WRITE - write from the memory to the ISA bus"
+msgstr "B_WRITE - запись из памяти на шину ISA"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:593
+msgid ""
+"B_RAW - if set then the DMA controller will remember the buffer and after "
+"the end of transfer will automatically re-initialize itself to repeat "
+"transfer of the same buffer again (of course, the driver may change the data "
+"in the buffer before initiating another transfer in the device). If not set "
+"then the parameters will work only for one transfer, and `isa_dmastart()` "
+"will have to be called again before initiating the next transfer. Using "
+"B_RAW makes sense only if the bounce buffer is not used."
+msgstr ""
+"B_RAW - если установлен, то контроллер DMA запомнит буфер и после завершения "
+"передачи автоматически переинициализирует себя для повторной передачи того "
+"же буфера (конечно, драйвер может изменить данные в буфере перед "
+"инициированием следующей передачи на устройстве). Если не установлен, то "
+"параметры будут работать только для одной передачи, и перед инициированием "
+"следующей передачи снова потребуется вызвать `isa_dmastart()`. Использование "
+"B_RAW имеет смысл только если промежуточный буфер не используется."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:595
+msgid "addr - virtual address of the buffer"
+msgstr "addr - виртуальный адрес буфера"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:596
+msgid ""
+"nbytes - length of the buffer. Must be less or equal to 64KB. Length of 0 is "
+"not allowed: the DMA controller will understand it as 64KB while the kernel "
+"code will understand it as 0 and that would cause unpredictable effects. For "
+"channels number 4 and higher the length must be even because these channels "
+"transfer 2 bytes at a time. In case of an odd length the last byte will not "
+"be transferred."
+msgstr ""
+"nbytes - длина буфера. Должна быть меньше или равна 64 КБ. Длина 0 не "
+"допускается: контроллер DMA интерпретирует это как 64 КБ, в то время как код "
+"ядра поймёт это как 0, что приведёт к непредсказуемым последствиям. Для "
+"каналов номер 4 и выше длина должна быть чётной, так как эти каналы передают "
+"по 2 байта за раз. В случае нечётной длины последний байт не будет передан."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:597
+msgid "chan - channel number"
+msgstr "chan - номер канала"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:598
+msgid "`void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan)`"
+msgstr "`void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:600
+msgid ""
+"Synchronize the memory after device reports that transfer is done. If that "
+"was a read operation with a bounce buffer then the data will be copied from "
+"the bounce buffer to the original buffer. Arguments are the same as for "
+"`isa_dmastart()`. Flag B_RAW is permitted but it does not affect "
+"`isa_dmadone()` in any way."
+msgstr ""
+"Синхронизировать память после того, как устройство сообщает о завершении "
+"передачи. Если это была операция чтения с промежуточным буфером, то данные "
+"будут скопированы из этого буфера в исходный буфер. Аргументы такие же, как "
+"у `isa_dmastart()`. Флаг B_RAW разрешён, но он никак не влияет на "
+"`isa_dmadone()`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:601
+msgid "`int isa_dmastatus(int channel_number)`"
+msgstr "`int isa_dmastatus(int channel_number)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:603
+msgid ""
+"Returns the number of bytes left in the current transfer to be transferred. "
+"In case the flag B_READ was set in `isa_dmastart()` the number returned will "
+"never be equal to zero. At the end of transfer it will be automatically "
+"reset back to the length of buffer. The normal use is to check the number of "
+"bytes left after the device signals that the transfer is completed. If the "
+"number of bytes is not 0 then something probably went wrong with that "
+"transfer."
+msgstr ""
+"Возвращает количество оставшихся для передачи байт в текущей передаче. Если "
+"флаг B_READ был установлен в `isa_dmastart()`, возвращаемое значение никогда "
+"не будет равно нулю. В конце передачи оно автоматически сбрасывается обратно "
+"к длине буфера. Обычное использование — проверка количества оставшихся байт "
+"после того, как устройство сигнализирует о завершении передачи. Если "
+"количество байт не равно 0, то, вероятно, в передаче произошла ошибка."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:604
+msgid "`int isa_dmastop(int channel_number)`"
+msgstr "`int isa_dmastop(int channel_number)`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:606
+msgid ""
+"Aborts the current transfer and returns the number of bytes left "
+"untransferred."
+msgstr ""
+"Прерывает текущую передачу и возвращает количество непереданных байтов."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:608
+#, no-wrap
+msgid "xxx_isa_probe"
+msgstr "xxx_isa_probe"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:611
+msgid ""
+"This function probes if a device is present. If the driver supports auto-"
+"detection of some part of device configuration (such as interrupt vector or "
+"memory address) this auto-detection must be done in this routine."
+msgstr ""
+"Эта функция проверяет наличие устройства. Если драйвер поддерживает "
+"автоматическое определение некоторых параметров конфигурации устройства "
+"(таких как вектор прерывания или адрес памяти), это автоматическое "
+"определение должно выполняться в данной процедуре."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:613
+msgid ""
+"As for any other bus, if the device cannot be detected or is detected but "
+"failed the self-test or some other problem happened then it returns a "
+"positive value of error. The value `ENXIO` must be returned if the device is "
+"not present. Other error values may mean other conditions. Zero or negative "
+"values mean success. Most of the drivers return zero as success."
+msgstr ""
+"Как и для любой другой шины, если устройство не может быть обнаружено, или "
+"обнаружено, но не прошло самопроверку, или возникла другая проблема, то "
+"возвращается положительное значение ошибки. Значение `ENXIO` должно "
+"возвращаться, если устройство отсутствует. Другие значения ошибок могут "
+"означать иные условия. Нулевые или отрицательные значения означают успех. "
+"Большинство драйверов возвращают ноль в случае успеха."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:615
+msgid ""
+"The negative return values are used when a PnP device supports multiple "
+"interfaces. For example, an older compatibility interface and a newer "
+"advanced interface which are supported by different drivers. Then both "
+"drivers would detect the device. The driver which returns a higher value in "
+"the probe routine takes precedence (in other words, the driver returning 0 "
+"has highest precedence, one returning -1 is next, one returning -2 is after "
+"it and so on). In result the devices which support only the old interface "
+"will be handled by the old driver (which should return -1 from the probe "
+"routine) while the devices supporting the new interface as well will be "
+"handled by the new driver (which should return 0 from the probe routine)."
+msgstr ""
+"Отрицательные возвращаемые значения используются, когда устройство PnP "
+"поддерживает несколько интерфейсов. Например, старый совместимый интерфейс и "
+"новый расширенный интерфейс, которые поддерживаются разными драйверами. В "
+"этом случае оба драйвера обнаружат устройство. Драйвер, который возвращает "
+"большее значение в процедуре обнаружения, получает приоритет (другими "
+"словами, драйвер, возвращающий 0, имеет наивысший приоритет, возвращающий -1 "
+"— следующий, возвращающий -2 — за ним и так далее). В результате устройства, "
+"поддерживающие только старый интерфейс, будут обрабатываться старым "
+"драйвером (который должен возвращать -1 из процедуры probe), а устройства, "
+"поддерживающие также новый интерфейс, будут обрабатываться новым драйвером "
+"(который должен возвращать 0 из процедуры обнаружения)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:617
+msgid ""
+"The device descriptor struct xxx_softc is allocated by the system before "
+"calling the probe routine. If the probe routine returns an error the "
+"descriptor will be automatically deallocated by the system. So if a probing "
+"error occurs the driver must make sure that all the resources it used during "
+"probe are deallocated and that nothing keeps the descriptor from being "
+"safely deallocated. If the probe completes successfully the descriptor will "
+"be preserved by the system and later passed to the routine "
+"`xxx_isa_attach()`. If a driver returns a negative value it can not be sure "
+"that it will have the highest priority and its attach routine will be "
+"called. So in this case it also must release all the resources before "
+"returning and if necessary allocate them again in the attach routine. When "
+"`xxx_isa_probe()` returns 0 releasing the resources before returning is also "
+"a good idea and a well-behaved driver should do so. But in cases where there "
+"is some problem with releasing the resources the driver is allowed to keep "
+"resources between returning 0 from the probe routine and execution of the "
+"attach routine."
+msgstr ""
+"Структура дескриптора устройства `xxx_softc` выделяется системой до вызова "
+"процедуры обнаружения. Если процедура обнаружения возвращает ошибку, "
+"дескриптор автоматически освобождается системой. Поэтому при возникновении "
+"ошибки обнаружения драйвер должен убедиться, что все ресурсы, использованные "
+"во время обнаружения, освобождены и ничто не мешает безопасному освобождению "
+"дескриптора.Если обнаружение завершается успешно, дескриптор сохраняется "
+"системой и позже передаётся в процедуру `xxx_isa_attach()`. Если драйвер "
+"возвращает отрицательное значение, он не может быть уверен, что получит "
+"наивысший приоритет и его процедура присоединения будет вызвана. Поэтому в "
+"этом случае он также должен освободить все ресурсы перед возвратом и, если "
+"необходимо, выделить их снова в процедуре присоединения. Когда "
+"`xxx_isa_probe()` возвращает 0, освобождение ресурсов перед возвратом также "
+"является хорошей практикой, и корректно работающий драйвер должен так "
+"поступать. Однако в случаях, когда возникают проблемы с освобождением "
+"ресурсов, драйверу разрешается сохранять ресурсы между возвратом 0 из "
+"процедуры обнаружения и выполнением процедуры присоединения."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:619
+msgid ""
+"A typical probe routine starts with getting the device descriptor and unit:"
+msgstr ""
+"Типичная процедура обнаружения начинается с получения дескриптора устройства "
+"и номера устройства:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:626
+#, no-wrap
+msgid ""
+" struct xxx_softc *sc = device_get_softc(dev);\n"
+" int unit = device_get_unit(dev);\n"
+" int pnperror;\n"
+" int error = 0;\n"
+msgstr ""
+" struct xxx_softc *sc = device_get_softc(dev);\n"
+" int unit = device_get_unit(dev);\n"
+" int pnperror;\n"
+" int error = 0;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:629
+#, no-wrap
+msgid ""
+" sc->dev = dev; /* link it back */\n"
+" sc->unit = unit;\n"
+msgstr ""
+" sc->dev = dev; /* link it back */\n"
+" sc->unit = unit;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:632
+msgid ""
+"Then check for the PnP devices. The check is carried out by a table "
+"containing the list of PnP IDs supported by this driver and human-readable "
+"descriptions of the device models corresponding to these IDs."
+msgstr ""
+"Затем проверьте устройства PnP. Проверка осуществляется с помощью таблицы, "
+"содержащей список PnP ID, поддерживаемых этим драйвером, и удобочитаемые "
+"описания моделей устройств, соответствующих этим ID."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:638
+#, no-wrap
+msgid ""
+" pnperror=ISA_PNP_PROBE(device_get_parent(dev), dev,\n"
+" xxx_pnp_ids); if(pnperror == ENXIO) return ENXIO;\n"
+msgstr ""
+" pnperror=ISA_PNP_PROBE(device_get_parent(dev), dev,\n"
+" xxx_pnp_ids); if(pnperror == ENXIO) return ENXIO;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:641
+msgid ""
+"The logic of ISA_PNP_PROBE is the following: If this card (device unit) was "
+"not detected as PnP then ENOENT will be returned. If it was detected as PnP "
+"but its detected ID does not match any of the IDs in the table then ENXIO is "
+"returned. Finally, if it has PnP support and it matches on of the IDs in the "
+"table, 0 is returned and the appropriate description from the table is set "
+"by `device_set_desc()`."
+msgstr ""
+"Логика работы `ISA_PNP_PROBE` следующая: если данная карта (устройство) не "
+"была обнаружена как PnP, то будет возвращено `ENOENT`. Если она была "
+"обнаружена как PnP, но её обнаруженный ID не совпадает ни с одним из ID в "
+"таблице, то возвращается `ENXIO`. Наконец, если устройство поддерживает PnP "
+"и его ID совпадает с одним из ID в таблице, возвращается `0`, а "
+"соответствующее описание из таблицы устанавливается с помощью "
+"`device_set_desc()`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:643
+msgid ""
+"If a driver supports only PnP devices then the condition would look like:"
+msgstr ""
+"Если драйвер поддерживает только устройства PnP, то условие будет выглядеть "
+"следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:648
+#, no-wrap
+msgid ""
+" if(pnperror != 0)\n"
+" return pnperror;\n"
+msgstr ""
+" if(pnperror != 0)\n"
+" return pnperror;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:651
+msgid ""
+"No special treatment is required for the drivers which do not support PnP "
+"because they pass an empty PnP ID table and will always get ENXIO if called "
+"on a PnP card."
+msgstr ""
+"Для драйверов, которые не поддерживают PnP, не требуется специальной "
+"обработки, так как они передают пустую таблицу идентификаторов PnP и всегда "
+"будут получать ENXIO при вызове на PnP-карте."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:653
+msgid ""
+"The probe routine normally needs at least some minimal set of resources, "
+"such as I/O port number to find the card and probe it. Depending on the "
+"hardware the driver may be able to discover the other necessary resources "
+"automatically. The PnP devices have all the resources pre-set by the PnP "
+"subsystem, so the driver does not need to discover them by itself."
+msgstr ""
+"Функция обнаружения обычно требует как минимум некоторый минимальный набор "
+"ресурсов, например, номер порта ввода-вывода, чтобы найти карту и проверить "
+"её. В зависимости от оборудования драйвер может автоматически обнаружить "
+"другие необходимые ресурсы. Устройства PnP имеют все ресурсы, предварительно "
+"установленные подсистемой PnP, поэтому драйверу не нужно обнаруживать их "
+"самостоятельно."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:655
+msgid ""
+"Typically the minimal information required to get access to the device is "
+"the I/O port number. Then some devices allow to get the rest of information "
+"from the device configuration registers (though not all devices do that). So "
+"first we try to get the port start value:"
+msgstr ""
+"Обычно минимальная информация, необходимая для доступа к устройству, — это "
+"номер порта ввода-вывода. Затем некоторые устройства позволяют получить "
+"остальную информацию из регистров конфигурации устройства (хотя не все "
+"устройства это поддерживают). Поэтому сначала мы пытаемся получить начальное "
+"значение порта:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:660
+#, no-wrap
+msgid ""
+" sc->port0 = bus_get_resource_start(dev,\n"
+" SYS_RES_IOPORT, 0 /*rid*/); if(sc->port0 == 0) return ENXIO;\n"
+msgstr ""
+" sc->port0 = bus_get_resource_start(dev,\n"
+" SYS_RES_IOPORT, 0 /*rid*/); if(sc->port0 == 0) return ENXIO;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:663
+msgid ""
+"The base port address is saved in the structure softc for future use. If it "
+"will be used very often then calling the resource function each time would "
+"be prohibitively slow. If we do not get a port we just return an error. Some "
+"device drivers can instead be clever and try to probe all the possible "
+"ports, like this:"
+msgstr ""
+"Базовый адрес порта сохраняется в структуре softc для последующего "
+"использования. Если он будет использоваться очень часто, то вызов функции "
+"ресурса каждый раз будет неприемлемо медленным. Если мы не получаем порт, мы "
+"просто возвращаем ошибку. Некоторые драйверы устройств могут вместо этого "
+"быть умнее и попытаться обнаружить все возможные порты, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:677
+#, no-wrap
+msgid ""
+" /* table of all possible base I/O port addresses for this device */\n"
+" static struct xxx_allports {\n"
+" u_short port; /* port address */\n"
+" short used; /* flag: if this port is already used by some unit */\n"
+" } xxx_allports = {\n"
+" { 0x300, 0 },\n"
+" { 0x320, 0 },\n"
+" { 0x340, 0 },\n"
+" { 0, 0 } /* end of table */\n"
+" };\n"
+msgstr ""
+" /* table of all possible base I/O port addresses for this device */\n"
+" static struct xxx_allports {\n"
+" u_short port; /* port address */\n"
+" short used; /* flag: if this port is already used by some unit */\n"
+" } xxx_allports = {\n"
+" { 0x300, 0 },\n"
+" { 0x320, 0 },\n"
+" { 0x340, 0 },\n"
+" { 0, 0 } /* end of table */\n"
+" };\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:681
+#, no-wrap
+msgid ""
+" ...\n"
+" int port, i;\n"
+" ...\n"
+msgstr ""
+" ...\n"
+" int port, i;\n"
+" ...\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:687
+#, no-wrap
+msgid ""
+" port = bus_get_resource_start(dev, SYS_RES_IOPORT, 0 /*rid*/);\n"
+" if(port !=0 ) {\n"
+" for(i=0; xxx_allports[i].port!=0; i++) {\n"
+" if(xxx_allports[i].used || xxx_allports[i].port != port)\n"
+" continue;\n"
+msgstr ""
+" port = bus_get_resource_start(dev, SYS_RES_IOPORT, 0 /*rid*/);\n"
+" if(port !=0 ) {\n"
+" for(i=0; xxx_allports[i].port!=0; i++) {\n"
+" if(xxx_allports[i].used || xxx_allports[i].port != port)\n"
+" continue;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:695
+#, no-wrap
+msgid ""
+" /* found it */\n"
+" xxx_allports[i].used = 1;\n"
+" /* do probe on a known port */\n"
+" return xxx_really_probe(dev, port);\n"
+" }\n"
+" return ENXIO; /* port is unknown or already used */\n"
+" }\n"
+msgstr ""
+" /* found it */\n"
+" xxx_allports[i].used = 1;\n"
+" /* do probe on a known port */\n"
+" return xxx_really_probe(dev, port);\n"
+" }\n"
+" return ENXIO; /* port is unknown or already used */\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:700
+#, no-wrap
+msgid ""
+" /* we get here only if we need to guess the port */\n"
+" for(i=0; xxx_allports[i].port!=0; i++) {\n"
+" if(xxx_allports[i].used)\n"
+" continue;\n"
+msgstr ""
+" /* we get here only if we need to guess the port */\n"
+" for(i=0; xxx_allports[i].port!=0; i++) {\n"
+" if(xxx_allports[i].used)\n"
+" continue;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:705
+#, no-wrap
+msgid ""
+" /* mark as used - even if we find nothing at this port\n"
+" * at least we won't probe it in future\n"
+" */\n"
+" xxx_allports[i].used = 1;\n"
+msgstr ""
+" /* mark as used - even if we find nothing at this port\n"
+" * at least we won't probe it in future\n"
+" */\n"
+" xxx_allports[i].used = 1;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:712
+#, no-wrap
+msgid ""
+" error = xxx_really_probe(dev, xxx_allports[i].port);\n"
+" if(error == 0) /* found a device at that port */\n"
+" return 0;\n"
+" }\n"
+" /* probed all possible addresses, none worked */\n"
+" return ENXIO;\n"
+msgstr ""
+" error = xxx_really_probe(dev, xxx_allports[i].port);\n"
+" if(error == 0) /* found a device at that port */\n"
+" return 0;\n"
+" }\n"
+" /* probed all possible addresses, none worked */\n"
+" return ENXIO;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:715
+msgid ""
+"Of course, normally the driver's `identify()` routine should be used for "
+"such things. But there may be one valid reason why it may be better to be "
+"done in `probe()`: if this probe would drive some other sensitive device "
+"crazy. The probe routines are ordered with consideration of the `sensitive` "
+"flag: the sensitive devices get probed first and the rest of the devices "
+"later. But the `identify()` routines are called before any probes, so they "
+"show no respect to the sensitive devices and may upset them."
+msgstr ""
+"Конечно, обычно для таких вещей следует использовать процедуру `identify()` "
+"драйвера. Однако может быть одна веская причина, почему лучше сделать это в "
+"`probe()`: если этот обнаружение может привести к сбою другого "
+"чувствительного устройства. Процедуры обнаружения упорядочены с учетом флага "
+"`sensitive`: чувствительные устройства проверяются первыми, а остальные "
+"устройства — позже. Но процедуры `identify()` вызываются до любого "
+"обнаружения, поэтому они не учитывают чувствительные устройства и могут "
+"вызвать их сбой."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:717
+msgid ""
+"Now, after we got the starting port we need to set the port count (except "
+"for PnP devices) because the kernel does not have this information in the "
+"configuration file."
+msgstr ""
+"Вот, после того как мы получили начальный порт, необходимо установить "
+"количество портов (за исключением устройств PnP), так как в файле "
+"конфигурации ядра эта информация отсутствует."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:725
+#, no-wrap
+msgid ""
+" if(pnperror /* only for non-PnP devices */\n"
+" && bus_set_resource(dev, SYS_RES_IOPORT, 0, sc->port0,\n"
+" XXX_PORT_COUNT)<0)\n"
+" return ENXIO;\n"
+msgstr ""
+" if(pnperror /* only for non-PnP devices */\n"
+" && bus_set_resource(dev, SYS_RES_IOPORT, 0, sc->port0,\n"
+" XXX_PORT_COUNT)<0)\n"
+" return ENXIO;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:728
+msgid ""
+"Finally allocate and activate a piece of port address space (special values "
+"of start and end mean \"use those we set by ``bus_set_resource()``\"):"
+msgstr ""
+"Наконец, выделите и активируйте часть адресного пространства порта "
+"(специальные значения start и end означают \"используйте те, что мы "
+"установили через ``bus_set_resource()``\"):"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:736
+#, no-wrap
+msgid ""
+" sc->port0_rid = 0;\n"
+" sc->port0_r = bus_alloc_resource(dev, SYS_RES_IOPORT,\n"
+" &sc->port0_rid,\n"
+" /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);\n"
+msgstr ""
+" sc->port0_rid = 0;\n"
+" sc->port0_r = bus_alloc_resource(dev, SYS_RES_IOPORT,\n"
+" &sc->port0_rid,\n"
+" /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:739
+#, no-wrap
+msgid ""
+" if(sc->port0_r == NULL)\n"
+" return ENXIO;\n"
+msgstr ""
+" if(sc->port0_r == NULL)\n"
+" return ENXIO;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:742
+msgid ""
+"Now having access to the port-mapped registers we can poke the device in "
+"some way and check if it reacts like it is expected to. If it does not then "
+"there is probably some other device or no device at all at this address."
+msgstr ""
+"Теперь, имея доступ к регистрам с отображением на порты, мы можем каким-либо "
+"образом взаимодействовать с устройством и проверить, реагирует ли оно так, "
+"как ожидается. Если этого не происходит, вероятно, по этому адресу находится "
+"другое устройство или его там нет вовсе."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:744
+msgid ""
+"Normally drivers do not set up the interrupt handlers until the attach "
+"routine. Instead they do probes in the polling mode using the `DELAY()` "
+"function for timeout. The probe routine must never hang forever, all the "
+"waits for the device must be done with timeouts. If the device does not "
+"respond within the time it is probably broken or misconfigured and the "
+"driver must return error. When determining the timeout interval give the "
+"device some extra time to be on the safe side: although `DELAY()` is "
+"supposed to delay for the same amount of time on any machine it has some "
+"margin of error, depending on the exact CPU."
+msgstr ""
+"Обычно драйверы не настраивают обработчики прерываний до вызова процедуры "
+"присоединения. Вместо этого они выполняют проверки в режиме опроса, "
+"используя функцию `DELAY()` для таймаута. Процедура проверки никогда не "
+"должна зависать навсегда, все ожидания ответа от устройства должны "
+"выполняться с таймаутами. Если устройство не отвечает в течение заданного "
+"времени, вероятно, оно неисправно или неправильно настроено, и драйвер "
+"должен вернуть ошибку. При определении интервала таймаута следует давать "
+"устройству дополнительное время для надежности: хотя предполагается, что "
+"`DELAY()` задерживает выполнение на одинаковое время на любой машине, "
+"существует некоторая погрешность, зависящая от конкретного процессора."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:746
+msgid ""
+"If the probe routine really wants to check that the interrupts really work "
+"it may configure and probe the interrupts too. But that is not recommended."
+msgstr ""
+"Если процедура проверки действительно хочет убедиться, что прерывания "
+"работают, она может также настроить и провести обнаружение прерываний. "
+"Однако это не рекомендуется."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:753
+#, no-wrap
+msgid ""
+" /* implemented in some very device-specific way */\n"
+" if(error = xxx_probe_ports(sc))\n"
+" goto bad; /* will deallocate the resources before returning */\n"
+msgstr ""
+" /* implemented in some very device-specific way */\n"
+" if(error = xxx_probe_ports(sc))\n"
+" goto bad; /* will deallocate the resources before returning */\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:756
+msgid ""
+"The function `xxx_probe_ports()` may also set the device description "
+"depending on the exact model of device it discovers. But if there is only "
+"one supported device model this can be as well done in a hardcoded way. Of "
+"course, for the PnP devices the PnP support sets the description from the "
+"table automatically."
+msgstr ""
+"Функция `xxx_probe_ports()` также может устанавливать описание устройства в "
+"зависимости от конкретной модели обнаруженного устройства. Но если "
+"поддерживается только одна модель устройства, это можно сделать и жёстко "
+"заданным способом. Конечно, для PnP-устройств поддержка PnP автоматически "
+"устанавливает описание из таблицы."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:761
+#, no-wrap
+msgid ""
+" if(pnperror)\n"
+" device_set_desc(dev, \"Our device model 1234\");\n"
+msgstr ""
+" if(pnperror)\n"
+" device_set_desc(dev, \"Our device model 1234\");\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:764
+msgid ""
+"Then the probe routine should either discover the ranges of all the "
+"resources by reading the device configuration registers or make sure that "
+"they were set explicitly by the user. We will consider it with an example of "
+"on-board memory. The probe routine should be as non-intrusive as possible, "
+"so allocation and check of functionality of the rest of resources (besides "
+"the ports) would be better left to the attach routine."
+msgstr ""
+"Затем процедура обнаружения должна либо определить диапазоны всех ресурсов, "
+"читая регистры конфигурации устройства, либо убедиться, что они были явно "
+"заданы пользователем. Мы рассмотрим это на примере встроенной памяти. "
+"Процедура обнаружения должна быть как можно менее навязчивой, поэтому "
+"выделение и проверку функциональности остальных ресурсов (кроме портов) "
+"лучше оставить для процедуры присоединения."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:766
+msgid ""
+"The memory address may be specified in the kernel configuration file or on "
+"some devices it may be pre-configured in non-volatile configuration "
+"registers. If both sources are available and different, which one should be "
+"used? Probably if the user bothered to set the address explicitly in the "
+"kernel configuration file they know what they are doing and this one should "
+"take precedence. An example of implementation could be:"
+msgstr ""
+"Адрес памяти может быть указан в конфигурационном файле ядра, а на некоторых "
+"устройствах он может быть предварительно настроен в энергонезависимых "
+"конфигурационных регистрах. Если доступны оба источника, и они различаются, "
+"какой из них следует использовать? Вероятно, если пользователь явно указал "
+"адрес в конфигурационном файле ядра, он знает, что делает, и этот адрес "
+"должен иметь приоритет. Пример реализации может выглядеть так:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:774
+#, no-wrap
+msgid ""
+" /* try to find out the config address first */\n"
+" sc->mem0_p = bus_get_resource_start(dev, SYS_RES_MEMORY, 0 /*rid*/);\n"
+" if(sc->mem0_p == 0) { /* nope, not specified by user */\n"
+" sc->mem0_p = xxx_read_mem0_from_device_config(sc);\n"
+msgstr ""
+" /* try to find out the config address first */\n"
+" sc->mem0_p = bus_get_resource_start(dev, SYS_RES_MEMORY, 0 /*rid*/);\n"
+" if(sc->mem0_p == 0) { /* nope, not specified by user */\n"
+" sc->mem0_p = xxx_read_mem0_from_device_config(sc);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:782
+#, no-wrap
+msgid ""
+" if(sc->mem0_p == 0)\n"
+" /* can't get it from device config registers either */\n"
+" goto bad;\n"
+" } else {\n"
+" if(xxx_set_mem0_address_on_device(sc) < 0)\n"
+" goto bad; /* device does not support that address */\n"
+" }\n"
+msgstr ""
+" if(sc->mem0_p == 0)\n"
+" /* can't get it from device config registers either */\n"
+" goto bad;\n"
+" } else {\n"
+" if(xxx_set_mem0_address_on_device(sc) < 0)\n"
+" goto bad; /* device does not support that address */\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:794
+#, no-wrap
+msgid ""
+" /* just like the port, set the memory size,\n"
+" * for some devices the memory size would not be constant\n"
+" * but should be read from the device configuration registers instead\n"
+" * to accommodate different models of devices. Another option would\n"
+" * be to let the user set the memory size as \"msize\" configuration\n"
+" * resource which will be automatically handled by the ISA bus.\n"
+" */\n"
+" if(pnperror) { /* only for non-PnP devices */\n"
+" sc->mem0_size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0 /*rid*/);\n"
+" if(sc->mem0_size == 0) /* not specified by user */\n"
+" sc->mem0_size = xxx_read_mem0_size_from_device_config(sc);\n"
+msgstr ""
+" /* just like the port, set the memory size,\n"
+" * for some devices the memory size would not be constant\n"
+" * but should be read from the device configuration registers instead\n"
+" * to accommodate different models of devices. Another option would\n"
+" * be to let the user set the memory size as \"msize\" configuration\n"
+" * resource which will be automatically handled by the ISA bus.\n"
+" */\n"
+" if(pnperror) { /* only for non-PnP devices */\n"
+" sc->mem0_size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0 /*rid*/);\n"
+" if(sc->mem0_size == 0) /* not specified by user */\n"
+" sc->mem0_size = xxx_read_mem0_size_from_device_config(sc);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:803
+#, no-wrap
+msgid ""
+" if(sc->mem0_size == 0) {\n"
+" /* suppose this is a very old model of device without\n"
+" * auto-configuration features and the user gave no preference,\n"
+" * so assume the minimalistic case\n"
+" * (of course, the real value will vary with the driver)\n"
+" */\n"
+" sc->mem0_size = 8*1024;\n"
+" }\n"
+msgstr ""
+" if(sc->mem0_size == 0) {\n"
+" /* suppose this is a very old model of device without\n"
+" * auto-configuration features and the user gave no preference,\n"
+" * so assume the minimalistic case\n"
+" * (of course, the real value will vary with the driver)\n"
+" */\n"
+" sc->mem0_size = 8*1024;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:806
+#, no-wrap
+msgid ""
+" if(xxx_set_mem0_size_on_device(sc) < 0)\n"
+" goto bad; /* device does not support that size */\n"
+msgstr ""
+" if(xxx_set_mem0_size_on_device(sc) < 0)\n"
+" goto bad; /* device does not support that size */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:813
+#, no-wrap
+msgid ""
+" if(bus_set_resource(dev, SYS_RES_MEMORY, /*rid*/0,\n"
+" sc->mem0_p, sc->mem0_size)<0)\n"
+" goto bad;\n"
+" } else {\n"
+" sc->mem0_size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0 /*rid*/);\n"
+" }\n"
+msgstr ""
+" if(bus_set_resource(dev, SYS_RES_MEMORY, /*rid*/0,\n"
+" sc->mem0_p, sc->mem0_size)<0)\n"
+" goto bad;\n"
+" } else {\n"
+" sc->mem0_size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0 /*rid*/);\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:816
+msgid "Resources for IRQ and DRQ are easy to check by analogy."
+msgstr "Ресурсы для IRQ и DRQ легко проверить по аналогии."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:818
+msgid "If all went well then release all the resources and return success."
+msgstr ""
+"Если всё прошло успешно, то освободите все ресурсы и верните успешный статус."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:823
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1086
+#, no-wrap
+msgid ""
+" xxx_free_resources(sc);\n"
+" return 0;\n"
+msgstr ""
+" xxx_free_resources(sc);\n"
+" return 0;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:826
+msgid ""
+"Finally, handle the troublesome situations. All the resources should be "
+"deallocated before returning. We make use of the fact that before the "
+"structure softc is passed to us it gets zeroed out, so we can find out if "
+"some resource was allocated: then its descriptor is non-zero."
+msgstr ""
+"Наконец, обработайте проблемные ситуации. Все ресурсы должны быть "
+"освобождены перед возвратом. Мы используем тот факт, что перед передачей нам "
+"структуры `softc` она обнуляется, поэтому мы можем определить, был ли "
+"выделен какой-либо ресурс: если его дескриптор не равен нулю."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:830
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1047
+#, no-wrap
+msgid " bad:\n"
+msgstr " bad:\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:836
+#, no-wrap
+msgid ""
+" xxx_free_resources(sc);\n"
+" if(error)\n"
+" return error;\n"
+" else /* exact error is unknown */\n"
+" return ENXIO;\n"
+msgstr ""
+" xxx_free_resources(sc);\n"
+" if(error)\n"
+" return error;\n"
+" else /* exact error is unknown */\n"
+" return ENXIO;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:839
+msgid ""
+"That would be all for the probe routine. Freeing of resources is done from "
+"multiple places, so it is moved to a function which may look like:"
+msgstr ""
+"Вот и всё для процедуры обнаружения. Освобождение ресурсов выполняется из "
+"нескольких мест, поэтому оно вынесено в функцию, которая может выглядеть так:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:847
+#, no-wrap
+msgid ""
+"static void\n"
+" xxx_free_resources(sc)\n"
+" struct xxx_softc *sc;\n"
+" {\n"
+" /* check every resource and free if not zero */\n"
+msgstr ""
+"static void\n"
+" xxx_free_resources(sc)\n"
+" struct xxx_softc *sc;\n"
+" {\n"
+" /* check every resource and free if not zero */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:855
+#, no-wrap
+msgid ""
+" /* interrupt handler */\n"
+" if(sc->intr_r) {\n"
+" bus_teardown_intr(sc->dev, sc->intr_r, sc->intr_cookie);\n"
+" bus_release_resource(sc->dev, SYS_RES_IRQ, sc->intr_rid,\n"
+" sc->intr_r);\n"
+" sc->intr_r = 0;\n"
+" }\n"
+msgstr ""
+" /* interrupt handler */\n"
+" if(sc->intr_r) {\n"
+" bus_teardown_intr(sc->dev, sc->intr_r, sc->intr_cookie);\n"
+" bus_release_resource(sc->dev, SYS_RES_IRQ, sc->intr_rid,\n"
+" sc->intr_r);\n"
+" sc->intr_r = 0;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:870
+#, no-wrap
+msgid ""
+" /* all kinds of memory maps we could have allocated */\n"
+" if(sc->data_p) {\n"
+" bus_dmamap_unload(sc->data_tag, sc->data_map);\n"
+" sc->data_p = 0;\n"
+" }\n"
+" if(sc->data) { /* sc->data_map may be legitimately equal to 0 */\n"
+" /* the map will also be freed */\n"
+" bus_dmamem_free(sc->data_tag, sc->data, sc->data_map);\n"
+" sc->data = 0;\n"
+" }\n"
+" if(sc->data_tag) {\n"
+" bus_dma_tag_destroy(sc->data_tag);\n"
+" sc->data_tag = 0;\n"
+" }\n"
+msgstr ""
+" /* all kinds of memory maps we could have allocated */\n"
+" if(sc->data_p) {\n"
+" bus_dmamap_unload(sc->data_tag, sc->data_map);\n"
+" sc->data_p = 0;\n"
+" }\n"
+" if(sc->data) { /* sc->data_map may be legitimately equal to 0 */\n"
+" /* the map will also be freed */\n"
+" bus_dmamem_free(sc->data_tag, sc->data, sc->data_map);\n"
+" sc->data = 0;\n"
+" }\n"
+" if(sc->data_tag) {\n"
+" bus_dma_tag_destroy(sc->data_tag);\n"
+" sc->data_tag = 0;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:872
+#, no-wrap
+msgid " ... free other maps and tags if we have them ...\n"
+msgstr " ... free other maps and tags if we have them ...\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:877
+#, no-wrap
+msgid ""
+" if(sc->parent_tag) {\n"
+" bus_dma_tag_destroy(sc->parent_tag);\n"
+" sc->parent_tag = 0;\n"
+" }\n"
+msgstr ""
+" if(sc->parent_tag) {\n"
+" bus_dma_tag_destroy(sc->parent_tag);\n"
+" sc->parent_tag = 0;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:891
+#, no-wrap
+msgid ""
+" /* release all the bus resources */\n"
+" if(sc->mem0_r) {\n"
+" bus_release_resource(sc->dev, SYS_RES_MEMORY, sc->mem0_rid,\n"
+" sc->mem0_r);\n"
+" sc->mem0_r = 0;\n"
+" }\n"
+" ...\n"
+" if(sc->port0_r) {\n"
+" bus_release_resource(sc->dev, SYS_RES_IOPORT, sc->port0_rid,\n"
+" sc->port0_r);\n"
+" sc->port0_r = 0;\n"
+" }\n"
+" }\n"
+msgstr ""
+" /* release all the bus resources */\n"
+" if(sc->mem0_r) {\n"
+" bus_release_resource(sc->dev, SYS_RES_MEMORY, sc->mem0_rid,\n"
+" sc->mem0_r);\n"
+" sc->mem0_r = 0;\n"
+" }\n"
+" ...\n"
+" if(sc->port0_r) {\n"
+" bus_release_resource(sc->dev, SYS_RES_IOPORT, sc->port0_rid,\n"
+" sc->port0_r);\n"
+" sc->port0_r = 0;\n"
+" }\n"
+" }\n"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:894
+#, no-wrap
+msgid "xxx_isa_attach"
+msgstr "xxx_isa_attach"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:897
+msgid ""
+"The attach routine actually connects the driver to the system if the probe "
+"routine returned success and the system had chosen to attach that driver. If "
+"the probe routine returned 0 then the attach routine may expect to receive "
+"the device structure softc intact, as it was set by the probe routine. Also "
+"if the probe routine returns 0 it may expect that the attach routine for "
+"this device shall be called at some point in the future. If the probe "
+"routine returns a negative value then the driver may make none of these "
+"assumptions."
+msgstr ""
+"Процедура присоединения фактически подключает драйвер к системе, если "
+"процедура обнаружения вернула успех, и система решила подключить этот "
+"драйвер. Если процедура обнаружения вернула 0, то процедура присоединения "
+"может ожидать, что получит структуру устройства `softc` в неизменном виде, "
+"как она была установлена процедурой обнаружения. Также, если обнаружение "
+"возвращает 0, она можно ожидать, что процедура присоединения для этого "
+"устройства будет вызвана в какой-то момент в будущем. Если процедура "
+"обнаружения возвращает отрицательное значение, то драйвер не может делать "
+"никаких из этих предположений."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:899
+msgid ""
+"The attach routine returns 0 if it completed successfully or error code "
+"otherwise."
+msgstr ""
+"Процедура присоединение возвращает 0 при успешном завершении или код ошибки "
+"в противном случае."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:901
+msgid ""
+"The attach routine starts just like the probe routine, with getting some "
+"frequently used data into more accessible variables."
+msgstr ""
+"Процедура присоединения начинается так же, как и процедура обнаружения, с "
+"помещения часто используемых данных в более доступные переменные."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:907
+#, no-wrap
+msgid ""
+" struct xxx_softc *sc = device_get_softc(dev);\n"
+" int unit = device_get_unit(dev);\n"
+" int error = 0;\n"
+msgstr ""
+" struct xxx_softc *sc = device_get_softc(dev);\n"
+" int unit = device_get_unit(dev);\n"
+" int error = 0;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:910
+msgid ""
+"Then allocate and activate all the necessary resources. As normally the port "
+"range will be released before returning from probe, it has to be allocated "
+"again. We expect that the probe routine had properly set all the resource "
+"ranges, as well as saved them in the structure softc. If the probe routine "
+"had left some resource allocated then it does not need to be allocated again "
+"(which would be considered an error)."
+msgstr ""
+"Затем выделите и активируйте все необходимые ресурсы. Обычно диапазон портов "
+"освобождается перед возвратом из обнаружения, поэтому его необходимо "
+"выделить снова. Мы предполагаем, что процедура обнаружения корректно "
+"установила все диапазоны ресурсов, а также сохранила их в структуре softc. "
+"Если процедура обнаружения оставила некоторые ресурсы выделенными, то их не "
+"нужно выделять снова (это будет считаться ошибкой)."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:916
+#, no-wrap
+msgid ""
+" sc->port0_rid = 0;\n"
+" sc->port0_r = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port0_rid,\n"
+" /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);\n"
+msgstr ""
+" sc->port0_rid = 0;\n"
+" sc->port0_r = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port0_rid,\n"
+" /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:919
+#, no-wrap
+msgid ""
+" if(sc->port0_r == NULL)\n"
+" return ENXIO;\n"
+msgstr ""
+" if(sc->port0_r == NULL)\n"
+" return ENXIO;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:924
+#, no-wrap
+msgid ""
+" /* on-board memory */\n"
+" sc->mem0_rid = 0;\n"
+" sc->mem0_r = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem0_rid,\n"
+" /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);\n"
+msgstr ""
+" /* on-board memory */\n"
+" sc->mem0_rid = 0;\n"
+" sc->mem0_r = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem0_rid,\n"
+" /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:927
+#, no-wrap
+msgid ""
+" if(sc->mem0_r == NULL)\n"
+" goto bad;\n"
+msgstr ""
+" if(sc->mem0_r == NULL)\n"
+" goto bad;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:930
+#, no-wrap
+msgid ""
+" /* get its virtual address */\n"
+" sc->mem0_v = rman_get_virtual(sc->mem0_r);\n"
+msgstr ""
+" /* get its virtual address */\n"
+" sc->mem0_v = rman_get_virtual(sc->mem0_r);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:933
+msgid ""
+"The DMA request channel (DRQ) is allocated likewise. To initialize it use "
+"functions of the `isa_dma*()` family. For example:"
+msgstr ""
+"Канал запроса DMA (DRQ) выделяется аналогично. Для его инициализации "
+"используйте функции семейства `isa_dma*()`. Например:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:935
+msgid "`isa_dmacascade(sc->drq0);`"
+msgstr "`isa_dmacascade(sc->drq0);`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:937
+msgid ""
+"The interrupt request line (IRQ) is a bit special. Besides allocation the "
+"driver's interrupt handler should be associated with it. Historically in the "
+"old ISA drivers the argument passed by the system to the interrupt handler "
+"was the device unit number. But in modern drivers the convention suggests "
+"passing the pointer to structure softc. The important reason is that when "
+"the structures softc are allocated dynamically then getting the unit number "
+"from softc is easy while getting softc from the unit number is difficult. "
+"Also this convention makes the drivers for different buses look more uniform "
+"and allows them to share the code: each bus gets its own probe, attach, "
+"detach and other bus-specific routines while the bulk of the driver code may "
+"be shared among them."
+msgstr ""
+"Строка запроса прерывания (IRQ) является особенной. Помимо выделения, "
+"обработчик прерывания драйвера должен быть связан с ней. Исторически в "
+"старых драйверах ISA аргумент, передаваемый системой обработчику прерывания, "
+"был номером устройства. Однако в современных драйверах принято передавать "
+"указатель на структуру `softc`. Важная причина этого заключается в том, что "
+"когда структуры `softc` выделяются динамически, получение номера устройства "
+"из `softc` является простым, в то время как получение `softc` из номера "
+"устройства затруднительно. Кроме того, такое соглашение делает драйверы для "
+"различных шин более однородными и позволяет им совместно использовать код: "
+"каждая шина получает свои собственные процедуры обнаружения, присоединения, "
+"отсоединения и другие специфичные для шины функции, в то время как основная "
+"часть кода драйвера может быть общей для них."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:944
+#, no-wrap
+msgid ""
+" sc->intr_rid = 0;\n"
+" sc->intr_r = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->intr_rid,\n"
+" /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);\n"
+msgstr ""
+" sc->intr_rid = 0;\n"
+" sc->intr_r = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->intr_rid,\n"
+" /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:947
+#, no-wrap
+msgid ""
+" if(sc->intr_r == NULL)\n"
+" goto bad;\n"
+msgstr ""
+" if(sc->intr_r == NULL)\n"
+" goto bad;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:956
+#, no-wrap
+msgid ""
+" /*\n"
+" * XXX_INTR_TYPE is supposed to be defined depending on the type of\n"
+" * the driver, for example as INTR_TYPE_CAM for a CAM driver\n"
+" */\n"
+" error = bus_setup_intr(dev, sc->intr_r, XXX_INTR_TYPE,\n"
+" (driver_intr_t *) xxx_intr, (void *) sc, &sc->intr_cookie);\n"
+" if(error)\n"
+" goto bad;\n"
+msgstr ""
+" /*\n"
+" * XXX_INTR_TYPE is supposed to be defined depending on the type of\n"
+" * the driver, for example as INTR_TYPE_CAM for a CAM driver\n"
+" */\n"
+" error = bus_setup_intr(dev, sc->intr_r, XXX_INTR_TYPE,\n"
+" (driver_intr_t *) xxx_intr, (void *) sc, &sc->intr_cookie);\n"
+" if(error)\n"
+" goto bad;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:959
+msgid ""
+"If the device needs to make DMA to the main memory then this memory should "
+"be allocated like described before:"
+msgstr ""
+"Если устройству необходимо выполнять DMA в основную память, то эта память "
+"должна быть выделена, как описано ранее:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:971
+#, no-wrap
+msgid ""
+" error=bus_dma_tag_create(NULL, /*alignment*/ 4,\n"
+" /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR_24BIT,\n"
+" /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL,\n"
+" /*maxsize*/ BUS_SPACE_MAXSIZE_24BIT,\n"
+" /*nsegments*/ BUS_SPACE_UNRESTRICTED,\n"
+" /*maxsegsz*/ BUS_SPACE_MAXSIZE_24BIT, /*flags*/ 0,\n"
+" &sc->parent_tag);\n"
+" if(error)\n"
+" goto bad;\n"
+msgstr ""
+" error=bus_dma_tag_create(NULL, /*alignment*/ 4,\n"
+" /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR_24BIT,\n"
+" /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL,\n"
+" /*maxsize*/ BUS_SPACE_MAXSIZE_24BIT,\n"
+" /*nsegments*/ BUS_SPACE_UNRESTRICTED,\n"
+" /*maxsegsz*/ BUS_SPACE_MAXSIZE_24BIT, /*flags*/ 0,\n"
+" &sc->parent_tag);\n"
+" if(error)\n"
+" goto bad;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:988
+#, no-wrap
+msgid ""
+" /* many things get inherited from the parent tag\n"
+" * sc->data is supposed to point to the structure with the shared data,\n"
+" * for example for a ring buffer it could be:\n"
+" * struct {\n"
+" * u_short rd_pos;\n"
+" * u_short wr_pos;\n"
+" * char bf[XXX_RING_BUFFER_SIZE]\n"
+" * } *data;\n"
+" */\n"
+" error=bus_dma_tag_create(sc->parent_tag, 1,\n"
+" 0, BUS_SPACE_MAXADDR, 0, /*filter*/ NULL, /*filterarg*/ NULL,\n"
+" /*maxsize*/ sizeof(* sc->data), /*nsegments*/ 1,\n"
+" /*maxsegsz*/ sizeof(* sc->data), /*flags*/ 0,\n"
+" &sc->data_tag);\n"
+" if(error)\n"
+" goto bad;\n"
+msgstr ""
+" /* many things get inherited from the parent tag\n"
+" * sc->data is supposed to point to the structure with the shared data,\n"
+" * for example for a ring buffer it could be:\n"
+" * struct {\n"
+" * u_short rd_pos;\n"
+" * u_short wr_pos;\n"
+" * char bf[XXX_RING_BUFFER_SIZE]\n"
+" * } *data;\n"
+" */\n"
+" error=bus_dma_tag_create(sc->parent_tag, 1,\n"
+" 0, BUS_SPACE_MAXADDR, 0, /*filter*/ NULL, /*filterarg*/ NULL,\n"
+" /*maxsize*/ sizeof(* sc->data), /*nsegments*/ 1,\n"
+" /*maxsegsz*/ sizeof(* sc->data), /*flags*/ 0,\n"
+" &sc->data_tag);\n"
+" if(error)\n"
+" goto bad;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:993
+#, no-wrap
+msgid ""
+" error = bus_dmamem_alloc(sc->data_tag, &sc->data, /* flags*/ 0,\n"
+" &sc->data_map);\n"
+" if(error)\n"
+" goto bad;\n"
+msgstr ""
+" error = bus_dmamem_alloc(sc->data_tag, &sc->data, /* flags*/ 0,\n"
+" &sc->data_map);\n"
+" if(error)\n"
+" goto bad;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1009
+#, no-wrap
+msgid ""
+" /* xxx_alloc_callback() just saves the physical address at\n"
+" * the pointer passed as its argument, in this case &sc->data_p.\n"
+" * See details in the section on bus memory mapping.\n"
+" * It can be implemented like:\n"
+" *\n"
+" * static void\n"
+" * xxx_alloc_callback(void *arg, bus_dma_segment_t *seg,\n"
+" * int nseg, int error)\n"
+" * {\n"
+" * *(bus_addr_t *)arg = seg[0].ds_addr;\n"
+" * }\n"
+" */\n"
+" bus_dmamap_load(sc->data_tag, sc->data_map, (void *)sc->data,\n"
+" sizeof (* sc->data), xxx_alloc_callback, (void *) &sc->data_p,\n"
+" /*flags*/0);\n"
+msgstr ""
+" /* xxx_alloc_callback() just saves the physical address at\n"
+" * the pointer passed as its argument, in this case &sc->data_p.\n"
+" * See details in the section on bus memory mapping.\n"
+" * It can be implemented like:\n"
+" *\n"
+" * static void\n"
+" * xxx_alloc_callback(void *arg, bus_dma_segment_t *seg,\n"
+" * int nseg, int error)\n"
+" * {\n"
+" * *(bus_addr_t *)arg = seg[0].ds_addr;\n"
+" * }\n"
+" */\n"
+" bus_dmamap_load(sc->data_tag, sc->data_map, (void *)sc->data,\n"
+" sizeof (* sc->data), xxx_alloc_callback, (void *) &sc->data_p,\n"
+" /*flags*/0);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1012
+msgid ""
+"After all the necessary resources are allocated the device should be "
+"initialized. The initialization may include testing that all the expected "
+"features are functional."
+msgstr ""
+"После выделения всех необходимых ресурсов устройство должно быть "
+"инициализировано. Инициализация может включать проверку работоспособности "
+"всех ожидаемых функций."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1017
+#, no-wrap
+msgid ""
+" if(xxx_initialize(sc) < 0)\n"
+" goto bad;\n"
+msgstr ""
+" if(xxx_initialize(sc) < 0)\n"
+" goto bad;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1020
+msgid ""
+"The bus subsystem will automatically print on the console the device "
+"description set by probe. But if the driver wants to print some extra "
+"information about the device it may do so, for example:"
+msgstr ""
+"Подсистема шины автоматически выводит на консоль описание устройства, "
+"установленное при проверке. Однако, если драйвер хочет вывести "
+"дополнительную информацию об устройстве, он может это сделать, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1025
+#, no-wrap
+msgid " device_printf(dev, \"has on-card FIFO buffer of %d bytes\\n\", sc->fifosize);\n"
+msgstr " device_printf(dev, \"has on-card FIFO buffer of %d bytes\\n\", sc->fifosize);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1028
+msgid ""
+"If the initialization routine experiences any problems then printing "
+"messages about them before returning error is also recommended."
+msgstr ""
+"Если в процессе выполнения инициализации возникают какие-либо проблемы, "
+"рекомендуется выводить сообщения об этих проблемах перед возвратом ошибки."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1030
+msgid ""
+"The final step of the attach routine is attaching the device to its "
+"functional subsystem in the kernel. The exact way to do it depends on the "
+"type of the driver: a character device, a block device, a network device, a "
+"CAM SCSI bus device and so on."
+msgstr ""
+"Последним шагом процедуры присоединения является подключение устройства к "
+"его функциональной подсистеме в ядре. Конкретный способ зависит от типа "
+"драйвера: символьное устройство, блочное устройство, сетевое устройство, "
+"устройство шины CAM SCSI и так далее."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1032
+msgid "If all went well then return success."
+msgstr "Если всё прошло успешно, вернуть успех."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1038
+#, no-wrap
+msgid ""
+" error = xxx_attach_subsystem(sc);\n"
+" if(error)\n"
+" goto bad;\n"
+msgstr ""
+" error = xxx_attach_subsystem(sc);\n"
+" if(error)\n"
+" goto bad;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1040
+#, no-wrap
+msgid " return 0;\n"
+msgstr " return 0;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1043
+msgid ""
+"Finally, handle the troublesome situations. All the resources should be "
+"deallocated before returning an error. We make use of the fact that before "
+"the structure softc is passed to us it gets zeroed out, so we can find out "
+"if some resource was allocated: then its descriptor is non-zero."
+msgstr ""
+"Наконец, обработаем проблемные ситуации. Все ресурсы должны быть освобождены "
+"перед возвратом ошибки. Мы используем тот факт, что перед передачей "
+"структуры `softc` она обнуляется, поэтому мы можем определить, был ли "
+"выделен какой-либо ресурс: если его дескриптор ненулевой."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1053
+#, no-wrap
+msgid ""
+" xxx_free_resources(sc);\n"
+" if(error)\n"
+" return error;\n"
+" else /* exact error is unknown */\n"
+" return ENXIO;\n"
+msgstr ""
+" xxx_free_resources(sc);\n"
+" if(error)\n"
+" return error;\n"
+" else /* exact error is unknown */\n"
+" return ENXIO;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1056
+msgid "That would be all for the attach routine."
+msgstr "Вот и всё для процедуры присоединения."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1058
+#, no-wrap
+msgid "xxx_isa_detach"
+msgstr "xxx_isa_detach"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1061
+msgid ""
+"If this function is present in the driver and the driver is compiled as a "
+"loadable module then the driver gets the ability to be unloaded. This is an "
+"important feature if the hardware supports hot plug. But the ISA bus does "
+"not support hot plug, so this feature is not particularly important for the "
+"ISA devices. The ability to unload a driver may be useful when debugging it, "
+"but in many cases installation of the new version of the driver would be "
+"required only after the old version somehow wedges the system and a reboot "
+"will be needed anyway, so the efforts spent on writing the detach routine "
+"may not be worth it. Another argument that unloading would allow upgrading "
+"the drivers on a production machine seems to be mostly theoretical. "
+"Installing a new version of a driver is a dangerous operation which should "
+"never be performed on a production machine (and which is not permitted when "
+"the system is running in secure mode). Still, the detach routine may be "
+"provided for the sake of completeness."
+msgstr ""
+"Если эта функция присутствует в драйвере и драйвер скомпилирован как "
+"загружаемый модуль, то драйвер получает возможность быть выгруженным. Это "
+"важная функция, если оборудование поддерживает горячее подключение. Однако "
+"шина ISA не поддерживает горячее подключение, поэтому эта функция не "
+"особенно важна для устройств ISA. Возможность выгрузки драйвера может быть "
+"полезна при его отладке, но во многих случаях установка новой версии "
+"драйвера потребуется только после того, как старая версия каким-то образом "
+"заблокирует систему и перезагрузка все равно будет необходима, поэтому "
+"усилия, затраченные на написание процедуры отсоединения, могут не окупиться. "
+"Другой аргумент, что выгрузка позволит обновлять драйверы на рабочей машине, "
+"кажется в основном теоретическим. Установка новой версии драйвера — это "
+"опасная операция, которую никогда не следует выполнять на рабочей машине (и "
+"которая не разрешена, когда система работает в безопасном режиме). Тем не "
+"менее, процедура отсоединения может быть предоставлена для полноты."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1063
+msgid ""
+"The detach routine returns 0 if the driver was successfully detached or the "
+"error code otherwise."
+msgstr ""
+"Процедура отсоединения возвращает 0, если драйвер был успешно отсоединён, "
+"или код ошибки в противном случае."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1065
+msgid ""
+"The logic of detach is a mirror of the attach. The first thing to do is to "
+"detach the driver from its kernel subsystem. If the device is currently open "
+"then the driver has two choices: refuse to be detached or forcibly close and "
+"proceed with detach. The choice used depends on the ability of the "
+"particular kernel subsystem to do a forced close and on the preferences of "
+"the driver's author. Generally the forced close seems to be the preferred "
+"alternative."
+msgstr ""
+"Логика отсоединения является зеркальной по отношению к присоединению. "
+"Первое, что нужно сделать, — это отсоединить драйвер от его подсистемы ядра. "
+"Если устройство в настоящее время открыто, у драйвера есть два варианта: "
+"отказаться от отсоединения или принудительно закрыть устройство и продолжить "
+"отсоединение. Выбор зависит от возможности конкретной подсистемы ядра "
+"выполнить принудительное закрытие и от предпочтений автора драйвера. Как "
+"правило, принудительное закрытие кажется предпочтительным вариантом."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1070
+#, no-wrap
+msgid ""
+" struct xxx_softc *sc = device_get_softc(dev);\n"
+" int error;\n"
+msgstr ""
+" struct xxx_softc *sc = device_get_softc(dev);\n"
+" int error;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1074
+#, no-wrap
+msgid ""
+" error = xxx_detach_subsystem(sc);\n"
+" if(error)\n"
+" return error;\n"
+msgstr ""
+" error = xxx_detach_subsystem(sc);\n"
+" if(error)\n"
+" return error;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1077
+msgid ""
+"Next the driver may want to reset the hardware to some consistent state. "
+"That includes stopping any ongoing transfers, disabling the DMA channels and "
+"interrupts to avoid memory corruption by the device. For most of the drivers "
+"this is exactly what the shutdown routine does, so if it is included in the "
+"driver we can just call it."
+msgstr ""
+"Далее драйвер может сбросить аппаратное обеспечение в согласованное "
+"состояние. Это включает остановку любых текущих передач, отключение каналов "
+"DMA и прерываний, чтобы избежать повреждения памяти устройством. Для "
+"большинства драйверов это именно то, что делает процедура выключения, "
+"поэтому, если она присутствует в драйвере, мы можем просто вызвать её."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1079
+msgid "`xxx_isa_shutdown(dev);`"
+msgstr "`xxx_isa_shutdown(dev);`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1081
+msgid "And finally release all the resources and return success."
+msgstr "И наконец освободить все ресурсы и вернуть успех."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1089
+#, no-wrap
+msgid "xxx_isa_shutdown"
+msgstr "xxx_isa_shutdown"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1092
+msgid ""
+"This routine is called when the system is about to be shut down. It is "
+"expected to bring the hardware to some consistent state. For most of the ISA "
+"devices no special action is required, so the function is not really "
+"necessary because the device will be re-initialized on reboot anyway. But "
+"some devices have to be shut down with a special procedure, to make sure "
+"that they will be properly detected after soft reboot (this is especially "
+"true for many devices with proprietary identification protocols). In any "
+"case disabling DMA and interrupts in the device registers and stopping any "
+"ongoing transfers is a good idea. The exact action depends on the hardware, "
+"so we do not consider it here in any detail."
+msgstr ""
+"Эта процедура вызывается, когда система собирается быть выключена. "
+"Ожидается, что она приведет оборудование в согласованное состояние. Для "
+"большинства устройств ISA не требуется никаких специальных действий, поэтому "
+"функция не является действительно необходимой, так как устройство будет "
+"переинициализировано при перезагрузке в любом случае. Однако некоторые "
+"устройства должны быть выключены с помощью специальной процедуры, чтобы "
+"убедиться, что они будут правильно обнаружены после мягкой перезагрузки (это "
+"особенно актуально для многих устройств с проприетарными протоколами "
+"идентификации). В любом случае отключение DMA и прерываний в регистрах "
+"устройства и остановка любых текущих передач — это хорошая идея. Точные "
+"действия зависят от оборудования, поэтому мы не рассматриваем их здесь "
+"подробно."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1094
+#, no-wrap
+msgid "xxx_intr"
+msgstr "xxx_intr"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1097
+msgid ""
+"The interrupt handler is called when an interrupt is received which may be "
+"from this particular device. The ISA bus does not support interrupt sharing "
+"(except in some special cases) so in practice if the interrupt handler is "
+"called then the interrupt almost for sure came from its device. Still, the "
+"interrupt handler must poll the device registers and make sure that the "
+"interrupt was generated by its device. If not it should just return."
+msgstr ""
+"Обработчик прерывания вызывается при получении прерывания, которое может "
+"быть от данного конкретного устройства. Шина ISA не поддерживает разделение "
+"прерываний (за исключением некоторых специальных случаев), поэтому на "
+"практике, если вызывается обработчик прерывания, то прерывание почти "
+"наверняка поступило от его устройства. Тем не менее, обработчик прерывания "
+"должен опросить регистры устройства и убедиться, что прерывание было "
+"сгенерировано его устройством. Если нет, он должен просто вернуть управление."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1099
+msgid ""
+"The old convention for the ISA drivers was getting the device unit number as "
+"an argument. This is obsolete, and the new drivers receive whatever argument "
+"was specified for them in the attach routine when calling "
+"`bus_setup_intr()`. By the new convention it should be the pointer to the "
+"structure softc. So the interrupt handler commonly starts as:"
+msgstr ""
+"Старая практика для драйверов ISA заключалась в получении номера устройства "
+"в качестве аргумента. Это устарело, и новые драйверы получают любой "
+"аргумент, указанный для них в процедуре присоединения при вызове "
+"`bus_setup_intr()`. Согласно новой практике, это должен быть указатель на "
+"структуру softc. Таким образом, обработчик прерываний обычно начинается так:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1106
+#, no-wrap
+msgid ""
+" static void\n"
+" xxx_intr(struct xxx_softc *sc)\n"
+" {\n"
+msgstr ""
+" static void\n"
+" xxx_intr(struct xxx_softc *sc)\n"
+" {\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1109
+msgid ""
+"It runs at the interrupt priority level specified by the interrupt type "
+"parameter of `bus_setup_intr()`. That means that all the other interrupts of "
+"the same type as well as all the software interrupts are disabled."
+msgstr ""
+"Он выполняется на уровне приоритета прерывания, указанном параметром типа "
+"прерывания в `bus_setup_intr()`. Это означает, что все остальные прерывания "
+"того же типа, а также все программные прерывания, отключены."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1111
+msgid "To avoid races it is commonly written as a loop:"
+msgstr "Во избежание гонок это обычно записывается в виде цикла:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1119
+#, no-wrap
+msgid ""
+" while(xxx_interrupt_pending(sc)) {\n"
+" xxx_process_interrupt(sc);\n"
+" xxx_acknowledge_interrupt(sc);\n"
+" }\n"
+msgstr ""
+" while(xxx_interrupt_pending(sc)) {\n"
+" xxx_process_interrupt(sc);\n"
+" xxx_acknowledge_interrupt(sc);\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/isa/_index.adoc:1121
+msgid ""
+"The interrupt handler has to acknowledge interrupt to the device only but "
+"not to the interrupt controller, the system takes care of the latter."
+msgstr ""
+"Обработчик прерывания должен подтвердить прерывание только устройству, но не "
+"контроллеру прерываний, система позаботится о последнем."
diff --git a/documentation/content/ru/books/arch-handbook/jail/_index.adoc b/documentation/content/ru/books/arch-handbook/jail/_index.adoc
new file mode 100644
index 0000000000..84c46e9fbd
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/jail/_index.adoc
@@ -0,0 +1,529 @@
+---
+description: 'Подсистема клеток'
+next: books/arch-handbook/sysinit
+params:
+ path: /books/arch-handbook/jail/
+prev: books/arch-handbook/kobj
+showBookMenu: true
+tags: ["jail", "architecture", "networking", "kernel"]
+title: 'Глава 4. Подсистема клеток'
+weight: 5
+---
+
+[[jail]]
+= Подсистема клеток
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 4
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+На большинстве систем UNIX(R) пользователь `root` обладает неограниченной властью. Это не способствует безопасности. Если злоумышленник получит права `root` в системе, у него окажутся все функции под рукой. В FreeBSD существуют sysctl-параметры, которые ограничивают власть `root`, чтобы минимизировать ущерб от действий злоумышленника. В частности, одна из таких функций называется `уровни безопасности`. Аналогично, другая функция, доступная начиная с FreeBSD 4.0, — это утилита man:jail[8] — клетка. Клетка создает chroot-окружение и накладывает определенные ограничения на процессы, запущенные внутри `клетки`. Например, процесс в `клетке` не может влиять на процессы вне её, использовать определенные системные вызовы или наносить какой-либо ущерб основной системе.
+
+Клетка становится новой моделью безопасности. Пользователи запускают потенциально уязвимые серверы, такие как Apache, BIND и sendmail, внутри клеток, так что если злоумышленник получит права `root` внутри клетки, это будет лишь неудобством, а не катастрофой. Данная статья в основном сосредоточена на внутреннем устройстве (исходном коде) клетки. Для получения информации о настройке клетки см. extref:{handbook} jails/[раздел о клетках Руководства FreeBSD, jails-synopsis].
+
+[[jail-arch]]
+== Архитектура
+
+`Клетка` состоит из двух областей: пользовательской программы man:jail[8] и кода, реализованного в ядре: системного вызова man:jail[2] и связанных с ним ограничений. Я расскажу о пользовательской программе, а затем о том, как `клетка` реализована в ядре.
+
+=== Код в пользовательском пространстве
+
+Исходный код пользовательской части `клетки` находится в [.filename]#/usr/src/usr.sbin/jail# и состоит из одного файла [.filename]#jail.c#. Программа принимает следующие аргументы: путь к `клетке`, имя хоста, IP-адрес и команду для выполнения.
+
+==== Структуры данных
+
+В файле [.filename]#jail.c# первое, на что я бы обратил внимание, это объявление важной структуры `struct jail j;`, которая была включена из [.filename]#/usr/include/sys/jail.h#.
+
+Определение структуры `jail` выглядит следующим образом:
+
+[.programlisting]
+....
+/usr/include/sys/jail.h:
+
+struct jail {
+ u_int32_t version;
+ char *path;
+ char *hostname;
+ u_int32_t ip_number;
+};
+....
+
+Как видно, существует запись для каждого из аргументов, переданных программе man:jail[8], и действительно, они устанавливаются во время её выполнения.
+
+[.programlisting]
+....
+/usr/src/usr.sbin/jail/jail.c
+char path[PATH_MAX];
+...
+if (realpath(argv[0], path) == NULL)
+ err(1, "realpath: %s", argv[0]);
+if (chdir(path) != 0)
+ err(1, "chdir: %s", path);
+memset(&j, 0, sizeof(j));
+j.version = 0;
+j.path = path;
+j.hostname = argv[1];
+....
+
+==== Сетевое взаимодействие
+
+Один из аргументов, передаваемых программе man:jail[8], — это IP-адрес, по которому можно получить доступ к клетке через сеть. man:jail[8] преобразует указанный IP-адрес в порядок байтов хоста и сохраняет его в `j` (структура `jail`).
+
+[.programlisting]
+....
+/usr/src/usr.sbin/jail/jail.c:
+struct in_addr in;
+...
+if (inet_aton(argv[2], &in) == 0)
+ errx(1, "Could not make sense of ip-number: %s", argv[2]);
+j.ip_number = ntohl(in.s_addr);
+....
+
+Функция man:inet_aton[3] "интерпретирует указанную строку символов как интернет-адрес, помещая адрес в предоставленную структуру." Член структуры `ip_number` в структуре `jail` устанавливается только тогда, когда IP-адрес, помещённый в структуру `in` функцией man:inet_aton[3], преобразуется в порядок байтов хоста с помощью man:ntohl[3].
+
+==== Процесс в клетке
+
+Наконец, пользовательская программа помещает процесс в `клетку`. Теперь `клетка` становится самим заключенным процессом и выполняет команду, используя man:execv[3].
+
+[.programlisting]
+....
+/usr/src/usr.sbin/jail/jail.c
+i = jail(&j);
+...
+if (execv(argv[3], argv + 3) != 0)
+ err(1, "execv: %s", argv[3]);
+....
+
+Как видно, вызывается функция `jail()`, и её аргументом является структура `jail`, заполненная аргументами, переданными программе. В конце выполняется указанная вами программа. Теперь я расскажу о том, как `клетка` реализована в ядре.
+
+=== Пространство ядра системы
+
+Мы сейчас рассмотрим файл [.filename]#/usr/src/sys/kern/kern_jail.c#. В этом файле определены системный вызов man:jail[2], соответствующие sysctls и сетевые функции.
+
+==== Управляемые переменные ядра sysctl
+
+В файле [.filename]#kern_jail.c# определены следующие параметры sysctl:
+
+[.programlisting]
+....
+/usr/src/sys/kern/kern_jail.c:
+int jail_set_hostname_allowed = 1;
+SYSCTL_INT(_security_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,
+ &jail_set_hostname_allowed, 0,
+ "Processes in jail can set their hostnames");
+
+int jail_socket_unixiproute_only = 1;
+SYSCTL_INT(_security_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,
+ &jail_socket_unixiproute_only, 0,
+ "Processes in jail are limited to creating UNIX/IPv4/route sockets only");
+
+int jail_sysvipc_allowed = 0;
+SYSCTL_INT(_security_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
+ &jail_sysvipc_allowed, 0,
+ "Processes in jail can use System V IPC primitives");
+
+static int jail_enforce_statfs = 2;
+SYSCTL_INT(_security_jail, OID_AUTO, enforce_statfs, CTLFLAG_RW,
+ &jail_enforce_statfs, 0,
+ "Processes in jail cannot see all mounted file systems");
+
+int jail_allow_raw_sockets = 0;
+SYSCTL_INT(_security_jail, OID_AUTO, allow_raw_sockets, CTLFLAG_RW,
+ &jail_allow_raw_sockets, 0,
+ "Prison root can create raw sockets");
+
+int jail_chflags_allowed = 0;
+SYSCTL_INT(_security_jail, OID_AUTO, chflags_allowed, CTLFLAG_RW,
+ &jail_chflags_allowed, 0,
+ "Processes in jail can alter system file flags");
+
+int jail_mount_allowed = 0;
+SYSCTL_INT(_security_jail, OID_AUTO, mount_allowed, CTLFLAG_RW,
+ &jail_mount_allowed, 0,
+ "Processes in jail can mount/unmount jail-friendly file systems");
+....
+
+Каждый из этих параметров sysctl может быть доступен пользователю через программу man:sysctl[8]. В ядре эти конкретные параметры sysctl распознаются по их именам. Например, имя первого параметра sysctl — `security.jail.set_hostname_allowed`.
+
+==== Системный вызов man:jail[2]
+
+Как и все системные вызовы, системный вызов man:jail[2] принимает два аргумента: `struct thread *td` и `struct jail_args *uap`. `td` — это указатель на структуру `thread`, которая описывает вызывающий поток. В данном контексте `uap` — это указатель на структуру, в которой содержится указатель на структуру `jail`, переданную из пользовательского пространства [.filename]#jail.c#. Ранее, когда я описывал пользовательскую программу, вы видели, что системному вызову man:jail[2] была передана структура `jail` в качестве собственного аргумента.
+
+[.programlisting]
+....
+/usr/src/sys/kern/kern_jail.c:
+/*
+ * struct jail_args {
+ * struct jail *jail;
+ * };
+ */
+int
+jail(struct thread *td, struct jail_args *uap)
+....
+
+Следовательно, `uap->jail` можно использовать для доступа к структуре `jail`, которая была передана системному вызову. Далее системный вызов копирует структуру `клетка` в пространство ядра с помощью функции man:copyin[9]. man:copyin[9] принимает три аргумента: адрес данных, которые нужно скопировать в пространство ядра (`uap->jail`), место для записи данных (`j`) и размер хранилища. Структура `jail`, на которую указывает `uap->jail`, копируется в пространство ядра и сохраняется в другой структуре `клетка` — `j`.
+
+[.programlisting]
+....
+/usr/src/sys/kern/kern_jail.c:
+error = copyin(uap->jail, &j, sizeof(j));
+....
+
+В [.filename]#jail.h# определена ещё одна важная структура — `prison`. Структура `prison` используется исключительно в пространстве ядра. Вот определение структуры `prison`.
+
+[.programlisting]
+....
+/usr/include/sys/jail.h:
+struct prison {
+ LIST_ENTRY(prison) pr_list; /* (a) all prisons */
+ int pr_id; /* (c) prison id */
+ int pr_ref; /* (p) refcount */
+ char pr_path[MAXPATHLEN]; /* (c) chroot path */
+ struct vnode *pr_root; /* (c) vnode to rdir */
+ char pr_host[MAXHOSTNAMELEN]; /* (p) jail hostname */
+ u_int32_t pr_ip; /* (c) ip addr host */
+ void *pr_linux; /* (p) linux abi */
+ int pr_securelevel; /* (p) securelevel */
+ struct task pr_task; /* (d) destroy task */
+ struct mtx pr_mtx;
+ void **pr_slots; /* (p) additional data */
+};
+....
+
+Системный вызов man:jail[2] затем выделяет память для структуры `prison` и копирует данные между структурой `клетка` и структурой `prison`.
+
+[.programlisting]
+....
+/usr/src/sys/kern/kern_jail.c:
+MALLOC(pr, struct prison *, sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO);
+...
+error = copyinstr(j.path, &pr->pr_path, sizeof(pr->pr_path), 0);
+if (error)
+ goto e_killmtx;
+...
+error = copyinstr(j.hostname, &pr->pr_host, sizeof(pr->pr_host), 0);
+if (error)
+ goto e_dropvnref;
+pr->pr_ip = j.ip_number;
+....
+
+Далее мы рассмотрим ещё один важный системный вызов man:jail_attach[2], который реализует функцию помещения процесса в клетку.
+
+[.programlisting]
+....
+/usr/src/sys/kern/kern_jail.c:
+/*
+ * struct jail_attach_args {
+ * int jid;
+ * };
+ */
+int
+jail_attach(struct thread *td, struct jail_attach_args *uap)
+....
+
+Этот системный вызов вносит изменения, которые позволяют отличить процесс в клетке от процессов вне клетки. Чтобы понять, что делает man:jail_attach[2], необходима некоторая справочная информация.
+
+В FreeBSD каждый видимый ядром поток идентифицируется своей структурой `thread`, а процессы описываются их структурами `proc`. Определения структур `thread` и `proc` можно найти в [.filename]#/usr/include/sys/proc.h#. Например, аргумент `td` в любом системном вызове на самом деле является указателем на структуру `thread` вызывающего потока, как было указано ранее. Член `td_proc` в структуре `thread`, на которую указывает `td`, является указателем на структуру `proc`, представляющую процесс, содержащий поток, представленный структурой `td`. Структура `proc` содержит члены, которые могут описывать идентификацию владельца (`p_ucred`), ограничения ресурсов процесса (`p_limit`) и так далее. В структуре `ucred`, на которую указывает член `p_ucred` в структуре `proc`, есть указатель на структуру `prison` (`cr_prison`).
+
+[.programlisting]
+....
+/usr/include/sys/proc.h:
+struct thread {
+ ...
+ struct proc *td_proc;
+ ...
+};
+struct proc {
+ ...
+ struct ucred *p_ucred;
+ ...
+};
+/usr/include/sys/ucred.h
+struct ucred {
+ ...
+ struct prison *cr_prison;
+ ...
+};
+....
+
+В файле [.filename]#kern_jail.c# функция `jail()` вызывает функцию `jail_attach()` с заданным `jid`. Затем `jail_attach()` вызывает функцию `change_root()` для изменения корневого каталога вызывающего процесса. Функция `jail_attach()` создает новую структуру `ucred` и присоединяет её к вызывающему процессу после успешного присоединения структуры `prison` к структуре `ucred`. С этого момента вызывающий процесс считается находящимся в клетке. Когда в ядре вызывается функция `jailed()` с вновь созданной структурой `ucred` в качестве аргумента, она возвращает 1, указывая, что учётные данные связаны с клеткой. Общим родительским процессом для всех процессов, созданных внутри клетки, является процесс, запускающий man:jail[8], так как он вызывает системный вызов man:jail[2]. При выполнении программы через man:execve[2] она наследует свойство клетки из структуры `ucred` родительского процесса, следовательно, у нее структура `ucred` тоже со свойством клетки.
+
+[.programlisting]
+....
+/usr/src/sys/kern/kern_jail.c
+int
+jail(struct thread *td, struct jail_args *uap)
+{
+...
+ struct jail_attach_args jaa;
+...
+ error = jail_attach(td, &jaa);
+ if (error)
+ goto e_dropprref;
+...
+}
+
+int
+jail_attach(struct thread *td, struct jail_attach_args *uap)
+{
+ struct proc *p;
+ struct ucred *newcred, *oldcred;
+ struct prison *pr;
+...
+ p = td->td_proc;
+...
+ pr = prison_find(uap->jid);
+...
+ change_root(pr->pr_root, td);
+...
+ newcred->cr_prison = pr;
+ p->p_ucred = newcred;
+...
+}
+....
+
+Когда процесс создается из родительского процесса, системный вызов man:fork[2] использует `crhold()` для поддержания учетных данных нового процесса. Это автоматически сохраняет учетные данные нового дочернего процесса согласованными с родительским, поэтому дочерний процесс также остается в клетке.
+
+[.programlisting]
+....
+/usr/src/sys/kern/kern_fork.c:
+p2->p_ucred = crhold(td->td_ucred);
+...
+td2->td_ucred = crhold(p2->p_ucred);
+....
+
+[[jail-restrictions]]
+== Ограничения
+
+В ядре существуют ограничения доступа, связанные с процессами в клетках. Обычно эти ограничения просто проверяют, находится ли процесс в клетке, и если да, возвращают ошибку. Например:
+
+[.programlisting]
+....
+if (jailed(td->td_ucred))
+ return (EPERM);
+....
+
+=== SysV IPC
+
+System V IPC основан на сообщениях. Процессы могут отправлять друг другу эти сообщения, которые указывают им, как действовать. Функции, работающие с сообщениями: man:msgctl[3], man:msgget[3], man:msgsnd[3] и man:msgrcv[3]. Ранее я упоминал, что существуют определённые sysctl, которые можно включать или выключать для изменения поведения клетки. Один из таких sysctl — `security.jail.sysvipc_allowed`. По умолчанию этот sysctl установлен в 0. Если бы он был установлен в 1, это бы свело на нет весь смысл клетки: привилегированные пользователи внутри клетки смогли бы влиять на процессы за её пределами. Разница между сообщением и сигналом заключается в том, что сообщение состоит только из номера сигнала.
+
+[.filename]#/usr/src/sys/kern/sysv_msg.c#:
+
+* `msgget(key, msgflg)`: `msgget` возвращает (и, возможно, создаёт) дескриптор сообщения, который обозначает очередь сообщений для использования в других функциях.
+* `msgctl(msgid, cmd, buf)`: С помощью этой функции процесс может запросить статус дескриптора сообщения.
+* `msgsnd(msgid, msgp, msgsz, msgflg)`: `msgsnd` отправляет сообщение процессу.
+* `msgrcv(msgid, msgp, msgsz, msgtyp, msgflg)`: процесс получает сообщения с помощью этой функции
+
+В каждом из системных вызовов, соответствующих этим функциям, присутствует следующее условие:
+
+[.programlisting]
+....
+/usr/src/sys/kern/sysv_msg.c:
+if (!jail_sysvipc_allowed && jailed(td->td_ucred))
+ return (ENOSYS);
+....
+
+Системные вызовы семафоров позволяют процессам синхронизировать выполнение, атомарно выполняя набор операций над набором семафоров. По сути, семафоры предоставляют ещё один способ для процессов блокировать ресурсы. Однако процесс, ожидающий семафор, который уже используется, будет находиться в состоянии сна до тех пор, пока ресурсы не будут освобождены. Следующие системные вызовы семафоров блокируются внутри клетки: man:semget[2], man:semctl[2] и man:semop[2].
+
+[.filename]#/usr/src/sys/kern/sysv_sem.c#:
+
+* `semctl(semid, semnum, cmd, ...)`: `semctl` выполняет указанную команду `cmd` для очереди семафоров, указанной в `semid`.
+* `semget(key, nsems, flag)`: `semget` создает массив семафоров, соответствующих `key`.
++
+`key и flag имеют то же значение, как и в msgget.`
+* `semop(semid, array, nops)`: `semop` выполняет набор операций, указанных в `array`, для набора семафоров, идентифицируемых `semid`.
+
+Система IPC System V позволяет процессам использовать общую память. Процессы могут взаимодействовать напрямую друг с другом, разделяя части своего виртуального адресного пространства и затем читая и записывая данные в общей памяти. Эти системные вызовы заблокированы в среде _клетки_: man:shmdt[2], man:shmat[2], man:shmctl[2] и man:shmget[2].
+
+[.filename]#/usr/src/sys/kern/sysv_shm.c#:
+
+* `shmctl(shmid, cmd, buf)`: `shmctl` выполняет различные управляющие операции над областью разделяемой памяти, идентифицируемой `shmid`.
+* `shmget(key, size, flag)`: `shmget` обращается к существующей или создает новую область разделяемой памяти размером `size` байт.
+* `shmat(shmid, addr, flag)`: `shmat` присоединяет область разделяемой памяти, идентифицируемую `shmid`, к адресному пространству процесса.
+* `shmdt(addr)`: `shmdt` отсоединяет ранее присоединенную область разделяемой памяти по адресу `addr`.
+
+=== Сокеты
+
+Клетка обрабатывает системный вызов man:socket[2] и связанные низкоуровневые функции сокетов особым образом. Для определения, разрешено ли создание определённого сокета, сначала проверяется значение sysctl `security.jail.socket_unixiproute_only`. Если оно установлено, сокеты разрешено создавать только в случае, если указанное семейство равно `PF_LOCAL`, `PF_INET` или `PF_ROUTE`. В противном случае возвращается ошибка.
+
+[.programlisting]
+....
+/usr/src/sys/kern/uipc_socket.c:
+int
+socreate(int dom, struct socket **aso, int type, int proto,
+ struct ucred *cred, struct thread *td)
+{
+ struct protosw *prp;
+...
+ if (jailed(cred) && jail_socket_unixiproute_only &&
+ prp->pr_domain->dom_family != PF_LOCAL &&
+ prp->pr_domain->dom_family != PF_INET &&
+ prp->pr_domain->dom_family != PF_ROUTE) {
+ return (EPROTONOSUPPORT);
+ }
+...
+}
+....
+
+=== Berkeley Packet Filter
+
+Берклиевский фильтр пакетов (BPF) предоставляет низкоуровневый интерфейс к канальному уровню, независимый от протокола. В настоящее время BPF управляется через man:devfs[8], который определяет возможность его использования в клетке.
+
+=== Протоколы
+
+Существуют определенные протоколы, которые очень распространены, такие как TCP, UDP, IP и ICMP. IP и ICMP находятся на одном уровне: сетевом уровне 2. Принимаются определенные меры предосторожности, чтобы предотвратить привязку протокола к определенному адресу процессом в клетке, только если установлен параметр `nam`. `nam` является указателем на структуру `sockaddr`, которая описывает адрес, к которому привязывается служба. Более точное определение заключается в том, что `sockaddr` "может использоваться как шаблон для ссылки на идентификационный тег и длину каждого адреса". В функции `in_pcbbind_setup()`, `sin` — это указатель на структуру `sockaddr_in`, которая содержит порт, адрес, длину и семейство доменов сокета, который должен быть привязан. В основном, это запрещает любым процессам из клетки указывать адрес, который не принадлежит клетке, в которой существует вызывающий процесс.
+
+[.programlisting]
+....
+/usr/src/sys/netinet/in_pcb.c:
+int
+in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
+ u_short *lportp, struct ucred *cred)
+{
+ ...
+ struct sockaddr_in *sin;
+ ...
+ if (nam) {
+ sin = (struct sockaddr_in *)nam;
+ ...
+ if (sin->sin_addr.s_addr != INADDR_ANY)
+ if (prison_ip(cred, 0, &sin->sin_addr.s_addr))
+ return(EINVAL);
+ ...
+ if (lport) {
+ ...
+ if (prison && prison_ip(cred, 0, &sin->sin_addr.s_addr))
+ return (EADDRNOTAVAIL);
+ ...
+ }
+ }
+ if (lport == 0) {
+ ...
+ if (laddr.s_addr != INADDR_ANY)
+ if (prison_ip(cred, 0, &laddr.s_addr))
+ return (EINVAL);
+ ...
+ }
+...
+ if (prison_ip(cred, 0, &laddr.s_addr))
+ return (EINVAL);
+...
+}
+....
+
+Вы можете задаться вопросом, какую функцию выполняет `prison_ip()`. `prison_ip()` принимает три аргумента: указатель на учетные данные (представленные как `cred`), любые флаги и IP-адрес. Она возвращает 1, если IP-адрес НЕ принадлежит клетке, и 0 в противном случае. Как видно из кода, если это действительно IP-адрес, не принадлежащий клетке, протоколу не разрешается привязываться к этому адресу.
+
+[.programlisting]
+....
+/usr/src/sys/kern/kern_jail.c:
+int
+prison_ip(struct ucred *cred, int flag, u_int32_t *ip)
+{
+ u_int32_t tmp;
+
+ if (!jailed(cred))
+ return (0);
+ if (flag)
+ tmp = *ip;
+ else
+ tmp = ntohl(*ip);
+ if (tmp == INADDR_ANY) {
+ if (flag)
+ *ip = cred->cr_prison->pr_ip;
+ else
+ *ip = htonl(cred->cr_prison->pr_ip);
+ return (0);
+ }
+ if (tmp == INADDR_LOOPBACK) {
+ if (flag)
+ *ip = cred->cr_prison->pr_ip;
+ else
+ *ip = htonl(cred->cr_prison->pr_ip);
+ return (0);
+ }
+ if (cred->cr_prison->pr_ip != tmp)
+ return (1);
+ return (0);
+}
+....
+
+=== Файловая система
+
+Даже пользователи с правами `root` внутри `клетки` не могут снять или изменить любые флаги файлов, такие как неизменяемый, только для добавления и неудаляемый, если уровень безопасности (`securelevel`) больше 0.
+
+[.programlisting]
+....
+/usr/src/sys/ufs/ufs/ufs_vnops.c:
+static int
+ufs_setattr(ap)
+ ...
+{
+ ...
+ if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) {
+ if (ip->i_flags
+ & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
+ error = securelevel_gt(cred, 0);
+ if (error)
+ return (error);
+ }
+ ...
+ }
+}
+/usr/src/sys/kern/kern_priv.c
+int
+priv_check_cred(struct ucred *cred, int priv, int flags)
+{
+ ...
+ error = prison_priv_check(cred, priv);
+ if (error)
+ return (error);
+ ...
+}
+/usr/src/sys/kern/kern_jail.c
+int
+prison_priv_check(struct ucred *cred, int priv)
+{
+ ...
+ switch (priv) {
+ ...
+ case PRIV_VFS_SYSFLAGS:
+ if (jail_chflags_allowed)
+ return (0);
+ else
+ return (EPERM);
+ ...
+ }
+ ...
+}
+....
diff --git a/documentation/content/ru/books/arch-handbook/jail/_index.po b/documentation/content/ru/books/arch-handbook/jail/_index.po
new file mode 100644
index 0000000000..b5efb43699
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/jail/_index.po
@@ -0,0 +1,1452 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-08-16 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookjail_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:14
+#, no-wrap
+msgid "The Jail Subsystem"
+msgstr "Подсистема клеток"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:1
+#, no-wrap
+msgid "Chapter 4. The Jail Subsystem"
+msgstr "Глава 4. Подсистема клеток"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:52
+msgid ""
+"On most UNIX(R) systems, `root` has omnipotent power. This promotes "
+"insecurity. If an attacker gained `root` on a system, he would have every "
+"function at his fingertips. In FreeBSD there are sysctls which dilute the "
+"power of `root`, in order to minimize the damage caused by an attacker. "
+"Specifically, one of these functions is called `secure levels`. Similarly, "
+"another function which is present from FreeBSD 4.0 and onward, is a utility "
+"called man:jail[8]. Jail chroots an environment and sets certain "
+"restrictions on processes which are forked within the jail. For example, a "
+"jailed process cannot affect processes outside the jail, utilize certain "
+"system calls, or inflict any damage on the host environment."
+msgstr ""
+"На большинстве систем UNIX(R) пользователь `root` обладает неограниченной "
+"властью. Это не способствует безопасности. Если злоумышленник получит права "
+"`root` в системе, у него окажутся все функции под рукой. В FreeBSD "
+"существуют sysctl-параметры, которые ограничивают власть `root`, чтобы "
+"минимизировать ущерб от действий злоумышленника. В частности, одна из таких "
+"функций называется `уровни безопасности`. Аналогично, другая функция, "
+"доступная начиная с FreeBSD 4.0, — это утилита man:jail[8] — клетка. Клетка "
+"создает chroot-окружение и накладывает определенные ограничения на процессы, "
+"запущенные внутри `клетки`. Например, процесс в `клетке` не может влиять на "
+"процессы вне её, использовать определенные системные вызовы или наносить "
+"какой-либо ущерб основной системе."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:54
+msgid ""
+"Jail is becoming the new security model. People are running potentially "
+"vulnerable servers such as Apache, BIND, and sendmail within jails, so that "
+"if an attacker gains `root` within the jail, it is only an annoyance, and "
+"not a devastation. This article mainly focuses on the internals (source "
+"code) of jail. For information on how to set up a jail see the extref:"
+"{handbook}[handbook entry on jails, jails]."
+msgstr ""
+"Клетка становится новой моделью безопасности. Пользователи запускают "
+"потенциально уязвимые серверы, такие как Apache, BIND и sendmail, внутри "
+"клеток, так что если злоумышленник получит права `root` внутри клетки, это "
+"будет лишь неудобством, а не катастрофой. Данная статья в основном "
+"сосредоточена на внутреннем устройстве (исходном коде) клетки. Для получения "
+"информации о настройке клетки см. extref:{handbook} jails/[раздел о клетках "
+"Руководства FreeBSD, jails-synopsis]."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:56
+#, no-wrap
+msgid "Architecture"
+msgstr "Архитектура"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:59
+msgid ""
+"Jail consists of two realms: the userland program, man:jail[8], and the code "
+"implemented within the kernel: the man:jail[2] system call and associated "
+"restrictions. I will be discussing the userland program and then how jail is "
+"implemented within the kernel."
+msgstr ""
+"`Клетка` состоит из двух областей: пользовательской программы man:jail[8] и "
+"кода, реализованного в ядре: системного вызова man:jail[2] и связанных с ним "
+"ограничений. Я расскажу о пользовательской программе, а затем о том, как "
+"`клетка` реализована в ядре."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:60
+#, no-wrap
+msgid "Userland Code"
+msgstr "Код в пользовательском пространстве"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:63
+msgid ""
+"The source for the userland jail is located in [.filename]#/usr/src/usr.sbin/"
+"jail#, consisting of one file, [.filename]#jail.c#. The program takes these "
+"arguments: the path of the jail, hostname, IP address, and the command to be "
+"executed."
+msgstr ""
+"Исходный код пользовательской части `клетки` находится в [.filename]#/usr/"
+"src/usr.sbin/jail# и состоит из одного файла [.filename]#jail.c#. Программа "
+"принимает следующие аргументы: путь к `клетке`, имя хоста, IP-адрес и "
+"команду для выполнения."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:64
+#, no-wrap
+msgid "Data Structures"
+msgstr "Структуры данных"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:67
+msgid ""
+"In [.filename]#jail.c#, the first thing I would note is the declaration of "
+"an important structure `struct jail j;` which was included from [.filename]#/"
+"usr/include/sys/jail.h#."
+msgstr ""
+"В файле [.filename]#jail.c# первое, на что я бы обратил внимание, это "
+"объявление важной структуры `struct jail j;`, которая была включена из "
+"[.filename]#/usr/include/sys/jail.h#."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:69
+msgid "The definition of the `jail` structure is:"
+msgstr "Определение структуры `jail` выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:73
+#, no-wrap
+msgid "/usr/include/sys/jail.h:\n"
+msgstr "/usr/include/sys/jail.h:\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:80
+#, no-wrap
+msgid ""
+"struct jail {\n"
+" u_int32_t version;\n"
+" char *path;\n"
+" char *hostname;\n"
+" u_int32_t ip_number;\n"
+"};\n"
+msgstr ""
+"struct jail {\n"
+" u_int32_t version;\n"
+" char *path;\n"
+" char *hostname;\n"
+" u_int32_t ip_number;\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:83
+msgid ""
+"As you can see, there is an entry for each of the arguments passed to the "
+"man:jail[8] program, and indeed, they are set during its execution."
+msgstr ""
+"Как видно, существует запись для каждого из аргументов, переданных программе "
+"man:jail[8], и действительно, они устанавливаются во время её выполнения."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:97
+#, no-wrap
+msgid ""
+"/usr/src/usr.sbin/jail/jail.c\n"
+"char path[PATH_MAX];\n"
+"...\n"
+"if (realpath(argv[0], path) == NULL)\n"
+" err(1, \"realpath: %s\", argv[0]);\n"
+"if (chdir(path) != 0)\n"
+" err(1, \"chdir: %s\", path);\n"
+"memset(&j, 0, sizeof(j));\n"
+"j.version = 0;\n"
+"j.path = path;\n"
+"j.hostname = argv[1];\n"
+msgstr ""
+"/usr/src/usr.sbin/jail/jail.c\n"
+"char path[PATH_MAX];\n"
+"...\n"
+"if (realpath(argv[0], path) == NULL)\n"
+" err(1, \"realpath: %s\", argv[0]);\n"
+"if (chdir(path) != 0)\n"
+" err(1, \"chdir: %s\", path);\n"
+"memset(&j, 0, sizeof(j));\n"
+"j.version = 0;\n"
+"j.path = path;\n"
+"j.hostname = argv[1];\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:99
+#, no-wrap
+msgid "Networking"
+msgstr "Сетевое взаимодействие"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:102
+msgid ""
+"One of the arguments passed to the man:jail[8] program is an IP address with "
+"which the jail can be accessed over the network. man:jail[8] translates the "
+"IP address given into host byte order and then stores it in `j` (the `jail` "
+"structure)."
+msgstr ""
+"Один из аргументов, передаваемых программе man:jail[8], — это IP-адрес, по "
+"которому можно получить доступ к клетке через сеть. man:jail[8] преобразует "
+"указанный IP-адрес в порядок байтов хоста и сохраняет его в `j` (структура "
+"`jail`)."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:111
+#, no-wrap
+msgid ""
+"/usr/src/usr.sbin/jail/jail.c:\n"
+"struct in_addr in;\n"
+"...\n"
+"if (inet_aton(argv[2], &in) == 0)\n"
+" errx(1, \"Could not make sense of ip-number: %s\", argv[2]);\n"
+"j.ip_number = ntohl(in.s_addr);\n"
+msgstr ""
+"/usr/src/usr.sbin/jail/jail.c:\n"
+"struct in_addr in;\n"
+"...\n"
+"if (inet_aton(argv[2], &in) == 0)\n"
+" errx(1, \"Could not make sense of ip-number: %s\", argv[2]);\n"
+"j.ip_number = ntohl(in.s_addr);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:114
+msgid ""
+"The man:inet_aton[3] function \"interprets the specified character string as "
+"an Internet address, placing the address into the structure provided.\" The "
+"`ip_number` member in the `jail` structure is set only when the IP address "
+"placed onto the `in` structure by man:inet_aton[3] is translated into host "
+"byte order by man:ntohl[3]."
+msgstr ""
+"Функция man:inet_aton[3] \"интерпретирует указанную строку символов как "
+"интернет-адрес, помещая адрес в предоставленную структуру.\" Член структуры "
+"`ip_number` в структуре `jail` устанавливается только тогда, когда IP-адрес, "
+"помещённый в структуру `in` функцией man:inet_aton[3], преобразуется в "
+"порядок байтов хоста с помощью man:ntohl[3]."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:115
+#, no-wrap
+msgid "Jailing the Process"
+msgstr "Процесс в клетке"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:118
+msgid ""
+"Finally, the userland program jails the process. Jail now becomes an "
+"imprisoned process itself and then executes the command given using "
+"man:execv[3]."
+msgstr ""
+"Наконец, пользовательская программа помещает процесс в `клетку`. Теперь "
+"`клетка` становится самим заключенным процессом и выполняет команду, "
+"используя man:execv[3]."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:126
+#, no-wrap
+msgid ""
+"/usr/src/usr.sbin/jail/jail.c\n"
+"i = jail(&j);\n"
+"...\n"
+"if (execv(argv[3], argv + 3) != 0)\n"
+" err(1, \"execv: %s\", argv[3]);\n"
+msgstr ""
+"/usr/src/usr.sbin/jail/jail.c\n"
+"i = jail(&j);\n"
+"...\n"
+"if (execv(argv[3], argv + 3) != 0)\n"
+" err(1, \"execv: %s\", argv[3]);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:129
+msgid ""
+"As you can see, the `jail()` function is called, and its argument is the "
+"`jail` structure which has been filled with the arguments given to the "
+"program. Finally, the program you specify is executed. I will now discuss "
+"how jail is implemented within the kernel."
+msgstr ""
+"Как видно, вызывается функция `jail()`, и её аргументом является структура "
+"`jail`, заполненная аргументами, переданными программе. В конце выполняется "
+"указанная вами программа. Теперь я расскажу о том, как `клетка` реализована "
+"в ядре."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:130
+#, no-wrap
+msgid "Kernel Space"
+msgstr "Пространство ядра системы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:133
+msgid ""
+"We will now be looking at the file [.filename]#/usr/src/sys/kern/"
+"kern_jail.c#. This is the file where the man:jail[2] system call, "
+"appropriate sysctls, and networking functions are defined."
+msgstr ""
+"Мы сейчас рассмотрим файл [.filename]#/usr/src/sys/kern/kern_jail.c#. В этом "
+"файле определены системный вызов man:jail[2], соответствующие sysctls и "
+"сетевые функции."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:134
+#, no-wrap
+msgid "Sysctls"
+msgstr "Управляемые переменные ядра sysctl"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:137
+msgid "In [.filename]#kern_jail.c#, the following sysctls are defined:"
+msgstr ""
+"В файле [.filename]#kern_jail.c# определены следующие параметры sysctl:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:145
+#, no-wrap
+msgid ""
+"/usr/src/sys/kern/kern_jail.c:\n"
+"int jail_set_hostname_allowed = 1;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,\n"
+" &jail_set_hostname_allowed, 0,\n"
+" \"Processes in jail can set their hostnames\");\n"
+msgstr ""
+"/usr/src/sys/kern/kern_jail.c:\n"
+"int jail_set_hostname_allowed = 1;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,\n"
+" &jail_set_hostname_allowed, 0,\n"
+" \"Processes in jail can set their hostnames\");\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:150
+#, no-wrap
+msgid ""
+"int jail_socket_unixiproute_only = 1;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,\n"
+" &jail_socket_unixiproute_only, 0,\n"
+" \"Processes in jail are limited to creating UNIX/IPv4/route sockets only\");\n"
+msgstr ""
+"int jail_socket_unixiproute_only = 1;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,\n"
+" &jail_socket_unixiproute_only, 0,\n"
+" \"Processes in jail are limited to creating UNIX/IPv4/route sockets only\");\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:155
+#, no-wrap
+msgid ""
+"int jail_sysvipc_allowed = 0;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,\n"
+" &jail_sysvipc_allowed, 0,\n"
+" \"Processes in jail can use System V IPC primitives\");\n"
+msgstr ""
+"int jail_sysvipc_allowed = 0;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,\n"
+" &jail_sysvipc_allowed, 0,\n"
+" \"Processes in jail can use System V IPC primitives\");\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:160
+#, no-wrap
+msgid ""
+"static int jail_enforce_statfs = 2;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, enforce_statfs, CTLFLAG_RW,\n"
+" &jail_enforce_statfs, 0,\n"
+" \"Processes in jail cannot see all mounted file systems\");\n"
+msgstr ""
+"static int jail_enforce_statfs = 2;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, enforce_statfs, CTLFLAG_RW,\n"
+" &jail_enforce_statfs, 0,\n"
+" \"Processes in jail cannot see all mounted file systems\");\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:165
+#, no-wrap
+msgid ""
+"int jail_allow_raw_sockets = 0;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, allow_raw_sockets, CTLFLAG_RW,\n"
+" &jail_allow_raw_sockets, 0,\n"
+" \"Prison root can create raw sockets\");\n"
+msgstr ""
+"int jail_allow_raw_sockets = 0;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, allow_raw_sockets, CTLFLAG_RW,\n"
+" &jail_allow_raw_sockets, 0,\n"
+" \"Prison root can create raw sockets\");\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:170
+#, no-wrap
+msgid ""
+"int jail_chflags_allowed = 0;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, chflags_allowed, CTLFLAG_RW,\n"
+" &jail_chflags_allowed, 0,\n"
+" \"Processes in jail can alter system file flags\");\n"
+msgstr ""
+"int jail_chflags_allowed = 0;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, chflags_allowed, CTLFLAG_RW,\n"
+" &jail_chflags_allowed, 0,\n"
+" \"Processes in jail can alter system file flags\");\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:175
+#, no-wrap
+msgid ""
+"int jail_mount_allowed = 0;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, mount_allowed, CTLFLAG_RW,\n"
+" &jail_mount_allowed, 0,\n"
+" \"Processes in jail can mount/unmount jail-friendly file systems\");\n"
+msgstr ""
+"int jail_mount_allowed = 0;\n"
+"SYSCTL_INT(_security_jail, OID_AUTO, mount_allowed, CTLFLAG_RW,\n"
+" &jail_mount_allowed, 0,\n"
+" \"Processes in jail can mount/unmount jail-friendly file systems\");\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:178
+msgid ""
+"Each of these sysctls can be accessed by the user through the man:sysctl[8] "
+"program. Throughout the kernel, these specific sysctls are recognized by "
+"their name. For example, the name of the first sysctl is "
+"`security.jail.set_hostname_allowed`."
+msgstr ""
+"Каждый из этих параметров sysctl может быть доступен пользователю через "
+"программу man:sysctl[8]. В ядре эти конкретные параметры sysctl распознаются "
+"по их именам. Например, имя первого параметра sysctl — "
+"`security.jail.set_hostname_allowed`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:179
+#, no-wrap
+msgid "man:jail[2] System Call"
+msgstr "Системный вызов man:jail[2]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:182
+msgid ""
+"Like all system calls, the man:jail[2] system call takes two arguments, "
+"`struct thread *td` and `struct jail_args *uap`. `td` is a pointer to the "
+"`thread` structure which describes the calling thread. In this context, "
+"`uap` is a pointer to the structure in which a pointer to the `jail` "
+"structure passed by the userland [.filename]#jail.c# is contained. When I "
+"described the userland program before, you saw that the man:jail[2] system "
+"call was given a `jail` structure as its own argument."
+msgstr ""
+"Как и все системные вызовы, системный вызов man:jail[2] принимает два "
+"аргумента: `struct thread *td` и `struct jail_args *uap`. `td` — это "
+"указатель на структуру `thread`, которая описывает вызывающий поток. В "
+"данном контексте `uap` — это указатель на структуру, в которой содержится "
+"указатель на структуру `jail`, переданную из пользовательского пространства "
+"[.filename]#jail.c#. Ранее, когда я описывал пользовательскую программу, вы "
+"видели, что системному вызову man:jail[2] была передана структура `jail` в "
+"качестве собственного аргумента."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:193
+#, no-wrap
+msgid ""
+"/usr/src/sys/kern/kern_jail.c:\n"
+"/*\n"
+" * struct jail_args {\n"
+" * struct jail *jail;\n"
+" * };\n"
+" */\n"
+"int\n"
+"jail(struct thread *td, struct jail_args *uap)\n"
+msgstr ""
+"/usr/src/sys/kern/kern_jail.c:\n"
+"/*\n"
+" * struct jail_args {\n"
+" * struct jail *jail;\n"
+" * };\n"
+" */\n"
+"int\n"
+"jail(struct thread *td, struct jail_args *uap)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:196
+msgid ""
+"Therefore, `uap->jail` can be used to access the `jail` structure which was "
+"passed to the system call. Next, the system call copies the `jail` structure "
+"into kernel space using the man:copyin[9] function. man:copyin[9] takes "
+"three arguments: the address of the data which is to be copied into kernel "
+"space, `uap->jail`, where to store it, `j` and the size of the storage. The "
+"`jail` structure pointed by `uap->jail` is copied into kernel space and is "
+"stored in another `jail` structure, `j`."
+msgstr ""
+"Следовательно, `uap->jail` можно использовать для доступа к структуре "
+"`jail`, которая была передана системному вызову. Далее системный вызов "
+"копирует структуру `клетка` в пространство ядра с помощью функции "
+"man:copyin[9]. man:copyin[9] принимает три аргумента: адрес данных, которые "
+"нужно скопировать в пространство ядра (`uap->jail`), место для записи данных "
+"(`j`) и размер хранилища. Структура `jail`, на которую указывает `uap-"
+">jail`, копируется в пространство ядра и сохраняется в другой структуре "
+"`клетка` — `j`."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:201
+#, no-wrap
+msgid ""
+"/usr/src/sys/kern/kern_jail.c:\n"
+"error = copyin(uap->jail, &j, sizeof(j));\n"
+msgstr ""
+"/usr/src/sys/kern/kern_jail.c:\n"
+"error = copyin(uap->jail, &j, sizeof(j));\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:204
+msgid ""
+"There is another important structure defined in [.filename]#jail.h#. It is "
+"the `prison` structure. The `prison` structure is used exclusively within "
+"kernel space. Here is the definition of the `prison` structure."
+msgstr ""
+"В [.filename]#jail.h# определена ещё одна важная структура — `prison`. "
+"Структура `prison` используется исключительно в пространстве ядра. Вот "
+"определение структуры `prison`."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:222
+#, no-wrap
+msgid ""
+"/usr/include/sys/jail.h:\n"
+"struct prison {\n"
+" LIST_ENTRY(prison) pr_list; /* (a) all prisons */\n"
+" int pr_id; /* (c) prison id */\n"
+" int pr_ref; /* (p) refcount */\n"
+" char pr_path[MAXPATHLEN]; /* (c) chroot path */\n"
+" struct vnode *pr_root; /* (c) vnode to rdir */\n"
+" char pr_host[MAXHOSTNAMELEN]; /* (p) jail hostname */\n"
+" u_int32_t pr_ip; /* (c) ip addr host */\n"
+" void *pr_linux; /* (p) linux abi */\n"
+" int pr_securelevel; /* (p) securelevel */\n"
+" struct task pr_task; /* (d) destroy task */\n"
+" struct mtx pr_mtx;\n"
+" void **pr_slots; /* (p) additional data */\n"
+"};\n"
+msgstr ""
+"/usr/include/sys/jail.h:\n"
+"struct prison {\n"
+" LIST_ENTRY(prison) pr_list; /* (a) all prisons */\n"
+" int pr_id; /* (c) prison id */\n"
+" int pr_ref; /* (p) refcount */\n"
+" char pr_path[MAXPATHLEN]; /* (c) chroot path */\n"
+" struct vnode *pr_root; /* (c) vnode to rdir */\n"
+" char pr_host[MAXHOSTNAMELEN]; /* (p) jail hostname */\n"
+" u_int32_t pr_ip; /* (c) ip addr host */\n"
+" void *pr_linux; /* (p) linux abi */\n"
+" int pr_securelevel; /* (p) securelevel */\n"
+" struct task pr_task; /* (d) destroy task */\n"
+" struct mtx pr_mtx;\n"
+" void **pr_slots; /* (p) additional data */\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:225
+msgid ""
+"The man:jail[2] system call then allocates memory for a `prison` structure "
+"and copies data between the `jail` and `prison` structure."
+msgstr ""
+"Системный вызов man:jail[2] затем выделяет память для структуры `prison` и "
+"копирует данные между структурой `клетка` и структурой `prison`."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:239
+#, no-wrap
+msgid ""
+"/usr/src/sys/kern/kern_jail.c:\n"
+"MALLOC(pr, struct prison *, sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO);\n"
+"...\n"
+"error = copyinstr(j.path, &pr->pr_path, sizeof(pr->pr_path), 0);\n"
+"if (error)\n"
+" goto e_killmtx;\n"
+"...\n"
+"error = copyinstr(j.hostname, &pr->pr_host, sizeof(pr->pr_host), 0);\n"
+"if (error)\n"
+" goto e_dropvnref;\n"
+"pr->pr_ip = j.ip_number;\n"
+msgstr ""
+"/usr/src/sys/kern/kern_jail.c:\n"
+"MALLOC(pr, struct prison *, sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO);\n"
+"...\n"
+"error = copyinstr(j.path, &pr->pr_path, sizeof(pr->pr_path), 0);\n"
+"if (error)\n"
+" goto e_killmtx;\n"
+"...\n"
+"error = copyinstr(j.hostname, &pr->pr_host, sizeof(pr->pr_host), 0);\n"
+"if (error)\n"
+" goto e_dropvnref;\n"
+"pr->pr_ip = j.ip_number;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:242
+msgid ""
+"Next, we will discuss another important system call man:jail_attach[2], "
+"which implements the function to put a process into the jail."
+msgstr ""
+"Далее мы рассмотрим ещё один важный системный вызов man:jail_attach[2], "
+"который реализует функцию помещения процесса в клетку."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:253
+#, no-wrap
+msgid ""
+"/usr/src/sys/kern/kern_jail.c:\n"
+"/*\n"
+" * struct jail_attach_args {\n"
+" * int jid;\n"
+" * };\n"
+" */\n"
+"int\n"
+"jail_attach(struct thread *td, struct jail_attach_args *uap)\n"
+msgstr ""
+"/usr/src/sys/kern/kern_jail.c:\n"
+"/*\n"
+" * struct jail_attach_args {\n"
+" * int jid;\n"
+" * };\n"
+" */\n"
+"int\n"
+"jail_attach(struct thread *td, struct jail_attach_args *uap)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:256
+msgid ""
+"This system call makes the changes that can distinguish a jailed process "
+"from those unjailed ones. To understand what man:jail_attach[2] does for us, "
+"certain background information is needed."
+msgstr ""
+"Этот системный вызов вносит изменения, которые позволяют отличить процесс в "
+"клетке от процессов вне клетки. Чтобы понять, что делает man:jail_attach[2], "
+"необходима некоторая справочная информация."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:258
+msgid ""
+"On FreeBSD, each kernel visible thread is identified by its `thread` "
+"structure, while the processes are described by their `proc` structures. You "
+"can find the definitions of the `thread` and `proc` structure in "
+"[.filename]#/usr/include/sys/proc.h#. For example, the `td` argument in any "
+"system call is actually a pointer to the calling thread's `thread` "
+"structure, as stated before. The `td_proc` member in the `thread` structure "
+"pointed by `td` is a pointer to the `proc` structure which represents the "
+"process that contains the thread represented by `td`. The `proc` structure "
+"contains members which can describe the owner's identity(`p_ucred`), the "
+"process resource limits(`p_limit`), and so on. In the `ucred` structure "
+"pointed by `p_ucred` member in the `proc` structure, there is a pointer to "
+"the `prison` structure(`cr_prison`)."
+msgstr ""
+"В FreeBSD каждый видимый ядром поток идентифицируется своей структурой "
+"`thread`, а процессы описываются их структурами `proc`. Определения структур "
+"`thread` и `proc` можно найти в [.filename]#/usr/include/sys/proc.h#. "
+"Например, аргумент `td` в любом системном вызове на самом деле является "
+"указателем на структуру `thread` вызывающего потока, как было указано ранее. "
+"Член `td_proc` в структуре `thread`, на которую указывает `td`, является "
+"указателем на структуру `proc`, представляющую процесс, содержащий поток, "
+"представленный структурой `td`. Структура `proc` содержит члены, которые "
+"могут описывать идентификацию владельца (`p_ucred`), ограничения ресурсов "
+"процесса (`p_limit`) и так далее. В структуре `ucred`, на которую указывает "
+"член `p_ucred` в структуре `proc`, есть указатель на структуру `prison` "
+"(`cr_prison`)."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:278
+#, no-wrap
+msgid ""
+"/usr/include/sys/proc.h:\n"
+"struct thread {\n"
+" ...\n"
+" struct proc *td_proc;\n"
+" ...\n"
+"};\n"
+"struct proc {\n"
+" ...\n"
+" struct ucred *p_ucred;\n"
+" ...\n"
+"};\n"
+"/usr/include/sys/ucred.h\n"
+"struct ucred {\n"
+" ...\n"
+" struct prison *cr_prison;\n"
+" ...\n"
+"};\n"
+msgstr ""
+"/usr/include/sys/proc.h:\n"
+"struct thread {\n"
+" ...\n"
+" struct proc *td_proc;\n"
+" ...\n"
+"};\n"
+"struct proc {\n"
+" ...\n"
+" struct ucred *p_ucred;\n"
+" ...\n"
+"};\n"
+"/usr/include/sys/ucred.h\n"
+"struct ucred {\n"
+" ...\n"
+" struct prison *cr_prison;\n"
+" ...\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:281
+msgid ""
+"In [.filename]#kern_jail.c#, the function `jail()` then calls function "
+"`jail_attach()` with a given `jid`. And `jail_attach()` calls function "
+"`change_root()` to change the root directory of the calling process. The "
+"`jail_attach()` then creates a new `ucred` structure, and attaches the newly "
+"created `ucred` structure to the calling process after it has successfully "
+"attached the `prison` structure to the `ucred` structure. From then on, the "
+"calling process is recognized as jailed. When the kernel routine `jailed()` "
+"is called in the kernel with the newly created `ucred` structure as its "
+"argument, it returns 1 to tell that the credential is connected with a jail. "
+"The public ancestor process of all the process forked within the jail, is "
+"the process which runs man:jail[8], as it calls the man:jail[2] system call. "
+"When a program is executed through man:execve[2], it inherits the jailed "
+"property of its parent's `ucred` structure, therefore it has a jailed "
+"`ucred` structure."
+msgstr ""
+"В файле [.filename]#kern_jail.c# функция `jail()` вызывает функцию "
+"`jail_attach()` с заданным `jid`. Затем `jail_attach()` вызывает функцию "
+"`change_root()` для изменения корневого каталога вызывающего процесса. "
+"Функция `jail_attach()` создает новую структуру `ucred` и присоединяет её к "
+"вызывающему процессу после успешного присоединения структуры `prison` к "
+"структуре `ucred`. С этого момента вызывающий процесс считается находящимся "
+"в клетке. Когда в ядре вызывается функция `jailed()` с вновь созданной "
+"структурой `ucred` в качестве аргумента, она возвращает 1, указывая, что "
+"учётные данные связаны с клеткой. Общим родительским процессом для всех "
+"процессов, созданных внутри клетки, является процесс, запускающий "
+"man:jail[8], так как он вызывает системный вызов man:jail[2]. При выполнении "
+"программы через man:execve[2] она наследует свойство клетки из структуры "
+"`ucred` родительского процесса, следовательно, у нее структура `ucred` тоже "
+"со свойством клетки."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:296
+#, no-wrap
+msgid ""
+"/usr/src/sys/kern/kern_jail.c\n"
+"int\n"
+"jail(struct thread *td, struct jail_args *uap)\n"
+"{\n"
+"...\n"
+" struct jail_attach_args jaa;\n"
+"...\n"
+" error = jail_attach(td, &jaa);\n"
+" if (error)\n"
+" goto e_dropprref;\n"
+"...\n"
+"}\n"
+msgstr ""
+"/usr/src/sys/kern/kern_jail.c\n"
+"int\n"
+"jail(struct thread *td, struct jail_args *uap)\n"
+"{\n"
+"...\n"
+" struct jail_attach_args jaa;\n"
+"...\n"
+" error = jail_attach(td, &jaa);\n"
+" if (error)\n"
+" goto e_dropprref;\n"
+"...\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:314
+#, no-wrap
+msgid ""
+"int\n"
+"jail_attach(struct thread *td, struct jail_attach_args *uap)\n"
+"{\n"
+" struct proc *p;\n"
+" struct ucred *newcred, *oldcred;\n"
+" struct prison *pr;\n"
+"...\n"
+" p = td->td_proc;\n"
+"...\n"
+" pr = prison_find(uap->jid);\n"
+"...\n"
+" change_root(pr->pr_root, td);\n"
+"...\n"
+" newcred->cr_prison = pr;\n"
+" p->p_ucred = newcred;\n"
+"...\n"
+"}\n"
+msgstr ""
+"int\n"
+"jail_attach(struct thread *td, struct jail_attach_args *uap)\n"
+"{\n"
+" struct proc *p;\n"
+" struct ucred *newcred, *oldcred;\n"
+" struct prison *pr;\n"
+"...\n"
+" p = td->td_proc;\n"
+"...\n"
+" pr = prison_find(uap->jid);\n"
+"...\n"
+" change_root(pr->pr_root, td);\n"
+"...\n"
+" newcred->cr_prison = pr;\n"
+" p->p_ucred = newcred;\n"
+"...\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:317
+msgid ""
+"When a process is forked from its parent process, the man:fork[2] system "
+"call uses `crhold()` to maintain the credential for the newly forked "
+"process. It inherently keep the newly forked child's credential consistent "
+"with its parent, so the child process is also jailed."
+msgstr ""
+"Когда процесс создается из родительского процесса, системный вызов "
+"man:fork[2] использует `crhold()` для поддержания учетных данных нового "
+"процесса. Это автоматически сохраняет учетные данные нового дочернего "
+"процесса согласованными с родительским, поэтому дочерний процесс также "
+"остается в клетке."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:324
+#, no-wrap
+msgid ""
+"/usr/src/sys/kern/kern_fork.c:\n"
+"p2->p_ucred = crhold(td->td_ucred);\n"
+"...\n"
+"td2->td_ucred = crhold(p2->p_ucred);\n"
+msgstr ""
+"/usr/src/sys/kern/kern_fork.c:\n"
+"p2->p_ucred = crhold(td->td_ucred);\n"
+"...\n"
+"td2->td_ucred = crhold(p2->p_ucred);\n"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:327
+#, no-wrap
+msgid "Restrictions"
+msgstr "Ограничения"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:330
+msgid ""
+"Throughout the kernel there are access restrictions relating to jailed "
+"processes. Usually, these restrictions only check whether the process is "
+"jailed, and if so, returns an error. For example:"
+msgstr ""
+"В ядре существуют ограничения доступа, связанные с процессами в клетках. "
+"Обычно эти ограничения просто проверяют, находится ли процесс в клетке, и "
+"если да, возвращают ошибку. Например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:335
+#, no-wrap
+msgid ""
+"if (jailed(td->td_ucred))\n"
+" return (EPERM);\n"
+msgstr ""
+"if (jailed(td->td_ucred))\n"
+" return (EPERM);\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:337
+#, no-wrap
+msgid "SysV IPC"
+msgstr "SysV IPC"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:340
+msgid ""
+"System V IPC is based on messages. Processes can send each other these "
+"messages which tell them how to act. The functions which deal with messages "
+"are: man:msgctl[3], man:msgget[3], man:msgsnd[3] and man:msgrcv[3]. Earlier, "
+"I mentioned that there were certain sysctls you could turn on or off in "
+"order to affect the behavior of jail. One of these sysctls was "
+"`security.jail.sysvipc_allowed`. By default, this sysctl is set to 0. If it "
+"were set to 1, it would defeat the whole purpose of having a jail; "
+"privileged users from the jail would be able to affect processes outside the "
+"jailed environment. The difference between a message and a signal is that "
+"the message only consists of the signal number."
+msgstr ""
+"System V IPC основан на сообщениях. Процессы могут отправлять друг другу эти "
+"сообщения, которые указывают им, как действовать. Функции, работающие с "
+"сообщениями: man:msgctl[3], man:msgget[3], man:msgsnd[3] и man:msgrcv[3]. "
+"Ранее я упоминал, что существуют определённые sysctl, которые можно включать "
+"или выключать для изменения поведения клетки. Один из таких sysctl — "
+"`security.jail.sysvipc_allowed`. По умолчанию этот sysctl установлен в 0. "
+"Если бы он был установлен в 1, это бы свело на нет весь смысл клетки: "
+"привилегированные пользователи внутри клетки смогли бы влиять на процессы за "
+"её пределами. Разница между сообщением и сигналом заключается в том, что "
+"сообщение состоит только из номера сигнала."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:342
+msgid "[.filename]#/usr/src/sys/kern/sysv_msg.c#:"
+msgstr "[.filename]#/usr/src/sys/kern/sysv_msg.c#:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:344
+msgid ""
+"`msgget(key, msgflg)`: `msgget` returns (and possibly creates) a message "
+"descriptor that designates a message queue for use in other functions."
+msgstr ""
+"`msgget(key, msgflg)`: `msgget` возвращает (и, возможно, создаёт) дескриптор "
+"сообщения, который обозначает очередь сообщений для использования в других "
+"функциях."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:345
+msgid ""
+"`msgctl(msgid, cmd, buf)`: Using this function, a process can query the "
+"status of a message descriptor."
+msgstr ""
+"`msgctl(msgid, cmd, buf)`: С помощью этой функции процесс может запросить "
+"статус дескриптора сообщения."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:346
+msgid ""
+"`msgsnd(msgid, msgp, msgsz, msgflg)`: `msgsnd` sends a message to a process."
+msgstr ""
+"`msgsnd(msgid, msgp, msgsz, msgflg)`: `msgsnd` отправляет сообщение процессу."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:347
+msgid ""
+"`msgrcv(msgid, msgp, msgsz, msgtyp, msgflg)`: a process receives messages "
+"using this function"
+msgstr ""
+"`msgrcv(msgid, msgp, msgsz, msgtyp, msgflg)`: процесс получает сообщения с "
+"помощью этой функции"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:349
+msgid ""
+"In each of the system calls corresponding to these functions, there is this "
+"conditional:"
+msgstr ""
+"В каждом из системных вызовов, соответствующих этим функциям, присутствует "
+"следующее условие:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:355
+#, no-wrap
+msgid ""
+"/usr/src/sys/kern/sysv_msg.c:\n"
+"if (!jail_sysvipc_allowed && jailed(td->td_ucred))\n"
+" return (ENOSYS);\n"
+msgstr ""
+"/usr/src/sys/kern/sysv_msg.c:\n"
+"if (!jail_sysvipc_allowed && jailed(td->td_ucred))\n"
+" return (ENOSYS);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:358
+msgid ""
+"Semaphore system calls allow processes to synchronize execution by doing a "
+"set of operations atomically on a set of semaphores. Basically semaphores "
+"provide another way for processes lock resources. However, process waiting "
+"on a semaphore, that is being used, will sleep until the resources are "
+"relinquished. The following semaphore system calls are blocked inside a "
+"jail: man:semget[2], man:semctl[2] and man:semop[2]."
+msgstr ""
+"Системные вызовы семафоров позволяют процессам синхронизировать выполнение, "
+"атомарно выполняя набор операций над набором семафоров. По сути, семафоры "
+"предоставляют ещё один способ для процессов блокировать ресурсы. Однако "
+"процесс, ожидающий семафор, который уже используется, будет находиться в "
+"состоянии сна до тех пор, пока ресурсы не будут освобождены. Следующие "
+"системные вызовы семафоров блокируются внутри клетки: man:semget[2], "
+"man:semctl[2] и man:semop[2]."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:360
+msgid "[.filename]#/usr/src/sys/kern/sysv_sem.c#:"
+msgstr "[.filename]#/usr/src/sys/kern/sysv_sem.c#:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:362
+msgid ""
+"`semctl(semid, semnum, cmd, ...)`: `semctl` does the specified `cmd` on the "
+"semaphore queue indicated by `semid`."
+msgstr ""
+"`semctl(semid, semnum, cmd, ...)`: `semctl` выполняет указанную команду "
+"`cmd` для очереди семафоров, указанной в `semid`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:363
+msgid ""
+"`semget(key, nsems, flag)`: `semget` creates an array of semaphores, "
+"corresponding to `key`."
+msgstr ""
+"`semget(key, nsems, flag)`: `semget` создает массив семафоров, "
+"соответствующих `key`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:365
+msgid "`key and flag take on the same meaning as they do in msgget.`"
+msgstr "`key и flag имеют то же значение, как и в msgget.`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:366
+msgid ""
+"`semop(semid, array, nops)`: `semop` performs a group of operations "
+"indicated by `array`, to the set of semaphores identified by `semid`."
+msgstr ""
+"`semop(semid, array, nops)`: `semop` выполняет набор операций, указанных в "
+"`array`, для набора семафоров, идентифицируемых `semid`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:368
+msgid ""
+"System V IPC allows for processes to share memory. Processes can communicate "
+"directly with each other by sharing parts of their virtual address space and "
+"then reading and writing data stored in the shared memory. These system "
+"calls are blocked within a jailed environment: man:shmdt[2], man:shmat[2], "
+"man:shmctl[2] and man:shmget[2]."
+msgstr ""
+"Система IPC System V позволяет процессам использовать общую память. Процессы "
+"могут взаимодействовать напрямую друг с другом, разделяя части своего "
+"виртуального адресного пространства и затем читая и записывая данные в общей "
+"памяти. Эти системные вызовы заблокированы в среде _клетки_: man:shmdt[2], "
+"man:shmat[2], man:shmctl[2] и man:shmget[2]."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:370
+msgid "[.filename]#/usr/src/sys/kern/sysv_shm.c#:"
+msgstr "[.filename]#/usr/src/sys/kern/sysv_shm.c#:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:372
+msgid ""
+"`shmctl(shmid, cmd, buf)`: `shmctl` does various control operations on the "
+"shared memory region identified by `shmid`."
+msgstr ""
+"`shmctl(shmid, cmd, buf)`: `shmctl` выполняет различные управляющие операции "
+"над областью разделяемой памяти, идентифицируемой `shmid`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:373
+msgid ""
+"`shmget(key, size, flag)`: `shmget` accesses or creates a shared memory "
+"region of `size` bytes."
+msgstr ""
+"`shmget(key, size, flag)`: `shmget` обращается к существующей или создает "
+"новую область разделяемой памяти размером `size` байт."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:374
+msgid ""
+"`shmat(shmid, addr, flag)`: `shmat` attaches a shared memory region "
+"identified by `shmid` to the address space of a process."
+msgstr ""
+"`shmat(shmid, addr, flag)`: `shmat` присоединяет область разделяемой памяти, "
+"идентифицируемую `shmid`, к адресному пространству процесса."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:375
+msgid ""
+"`shmdt(addr)`: `shmdt` detaches the shared memory region previously attached "
+"at `addr`."
+msgstr ""
+"`shmdt(addr)`: `shmdt` отсоединяет ранее присоединенную область разделяемой "
+"памяти по адресу `addr`."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:376
+#, no-wrap
+msgid "Sockets"
+msgstr "Сокеты"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:379
+msgid ""
+"Jail treats the man:socket[2] system call and related lower-level socket "
+"functions in a special manner. In order to determine whether a certain "
+"socket is allowed to be created, it first checks to see if the sysctl "
+"`security.jail.socket_unixiproute_only` is set. If set, sockets are only "
+"allowed to be created if the family specified is either `PF_LOCAL`, "
+"`PF_INET` or `PF_ROUTE`. Otherwise, it returns an error."
+msgstr ""
+"Клетка обрабатывает системный вызов man:socket[2] и связанные низкоуровневые "
+"функции сокетов особым образом. Для определения, разрешено ли создание "
+"определённого сокета, сначала проверяется значение sysctl "
+"`security.jail.socket_unixiproute_only`. Если оно установлено, сокеты "
+"разрешено создавать только в случае, если указанное семейство равно "
+"`PF_LOCAL`, `PF_INET` или `PF_ROUTE`. В противном случае возвращается ошибка."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:397
+#, no-wrap
+msgid ""
+"/usr/src/sys/kern/uipc_socket.c:\n"
+"int\n"
+"socreate(int dom, struct socket **aso, int type, int proto,\n"
+" struct ucred *cred, struct thread *td)\n"
+"{\n"
+" struct protosw *prp;\n"
+"...\n"
+" if (jailed(cred) && jail_socket_unixiproute_only &&\n"
+" prp->pr_domain->dom_family != PF_LOCAL &&\n"
+" prp->pr_domain->dom_family != PF_INET &&\n"
+" prp->pr_domain->dom_family != PF_ROUTE) {\n"
+" return (EPROTONOSUPPORT);\n"
+" }\n"
+"...\n"
+"}\n"
+msgstr ""
+"/usr/src/sys/kern/uipc_socket.c:\n"
+"int\n"
+"socreate(int dom, struct socket **aso, int type, int proto,\n"
+" struct ucred *cred, struct thread *td)\n"
+"{\n"
+" struct protosw *prp;\n"
+"...\n"
+" if (jailed(cred) && jail_socket_unixiproute_only &&\n"
+" prp->pr_domain->dom_family != PF_LOCAL &&\n"
+" prp->pr_domain->dom_family != PF_INET &&\n"
+" prp->pr_domain->dom_family != PF_ROUTE) {\n"
+" return (EPROTONOSUPPORT);\n"
+" }\n"
+"...\n"
+"}\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:399
+#, no-wrap
+msgid "Berkeley Packet Filter"
+msgstr "Berkeley Packet Filter"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:402
+msgid ""
+"The Berkeley Packet Filter provides a raw interface to data link layers in a "
+"protocol independent fashion. BPF is now controlled by the man:devfs[8] "
+"whether it can be used in a jailed environment."
+msgstr ""
+"Берклиевский фильтр пакетов (BPF) предоставляет низкоуровневый интерфейс к "
+"канальному уровню, независимый от протокола. В настоящее время BPF "
+"управляется через man:devfs[8], который определяет возможность его "
+"использования в клетке."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:403
+#, no-wrap
+msgid "Protocols"
+msgstr "Протоколы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:406
+msgid ""
+"There are certain protocols which are very common, such as TCP, UDP, IP and "
+"ICMP. IP and ICMP are on the same level: the network layer 2. There are "
+"certain precautions which are taken in order to prevent a jailed process "
+"from binding a protocol to a certain address only if the `nam` parameter is "
+"set. `nam` is a pointer to a `sockaddr` structure, which describes the "
+"address on which to bind the service. A more exact definition is that "
+"`sockaddr` \"may be used as a template for referring to the identifying tag "
+"and length of each address\". In the function `in_pcbbind_setup()`, `sin` is "
+"a pointer to a `sockaddr_in` structure, which contains the port, address, "
+"length and domain family of the socket which is to be bound. Basically, this "
+"disallows any processes from jail to be able to specify the address that "
+"does not belong to the jail in which the calling process exists."
+msgstr ""
+"Существуют определенные протоколы, которые очень распространены, такие как "
+"TCP, UDP, IP и ICMP. IP и ICMP находятся на одном уровне: сетевом уровне 2. "
+"Принимаются определенные меры предосторожности, чтобы предотвратить привязку "
+"протокола к определенному адресу процессом в клетке, только если установлен "
+"параметр `nam`. `nam` является указателем на структуру `sockaddr`, которая "
+"описывает адрес, к которому привязывается служба. Более точное определение "
+"заключается в том, что `sockaddr` \"может использоваться как шаблон для "
+"ссылки на идентификационный тег и длину каждого адреса\". В функции "
+"`in_pcbbind_setup()`, `sin` — это указатель на структуру `sockaddr_in`, "
+"которая содержит порт, адрес, длину и семейство доменов сокета, который "
+"должен быть привязан. В основном, это запрещает любым процессам из клетки "
+"указывать адрес, который не принадлежит клетке, в которой существует "
+"вызывающий процесс."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:443
+#, no-wrap
+msgid ""
+"/usr/src/sys/netinet/in_pcb.c:\n"
+"int\n"
+"in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,\n"
+" u_short *lportp, struct ucred *cred)\n"
+"{\n"
+" ...\n"
+" struct sockaddr_in *sin;\n"
+" ...\n"
+" if (nam) {\n"
+" sin = (struct sockaddr_in *)nam;\n"
+" ...\n"
+" if (sin->sin_addr.s_addr != INADDR_ANY)\n"
+" if (prison_ip(cred, 0, &sin->sin_addr.s_addr))\n"
+" return(EINVAL);\n"
+" ...\n"
+" if (lport) {\n"
+" ...\n"
+" if (prison && prison_ip(cred, 0, &sin->sin_addr.s_addr))\n"
+" return (EADDRNOTAVAIL);\n"
+" ...\n"
+" }\n"
+" }\n"
+" if (lport == 0) {\n"
+" ...\n"
+" if (laddr.s_addr != INADDR_ANY)\n"
+" if (prison_ip(cred, 0, &laddr.s_addr))\n"
+" return (EINVAL);\n"
+" ...\n"
+" }\n"
+"...\n"
+" if (prison_ip(cred, 0, &laddr.s_addr))\n"
+" return (EINVAL);\n"
+"...\n"
+"}\n"
+msgstr ""
+"/usr/src/sys/netinet/in_pcb.c:\n"
+"int\n"
+"in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,\n"
+" u_short *lportp, struct ucred *cred)\n"
+"{\n"
+" ...\n"
+" struct sockaddr_in *sin;\n"
+" ...\n"
+" if (nam) {\n"
+" sin = (struct sockaddr_in *)nam;\n"
+" ...\n"
+" if (sin->sin_addr.s_addr != INADDR_ANY)\n"
+" if (prison_ip(cred, 0, &sin->sin_addr.s_addr))\n"
+" return(EINVAL);\n"
+" ...\n"
+" if (lport) {\n"
+" ...\n"
+" if (prison && prison_ip(cred, 0, &sin->sin_addr.s_addr))\n"
+" return (EADDRNOTAVAIL);\n"
+" ...\n"
+" }\n"
+" }\n"
+" if (lport == 0) {\n"
+" ...\n"
+" if (laddr.s_addr != INADDR_ANY)\n"
+" if (prison_ip(cred, 0, &laddr.s_addr))\n"
+" return (EINVAL);\n"
+" ...\n"
+" }\n"
+"...\n"
+" if (prison_ip(cred, 0, &laddr.s_addr))\n"
+" return (EINVAL);\n"
+"...\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:446
+msgid ""
+"You might be wondering what function `prison_ip()` does. `prison_ip()` is "
+"given three arguments, a pointer to the credential(represented by `cred`), "
+"any flags, and an IP address. It returns 1 if the IP address does NOT belong "
+"to the jail or 0 otherwise. As you can see from the code, if it is indeed an "
+"IP address not belonging to the jail, the protocol is not allowed to bind to "
+"that address."
+msgstr ""
+"Вы можете задаться вопросом, какую функцию выполняет `prison_ip()`. "
+"`prison_ip()` принимает три аргумента: указатель на учетные данные "
+"(представленные как `cred`), любые флаги и IP-адрес. Она возвращает 1, если "
+"IP-адрес НЕ принадлежит клетке, и 0 в противном случае. Как видно из кода, "
+"если это действительно IP-адрес, не принадлежащий клетке, протоколу не "
+"разрешается привязываться к этому адресу."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:454
+#, no-wrap
+msgid ""
+"/usr/src/sys/kern/kern_jail.c:\n"
+"int\n"
+"prison_ip(struct ucred *cred, int flag, u_int32_t *ip)\n"
+"{\n"
+" u_int32_t tmp;\n"
+msgstr ""
+"/usr/src/sys/kern/kern_jail.c:\n"
+"int\n"
+"prison_ip(struct ucred *cred, int flag, u_int32_t *ip)\n"
+"{\n"
+" u_int32_t tmp;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:479
+#, no-wrap
+msgid ""
+" if (!jailed(cred))\n"
+" return (0);\n"
+" if (flag)\n"
+" tmp = *ip;\n"
+" else\n"
+" tmp = ntohl(*ip);\n"
+" if (tmp == INADDR_ANY) {\n"
+" if (flag)\n"
+" *ip = cred->cr_prison->pr_ip;\n"
+" else\n"
+" *ip = htonl(cred->cr_prison->pr_ip);\n"
+" return (0);\n"
+" }\n"
+" if (tmp == INADDR_LOOPBACK) {\n"
+" if (flag)\n"
+" *ip = cred->cr_prison->pr_ip;\n"
+" else\n"
+" *ip = htonl(cred->cr_prison->pr_ip);\n"
+" return (0);\n"
+" }\n"
+" if (cred->cr_prison->pr_ip != tmp)\n"
+" return (1);\n"
+" return (0);\n"
+"}\n"
+msgstr ""
+" if (!jailed(cred))\n"
+" return (0);\n"
+" if (flag)\n"
+" tmp = *ip;\n"
+" else\n"
+" tmp = ntohl(*ip);\n"
+" if (tmp == INADDR_ANY) {\n"
+" if (flag)\n"
+" *ip = cred->cr_prison->pr_ip;\n"
+" else\n"
+" *ip = htonl(cred->cr_prison->pr_ip);\n"
+" return (0);\n"
+" }\n"
+" if (tmp == INADDR_LOOPBACK) {\n"
+" if (flag)\n"
+" *ip = cred->cr_prison->pr_ip;\n"
+" else\n"
+" *ip = htonl(cred->cr_prison->pr_ip);\n"
+" return (0);\n"
+" }\n"
+" if (cred->cr_prison->pr_ip != tmp)\n"
+" return (1);\n"
+" return (0);\n"
+"}\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:481
+#, no-wrap
+msgid "Filesystem"
+msgstr "Файловая система"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:484
+msgid ""
+"Even `root` users within the jail are not allowed to unset or modify any "
+"file flags, such as immutable, append-only, and undeleteable flags, if the "
+"securelevel is greater than 0."
+msgstr ""
+"Даже пользователи с правами `root` внутри `клетки` не могут снять или "
+"изменить любые флаги файлов, такие как неизменяемый, только для добавления и "
+"неудаляемый, если уровень безопасности (`securelevel`) больше 0."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/jail/_index.adoc:529
+#, no-wrap
+msgid ""
+"/usr/src/sys/ufs/ufs/ufs_vnops.c:\n"
+"static int\n"
+"ufs_setattr(ap)\n"
+" ...\n"
+"{\n"
+" ...\n"
+" if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) {\n"
+" if (ip->i_flags\n"
+" & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {\n"
+" error = securelevel_gt(cred, 0);\n"
+" if (error)\n"
+" return (error);\n"
+" }\n"
+" ...\n"
+" }\n"
+"}\n"
+"/usr/src/sys/kern/kern_priv.c\n"
+"int\n"
+"priv_check_cred(struct ucred *cred, int priv, int flags)\n"
+"{\n"
+" ...\n"
+" error = prison_priv_check(cred, priv);\n"
+" if (error)\n"
+" return (error);\n"
+" ...\n"
+"}\n"
+"/usr/src/sys/kern/kern_jail.c\n"
+"int\n"
+"prison_priv_check(struct ucred *cred, int priv)\n"
+"{\n"
+" ...\n"
+" switch (priv) {\n"
+" ...\n"
+" case PRIV_VFS_SYSFLAGS:\n"
+" if (jail_chflags_allowed)\n"
+" return (0);\n"
+" else\n"
+" return (EPERM);\n"
+" ...\n"
+" }\n"
+" ...\n"
+"}\n"
+msgstr ""
+"/usr/src/sys/ufs/ufs/ufs_vnops.c:\n"
+"static int\n"
+"ufs_setattr(ap)\n"
+" ...\n"
+"{\n"
+" ...\n"
+" if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) {\n"
+" if (ip->i_flags\n"
+" & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {\n"
+" error = securelevel_gt(cred, 0);\n"
+" if (error)\n"
+" return (error);\n"
+" }\n"
+" ...\n"
+" }\n"
+"}\n"
+"/usr/src/sys/kern/kern_priv.c\n"
+"int\n"
+"priv_check_cred(struct ucred *cred, int priv, int flags)\n"
+"{\n"
+" ...\n"
+" error = prison_priv_check(cred, priv);\n"
+" if (error)\n"
+" return (error);\n"
+" ...\n"
+"}\n"
+"/usr/src/sys/kern/kern_jail.c\n"
+"int\n"
+"prison_priv_check(struct ucred *cred, int priv)\n"
+"{\n"
+" ...\n"
+" switch (priv) {\n"
+" ...\n"
+" case PRIV_VFS_SYSFLAGS:\n"
+" if (jail_chflags_allowed)\n"
+" return (0);\n"
+" else\n"
+" return (EPERM);\n"
+" ...\n"
+" }\n"
+" ...\n"
+"}\n"
diff --git a/documentation/content/ru/books/arch-handbook/kobj/_index.adoc b/documentation/content/ru/books/arch-handbook/kobj/_index.adoc
new file mode 100644
index 0000000000..bcc42632da
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/kobj/_index.adoc
@@ -0,0 +1,259 @@
+---
+description: 'Объекты ядра'
+next: books/arch-handbook/jail
+params:
+ path: /books/arch-handbook/kobj/
+prev: books/arch-handbook/locking
+showBookMenu: true
+tags: ["kernel objects", "kobj", "guide", "FreeBSD"]
+title: 'Глава 3. Объекты ядра'
+weight: 4
+---
+
+[[kernel-objects]]
+= Объекты ядра
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 3
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+Объекты ядра, или _Kobj_, предоставляют объектно-ориентированную систему программирования на языке C для ядра. Таким образом, данные, с которыми производится работа, содержат описание того, как над ними следует выполнять операции. Это позволяет добавлять и удалять операции из интерфейса во время выполнения без нарушения бинарной совместимости.
+
+[[kernel-objects-term]]
+== Терминология
+
+Объект::
+Набор данных - структура данных - аллокация данных.
+
+Метод::
+Операция — функция.
+
+Класс::
+Один или несколько методов.
+
+Интерфейс::
+Стандартный набор из одного или нескольких методов.
+
+[[kernel-objects-operation]]
+== Как работает Kobj
+
+Kobj работает путем генерации описаний методов. Каждое описание содержит уникальный идентификатор, а также функцию по умолчанию. Адрес описания используется для однозначной идентификации метода в таблице методов класса.
+
+Класс создается путем построения таблицы методов, связывающей одну или несколько функций с описаниями методов. Перед использованием класс компилируется. В процессе компиляции выделяется кэш и связывается с классом. Уникальный идентификатор назначается каждому описанию метода в таблице методов класса, если это еще не было сделано другой ссылающейся компиляцией класса. Для каждого используемого метода скриптом генерируется функция для проверки аргументов и автоматического обращения к описанию метода для поиска. Сгенерированная функция ищет метод, используя уникальный идентификатор, связанный с описанием метода, в качестве хэша для доступа к кэшу, связанному с классом объекта. Если метод не найден в кэше, сгенерированная функция использует таблицу класса для поиска метода. Если метод найден, используется связанная с ним функция внутри класса; в противном случае используется функция по умолчанию, связанная с описанием метода.
+
+Эти перенаправления можно визуализировать следующим образом:
+
+[.programlisting]
+....
+object->cache<->class
+....
+
+[[kernel-objects-using]]
+== Использование Kobj
+
+=== Структуры
+
+[.programlisting]
+....
+struct kobj_method
+....
+
+=== Функции
+
+[.programlisting]
+....
+void kobj_class_compile(kobj_class_t cls);
+void kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops);
+void kobj_class_free(kobj_class_t cls);
+kobj_t kobj_create(kobj_class_t cls, struct malloc_type *mtype, int mflags);
+void kobj_init(kobj_t obj, kobj_class_t cls);
+void kobj_delete(kobj_t obj, struct malloc_type *mtype);
+....
+
+=== Макросы
+
+[.programlisting]
+....
+KOBJ_CLASS_FIELDS
+KOBJ_FIELDS
+DEFINE_CLASS(name, methods, size)
+KOBJMETHOD(NAME, FUNC)
+....
+
+=== Заголовки
+
+[.programlisting]
+....
+<sys/param.h>
+<sys/kobj.h>
+....
+
+=== Создание шаблона интерфейса
+
+Первым шагом в использовании Kobj является создание интерфейса. Создание интерфейса включает в себя создание шаблона, который скрипт [.filename]#src/sys/kern/makeobjops.pl# может использовать для генерации заголовочного файла и кода объявлений методов и функций поиска методов.
+
+В этом шаблоне используются следующие ключевые слова: `#include`, `INTERFACE`, `CODE`, `EPILOG`, `HEADER`, `METHOD`, `PROLOG`, `STATICMETHOD` и `DEFAULT`.
+
+Включение директивы `#include` и всего, что следует за ней, копируется дословно в начало сгенерированного файла с кодом.
+
+Например:
+
+[.programlisting]
+....
+#include <sys/foo.h>
+....
+
+Ключевое слово `INTERFACE` используется для определения имени интерфейса. Это имя объединяется с каждым именем метода в формате [имя интерфейса]_[имя метода]. Его синтаксис: `INTERFACE [имя интерфейса];`.
+
+Например:
+
+[.programlisting]
+....
+INTERFACE foo;
+....
+
+Ключевое слово `CODE` копирует свои аргументы дословно в файл кода. Его синтаксис: `CODE { [что угодно] };`
+
+Например:
+
+[.programlisting]
+....
+CODE {
+ struct foo * foo_alloc_null(struct bar *)
+ {
+ return NULL;
+ }
+};
+....
+
+Ключевое слово `HEADER` копирует свои аргументы в заголовочный файл без изменений. Его синтаксис: `HEADER { [что угодно] };`
+
+Например:
+
+[.programlisting]
+....
+HEADER {
+ struct mumble;
+ struct grumble;
+};
+....
+
+Ключевое слово `METHOD` описывает метод. Его синтаксис: `METHOD [возвращаемый тип] [имя метода] { [объект [, аргументы]] };`
+
+Например:
+
+[.programlisting]
+....
+METHOD int bar {
+ struct object *;
+ struct foo *;
+ struct bar;
+};
+....
+
+Ключевое слово `DEFAULT` может следовать за ключевым словом `METHOD`. Оно расширяет ключевое слово `METHOD`, включая функцию по умолчанию для метода. Расширенный синтаксис выглядит так: `METHOD [тип возвращаемого значения] [имя метода] { [объект; [другие аргументы]] } DEFAULT [функция по умолчанию];`
+
+Например:
+
+[.programlisting]
+....
+METHOD int bar {
+ struct object *;
+ struct foo *;
+ int bar;
+} DEFAULT foo_hack;
+....
+
+Ключевое слово `STATICMETHOD` используется аналогично ключевому слову `METHOD`, за исключением того, что данные kobj не находятся в начале структуры объекта, поэтому приведение к типу kobj_t было бы некорректным. Вместо этого `STATICMETHOD` полагается на то, что данные Kobj указаны как 'ops'. Это также полезно для вызова методов напрямую из таблицы методов класса.
+
+Ключевые слова `PROLOG` и `EPILOG` вставляют код непосредственно перед или сразу после `METHOD`, к которому они прикреплены. Эта функция в основном используется для профилирования в ситуациях, когда сложно получить информацию другим способом.
+
+Другие полные примеры:
+
+[.programlisting]
+....
+src/sys/kern/bus_if.m
+src/sys/kern/device_if.m
+....
+
+=== Создание класса
+
+Второй шаг в использовании Kobj — это создание класса. Класс состоит из имени, таблицы методов и размера объектов, если используются средства обработки объектов Kobj. Для создания класса используйте макрос `DEFINE_CLASS()`. Чтобы создать таблицу методов, создайте массив элементов kobj_method_t, завершающийся записью NULL. Каждую не-NULL запись можно создать с помощью макроса `KOBJMETHOD()`.
+
+Например:
+
+[.programlisting]
+....
+DEFINE_CLASS(fooclass, foomethods, sizeof(struct foodata));
+
+kobj_method_t foomethods[] = {
+ KOBJMETHOD(bar_doo, foo_doo),
+ KOBJMETHOD(bar_foo, foo_foo),
+ { NULL, NULL}
+};
+....
+
+Класс должен быть "скомпилирован". В зависимости от состояния системы на момент инициализации класса, необходимо использовать статически выделенный кэш, "таблицу операций". Это может быть достигнуто путем объявления `struct kobj_ops` и использования `kobj_class_compile_static();` в противном случае следует использовать `kobj_class_compile()`.
+
+=== Создание объекта
+
+Третий шаг в использовании Kobj связан с определением объекта. Процедуры создания объекта Kobj предполагают, что данные Kobj находятся в начале объекта. Если это не подходит, вам придется самостоятельно выделить память для объекта, а затем использовать `kobj_init()` для части объекта, относящейся к Kobj; в противном случае вы можете использовать `kobj_create()` для автоматического выделения и инициализации части объекта, относящейся к Kobj. `kobj_init()` также может использоваться для изменения класса, который использует объект.
+
+Для интеграции Kobj в объект следует использовать макрос `KOBJ_FIELDS`.
+
+Например
+
+[.programlisting]
+....
+struct foo_data {
+ KOBJ_FIELDS;
+ foo_foo;
+ foo_bar;
+};
+....
+
+=== Вызов методов
+
+Последним шагом в использовании Kobj является простое использование сгенерированных функций для вызова нужного метода в классе объекта. Это так же просто, как использование имени интерфейса и имени метода с небольшими изменениями. Имя интерфейса должно быть соединено с именем метода с использованием символа '_' между ними, все в верхнем регистре.
+
+Например, если имя интерфейса было foo, а метод — bar, то вызов будет выглядеть следующим образом:
+
+[.programlisting]
+....
+[return value = ] FOO_BAR(object [, other parameters]);
+....
+
+=== Очистка
+
+Когда объект, выделенный через `kobj_create()`, больше не нужен, можно вызвать для него `kobj_delete()`, а когда класс больше не используется, можно вызвать для него `kobj_class_free()`.
diff --git a/documentation/content/ru/books/arch-handbook/kobj/_index.po b/documentation/content/ru/books/arch-handbook/kobj/_index.po
new file mode 100644
index 0000000000..36d7ddff70
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/kobj/_index.po
@@ -0,0 +1,622 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-07-02 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookkobj_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:14
+#, no-wrap
+msgid "Kernel Objects"
+msgstr "Объекты ядра"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:1
+#, no-wrap
+msgid "Chapter 3. Kernel Objects"
+msgstr "Глава 3. Объекты ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:54
+msgid ""
+"Kernel Objects, or _Kobj_ provides an object-oriented C programming system "
+"for the kernel. As such the data being operated on carries the description "
+"of how to operate on it. This allows operations to be added and removed "
+"from an interface at run time and without breaking binary compatibility."
+msgstr ""
+"Объекты ядра, или _Kobj_, предоставляют объектно-ориентированную систему "
+"программирования на языке C для ядра. Таким образом, данные, с которыми "
+"производится работа, содержат описание того, как над ними следует выполнять "
+"операции. Это позволяет добавлять и удалять операции из интерфейса во время "
+"выполнения без нарушения бинарной совместимости."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:56
+#, no-wrap
+msgid "Terminology"
+msgstr "Терминология"
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:58
+#, no-wrap
+msgid "Object"
+msgstr "Объект"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:60
+msgid "A set of data - data structure - data allocation."
+msgstr "Набор данных - структура данных - аллокация данных."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:61
+#, no-wrap
+msgid "Method"
+msgstr "Метод"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:63
+msgid "An operation - function."
+msgstr "Операция — функция."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:64
+#, no-wrap
+msgid "Class"
+msgstr "Класс"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:66
+msgid "One or more methods."
+msgstr "Один или несколько методов."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:67
+#, no-wrap
+msgid "Interface"
+msgstr "Интерфейс"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:69
+msgid "A standard set of one or more methods."
+msgstr "Стандартный набор из одного или нескольких методов."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:71
+#, no-wrap
+msgid "Kobj Operation"
+msgstr "Как работает Kobj"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:76
+msgid ""
+"Kobj works by generating descriptions of methods. Each description holds a "
+"unique id as well as a default function. The description's address is used "
+"to uniquely identify the method within a class' method table."
+msgstr ""
+"Kobj работает путем генерации описаний методов. Каждое описание содержит "
+"уникальный идентификатор, а также функцию по умолчанию. Адрес описания "
+"используется для однозначной идентификации метода в таблице методов класса."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:85
+msgid ""
+"A class is built by creating a method table associating one or more "
+"functions with method descriptions. Before use the class is compiled. The "
+"compilation allocates a cache and associates it with the class. A unique id "
+"is assigned to each method description within the method table of the class "
+"if not already done so by another referencing class compilation. For every "
+"method to be used a function is generated by script to qualify arguments and "
+"automatically reference the method description for a lookup. The generated "
+"function looks up the method by using the unique id associated with the "
+"method description as a hash into the cache associated with the object's "
+"class. If the method is not cached the generated function proceeds to use "
+"the class' table to find the method. If the method is found then the "
+"associated function within the class is used; otherwise, the default "
+"function associated with the method description is used."
+msgstr ""
+"Класс создается путем построения таблицы методов, связывающей одну или "
+"несколько функций с описаниями методов. Перед использованием класс "
+"компилируется. В процессе компиляции выделяется кэш и связывается с классом. "
+"Уникальный идентификатор назначается каждому описанию метода в таблице "
+"методов класса, если это еще не было сделано другой ссылающейся компиляцией "
+"класса. Для каждого используемого метода скриптом генерируется функция для "
+"проверки аргументов и автоматического обращения к описанию метода для "
+"поиска. Сгенерированная функция ищет метод, используя уникальный "
+"идентификатор, связанный с описанием метода, в качестве хэша для доступа к "
+"кэшу, связанному с классом объекта. Если метод не найден в кэше, "
+"сгенерированная функция использует таблицу класса для поиска метода. Если "
+"метод найден, используется связанная с ним функция внутри класса; в "
+"противном случае используется функция по умолчанию, связанная с описанием "
+"метода."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:87
+msgid "These indirections can be visualized as the following:"
+msgstr "Эти перенаправления можно визуализировать следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:91
+#, no-wrap
+msgid "object->cache<->class\n"
+msgstr "object->cache<->class\n"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:94
+#, no-wrap
+msgid "Using Kobj"
+msgstr "Использование Kobj"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:96
+#, no-wrap
+msgid "Structures"
+msgstr "Структуры"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:101
+#, no-wrap
+msgid "struct kobj_method\n"
+msgstr "struct kobj_method\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:103
+#, no-wrap
+msgid "Functions"
+msgstr "Функции"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:113
+#, no-wrap
+msgid ""
+"void kobj_class_compile(kobj_class_t cls);\n"
+"void kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops);\n"
+"void kobj_class_free(kobj_class_t cls);\n"
+"kobj_t kobj_create(kobj_class_t cls, struct malloc_type *mtype, int mflags);\n"
+"void kobj_init(kobj_t obj, kobj_class_t cls);\n"
+"void kobj_delete(kobj_t obj, struct malloc_type *mtype);\n"
+msgstr ""
+"void kobj_class_compile(kobj_class_t cls);\n"
+"void kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops);\n"
+"void kobj_class_free(kobj_class_t cls);\n"
+"kobj_t kobj_create(kobj_class_t cls, struct malloc_type *mtype, int mflags);\n"
+"void kobj_init(kobj_t obj, kobj_class_t cls);\n"
+"void kobj_delete(kobj_t obj, struct malloc_type *mtype);\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:115
+#, no-wrap
+msgid "Macros"
+msgstr "Макросы"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:123
+#, no-wrap
+msgid ""
+"KOBJ_CLASS_FIELDS\n"
+"KOBJ_FIELDS\n"
+"DEFINE_CLASS(name, methods, size)\n"
+"KOBJMETHOD(NAME, FUNC)\n"
+msgstr ""
+"KOBJ_CLASS_FIELDS\n"
+"KOBJ_FIELDS\n"
+"DEFINE_CLASS(name, methods, size)\n"
+"KOBJMETHOD(NAME, FUNC)\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:125
+#, no-wrap
+msgid "Headers"
+msgstr "Заголовки"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:131
+#, no-wrap
+msgid ""
+"<sys/param.h>\n"
+"<sys/kobj.h>\n"
+msgstr ""
+"<sys/param.h>\n"
+"<sys/kobj.h>\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:133
+#, no-wrap
+msgid "Creating an Interface Template"
+msgstr "Создание шаблона интерфейса"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:137
+msgid ""
+"The first step in using Kobj is to create an Interface. Creating the "
+"interface involves creating a template that the script [.filename]#src/sys/"
+"kern/makeobjops.pl# can use to generate the header and code for the method "
+"declarations and method lookup functions."
+msgstr ""
+"Первым шагом в использовании Kobj является создание интерфейса. Создание "
+"интерфейса включает в себя создание шаблона, который скрипт [.filename]#src/"
+"sys/kern/makeobjops.pl# может использовать для генерации заголовочного файла "
+"и кода объявлений методов и функций поиска методов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:139
+msgid ""
+"Within this template the following keywords are used: `#include`, "
+"`INTERFACE`, `CODE`, `EPILOG`, `HEADER`, `METHOD`, `PROLOG`, `STATICMETHOD`, "
+"and `DEFAULT`."
+msgstr ""
+"В этом шаблоне используются следующие ключевые слова: `#include`, "
+"`INTERFACE`, `CODE`, `EPILOG`, `HEADER`, `METHOD`, `PROLOG`, `STATICMETHOD` "
+"и `DEFAULT`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:141
+msgid ""
+"The `#include` statement and what follows it is copied verbatim to the head "
+"of the generated code file."
+msgstr ""
+"Включение директивы `#include` и всего, что следует за ней, копируется "
+"дословно в начало сгенерированного файла с кодом."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:143
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:154
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:164
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:179
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:192
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:207
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:241
+msgid "For example:"
+msgstr "Например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:147
+#, no-wrap
+msgid "#include <sys/foo.h>\n"
+msgstr "#include <sys/foo.h>\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:152
+msgid ""
+"The `INTERFACE` keyword is used to define the interface name. This name is "
+"concatenated with each method name as [interface name]_[method name]. Its "
+"syntax is INTERFACE [interface name];."
+msgstr ""
+"Ключевое слово `INTERFACE` используется для определения имени интерфейса. "
+"Это имя объединяется с каждым именем метода в формате [имя интерфейса]_[имя "
+"метода]. Его синтаксис: `INTERFACE [имя интерфейса];`."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:158
+#, no-wrap
+msgid "INTERFACE foo;\n"
+msgstr "INTERFACE foo;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:162
+msgid ""
+"The `CODE` keyword copies its arguments verbatim into the code file. Its "
+"syntax is `CODE { [whatever] };`"
+msgstr ""
+"Ключевое слово `CODE` копирует свои аргументы дословно в файл кода. Его "
+"синтаксис: `CODE { [что угодно] };`"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:173
+#, no-wrap
+msgid ""
+"CODE {\n"
+"\tstruct foo * foo_alloc_null(struct bar *)\n"
+"\t{\n"
+"\t\treturn NULL;\n"
+"\t}\n"
+"};\n"
+msgstr ""
+"CODE {\n"
+"\tstruct foo * foo_alloc_null(struct bar *)\n"
+"\t{\n"
+"\t\treturn NULL;\n"
+"\t}\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:177
+msgid ""
+"The `HEADER` keyword copies its arguments verbatim into the header file. "
+"Its syntax is `HEADER { [whatever] };`"
+msgstr ""
+"Ключевое слово `HEADER` копирует свои аргументы в заголовочный файл без "
+"изменений. Его синтаксис: `HEADER { [что угодно] };`"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:186
+#, no-wrap
+msgid ""
+"HEADER {\n"
+" struct mumble;\n"
+" struct grumble;\n"
+"};\n"
+msgstr ""
+"HEADER {\n"
+" struct mumble;\n"
+" struct grumble;\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:190
+msgid ""
+"The `METHOD` keyword describes a method. Its syntax is `METHOD [return "
+"type] [method name] { [object [, arguments]] };`"
+msgstr ""
+"Ключевое слово `METHOD` описывает метод. Его синтаксис: `METHOD "
+"[возвращаемый тип] [имя метода] { [объект [, аргументы]] };`"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:200
+#, no-wrap
+msgid ""
+"METHOD int bar {\n"
+"\tstruct object *;\n"
+"\tstruct foo *;\n"
+"\tstruct bar;\n"
+"};\n"
+msgstr ""
+"METHOD int bar {\n"
+"\tstruct object *;\n"
+"\tstruct foo *;\n"
+"\tstruct bar;\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:205
+msgid ""
+"The `DEFAULT` keyword may follow the `METHOD` keyword. It extends the "
+"`METHOD` key word to include the default function for method. The extended "
+"syntax is `METHOD [return type] [method name] { [object; [other arguments]] }"
+"DEFAULT [default function];`"
+msgstr ""
+"Ключевое слово `DEFAULT` может следовать за ключевым словом `METHOD`. Оно "
+"расширяет ключевое слово `METHOD`, включая функцию по умолчанию для метода. "
+"Расширенный синтаксис выглядит так: `METHOD [тип возвращаемого значения] "
+"[имя метода] { [объект; [другие аргументы]] } DEFAULT [функция по "
+"умолчанию];`"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:215
+#, no-wrap
+msgid ""
+"METHOD int bar {\n"
+"\tstruct object *;\n"
+"\tstruct foo *;\n"
+"\tint bar;\n"
+"} DEFAULT foo_hack;\n"
+msgstr ""
+"METHOD int bar {\n"
+"\tstruct object *;\n"
+"\tstruct foo *;\n"
+"\tint bar;\n"
+"} DEFAULT foo_hack;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:220
+msgid ""
+"The `STATICMETHOD` keyword is used like the `METHOD` keyword except the kobj "
+"data is not at the head of the object structure so casting to kobj_t would "
+"be incorrect. Instead `STATICMETHOD` relies on the Kobj data being "
+"referenced as 'ops'. This is also useful for calling methods directly out "
+"of a class's method table."
+msgstr ""
+"Ключевое слово `STATICMETHOD` используется аналогично ключевому слову "
+"`METHOD`, за исключением того, что данные kobj не находятся в начале "
+"структуры объекта, поэтому приведение к типу kobj_t было бы некорректным. "
+"Вместо этого `STATICMETHOD` полагается на то, что данные Kobj указаны как "
+"'ops'. Это также полезно для вызова методов напрямую из таблицы методов "
+"класса."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:223
+msgid ""
+"The `PROLOG` and `EPILOG` keywords sets inserts code immediately before or "
+"directly after the `METHOD` they are attached to. This feature is used "
+"primarily for profiling situations where it's difficult to obtain the "
+"information in another way."
+msgstr ""
+"Ключевые слова `PROLOG` и `EPILOG` вставляют код непосредственно перед или "
+"сразу после `METHOD`, к которому они прикреплены. Эта функция в основном "
+"используется для профилирования в ситуациях, когда сложно получить "
+"информацию другим способом."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:225
+msgid "Other complete examples:"
+msgstr "Другие полные примеры:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:230
+#, no-wrap
+msgid ""
+"src/sys/kern/bus_if.m\n"
+"src/sys/kern/device_if.m\n"
+msgstr ""
+"src/sys/kern/bus_if.m\n"
+"src/sys/kern/device_if.m\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:232
+#, no-wrap
+msgid "Creating a Class"
+msgstr "Создание класса"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:239
+msgid ""
+"The second step in using Kobj is to create a class. A class consists of a "
+"name, a table of methods, and the size of objects if Kobj's object handling "
+"facilities are used. To create the class use the macro `DEFINE_CLASS()`. "
+"To create the method table create an array of kobj_method_t terminated by a "
+"NULL entry. Each non-NULL entry may be created using the macro "
+"`KOBJMETHOD()`."
+msgstr ""
+"Второй шаг в использовании Kobj — это создание класса. Класс состоит из "
+"имени, таблицы методов и размера объектов, если используются средства "
+"обработки объектов Kobj. Для создания класса используйте макрос "
+"`DEFINE_CLASS()`. Чтобы создать таблицу методов, создайте массив элементов "
+"kobj_method_t, завершающийся записью NULL. Каждую не-NULL запись можно "
+"создать с помощью макроса `KOBJMETHOD()`."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:245
+#, no-wrap
+msgid "DEFINE_CLASS(fooclass, foomethods, sizeof(struct foodata));\n"
+msgstr "DEFINE_CLASS(fooclass, foomethods, sizeof(struct foodata));\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:251
+#, no-wrap
+msgid ""
+"kobj_method_t foomethods[] = {\n"
+"\tKOBJMETHOD(bar_doo, foo_doo),\n"
+"\tKOBJMETHOD(bar_foo, foo_foo),\n"
+"\t{ NULL, NULL}\n"
+"};\n"
+msgstr ""
+"kobj_method_t foomethods[] = {\n"
+"\tKOBJMETHOD(bar_doo, foo_doo),\n"
+"\tKOBJMETHOD(bar_foo, foo_foo),\n"
+"\t{ NULL, NULL}\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:256
+msgid ""
+"The class must be \"compiled\". Depending on the state of the system at the "
+"time that the class is to be initialized a statically allocated cache, \"ops "
+"table\" have to be used. This can be accomplished by declaring a `struct "
+"kobj_ops` and using `kobj_class_compile_static();` otherwise, "
+"`kobj_class_compile()` should be used."
+msgstr ""
+"Класс должен быть \"скомпилирован\". В зависимости от состояния системы на "
+"момент инициализации класса, необходимо использовать статически выделенный "
+"кэш, \"таблицу операций\". Это может быть достигнуто путем объявления "
+"`struct kobj_ops` и использования `kobj_class_compile_static();` в противном "
+"случае следует использовать `kobj_class_compile()`."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:257
+#, no-wrap
+msgid "Creating an Object"
+msgstr "Создание объекта"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:263
+msgid ""
+"The third step in using Kobj involves how to define the object. Kobj object "
+"creation routines assume that Kobj data is at the head of an object. If "
+"this in not appropriate you will have to allocate the object yourself and "
+"then use `kobj_init()` on the Kobj portion of it; otherwise, you may use "
+"`kobj_create()` to allocate and initialize the Kobj portion of the object "
+"automatically. `kobj_init()` may also be used to change the class that an "
+"object uses."
+msgstr ""
+"Третий шаг в использовании Kobj связан с определением объекта. Процедуры "
+"создания объекта Kobj предполагают, что данные Kobj находятся в начале "
+"объекта. Если это не подходит, вам придется самостоятельно выделить память "
+"для объекта, а затем использовать `kobj_init()` для части объекта, "
+"относящейся к Kobj; в противном случае вы можете использовать "
+"`kobj_create()` для автоматического выделения и инициализации части объекта, "
+"относящейся к Kobj. `kobj_init()` также может использоваться для изменения "
+"класса, который использует объект."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:265
+msgid "To integrate Kobj into the object you should use the macro KOBJ_FIELDS."
+msgstr ""
+"Для интеграции Kobj в объект следует использовать макрос `KOBJ_FIELDS`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:267
+msgid "For example"
+msgstr "Например"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:275
+#, no-wrap
+msgid ""
+"struct foo_data {\n"
+"\tKOBJ_FIELDS;\n"
+"\tfoo_foo;\n"
+"\tfoo_bar;\n"
+"};\n"
+msgstr ""
+"struct foo_data {\n"
+"\tKOBJ_FIELDS;\n"
+"\tfoo_foo;\n"
+"\tfoo_bar;\n"
+"};\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:277
+#, no-wrap
+msgid "Calling Methods"
+msgstr "Вызов методов"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:282
+msgid ""
+"The last step in using Kobj is to simply use the generated functions to use "
+"the desired method within the object's class. This is as simple as using "
+"the interface name and the method name with a few modifications. The "
+"interface name should be concatenated with the method name using a '_' "
+"between them, all in upper case."
+msgstr ""
+"Последним шагом в использовании Kobj является простое использование "
+"сгенерированных функций для вызова нужного метода в классе объекта. Это так "
+"же просто, как использование имени интерфейса и имени метода с небольшими "
+"изменениями. Имя интерфейса должно быть соединено с именем метода с "
+"использованием символа '_' между ними, все в верхнем регистре."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:284
+msgid ""
+"For example, if the interface name was foo and the method was bar then the "
+"call would be:"
+msgstr ""
+"Например, если имя интерфейса было foo, а метод — bar, то вызов будет "
+"выглядеть следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:288
+#, no-wrap
+msgid "[return value = ] FOO_BAR(object [, other parameters]);\n"
+msgstr "[return value = ] FOO_BAR(object [, other parameters]);\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:290
+#, no-wrap
+msgid "Cleaning Up"
+msgstr "Очистка"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/kobj/_index.adoc:292
+msgid ""
+"When an object allocated through `kobj_create()` is no longer needed "
+"`kobj_delete()` may be called on it, and when a class is no longer being "
+"used `kobj_class_free()` may be called on it."
+msgstr ""
+"Когда объект, выделенный через `kobj_create()`, больше не нужен, можно "
+"вызвать для него `kobj_delete()`, а когда класс больше не используется, "
+"можно вызвать для него `kobj_class_free()`."
diff --git a/documentation/content/ru/books/arch-handbook/locking/_index.adoc b/documentation/content/ru/books/arch-handbook/locking/_index.adoc
new file mode 100644
index 0000000000..b55e13d0a0
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/locking/_index.adoc
@@ -0,0 +1,145 @@
+---
+description: 'Заметки о блокировках'
+next: books/arch-handbook/kobj
+params:
+ path: /books/arch-handbook/locking/
+prev: books/arch-handbook/boot
+showBookMenu: true
+tags: ["locking", "notes", "SMP", "Mutexes"]
+title: 'Глава 2. Заметки о блокировках'
+weight: 3
+---
+
+[[locking]]
+= Заметки о блокировках
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 2
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+_Эта глава сопровождается и поддерживается проектом FreeBSD SMP Next Generation._
+
+Этот документ описывает механизмы блокировки, используемые в ядре FreeBSD для эффективной многопроцессорной обработки. Блокировка может быть достигнута несколькими способами. Структуры данных могут защищаться мьютексами или блокировками (lock) из man:lockmgr[9]. Некоторые переменные защищаются просто за счёт использования атомарных операций для доступа к ним.
+
+[[locking-mutexes]]
+== Mutexes
+
+Мьютекс — это просто блокировка, используемая для обеспечения взаимного исключения. Конкретно, мьютекс может принадлежать только одному объекту в один момент времени. Если другой объект хочет получить мьютекс, который уже принадлежит кому-то, он должен ждать, пока мьютекс не будет освобожден. В ядре FreeBSD мьютексы принадлежат процессам.
+
+Мьютексы могут быть получены рекурсивно, но предполагается, что они удерживаются в течение короткого периода времени. В частности, нельзя переходить в режим сна, удерживая мьютекс. Если необходимо удерживать блокировку во время сна, используйте блокировку man:lockmgr[9].
+
+Каждый мьютекс обладает несколькими важными свойствами:
+
+Имя переменной::
+Имя переменной struct mtx в исходном коде ядра.
+
+Логическое имя::
+Имя мьютекса, назначенное ему с помощью `mtx_init`. Это имя отображается в сообщениях трассировки KTR, ошибках и предупреждениях witness, а также используется для различения мьютексов в коде witness.
+
+Тип::
+Тип мьютекса в терминах флагов `MTX_*`. Значение каждого флага связано с его значением, как описано в man:mutex[9].
+
+`MTX_DEF`:::
+Мьютекс блокировки с ожиданием
+
+`MTX_SPIN`:::
+Мьютекс с вращающейся блокировкой (spin mutex)
+
+`MTX_RECURSE`:::
+Этот мьютекс допускает рекурсию.
+
+Защищаемые системы::
+Список структур данных или членов структур данных, которые защищает эта запись. Для членов структур данных имя будет указано в формате `имя структуры`.`имя члена`.
+
+Зависимые функции::
+Функции, которые могут быть вызваны только при удержании этого мьютекса.
+
+.Список мьютексов
+[cols="15%,10%,10%,55%,20%", frame="all", options="header"]
+|===
+| Имя переменной
+| Логическое имя
+| Тип
+| Защищаемые системы
+| Зависимые функции
+
+|sched_lock
+|"sched lock"
+|`MTX_SPIN` \| `MTX_RECURSE`
+|`_gmonparam`, `cnt.v_swtch`, `cp_time`, `curpriority`, `mtx`.`mtx_blocked`, `mtx`.`mtx_contested`, `proc`.`p_procq`, `proc`.`p_slpq`, `proc`.`p_sflag`, `proc`.`p_stat`, `proc`.`p_estcpu`, `proc`.`p_cpticks` `proc`.`p_pctcpu`, `proc`.`p_wchan`, `proc`.`p_wmesg`, `proc`.`p_swtime`, `proc`.`p_slptime`, `proc`.`p_runtime`, `proc`.`p_uu`, `proc`.`p_su`, `proc`.`p_iu`, `proc`.`p_uticks`, `proc`.`p_sticks`, `proc`.`p_iticks`, `proc`.`p_oncpu`, `proc`.`p_lastcpu`, `proc`.`p_rqindex`, `proc`.`p_heldmtx`, `proc`.`p_blocked`, `proc`.`p_mtxname`, `proc`.`p_contested`, `proc`.`p_priority`, `proc`.`p_usrpri`, `proc`.`p_nativepri`, `proc`.`p_nice`, `proc`.`p_rtprio`, `pscnt`, `slpque`, `itqueuebits`, `itqueues`, `rtqueuebits`, `rtqueues`, `queuebits`, `queues`, `idqueuebits`, `idqueues`, `switchtime`, `switchticks`
+|`setrunqueue`, `remrunqueue`, `mi_switch`, `chooseproc`, `schedclock`, `resetpriority`, `updatepri`, `maybe_resched`, `cpu_switch`, `cpu_throw`, `need_resched`, `resched_wanted`, `clear_resched`, `aston`, `astoff`, `astpending`, `calcru`, `proc_compare`
+
+|vm86pcb_lock
+|"vm86pcb lock"
+|`MTX_DEF`
+|`vm86pcb`
+|`vm86_bioscall`
+
+|Giant
+|"Giant"
+|`MTX_DEF` \| `MTX_RECURSE`
+|почти всё
+|много
+
+|callout_lock
+|"callout lock"
+|`MTX_SPIN` \| `MTX_RECURSE`
+|`callfree`, `callwheel`, `nextsoftcheck`, `proc`.`p_itcallout`, `proc`.`p_slpcallout`, `softticks`, `ticks`
+|
+|===
+
+[[locking-sx]]
+== Разделяемые эксклюзивные блокировки
+
+Эти блокировки обеспечивают базовую функциональность типа читатель-писатель и могут удерживаться спящим процессом. В настоящее время они реализованы через man:lockmgr[9].
+
+.Список разделяемых эксклюзивных блокировок
+[cols="20%,80%", options="header"]
+|===
+| Имя переменной
+| Защищаемые системы
+
+|`allproc_lock`
+|`allproc` `zombproc` `pidhashtbl` `proc`.`p_list` `proc`.`p_hash` `nextpid`
+
+|`proctree_lock`
+|`proc`.`p_children` `proc`.`p_sibling`
+|===
+
+[[locking-atomic]]
+== Атомарно защищённые переменные
+
+Переменная с атомарной защитой — это специальная переменная, которая не защищена явной блокировкой. Вместо этого все операции доступа к данным этой переменной используют специальные атомарные операции, как описано в man:atomic[9]. Очень немногие переменные обрабатываются таким образом, хотя другие примитивы синхронизации, такие как мьютексы, реализованы с использованием переменных с атомарной защитой.
+
+* `mtx`.`mtx_lock`
diff --git a/documentation/content/ru/books/arch-handbook/locking/_index.po b/documentation/content/ru/books/arch-handbook/locking/_index.po
new file mode 100644
index 0000000000..6985f68d2f
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/locking/_index.po
@@ -0,0 +1,390 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-06-30 23:10+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbooklocking_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:14
+#, no-wrap
+msgid "Locking Notes"
+msgstr "Заметки о блокировках"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:1
+#, no-wrap
+msgid "Chapter 2. Locking Notes"
+msgstr "Глава 2. Заметки о блокировках"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:52
+msgid ""
+"_This chapter is maintained by the FreeBSD SMP Next Generation Project._"
+msgstr ""
+"_Эта глава сопровождается и поддерживается проектом FreeBSD SMP Next "
+"Generation._"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:54
+msgid ""
+"This document outlines the locking used in the FreeBSD kernel to permit "
+"effective multi-processing within the kernel. Locking can be achieved via "
+"several means. Data structures can be protected by mutexes or man:lockmgr[9] "
+"locks. A few variables are protected simply by always using atomic "
+"operations to access them."
+msgstr ""
+"Этот документ описывает механизмы блокировки, используемые в ядре FreeBSD "
+"для эффективной многопроцессорной обработки. Блокировка может быть "
+"достигнута несколькими способами. Структуры данных могут защищаться "
+"мьютексами или блокировками (lock) из man:lockmgr[9]. Некоторые переменные "
+"защищаются просто за счёт использования атомарных операций для доступа к ним."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:56
+#, no-wrap
+msgid "Mutexes"
+msgstr "Mutexes"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:59
+msgid ""
+"A mutex is simply a lock used to guarantee mutual exclusion. Specifically, a "
+"mutex may only be owned by one entity at a time. If another entity wishes to "
+"obtain a mutex that is already owned, it must wait until the mutex is "
+"released. In the FreeBSD kernel, mutexes are owned by processes."
+msgstr ""
+"Мьютекс — это просто блокировка, используемая для обеспечения взаимного "
+"исключения. Конкретно, мьютекс может принадлежать только одному объекту в "
+"один момент времени. Если другой объект хочет получить мьютекс, который уже "
+"принадлежит кому-то, он должен ждать, пока мьютекс не будет освобожден. В "
+"ядре FreeBSD мьютексы принадлежат процессам."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:61
+msgid ""
+"Mutexes may be recursively acquired, but they are intended to be held for a "
+"short period of time. Specifically, one may not sleep while holding a mutex. "
+"If you need to hold a lock across a sleep, use a man:lockmgr[9] lock."
+msgstr ""
+"Мьютексы могут быть получены рекурсивно, но предполагается, что они "
+"удерживаются в течение короткого периода времени. В частности, нельзя "
+"переходить в режим сна, удерживая мьютекс. Если необходимо удерживать "
+"блокировку во время сна, используйте блокировку man:lockmgr[9]."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:63
+msgid "Each mutex has several properties of interest:"
+msgstr "Каждый мьютекс обладает несколькими важными свойствами:"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:64
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:92
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:131
+#, no-wrap
+msgid "Variable Name"
+msgstr "Имя переменной"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:66
+msgid "The name of the struct mtx variable in the kernel source."
+msgstr "Имя переменной struct mtx в исходном коде ядра."
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:67
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:93
+#, no-wrap
+msgid "Logical Name"
+msgstr "Логическое имя"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:69
+msgid ""
+"The name of the mutex assigned to it by `mtx_init`. This name is displayed "
+"in KTR trace messages and witness errors and warnings and is used to "
+"distinguish mutexes in the witness code."
+msgstr ""
+"Имя мьютекса, назначенное ему с помощью `mtx_init`. Это имя отображается в "
+"сообщениях трассировки KTR, ошибках и предупреждениях witness, а также "
+"используется для различения мьютексов в коде witness."
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:70
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:94
+#, no-wrap
+msgid "Type"
+msgstr "Тип"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:72
+msgid ""
+"The type of the mutex in terms of the `MTX_*` flags. The meaning for each "
+"flag is related to its meaning as documented in man:mutex[9]."
+msgstr ""
+"Тип мьютекса в терминах флагов `MTX_*`. Значение каждого флага связано с его "
+"значением, как описано в man:mutex[9]."
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:73
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:106
+#, no-wrap
+msgid "`MTX_DEF`"
+msgstr "`MTX_DEF`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:75
+msgid "A sleep mutex"
+msgstr "Мьютекс блокировки с ожиданием"
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:76
+#, no-wrap
+msgid "`MTX_SPIN`"
+msgstr "`MTX_SPIN`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:78
+msgid "A spin mutex"
+msgstr "Мьютекс с вращающейся блокировкой (spin mutex)"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:79
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:100
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:112
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:118
+#, no-wrap
+msgid "`MTX_RECURSE`"
+msgstr "`MTX_RECURSE`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:81
+msgid "This mutex is allowed to recurse."
+msgstr "Этот мьютекс допускает рекурсию."
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:82
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:95
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:133
+#, no-wrap
+msgid "Protectees"
+msgstr "Защищаемые системы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:84
+msgid ""
+"A list of data structures or data structure members that this entry "
+"protects. For data structure members, the name will be in the form of "
+"`structure name`.`member name`."
+msgstr ""
+"Список структур данных или членов структур данных, которые защищает эта "
+"запись. Для членов структур данных имя будет указано в формате `имя "
+"структуры`.`имя члена`."
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:85
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:97
+#, no-wrap
+msgid "Dependent Functions"
+msgstr "Зависимые функции"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:87
+msgid "Functions that can only be called if this mutex is held."
+msgstr ""
+"Функции, которые могут быть вызваны только при удержании этого мьютекса."
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:88
+#, no-wrap
+msgid "Mutex List"
+msgstr "Список мьютексов"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:98
+#, no-wrap
+msgid "sched_lock"
+msgstr "sched_lock"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:99
+#, no-wrap
+msgid "\"sched lock\""
+msgstr "\"sched lock\""
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:99
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:117
+#, no-wrap
+msgid "`MTX_SPIN` \\"
+msgstr "`MTX_SPIN` \\"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:101
+#, no-wrap
+msgid "`_gmonparam`, `cnt.v_swtch`, `cp_time`, `curpriority`, `mtx`.`mtx_blocked`, `mtx`.`mtx_contested`, `proc`.`p_procq`, `proc`.`p_slpq`, `proc`.`p_sflag`, `proc`.`p_stat`, `proc`.`p_estcpu`, `proc`.`p_cpticks` `proc`.`p_pctcpu`, `proc`.`p_wchan`, `proc`.`p_wmesg`, `proc`.`p_swtime`, `proc`.`p_slptime`, `proc`.`p_runtime`, `proc`.`p_uu`, `proc`.`p_su`, `proc`.`p_iu`, `proc`.`p_uticks`, `proc`.`p_sticks`, `proc`.`p_iticks`, `proc`.`p_oncpu`, `proc`.`p_lastcpu`, `proc`.`p_rqindex`, `proc`.`p_heldmtx`, `proc`.`p_blocked`, `proc`.`p_mtxname`, `proc`.`p_contested`, `proc`.`p_priority`, `proc`.`p_usrpri`, `proc`.`p_nativepri`, `proc`.`p_nice`, `proc`.`p_rtprio`, `pscnt`, `slpque`, `itqueuebits`, `itqueues`, `rtqueuebits`, `rtqueues`, `queuebits`, `queues`, `idqueuebits`, `idqueues`, `switchtime`, `switchticks`"
+msgstr "`_gmonparam`, `cnt.v_swtch`, `cp_time`, `curpriority`, `mtx`.`mtx_blocked`, `mtx`.`mtx_contested`, `proc`.`p_procq`, `proc`.`p_slpq`, `proc`.`p_sflag`, `proc`.`p_stat`, `proc`.`p_estcpu`, `proc`.`p_cpticks` `proc`.`p_pctcpu`, `proc`.`p_wchan`, `proc`.`p_wmesg`, `proc`.`p_swtime`, `proc`.`p_slptime`, `proc`.`p_runtime`, `proc`.`p_uu`, `proc`.`p_su`, `proc`.`p_iu`, `proc`.`p_uticks`, `proc`.`p_sticks`, `proc`.`p_iticks`, `proc`.`p_oncpu`, `proc`.`p_lastcpu`, `proc`.`p_rqindex`, `proc`.`p_heldmtx`, `proc`.`p_blocked`, `proc`.`p_mtxname`, `proc`.`p_contested`, `proc`.`p_priority`, `proc`.`p_usrpri`, `proc`.`p_nativepri`, `proc`.`p_nice`, `proc`.`p_rtprio`, `pscnt`, `slpque`, `itqueuebits`, `itqueues`, `rtqueuebits`, `rtqueues`, `queuebits`, `queues`, `idqueuebits`, `idqueues`, `switchtime`, `switchticks`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:103
+#, no-wrap
+msgid "`setrunqueue`, `remrunqueue`, `mi_switch`, `chooseproc`, `schedclock`, `resetpriority`, `updatepri`, `maybe_resched`, `cpu_switch`, `cpu_throw`, `need_resched`, `resched_wanted`, `clear_resched`, `aston`, `astoff`, `astpending`, `calcru`, `proc_compare`"
+msgstr "`setrunqueue`, `remrunqueue`, `mi_switch`, `chooseproc`, `schedclock`, `resetpriority`, `updatepri`, `maybe_resched`, `cpu_switch`, `cpu_throw`, `need_resched`, `resched_wanted`, `clear_resched`, `aston`, `astoff`, `astpending`, `calcru`, `proc_compare`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:104
+#, no-wrap
+msgid "vm86pcb_lock"
+msgstr "vm86pcb_lock"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:105
+#, no-wrap
+msgid "\"vm86pcb lock\""
+msgstr "\"vm86pcb lock\""
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:107
+#, no-wrap
+msgid "`vm86pcb`"
+msgstr "`vm86pcb`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:109
+#, no-wrap
+msgid "`vm86_bioscall`"
+msgstr "`vm86_bioscall`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:110
+#, no-wrap
+msgid "Giant"
+msgstr "Giant"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:111
+#, no-wrap
+msgid "\"Giant\""
+msgstr "\"Giant\""
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:111
+#, no-wrap
+msgid "`MTX_DEF` \\"
+msgstr "`MTX_DEF` \\"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:113
+#, no-wrap
+msgid "nearly everything"
+msgstr "почти всё"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:115
+#, no-wrap
+msgid "lots"
+msgstr "много"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:116
+#, no-wrap
+msgid "callout_lock"
+msgstr "callout_lock"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:117
+#, no-wrap
+msgid "\"callout lock\""
+msgstr "\"callout lock\""
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:119
+#, no-wrap
+msgid "`callfree`, `callwheel`, `nextsoftcheck`, `proc`.`p_itcallout`, `proc`.`p_slpcallout`, `softticks`, `ticks`"
+msgstr "`callfree`, `callwheel`, `nextsoftcheck`, `proc`.`p_itcallout`, `proc`.`p_slpcallout`, `softticks`, `ticks`"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:123
+#, no-wrap
+msgid "Shared Exclusive Locks"
+msgstr "Разделяемые эксклюзивные блокировки"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:126
+msgid ""
+"These locks provide basic reader-writer type functionality and may be held "
+"by a sleeping process. Currently they are backed by man:lockmgr[9]."
+msgstr ""
+"Эти блокировки обеспечивают базовую функциональность типа читатель-писатель "
+"и могут удерживаться спящим процессом. В настоящее время они реализованы "
+"через man:lockmgr[9]."
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:127
+#, no-wrap
+msgid "Shared Exclusive Lock List"
+msgstr "Список разделяемых эксклюзивных блокировок"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:134
+#, no-wrap
+msgid "`allproc_lock`"
+msgstr "`allproc_lock`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:136
+#, no-wrap
+msgid "`allproc` `zombproc` `pidhashtbl` `proc`.`p_list` `proc`.`p_hash` `nextpid`"
+msgstr "`allproc` `zombproc` `pidhashtbl` `proc`.`p_list` `proc`.`p_hash` `nextpid`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:137
+#, no-wrap
+msgid "`proctree_lock`"
+msgstr "`proctree_lock`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:138
+#, no-wrap
+msgid "`proc`.`p_children` `proc`.`p_sibling`"
+msgstr "`proc`.`p_children` `proc`.`p_sibling`"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:141
+#, no-wrap
+msgid "Atomically Protected Variables"
+msgstr "Атомарно защищённые переменные"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:144
+msgid ""
+"An atomically protected variable is a special variable that is not protected "
+"by an explicit lock. Instead, all data accesses to the variables use special "
+"atomic operations as described in man:atomic[9]. Very few variables are "
+"treated this way, although other synchronization primitives such as mutexes "
+"are implemented with atomically protected variables."
+msgstr ""
+"Переменная с атомарной защитой — это специальная переменная, которая не "
+"защищена явной блокировкой. Вместо этого все операции доступа к данным этой "
+"переменной используют специальные атомарные операции, как описано в "
+"man:atomic[9]. Очень немногие переменные обрабатываются таким образом, хотя "
+"другие примитивы синхронизации, такие как мьютексы, реализованы с "
+"использованием переменных с атомарной защитой."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/locking/_index.adoc:145
+msgid "`mtx`.`mtx_lock`"
+msgstr "`mtx`.`mtx_lock`"
diff --git a/documentation/content/ru/books/arch-handbook/mac/_index.adoc b/documentation/content/ru/books/arch-handbook/mac/_index.adoc
new file mode 100644
index 0000000000..66f6957a00
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/mac/_index.adoc
@@ -0,0 +1,5080 @@
+---
+authors:
+ -
+ author: 'Chris Costello'
+ email: chris@FreeBSD.org
+ -
+ author: 'Robert Watson'
+ email: rwatson@FreeBSD.org
+description: 'Фреймворк TrustedBSD MAC'
+next: books/arch-handbook/vm
+params:
+ path: /books/arch-handbook/mac/
+prev: books/arch-handbook/sysinit
+showBookMenu: true
+tags: ["TrustedBSD", "MAC"]
+title: 'Глава 6. Фреймворк TrustedBSD MAC'
+weight: 7
+---
+
+[[mac]]
+= Фреймворк TrustedBSD MAC
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 6
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[mac-copyright]]
+== Авторские права документации MAC
+
+Этот документ был разработан для проекта FreeBSD Крисом Костелло из Safeport Network Services и Network Associates Laboratories, подразделения исследований безопасности Network Associates, Inc., по контракту DARPA/SPAWAR N66001-01-C-8035 ("CBOSS") в рамках исследовательской программы DARPA CHATS.
+
+Redistribution and use in source (SGML DocBook) and 'compiled' forms (SGML, HTML, PDF, PostScript, RTF and so forth) with or without modification, are permitted provided that the following conditions are met:
+
+. Redistributions of source code (SGML DocBook) must retain the above copyright notice, this list of conditions and the following disclaimer as the first lines of this file unmodified.
+. Распространение в скомпилированной форме (преобразованное в другие DTD, конвертированное в PDF, PostScript, RTF и другие форматы) должно включать указанное выше уведомление об авторских правах, данный список условий и следующий отказ от ответственности в документации и/или других материалах, предоставляемых вместе с распространением.
+
+[IMPORTANT]
+====
+ЭТА ДОКУМЕНТАЦИЯ ПРЕДОСТАВЛЯЕТСЯ NETWORKS ASSOCIATES TECHNOLOGY, INC «КАК ЕСТЬ», И ЛЮБЫЕ ЯВНЫЕ ИЛИ ПОДРАЗУМЕВАЕМЫЕ ГАРАНТИИ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ, ПОДРАЗУМЕВАЕМЫЕ ГАРАНТИИ ТОВАРНОЙ ПРИГОДНОСТИ И ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ ОТРИЦАЮТСЯ. НИ ПРИ КАКИХ ОБСТОЯТЕЛЬСТВАХ NETWORKS ASSOCIATES TECHNOLOGY, INC НЕ НЕСЕТ ОТВЕТСТВЕННОСТИ ЗА ЛЮБЫЕ ПРЯМЫЕ, КОСВЕННЫЕ, СЛУЧАЙНЫЕ, СПЕЦИАЛЬНЫЕ, ШТРАФНЫЕ ИЛИ КОСВЕННЫЕ УБЫТКИ (ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ, ЗАТРАТЫ НА ЗАМЕНУ ТОВАРОВ ИЛИ УСЛУГ; ПОТЕРЮ ИСПОЛЬЗОВАНИЯ, ДАННЫХ ИЛИ ПРИБЫЛИ; ЛИБО ПРЕРЫВАНИЕ БИЗНЕСА), ВЫЗВАННЫЕ ЛЮБЫМ ОБРАЗОМ И НА ОСНОВАНИИ ЛЮБОЙ ТЕОРИИ ОТВЕТСТВЕННОСТИ, БУДЬ ТО В РАМКАХ ДОГОВОРА, СТРОГОЙ ОТВЕТСТВЕННОСТИ ИЛИ ДЕЛИКТА (ВКЛЮЧАЯ НЕБРЕЖНОСТЬ ИЛИ ИНОЕ), ВОЗНИКШИЕ ВСЛЕДСТВИЕ ИСПОЛЬЗОВАНИЯ ЭТОЙ ДОКУМЕНТАЦИИ, ДАЖЕ ЕСЛИ БЫЛО ПРЕДУПРЕЖДЕНИЕ О ВОЗМОЖНОСТИ ТАКИХ УБЫТКОВ.
+====
+
+[[mac-synopsis]]
+== Обзор
+
+FreeBSD включает экспериментальную поддержку нескольких политик обязательного контроля доступа, а также инфраструктуру для расширяемости безопасности ядра — TrustedBSD MAC Framework. MAC Framework представляет собой модульную инфраструктуру контроля доступа, позволяющую легко встраивать новые политики безопасности в ядро, загружать их при старте системы или динамически во время работы. Инфраструктура предоставляет множество возможностей для упрощения реализации новых политик безопасности, включая возможность легко присваивать метки безопасности (например, информацию о конфиденциальности) объектам системы.
+
+Эта глава представляет фреймворк политик MAC и содержит документацию для образца модуля политики MAC.
+
+[[mac-introduction]]
+== Введение
+
+Фреймворк TrustedBSD MAC предоставляет механизм для расширения модели контроля доступа ядра во время компиляции или выполнения. Новые политики системы могут быть реализованы в виде модулей ядра и связаны с ним; если присутствуют несколько модулей политик, их результаты будут объединены. Фреймворк MAC предоставляет различные инфраструктурные сервисы контроля доступа для помощи разработчикам политик, включая поддержку временных и постоянных меток безопасности объектов, не зависящих от политик. В настоящее время эта поддержка считается экспериментальной.
+
+Эта глава предоставляет информацию, предназначенную для разработчиков модулей политик, а также потенциальных пользователей сред с поддержкой MAC, чтобы узнать о том, как MAC Framework поддерживает расширение контроля доступа в ядре.
+
+[[mac-background]]
+== Общие сведения о политиках
+
+Мандатное управление доступом (MAC — Mandatory Access Control) относится к набору политик контроля доступа, которые в обязательном порядке применяются операционной системой к пользователям. Политики MAC можно противопоставить защите на основе дискреционного управления доступом (DAC — Discretionary Access Control), при которой непривилегированные пользователи могут (по своему усмотрению) защищать объекты. В традиционных UNIX-системах защита DAC включает права доступа к файлам и списки контроля доступа; защита MAC включает управление процессами, предотвращающее отладку между пользователями, и межсетевые экраны. Различные политики MAC были разработаны создателями операционных систем и исследователями безопасности, включая политику конфиденциальности многоуровневой безопасности (MLS — Multi-Level Security), политику целостности Biba, управление доступом на основе ролей (RBAC — Role-Based Access Control), принудительное применение доменов и типов (DTE — Domain and Type Enforcement) и принудительное применение типов (TE — Type Enforcement). Каждая модель основывает решения на различных факторах, включая идентификатор пользователя, роль и уровень доступа, а также метки безопасности на объектах, представляющих такие концепции, как конфиденциальность и целостность данных.
+
+Фреймворк TrustedBSD MAC способен поддерживать модули политик, реализующие все эти политики, а также широкий класс политик усиления защиты системы, которые могут использовать существующие атрибуты безопасности, такие как идентификаторы пользователей и групп, а также расширенные атрибуты файлов и другие свойства системы. Кроме того, несмотря на название, фреймворк MAC также может использоваться для реализации чисто дискреционных политик, поскольку модулям политик предоставляется значительная гибкость в том, как они авторизуют защиту.
+
+[[mac-framework-kernel-arch]]
+== Архитектура MAC Framework в ядре
+
+Фреймворк TrustedBSD MAC позволяет модулям ядра расширять политику безопасности операционной системы, а также предоставляет функциональность инфраструктуры, необходимую многим модулям контроля доступа. Если одновременно загружено несколько политик, фреймворк MAC полезным образом (в некотором смысле полезным) объединит результаты этих политик.
+
+[[mac-framework-kernel-arch-elements]]
+=== Элементы ядра
+
+В рамках MAC Framework реализован ряд элементов ядра:
+
+* Интерфейсы управления фреймворком
+* Параллелизм и примитивы синхронизации.
+* Регистрация политики
+* Расширяемая метка безопасности для объектов ядра
+* Операторы композиции точки входа политики
+* Примитивы управления метками
+* Точка входа API, вызываемая службами ядра
+* Точка входа API для модулей политик
+* Реализации точек входа (жизненный цикл политики, жизненный цикл объекта/управление метками, проверки контроля доступа).
+* Системные вызовы, независимые от политик, для управления метками
+* `mac_syscall()` мультиплексный системный вызов
+* Различные политики безопасности, реализованные в виде модулей политики MAC
+
+[[mac-framework-kernel-arch-management]]
+=== Интерфейсы управления фреймворком
+
+Фреймворком TrustedBSD MAC можно напрямую управлять с помощью sysctl, параметров загрузчика и системных вызовов.
+
+В большинстве случаев одноимённые параметры sysctl и настройки загрузчика изменяют одни и те же параметры и управляют поведением, таким как применение защитных механизмов, связанных с различными подсистемами ядра. Кроме того, если в ядро включена поддержка отладки MAC, будет вестись несколько счётчиков для отслеживания выделения меток. Обычно рекомендуется не использовать общие настройки подсистем для управления поведением политик в рабочих средах, так как они широко влияют на работу всех активных политик. Вместо этого следует предпочитать настройки отдельных политик, поскольку они обеспечивают более высокую детализацию и большую операционную согласованность для модулей политик.
+
+Загрузка и выгрузка модулей политики выполняется с использованием системных вызовов управления модулями и других системных интерфейсов, включая переменные загрузчика; модули политики получат возможность влиять на события загрузки и выгрузки, включая предотвращение нежелательной выгрузки политики.
+
+[[mac-framework-kernel-arch-synchronization]]
+=== Список политик параллелизма и синхронизации
+
+Поскольку набор активных политик может изменяться во время выполнения, а вызов точек входа не является атомарным, требуется синхронизация для предотвращения загрузки или выгрузки политик во время выполнения вызова точки входа, фиксируя набор активных политик на время выполнения. Это достигается с помощью счетчика занятости фреймворка: при входе в точку входа счетчик увеличивается; при выходе из нее — уменьшается. Пока счетчик занятости повышен, изменения списка политик запрещены, и потоки, пытающиеся изменить список политик, будут ждать, пока список не освободится. Счетчик занятости защищается мьютексом, а условная переменная используется для пробуждения потоков, ожидающих изменений списка политик. Побочным эффектом этой модели синхронизации является то, что рекурсивный вход в MAC Framework из модуля политики разрешен, хотя обычно не используется.
+
+Для снижения накладных расходов счётчика занятости используются различные оптимизации, включая избегание полной стоимости увеличения и уменьшения, если список пуст или содержит только статические записи (политики, загруженные до старта системы, которые нельзя выгрузить). Также предоставляется опция на этапе компиляции, которая предотвращает любые изменения в наборе загруженных политик во время выполнения, что устраняет затраты на блокировку мьютексов, связанные с поддержкой динамически загружаемых и выгружаемых политик, поскольку синхронизация больше не требуется.
+
+Поскольку MAC Framework не может блокировать некоторые точки входа, обычная блокировка сна не может быть использована; в результате попытка загрузки или выгрузки может блокироваться на значительное время, ожидая, пока фреймворк станет свободным.
+
+[[mac-framework-kernel-arch-label-synchronization]]
+=== Синхронизация меток
+
+Поскольку к объектам ядра обычно может обращаться более одного потока одновременно, и допускается одновременный вход нескольких потоков в MAC Framework, хранение атрибутов безопасности, поддерживаемое MAC Framework, тщательно синхронизировано. Как правило, существующая синхронизация ядра для данных объектов ядра используется для защиты меток безопасности MAC Framework на объекте: например, метки MAC на сокетах защищаются с помощью существующего мьютекса сокета. Аналогично, семантика параллельного доступа обычно идентична семантике контейнерных объектов: для учетных данных поддерживается семантика копирования при записи для содержимого меток, как и для остальной структуры учетных данных. MAC Framework устанавливает необходимые блокировки на объекты при вызове с ссылкой на объект. Авторам политик необходимо учитывать эти семантики синхронизации, так как они иногда ограничивают типы доступа к меткам: например, когда ссылка только для чтения на учетные данные передается политике через точку входа, разрешены только операции чтения для состояния метки, прикрепленного к учетным данным.
+
+[[mac-framework-kernel-arch-policy-synchronization]]
+=== Синхронизация политики и параллелизм
+
+Модули политик должны быть написаны с учетом того, что множество потоков ядра могут одновременно войти в одну или несколько точек входа политики из-за параллельной и вытесняющей природы ядра FreeBSD. Если модуль политики использует изменяемое состояние, это может потребовать применения примитивов синхронизации внутри политики, чтобы предотвратить несогласованные представления этого состояния, ведущие к некорректной работе политики. Политики, как правило, могут использовать существующие примитивы синхронизации FreeBSD для этой цели, включая мьютексы, блокировки с ожиданием, условные переменные и счётные семафоры. Однако политики должны быть написаны так, чтобы применять эти примитивы осторожно, соблюдая существующие порядки блокировок в ядре и учитывая, что некоторые точки входа не допускают ожидания, ограничивая использование примитивов в этих точках входа мьютексами и операциями пробуждения.
+
+Когда модули политики обращаются к другим подсистемам ядра, они обычно должны освобождать любые блокировки внутри политики, чтобы избежать нарушения порядка блокировок ядра или риска рекурсивных блокировок. Это позволит сохранить блокировки политики как конечные блокировки в глобальном порядке блокировок, помогая избежать взаимоблокировки.
+
+[[mac-framework-kernel-arch-registration]]
+=== Регистрация политики
+
+Фреймворк MAC поддерживает два списка активных политик: статический список и динамический список. Списки отличаются только в отношении их семантики блокировки: для использования статического списка не требуется повышенный счетчик ссылок. Когда загружаются модули ядра, содержащие политики фреймворка MAC, модуль политики использует `SYSINIT` для вызова функции регистрации; когда модуль политики выгружается, `SYSINIT` аналогично вызывает функцию отмены регистрации. Регистрация может завершиться неудачей, если модуль политики загружается более одного раза, если для регистрации недостаточно ресурсов (например, политика может требовать маркировки, а доступного состояния маркировки может быть недостаточно), или другие предварительные условия политики могут не выполняться (некоторые политики могут быть загружены только до загрузки системы). Аналогично, отмена регистрации может завершиться неудачей, если политика помечена как невыгружаемая.
+
+[[mac-framework-kernel-arch-entrypoints]]
+=== Точки входа
+
+Ядро взаимодействует с MAC Framework двумя способами: вызывает набор API для уведомления фреймворка о соответствующих событиях и предоставляет указатель на структуру меток, не зависящую от политики, в объектах, связанных с безопасностью. Указатель метки управляется MAC Framework через точки входа управления метками, что позволяет фреймворку предоставлять службу маркировки модулям политик с относительно минимальными изменениями в подсистеме ядра, управляющей объектом. Например, указатели меток были добавлены к процессам, учетным данным процессов, сокетам, каналам, vnode, Mbuf, сетевым интерфейсам, очередям сборки IP-пакетов и множеству других структур, связанных с безопасностью. Ядро также вызывает MAC Framework при принятии важных решений по безопасности, позволяя модулям политик дополнять эти решения на основе собственных критериев (включая, возможно, данные, хранящиеся в метках безопасности). Большинство этих критически важных решений по безопасности будут явными проверками контроля доступа; однако некоторые влияют на более общие функции принятия решений, такие как сопоставление пакетов для сокетов и переход меток при выполнении программы.
+
+[[mac-framework-kernel-arch-composition]]
+=== Композиция политик
+
+Когда в ядро загружено более одного модуля политики одновременно, результаты работы модулей политики будут объединены фреймворком с использованием оператора композиции. Этот оператор в настоящее время жёстко закодирован и требует, чтобы все активные политики одобрили запрос для возврата успешного результата. Поскольку политики могут возвращать различные условия ошибки (успех, доступ запрещён, объект не существует, ...), оператор старшинства выбирает результирующую ошибку из набора ошибок, возвращаемых политиками. В общем случае, ошибки, указывающие на то, что объект не существует, будут предпочтительнее ошибок, указывающих на запрет доступа к объекту. Хотя не гарантируется, что результирующая композиция будет полезной или безопасной, мы обнаружили, что это так для многих полезных наборов политик. Например, традиционные доверенные системы часто поставляются с двумя или более политиками, использующими аналогичную композицию.
+
+[[mac-framework-kernel-arch-labels]]
+=== Поддержка меток
+
+Поскольку многие интересные расширения контроля доступа зависят от меток безопасности объектов, MAC Framework предоставляет набор системных вызовов для управления метками, не зависящих от политик, охватывающих различные объекты, доступные пользователю. Общие типы меток включают идентификаторы разделов, метки конфиденциальности, метки целостности, отделы (compartment), домены, роли и типы. Под "не зависящими от политик" подразумевается, что модули политик могут полностью определять семантику метаданных, связанных с объектом. Модули политик участвуют в интернализации и экстернализации строковых меток, предоставляемых пользовательскими приложениями, и могут при необходимости предоставлять приложениям несколько элементов меток.
+
+Метки в памяти хранятся в `struct label`, выделяемой через slab-аллокатор. Эта структура состоит из массива фиксированной длины, содержащего объединения, каждое из которых хранит указатель `void *` и значение типа `long`. Политикам, регистрирующим хранилище меток, назначается идентификатор "слота", который может использоваться для разыменования хранилища меток. Семантика хранилища полностью определяется модулем политики: модулям предоставляется набор точек входа, связанных с жизненным циклом объектов ядра, включая инициализацию, связывание/создание и уничтожение. Используя эти интерфейсы, можно реализовать подсчёт ссылок и другие модели хранения. Прямой доступ к структуре объекта, как правило, не требуется модулям политики для получения метки, поскольку MAC Framework обычно передаёт в точки входа как указатель на объект, так и прямой указатель на метку объекта. Основным исключением из этого правила являются учётные данные процесса, для доступа к метке которых требуется ручное разыменование. Это может измениться в будущих версиях MAC Framework.
+
+Входные точки инициализации часто включают флаг режима сна, указывающий, разрешено ли инициализации переходить в режим сна; если сон не разрешен, может быть возвращена ошибка для отмены выделения метки (и, следовательно, объекта). Это может произойти, например, в сетевом стеке во время обработки прерывания, где сон не разрешен, или пока вызывающий удерживает мьютекс. Из-за затрат производительности на поддержание меток на передаваемых сетевых пакетах (Mbuf), политики должны явно объявлять требование о выделении меток для Mbuf. Динамически загружаемые политики, использующие метки, должны быть способны обрабатывать случай, когда их функция инициализации не была вызвана для объекта, так как объекты могут уже существовать при загрузке политики. MAC Framework гарантирует, что неинициализированные слоты меток будут содержать значение 0 или NULL, что политики могут использовать для обнаружения неинициализированных значений. Однако, поскольку выделение меток для Mbuf условно, политики также должны быть способны обрабатывать указатель на метку NULL для Mbuf, если они были загружены динамически.
+
+В случае меток файловых систем предусмотрена специальная поддержка для постоянного хранения меток безопасности в расширенных атрибутах. Там, где это возможно, используются транзакции расширенных атрибутов, чтобы обеспечить согласованные составные обновления меток безопасности на vnode — в настоящее время такая поддержка присутствует только в файловой системе UFS2. Авторы политик могут выбрать реализацию многометочных меток объектов файловой системы с использованием одного (или нескольких) расширенных атрибутов. По соображениям эффективности метка vnode (`v_label`) является кэшем любой метки на диске; политики могут загружать значения в кэш при создании vnode и обновлять кэш по мере необходимости. В результате нет необходимости напрямую обращаться к расширенному атрибуту при каждой проверке контроля доступа.
+
+[NOTE]
+====
+В настоящее время, если помеченная политика разрешает динамическую выгрузку, её слот состояния не может быть освобождён, что накладывает строгое (и относительно низкое) ограничение на количество операций выгрузки-перезагрузки для помеченных политик.
+====
+
+[[mac-framework-kernel-arch-syscalls]]
+=== Системные вызовы
+
+В рамках MAC Framework реализован ряд системных вызовов: большинство из них поддерживают API для получения и управления метками, не зависящий от политики и доступный пользовательским приложениям.
+
+Вызовы управления метками принимают структуру описания метки `struct mac`, которая содержит серию элементов метки MAC. Каждый элемент содержит строку с именем и строку со значением. Каждой политике будет предоставлена возможность запросить определённое имя элемента, позволяя политикам предоставлять несколько независимых элементов, если это необходимо. Модули политик выполняют интернализацию и экстернализацию между метками ядра и метками, предоставленными пользователем, через точки входа, что позволяет использовать различные семантики. Системные вызовы управления метками обычно обёрнуты в функции пользовательской библиотеки для выполнения выделения памяти и обработки ошибок, упрощая пользовательские приложения, которые должны управлять метками.
+
+В ядре FreeBSD есть следующие системные вызовы, связанные с MAC:
+
+* `mac_get_proc()` может использоваться для получения метки текущего процесса.
+* `mac_set_proc()` может использоваться, чтобы запросить изменение метки текущего процесса.
+* `mac_get_fd()` может использоваться для получения метки объекта (файл, сокет, канал, ...), на который ссылается файловый дескриптор.
+* `mac_get_file()` может использоваться для получения метки объекта, на который ссылается путь в файловой системе.
+* `mac_set_fd()` может использоваться для запроса изменения метки объекта (файл, сокет, канал, ...), на который ссылается файловый дескриптор.
+* `mac_set_file()` может использоваться для запроса изменения метки объекта, указанного по пути в файловой системе.
+* `mac_syscall()` позволяет модулям политик создавать новые системные вызовы без изменения таблицы системных вызовов; она принимает имя целевой политики, номер операции и непрозрачный аргумент для использования политикой.
+* `mac_get_pid()` может использоваться для запроса метки другого процесса по его идентификатору.
+* `mac_get_link()` идентична `mac_get_file()`, но не переходит по символической ссылке, если она является конечным элементом пути, поэтому может использоваться для получения метки на символьной ссылке.
+* `mac_set_link()` идентична `mac_set_file()`, за исключением того, что она не следует по символической ссылке, если это конечный элемент пути, поэтому может использоваться для изменения метки на символьной ссылке.
+* `mac_execve()` идентична системному вызову `execve()`, но также принимает запрошенную метку, которая будет установлена для процесса при начале выполнения новой программы. Это изменение метки при выполнении называется "переходом".
+* `mac_get_peer()`, фактически реализованный через параметр сокета, извлекает метку удалённого узла на сокете, если она доступна.
+
+В дополнение к этим системным вызовам, сетевые ioctl-команды `SIOCSIGMAC` и `SIOCSIFMAC` позволяют получать и устанавливать метки на сетевых интерфейсах.
+
+[[mac-policy-architecture]]
+== Архитектура политик MAC
+
+Политики безопасности либо непосредственно встроены в ядро, либо скомпилированы в загружаемые модули ядра, которые могут быть загружены при загрузке системы или динамически с использованием системных вызовов загрузки модулей во время выполнения. Модули политик взаимодействуют с системой через набор объявленных точек входа, предоставляя доступ к потоку системных событий и позволяя политике влиять на решения контроля доступа. Каждая политика содержит ряд элементов:
+
+* Необязательные параметры конфигурации для политики.
+* Централизованная реализация логики политики и параметров.
+* Необязательная реализация событий жизненного цикла политики, таких как инициализация и уничтожение.
+* Необязательная поддержка инициализации, обслуживания и удаления меток на выбранных объектах ядра.
+* Дополнительная поддержка проверки процессов пользователя и изменения меток на выбранных объектах.
+* Реализация выбранных точек входа контроля доступа, представляющих интерес для политики.
+* Объявление идентификатора политики, точек входа модуля и свойств политики.
+
+[[mac-policy-declaration]]
+=== Объявление политики
+
+Модули могут быть объявлены с использованием макроса `MAC_POLICY_SET()`, который задаёт имя политики, предоставляет ссылку на вектор точек входа MAC, указывает флаги загрузки, определяющие, как фреймворк политик должен обрабатывать политику, и при необходимости запрашивает выделение состояния метки фреймворком.
+
+[.programlisting]
+....
+static struct mac_policy_ops mac_policy_ops =
+{
+ .mpo_destroy = mac_policy_destroy,
+ .mpo_init = mac_policy_init,
+ .mpo_init_bpfdesc_label = mac_policy_init_bpfdesc_label,
+ .mpo_init_cred_label = mac_policy_init_label,
+/* ... */
+ .mpo_check_vnode_setutimes = mac_policy_check_vnode_setutimes,
+ .mpo_check_vnode_stat = mac_policy_check_vnode_stat,
+ .mpo_check_vnode_write = mac_policy_check_vnode_write,
+};
+....
+
+The MAC policy entry point vector, `mac__policy__ops` in this example, associates functions defined in the module with specific entry points. A complete listing of available entry points and their prototypes may be found in the MAC entry point reference section. Of specific interest during module registration are the .mpo_destroy and .mpo_init entry points. .mpo_init will be invoked once a policy is successfully registered with the module framework but prior to any other entry points becoming active. This permits the policy to perform any policy-specific allocation and initialization, such as initialization of any data or locks. .mpo_destroy will be invoked when a policy module is unloaded to permit releasing of any allocated memory and destruction of locks. Currently, these two entry points are invoked with the MAC policy list mutex held to prevent any other entry points from being invoked: this will be changed, but in the mean time, policies should be careful about what kernel primitives they invoke so as to avoid lock ordering or sleeping problems.
+
+Поле имени модуля в объявлении политики существует для того, чтобы модуль мог быть однозначно идентифицирован с целью управления зависимостями модулей. Следует выбрать подходящую строку. Полное имя политики отображается пользователю в журнале ядра при загрузке и выгрузке, а также экспортируется при предоставлении информации о статусе процессам в пользовательском пространстве.
+
+[[mac-policy-flags]]
+=== Флаги политик
+
+Поле флагов объявления политики позволяет модулю предоставлять фреймворку информацию о своих возможностях во время загрузки модуля. В настоящее время определены три флага:
+
+MPC_LOADTIME_FLAG_UNLOADOK::
+Этот флаг указывает, что модуль политики может быть выгружен. Если этот флаг не указан, то фреймворк политики отклонит запросы на выгрузку модуля. Этот флаг может использоваться модулями, которые выделяют состояние метки и не могут освободить это состояние во время выполнения.
+
+MPC_LOADTIME_FLAG_NOTLATE::
+Этот флаг указывает, что модуль политики должен быть загружен и инициализирован на раннем этапе процесса загрузки. Если флаг указан, попытки зарегистрировать модуль после загрузки будут отклонены. Флаг может использоваться политиками, которые требуют повсеместной маркировки всех системных объектов и не могут обрабатывать объекты, не прошедшие надлежащую инициализацию политикой.
+
+MPC_LOADTIME_FLAG_LABELMBUFS::
+Этот флаг указывает, что модуль политики требует маркировки Mbuf, и память всегда должна выделяться для хранения меток Mbuf. По умолчанию MAC Framework не выделяет память для хранения меток Mbuf, если хотя бы одна загруженная политика не установила этот флаг. Это заметно улучшает производительность сети, когда политики не требуют маркировки Mbuf. Существует опция ядра `MAC_ALWAYS_LABEL_MBUF`, которая заставляет MAC Framework выделять память для хранения меток Mbuf независимо от установки этого флага, и может быть полезной в некоторых средах.
+
+[NOTE]
+====
+Политики, использующие `MPC_LOADTIME_FLAG_LABELMBUFS` без установленного флага `MPC_LOADTIME_FLAG_NOTLATE`, должны корректно обрабатывать переданные `NULL` указатели меток Mbuf в точках входа. Это необходимо, так как Mbuf в процессе передачи без хранилища меток могут сохраняться после загрузки политики, включающей маркировку Mbuf. Если политика загружена до активации сетевой подсистемы (т.е. политика не загружается поздно), то все Mbuf гарантированно имеют хранилище меток.
+====
+
+[[mac-policy-entry-points]]
+=== Точки входа политики
+
+Четыре класса точек входа предоставляются политикам, зарегистрированным в рамках системы: точки входа, связанные с регистрацией и управлением политиками, точки входа, обозначающие инициализацию, создание, уничтожение и другие события жизненного цикла объектов ядра, события, связанные с решениями контроля доступа, на которые политика может влиять, и вызовы, связанные с управлением метками на объектах. Кроме того, предоставляется точка входа `mac_syscall()`, позволяющая политикам расширять интерфейс ядра без регистрации новых системных вызовов.
+
+Авторы модулей политик должны быть осведомлены о стратегии блокировок в ядре, а также о том, какие блокировки объектов доступны на различных точках входа. Им следует избегать сценариев взаимоблокировок, не захватывая нелистовые блокировки внутри точек входа, а также соблюдать протокол блокировок для доступа и изменения объектов. В частности, авторы должны учитывать, что хотя необходимые блокировки для доступа к объектам и их меткам обычно удерживаются, достаточные блокировки для изменения объекта или его метки могут отсутствовать для всех точек входа. Информация о блокировках аргументов документирована в описании точек входа фреймворка MAC.
+
+Точки входа политики будут передавать ссылку на метку объекта вместе с самим объектом. Это позволяет помеченным политикам не знать внутренней структуры объекта, но при этом принимать решения на основе метки. Исключением из этого являются учетные данные процесса, для которые предполагается, что политики понимают их, как объект безопасности первого класса в ядре.
+
+[[mac-entry-point-reference]]
+== Справочник по точкам входа политики MAC
+
+[[mac-mpo-general]]
+=== Общие точки входа модуля
+
+[[mac-mpo-init]]
+==== `mpo_init`
+
+[source, c]
+----
+void mpo_init(struct mac_policy_conf *conf);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`conf`
+|Определение политики MAC
+|
+|===
+
+Событие загрузки политики. Мьютекс списка политик удерживается, поэтому операции ожидания выполнить нельзя, а вызовы других подсистем ядра должны осуществляться с осторожностью. Если во время инициализации политики требуются потенциально блокирующие выделения памяти, их следует выполнять с использованием отдельного модуля SYSINIT().
+
+[[mpo-destroy]]
+==== `mpo_destroy`
+
+[source, c]
+----
+void mpo_destroy(struct mac_policy_conf *conf);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`conf`
+|Определение политики MAC
+|
+|===
+
+Событие загрузки политики. Мьютекс списка политик удерживается, поэтому следует соблюдать осторожность.
+
+[[mac-mpo-syscall]]
+==== `mpo_syscall`
+
+[source, c]
+----
+int mpo_syscall(struct thread *td, int call, void *arg);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`td`
+|Вызывающий поток
+|
+
+|`call`
+|Номер системного вызова, зависящий от политики
+|
+
+|`arg`
+|Указатель на аргументы системного вызова
+|
+|===
+
+Этот точку входа предоставляет мультиплексированный системный вызов на основе политик, что позволяет политикам предоставлять дополнительные сервисы пользовательским процессам без регистрации конкретных системных вызовов. Имя политики, указанное при регистрации, используется для демультиплексирования вызовов из пользовательского пространства, а аргументы будут переданы в эту точку входа. При реализации новых сервисов модули безопасности должны убедиться, что вызывают соответствующие проверки контроля доступа из MAC-фреймворка по мере необходимости. Например, если политика реализует расширенную функциональность сигналов, она должна вызывать необходимые проверки контроля доступа сигналов для задействования MAC-фреймворка и других зарегистрированных политик.
+
+[NOTE]
+====
+Модули в настоящее время должны самостоятельно выполнять `copyin()` для данных системного вызова.
+====
+
+[[mac-mpo-thread-userret]]
+==== `mpo_thread_userret`
+
+[source, c]
+----
+void mpo_thread_userret(struct thread *td);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`td`
+|Возвращающий поток
+|
+|===
+
+Этот точка входа позволяет модулям политики выполнять события, связанные с MAC, когда поток возвращается в пользовательское пространство, через возврат системного вызова, возврат из ловушки или иным образом. Это необходимо для политик, имеющих плавающие метки процессов, так как не всегда возможно получить блокировку процесса в произвольных точках стека во время обработки системного вызова; метки процессов могут представлять традиционные данные аутентификации, информацию об истории процесса или другие данные. Для использования этого механизма предполагаемые изменения метки учётных данных процесса могут быть сохранены в `p_label`, защищённом спин-блокировкой для каждой политики, а затем установить флаг `TDF_ASTPENDING` для потока и флаг `PS_MACPENDM` для процесса, чтобы запланировать вызов точки входа `userret`. С этой точки входа политика может создать замену учётных данных с меньшими опасениями относительно контекста блокировки. Авторам политик следует учитывать, что порядок событий, связанных с планированием AST и выполнением AST, может быть сложным и переплетённым в многопоточных приложениях.
+
+[[mac-label-ops]]
+=== Операции с метками
+
+[[mac-mpo-init-bpfdesc]]
+==== `mpo_init_bpfdesc_label`
+
+[source, c]
+----
+void mpo_init_bpfdesc_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Новая метка для инициализации
+|
+|===
+
+Инициализировать метку на только что созданном bpfdesc (дескрипторе BPF). Разрешено использование режима сна.
+
+[[mac-mpo-init-cred-label]]
+==== `mpo_init_cred_label`
+
+[source, c]
+----
+void mpo_init_cred_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Новая метка для инициализации
+|
+|===
+
+Инициализировать метку для вновь созданных учетных данных пользователя. Разрешено приостанавливать выполнение.
+
+[[mac-mpo-init-devfsdirent]]
+==== `mpo_init_devfsdirent_label`
+
+[source, c]
+----
+void mpo_init_devfsdirent_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Новая метка для инициализации
+|
+|===
+
+Инициализировать метку на только что созданной записи devfs. Разрешено использование режима сна.
+
+[[mac-mpo-init-ifnet]]
+==== `mpo_init_ifnet_label`
+
+[source, c]
+----
+void mpo_init_ifnet_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Новая метка для инициализации
+|
+|===
+
+Инициализировать метку на только что созданном сетевом интерфейсе. Разрешено приостанавливать выполнение.
+
+[[mac-mpo-init-ipq]]
+==== `mpo_init_ipq_label`
+
+[source, c]
+----
+void mpo_init_ipq_label(struct label *label, int flag);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Новая метка для инициализации
+|
+
+|`flag`
+|Спящий/неспящий man:malloc[9]; см. ниже
+|
+|===
+
+Инициализировать метку в только что созданной очереди сборки IP-фрагментов. Поле `flag` может принимать одно из значений M_WAITOK или M_NOWAIT и должно использоваться, чтобы избежать выполнения "спящего" man:malloc[9] во время этого вызова инициализации. Выделение очереди сборки IP-фрагментов часто происходит в средах, чувствительных к производительности, и реализация должна избегать "спящих" или длительных операций. Этой точке входа разрешено завершаться неудачей, что приведёт к невозможности выделения очереди сборки IP-фрагментов.
+
+[[mac-mpo-init-mbuf]]
+==== `mpo_init_mbuf_label`
+
+[source, c]
+----
+void mpo_init_mbuf_label(int flag, struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`flag`
+|Спящий/неспящий man:malloc[9]; см. ниже
+|
+
+|`label`
+|Метка политики для инициализации
+|
+|===
+
+Инициализировать на только что созданном заголовке пакета mbuf метку (`mbuf`). Поле `flag` может принимать одно из значений M_WAITOK или M_NOWAIT и должно использоваться, чтобы избежать выполнения "спящего" man:malloc[9] во время этого вызова инициализации. Выделение mbuf часто происходит в чувствительных к производительности средах, и реализация должна избегать "спящего" режима или длительных операций. Этой точке входа разрешено завершаться неудачей, что приведёт к невозможности выделения заголовка mbuf.
+
+[[mac-mpo-init-mount]]
+==== `mpo_init_mount_label`
+
+[source, c]
+----
+void mpo_init_mount_label(struct label *mntlabel, struct label *fslabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`mntlabel`
+|Метка политики для инициализации самой точки монтирования
+|
+
+|`fslabel`
+|Метка политики для инициализации файловой системы
+|
+|===
+
+Инициализировать метки на новой точке монтирования. Разрешено приостанавливать выполнение.
+
+[[mac-mpo-init-mount-fs-label]]
+==== `mpo_init_mount_fs_label`
+
+[source, c]
+----
+void mpo_init_mount_fs_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для инициализации
+|
+|===
+
+Инициализировать метку на только что смонтированной файловой системе. Разрешено приостановление работы
+
+[[mac-mpo-init-pipe-label]]
+==== `mpo_init_pipe_label`
+
+[source, c]
+----
+void mpo_init_pipe_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для заполнения
+|
+|===
+
+Инициализировать метку для только что созданного канала. Разрешено приостановление выполнения.
+
+[[mac-mpo-init-socket]]
+==== `mpo_init_socket_label`
+
+[source, c]
+----
+void mpo_init_socket_label(struct label *label, int flag);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Новая метка для инициализации
+|
+
+|`flag`
+|флаги man:malloc[9]
+|
+|===
+
+Инициализировать метку для нового сокета. Поле `flag` может принимать одно из значений M_WAITOK или M_NOWAIT и должно использоваться, чтобы избежать выполнения спящего man:malloc[9] во время этого вызова инициализации.
+
+[[mac-mpo-init-socket-peer-label]]
+==== `mpo_init_socket_peer_label`
+
+[source, c]
+----
+void mpo_init_socket_peer_label(struct label *label, int flag);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Новая метка для инициализации
+|
+
+|`flag`
+|флаги man:malloc[9]
+|
+|===
+
+Инициализировать метку однорангового узла (peer) для вновь созданного сокета. Поле `flag` может принимать одно из значений M_WAITOK или M_NOWAIT и должно использоваться для избежания выполнения спящего man:malloc[9] во время этого вызова инициализации.
+
+[[mac-mpo-init-proc-label]]
+==== `mpo_init_proc_label`
+
+[source, c]
+----
+void mpo_init_proc_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Новая метка для инициализации
+|
+|===
+
+Инициализировать метку для вновь созданного процесса. Разрешено приостановление выполнения.
+
+[[mac-mpo-init-vnode]]
+==== `mpo_init_vnode_label`
+
+[source, c]
+----
+void mpo_init_vnode_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Новая метка для инициализации
+|
+|===
+
+Инициализировать метку на только что созданном vnode. Разрешено приостанавливать выполнение.
+
+[[mac-mpo-destroy-bpfdesc]]
+==== `mpo_destroy_bpfdesc_label`
+
+[source, c]
+----
+void mpo_destroy_bpfdesc_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|bpfdesc label
+|
+|===
+
+Уничтожить метку на дескрипторе BPF. В этой точке входа политика должна освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно было уничтожить.
+
+[[mac-mpo-destroy-cred]]
+==== `mpo_destroy_cred_label`
+
+[source, c]
+----
+void mpo_destroy_cred_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка уничтожается
+|
+|===
+
+Уничтожить метку на учетных данных. В этой точке входа модуль политики должен освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно было уничтожить.
+
+[[mac-mpo-destroy-devfsdirent]]
+==== `mpo_destroy_devfsdirent_label`
+
+[source, c]
+----
+void mpo_destroy_devfsdirent_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка уничтожается
+|
+|===
+
+Уничтожить метку на записи devfs. В этой точке входа модуль политики должен освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно было уничтожить.
+
+[[mac-mpo-destroy-ifnet-label]]
+==== `mpo_destroy_ifnet_label`
+
+[source, c]
+----
+void mpo_destroy_ifnet_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка уничтожается
+|
+|===
+
+Уничтожить метку на удаленном интерфейсе. В этой точке входа модуль политики должен освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно было уничтожить.
+
+[[mac-mpo-destroy-ipq-label]]
+==== `mpo_destroy_ipq_label`
+
+[source, c]
+----
+void mpo_destroy_ipq_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка уничтожается
+|
+|===
+
+Уничтожить метку в очереди IP-фрагментов. В этой точке входа модуль политики должен освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно было уничтожить.
+
+[[mac-mpo-destroy-mbuf-label]]
+==== `mpo_destroy_mbuf_label`
+
+[source, c]
+----
+void mpo_destroy_mbuf_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка уничтожается
+|
+|===
+
+Уничтожить метку в заголовке mbuf. В этой точке входа модуль политики должен освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно было уничтожить.
+
+[[mac-mpo-destroy-mount-label]]
+==== `mpo_destroy_mount_label`
+
+[source, c]
+----
+void mpo_destroy_mount_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Точка монтирования метки уничтожается
+|
+|===
+
+Уничтожить метки на точке монтирования. В этой точке входа модуль политики должен освободить внутреннее хранилище, связанное с `mntlabel`, чтобы ее можно было уничтожить.
+
+[[mac-mpo-destroy-mount]]
+==== `mpo_destroy_mount_label`
+
+[source, c]
+----
+void mpo_destroy_mount_label(struct label *mntlabel, struct label *fslabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`mntlabel`
+|Точка монтирования метки уничтожается
+|
+
+|`fslabel`
+|Метка файловой системы уничтожается
+|
+|===
+
+Уничтожить метки на точке монтирования. В этой точке входа модуль политики должен освободить внутреннее хранилище, связанное с `mntlabel` и `fslabel`, чтобы их можно было уничтожить.
+
+[[mac-mpo-destroy-socket]]
+==== `mpo_destroy_socket_label`
+
+[source, c]
+----
+void mpo_destroy_socket_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Уничтожение метки сокета
+|
+|===
+
+Уничтожить метку на сокете. В этой точке входа модуль политики должен освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно было уничтожить.
+
+[[mac-mpo-destroy-socket-peer-label]]
+==== `mpo_destroy_socket_peer_label`
+
+[source, c]
+----
+void mpo_destroy_socket_peer_label(struct label *peerlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`peerlabel`
+|Сокет: метка однорангового узла уничтожается
+|
+|===
+
+Уничтожить метку однорангового узла на сокете. В этой точке входа модуль политики должен освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно было уничтожить.
+
+[[mac-mpo-destroy-pipe-label]]
+==== `mpo_destroy_pipe_label`
+
+[source, c]
+----
+void mpo_destroy_pipe_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка канала (pipe)
+|
+|===
+
+Уничтожить метку на канале. В этой точке входа модуль политики должен освободить всю внутреннюю память, связанную с `label`, чтобы её можно было уничтожить.
+
+[[mac-mpo-destroy-proc-label]]
+==== `mpo_destroy_proc_label`
+
+[source, c]
+----
+void mpo_destroy_proc_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка процесса
+|
+|===
+
+Уничтожить метку на процессе. В этой точке входа модуль политики должен освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно было уничтожить.
+
+[[mac-mpo-destroy-vnode-label]]
+==== `mpo_destroy_vnode_label`
+
+[source, c]
+----
+void mpo_destroy_vnode_label(struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка процесса
+|
+|===
+
+Уничтожить метку на vnode. В этой точке входа модуль политики должен освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно было уничтожить.
+
+[[mac-mpo-copy-mbuf-label]]
+==== `mpo_copy_mbuf_label`
+
+[source, c]
+----
+void mpo_copy_mbuf_label(struct label *src, struct label *dest);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`src`
+|Метка источника
+|
+
+|`dest`
+|Метка назначения
+|
+|===
+
+Скопировать информацию метки из `src` в `dest`.
+
+[[mac-mpo-copy-pipe-label]]
+==== `mpo_copy_pipe_label`
+
+[source, c]
+----
+void mpo_copy_pipe_label(struct label *src, struct label *dest);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`src`
+|Метка источника
+|
+
+|`dest`
+|Метка назначения
+|
+|===
+
+Скопировать информацию метки из `src` в `dest`.
+
+[[mac-mpo-copy-vnode-label]]
+==== `mpo_copy_vnode_label`
+
+[source, c]
+----
+void mpo_copy_vnode_label(struct label *src, struct label *dest);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`src`
+|Метка источника
+|
+
+|`dest`
+|Метка назначения
+|
+|===
+
+Скопировать информацию метки из `src` в `dest`.
+
+[[mac-mpo-externalize-cred-label]]
+==== `mpo_externalize_cred_label`
+
+[source, c]
+----
+int mpo_externalize_cred_label(struct label *label, char *element_name,
+ struct sbuf *sb, int *claimed);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для вынесения во внешний ресурс
+|
+
+|`element_name`
+|Имя политики, метка которой должна быть вынесена во внешний ресурс
+|
+
+|`sb`
+|Буфер строки для заполнения текстовым представлением метки
+|
+
+|`claimed`
+|Должно быть увеличено, когда `element_data` может быть заполнено.
+|
+|===
+
+Создать внешнее представление метки на основе переданной структуры метки. Внешнее представление метки состоит из текстового представления содержимого метки, которое может использоваться пользовательскими приложениями и прочитано пользователем. В настоящее время будут вызываться точки входа `externalize` всех политик, поэтому реализация должна проверить содержимое `element_name` перед попыткой заполнить `sb`. Если `element_name` не соответствует имени вашей политики, просто верните 0. Возвращайте ненулевое значение только в случае ошибки при внешнем представлении данных метки. После того как политика заполнит `element_data`, `*claimed` должен быть увеличен.
+
+[[mac-mpo-externalize-ifnet-label]]
+==== `mpo_externalize_ifnet_label`
+
+[source, c]
+----
+int mpo_externalize_ifnet_label(struct label *label, char *element_name,
+ struct sbuf *sb, int *claimed);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для вынесения во внешний ресурс
+|
+
+|`element_name`
+|Имя политики, метка которой должна быть вынесена во внешний ресурс
+|
+
+|`sb`
+|Буфер строки для заполнения текстовым представлением метки
+|
+
+|`claimed`
+|Должно быть увеличено, когда `element_data` может быть заполнено.
+|
+|===
+
+Создать внешнее представление метки на основе переданной структуры метки. Внешнее представление метки состоит из текстового представления содержимого метки, которое может использоваться пользовательскими приложениями и прочитано пользователем. В настоящее время будут вызываться точки входа `externalize` всех политик, поэтому реализация должна проверить содержимое `element_name` перед попыткой заполнить `sb`. Если `element_name` не соответствует имени вашей политики, просто верните 0. Возвращайте ненулевое значение только в случае ошибки при внешнем представлении данных метки. После того как политика заполнит `element_data`, `*claimed` должен быть увеличен.
+
+[[mac-mpo-externalize-pipe-label]]
+==== `mpo_externalize_pipe_label`
+
+[source, c]
+----
+int mpo_externalize_pipe_label(struct label *label, char *element_name,
+ struct sbuf *sb, int *claimed);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для вынесения во внешний ресурс
+|
+
+|`element_name`
+|Имя политики, метка которой должна быть вынесена во внешний ресурс
+|
+
+|`sb`
+|Буфер строки для заполнения текстовым представлением метки
+|
+
+|`claimed`
+|Должно быть увеличено, когда `element_data` может быть заполнено.
+|
+|===
+
+Создать внешнее представление метки на основе переданной структуры метки. Внешнее представление метки состоит из текстового представления содержимого метки, которое может использоваться пользовательскими приложениями и прочитано пользователем. В настоящее время будут вызываться точки входа `externalize` всех политик, поэтому реализация должна проверить содержимое `element_name` перед попыткой заполнить `sb`. Если `element_name` не соответствует имени вашей политики, просто верните 0. Возвращайте ненулевое значение только в случае ошибки при внешнем представлении данных метки. После того как политика заполнит `element_data`, `*claimed` должен быть увеличен.
+
+[[mac-mpo-externalize-socket-label]]
+==== `mpo_externalize_socket_label`
+
+[source, c]
+----
+int mpo_externalize_socket_label(struct label *label, char *element_name,
+ struct sbuf *sb, int *claimed);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для вынесения во внешний ресурс
+|
+
+|`element_name`
+|Имя политики, метка которой должна быть вынесена во внешний ресурс
+|
+
+|`sb`
+|Буфер строки для заполнения текстовым представлением метки
+|
+
+|`claimed`
+|Должно быть увеличено, когда `element_data` может быть заполнено.
+|
+|===
+
+Создать внешнее представление метки на основе переданной структуры метки. Внешнее представление метки состоит из текстового представления содержимого метки, которое может использоваться пользовательскими приложениями и прочитано пользователем. В настоящее время будут вызываться точки входа `externalize` всех политик, поэтому реализация должна проверить содержимое `element_name` перед попыткой заполнить `sb`. Если `element_name` не соответствует имени вашей политики, просто верните 0. Возвращайте ненулевое значение только в случае ошибки при внешнем представлении данных метки. После того как политика заполнит `element_data`, `*claimed` должен быть увеличен.
+
+[[mac-mpo-externalize-socket-peer-label]]
+==== `mpo_externalize_socket_peer_label`
+
+[source, c]
+----
+int mpo_externalize_socket_peer_label(struct label *label, char *element_name,
+ struct sbuf *sb, int *claimed);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для вынесения во внешний ресурс
+|
+
+|`element_name`
+|Имя политики, метка которой должна быть вынесена во внешний ресурс
+|
+
+|`sb`
+|Буфер строки для заполнения текстовым представлением метки
+|
+
+|`claimed`
+|Должно быть увеличено, когда `element_data` может быть заполнено.
+|
+|===
+
+Создать внешнее представление метки на основе переданной структуры метки. Внешнее представление метки состоит из текстового представления содержимого метки, которое может использоваться пользовательскими приложениями и прочитано пользователем. В настоящее время будут вызываться точки входа `externalize` всех политик, поэтому реализация должна проверить содержимое `element_name` перед попыткой заполнить `sb`. Если `element_name` не соответствует имени вашей политики, просто верните 0. Возвращайте ненулевое значение только в случае ошибки при внешнем представлении данных метки. После того как политика заполнит `element_data`, `*claimed` должен быть увеличен.
+
+[[mac-mpo-externalize-vnode-label]]
+==== `mpo_externalize_vnode_label`
+
+[source, c]
+----
+int mpo_externalize_vnode_label(struct label *label, char *element_name,
+ struct sbuf *sb, int *claimed);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для вынесения во внешний ресурс
+|
+
+|`element_name`
+|Имя политики, метка которой должна быть вынесена во внешний ресурс
+|
+
+|`sb`
+|Буфер строки для заполнения текстовым представлением метки
+|
+
+|`claimed`
+|Должно быть увеличено, когда `element_data` может быть заполнено.
+|
+|===
+
+Создать внешнее представление метки на основе переданной структуры метки. Внешнее представление метки состоит из текстового представления содержимого метки, которое может использоваться пользовательскими приложениями и прочитано пользователем. В настоящее время будут вызываться точки входа `externalize` всех политик, поэтому реализация должна проверить содержимое `element_name` перед попыткой заполнить `sb`. Если `element_name` не соответствует имени вашей политики, просто верните 0. Возвращайте ненулевое значение только в случае ошибки при внешнем представлении данных метки. После того как политика заполнит `element_data`, `*claimed` должен быть увеличен.
+
+[[mac-mpo-internalize-cred-label]]
+==== `mpo_internalize_cred_label`
+
+[source, c]
+----
+int mpo_internalize_cred_label(struct label *label, char *element_name,
+ char *element_data, int *claimed);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для заполнения
+|
+
+|`element_name`
+|Имя политики, метка которой должна быть приведена к внутреннему представлению
+|
+
+|`element_data`
+|Текстовые данные для преобразования к внутреннему представлению
+|
+
+|`claimed`
+|Должно увеличиваться, когда данные могут быть успешно преобразовываться вовнутреннее представление.
+|
+|===
+
+Создать внутреннюю структуру меток на основе данных метки вов нешнем представлении в текстовом формате. В настоящее время, при запросе преобразования во внутреннее представление вызываются точки входа `internalize` всех политик, поэтому реализация должна сравнивать содержимое `element_name` со своим именем, чтобы убедиться, что она должна преобразовывать данные в `element_data`. Как и в точках входа `externalize`, точка входа должна возвращать 0, если `element_name` не совпадает с её собственным именем, или когда данные могут быть успешно преобразованы, в этом случае `*claimed` должен быть увеличен.
+
+[[mac-mpo-internalize-ifnet-label]]
+==== `mpo_internalize_ifnet_label`
+
+[source, c]
+----
+int mpo_internalize_ifnet_label(struct label *label, char *element_name,
+ char *element_data, int *claimed);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для заполнения
+|
+
+|`element_name`
+|Имя политики, метка которой должна быть приведена к внутреннему представлению
+|
+
+|`element_data`
+|Текстовые данные для преобразования к внутреннему представлению
+|
+
+|`claimed`
+|Должно увеличиваться, когда данные могут быть успешно преобразовываться вовнутреннее представление.
+|
+|===
+
+Создать внутреннюю структуру меток на основе данных метки вов нешнем представлении в текстовом формате. В настоящее время, при запросе преобразования во внутреннее представление вызываются точки входа `internalize` всех политик, поэтому реализация должна сравнивать содержимое `element_name` со своим именем, чтобы убедиться, что она должна преобразовывать данные в `element_data`. Как и в точках входа `externalize`, точка входа должна возвращать 0, если `element_name` не совпадает с её собственным именем, или когда данные могут быть успешно преобразованы, в этом случае `*claimed` должен быть увеличен.
+
+[[mac-mpo-internalize-pipe-label]]
+==== `mpo_internalize_pipe_label`
+
+[source, c]
+----
+int mpo_internalize_pipe_label(struct label *label, char *element_name,
+ char *element_data, int *claimed);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для заполнения
+|
+
+|`element_name`
+|Имя политики, метка которой должна быть приведена к внутреннему представлению
+|
+
+|`element_data`
+|Текстовые данные для преобразования к внутреннему представлению
+|
+
+|`claimed`
+|Должно увеличиваться, когда данные могут быть успешно преобразовываться вовнутреннее представление.
+|
+|===
+
+Создать внутреннюю структуру меток на основе данных метки вов нешнем представлении в текстовом формате. В настоящее время, при запросе преобразования во внутреннее представление вызываются точки входа `internalize` всех политик, поэтому реализация должна сравнивать содержимое `element_name` со своим именем, чтобы убедиться, что она должна преобразовывать данные в `element_data`. Как и в точках входа `externalize`, точка входа должна возвращать 0, если `element_name` не совпадает с её собственным именем, или когда данные могут быть успешно преобразованы, в этом случае `*claimed` должен быть увеличен.
+
+[[mac-mpo-internalize-socket-label]]
+==== `mpo_internalize_socket_label`
+
+[source, c]
+----
+int mpo_internalize_socket_label(struct label *label, char *element_name,
+ char *element_data, int *claimed);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для заполнения
+|
+
+|`element_name`
+|Имя политики, метка которой должна быть приведена к внутреннему представлению
+|
+
+|`element_data`
+|Текстовые данные для преобразования к внутреннему представлению
+|
+
+|`claimed`
+|Должно увеличиваться, когда данные могут быть успешно преобразовываться вовнутреннее представление.
+|
+|===
+
+Создать внутреннюю структуру меток на основе данных метки вов нешнем представлении в текстовом формате. В настоящее время, при запросе преобразования во внутреннее представление вызываются точки входа `internalize` всех политик, поэтому реализация должна сравнивать содержимое `element_name` со своим именем, чтобы убедиться, что она должна преобразовывать данные в `element_data`. Как и в точках входа `externalize`, точка входа должна возвращать 0, если `element_name` не совпадает с её собственным именем, или когда данные могут быть успешно преобразованы, в этом случае `*claimed` должен быть увеличен.
+
+[[mac-mpo-internalize-vnode-label]]
+==== `mpo_internalize_vnode_label`
+
+[source, c]
+----
+int mpo_internalize_vnode_label(struct label *label, char *element_name,
+ char *element_data, int *claimed);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`label`
+|Метка для заполнения
+|
+
+|`element_name`
+|Имя политики, метка которой должна быть приведена к внутреннему представлению
+|
+
+|`element_data`
+|Текстовые данные для преобразования к внутреннему представлению
+|
+
+|`claimed`
+|Должно увеличиваться, когда данные могут быть успешно преобразовываться вовнутреннее представление.
+|
+|===
+
+Создать внутреннюю структуру меток на основе данных метки вов нешнем представлении в текстовом формате. В настоящее время, при запросе преобразования во внутреннее представление вызываются точки входа `internalize` всех политик, поэтому реализация должна сравнивать содержимое `element_name` со своим именем, чтобы убедиться, что она должна преобразовывать данные в `element_data`. Как и в точках входа `externalize`, точка входа должна возвращать 0, если `element_name` не совпадает с её собственным именем, или когда данные могут быть успешно преобразованы, в этом случае `*claimed` должен быть увеличен.
+
+[[mac-label-events]]
+=== События метки
+
+Этот класс точек входа используется фреймворком MAC для разрешения политикам поддерживать информацию о метках на объектах ядра. Для каждого помеченного объекта ядра, представляющего интерес для политики MAC, могут быть зарегистрированы точки входа для соответствующих событий жизненного цикла. Все объекты реализуют хуки инициализации, создания и уничтожения. Некоторые объекты также реализуют перемаркировку, позволяя пользовательским процессам изменять метки на объектах. Некоторые объекты также реализуют специфичные для объекта события, такие как события меток, связанные с повторной сборкой IP. Типичный помеченный объект будет иметь следующий жизненный цикл точек входа:
+
+[.programlisting]
+....
+Label initialization o
+(object-specific wait) \
+Label creation o
+ \
+Relabel events, o--<--.
+Various object-specific, | |
+Access control events ~-->--o
+ \
+Label destruction o
+....
+
+Инициализация меток позволяет политикам выделять память и устанавливать начальные значения для меток без контекста использования объекта. Слот метки, выделенный для политики, по умолчанию будет обнулен, поэтому некоторым политикам может не потребоваться выполнять инициализацию.
+
+Создание метки происходит, когда структура ядра связывается с реальным объектом ядра. Например, Mbuf могут быть выделены и оставаться неиспользованными в пуле до тех пор, пока они не понадобятся. Выделение mbuf приводит к инициализации метки на mbuf, но создание mbuf происходит, когда mbuf связывается с датаграммой. Обычно для события создания предоставляется контекст, включая обстоятельства создания и метки других значимых объектов в процессе создания. Например, когда mbuf создаётся из сокета, сокет и его метка будут переданы зарегистрированным политикам в дополнение к новому mbuf и его метке. Выделение памяти в событиях создания не рекомендуется, так как это может происходить в чувствительных к производительности участках ядра; кроме того, вызовы создания не могут завершиться неудачей, поэтому невозможность выделить память не может быть сообщена.
+
+События, привящанные к объектам, обычно не попадают в другие классы событий меток, но, как правило, предоставляют возможность изменить или обновить метку объекта на основе дополнительного контекста. Например, метка в очереди сборки IP-фрагментов может быть обновлена во время точки входа `MAC_UPDATE_IPQ` в результате принятия дополнительного mbuf в эту очередь.
+
+События контроля доступа подробно рассматриваются в следующем разделе.
+
+Уничтожение метки позволяет политикам освобождать хранилище или состояние, связанное с меткой во время её ассоциации с объектом, чтобы структуры данных ядра, поддерживающие объект, могли быть повторно использованы или освобождены.
+
+В дополнение к меткам, связанным с определёнными объектами ядра, существует дополнительный класс меток: временные метки. Эти метки используются для хранения информации об обновлениях, отправляемых пользовательскими процессами. Они инициализируются и уничтожаются так же, как и другие типы меток, но событие создания — это `MAC_INTERNALIZE`, которое принимает пользовательскую метку для преобразования во внутреннее представление в ядре.
+
+[[mac-fs-label-event-ops]]
+==== Действия с событиями меток объектов файловой системы
+
+[[mac-mpo-associate-vnode-devfs]]
+===== `mpo_associate_vnode_devfs`
+
+[source, c]
+----
+void mpo_associate_vnode_devfs(struct mount *mp, struct label *fslabel,
+ struct devfs_dirent *de, struct label *delabel, struct vnode *vp,
+ struct label *vlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`mp`
+|Точка монтирования devfs
+|
+
+|`fslabel`
+|Метка файловой системы devfs (`mp->mnt_fslabel`)
+|
+
+|`de`
+|Запись каталога devfs
+|
+
+|`delabel`
+|Метка политики, связанная с `de`
+|
+
+|`vp`
+|узел vnode, связанный с `de`
+|
+
+|`vlabel`
+|Метка политики, связанная с `vp`
+|
+|===
+
+Заполнить метку (`vlabel`) для только что созданного devfs vnode на основе записи каталога devfs, переданной в `de`, и её метки.
+
+[[mac-mpo-associate-vnode-extattr]]
+===== `mpo_associate_vnode_extattr`
+
+[source, c]
+----
+int mpo_associate_vnode_extattr(struct mount *mp, struct label *fslabel,
+ struct vnode *vp, struct label *vlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`mp`
+|Точка монтирования файловой системы
+|
+
+|`fslabel`
+|Метка файловой системы
+|
+
+|`vp`
+|Узел vnode для метки
+|
+
+|`vlabel`
+|Метка политики, связанная с `vp`
+|
+|===
+
+Попытка получить метку для `vp` из расширенных атрибутов файловой системы. В случае успеха возвращается значение `0`. Если получение расширенных атрибутов не поддерживается, допустимым резервным вариантом является копирование `fslabel` в `vlabel`. В случае ошибки должно быть возвращено соответствующее значение `errno`.
+
+[[mac-mpo-associate-vnode-singlelabel]]
+===== `mpo_associate_vnode_singlelabel`
+
+[source, c]
+----
+void mpo_associate_vnode_singlelabel(struct mount *mp, struct label *fslabel,
+ struct vnode *vp, struct label *vlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`mp`
+|Точка монтирования файловой системы
+|
+
+|`fslabel`
+|Метка файловой системы
+|
+
+|`vp`
+|Узел vnode для метки
+|
+
+|`vlabel`
+|Метка политики, связанная с `vp`
+|
+|===
+
+На файловых системах без поддержки multilabel эта точка входа вызывается для установки метки политики для `vp` на основе метки файловой системы `fslabel`.
+
+[[mac-mpo-create-devfs-device]]
+===== `mpo_create_devfs_device`
+
+[source, c]
+----
+void mpo_create_devfs_device(dev_t dev, struct devfs_dirent *devfs_dirent,
+ struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`dev`
+|Устройство, соответствующее `devfs_dirent`
+|
+
+|`devfs_dirent`
+|Запись в каталоге devfs, для которой создается метка.
+|
+
+|`label`
+|Метка для `devfs_dirent`, которую нужно заполнить.
+|
+|===
+
+Заполнить метку на devfs_dirent, создаваемом для переданного устройства. Этот вызов будет выполнен при монтировании файловой системы устройств, её восстановлении или при появлении нового устройства.
+
+[[mac-mpo-create-devfs-directory]]
+===== `mpo_create_devfs_directory`
+
+[source, c]
+----
+void mpo_create_devfs_directory(char *dirname, int dirnamelen,
+ struct devfs_dirent *devfs_dirent, struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`dirname`
+|Имя создаваемого каталога
+|
+
+|`namelen`
+|Длина строки `dirname`
+|
+
+|`devfs_dirent`
+|Запись в devfs для создаваемого каталога.
+|
+|===
+
+Заполнить метку на devfs_dirent, создаваемом для переданного каталога. Этот вызов будет выполнен при монтировании файловой системы устройств, её восстановлении или при появлении нового устройства, требующего определённой иерархии каталогов.
+
+[[mac-mpo-create-devfs-symlink]]
+===== `mpo_create_devfs_symlink`
+
+[source, c]
+----
+void mpo_create_devfs_symlink(struct ucred *cred, struct mount *mp,
+ struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de,
+ struct label *delabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`mp`
+|Точка монтирования devfs
+|
+
+|`dd`
+|Назначения cсылки
+|
+
+|`ddlabel`
+|Метка, связанная с `dd`
+|
+
+|`de`
+|Символьная ссылка записи
+|
+
+|`delabel`
+|Метка, связанная с `de`
+|
+|===
+
+Заполнить метку (`delabel`) для новой структуры man:devfs[5] символьной ссылки.
+
+[[mac-mpo-create-vnode-extattr]]
+===== `mpo_create_vnode_extattr`
+
+[source, c]
+----
+int mpo_create_vnode_extattr(struct ucred *cred, struct mount *mp,
+ struct label *fslabel, struct vnode *dvp, struct label *dlabel,
+ struct vnode *vp, struct label *vlabel, struct componentname *cnp);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`mount`
+|Точка монтирования файловой системы
+|
+
+|`label`
+|Метка файловой системы
+|
+
+|`dvp`
+|Родительский каталог vnode
+|
+
+|`dlabel`
+|Метка, связанная с `dvp`
+|
+
+|`vp`
+|Вновь созданная vnode
+|
+
+|`vlabel`
+|Метка политики, связанная с `vp`
+|
+
+|`cnp`
+|Название компонента для `vp`
+|
+|===
+
+Записать метку для `vp` в соответствующий расширенный атрибут. Если запись прошла успешно, заполняет `vlabel` меткой и возвращает 0. В противном случае вернет соответствующую ошибку.
+
+[[mac-mpo-create-mount]]
+===== `mpo_create_mount`
+
+[source, c]
+----
+void mpo_create_mount(struct ucred *cred, struct mount *mp, struct label *mnt,
+ struct label *fslabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`mp`
+|Объект; файловая система, которая монтируется
+|
+
+|`mntlabel`
+|Метка политики для заполнения в `mp`
+|
+
+|`fslabel`
+|Метка политики для файловой системы, монтируемой в `mp`.
+|
+|===
+
+Заполнить метки на точке монтирования, создаваемой переданными учетными данными субъекта. Этот вызов будет выполнен при монтировании новой файловой системы.
+
+[[mac-mpo-create-root-mount]]
+===== `mpo_create_root_mount`
+
+[source, c]
+----
+void mpo_create_root_mount(struct ucred *cred, struct mount *mp,
+ struct label *mntlabel, struct label *fslabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+3+|См. crossref:mac[mac-mpo-create-mount, `mpo_create_mount`].
+|===
+
+Заполнить метки на точке монтирования, создаваемой переданными учетными данными субъекта. Этот вызов будет выполнен при монтировании корневой файловой системы после `mpo_create_mount;`.
+
+[[mac-mpo-relabel-vnode]]
+===== `mpo_relabel_vnode`
+
+[source, c]
+----
+void mpo_relabel_vnode(struct ucred *cred, struct vnode *vp,
+ struct label *vnodelabel, struct label *newlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|vnode для перемаркировки
+|
+
+|`vnodelabel`
+|Существующая метка политики для `vp`
+|
+
+|`newlabel`
+|Новая, возможно частичная метка для замены `vnodelabel`
+|
+|===
+
+Обновить метку на переданном vnode с учетом переданной обновленной метки vnode и переданных учетных данных субъекта.
+
+[[mac-mpo-setlabel-vnode-extattr]]
+===== `mpo_setlabel_vnode_extattr`
+
+[source, c]
+----
+int mpo_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp,
+ struct label *vlabel, struct label *intlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|vnode, для которой записывается метка
+|
+
+|`vlabel`
+|Метка политики, связанная с `vp`
+|
+
+|`intlabel`
+|Метка для записи
+|
+|===
+
+Записать политику из `intlabel` в расширенный атрибут. Этот метод вызывается из `vop_stdcreatevnode_ea`.
+
+[[mac-mpo-update-devfsdirent]]
+===== `mpo_update_devfsdirent`
+
+[source, c]
+----
+void mpo_update_devfsdirent(struct devfs_dirent *devfs_dirent,
+ struct label *direntlabel, struct vnode *vp, struct label *vnodelabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`devfs_dirent`
+|Объект; каталожная запись devfs
+|
+
+|`direntlabel`
+|Метка политики для `devfs_dirent`, которая будет обновлена.
+|
+
+|`vp`
+|Родительский vnode
+|Заблокирован
+
+|`vnodelabel`
+|Метка политики для `vp`
+|
+|===
+
+Обновить метку `devfs_dirent` из переданной метки devfs vnode. Этот вызов будет выполнен, когда devfs vnode успешно перемаркирован, чтобы зафиксировать изменение метки, чтобы оно сохранилось, даже если vnode будет переиспользован. Он также будет выполнен при создании символьной ссылки в devfs после вызова `mac_vnode_create_from_vnode` для инициализации метки vnode.
+
+[[mac-ipc-label-ops]]
+==== Действия с событиями меток объектов IPC
+
+[[mac-mpo-create-mbuf-from-socket]]
+===== `mpo_create_mbuf_from_socket`
+
+[source, c]
+----
+void mpo_create_mbuf_from_socket(struct socket *so, struct label *socketlabel,
+ struct mbuf *m, struct label *mbuflabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`socket`
+|Сокет
+|Блокировка сокетов — работа в процессе
+
+|`socketlabel`
+|Метка политики для `socket`
+|
+
+|`m`
+|Объект; mbuf
+|
+
+|`mbuflabel`
+|Метка политики для заполнения для `m`
+|
+|===
+
+Установить метку на только что созданном заголовке mbuf из переданной метки сокета. Этот вызов выполняется, когда новый датаграмма или сообщение генерируется сокетом и сохраняется в переданном mbuf.
+
+[[mac-mpo-create-pipe]]
+===== `mpo_create_pipe`
+
+[source, c]
+----
+void mpo_create_pipe(struct ucred *cred, struct pipe *pipe,
+ struct label *pipelabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`pipe`
+|Канал
+|
+
+|`pipelabel`
+|Метка политики, связанная с `pipe`
+|
+|===
+
+Установить метку на только что созданном канале из переданных учетных данных субъекта. Этот вызов выполняется при создании нового канала.
+
+[[mac-mpo-create-socket]]
+===== `mpo_create_socket`
+
+[source, c]
+----
+void mpo_create_socket(struct ucred *cred, struct socket *so,
+ struct label *socketlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|Неизменяемый
+
+|`so`
+|Объект; сокет для добавления метки
+|
+
+|`socketlabel`
+|Метка для заполнения для `so`
+|
+|===
+
+Установить метку на новом сокете из переданных учетных данных субъекта. Этот вызов выполняется при создании сокета.
+
+[[mac-mpo-create-socket-from-socket]]
+===== `mpo_create_socket_from_socket`
+
+[source, c]
+----
+void mpo_create_socket_from_socket(struct socket *oldsocket,
+ struct label *oldsocketlabel, struct socket *newsocket,
+ struct label *newsocketlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`oldsocket`
+|Сокет, вызвавший listen
+|
+
+|`oldsocketlabel`
+|Метка политики, связанная с `oldsocket`
+|
+
+|`newsocket`
+|Новый сокет
+|
+
+|`newsocketlabel`
+|Метка политики, связанная с `newsocketlabel`
+|
+|===
+
+Создать метку сокета `newsocket`, только что принявшему соединение через man:accept[2], на основе сокета `oldsocket`, вызвавшего man:listen[2] .
+
+[[mac-mpo-relabel-pipe]]
+===== `mpo_relabel_pipe`
+
+[source, c]
+----
+void mpo_relabel_pipe(struct ucred *cred, struct pipe *pipe,
+ struct label *oldlabel, struct label *newlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`pipe`
+|Канал
+|
+
+|`oldlabel`
+|Текущая метка политики, связанная с `pipe`
+|
+
+|`newlabel`
+|Обновление метки политики для применения к `pipe`
+|
+|===
+
+Применить новую метку `newlabel` к `pipe`.
+
+[[mac-mpo-relabel-socket]]
+===== `mpo_relabel_socket`
+
+[source, c]
+----
+void mpo_relabel_socket(struct ucred *cred, struct socket *so,
+ struct label *oldlabel, struct label *newlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|Неизменяемый
+
+|`so`
+|Объект; сокет
+|
+
+|`oldlabel`
+|Текущая метка для `so`
+|
+
+|`newlabel`
+|Метка обновления для `so`
+|
+|===
+
+Обновить метку на сокете из переданного обновления метки сокета.
+
+[[mpo-set-socket-peer-from-mbuf]]
+===== `mpo_set_socket_peer_from_mbuf`
+
+[source, c]
+----
+void mpo_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct label *mbuflabel,
+ struct label *oldlabel, struct label *newlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`mbuf`
+|Первый датаграмм, полученный через сокет
+|
+
+|`mbuflabel`
+|Метка для `mbuf`
+|
+
+|`oldlabel`
+|Текущая метка для сокета
+|
+
+|`newlabel`
+|Метка политики для заполнения сокета
+|
+|===
+
+Установить метку однорангового узла на потоковом сокете из переданной метки mbuf. Этот вызов будет выполнен при получении первого датаграммы потоковым сокетом, за исключением сокетов домена Unix.
+
+[[mac-mpo-set-socket-peer-from-socket]]
+===== `mpo_set_socket_peer_from_socket`
+
+[source, c]
+----
+void mpo_set_socket_peer_from_socket(struct socket *oldsocket,
+ struct label *oldsocketlabel, struct socket *newsocket,
+ struct label *newsocketpeerlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`oldsocket`
+|Локальный сокет
+|
+
+|`oldsocketlabel`
+|Метка политики для `oldsocket`
+|
+
+|`newsocket`
+|Сокет однорангового узла (peer socket)
+|
+
+|`newsocketpeerlabel`
+|Метка политики для заполнения для `newsocket`
+|
+|===
+
+Установите метку однорангового узла на потоковом UNIX-сокете из переданной конечной точки удаленного сокета. Этот вызов будет выполнен при соединении пары сокетов и будет произведен для обеих конечных точек.
+
+[[mac-net-labeling-event-ops]]
+==== Действия с событиями меток сетевых объектов
+
+[[mac-mpo-create-bpfdesc]]
+===== `mpo_create_bpfdesc`
+
+[source, c]
+----
+void mpo_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d,
+ struct label *bpflabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|Неизменяемый
+
+|`bpf_d`
+|Объект; дескриптор bpf
+|
+
+|`bpf`
+|Метка политики для заполнения для `bpf_d`
+|
+|===
+
+Установить метку на новом дескрипторе BPF из переданных учётных данных субъекта. Этот вызов будет выполнен при открытии узла устройства BPF процессом с переданными учётными данными субъекта.
+
+[[mac-mpo-create-ifnet]]
+===== `mpo_create_ifnet`
+
+[source, c]
+----
+void mpo_create_ifnet(struct ifnet *ifnet, struct label *ifnetlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`ifnet`
+|Сетевой интерфейс
+|
+
+|`ifnetlabel`
+|Метка политики для заполнения для `ifnet`
+|
+|===
+
+Установить метку на вновь созданном интерфейсе. Этот вызов может быть выполнен, когда новое физическое устройство становится доступным системе, или когда псевдо-интерфейс создаётся во время загрузки или в результате действия пользователя.
+
+[[mac-mpo-create-ipq]]
+===== `mpo_create_ipq`
+
+[source, c]
+----
+void mpo_create_ipq(struct mbuf *fragment, struct label *fragmentlabel,
+ struct ipq *ipq, struct label *ipqlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`fragment`
+|Первый полученный IP-фрагмент
+|
+
+|`fragmentlabel`
+|Метка политики для `fragment`
+|
+
+|`ipq`
+|Очередь повторной сборки IP, которой добавляетя метка
+|
+
+|`ipqlabel`
+|Метка политики для заполнения в `ipq`
+|
+|===
+
+Установить метку на вновь созданной очереди сборки IP-фрагментов из заголовка mbuf первого полученного фрагмента.
+
+[[mac-mpo-create-datagram-from-ipq]]
+===== `mpo_create_datagram_from_ipq`
+
+[source, c]
+----
+void mpo_create_create_datagram_from_ipq(struct ipq *ipq,
+ struct label *ipqlabel, struct mbuf *datagram, struct label *datagramlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`ipq`
+|Очередь повторной сборки IP
+|
+
+|`ipqlabel`
+|Метка политики для `ipq`
+|
+
+|`datagram`
+|Датаграмма для добавления метки
+|
+
+|`datagramlabel`
+|Метка политики для заполнения в `datagramlabel`
+|
+|===
+
+Установите метку на вновь собранный IP-датаграмму из очереди сборки IP-фрагментов, из которой он был сгенерирован.
+
+[[mac-mpo-create-fragment]]
+===== `mpo_create_fragment`
+
+[source, c]
+----
+void mpo_create_fragment(struct mbuf *datagram, struct label *datagramlabel,
+ struct mbuf *fragment, struct label *fragmentlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`datagram`
+|Датаграмма
+|
+
+|`datagramlabel`
+|Метка политики для `datagram`
+|
+
+|`fragment`
+|Фрагмент, которому будет установлена метка
+|
+
+|`fragmentlabel`
+|Метка политики для заполнения для `datagram`
+|
+|===
+
+Установить метку на заголовке mbuf вновь созданного IP-фрагмента из метки на заголовке mbuf датаграммы, из которой он был сгенерирован.
+
+[[mac-mpo-create-mbuf-from-mbuf]]
+===== `mpo_create_mbuf_from_mbuf`
+
+[source, c]
+----
+void mpo_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct label *oldmbuflabel,
+ struct mbuf *newmbuf, struct label *newmbuflabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`oldmbuf`
+|Существующий (исходный) mbuf
+|
+
+|`oldmbuflabel`
+|Метка политики для `oldmbuf`
+|
+
+|`newmbuf`
+|Новый mbuf для добавления метки
+|
+
+|`newmbuflabel`
+|Метка политики для заполнения в `newmbuf`
+|
+|===
+
+Установить метку в заголовке mbuf для вновь созданной датаграммы на основе заголовка mbuf существующей датаграммы. Этот вызов может быть выполнен в ряде ситуаций, включая случаи, когда для mbuf заново выделяется память для целей выравнивания.
+
+[[mac-mpo-create-mbuf-linklayer]]
+===== `mpo_create_mbuf_linklayer`
+
+[source, c]
+----
+void mpo_create_mbuf_linklayer(struct ifnet *ifnet, struct label *ifnetlabel,
+ struct mbuf *mbuf, struct label *mbuflabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`ifnet`
+|Сетевой интерфейс
+|
+
+|`ifnetlabel`
+|Метка политики для `ifnet`
+|
+
+|`mbuf`
+|заголовок mbuf для новой датаграммы
+|
+
+|`mbuflabel`
+|Метка политики для заполнения для `mbuf`
+|
+|===
+
+Установить метку в заголовке mbuf для вновь созданной датаграммы, сгенерированного для целей ответа на канальном уровне для переданного интерфейса. Этот вызов может быть выполнен в ряде ситуаций, включая ответы ARP или ND6 в стеках IPv4 и IPv6.
+
+[[mac-mpo-create-mbuf-from-bpfdesc]]
+===== `mpo_create_mbuf_from_bpfdesc`
+
+[source, c]
+----
+void mpo_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct label *bpflabel,
+ struct mbuf *mbuf, struct label *mbuflabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`bpf_d`
+|Дескриптор BPF
+|
+
+|`bpflabel`
+|Метка политики для `bpflabel`
+|
+
+|`mbuf`
+|Новый mbuf для добавления метки
+|
+
+|`mbuflabel`
+|Метка политики для заполнения `mbuf`
+|
+|===
+
+Установить метку на заголовок mbuf вновь созданной датаграммы, сгенерированной с использованием переданного дескриптора BPF. Этот вызов выполняется при записи в устройство BPF, связанное с переданным дескриптором BPF.
+
+[[mac-mpo-create-mbuf-from-ifnet]]
+===== `mpo_create_mbuf_from_ifnet`
+
+[source, c]
+----
+void mpo_create_mbuf_from_ifnet(struct ifnet *ifnet, struct label *ifnetlabel,
+ struct mbuf *mbuf, struct label *mbuflabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`ifnet`
+|Сетевой интерфейс
+|
+
+|`ifnetlabel`
+|Метка политики для `ifnetlabel`
+|
+
+|`mbuf`
+|заголовок mbuf для новой датаграммы
+|
+
+|`mbuflabel`
+|Метка политики для заполнения для `mbuf`
+|
+|===
+
+Установить метку на заголовке mbuf вновь созданной датаграммы, сгенерированной из переданного сетевого интерфейса.
+
+[[mac-mpo-create-mbuf-multicast-encap]]
+===== `mpo_create_mbuf_multicast_encap`
+
+[source, c]
+----
+void mpo_create_mbuf_multicast_encap(struct mbuf *oldmbuf,
+ struct label *oldmbuflabel, struct ifnet *ifnet, struct label *ifnetlabel,
+ struct mbuf *newmbuf, struct label *newmbuflabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`oldmbuf`
+|Заголовок mbuf для существующего датаграммы
+|
+
+|`oldmbuflabel`
+|Метка политики для `oldmbuf`
+|
+
+|`ifnet`
+|Сетевой интерфейс
+|
+
+|`ifnetlabel`
+|Метка политики для `ifnet`
+|
+
+|`newmbuf`
+|Заголовок mbuf для пометки новой датаграммы
+|
+
+|`newmbuflabel`
+|Метка политики для заполнения в `newmbuf`
+|
+|===
+
+Установить метку в заголовке mbuf для вновь созданной датаграммы, сгенерированной из существующей переданной датаграммы, при её обработке переданным интерфейсом мультикастовой инкапсуляции. Этот вызов происходит при доставке mbuf с использованием виртуального интерфейса.
+
+[[mac-mpo-create-mbuf-netlayer]]
+===== `mpo_create_mbuf_netlayer`
+
+[source, c]
+----
+void mpo_create_mbuf_netlayer(struct mbuf *oldmbuf, struct label *oldmbuflabel,
+ struct mbuf *newmbuf, struct label *newmbuflabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`oldmbuf`
+|Полученная датаграмма
+|
+
+|`oldmbuflabel`
+|Метка политики для `oldmbuf`
+|
+
+|`newmbuf`
+|Вновь созданная датаграмма
+|
+
+|`newmbuflabel`
+|Метка политики для `newmbuf`
+|
+|===
+
+Установить метку на заголовок mbuf вновь созданной датаграммы, сгенерированной стеком IP в ответ на полученную датаграмму (`oldmbuf`). Этот вызов может быть выполнен в различных ситуациях, включая ответ на датаграммы ICMP-запросов.
+
+[[mac-mpo-fragment-match]]
+===== `mpo_fragment_match`
+
+[source, c]
+----
+int mpo_fragment_match(struct mbuf *fragment, struct label *fragmentlabel,
+ struct ipq *ipq, struct label *ipqlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`fragment`
+|Фрагмент IP-датаграммы
+|
+
+|`fragmentlabel`
+|Метка политики для `fragment`
+|
+
+|`ipq`
+|Очередь сборки IP-фрагментов
+|
+
+|`ipqlabel`
+|Метка политики для `ipq`
+|
+|===
+
+Определить, соответствует ли заголовок mbuf, содержащий фрагмент IP-датаграммы (`fragment`), метке переданной очереди сборки IP-фрагментов (`ipq`). Возвращает (1) при успешном совпадении или (0) при отсутствии совпадения. Этот вызов выполняется, когда IP-стек пытается найти существующую очередь сборки фрагментов для вновь полученного фрагмента; если поиск не удаётся, для фрагмента может быть создана новая очередь сборки. Политики могут использовать эту точку входа, чтобы предотвратить сборку в остальном подходящих IP-фрагментов, если политика не разрешает их сборку на основе метки или другой информации.
+
+[[mac-mpo-ifnet-relabel]]
+===== `mpo_relabel_ifnet`
+
+[source, c]
+----
+void mpo_relabel_ifnet(struct ucred *cred, struct ifnet *ifnet,
+ struct label *ifnetlabel, struct label *newlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`ifnet`
+|Объект; Сетевой интерфейс
+|
+
+|`ifnetlabel`
+|Метка политики для `ifnet`
+|
+
+|`newlabel`
+|Метка обновления для применения к `ifnet`
+|
+|===
+
+Обновить метку сетевого интерфейса, `ifnet`, на основе переданной новой метки, `newlabel`, и переданных учетных данных субъекта, `cred`.
+
+[[mac-mpo-update-ipq]]
+===== `mpo_update_ipq`
+
+[source, c]
+----
+void mpo_update_ipq(struct mbuf *fragment, struct label *fragmentlabel,
+ struct ipq *ipq, struct label *ipqlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`mbuf`
+|IP фрагмент
+|
+
+|`mbuflabel`
+|Метка политики для `mbuf`
+|
+
+|`ipq`
+|Очередь сборки IP-фрагментов
+|
+
+|`ipqlabel`
+|Метка политики для обновления для `ipq`
+|
+|===
+
+Обновить метку в очереди сборки IP-фрагментов (`ipq`) на основе принятия переданного заголовка IP-фрагмента mbuf (`mbuf`).
+
+[[mac-proc-labeling-event-ops]]
+==== Действия с событиями меток процессов
+
+[[mac-mpo-create-cred]]
+===== `mpo_create_cred`
+
+[source, c]
+----
+void mpo_create_cred(struct ucred *parent_cred, struct ucred *child_cred);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`parent_cred`
+|Учетные данные субъекта‐родителя
+|
+
+|`child_cred`
+|Учётные данные дочернего субъекта
+|
+|===
+
+Установить метку вновь созданного субъекта из переданного субъекта. Этот вызов будет выполнен при вызове man:crcopy[9] для только что созданной структуры `struct ucred`. Этот вызов не следует путать с событием создания или ветвления процесса.
+
+[[mac-mpo-execve-transition]]
+===== `mpo_execve_transition`
+
+[source, c]
+----
+void mpo_execve_transition(struct ucred *old, struct ucred *new,
+ struct vnode *vp, struct label *vnodelabel);
+
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`old`
+|Учетные данные существующего субъекта
+|Неизменяемый
+
+|`new`
+|Учетные данные нового субъекта для добавления метки
+|
+
+|`vp`
+|Файл для выполнения
+|Заблокирован
+
+|`vnodelabel`
+|Метка политики для `vp`
+|
+|===
+
+Обновить метку учетных данных вновь созданного субъекта (`new`) на основе переданных учетных данных существующего субъекта (`old`) в соответствии с переходом метки, вызванным выполнением переданного vnode (`vp`). Этот вызов происходит, когда процесс выполняет переданный vnode, и одна из политик возвращает успех из точки входа `mpo_execve_will_transition`. Политики могут выбрать реализацию этого вызова просто путем вызова `mpo_create_cred` и передачи двух субъектов учетных данных, чтобы не реализовывать событие перехода. Политики не должны оставлять эту точку входа нереализованной, если они реализуют `mpo_create_cred`, даже если они не реализуют `mpo_execve_will_transition`.
+
+[[mac-mpo-execve-will-transition]]
+===== `mpo_execve_will_transition`
+
+[source, c]
+----
+int mpo_execve_will_transition(struct ucred *old, struct vnode *vp,
+ struct label *vnodelabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`old`
+|Учетные данные субъекта перед man:execve[2]
+|Неизменяемый
+
+|`vp`
+|Файл для выполнения
+|
+
+|`vnodelabel`
+|Метка политики для `vp`
+|
+|===
+
+Определить, будет ли политика выполнять событие перехода в результате выполнения переданного vnode с использованием переданных учетных данных субъекта. Вернуть 1, если переход требуется, и 0, если нет. Даже если политика возвращает 0, она должна корректно обрабатывать неожиданный вызов `mpo_execve_transition`, так как этот вызов может произойти из-за запроса перехода другой политикой.
+
+[[mac-mpo-create-proc0]]
+===== `mpo_create_proc0`
+
+[source, c]
+----
+void mpo_create_proc0(struct ucred *cred);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта для заполнения
+|
+|===
+
+Создать учетные данные субъекта процесса 0, родителя всех процессов ядра.
+
+[[mac-mpo-create-proc1]]
+===== `mpo_create_proc1`
+
+[source, c]
+----
+void mpo_create_proc1(struct ucred *cred);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта для заполнения
+|
+|===
+
+Создать учетные данные субъекта процесса 1, родителя всех пользовательских процессов.
+
+[[mac-mpo-relabel-cred]]
+===== `mpo_relabel_cred`
+
+[source, c]
+----
+void mpo_relabel_cred(struct ucred *cred, struct label *newlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`newlabel`
+|Обновление метки для применения к `cred`
+|
+|===
+
+Обновить метку на учетных данных субъекта из переданной обновляемой метки.
+
+[[mac-access-control-checks]]
+=== Проверки контроля доступа
+
+Точки входа контроля доступа позволяют модулям политики влиять на решения по контролю доступа, принимаемые ядром. Обычно, хотя и не всегда, аргументы точки входа контроля доступа включают одно или несколько удостоверяющих полномочий, информацию (возможно, включая метку) для любых других объектов, участвующих в операции. Точка входа контроля доступа может вернуть 0 для разрешения операции или значение ошибки man:errno[2]. Результаты вызова точки входа через различные зарегистрированные модули политики будут объединены следующим образом: если все модули разрешают успешное выполнение операции, будет возвращен успех. Если один или несколько модулей возвращают ошибку, будет возвращена ошибка. Если более одного модуля возвращают ошибку, значение errno, которое будет возвращено пользователю, выбирается с использованием следующего приоритета, реализованного функцией `error_select()` в [.filename]#kern_mac.c#:
+
+[.informaltable]
+[cols="1,1", frame="none"]
+|===
+
+|Наивысший приоритет
+|EDEADLK
+
+|
+|EINVAL
+
+|
+|ESRCH
+
+|
+|EACCES
+
+|Наименьший приоритет
+|EPERM
+|===
+
+Если ни одно из значений ошибок, возвращаемых всеми модулями, не указано в таблице приоритетов, будет возвращено произвольно выбранное значение из набора. В общем случае правила устанавливают следующий порядок приоритетов ошибок: сбои ядра, неверные аргументы, отсутствие объекта, отсутствие доступа, прочие.
+
+[[mac-mpo-bpfdesc-check-receive-from-ifnet]]
+==== `mpo_check_bpfdesc_receive`
+
+[source, c]
+----
+int mpo_check_bpfdesc_receive(struct bpf_d *bpf_d, struct label *bpflabel,
+ struct ifnet *ifnet, struct label *ifnetlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`bpf_d`
+|Субъект; Дескриптор BPF
+|
+
+|`bpflabel`
+|Метка политики для `bpf_d`
+|
+
+|`ifnet`
+|Объект; сетевой интерфейс
+|
+
+|`ifnetlabel`
+|Метка политики для `ifnet`
+|
+|===
+
+Определить, должен ли framework MAC разрешать доставку датаграмм с переданного интерфейса в буферы переданного BPF-дескриптора. Возвращает (0) при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии меток, EPERM при отсутствии привилегий.
+
+[[mac-mpo-check-kenv-dump]]
+==== `mpo_check_kenv_dump`
+
+[source, c]
+----
+int mpo_check_kenv_dump(struct ucred *cred);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+|===
+
+Определить, следует ли разрешить субъекту получать доступ к окружению ядра (см. man:kenv[2]).
+
+[[mac-mpo-check-kenv-get]]
+==== `mpo_check_kenv_get`
+
+[source, c]
+----
+int mpo_check_kenv_get(struct ucred *cred, char *name);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`name`
+|Имя переменной окружения ядра
+|
+|===
+
+Определить, следует ли разрешить субъекту получать значение указанной переменной окружения ядра.
+
+[[mac-mpo-check-kenv-set]]
+==== `mpo_check_kenv_set`
+
+[source, c]
+----
+int mpo_check_kenv_set(struct ucred *cred, char *name);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`name`
+|Имя переменной окружения ядра
+|
+|===
+
+Определить, следует ли разрешить субъекту устанавливать указанную переменную окружения ядра.
+
+[[mac-mpo-check-kenv-unset]]
+==== `mpo_check_kenv_unset`
+
+[source, c]
+----
+int mpo_check_kenv_unset(struct ucred *cred, char *name);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`name`
+|Имя переменной окружения ядра
+|
+|===
+
+Определить, следует ли разрешить субъекту сбросить указанную переменную окружения ядра.
+
+[[mac-mpo-check-kld-load]]
+==== `mpo_check_kld_load`
+
+[source, c]
+----
+int mpo_check_kld_load(struct ucred *cred, struct vnode *vp,
+ struct label *vlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|vnode модуля ядра
+|
+
+|`vlabel`
+|Метка, связанная с `vp`
+|
+|===
+
+Определить, следует ли разрешить субъекту загружать указанный файл модуля.
+
+[[mac-mpo-check-kld-stat]]
+==== `mpo_check_kld_stat`
+
+[source, c]
+----
+int mpo_check_kld_stat(struct ucred *cred);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+|===
+
+Определить, следует ли разрешить субъекту получать список загруженных файлов модулей ядра и связанную с ними статистику.
+
+[[mac-mpo-check-kld-unload]]
+==== `mpo_check_kld_unload`
+
+[source, c]
+----
+int mpo_check_kld_unload(struct ucred *cred);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+|===
+
+Определить, следует ли разрешить субъекту выгружать модуль ядра.
+
+[[mac-mpo-check-pipe-ioctl]]
+==== `mpo_check_pipe_ioctl`
+
+[source, c]
+----
+int mpo_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe,
+ struct label *pipelabel, unsigned long cmd, void *data);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`pipe`
+|Канал
+|
+
+|`pipelabel`
+|Метка политики, связанная с `pipe`
+|
+
+|`cmd`
+|команда nman:ioctl[2]
+|
+
+|`data`
+|данные man:ioctl[2]
+|
+|===
+
+Определить, следует ли разрешить субъекту выполнять указанный вызов man:ioctl[2].
+
+[[mac-mpo-check-pipe-poll]]
+==== `mpo_check_pipe_poll`
+
+[source, c]
+----
+int mpo_check_pipe_poll(struct ucred *cred, struct pipe *pipe,
+ struct label *pipelabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`pipe`
+|Канал
+|
+
+|`pipelabel`
+|Метка политики, связанная с `pipe`
+|
+|===
+
+Определить, следует ли разрешить субъекту опрашивать `pipe`.
+
+[[mac-mpo-check-pipe-read]]
+==== `mpo_check_pipe_read`
+
+[source, c]
+----
+int mpo_check_pipe_read(struct ucred *cred, struct pipe *pipe,
+ struct label *pipelabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`pipe`
+|Канал
+|
+
+|`pipelabel`
+|Метка политики, связанная с `pipe`
+|
+|===
+
+Определить, следует ли разрешить субъекту доступ на чтение к `pipe`.
+
+[[mac-mpo-check-pipe-relabel]]
+==== `mpo_check_pipe_relabel`
+
+[source, c]
+----
+int mpo_check_pipe_relabel(struct ucred *cred, struct pipe *pipe,
+ struct label *pipelabel, struct label *newlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`pipe`
+|Канал
+|
+
+|`pipelabel`
+|Текущая метка политики, связанная с `pipe`
+|
+
+|`newlabel`
+|Обновление метки до `pipelabel`
+|
+|===
+
+Определить, следует ли разрешить субъекту изменять метку `pipe`.
+
+[[mac-mpo-check-pipe-stat]]
+==== `mpo_check_pipe_stat`
+
+[source, c]
+----
+int mpo_check_pipe_stat(struct ucred *cred, struct pipe *pipe,
+ struct label *pipelabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`pipe`
+|Канал
+|
+
+|`pipelabel`
+|Метка политики, связанная с `pipe`
+|
+|===
+
+Определить, следует ли разрешить субъекту получать статистику, связанную с `pipe`.
+
+[[mac-mpo-check-pipe-write]]
+==== `mpo_check_pipe_write`
+
+[source, c]
+----
+int mpo_check_pipe_write(struct ucred *cred, struct pipe *pipe,
+ struct label *pipelabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`pipe`
+|Канал
+|
+
+|`pipelabel`
+|Метка политики, связанная с `pipe`
+|
+|===
+
+Определить, следует ли разрешить субъекту запись в `pipe`.
+
+[[mac-mpo-cred-check-socket-bind]]
+==== `mpo_check_socket_bind`
+
+[source, c]
+----
+int mpo_check_socket_bind(struct ucred *cred, struct socket *socket,
+ struct label *socketlabel, struct sockaddr *sockaddr);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`socket`
+|Сокет для привязки
+|
+
+|`socketlabel`
+|Метка политики для `socket`
+|
+
+|`sockaddr`
+|Адрес `socket`
+|
+|===
+
+[[mac-mpo-cred-check-socket-connect]]
+==== `mpo_check_socket_connect`
+
+[source, c]
+----
+int mpo_check_socket_connect(struct ucred *cred, struct socket *socket,
+ struct label *socketlabel, struct sockaddr *sockaddr);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`socket`
+|Сокет для подключения
+|
+
+|`socketlabel`
+|Метка политики для `socket`
+|
+
+|`sockaddr`
+|Адрес `socket`
+|
+|===
+
+Определить, может ли субъект с учётными данными (`cred`) подключить переданный сокет (`socket`) к переданному адресу сокета (`sockaddr`). Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии меток, EPERM при отсутствии прав.
+
+[[mac-mpo-check-socket-receive]]
+==== `mpo_check_socket_receive`
+
+[source, c]
+----
+int mpo_check_socket_receive(struct ucred *cred, struct socket *so,
+ struct label *socketlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`so`
+|Сокет
+|
+
+|`socketlabel`
+|Метка политики, связанная с `so`
+|
+|===
+
+Определить, следует ли разрешить субъекту получать информацию из сокета `so`.
+
+[[mac-mpo-check-socket-send]]
+==== `mpo_check_socket_send`
+
+[source, c]
+----
+int mpo_check_socket_send(struct ucred *cred, struct socket *so,
+ struct label *socketlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`so`
+|Сокет
+|
+
+|`socketlabel`
+|Метка политики, связанная с `so`
+|
+|===
+
+Определить, следует ли разрешить субъекту передавать информацию через сокет `so`.
+
+[[mac-mpo-check-cred-visible]]
+==== `mpo_check_cred_visible`
+
+[source, c]
+----
+int mpo_check_cred_visible(struct ucred *u1, struct ucred *u2);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`u1`
+|Учетные данные субъекта
+|
+
+|`u2`
+|Учетные данные объекта
+|
+|===
+
+Определить, может ли субъект с учётными данными `u1` "видеть" другие субъекты с переданными учётными данными `u2`. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии меток, EPERM при отсутствии привилегий или ESRCH для скрытия видимости. Этот вызов может выполняться в различных ситуациях, включая системные вызовы состояния межпроцессного взаимодействия, используемые `ps`, и при поиске в procfs.
+
+[[mac-mpo-cred-check-socket-visible]]
+==== `mpo_check_socket_visible`
+
+[source, c]
+----
+int mpo_check_socket_visible(struct ucred *cred, struct socket *socket,
+ struct label *socketlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`socket`
+|Объект; сокет
+|
+
+|`socketlabel`
+|Метка политики для `socket`
+|
+|===
+
+[[mac-mpo-cred-check-ifnet-relabel]]
+==== `mpo_check_ifnet_relabel`
+
+[source, c]
+----
+int mpo_check_ifnet_relabel(struct ucred *cred, struct ifnet *ifnet,
+ struct label *ifnetlabel, struct label *newlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`ifnet`
+|Объект; сетевой интерфейс
+|
+
+|`ifnetlabel`
+|Существующая метка политики для `ifnet`
+|
+
+|`newlabel`
+|Обновление метки политики для последующего применения к `ifnet`
+|
+|===
+
+Определить, может ли учетные данные субъекта перемаркировать переданный сетевой интерфейс в соответствии с переданным обновлением метки.
+
+[[mac-mpo-cred-check-socket-relabel]]
+==== `mpo_check_socket_relabel`
+
+[source, c]
+----
+int mpo_check_socket_relabel(struct ucred *cred, struct socket *socket,
+ struct label *socketlabel, struct label *newlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`socket`
+|Объект; сокет
+|
+
+|`socketlabel`
+|Метка существующей политики для `socket`
+|
+
+|`newlabel`
+|Обновление метки для последующего применения к `socketlabel`
+|
+|===
+
+Определить, могут ли учётные данные субъекта перемаркировать переданный сокет в соответствии с переданным обновлением метки.
+
+[[mac-mpo-cred-check-cred-relabel]]
+==== `mpo_check_cred_relabel`
+
+[source, c]
+----
+int mpo_check_cred_relabel(struct ucred *cred, struct label *newlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`newlabel`
+|Обновление метки для последующего применения к `cred`
+|
+|===
+
+Определить, могут ли учетные данные субъекта перемаркировать себя в соответствии с переданным обновлением метки.
+
+[[mac-mpo-cred-check-vnode-relabel]]
+==== `mpo_check_vnode_relabel`
+
+[source, c]
+----
+int mpo_check_vnode_relabel(struct ucred *cred, struct vnode *vp,
+ struct label *vnodelabel, struct label *newlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|Неизменяемый
+
+|`vp`
+|Объект; vnode
+|Заблокирован
+
+|`vnodelabel`
+|Существующая метка политики для `vp`
+|
+
+|`newlabel`
+|Обновление метки политики для последующего применения к `vp`
+|
+|===
+
+Определить, могут ли учётные данные субъекта изменить метку переданного vnode на переданную обновлённую метку.
+
+[[mpo-cred-check-mount-stat]]
+==== `mpo_check_mount_stat`
+
+[source, c]
+----
+int mpo_check_mount_stat(struct ucred *cred, struct mount *mp,
+ struct label *mountlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`mp`
+|Объект; точка монтирования файловой системы
+|
+
+|`mountlabel`
+|Метка политики для `mp`
+|
+|===
+
+Определить, могут ли учетные данные субъекта видеть результаты выполнения statfs для файловой системы. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии меток или EPERM при отсутствии привилегий. Этот вызов может выполняться в различных ситуациях, включая вызовы man:statfs[2] и связанных функций, а также для определения, какие файловые системы исключать из списка, например, при вызове man:getfsstat[2].
+
+[[mac-mpo-cred-check-proc-debug]]
+==== `mpo_check_proc_debug`
+
+[source, c]
+----
+int mpo_check_proc_debug(struct ucred *cred, struct proc *proc);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|Неизменяемый
+
+|`proc`
+|Объект; процесс
+|
+|===
+
+Определить, могут ли учётные данные субъекта отлаживать переданный процесс. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки, EPERM при недостатке прав или ESRCH для скрытия видимости цели. Этот вызов может использоваться в различных ситуациях, включая использование API man:ptrace[2] и man:ktrace[2], а также для некоторых операций с procfs.
+
+[[mac-mpo-cred-check-vnode-access]]
+==== `mpo_check_vnode_access`
+
+[source, c]
+----
+int mpo_check_vnode_access(struct ucred *cred, struct vnode *vp,
+ struct label *label, int flags);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode
+|
+
+|`label`
+|Метка политики для `vp`
+|
+
+|`flags`
+|флаги man:access[2]
+|
+|===
+
+Определить, как должны возвращаться вызовы man:access[2] и связанные вызовы для субъекта с указанными учетными данными при выполнении на переданном vnode с использованием переданных флагов доступа. Обычно это должно быть реализовано с использованием той же семантики, что и в `mpo_check_vnode_open`. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии меток или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-vnode-chdir]]
+==== `mpo_check_vnode_chdir`
+
+[source, c]
+----
+int mpo_check_vnode_chdir(struct ucred *cred, struct vnode *dvp,
+ struct label *dlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`dvp`
+|Объект; vnode, в который делается man:chdir[2]
+|
+
+|`dlabel`
+|Метка политики для `dvp`
+|
+|===
+
+Определить, могут ли учётные данные субъекта изменить рабочий каталог процесса на переданный vnode. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-check-vnode-chroot]]
+==== `mpo_check_vnode_chroot`
+
+[source, c]
+----
+int mpo_check_vnode_chroot(struct ucred *cred, struct vnode *dvp,
+ struct label *dlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`dvp`
+|vnode каталога
+|
+
+|`dlabel`
+|Метка политики, связанная с `dvp`
+|
+|===
+
+Определить, следует ли разрешить субъекту выполнять man:chroot[2] в указанный каталог (`dvp`).
+
+[[mac-mpo-cred-check-vnode-create]]
+==== `mpo_check_vnode_create`
+
+[source, c]
+----
+int mpo_check_vnode_create(struct ucred *cred, struct vnode *dvp,
+ struct label *dlabel, struct componentname *cnp, struct vattr *vap);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`dvp`
+|Объект; vnode
+|
+
+|`dlabel`
+|Метка политики для `dvp`
+|
+
+|`cnp`
+|Название компонента для `dvp`
+|
+
+|`vap`
+|атрибуты vnode для `vap`
+|
+|===
+
+Определить, могут ли учетные данные субъекта создать vnode с указанной родительской директорией, информацией о имени и атрибутами. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий. Этот вызов может выполняться в различных ситуациях, включая вызовы man:open[2] с O_CREAT, man:mkfifo[2] и другие.
+
+[[mac-mpo-cred-check-vnode-delete]]
+==== `mpo_check_vnode_delete`
+
+[source, c]
+----
+int mpo_check_vnode_delete(struct ucred *cred, struct vnode *dvp,
+ struct label *dlabel, struct vnode *vp, void *label,
+ struct componentname *cnp);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`dvp`
+|Родительский каталог vnode
+|
+
+|`dlabel`
+|Метка политики для `dvp`
+|
+
+|`vp`
+|Объект; vnode для удаления
+|
+
+|`label`
+|Метка политики для `vp`
+|
+
+|`cnp`
+|Название компонента для `vp`
+|
+|===
+
+Определить, может ли субъект с данными учетными данными удалить vnode из переданного родительского каталога и переданной информации о имени. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий. Этот вызов может быть выполнен в различных ситуациях, включая вызовы man:unlink[2] и man:rmdir[2]. Политики, реализующие эту точку входа, также должны реализовывать `mpo_check_rename_to` для авторизации удаления объектов в результате их переименования.
+
+[[mac-mpo-cred-check-vnode-deleteacl]]
+==== `mpo_check_vnode_deleteacl`
+
+[source, c]
+----
+int mpo_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp,
+ struct label *label, acl_type_t type);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|Неизменяемый
+
+|`vp`
+|Объект; vnode
+|Заблокирован
+
+|`label`
+|Метка политики для `vp`
+|
+
+|`type`
+|Тип ACL
+|
+|===
+
+Определить, могут ли учетные данные субъекта удалить ACL указанного типа из переданного vnode. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-vnode-exec]]
+==== `mpo_check_vnode_exec`
+
+[source, c]
+----
+int mpo_check_vnode_exec(struct ucred *cred, struct vnode *vp,
+ struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode для выполнения
+|
+
+|`label`
+|Метка политики для `vp`
+|
+|===
+
+Определить, могут ли учётные данные субъекта выполнить переданный vnode. Проверка права на выполнение осуществляется отдельно от решений о любом переходном событии. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии прав.
+
+[[mpo-cred-check-vnode-getacl]]
+==== `mpo_check_vnode_getacl`
+
+[source, c]
+----
+int mpo_check_vnode_getacl(struct ucred *cred, struct vnode *vp,
+ struct label *label, acl_type_t type);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode
+|
+
+|`label`
+|Метка политики для `vp`
+|
+
+|`type`
+|Тип ACL
+|
+|===
+
+Определить, может ли учётное данное субъекта получить ACL указанного типа из переданного vnode. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-vnode-getextattr]]
+==== `mpo_check_vnode_getextattr`
+
+[source, c]
+----
+int mpo_check_vnode_getextattr(struct ucred *cred, struct vnode *vp,
+ struct label *label, int attrnamespace, const char *name, struct uio *uio);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode
+|
+
+|`label`
+|Метка политики для `vp`
+|
+
+|`attrnamespace`
+|Пространство имен расширенных атрибутов
+|
+
+|`name`
+|Имя расширенного атрибута
+|
+
+|`uio`
+|Указатель структуры ввода-вывода; см. man:uio[9]
+|
+|===
+
+Определить, может ли субъект с указанными учетными данными получить расширенный атрибут с заданным пространством имен и именем из указанного vnode. Политики, реализующие маркировку с использованием расширенных атрибутов, могут требовать особой обработки операций с этими атрибутами. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-check-vnode-link]]
+==== `mpo_check_vnode_link`
+
+[source, c]
+----
+int mpo_check_vnode_link(struct ucred *cred, struct vnode *dvp,
+ struct label *dlabel, struct vnode *vp, struct label *label,
+ struct componentname *cnp);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`dvp`
+|vnode каталога
+|
+
+|`dlabel`
+|Метка политики, связанная с `dvp`
+|
+
+|`vp`
+|vnode целевого линка
+|
+
+|`label`
+|Метка политики, связанная с `vp`
+|
+
+|`cnp`
+|Имя компонента для создаваемой ссылки
+|
+|===
+
+Определить, следует ли разрешить субъекту создавать ссылку на vnode `vp` с именем, указанным в `cnp`.
+
+[[mac-mpo-check-vnode-mmap]]
+==== `mpo_check_vnode_mmap`
+
+[source, c]
+----
+int mpo_check_vnode_mmap(struct ucred *cred, struct vnode *vp,
+ struct label *label, int prot);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|vnode для mmap
+|
+
+|`label`
+|Метка политики, связанная с `vp`
+|
+
+|`prot`
+|Защита mmap (см. man:mmap[2])
+|
+|===
+
+Определить, следует ли разрешить субъекту отображать vnode `vp` с указанными в `prot` правами доступа.
+
+[[mac-mpo-check-vnode-mmap-downgrade]]
+==== `mpo_check_vnode_mmap_downgrade`
+
+[source, c]
+----
+void mpo_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp,
+ struct label *label, int *prot);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|См. crossref:mac[mac-mpo-check-vnode-mmap, `mpo_check_vnode_mmap`].
+|
+
+|`vp`
+|
+|
+
+|`label`
+|
+|
+
+|`prot`
+|Защита mmap для понижения уровня
+|
+|===
+
+Понизить уровень защиты mmap на основе меток субъекта и объекта.
+
+[[mac-mpo-check-vnode-mprotect]]
+==== `mpo_check_vnode_mprotect`
+
+[source, c]
+----
+int mpo_check_vnode_mprotect(struct ucred *cred, struct vnode *vp,
+ struct label *label, int prot);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Отображенный vnode
+|
+
+|`prot`
+|Защита памяти
+|
+|===
+
+Определить, следует ли разрешить субъекту устанавливать указанные защиты памяти для памяти, отображенной из vnode `vp`.
+
+[[mac-mpo-check-vnode-poll]]
+==== `mpo_check_vnode_poll`
+
+[source, c]
+----
+int mpo_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred,
+ struct vnode *vp, struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`active_cred`
+|Учетные данные субъекта
+|
+
+|`file_cred`
+|Учетные данные, связанные со структурой file
+|
+
+|`vp`
+|vnode, на котором вызывается poll
+|
+
+|`label`
+|Метка политики, связанная с `vp`
+|
+|===
+
+Определить, следует ли разрешить субъекту вызывать poll на vnode `vp`.
+
+[[mac-mpo-check-vnode-rename-from]]
+==== `mpo_check_vnode_rename_from`
+
+[source, c]
+----
+int mpo_vnode_rename_from(struct ucred *cred, struct vnode *dvp,
+ struct label *dlabel, struct vnode *vp, struct label *label,
+ struct componentname *cnp);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`dvp`
+|vnode каталога
+|
+
+|`dlabel`
+|Метка политики, связанная с `dvp`
+|
+
+|`vp`
+|vnode для переименования
+|
+
+|`label`
+|Метка политики, связанная с `vp`
+|
+
+|`cnp`
+|Название компонента для `vp`
+|
+|===
+
+Определить, следует ли разрешить субъекту переименовать vnode `vp` во что-то другое.
+
+[[mac-mpo-check-vnode-rename-to]]
+==== `mpo_check_vnode_rename_to`
+
+[source, c]
+----
+int mpo_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp,
+ struct label *dlabel, struct vnode *vp, struct label *label, int samedir,
+ struct componentname *cnp);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`dvp`
+|vnode каталога
+|
+
+|`dlabel`
+|Метка политики, связанная с `dvp`
+|
+
+|`vp`
+|vnode, который будет перезаписан
+|
+
+|`label`
+|Метка политики, связанная с `vp`
+|
+
+|`samedir`
+|Логическое значение; `1`, если исходный и целевой каталоги совпадают
+|
+
+|`cnp`
+|Имя компонента назначения
+|
+|===
+
+Определить, следует ли разрешить субъекту переименование vnode `vp` в директорию `dvp` или в имя, представленное `cnp`. Если не существует файла для перезаписи, `vp` и `label` будут NULL.
+
+[[mac-mpo-cred-check-socket-listen]]
+==== `mpo_check_socket_listen`
+
+[source, c]
+----
+int mpo_check_socket_listen(struct ucred *cred, struct socket *socket,
+ struct label *socketlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`socket`
+|Объект; сокет
+|
+
+|`socketlabel`
+|Метка политики для `socket`
+|
+|===
+
+Определить, могут ли учётные данные субъекта прослушивать переданный сокет (вызывать listen). Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-vnode-lookup]]
+==== `mpo_check_vnode_lookup`
+
+[source, c]
+----
+int mpo_check_vnode_lookup(struct ucred *cred, struct vnode *dvp,
+ struct label *dlabel, struct componentname *cnp);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`dvp`
+|Объект; vnode
+|
+
+|`dlabel`
+|Метка политики для `dvp`
+|
+
+|`cnp`
+|Имя компонента, который ищется
+|
+|===
+
+Определить, могут ли учётные данные субъекта выполнить поиск указанного имени в каталог с переданном vnode. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-vnode-open]]
+==== `mpo_check_vnode_open`
+
+[source, c]
+----
+int mpo_check_vnode_open(struct ucred *cred, struct vnode *vp,
+ struct label *label, int acc_mode);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode
+|
+
+|`label`
+|Метка политики для `vp`
+|
+
+|`acc_mode`
+|режим доступа от man:open[2]
+|
+|===
+
+Определить, могут ли учетные данные субъекта выполнить операцию открытия переданного vnode с указанным режимом доступа. Возвращает 0 в случае успеха или значение errno при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-vnode-readdir]]
+==== `mpo_check_vnode_readdir`
+
+[source, c]
+----
+int mpo_check_vnode_readdir(struct ucred *cred, struct vnode *dvp,
+ struct label *dlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`dvp`
+|Объект; vnode каталога
+|
+
+|`dlabel`
+|Метка политики для `dvp`
+|
+|===
+
+Определить, могут ли учетные данные субъекта выполнить операцию `readdir` для переданного vnode каталога. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-vnode-readlink]]
+==== `mpo_check_vnode_readlink`
+
+[source, c]
+----
+int mpo_check_vnode_readlink(struct ucred *cred, struct vnode *vp,
+ struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode
+|
+
+|`label`
+|Метка политики для `vp`
+|
+|===
+
+Определить, могут ли учетные данные субъекта выполнить операцию `readlink` для переданного символьного vnode. Возвращает 0 в случае успеха или значение `errno` в случае ошибки. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий. Этот вызов может быть выполнен в различных ситуациях, включая явный вызов `readlink` пользовательским процессом или неявный `readlink` во время поиска имени процессом.
+
+[[mac-mpo-cred-check-vnode-revoke]]
+==== `mpo_check_vnode_revoke`
+
+[source, c]
+----
+int mpo_check_vnode_revoke(struct ucred *cred, struct vnode *vp,
+ struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode
+|
+
+|`label`
+|Метка политики для `vp`
+|
+|===
+
+Определить, могут ли учётные данные субъекта отозвать доступ к переданному vnode. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-vnode-setacl]]
+==== `mpo_check_vnode_setacl`
+
+[source, c]
+----
+int mpo_check_vnode_setacl(struct ucred *cred, struct vnode *vp,
+ struct label *label, acl_type_t type, struct acl *acl);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode
+|
+
+|`label`
+|Метка политики для `vp`
+|
+
+|`type`
+|Тип ACL
+|
+
+|`acl`
+|ACL
+|
+|===
+
+Определить, могут ли учётные данные субъекта установить переданный ACL указанного типа для переданного vnode. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-vnode-setextattr]]
+==== `mpo_check_vnode_setextattr`
+
+[source, c]
+----
+int mpo_check_vnode_setextattr(struct ucred *cred, struct vnode *vp,
+ struct label *label, int attrnamespace, const char *name, struct uio *uio);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode
+|
+
+|`label`
+|Метка политики для `vp`
+|
+
+|`attrnamespace`
+|Пространство имен расширенных атрибутов
+|
+
+|`name`
+|Имя расширенного атрибута
+|
+
+|`uio`
+|Указатель структуры ввода-вывода; см. man:uio[9]
+|
+|===
+
+Определить, могут ли учетные данные субъекта установить расширенный атрибут с переданным именем и пространством имен на переданном vnode. Политики, реализующие метки безопасности, основанные на расширенных атрибутах, могут предусматривать дополнительные защиты для этих атрибутов. Кроме того, политикам следует избегать принятия решений на основе данных, на которые ссылается `uio`, так как существует потенциальное состояние гонки между этой проверкой и фактической операцией. `uio` также может быть `NULL`, если выполняется операция удаления. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-vnode-setflags]]
+==== `mpo_check_vnode_setflags`
+
+[source, c]
+----
+int mpo_check_vnode_setflags(struct ucred *cred, struct vnode *vp,
+ struct label *label, u_long flags);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode
+|
+
+|`label`
+|Метка политики для `vp`
+|
+
+|`flags`
+|Флаги файла; см. man:chflags[2]
+|
+|===
+
+Определить, могут ли учётные данные субъекта установить переданные флаги на переданном vnode. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-vnode-setmode]]
+==== `mpo_check_vnode_setmode`
+
+[source, c]
+----
+int mpo_check_vnode_setmode(struct ucred *cred, struct vnode *vp,
+ struct label *label, mode_t mode);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode
+|
+
+|`label`
+|Метка политики для `vp`
+|
+
+|`mode`
+|Режим файла; см. man:chmod[2]
+|
+|===
+
+Определить, могут ли учётные данные субъекта установить переданный режим для переданного vnode. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при недостатке привилегий.
+
+[[mac-mpo-cred-check-vnode-setowner]]
+==== `mpo_check_vnode_setowner`
+
+[source, c]
+----
+int mpo_check_vnode_setowner(struct ucred *cred, struct vnode *vp,
+ struct label *label, uid_t uid, gid_t gid);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode
+|
+
+|`label`
+|Метка политики для `vp`
+|
+
+|`uid`
+|User ID
+|
+
+|`gid`
+|Идентификатор группы
+|
+|===
+
+Определить, могут ли учетные данные субъекта установить переданный uid и переданный gid в качестве uid файла и gid файла для переданного vnode. Идентификаторы могут быть установлены в (`-1`) для запроса отсутствия обновления. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-vnode-setutimes]]
+==== `mpo_check_vnode_setutimes`
+
+[source, c]
+----
+int mpo_check_vnode_setutimes(struct ucred *cred, struct vnode *vp,
+ struct label *label, struct timespec atime, struct timespec mtime);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vp
+|
+
+|`label`
+|Метка политики для `vp`
+|
+
+|`atime`
+|Время доступа; см. man:utimes[2]
+|
+
+|`mtime`
+|Время изменения; см. man:utimes[2]
+|
+|===
+
+Определить, могут ли учётные данные субъекта установить переданные времена доступа на переданном vnode. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-proc-sched]]
+==== `mpo_check_proc_sched`
+
+[source, c]
+----
+int mpo_check_proc_sched(struct ucred *ucred, struct proc *proc);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`proc`
+|Объект; процесс
+|
+|===
+
+Определить, могут ли учетные данные субъекта изменить параметры планирования переданного процесса. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки, EPERM при отсутствии привилегий или ESRCH для ограничения видимости.
+
+См. man:setpriority[2] для получения дополнительной информации.
+
+[[mac-mpo-cred-check-proc-signal]]
+==== `mpo_check_proc_signal`
+
+[source, c]
+----
+int mpo_check_proc_signal(struct ucred *cred, struct proc *proc, int signal);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`proc`
+|Объект; процесс
+|
+
+|`signal`
+|Сигнал; см. man:kill[2]
+|
+|===
+
+Определить, могут ли учётные данные субъекта доставить указанный сигнал указанному процессу. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые коды ошибок: EACCES при несоответствии метки, EPERM при недостатке прав или ESRCH для ограничения видимости.
+
+[[mac-mpo-cred-check-vnode-stat]]
+==== `mpo_check_vnode_stat`
+
+[source, c]
+----
+int mpo_check_vnode_stat(struct ucred *cred, struct vnode *vp,
+ struct label *label);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Объект; vnode
+|
+
+|`label`
+|Метка политики для `vp`
+|
+|===
+
+Определить, могут ли учетные данные субъекта выполнять `stat` для переданного vnode. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+См. man:stat[2] для получения дополнительной информации.
+
+[[mac-mpo-cred-check-ifnet-transmit]]
+==== `mpo_check_ifnet_transmit`
+
+[source, c]
+----
+int mpo_check_ifnet_transmit(struct ucred *cred, struct ifnet *ifnet,
+ struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`ifnet`
+|Сетевой интерфейс
+|
+
+|`ifnetlabel`
+|Метка политики для `ifnet`
+|
+
+|`mbuf`
+|Объект; mbuf для отправки
+|
+
+|`mbuflabel`
+|Метка политики для `mbuf`
+|
+|===
+
+Определить, может ли сетевой интерфейс передать mbuf, переданный в качестве параметра. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-cred-check-socket-deliver]]
+==== `mpo_check_socket_deliver`
+
+[source, c]
+----
+int mpo_check_socket_deliver(struct ucred *cred, struct ifnet *ifnet,
+ struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`ifnet`
+|Сетевой интерфейс
+|
+
+|`ifnetlabel`
+|Метка политики для `ifnet`
+|
+
+|`mbuf`
+|Объект; mbuf для доставки
+|
+
+|`mbuflabel`
+|Метка политики для `mbuf`
+|
+|===
+
+Определить, может ли сокет принять датаграмму, хранящуюся в переданном заголовке mbuf. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий.
+
+[[mac-mpo-check-socket-visible]]
+==== `mpo_check_socket_visible`
+
+[source, c]
+----
+int mpo_check_socket_visible(struct ucred *cred, struct socket *so,
+ struct label *socketlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|Неизменяемый
+
+|`so`
+|Объект; сокет
+|
+
+|`socketlabel`
+|Метка политики для `so`
+|
+|===
+
+Определить, могут ли учетные данные субъекта cred "видеть" переданный сокет (`socket`), используя функции системного мониторинга, такие как те, что применяются в man:netstat[8] и man:sockstat[1]. Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии меток, EPERM при отсутствии привилегий или ESRCH для скрытия видимости.
+
+[[mac-mpo-check-system-acct]]
+==== `mpo_check_system_acct`
+
+[source, c]
+----
+int mpo_check_system_acct(struct ucred *ucred, struct vnode *vp,
+ struct label *vlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`ucred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Файл учёта; man:acct[5]
+|
+
+|`vlabel`
+|Метка, связанная с `vp`
+|
+|===
+
+Определить, следует ли разрешить субъекту включение учёта, основываясь на его метке и метке файла журнала учёта.
+
+[[mac-mpo-check-system-nfsd]]
+==== `mpo_check_system_nfsd`
+
+[source, c]
+----
+int mpo_check_system_nfsd(struct ucred *cred);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+|===
+
+Определить, следует ли разрешить субъекту вызывать man:nfssvc[2].
+
+[[mac-mpo-check-system-reboot]]
+==== `mpo_check_system_reboot`
+
+[source, c]
+----
+int mpo_check_system_reboot(struct ucred *cred, int howto);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`howto`
+|Параметр `howto` из man:reboot[2]
+|
+|===
+
+Определить, следует ли разрешить субъекту перезагружать систему указанным способом.
+
+[[mac-mpo-check-system-settime]]
+==== `mpo_check_system_settime`
+
+[source, c]
+----
+int mpo_check_system_settime(struct ucred *cred);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+|===
+
+Определить, разрешено ли пользователю устанавливать системные часы.
+
+[[mac-mpo-check-system-swapon]]
+==== `mpo_check_system_swapon`
+
+[source, c]
+----
+int mpo_check_system_swapon(struct ucred *cred, struct vnode *vp,
+ struct label *vlabel);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`vp`
+|Устройство подкачки
+|
+
+|`vlabel`
+|Метка, связанная с `vp`
+|
+|===
+
+Определить, следует ли разрешить субъекту добавлять `vp` как устройство подкачки.
+
+[[mac-mpo-check-system-sysctl]]
+==== `mpo_check_system_sysctl`
+
+[source, c]
+----
+int mpo_check_system_sysctl(struct ucred *cred, int *name, u_int *namelen,
+ void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen);
+----
+
+[.informaltable]
+[cols="1,1,1", frame="none", options="header"]
+|===
+| Параметр
+| Описание
+| Блокировка
+
+|`cred`
+|Учетные данные субъекта
+|
+
+|`name`
+|См. man:sysctl[3]
+|
+
+|`namelen`
+|
+|
+
+|`old`
+|
+|
+
+|`oldlenp`
+|
+|
+
+|`inkernel`
+|Логический; `1`, если вызвано из ядра
+|
+
+|`new`
+|См. man:sysctl[3]
+|
+
+|`newlen`
+|
+|
+|===
+
+Определить, следует ли разрешить субъекту выполнять указанную транзакцию man:sysctl[3].
+
+[[mac-label-management]]
+=== Вызовы при управления метками
+
+События изменения метки происходят, когда пользовательский процесс запрашивает изменение метки на объекте. Происходит двухэтапное обновление: сначала выполняется проверка контроля доступа, чтобы определить, является ли обновление допустимым и разрешённым, а затем само обновление выполняется через отдельную точку входа. Точки входа для изменения метки обычно принимают объект, ссылку на метку объекта и новую метку, предоставленную процессом. Выделение памяти во время изменения метки не рекомендуется, так как вызовы изменения метки не могут завершиться неудачей (ошибка должна быть обнаружена ранее на этапе проверки изменения метки).
+
+[[mac-userland-arch]]
+== Пользовательская архитектура
+
+В фреймворк TrustedBSD MAC входит ряд элементов, не зависящих от политик, включая интерфейсы MAC-библиотеки для абстрактного управления метками, изменения в управлении системными учетными данными и библиотеках входа в систему для поддержки назначения MAC-меток пользователям, а также набор инструментов для мониторинга и изменения меток процессов, файлов и сетевых интерфейсов. Более подробная информация о пользовательской архитектуре будет добавлена в этот раздел в ближайшее время.
+
+[[mac-userland-labels]]
+=== API для управления метками, не зависящими от политики
+
+Фреймворк TrustedBSD MAC предоставляет ряд библиотечных и системных вызовов, позволяющих приложениям управлять метками MAC на объектах с использованием политико-независимого интерфейса. Это позволяет приложениям манипулировать метками для различных политик без необходимости поддержки конкретных политик. Эти интерфейсы используются универсальными инструментами, такими как man:ifconfig[8], man:ls[1] и man:ps[1], для просмотра меток на сетевых интерфейсах, файлах и процессах. API также поддерживают инструменты управления MAC, включая man:getfmac[8], man:getpmac[8], man:setfmac[8], man:setfsmac[8] и man:setpmac[8]. API MAC документированы в man:mac[3].
+
+Приложения обрабатывают метки MAC в двух формах: внутренней форме, используемой для возврата и установки меток для процессов и объектов (`mac_t`), и внешней форме, основанной на строках C, подходящих для хранения в конфигурационных файлах, отображения пользователю или ввода от пользователя. Каждая метка MAC содержит ряд элементов, каждый из которых состоит из пары имя-значение. Модули политик в ядре привязываются к определённым именам и интерпретируют значения специфичным для политики образом. Во внешней строковой форме метки представляются списком пар имя-значение, разделённых запятыми и символом `/`. Метки могут быть напрямую преобразованы в текст и обратно с использованием предоставленных API; при извлечении меток из ядра внутреннее хранилище меток должно быть сначала подготовлено для желаемого набора элементов метки. Обычно это делается одним из двух способов: с использованием man:mac_prepare[3] и произвольного списка желаемых элементов метки, или одной из вариаций вызова, который загружает набор элементов по умолчанию из конфигурационного файла man:mac.conf[5]. Значения по умолчанию для каждого объекта позволяют разработчикам приложений удобно отображать метки, связанные с объектами, без необходимости знать о присутствующих в системе политиках.
+
+[NOTE]
+====
+В настоящее время прямое манипулирование элементами меток, кроме как путем преобразования в текстовую строку, редактирования строки и обратного преобразования во внутреннюю метку, не поддерживается библиотекой MAC. Такие интерфейсы могут быть добавлены в будущем, если окажется, что они необходимы разработчикам приложений.
+====
+
+[[mac-userland-credentials]]
+=== Привязка меток к пользователям
+
+Стандартный интерфейс управления контекстом пользователя, man:setusercontext[3], был изменён для получения меток MAC, связанных с классом пользователя, из man:login.conf[5]. Эти метки устанавливаются вместе с остальным контекстом пользователя, когда указан `LOGIN_SETALL` или явно указан `LOGIN_SETMAC`.
+
+[NOTE]
+====
+Ожидается, что в будущей версии FreeBSD база данных меток MAC будет отделена от абстракции классов пользователей [.filename]#login.conf# и будет поддерживаться в отдельной базе данных. Однако API man:setusercontext[3] должно остаться неизменным после такого изменения.
+====
+
+[[mac-conclusion]]
+== Заключение
+
+Фреймворк TrustedBSD MAC позволяет модулям ядра расширять политику безопасности системы высокоинтегрированным способом. Они могут делать это на основе существующих свойств объектов или данных меток, которые поддерживаются с помощью фреймворка MAC. Фреймворк достаточно гибкий для реализации различных типов политик, включая политики безопасности информационных потоков, такие как MLS и Biba, а также политики, основанные на существующих учетных данных BSD или защите файлов. Авторам политик может быть полезно ознакомиться с этой документацией, а также с существующими модулями безопасности при реализации новой службы безопасности.
diff --git a/documentation/content/ru/books/arch-handbook/mac/_index.po b/documentation/content/ru/books/arch-handbook/mac/_index.po
new file mode 100644
index 0000000000..f93f0bf538
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/mac/_index.po
@@ -0,0 +1,8639 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-08-26 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookmac_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:19
+#, no-wrap
+msgid "The TrustedBSD MAC Framework"
+msgstr "Фреймворк TrustedBSD MAC"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1
+#, no-wrap
+msgid "Chapter 6. The TrustedBSD MAC Framework"
+msgstr "Глава 6. Фреймворк TrustedBSD MAC"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:57
+#, no-wrap
+msgid "MAC Documentation Copyright"
+msgstr "Авторские права документации MAC"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:60
+msgid ""
+"This documentation was developed for the FreeBSD Project by Chris Costello "
+"at Safeport Network Services and Network Associates Laboratories, the "
+"Security Research Division of Network Associates, Inc. under DARPA/SPAWAR "
+"contract N66001-01-C-8035 (\"CBOSS\"), as part of the DARPA CHATS research "
+"program."
+msgstr ""
+"Этот документ был разработан для проекта FreeBSD Крисом Костелло из Safeport "
+"Network Services и Network Associates Laboratories, подразделения "
+"исследований безопасности Network Associates, Inc., по контракту DARPA/"
+"SPAWAR N66001-01-C-8035 (\"CBOSS\") в рамках исследовательской программы "
+"DARPA CHATS."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:62
+msgid ""
+"Redistribution and use in source (SGML DocBook) and 'compiled' forms (SGML, "
+"HTML, PDF, PostScript, RTF and so forth) with or without modification, are "
+"permitted provided that the following conditions are met:"
+msgstr ""
+"Redistribution and use in source (SGML DocBook) and 'compiled' forms (SGML, "
+"HTML, PDF, PostScript, RTF and so forth) with or without modification, are "
+"permitted provided that the following conditions are met:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:64
+msgid ""
+"Redistributions of source code (SGML DocBook) must retain the above "
+"copyright notice, this list of conditions and the following disclaimer as "
+"the first lines of this file unmodified."
+msgstr ""
+"Redistributions of source code (SGML DocBook) must retain the above "
+"copyright notice, this list of conditions and the following disclaimer as "
+"the first lines of this file unmodified."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:65
+msgid ""
+"Redistributions in compiled form (transformed to other DTDs, converted to "
+"PDF, PostScript, RTF and other formats) must reproduce the above copyright "
+"notice, this list of conditions and the following disclaimer in the "
+"documentation and/or other materials provided with the distribution."
+msgstr ""
+"Распространение в скомпилированной форме (преобразованное в другие DTD, "
+"конвертированное в PDF, PostScript, RTF и другие форматы) должно включать "
+"указанное выше уведомление об авторских правах, данный список условий и "
+"следующий отказ от ответственности в документации и/или других материалах, "
+"предоставляемых вместе с распространением."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:70
+msgid ""
+"THIS DOCUMENTATION IS PROVIDED BY THE NETWORKS ASSOCIATES TECHNOLOGY, INC "
+"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED "
+"TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR "
+"PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NETWORKS ASSOCIATES TECHNOLOGY, "
+"INC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR "
+"CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF "
+"SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS "
+"INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN "
+"CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) "
+"ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENTATION, EVEN IF ADVISED OF "
+"THE POSSIBILITY OF SUCH DAMAGE."
+msgstr ""
+"ЭТА ДОКУМЕНТАЦИЯ ПРЕДОСТАВЛЯЕТСЯ NETWORKS ASSOCIATES TECHNOLOGY, INC «КАК "
+"ЕСТЬ», И ЛЮБЫЕ ЯВНЫЕ ИЛИ ПОДРАЗУМЕВАЕМЫЕ ГАРАНТИИ, ВКЛЮЧАЯ, НО НЕ "
+"ОГРАНИЧИВАЯСЬ ИМИ, ПОДРАЗУМЕВАЕМЫЕ ГАРАНТИИ ТОВАРНОЙ ПРИГОДНОСТИ И "
+"ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ ОТРИЦАЮТСЯ. НИ ПРИ КАКИХ ОБСТОЯТЕЛЬСТВАХ "
+"NETWORKS ASSOCIATES TECHNOLOGY, INC НЕ НЕСЕТ ОТВЕТСТВЕННОСТИ ЗА ЛЮБЫЕ "
+"ПРЯМЫЕ, КОСВЕННЫЕ, СЛУЧАЙНЫЕ, СПЕЦИАЛЬНЫЕ, ШТРАФНЫЕ ИЛИ КОСВЕННЫЕ УБЫТКИ "
+"(ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ, ЗАТРАТЫ НА ЗАМЕНУ ТОВАРОВ ИЛИ УСЛУГ; "
+"ПОТЕРЮ ИСПОЛЬЗОВАНИЯ, ДАННЫХ ИЛИ ПРИБЫЛИ; ЛИБО ПРЕРЫВАНИЕ БИЗНЕСА), "
+"ВЫЗВАННЫЕ ЛЮБЫМ ОБРАЗОМ И НА ОСНОВАНИИ ЛЮБОЙ ТЕОРИИ ОТВЕТСТВЕННОСТИ, БУДЬ ТО "
+"В РАМКАХ ДОГОВОРА, СТРОГОЙ ОТВЕТСТВЕННОСТИ ИЛИ ДЕЛИКТА (ВКЛЮЧАЯ НЕБРЕЖНОСТЬ "
+"ИЛИ ИНОЕ), ВОЗНИКШИЕ ВСЛЕДСТВИЕ ИСПОЛЬЗОВАНИЯ ЭТОЙ ДОКУМЕНТАЦИИ, ДАЖЕ ЕСЛИ "
+"БЫЛО ПРЕДУПРЕЖДЕНИЕ О ВОЗМОЖНОСТИ ТАКИХ УБЫТКОВ."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:73
+#, no-wrap
+msgid "Synopsis"
+msgstr "Обзор"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:78
+msgid ""
+"FreeBSD includes experimental support for several mandatory access control "
+"policies, as well as a framework for kernel security extensibility, the "
+"TrustedBSD MAC Framework. The MAC Framework is a pluggable access control "
+"framework, permitting new security policies to be easily linked into the "
+"kernel, loaded at boot, or loaded dynamically at run-time. The framework "
+"provides a variety of features to make it easier to implement new security "
+"policies, including the ability to easily tag security labels (such as "
+"confidentiality information) onto system objects."
+msgstr ""
+"FreeBSD включает экспериментальную поддержку нескольких политик "
+"обязательного контроля доступа, а также инфраструктуру для расширяемости "
+"безопасности ядра — TrustedBSD MAC Framework. MAC Framework представляет "
+"собой модульную инфраструктуру контроля доступа, позволяющую легко "
+"встраивать новые политики безопасности в ядро, загружать их при старте "
+"системы или динамически во время работы. Инфраструктура предоставляет "
+"множество возможностей для упрощения реализации новых политик безопасности, "
+"включая возможность легко присваивать метки безопасности (например, "
+"информацию о конфиденциальности) объектам системы."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:80
+msgid ""
+"This chapter introduces the MAC policy framework and provides documentation "
+"for a sample MAC policy module."
+msgstr ""
+"Эта глава представляет фреймворк политик MAC и содержит документацию для "
+"образца модуля политики MAC."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:82
+#, no-wrap
+msgid "Introduction"
+msgstr "Введение"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:88
+msgid ""
+"The TrustedBSD MAC framework provides a mechanism to allow the compile-time "
+"or run-time extension of the kernel access control model. New system "
+"policies may be implemented as kernel modules and linked to the kernel; if "
+"multiple policy modules are present, their results will be composed. The "
+"MAC Framework provides a variety of access control infrastructure services "
+"to assist policy writers, including support for transient and persistent "
+"policy-agnostic object security labels. This support is currently "
+"considered experimental."
+msgstr ""
+"Фреймворк TrustedBSD MAC предоставляет механизм для расширения модели "
+"контроля доступа ядра во время компиляции или выполнения. Новые политики "
+"системы могут быть реализованы в виде модулей ядра и связаны с ним; если "
+"присутствуют несколько модулей политик, их результаты будут объединены. "
+"Фреймворк MAC предоставляет различные инфраструктурные сервисы контроля "
+"доступа для помощи разработчикам политик, включая поддержку временных и "
+"постоянных меток безопасности объектов, не зависящих от политик. В настоящее "
+"время эта поддержка считается экспериментальной."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:90
+msgid ""
+"This chapter provides information appropriate for developers of policy "
+"modules, as well as potential consumers of MAC-enabled environments, to "
+"learn about how the MAC Framework supports access control extension of the "
+"kernel."
+msgstr ""
+"Эта глава предоставляет информацию, предназначенную для разработчиков "
+"модулей политик, а также потенциальных пользователей сред с поддержкой MAC, "
+"чтобы узнать о том, как MAC Framework поддерживает расширение контроля "
+"доступа в ядре."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:92
+#, no-wrap
+msgid "Policy Background"
+msgstr "Общие сведения о политиках"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:99
+msgid ""
+"Mandatory Access Control (MAC), refers to a set of access control policies "
+"that are mandatorily enforced on users by the operating system. MAC "
+"policies may be contrasted with Discretionary Access Control (DAC) "
+"protections, by which non-administrative users may (at their discretion) "
+"protect objects. In traditional UNIX systems, DAC protections include file "
+"permissions and access control lists; MAC protections include process "
+"controls preventing inter-user debugging and firewalls. A variety of MAC "
+"policies have been formulated by operating system designers and security "
+"researches, including the Multi-Level Security (MLS) confidentiality policy, "
+"the Biba integrity policy, Role-Based Access Control (RBAC), Domain and Type "
+"Enforcement (DTE), and Type Enforcement (TE). Each model bases decisions on "
+"a variety of factors, including user identity, role, and security clearance, "
+"as well as security labels on objects representing concepts such as data "
+"sensitivity and integrity."
+msgstr ""
+"Мандатное управление доступом (MAC — Mandatory Access Control) относится к "
+"набору политик контроля доступа, которые в обязательном порядке применяются "
+"операционной системой к пользователям. Политики MAC можно противопоставить "
+"защите на основе дискреционного управления доступом (DAC — Discretionary "
+"Access Control), при которой непривилегированные пользователи могут (по "
+"своему усмотрению) защищать объекты. В традиционных UNIX-системах защита DAC "
+"включает права доступа к файлам и списки контроля доступа; защита MAC "
+"включает управление процессами, предотвращающее отладку между "
+"пользователями, и межсетевые экраны. Различные политики MAC были разработаны "
+"создателями операционных систем и исследователями безопасности, включая "
+"политику конфиденциальности многоуровневой безопасности (MLS — Multi-Level "
+"Security), политику целостности Biba, управление доступом на основе ролей "
+"(RBAC — Role-Based Access Control), принудительное применение доменов и "
+"типов (DTE — Domain and Type Enforcement) и принудительное применение типов "
+"(TE — Type Enforcement). Каждая модель основывает решения на различных "
+"факторах, включая идентификатор пользователя, роль и уровень доступа, а "
+"также метки безопасности на объектах, представляющих такие концепции, как "
+"конфиденциальность и целостность данных."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:102
+msgid ""
+"The TrustedBSD MAC Framework is capable of supporting policy modules that "
+"implement all of these policies, as well as a broad class of system "
+"hardening policies, which may use existing security attributes, such as user "
+"and group IDs, as well as extended attributes on files, and other system "
+"properties. In addition, despite the name, the MAC Framework can also be "
+"used to implement purely discretionary policies, as policy modules are given "
+"substantial flexibility in how they authorize protections."
+msgstr ""
+"Фреймворк TrustedBSD MAC способен поддерживать модули политик, реализующие "
+"все эти политики, а также широкий класс политик усиления защиты системы, "
+"которые могут использовать существующие атрибуты безопасности, такие как "
+"идентификаторы пользователей и групп, а также расширенные атрибуты файлов и "
+"другие свойства системы. Кроме того, несмотря на название, фреймворк MAC "
+"также может использоваться для реализации чисто дискреционных политик, "
+"поскольку модулям политик предоставляется значительная гибкость в том, как "
+"они авторизуют защиту."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:104
+#, no-wrap
+msgid "MAC Framework Kernel Architecture"
+msgstr "Архитектура MAC Framework в ядре"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:108
+msgid ""
+"The TrustedBSD MAC Framework permits kernel modules to extend the operating "
+"system security policy, as well as providing infrastructure functionality "
+"required by many access control modules. If multiple policies are "
+"simultaneously loaded, the MAC Framework will usefully (for some definition "
+"of useful) compose the results of the policies."
+msgstr ""
+"Фреймворк TrustedBSD MAC позволяет модулям ядра расширять политику "
+"безопасности операционной системы, а также предоставляет функциональность "
+"инфраструктуры, необходимую многим модулям контроля доступа. Если "
+"одновременно загружено несколько политик, фреймворк MAC полезным образом (в "
+"некотором смысле полезным) объединит результаты этих политик."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:110
+#, no-wrap
+msgid "Kernel Elements"
+msgstr "Элементы ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:113
+msgid "The MAC Framework contains a number of kernel elements:"
+msgstr "В рамках MAC Framework реализован ряд элементов ядра:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:115
+msgid "Framework management interfaces"
+msgstr "Интерфейсы управления фреймворком"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:116
+msgid "Concurrency and synchronization primitives."
+msgstr "Параллелизм и примитивы синхронизации."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:117
+msgid "Policy registration"
+msgstr "Регистрация политики"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:118
+msgid "Extensible security label for kernel objects"
+msgstr "Расширяемая метка безопасности для объектов ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:119
+msgid "Policy entry point composition operators"
+msgstr "Операторы композиции точки входа политики"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:120
+msgid "Label management primitives"
+msgstr "Примитивы управления метками"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:121
+msgid "Entry point API invoked by kernel services"
+msgstr "Точка входа API, вызываемая службами ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:122
+msgid "Entry point API to policy modules"
+msgstr "Точка входа API для модулей политик"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:123
+msgid ""
+"Entry points implementations (policy life cycle, object life cycle/label "
+"management, access control checks)."
+msgstr ""
+"Реализации точек входа (жизненный цикл политики, жизненный цикл объекта/"
+"управление метками, проверки контроля доступа)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:124
+msgid "Policy-agnostic label-management system calls"
+msgstr "Системные вызовы, независимые от политик, для управления метками"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:125
+msgid "`mac_syscall()` multiplex system call"
+msgstr "`mac_syscall()` мультиплексный системный вызов"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:126
+msgid "Various security policies implemented as MAC policy modules"
+msgstr ""
+"Различные политики безопасности, реализованные в виде модулей политики MAC"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:128
+#, no-wrap
+msgid "Framework Management Interfaces"
+msgstr "Интерфейсы управления фреймворком"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:131
+msgid ""
+"The TrustedBSD MAC Framework may be directly managed using sysctl's, loader "
+"tunables, and system calls."
+msgstr ""
+"Фреймворком TrustedBSD MAC можно напрямую управлять с помощью sysctl, "
+"параметров загрузчика и системных вызовов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:136
+msgid ""
+"In most cases, sysctl's and loader tunables of the same name modify the same "
+"parameters, and control behavior such as enforcement of protections relating "
+"to various kernel subsystems. In addition, if MAC debugging support is "
+"compiled into the kernel, several counters will be maintained tracking label "
+"allocation. It is generally advisable that per-subsystem enforcement "
+"controls not be used to control policy behavior in production environments, "
+"as they broadly impact the operation of all active policies. Instead, per-"
+"policy controls should be preferred, as they provide greater granularity and "
+"greater operational consistency for policy modules."
+msgstr ""
+"В большинстве случаев одноимённые параметры sysctl и настройки загрузчика "
+"изменяют одни и те же параметры и управляют поведением, таким как применение "
+"защитных механизмов, связанных с различными подсистемами ядра. Кроме того, "
+"если в ядро включена поддержка отладки MAC, будет вестись несколько "
+"счётчиков для отслеживания выделения меток. Обычно рекомендуется не "
+"использовать общие настройки подсистем для управления поведением политик в "
+"рабочих средах, так как они широко влияют на работу всех активных политик. "
+"Вместо этого следует предпочитать настройки отдельных политик, поскольку они "
+"обеспечивают более высокую детализацию и большую операционную "
+"согласованность для модулей политик."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:138
+msgid ""
+"Loading and unloading of policy modules is performed using the system module "
+"management system calls and other system interfaces, including boot loader "
+"variables; policy modules will have the opportunity to influence load and "
+"unload events, including preventing undesired unloading of the policy."
+msgstr ""
+"Загрузка и выгрузка модулей политики выполняется с использованием системных "
+"вызовов управления модулями и других системных интерфейсов, включая "
+"переменные загрузчика; модули политики получат возможность влиять на события "
+"загрузки и выгрузки, включая предотвращение нежелательной выгрузки политики."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:140
+#, no-wrap
+msgid "Policy List Concurrency and Synchronization"
+msgstr "Список политик параллелизма и синхронизации"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:147
+msgid ""
+"As the set of active policies may change at run-time, and the invocation of "
+"entry points is non-atomic, synchronization is required to prevent loading "
+"or unloading of policies while an entry point invocation is in progress, "
+"freezing the set of active policies for the duration. This is accomplished "
+"by means of a framework busy count: whenever an entry point is entered, the "
+"busy count is incremented; whenever it is exited, the busy count is "
+"decremented. While the busy count is elevated, policy list changes are not "
+"permitted, and threads attempting to modify the policy list will sleep until "
+"the list is not busy. The busy count is protected by a mutex, and a "
+"condition variable is used to wake up sleepers waiting on policy list "
+"modifications. One side effect of this synchronization model is that "
+"recursion into the MAC Framework from within a policy module is permitted, "
+"although not generally used."
+msgstr ""
+"Поскольку набор активных политик может изменяться во время выполнения, а "
+"вызов точек входа не является атомарным, требуется синхронизация для "
+"предотвращения загрузки или выгрузки политик во время выполнения вызова "
+"точки входа, фиксируя набор активных политик на время выполнения. Это "
+"достигается с помощью счетчика занятости фреймворка: при входе в точку входа "
+"счетчик увеличивается; при выходе из нее — уменьшается. Пока счетчик "
+"занятости повышен, изменения списка политик запрещены, и потоки, пытающиеся "
+"изменить список политик, будут ждать, пока список не освободится. Счетчик "
+"занятости защищается мьютексом, а условная переменная используется для "
+"пробуждения потоков, ожидающих изменений списка политик. Побочным эффектом "
+"этой модели синхронизации является то, что рекурсивный вход в MAC Framework "
+"из модуля политики разрешен, хотя обычно не используется."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:150
+msgid ""
+"Various optimizations are used to reduce the overhead of the busy count, "
+"including avoiding the full cost of incrementing and decrementing if the "
+"list is empty or contains only static entries (policies that are loaded "
+"before the system starts, and cannot be unloaded). A compile-time option is "
+"also provided which prevents any change in the set of loaded policies at run-"
+"time, which eliminates the mutex locking costs associated with supporting "
+"dynamically loaded and unloaded policies as synchronization is no longer "
+"required."
+msgstr ""
+"Для снижения накладных расходов счётчика занятости используются различные "
+"оптимизации, включая избегание полной стоимости увеличения и уменьшения, "
+"если список пуст или содержит только статические записи (политики, "
+"загруженные до старта системы, которые нельзя выгрузить). Также "
+"предоставляется опция на этапе компиляции, которая предотвращает любые "
+"изменения в наборе загруженных политик во время выполнения, что устраняет "
+"затраты на блокировку мьютексов, связанные с поддержкой динамически "
+"загружаемых и выгружаемых политик, поскольку синхронизация больше не "
+"требуется."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:152
+msgid ""
+"As the MAC Framework is not permitted to block in some entry points, a "
+"normal sleep lock cannot be used; as a result, it is possible for the load "
+"or unload attempt to block for a substantial period of time waiting for the "
+"framework to become idle."
+msgstr ""
+"Поскольку MAC Framework не может блокировать некоторые точки входа, обычная "
+"блокировка сна не может быть использована; в результате попытка загрузки или "
+"выгрузки может блокироваться на значительное время, ожидая, пока фреймворк "
+"станет свободным."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:154
+#, no-wrap
+msgid "Label Synchronization"
+msgstr "Синхронизация меток"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:161
+msgid ""
+"As kernel objects of interest may generally be accessed from more than one "
+"thread at a time, and simultaneous entry of more than one thread into the "
+"MAC Framework is permitted, security attribute storage maintained by the MAC "
+"Framework is carefully synchronized. In general, existing kernel "
+"synchronization on kernel object data is used to protect MAC Framework "
+"security labels on the object: for example, MAC labels on sockets are "
+"protected using the existing socket mutex. Likewise, semantics for "
+"concurrent access are generally identical to those of the container objects: "
+"for credentials, copy-on-write semantics are maintained for label contents "
+"as with the remainder of the credential structure. The MAC Framework "
+"asserts necessary locks on objects when invoked with an object reference. "
+"Policy authors must be aware of these synchronization semantics, as they "
+"will sometimes limit the types of accesses permitted on labels: for example, "
+"when a read-only reference to a credential is passed to a policy via an "
+"entry point, only read operations are permitted on the label state attached "
+"to the credential."
+msgstr ""
+"Поскольку к объектам ядра обычно может обращаться более одного потока "
+"одновременно, и допускается одновременный вход нескольких потоков в MAC "
+"Framework, хранение атрибутов безопасности, поддерживаемое MAC Framework, "
+"тщательно синхронизировано. Как правило, существующая синхронизация ядра для "
+"данных объектов ядра используется для защиты меток безопасности MAC "
+"Framework на объекте: например, метки MAC на сокетах защищаются с помощью "
+"существующего мьютекса сокета. Аналогично, семантика параллельного доступа "
+"обычно идентична семантике контейнерных объектов: для учетных данных "
+"поддерживается семантика копирования при записи для содержимого меток, как и "
+"для остальной структуры учетных данных. MAC Framework устанавливает "
+"необходимые блокировки на объекты при вызове с ссылкой на объект. Авторам "
+"политик необходимо учитывать эти семантики синхронизации, так как они иногда "
+"ограничивают типы доступа к меткам: например, когда ссылка только для чтения "
+"на учетные данные передается политике через точку входа, разрешены только "
+"операции чтения для состояния метки, прикрепленного к учетным данным."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:163
+#, no-wrap
+msgid "Policy Synchronization and Concurrency"
+msgstr "Синхронизация политики и параллелизм"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:169
+msgid ""
+"Policy modules must be written to assume that many kernel threads may "
+"simultaneously enter one more policy entry points due to the parallel and "
+"preemptive nature of the FreeBSD kernel. If the policy module makes use of "
+"mutable state, this may require the use of synchronization primitives within "
+"the policy to prevent inconsistent views on that state resulting in "
+"incorrect operation of the policy. Policies will generally be able to make "
+"use of existing FreeBSD synchronization primitives for this purpose, "
+"including mutexes, sleep locks, condition variables, and counting "
+"semaphores. However, policies should be written to employ these primitives "
+"carefully, respecting existing kernel lock orders, and recognizing that some "
+"entry points are not permitted to sleep, limiting the use of primitives in "
+"those entry points to mutexes and wakeup operations."
+msgstr ""
+"Модули политик должны быть написаны с учетом того, что множество потоков "
+"ядра могут одновременно войти в одну или несколько точек входа политики из-"
+"за параллельной и вытесняющей природы ядра FreeBSD. Если модуль политики "
+"использует изменяемое состояние, это может потребовать применения примитивов "
+"синхронизации внутри политики, чтобы предотвратить несогласованные "
+"представления этого состояния, ведущие к некорректной работе политики. "
+"Политики, как правило, могут использовать существующие примитивы "
+"синхронизации FreeBSD для этой цели, включая мьютексы, блокировки с "
+"ожиданием, условные переменные и счётные семафоры. Однако политики должны "
+"быть написаны так, чтобы применять эти примитивы осторожно, соблюдая "
+"существующие порядки блокировок в ядре и учитывая, что некоторые точки входа "
+"не допускают ожидания, ограничивая использование примитивов в этих точках "
+"входа мьютексами и операциями пробуждения."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:172
+msgid ""
+"When policy modules call out to other kernel subsystems, they will generally "
+"need to release any in-policy locks in order to avoid violating the kernel "
+"lock order or risking lock recursion. This will maintain policy locks as "
+"leaf locks in the global lock order, helping to avoid deadlock."
+msgstr ""
+"Когда модули политики обращаются к другим подсистемам ядра, они обычно "
+"должны освобождать любые блокировки внутри политики, чтобы избежать "
+"нарушения порядка блокировок ядра или риска рекурсивных блокировок. Это "
+"позволит сохранить блокировки политики как конечные блокировки в глобальном "
+"порядке блокировок, помогая избежать взаимоблокировки."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:174
+#, no-wrap
+msgid "Policy Registration"
+msgstr "Регистрация политики"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:181
+msgid ""
+"The MAC Framework maintains two lists of active policies: a static list, and "
+"a dynamic list. The lists differ only with regards to their locking "
+"semantics: an elevated reference count is not required to make use of the "
+"static list. When kernel modules containing MAC Framework policies are "
+"loaded, the policy module will use `SYSINIT` to invoke a registration "
+"function; when a policy module is unloaded, `SYSINIT` will likewise invoke a "
+"de-registration function. Registration may fail if a policy module is "
+"loaded more than once, if insufficient resources are available for the "
+"registration (for example, the policy might require labeling and "
+"insufficient labeling state might be available), or other policy "
+"prerequisites might not be met (some policies may only be loaded prior to "
+"boot). Likewise, de-registration may fail if a policy is flagged as not "
+"unloadable."
+msgstr ""
+"Фреймворк MAC поддерживает два списка активных политик: статический список и "
+"динамический список. Списки отличаются только в отношении их семантики "
+"блокировки: для использования статического списка не требуется повышенный "
+"счетчик ссылок. Когда загружаются модули ядра, содержащие политики "
+"фреймворка MAC, модуль политики использует `SYSINIT` для вызова функции "
+"регистрации; когда модуль политики выгружается, `SYSINIT` аналогично "
+"вызывает функцию отмены регистрации. Регистрация может завершиться неудачей, "
+"если модуль политики загружается более одного раза, если для регистрации "
+"недостаточно ресурсов (например, политика может требовать маркировки, а "
+"доступного состояния маркировки может быть недостаточно), или другие "
+"предварительные условия политики могут не выполняться (некоторые политики "
+"могут быть загружены только до загрузки системы). Аналогично, отмена "
+"регистрации может завершиться неудачей, если политика помечена как "
+"невыгружаемая."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:183
+#, no-wrap
+msgid "Entry Points"
+msgstr "Точки входа"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:190
+msgid ""
+"Kernel services interact with the MAC Framework in two ways: they invoke a "
+"series of APIs to notify the framework of relevant events, and they provide "
+"a policy-agnostic label structure pointer in security-relevant objects. The "
+"label pointer is maintained by the MAC Framework via label management entry "
+"points, and permits the Framework to offer a labeling service to policy "
+"modules through relatively non-invasive changes to the kernel subsystem "
+"maintaining the object. For example, label pointers have been added to "
+"processes, process credentials, sockets, pipes, vnodes, Mbufs, network "
+"interfaces, IP reassembly queues, and a variety of other security-relevant "
+"structures. Kernel services also invoke the MAC Framework when they perform "
+"important security decisions, permitting policy modules to augment those "
+"decisions based on their own criteria (possibly including data stored in "
+"security labels). Most of these security critical decisions will be "
+"explicit access control checks; however, some affect more general decision "
+"functions such as packet matching for sockets and label transition at "
+"program execution."
+msgstr ""
+"Ядро взаимодействует с MAC Framework двумя способами: вызывает набор API для "
+"уведомления фреймворка о соответствующих событиях и предоставляет указатель "
+"на структуру меток, не зависящую от политики, в объектах, связанных с "
+"безопасностью. Указатель метки управляется MAC Framework через точки входа "
+"управления метками, что позволяет фреймворку предоставлять службу маркировки "
+"модулям политик с относительно минимальными изменениями в подсистеме ядра, "
+"управляющей объектом. Например, указатели меток были добавлены к процессам, "
+"учетным данным процессов, сокетам, каналам, vnode, Mbuf, сетевым "
+"интерфейсам, очередям сборки IP-пакетов и множеству других структур, "
+"связанных с безопасностью. Ядро также вызывает MAC Framework при принятии "
+"важных решений по безопасности, позволяя модулям политик дополнять эти "
+"решения на основе собственных критериев (включая, возможно, данные, "
+"хранящиеся в метках безопасности). Большинство этих критически важных "
+"решений по безопасности будут явными проверками контроля доступа; однако "
+"некоторые влияют на более общие функции принятия решений, такие как "
+"сопоставление пакетов для сокетов и переход меток при выполнении программы."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:192
+#, no-wrap
+msgid "Policy Composition"
+msgstr "Композиция политик"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:200
+msgid ""
+"When more than one policy module is loaded into the kernel at a time, the "
+"results of the policy modules will be composed by the framework using a "
+"composition operator. This operator is currently hard-coded, and requires "
+"that all active policies must approve a request for it to return success. "
+"As policies may return a variety of error conditions (success, access "
+"denied, object does not exist, ...), a precedence operator selects the "
+"resulting error from the set of errors returned by policies. In general, "
+"errors indicating that an object does not exist will be preferred to errors "
+"indicating that access to an object is denied. While it is not guaranteed "
+"that the resulting composition will be useful or secure, we have found that "
+"it is for many useful selections of policies. For example, traditional "
+"trusted systems often ship with two or more policies using a similar "
+"composition."
+msgstr ""
+"Когда в ядро загружено более одного модуля политики одновременно, результаты "
+"работы модулей политики будут объединены фреймворком с использованием "
+"оператора композиции. Этот оператор в настоящее время жёстко закодирован и "
+"требует, чтобы все активные политики одобрили запрос для возврата успешного "
+"результата. Поскольку политики могут возвращать различные условия ошибки "
+"(успех, доступ запрещён, объект не существует, ...), оператор старшинства "
+"выбирает результирующую ошибку из набора ошибок, возвращаемых политиками. В "
+"общем случае, ошибки, указывающие на то, что объект не существует, будут "
+"предпочтительнее ошибок, указывающих на запрет доступа к объекту. Хотя не "
+"гарантируется, что результирующая композиция будет полезной или безопасной, "
+"мы обнаружили, что это так для многих полезных наборов политик. Например, "
+"традиционные доверенные системы часто поставляются с двумя или более "
+"политиками, использующими аналогичную композицию."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:202
+#, no-wrap
+msgid "Labeling Support"
+msgstr "Поддержка меток"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:208
+msgid ""
+"As many interesting access control extensions rely on security labels on "
+"objects, the MAC Framework provides a set of policy-agnostic label "
+"management system calls covering a variety of user-exposed objects. Common "
+"label types include partition identifiers, sensitivity labels, integrity "
+"labels, compartments, domains, roles, and types. By policy agnostic, we "
+"mean that policy modules are able to completely define the semantics of meta-"
+"data associated with an object. Policy modules participate in the "
+"internalization and externalization of string-based labels provides by user "
+"applications, and can expose multiple label elements to applications if "
+"desired."
+msgstr ""
+"Поскольку многие интересные расширения контроля доступа зависят от меток "
+"безопасности объектов, MAC Framework предоставляет набор системных вызовов "
+"для управления метками, не зависящих от политик, охватывающих различные "
+"объекты, доступные пользователю. Общие типы меток включают идентификаторы "
+"разделов, метки конфиденциальности, метки целостности, отделы (compartment), "
+"домены, роли и типы. Под \"не зависящими от политик\" подразумевается, что "
+"модули политик могут полностью определять семантику метаданных, связанных с "
+"объектом. Модули политик участвуют в интернализации и экстернализации "
+"строковых меток, предоставляемых пользовательскими приложениями, и могут при "
+"необходимости предоставлять приложениям несколько элементов меток."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:216
+msgid ""
+"In-memory labels are stored in slab-allocated `struct label`, which consists "
+"of a fixed-length array of unions, each holding a `void *` pointer and a "
+"`long`. Policies registering for label storage will be assigned a \"slot\" "
+"identifier, which may be used to dereference the label storage. The "
+"semantics of the storage are left entirely up to the policy module: modules "
+"are provided with a variety of entry points associated with the kernel "
+"object life cycle, including initialization, association/creation, and "
+"destruction. Using these interfaces, it is possible to implement reference "
+"counting and other storage models. Direct access to the object structure is "
+"generally not required by policy modules to retrieve a label, as the MAC "
+"Framework generally passes both a pointer to the object and a direct pointer "
+"to the object's label into entry points. The primary exception to this rule "
+"is the process credential, which must be manually dereferenced to access the "
+"credential label. This may change in future revisions of the MAC Framework."
+msgstr ""
+"Метки в памяти хранятся в `struct label`, выделяемой через slab-аллокатор. "
+"Эта структура состоит из массива фиксированной длины, содержащего "
+"объединения, каждое из которых хранит указатель `void *` и значение типа "
+"`long`. Политикам, регистрирующим хранилище меток, назначается идентификатор "
+"\"слота\", который может использоваться для разыменования хранилища меток. "
+"Семантика хранилища полностью определяется модулем политики: модулям "
+"предоставляется набор точек входа, связанных с жизненным циклом объектов "
+"ядра, включая инициализацию, связывание/создание и уничтожение. Используя "
+"эти интерфейсы, можно реализовать подсчёт ссылок и другие модели хранения. "
+"Прямой доступ к структуре объекта, как правило, не требуется модулям "
+"политики для получения метки, поскольку MAC Framework обычно передаёт в "
+"точки входа как указатель на объект, так и прямой указатель на метку "
+"объекта. Основным исключением из этого правила являются учётные данные "
+"процесса, для доступа к метке которых требуется ручное разыменование. Это "
+"может измениться в будущих версиях MAC Framework."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:223
+msgid ""
+"Initialization entry points frequently include a sleeping disposition flag "
+"indicating whether or not an initialization is permitted to sleep; if "
+"sleeping is not permitted, a failure may be returned to cancel allocation of "
+"the label (and hence object). This may occur, for example, in the network "
+"stack during interrupt handling, where sleeping is not permitted, or while "
+"the caller holds a mutex. Due to the performance cost of maintaining labels "
+"on in-flight network packets (Mbufs), policies must specifically declare a "
+"requirement that Mbuf labels be allocated. Dynamically loaded policies "
+"making use of labels must be able to handle the case where their init "
+"function has not been called on an object, as objects may already exist when "
+"the policy is loaded. The MAC Framework guarantees that uninitialized label "
+"slots will hold a 0 or NULL value, which policies may use to detect "
+"uninitialized values. However, as allocation of Mbuf labels is conditional, "
+"policies must also be able to handle a NULL label pointer for Mbufs if they "
+"have been loaded dynamically."
+msgstr ""
+"Входные точки инициализации часто включают флаг режима сна, указывающий, "
+"разрешено ли инициализации переходить в режим сна; если сон не разрешен, "
+"может быть возвращена ошибка для отмены выделения метки (и, следовательно, "
+"объекта). Это может произойти, например, в сетевом стеке во время обработки "
+"прерывания, где сон не разрешен, или пока вызывающий удерживает мьютекс. Из-"
+"за затрат производительности на поддержание меток на передаваемых сетевых "
+"пакетах (Mbuf), политики должны явно объявлять требование о выделении меток "
+"для Mbuf. Динамически загружаемые политики, использующие метки, должны быть "
+"способны обрабатывать случай, когда их функция инициализации не была вызвана "
+"для объекта, так как объекты могут уже существовать при загрузке политики. "
+"MAC Framework гарантирует, что неинициализированные слоты меток будут "
+"содержать значение 0 или NULL, что политики могут использовать для "
+"обнаружения неинициализированных значений. Однако, поскольку выделение меток "
+"для Mbuf условно, политики также должны быть способны обрабатывать указатель "
+"на метку NULL для Mbuf, если они были загружены динамически."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:229
+msgid ""
+"In the case of file system labels, special support is provided for the "
+"persistent storage of security labels in extended attributes. Where "
+"available, extended attribute transactions are used to permit consistent "
+"compound updates of security labels on vnodes--currently this support is "
+"present only in the UFS2 file system. Policy authors may choose to "
+"implement multilabel file system object labels using one (or more) extended "
+"attributes. For efficiency reasons, the vnode label (`v_label`) is a cache "
+"of any on-disk label; policies are able to load values into the cache when "
+"the vnode is instantiated, and update the cache as needed. As a result, the "
+"extended attribute need not be directly accessed with every access control "
+"check."
+msgstr ""
+"В случае меток файловых систем предусмотрена специальная поддержка для "
+"постоянного хранения меток безопасности в расширенных атрибутах. Там, где "
+"это возможно, используются транзакции расширенных атрибутов, чтобы "
+"обеспечить согласованные составные обновления меток безопасности на vnode — "
+"в настоящее время такая поддержка присутствует только в файловой системе "
+"UFS2. Авторы политик могут выбрать реализацию многометочных меток объектов "
+"файловой системы с использованием одного (или нескольких) расширенных "
+"атрибутов. По соображениям эффективности метка vnode (`v_label`) является "
+"кэшем любой метки на диске; политики могут загружать значения в кэш при "
+"создании vnode и обновлять кэш по мере необходимости. В результате нет "
+"необходимости напрямую обращаться к расширенному атрибуту при каждой "
+"проверке контроля доступа."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:233
+msgid ""
+"Currently, if a labeled policy permits dynamic unloading, its state slot "
+"cannot be reclaimed, which places a strict (and relatively low) bound on the "
+"number of unload-reload operations for labeled policies."
+msgstr ""
+"В настоящее время, если помеченная политика разрешает динамическую выгрузку, "
+"её слот состояния не может быть освобождён, что накладывает строгое (и "
+"относительно низкое) ограничение на количество операций выгрузки-"
+"перезагрузки для помеченных политик."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:236
+#, no-wrap
+msgid "System Calls"
+msgstr "Системные вызовы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:239
+msgid ""
+"The MAC Framework implements a number of system calls: most of these calls "
+"support the policy-agnostic label retrieval and manipulation APIs exposed to "
+"user applications."
+msgstr ""
+"В рамках MAC Framework реализован ряд системных вызовов: большинство из них "
+"поддерживают API для получения и управления метками, не зависящий от "
+"политики и доступный пользовательским приложениям."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:245
+msgid ""
+"The label management calls accept a label description structure, `struct "
+"mac`, which contains a series of MAC label elements. Each element contains "
+"a character string name, and character string value. Each policy will be "
+"given the chance to claim a particular element name, permitting policies to "
+"expose multiple independent elements if desired. Policy modules perform the "
+"internalization and externalization between kernel labels and user-provided "
+"labels via entry points, permitting a variety of semantics. Label "
+"management system calls are generally wrapped by user library functions to "
+"perform memory allocation and error handling, simplifying user applications "
+"that must manage labels."
+msgstr ""
+"Вызовы управления метками принимают структуру описания метки `struct mac`, "
+"которая содержит серию элементов метки MAC. Каждый элемент содержит строку с "
+"именем и строку со значением. Каждой политике будет предоставлена "
+"возможность запросить определённое имя элемента, позволяя политикам "
+"предоставлять несколько независимых элементов, если это необходимо. Модули "
+"политик выполняют интернализацию и экстернализацию между метками ядра и "
+"метками, предоставленными пользователем, через точки входа, что позволяет "
+"использовать различные семантики. Системные вызовы управления метками обычно "
+"обёрнуты в функции пользовательской библиотеки для выполнения выделения "
+"памяти и обработки ошибок, упрощая пользовательские приложения, которые "
+"должны управлять метками."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:247
+msgid ""
+"The following MAC-related system calls are present in the FreeBSD kernel:"
+msgstr "В ядре FreeBSD есть следующие системные вызовы, связанные с MAC:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:249
+msgid ""
+"`mac_get_proc()` may be used to retrieve the label of the current process."
+msgstr ""
+"`mac_get_proc()` может использоваться для получения метки текущего процесса."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:250
+msgid ""
+"`mac_set_proc()` may be used to request a change in the label of the current "
+"process."
+msgstr ""
+"`mac_set_proc()` может использоваться, чтобы запросить изменение метки "
+"текущего процесса."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:251
+msgid ""
+"`mac_get_fd()` may be used to retrieve the label of an object (file, socket, "
+"pipe, ...) referenced by a file descriptor."
+msgstr ""
+"`mac_get_fd()` может использоваться для получения метки объекта (файл, "
+"сокет, канал, ...), на который ссылается файловый дескриптор."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:252
+msgid ""
+"`mac_get_file()` may be used to retrieve the label of an object referenced "
+"by a file system path."
+msgstr ""
+"`mac_get_file()` может использоваться для получения метки объекта, на "
+"который ссылается путь в файловой системе."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:253
+msgid ""
+"`mac_set_fd()` may be used to request a change in the label of an object "
+"(file, socket, pipe, ...) referenced by a file descriptor."
+msgstr ""
+"`mac_set_fd()` может использоваться для запроса изменения метки объекта "
+"(файл, сокет, канал, ...), на который ссылается файловый дескриптор."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:254
+msgid ""
+"`mac_set_file()` may be used to request a change in the label of an object "
+"referenced by a file system path."
+msgstr ""
+"`mac_set_file()` может использоваться для запроса изменения метки объекта, "
+"указанного по пути в файловой системе."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:255
+msgid ""
+"`mac_syscall()` permits policy modules to create new system calls without "
+"modifying the system call table; it accepts a target policy name, operation "
+"number, and opaque argument for use by the policy."
+msgstr ""
+"`mac_syscall()` позволяет модулям политик создавать новые системные вызовы "
+"без изменения таблицы системных вызовов; она принимает имя целевой политики, "
+"номер операции и непрозрачный аргумент для использования политикой."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:256
+msgid ""
+"`mac_get_pid()` may be used to request the label of another process by "
+"process id."
+msgstr ""
+"`mac_get_pid()` может использоваться для запроса метки другого процесса по "
+"его идентификатору."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:257
+msgid ""
+"`mac_get_link()` is identical to `mac_get_file()`, only it will not follow a "
+"symbolic link if it is the final entry in the path, so may be used to "
+"retrieve the label on a symlink."
+msgstr ""
+"`mac_get_link()` идентична `mac_get_file()`, но не переходит по "
+"символической ссылке, если она является конечным элементом пути, поэтому "
+"может использоваться для получения метки на символьной ссылке."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:258
+msgid ""
+"`mac_set_link()` is identical to `mac_set_file()`, only it will not follow a "
+"symbolic link if it is the final entry in a path, so may be used to "
+"manipulate the label on a symlink."
+msgstr ""
+"`mac_set_link()` идентична `mac_set_file()`, за исключением того, что она не "
+"следует по символической ссылке, если это конечный элемент пути, поэтому "
+"может использоваться для изменения метки на символьной ссылке."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:260
+msgid ""
+"`mac_execve()` is identical to the `execve()` system call, only it also "
+"accepts a requested label to set the process label to when beginning "
+"execution of a new program. This change in label on execution is referred "
+"to as a \"transition\"."
+msgstr ""
+"`mac_execve()` идентична системному вызову `execve()`, но также принимает "
+"запрошенную метку, которая будет установлена для процесса при начале "
+"выполнения новой программы. Это изменение метки при выполнении называется "
+"\"переходом\"."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:261
+msgid ""
+"`mac_get_peer()`, actually implemented via a socket option, retrieves the "
+"label of a remote peer on a socket, if available."
+msgstr ""
+"`mac_get_peer()`, фактически реализованный через параметр сокета, извлекает "
+"метку удалённого узла на сокете, если она доступна."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:263
+msgid ""
+"In addition to these system calls, the `SIOCSIGMAC` and `SIOCSIFMAC` network "
+"interface ioctls permit the labels on network interfaces to be retrieved and "
+"set."
+msgstr ""
+"В дополнение к этим системным вызовам, сетевые ioctl-команды `SIOCSIGMAC` и "
+"`SIOCSIFMAC` позволяют получать и устанавливать метки на сетевых интерфейсах."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:265
+#, no-wrap
+msgid "MAC Policy Architecture"
+msgstr "Архитектура политик MAC"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:270
+msgid ""
+"Security policies are either linked directly into the kernel, or compiled "
+"into loadable kernel modules that may be loaded at boot, or dynamically "
+"using the module loading system calls at runtime. Policy modules interact "
+"with the system through a set of declared entry points, providing access to "
+"a stream of system events and permitting the policy to influence access "
+"control decisions. Each policy contains a number of elements:"
+msgstr ""
+"Политики безопасности либо непосредственно встроены в ядро, либо "
+"скомпилированы в загружаемые модули ядра, которые могут быть загружены при "
+"загрузке системы или динамически с использованием системных вызовов загрузки "
+"модулей во время выполнения. Модули политик взаимодействуют с системой через "
+"набор объявленных точек входа, предоставляя доступ к потоку системных "
+"событий и позволяя политике влиять на решения контроля доступа. Каждая "
+"политика содержит ряд элементов:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:272
+msgid "Optional configuration parameters for policy."
+msgstr "Необязательные параметры конфигурации для политики."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:273
+msgid "Centralized implementation of the policy logic and parameters."
+msgstr "Централизованная реализация логики политики и параметров."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:274
+msgid ""
+"Optional implementation of policy life cycle events, such as initialization "
+"and destruction."
+msgstr ""
+"Необязательная реализация событий жизненного цикла политики, таких как "
+"инициализация и уничтожение."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:275
+msgid ""
+"Optional support for initializing, maintaining, and destroying labels on "
+"selected kernel objects."
+msgstr ""
+"Необязательная поддержка инициализации, обслуживания и удаления меток на "
+"выбранных объектах ядра."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:276
+msgid ""
+"Optional support for user process inspection and modification of labels on "
+"selected objects."
+msgstr ""
+"Дополнительная поддержка проверки процессов пользователя и изменения меток "
+"на выбранных объектах."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:277
+msgid ""
+"Implementation of selected access control entry points that are of interest "
+"to the policy."
+msgstr ""
+"Реализация выбранных точек входа контроля доступа, представляющих интерес "
+"для политики."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:278
+msgid ""
+"Declaration of policy identity, module entry points, and policy properties."
+msgstr ""
+"Объявление идентификатора политики, точек входа модуля и свойств политики."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:280
+#, no-wrap
+msgid "Policy Declaration"
+msgstr "Объявление политики"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:283
+msgid ""
+"Modules may be declared using the `MAC_POLICY_SET()` macro, which names the "
+"policy, provides a reference to the MAC entry point vector, provides load-"
+"time flags determining how the policy framework should handle the policy, "
+"and optionally requests the allocation of label state by the framework."
+msgstr ""
+"Модули могут быть объявлены с использованием макроса `MAC_POLICY_SET()`, "
+"который задаёт имя политики, предоставляет ссылку на вектор точек входа MAC, "
+"указывает флаги загрузки, определяющие, как фреймворк политик должен "
+"обрабатывать политику, и при необходимости запрашивает выделение состояния "
+"метки фреймворком."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:297
+#, no-wrap
+msgid ""
+"static struct mac_policy_ops mac_policy_ops =\n"
+"{\n"
+" .mpo_destroy = mac_policy_destroy,\n"
+" .mpo_init = mac_policy_init,\n"
+" .mpo_init_bpfdesc_label = mac_policy_init_bpfdesc_label,\n"
+" .mpo_init_cred_label = mac_policy_init_label,\n"
+"/* ... */\n"
+" .mpo_check_vnode_setutimes = mac_policy_check_vnode_setutimes,\n"
+" .mpo_check_vnode_stat = mac_policy_check_vnode_stat,\n"
+" .mpo_check_vnode_write = mac_policy_check_vnode_write,\n"
+"};\n"
+msgstr ""
+"static struct mac_policy_ops mac_policy_ops =\n"
+"{\n"
+" .mpo_destroy = mac_policy_destroy,\n"
+" .mpo_init = mac_policy_init,\n"
+" .mpo_init_bpfdesc_label = mac_policy_init_bpfdesc_label,\n"
+" .mpo_init_cred_label = mac_policy_init_label,\n"
+"/* ... */\n"
+" .mpo_check_vnode_setutimes = mac_policy_check_vnode_setutimes,\n"
+" .mpo_check_vnode_stat = mac_policy_check_vnode_stat,\n"
+" .mpo_check_vnode_write = mac_policy_check_vnode_write,\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:306
+msgid ""
+"The MAC policy entry point vector, `mac__policy__ops` in this example, "
+"associates functions defined in the module with specific entry points. A "
+"complete listing of available entry points and their prototypes may be found "
+"in the MAC entry point reference section. Of specific interest during "
+"module registration are the .mpo_destroy and .mpo_init entry "
+"points. .mpo_init will be invoked once a policy is successfully registered "
+"with the module framework but prior to any other entry points becoming "
+"active. This permits the policy to perform any policy-specific allocation "
+"and initialization, such as initialization of any data or "
+"locks. .mpo_destroy will be invoked when a policy module is unloaded to "
+"permit releasing of any allocated memory and destruction of locks. "
+"Currently, these two entry points are invoked with the MAC policy list mutex "
+"held to prevent any other entry points from being invoked: this will be "
+"changed, but in the mean time, policies should be careful about what kernel "
+"primitives they invoke so as to avoid lock ordering or sleeping problems."
+msgstr ""
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:310
+msgid ""
+"The policy declaration's module name field exists so that the module may be "
+"uniquely identified for the purposes of module dependencies. An appropriate "
+"string should be selected. The full string name of the policy is displayed "
+"to the user via the kernel log during load and unload events, and also "
+"exported when providing status information to userland processes."
+msgstr ""
+"Поле имени модуля в объявлении политики существует для того, чтобы модуль "
+"мог быть однозначно идентифицирован с целью управления зависимостями "
+"модулей. Следует выбрать подходящую строку. Полное имя политики отображается "
+"пользователю в журнале ядра при загрузке и выгрузке, а также экспортируется "
+"при предоставлении информации о статусе процессам в пользовательском "
+"пространстве."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:312
+#, no-wrap
+msgid "Policy Flags"
+msgstr "Флаги политик"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:316
+msgid ""
+"The policy declaration flags field permits the module to provide the "
+"framework with information about its capabilities at the time the module is "
+"loaded. Currently, three flags are defined:"
+msgstr ""
+"Поле флагов объявления политики позволяет модулю предоставлять фреймворку "
+"информацию о своих возможностях во время загрузки модуля. В настоящее время "
+"определены три флага:"
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:317
+#, no-wrap
+msgid "MPC_LOADTIME_FLAG_UNLOADOK"
+msgstr "MPC_LOADTIME_FLAG_UNLOADOK"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:321
+msgid ""
+"This flag indicates that the policy module may be unloaded. If this flag is "
+"not provided, then the policy framework will reject requests to unload the "
+"module. This flag might be used by modules that allocate label state and "
+"are unable to free that state at runtime."
+msgstr ""
+"Этот флаг указывает, что модуль политики может быть выгружен. Если этот флаг "
+"не указан, то фреймворк политики отклонит запросы на выгрузку модуля. Этот "
+"флаг может использоваться модулями, которые выделяют состояние метки и не "
+"могут освободить это состояние во время выполнения."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:322
+#, no-wrap
+msgid "MPC_LOADTIME_FLAG_NOTLATE"
+msgstr "MPC_LOADTIME_FLAG_NOTLATE"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:326
+msgid ""
+"This flag indicates that the policy module must be loaded and initialized "
+"early in the boot process. If the flag is specified, attempts to register "
+"the module following boot will be rejected. The flag may be used by "
+"policies that require pervasive labeling of all system objects, and cannot "
+"handle objects that have not been properly initialized by the policy."
+msgstr ""
+"Этот флаг указывает, что модуль политики должен быть загружен и "
+"инициализирован на раннем этапе процесса загрузки. Если флаг указан, попытки "
+"зарегистрировать модуль после загрузки будут отклонены. Флаг может "
+"использоваться политиками, которые требуют повсеместной маркировки всех "
+"системных объектов и не могут обрабатывать объекты, не прошедшие надлежащую "
+"инициализацию политикой."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:327
+#, no-wrap
+msgid "MPC_LOADTIME_FLAG_LABELMBUFS"
+msgstr "MPC_LOADTIME_FLAG_LABELMBUFS"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:332
+msgid ""
+"This flag indicates that the policy module requires labeling of Mbufs, and "
+"that memory should always be allocated for the storage of Mbuf labels. By "
+"default, the MAC Framework will not allocate label storage for Mbufs unless "
+"at least one loaded policy has this flag set. This measurably improves "
+"network performance when policies do not require Mbuf labeling. A kernel "
+"option, `MAC_ALWAYS_LABEL_MBUF`, exists to force the MAC Framework to "
+"allocate Mbuf label storage regardless of the setting of this flag, and may "
+"be useful in some environments."
+msgstr ""
+"Этот флаг указывает, что модуль политики требует маркировки Mbuf, и память "
+"всегда должна выделяться для хранения меток Mbuf. По умолчанию MAC Framework "
+"не выделяет память для хранения меток Mbuf, если хотя бы одна загруженная "
+"политика не установила этот флаг. Это заметно улучшает производительность "
+"сети, когда политики не требуют маркировки Mbuf. Существует опция ядра "
+"`MAC_ALWAYS_LABEL_MBUF`, которая заставляет MAC Framework выделять память "
+"для хранения меток Mbuf независимо от установки этого флага, и может быть "
+"полезной в некоторых средах."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:338
+msgid ""
+"Policies using the `MPC_LOADTIME_FLAG_LABELMBUFS` without the "
+"`MPC_LOADTIME_FLAG_NOTLATE` flag set must be able to correctly handle `NULL` "
+"Mbuf label pointers passed into entry points. This is necessary as in-"
+"flight Mbufs without label storage may persist after a policy enabling Mbuf "
+"labeling has been loaded. If a policy is loaded before the network "
+"subsystem is active (i.e., the policy is not being loaded late), then all "
+"Mbufs are guaranteed to have label storage."
+msgstr ""
+"Политики, использующие `MPC_LOADTIME_FLAG_LABELMBUFS` без установленного "
+"флага `MPC_LOADTIME_FLAG_NOTLATE`, должны корректно обрабатывать переданные "
+"`NULL` указатели меток Mbuf в точках входа. Это необходимо, так как Mbuf в "
+"процессе передачи без хранилища меток могут сохраняться после загрузки "
+"политики, включающей маркировку Mbuf. Если политика загружена до активации "
+"сетевой подсистемы (т.е. политика не загружается поздно), то все Mbuf "
+"гарантированно имеют хранилище меток."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:341
+#, no-wrap
+msgid "Policy Entry Points"
+msgstr "Точки входа политики"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:345
+msgid ""
+"Four classes of entry points are offered to policies registered with the "
+"framework: entry points associated with the registration and management of "
+"policies, entry points denoting initialization, creation, destruction, and "
+"other life cycle events for kernel objects, events associated with access "
+"control decisions that the policy module may influence, and calls associated "
+"with the management of labels on objects. In addition, a `mac_syscall()` "
+"entry point is provided so that policies may extend the kernel interface "
+"without registering new system calls."
+msgstr ""
+"Четыре класса точек входа предоставляются политикам, зарегистрированным в "
+"рамках системы: точки входа, связанные с регистрацией и управлением "
+"политиками, точки входа, обозначающие инициализацию, создание, уничтожение и "
+"другие события жизненного цикла объектов ядра, события, связанные с "
+"решениями контроля доступа, на которые политика может влиять, и вызовы, "
+"связанные с управлением метками на объектах. Кроме того, предоставляется "
+"точка входа `mac_syscall()`, позволяющая политикам расширять интерфейс ядра "
+"без регистрации новых системных вызовов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:350
+msgid ""
+"Policy module writers should be aware of the kernel locking strategy, as "
+"well as what object locks are available during which entry points. Writers "
+"should attempt to avoid deadlock scenarios by avoiding grabbing non-leaf "
+"locks inside of entry points, and also follow the locking protocol for "
+"object access and modification. In particular, writers should be aware that "
+"while necessary locks to access objects and their labels are generally held, "
+"sufficient locks to modify an object or its label may not be present for all "
+"entry points. Locking information for arguments is documented in the MAC "
+"framework entry point document."
+msgstr ""
+"Авторы модулей политик должны быть осведомлены о стратегии блокировок в "
+"ядре, а также о том, какие блокировки объектов доступны на различных точках "
+"входа. Им следует избегать сценариев взаимоблокировок, не захватывая "
+"нелистовые блокировки внутри точек входа, а также соблюдать протокол "
+"блокировок для доступа и изменения объектов. В частности, авторы должны "
+"учитывать, что хотя необходимые блокировки для доступа к объектам и их "
+"меткам обычно удерживаются, достаточные блокировки для изменения объекта или "
+"его метки могут отсутствовать для всех точек входа. Информация о блокировках "
+"аргументов документирована в описании точек входа фреймворка MAC."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:354
+msgid ""
+"Policy entry points will pass a reference to the object label along with the "
+"object itself. This permits labeled policies to be unaware of the internals "
+"of the object yet still make decisions based on the label. The exception to "
+"this is the process credential, which is assumed to be understood by "
+"policies as a first class security object in the kernel."
+msgstr ""
+"Точки входа политики будут передавать ссылку на метку объекта вместе с самим "
+"объектом. Это позволяет помеченным политикам не знать внутренней структуры "
+"объекта, но при этом принимать решения на основе метки. Исключением из этого "
+"являются учетные данные процесса, для которые предполагается, что политики "
+"понимают их, как объект безопасности первого класса в ядре."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:356
+#, no-wrap
+msgid "MAC Policy Entry Point Reference"
+msgstr "Справочник по точкам входа политики MAC"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:359
+#, no-wrap
+msgid "General-Purpose Module Entry Points"
+msgstr "Общие точки входа модуля"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:362
+#, no-wrap
+msgid "`mpo_init`"
+msgstr "`mpo_init`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:367
+#, no-wrap
+msgid "void mpo_init(struct mac_policy_conf *conf);\n"
+msgstr "void mpo_init(struct mac_policy_conf *conf);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:373
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:397
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:420
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:458
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:487
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:510
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:533
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:556
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:579
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:608
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:637
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:664
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:687
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:710
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:737
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:764
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:787
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:810
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:833
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:856
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:879
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:902
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:925
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:948
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:971
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:998
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1021
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1044
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1067
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1090
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1113
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1139
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1165
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1192
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1232
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1272
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1312
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1352
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1392
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1432
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1469
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1506
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1543
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1580
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1665
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1708
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1746
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1781
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1813
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1846
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1890
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1943
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1979
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2001
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2036
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2072
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2112
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2148
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2180
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2213
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2248
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2283
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2318
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2355
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2394
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2425
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2453
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2488
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2523
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2558
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2594
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2630
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2666
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2702
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2746
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2782
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2820
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2855
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2892
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2922
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2960
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2992
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3014
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3036
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3096
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3131
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3153
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3179
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3205
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3232
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3262
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3284
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3307
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3346
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3377
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3408
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3443
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3474
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3505
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3538
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3575
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3606
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3636
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3666
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3695
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3730
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3764
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3791
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3826
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3859
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3889
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3927
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3960
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3991
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4034
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4081
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4118
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4152
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4189
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4236
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4279
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4314
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4349
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4380
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4416
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4460
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4508
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4541
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4578
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4615
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4648
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4682
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4715
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4756
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4804
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4841
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4878
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4920
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4960
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4990
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5023
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5058
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5099
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5140
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5173
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5203
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5225
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5251
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5274
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5305
+#, no-wrap
+msgid "Parameter"
+msgstr "Параметр"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:374
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:398
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:421
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:459
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:488
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:511
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:534
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:557
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:580
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:609
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:638
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:665
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:688
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:711
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:738
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:765
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:788
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:811
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:834
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:857
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:880
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:903
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:926
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:949
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:972
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:999
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1022
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1045
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1068
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1091
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1114
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1140
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1166
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1193
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1233
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1273
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1313
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1353
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1393
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1433
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1470
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1507
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1544
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1581
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1666
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1709
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1747
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1782
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1814
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1847
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1891
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1944
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1980
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2002
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2037
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2073
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2113
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2149
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2181
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2214
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2249
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2284
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2319
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2356
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2395
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2426
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2454
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2489
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2524
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2559
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2595
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2631
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2667
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2703
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2747
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2783
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2821
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2856
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2893
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2923
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2961
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2993
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3015
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3037
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3097
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3132
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3154
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3180
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3206
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3233
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3263
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3285
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3308
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3347
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3378
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3409
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3444
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3475
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3506
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3539
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3576
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3607
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3637
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3667
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3696
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3731
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3765
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3792
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3827
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3860
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3890
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3928
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3961
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3992
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4035
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4082
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4119
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4153
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4190
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4237
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4280
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4315
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4350
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4381
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4417
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4461
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4509
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4542
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4579
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4616
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4649
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4683
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4716
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4757
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4805
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4842
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4879
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4921
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4961
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4991
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5024
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5059
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5100
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5141
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5174
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5204
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5226
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5252
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5275
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5306
+#, no-wrap
+msgid "Description"
+msgstr "Описание"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:376
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:400
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:423
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:461
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:490
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:513
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:536
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:559
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:582
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:611
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:640
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:667
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:690
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:713
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:740
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:767
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:790
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:813
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:836
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:859
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:882
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:905
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:928
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:951
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:974
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1001
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1024
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1047
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1070
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1093
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1116
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1142
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1168
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1195
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1235
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1275
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1315
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1355
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1395
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1435
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1472
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1509
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1546
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1583
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1668
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1711
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1749
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1784
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1816
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1849
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1893
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1946
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1982
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2004
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2039
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2075
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2115
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2151
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2183
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2216
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2251
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2286
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2321
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2358
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2397
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2428
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2456
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2491
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2526
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2561
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2597
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2633
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2669
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2705
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2749
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2785
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2823
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2858
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2895
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2925
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2963
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2995
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3017
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3039
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3099
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3134
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3156
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3182
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3208
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3235
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3265
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3287
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3310
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3349
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3380
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3411
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3446
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3477
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3508
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3541
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3578
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3609
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3639
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3669
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3698
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3733
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3767
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3794
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3829
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3862
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3892
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3930
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3963
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3994
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4037
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4084
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4121
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4155
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4192
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4239
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4282
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4317
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4352
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4383
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4419
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4463
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4511
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4544
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4581
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4618
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4651
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4685
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4718
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4759
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4807
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4844
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4881
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4923
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4963
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4993
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5026
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5061
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5102
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5143
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5176
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5206
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5228
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5254
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5277
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5308
+#, no-wrap
+msgid "Locking"
+msgstr "Блокировка"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:377
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:401
+#, no-wrap
+msgid "`conf`"
+msgstr "`conf`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:378
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:402
+#, no-wrap
+msgid "MAC policy definition"
+msgstr "Определение политики MAC"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:384
+msgid ""
+"Policy load event. The policy list mutex is held, so sleep operations "
+"cannot be performed, and calls out to other kernel subsystems must be made "
+"with caution. If potentially sleeping memory allocations are required "
+"during policy initialization, they should be made using a separate module "
+"SYSINIT()."
+msgstr ""
+"Событие загрузки политики. Мьютекс списка политик удерживается, поэтому "
+"операции ожидания выполнить нельзя, а вызовы других подсистем ядра должны "
+"осуществляться с осторожностью. Если во время инициализации политики "
+"требуются потенциально блокирующие выделения памяти, их следует выполнять с "
+"использованием отдельного модуля SYSINIT()."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:386
+#, no-wrap
+msgid "`mpo_destroy`"
+msgstr "`mpo_destroy`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:391
+#, no-wrap
+msgid "void mpo_destroy(struct mac_policy_conf *conf);\n"
+msgstr "void mpo_destroy(struct mac_policy_conf *conf);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:407
+msgid ""
+"Policy load event. The policy list mutex is held, so caution should be "
+"applied."
+msgstr ""
+"Событие загрузки политики. Мьютекс списка политик удерживается, поэтому "
+"следует соблюдать осторожность."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:409
+#, no-wrap
+msgid "`mpo_syscall`"
+msgstr "`mpo_syscall`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:414
+#, no-wrap
+msgid "int mpo_syscall(struct thread *td, int call, void *arg);\n"
+msgstr "int mpo_syscall(struct thread *td, int call, void *arg);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:424
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:462
+#, no-wrap
+msgid "`td`"
+msgstr "`td`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:425
+#, no-wrap
+msgid "Calling thread"
+msgstr "Вызывающий поток"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:428
+#, no-wrap
+msgid "`call`"
+msgstr "`call`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:429
+#, no-wrap
+msgid "Policy-specific syscall number"
+msgstr "Номер системного вызова, зависящий от политики"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:432
+#, no-wrap
+msgid "`arg`"
+msgstr "`arg`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:433
+#, no-wrap
+msgid "Pointer to syscall arguments"
+msgstr "Указатель на аргументы системного вызова"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:440
+msgid ""
+"This entry point provides a policy-multiplexed system call so that policies "
+"may provide additional services to user processes without registering "
+"specific system calls. The policy name provided during registration is used "
+"to demultiplexer calls from userland, and the arguments will be forwarded to "
+"this entry point. When implementing new services, security modules should "
+"be sure to invoke appropriate access control checks from the MAC framework "
+"as needed. For example, if a policy implements an augmented signal "
+"functionality, it should call the necessary signal access control checks to "
+"invoke the MAC framework and other registered policies."
+msgstr ""
+"Этот точку входа предоставляет мультиплексированный системный вызов на "
+"основе политик, что позволяет политикам предоставлять дополнительные сервисы "
+"пользовательским процессам без регистрации конкретных системных вызовов. Имя "
+"политики, указанное при регистрации, используется для демультиплексирования "
+"вызовов из пользовательского пространства, а аргументы будут переданы в эту "
+"точку входа. При реализации новых сервисов модули безопасности должны "
+"убедиться, что вызывают соответствующие проверки контроля доступа из MAC-"
+"фреймворка по мере необходимости. Например, если политика реализует "
+"расширенную функциональность сигналов, она должна вызывать необходимые "
+"проверки контроля доступа сигналов для задействования MAC-фреймворка и "
+"других зарегистрированных политик."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:444
+msgid ""
+"Modules must currently perform the `copyin()` of the syscall data on their "
+"own."
+msgstr ""
+"Модули в настоящее время должны самостоятельно выполнять `copyin()` для "
+"данных системного вызова."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:447
+#, no-wrap
+msgid "`mpo_thread_userret`"
+msgstr "`mpo_thread_userret`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:452
+#, no-wrap
+msgid "void mpo_thread_userret(struct thread *td);\n"
+msgstr "void mpo_thread_userret(struct thread *td);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:463
+#, no-wrap
+msgid "Returning thread"
+msgstr "Возвращающий поток"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:471
+msgid ""
+"This entry point permits policy modules to perform MAC-related events when a "
+"thread returns to user space, via a system call return, trap return, or "
+"otherwise. This is required for policies that have floating process labels, "
+"as it is not always possible to acquire the process lock at arbitrary points "
+"in the stack during system call processing; process labels might represent "
+"traditional authentication data, process history information, or other "
+"data. To employ this mechanism, intended changes to the process credential "
+"label may be stored in the `p_label` protected by a per-policy spin lock, "
+"and then set the per-thread `TDF_ASTPENDING` flag and per-process "
+"`PS_MACPENDM` flag to schedule a call to the `userret` entry point. From "
+"this entry point, the policy may create a replacement credential with less "
+"concern about the locking context. Policy writers are cautioned that event "
+"ordering relating to scheduling an AST and the AST being performed may be "
+"complex and interlaced in multithreaded applications."
+msgstr ""
+"Этот точка входа позволяет модулям политики выполнять события, связанные с "
+"MAC, когда поток возвращается в пользовательское пространство, через возврат "
+"системного вызова, возврат из ловушки или иным образом. Это необходимо для "
+"политик, имеющих плавающие метки процессов, так как не всегда возможно "
+"получить блокировку процесса в произвольных точках стека во время обработки "
+"системного вызова; метки процессов могут представлять традиционные данные "
+"аутентификации, информацию об истории процесса или другие данные. Для "
+"использования этого механизма предполагаемые изменения метки учётных данных "
+"процесса могут быть сохранены в `p_label`, защищённом спин-блокировкой для "
+"каждой политики, а затем установить флаг `TDF_ASTPENDING` для потока и флаг "
+"`PS_MACPENDM` для процесса, чтобы запланировать вызов точки входа `userret`. "
+"С этой точки входа политика может создать замену учётных данных с меньшими "
+"опасениями относительно контекста блокировки. Авторам политик следует "
+"учитывать, что порядок событий, связанных с планированием AST и выполнением "
+"AST, может быть сложным и переплетённым в многопоточных приложениях."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:473
+#, no-wrap
+msgid "Label Operations"
+msgstr "Операции с метками"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:476
+#, no-wrap
+msgid "`mpo_init_bpfdesc_label`"
+msgstr "`mpo_init_bpfdesc_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:481
+#, no-wrap
+msgid "void mpo_init_bpfdesc_label(struct label *label);\n"
+msgstr "void mpo_init_bpfdesc_label(struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:491
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:514
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:537
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:560
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:583
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:616
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:668
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:691
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:714
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:741
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:768
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:791
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:814
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:837
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:860
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:883
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:906
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:929
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:952
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1002
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1048
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1071
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1094
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1196
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1236
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1276
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1316
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1356
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1396
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1436
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1473
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1510
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1547
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1584
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1793
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1902
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3901
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4054
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4093
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4130
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4164
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4201
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4256
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4291
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4326
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4396
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4436
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4480
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4590
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4660
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4694
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4727
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4768
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4816
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4853
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4890
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4932
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5035
+#, no-wrap
+msgid "`label`"
+msgstr "`label`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:492
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:538
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:561
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:584
+#, no-wrap
+msgid "New label to apply"
+msgstr "Новая метка для инициализации"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:497
+msgid ""
+"Initialize the label on a newly instantiated bpfdesc (BPF descriptor). "
+"Sleeping is permitted."
+msgstr ""
+"Инициализировать метку на только что созданном bpfdesc (дескрипторе BPF). "
+"Разрешено использование режима сна."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:499
+#, no-wrap
+msgid "`mpo_init_cred_label`"
+msgstr "`mpo_init_cred_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:504
+#, no-wrap
+msgid "void mpo_init_cred_label(struct label *label);\n"
+msgstr "void mpo_init_cred_label(struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:515
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:715
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:742
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:769
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:792
+#, no-wrap
+msgid "New label to initialize"
+msgstr "Новая метка для инициализации"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:520
+msgid ""
+"Initialize the label for a newly instantiated user credential. Sleeping is "
+"permitted."
+msgstr ""
+"Инициализировать метку для вновь созданных учетных данных пользователя. "
+"Разрешено приостанавливать выполнение."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:522
+#, no-wrap
+msgid "`mpo_init_devfsdirent_label`"
+msgstr "`mpo_init_devfsdirent_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:527
+#, no-wrap
+msgid "void mpo_init_devfsdirent_label(struct label *label);\n"
+msgstr "void mpo_init_devfsdirent_label(struct label *label);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:543
+msgid ""
+"Initialize the label on a newly instantiated devfs entry. Sleeping is "
+"permitted."
+msgstr ""
+"Инициализировать метку на только что созданной записи devfs. Разрешено "
+"использование режима сна."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:545
+#, no-wrap
+msgid "`mpo_init_ifnet_label`"
+msgstr "`mpo_init_ifnet_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:550
+#, no-wrap
+msgid "void mpo_init_ifnet_label(struct label *label);\n"
+msgstr "void mpo_init_ifnet_label(struct label *label);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:566
+msgid ""
+"Initialize the label on a newly instantiated network interface. Sleeping is "
+"permitted."
+msgstr ""
+"Инициализировать метку на только что созданном сетевом интерфейсе. Разрешено "
+"приостанавливать выполнение."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:568
+#, no-wrap
+msgid "`mpo_init_ipq_label`"
+msgstr "`mpo_init_ipq_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:573
+#, no-wrap
+msgid "void mpo_init_ipq_label(struct label *label, int flag);\n"
+msgstr "void mpo_init_ipq_label(struct label *label, int flag);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:587
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:612
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:718
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:745
+#, no-wrap
+msgid "`flag`"
+msgstr "`flag`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:588
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:613
+#, no-wrap
+msgid "Sleeping/non-sleeping man:malloc[9]; see below"
+msgstr "Спящий/неспящий man:malloc[9]; см. ниже"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:595
+msgid ""
+"Initialize the label on a newly instantiated IP fragment reassembly queue. "
+"The `flag` field may be one of M_WAITOK and M_NOWAIT, and should be employed "
+"to avoid performing a sleeping man:malloc[9] during this initialization "
+"call. IP fragment reassembly queue allocation frequently occurs in "
+"performance sensitive environments, and the implementation should be careful "
+"to avoid sleeping or long-lived operations. This entry point is permitted "
+"to fail resulting in the failure to allocate the IP fragment reassembly "
+"queue."
+msgstr ""
+"Инициализировать метку в только что созданной очереди сборки IP-фрагментов. "
+"Поле `flag` может принимать одно из значений M_WAITOK или M_NOWAIT и должно "
+"использоваться, чтобы избежать выполнения \"спящего\" man:malloc[9] во время "
+"этого вызова инициализации. Выделение очереди сборки IP-фрагментов часто "
+"происходит в средах, чувствительных к производительности, и реализация "
+"должна избегать \"спящих\" или длительных операций. Этой точке входа "
+"разрешено завершаться неудачей, что приведёт к невозможности выделения "
+"очереди сборки IP-фрагментов."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:597
+#, no-wrap
+msgid "`mpo_init_mbuf_label`"
+msgstr "`mpo_init_mbuf_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:602
+#, no-wrap
+msgid "void mpo_init_mbuf_label(int flag, struct label *label);\n"
+msgstr "void mpo_init_mbuf_label(int flag, struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:617
+#, no-wrap
+msgid "Policy label to initialize"
+msgstr "Метка политики для инициализации"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:624
+msgid ""
+"Initialize the label on a newly instantiated mbuf packet header (`mbuf`). "
+"The `flag` field may be one of M_WAITOK and M_NOWAIT, and should be employed "
+"to avoid performing a sleeping man:malloc[9] during this initialization "
+"call. Mbuf allocation frequently occurs in performance sensitive "
+"environments, and the implementation should be careful to avoid sleeping or "
+"long-lived operations. This entry point is permitted to fail resulting in "
+"the failure to allocate the mbuf header."
+msgstr ""
+"Инициализировать на только что созданном заголовке пакета mbuf метку "
+"(`mbuf`). Поле `flag` может принимать одно из значений M_WAITOK или M_NOWAIT "
+"и должно использоваться, чтобы избежать выполнения \"спящего\" man:malloc[9] "
+"во время этого вызова инициализации. Выделение mbuf часто происходит в "
+"чувствительных к производительности средах, и реализация должна избегать "
+"\"спящего\" режима или длительных операций. Этой точке входа разрешено "
+"завершаться неудачей, что приведёт к невозможности выделения заголовка mbuf."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:626
+#, no-wrap
+msgid "`mpo_init_mount_label`"
+msgstr "`mpo_init_mount_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:631
+#, no-wrap
+msgid "void mpo_init_mount_label(struct label *mntlabel, struct label *fslabel);\n"
+msgstr "void mpo_init_mount_label(struct label *mntlabel, struct label *fslabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:641
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:975
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1955
+#, no-wrap
+msgid "`mntlabel`"
+msgstr "`mntlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:642
+#, no-wrap
+msgid "Policy label to be initialized for the mount itself"
+msgstr "Метка политики для инициализации самой точки монтирования"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:645
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:979
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1673
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1716
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1754
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1959
+#, no-wrap
+msgid "`fslabel`"
+msgstr "`fslabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:646
+#, no-wrap
+msgid "Policy label to be initialized for the file system"
+msgstr "Метка политики для инициализации файловой системы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:651
+msgid ""
+"Initialize the labels on a newly instantiated mount point. Sleeping is "
+"permitted."
+msgstr ""
+"Инициализировать метки на новой точке монтирования. Разрешено "
+"приостанавливать выполнение."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:653
+#, no-wrap
+msgid "`mpo_init_mount_fs_label`"
+msgstr "`mpo_init_mount_fs_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:658
+#, no-wrap
+msgid "void mpo_init_mount_fs_label(struct label *label);\n"
+msgstr "void mpo_init_mount_fs_label(struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:669
+#, no-wrap
+msgid "Label to be initialized"
+msgstr "Метка для инициализации"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:674
+msgid ""
+"Initialize the label on a newly mounted file system. Sleeping is permitted"
+msgstr ""
+"Инициализировать метку на только что смонтированной файловой системе. "
+"Разрешено приостановление работы"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:676
+#, no-wrap
+msgid "`mpo_init_pipe_label`"
+msgstr "`mpo_init_pipe_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:681
+#, no-wrap
+msgid "void mpo_init_pipe_label(struct label *label);\n"
+msgstr "void mpo_init_pipe_label(struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:692
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1437
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1474
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1511
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1548
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1585
+#, no-wrap
+msgid "Label to be filled in"
+msgstr "Метка для заполнения"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:697
+msgid ""
+"Initialize a label for a newly instantiated pipe. Sleeping is permitted."
+msgstr ""
+"Инициализировать метку для только что созданного канала. Разрешено "
+"приостановление выполнения."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:699
+#, no-wrap
+msgid "`mpo_init_socket_label`"
+msgstr "`mpo_init_socket_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:704
+#, no-wrap
+msgid "void mpo_init_socket_label(struct label *label, int flag);\n"
+msgstr "void mpo_init_socket_label(struct label *label, int flag);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:719
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:746
+#, no-wrap
+msgid "man:malloc[9] flags"
+msgstr "флаги man:malloc[9]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:724
+msgid ""
+"Initialize a label for a newly instantiated socket. The `flag` field may be "
+"one of M_WAITOK and M_NOWAIT, and should be employed to avoid performing a "
+"sleeping man:malloc[9] during this initialization call."
+msgstr ""
+"Инициализировать метку для нового сокета. Поле `flag` может принимать одно "
+"из значений M_WAITOK или M_NOWAIT и должно использоваться, чтобы избежать "
+"выполнения спящего man:malloc[9] во время этого вызова инициализации."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:726
+#, no-wrap
+msgid "`mpo_init_socket_peer_label`"
+msgstr "`mpo_init_socket_peer_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:731
+#, no-wrap
+msgid "void mpo_init_socket_peer_label(struct label *label, int flag);\n"
+msgstr "void mpo_init_socket_peer_label(struct label *label, int flag);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:751
+msgid ""
+"Initialize the peer label for a newly instantiated socket. The `flag` field "
+"may be one of M_WAITOK and M_NOWAIT, and should be employed to avoid "
+"performing a sleeping man:malloc[9] during this initialization call."
+msgstr ""
+"Инициализировать метку однорангового узла (peer) для вновь созданного "
+"сокета. Поле `flag` может принимать одно из значений M_WAITOK или M_NOWAIT и "
+"должно использоваться для избежания выполнения спящего man:malloc[9] во "
+"время этого вызова инициализации."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:753
+#, no-wrap
+msgid "`mpo_init_proc_label`"
+msgstr "`mpo_init_proc_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:758
+#, no-wrap
+msgid "void mpo_init_proc_label(struct label *label);\n"
+msgstr "void mpo_init_proc_label(struct label *label);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:774
+msgid ""
+"Initialize the label for a newly instantiated process. Sleeping is "
+"permitted."
+msgstr ""
+"Инициализировать метку для вновь созданного процесса. Разрешено "
+"приостановление выполнения."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:776
+#, no-wrap
+msgid "`mpo_init_vnode_label`"
+msgstr "`mpo_init_vnode_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:781
+#, no-wrap
+msgid "void mpo_init_vnode_label(struct label *label);\n"
+msgstr "void mpo_init_vnode_label(struct label *label);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:797
+msgid ""
+"Initialize the label on a newly instantiated vnode. Sleeping is permitted."
+msgstr ""
+"Инициализировать метку на только что созданном vnode. Разрешено "
+"приостанавливать выполнение."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:799
+#, no-wrap
+msgid "`mpo_destroy_bpfdesc_label`"
+msgstr "`mpo_destroy_bpfdesc_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:804
+#, no-wrap
+msgid "void mpo_destroy_bpfdesc_label(struct label *label);\n"
+msgstr "void mpo_destroy_bpfdesc_label(struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:815
+#, no-wrap
+msgid "bpfdesc label"
+msgstr "bpfdesc label"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:820
+msgid ""
+"Destroy the label on a BPF descriptor. In this entry point a policy should "
+"free any internal storage associated with `label` so that it may be "
+"destroyed."
+msgstr ""
+"Уничтожить метку на дескрипторе BPF. В этой точке входа политика должна "
+"освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно "
+"было уничтожить."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:822
+#, no-wrap
+msgid "`mpo_destroy_cred_label`"
+msgstr "`mpo_destroy_cred_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:827
+#, no-wrap
+msgid "void mpo_destroy_cred_label(struct label *label);\n"
+msgstr "void mpo_destroy_cred_label(struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:838
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:861
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:884
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:907
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:930
+#, no-wrap
+msgid "Label being destroyed"
+msgstr "Метка уничтожается"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:843
+msgid ""
+"Destroy the label on a credential. In this entry point, a policy module "
+"should free any internal storage associated with `label` so that it may be "
+"destroyed."
+msgstr ""
+"Уничтожить метку на учетных данных. В этой точке входа модуль политики "
+"должен освободить любое внутреннее хранилище, связанное с `label`, чтобы ее "
+"можно было уничтожить."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:845
+#, no-wrap
+msgid "`mpo_destroy_devfsdirent_label`"
+msgstr "`mpo_destroy_devfsdirent_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:850
+#, no-wrap
+msgid "void mpo_destroy_devfsdirent_label(struct label *label);\n"
+msgstr "void mpo_destroy_devfsdirent_label(struct label *label);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:866
+msgid ""
+"Destroy the label on a devfs entry. In this entry point, a policy module "
+"should free any internal storage associated with `label` so that it may be "
+"destroyed."
+msgstr ""
+"Уничтожить метку на записи devfs. В этой точке входа модуль политики должен "
+"освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно "
+"было уничтожить."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:868
+#, no-wrap
+msgid "`mpo_destroy_ifnet_label`"
+msgstr "`mpo_destroy_ifnet_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:873
+#, no-wrap
+msgid "void mpo_destroy_ifnet_label(struct label *label);\n"
+msgstr "void mpo_destroy_ifnet_label(struct label *label);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:889
+msgid ""
+"Destroy the label on a removed interface. In this entry point, a policy "
+"module should free any internal storage associated with `label` so that it "
+"may be destroyed."
+msgstr ""
+"Уничтожить метку на удаленном интерфейсе. В этой точке входа модуль политики "
+"должен освободить любое внутреннее хранилище, связанное с `label`, чтобы ее "
+"можно было уничтожить."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:891
+#, no-wrap
+msgid "`mpo_destroy_ipq_label`"
+msgstr "`mpo_destroy_ipq_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:896
+#, no-wrap
+msgid "void mpo_destroy_ipq_label(struct label *label);\n"
+msgstr "void mpo_destroy_ipq_label(struct label *label);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:912
+msgid ""
+"Destroy the label on an IP fragment queue. In this entry point, a policy "
+"module should free any internal storage associated with `label` so that it "
+"may be destroyed."
+msgstr ""
+"Уничтожить метку в очереди IP-фрагментов. В этой точке входа модуль политики "
+"должен освободить любое внутреннее хранилище, связанное с `label`, чтобы ее "
+"можно было уничтожить."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:914
+#, no-wrap
+msgid "`mpo_destroy_mbuf_label`"
+msgstr "`mpo_destroy_mbuf_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:919
+#, no-wrap
+msgid "void mpo_destroy_mbuf_label(struct label *label);\n"
+msgstr "void mpo_destroy_mbuf_label(struct label *label);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:935
+msgid ""
+"Destroy the label on an mbuf header. In this entry point, a policy module "
+"should free any internal storage associated with `label` so that it may be "
+"destroyed."
+msgstr ""
+"Уничтожить метку в заголовке mbuf. В этой точке входа модуль политики должен "
+"освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно "
+"было уничтожить."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:937
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:960
+#, no-wrap
+msgid "`mpo_destroy_mount_label`"
+msgstr "`mpo_destroy_mount_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:942
+#, no-wrap
+msgid "void mpo_destroy_mount_label(struct label *label);\n"
+msgstr "void mpo_destroy_mount_label(struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:953
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:976
+#, no-wrap
+msgid "Mount point label being destroyed"
+msgstr "Точка монтирования метки уничтожается"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:958
+msgid ""
+"Destroy the labels on a mount point. In this entry point, a policy module "
+"should free the internal storage associated with `mntlabel` so that they may "
+"be destroyed."
+msgstr ""
+"Уничтожить метки на точке монтирования. В этой точке входа модуль политики "
+"должен освободить внутреннее хранилище, связанное с `mntlabel`, чтобы ее "
+"можно было уничтожить."
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:965
+#, no-wrap
+msgid "void mpo_destroy_mount_label(struct label *mntlabel, struct label *fslabel);\n"
+msgstr "void mpo_destroy_mount_label(struct label *mntlabel, struct label *fslabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:980
+#, no-wrap
+msgid "File system label being destroyed>"
+msgstr "Метка файловой системы уничтожается"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:985
+msgid ""
+"Destroy the labels on a mount point. In this entry point, a policy module "
+"should free the internal storage associated with `mntlabel` and `fslabel` so "
+"that they may be destroyed."
+msgstr ""
+"Уничтожить метки на точке монтирования. В этой точке входа модуль политики "
+"должен освободить внутреннее хранилище, связанное с `mntlabel` и `fslabel`, "
+"чтобы их можно было уничтожить."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:987
+#, no-wrap
+msgid "`mpo_destroy_socket_label`"
+msgstr "`mpo_destroy_socket_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:992
+#, no-wrap
+msgid "void mpo_destroy_socket_label(struct label *label);\n"
+msgstr "void mpo_destroy_socket_label(struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1003
+#, no-wrap
+msgid "Socket label being destroyed"
+msgstr "Уничтожение метки сокета"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1008
+msgid ""
+"Destroy the label on a socket. In this entry point, a policy module should "
+"free any internal storage associated with `label` so that it may be "
+"destroyed."
+msgstr ""
+"Уничтожить метку на сокете. В этой точке входа модуль политики должен "
+"освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно "
+"было уничтожить."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1010
+#, no-wrap
+msgid "`mpo_destroy_socket_peer_label`"
+msgstr "`mpo_destroy_socket_peer_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1015
+#, no-wrap
+msgid "void mpo_destroy_socket_peer_label(struct label *peerlabel);\n"
+msgstr "void mpo_destroy_socket_peer_label(struct label *peerlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1025
+#, no-wrap
+msgid "`peerlabel`"
+msgstr "`peerlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1026
+#, no-wrap
+msgid "Socket peer label being destroyed"
+msgstr "Сокет: метка однорангового узла уничтожается"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1031
+msgid ""
+"Destroy the peer label on a socket. In this entry point, a policy module "
+"should free any internal storage associated with `label` so that it may be "
+"destroyed."
+msgstr ""
+"Уничтожить метку однорангового узла на сокете. В этой точке входа модуль "
+"политики должен освободить любое внутреннее хранилище, связанное с `label`, "
+"чтобы ее можно было уничтожить."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1033
+#, no-wrap
+msgid "`mpo_destroy_pipe_label`"
+msgstr "`mpo_destroy_pipe_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1038
+#, no-wrap
+msgid "void mpo_destroy_pipe_label(struct label *label);\n"
+msgstr "void mpo_destroy_pipe_label(struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1049
+#, no-wrap
+msgid "Pipe label"
+msgstr "Метка канала (pipe)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1054
+msgid ""
+"Destroy the label on a pipe. In this entry point, a policy module should "
+"free any internal storage associated with `label` so that it may be "
+"destroyed."
+msgstr ""
+"Уничтожить метку на канале. В этой точке входа модуль политики должен "
+"освободить всю внутреннюю память, связанную с `label`, чтобы её можно было "
+"уничтожить."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1056
+#, no-wrap
+msgid "`mpo_destroy_proc_label`"
+msgstr "`mpo_destroy_proc_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1061
+#, no-wrap
+msgid "void mpo_destroy_proc_label(struct label *label);\n"
+msgstr "void mpo_destroy_proc_label(struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1072
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1095
+#, no-wrap
+msgid "Process label"
+msgstr "Метка процесса"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1077
+msgid ""
+"Destroy the label on a process. In this entry point, a policy module should "
+"free any internal storage associated with `label` so that it may be "
+"destroyed."
+msgstr ""
+"Уничтожить метку на процессе. В этой точке входа модуль политики должен "
+"освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно "
+"было уничтожить."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1079
+#, no-wrap
+msgid "`mpo_destroy_vnode_label`"
+msgstr "`mpo_destroy_vnode_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1084
+#, no-wrap
+msgid "void mpo_destroy_vnode_label(struct label *label);\n"
+msgstr "void mpo_destroy_vnode_label(struct label *label);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1100
+msgid ""
+"Destroy the label on a vnode. In this entry point, a policy module should "
+"free any internal storage associated with `label` so that it may be "
+"destroyed."
+msgstr ""
+"Уничтожить метку на vnode. В этой точке входа модуль политики должен "
+"освободить любое внутреннее хранилище, связанное с `label`, чтобы ее можно "
+"было уничтожить."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1102
+#, no-wrap
+msgid "`mpo_copy_mbuf_label`"
+msgstr "`mpo_copy_mbuf_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1107
+#, no-wrap
+msgid "void mpo_copy_mbuf_label(struct label *src, struct label *dest);\n"
+msgstr "void mpo_copy_mbuf_label(struct label *src, struct label *dest);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1117
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1143
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1169
+#, no-wrap
+msgid "`src`"
+msgstr "`src`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1118
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1144
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1170
+#, no-wrap
+msgid "Source label"
+msgstr "Метка источника"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1121
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1147
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1173
+#, no-wrap
+msgid "`dest`"
+msgstr "`dest`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1122
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1148
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1174
+#, no-wrap
+msgid "Destination label"
+msgstr "Метка назначения"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1126
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1152
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1178
+msgid "Copy the label information in `src` into `dest`."
+msgstr "Скопировать информацию метки из `src` в `dest`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1128
+#, no-wrap
+msgid "`mpo_copy_pipe_label`"
+msgstr "`mpo_copy_pipe_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1133
+#, no-wrap
+msgid "void mpo_copy_pipe_label(struct label *src, struct label *dest);\n"
+msgstr "void mpo_copy_pipe_label(struct label *src, struct label *dest);\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1154
+#, no-wrap
+msgid "`mpo_copy_vnode_label`"
+msgstr "`mpo_copy_vnode_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1159
+#, no-wrap
+msgid "void mpo_copy_vnode_label(struct label *src, struct label *dest);\n"
+msgstr "void mpo_copy_vnode_label(struct label *src, struct label *dest);\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1180
+#, no-wrap
+msgid "`mpo_externalize_cred_label`"
+msgstr "`mpo_externalize_cred_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1186
+#, no-wrap
+msgid ""
+"int mpo_externalize_cred_label(struct label *label, char *element_name,\n"
+" struct sbuf *sb, int *claimed);\n"
+msgstr ""
+"int mpo_externalize_cred_label(struct label *label, char *element_name,\n"
+" struct sbuf *sb, int *claimed);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1197
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1237
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1277
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1317
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1357
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1397
+#, no-wrap
+msgid "Label to be externalized"
+msgstr "Метка для вынесения во внешний ресурс"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1200
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1240
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1280
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1320
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1360
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1400
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1440
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1477
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1514
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1551
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1588
+#, no-wrap
+msgid "`element_name`"
+msgstr "`element_name`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1201
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1241
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1281
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1321
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1361
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1401
+#, no-wrap
+msgid "Name of the policy whose label should be externalized"
+msgstr "Имя политики, метка которой должна быть вынесена во внешний ресурс"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1204
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1244
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1284
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1324
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1364
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1404
+#, no-wrap
+msgid "`sb`"
+msgstr "`sb`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1205
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1245
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1285
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1325
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1365
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1405
+#, no-wrap
+msgid "String buffer to be filled with a text representation of label"
+msgstr "Буфер строки для заполнения текстовым представлением метки"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1208
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1248
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1288
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1328
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1368
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1408
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1448
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1485
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1522
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1559
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1596
+#, no-wrap
+msgid "`claimed`"
+msgstr "`claimed`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1209
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1249
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1289
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1329
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1369
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1409
+#, no-wrap
+msgid "Should be incremented when `element_data` can be filled in."
+msgstr "Должно быть увеличено, когда `element_data` может быть заполнено."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1218
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1258
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1298
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1338
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1378
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1418
+msgid ""
+"Produce an externalized label based on the label structure passed. An "
+"externalized label consists of a text representation of the label contents "
+"that can be used with userland applications and read by the user. "
+"Currently, all policies' `externalize` entry points will be called, so the "
+"implementation should check the contents of `element_name` before attempting "
+"to fill in `sb`. If `element_name` does not match the name of your policy, "
+"simply return 0. Only return nonzero if an error occurs while externalizing "
+"the label data. Once the policy fills in `element_data`, `*claimed` should "
+"be incremented."
+msgstr ""
+"Создать внешнее представление метки на основе переданной структуры метки. "
+"Внешнее представление метки состоит из текстового представления содержимого "
+"метки, которое может использоваться пользовательскими приложениями и "
+"прочитано пользователем. В настоящее время будут вызываться точки входа "
+"`externalize` всех политик, поэтому реализация должна проверить содержимое "
+"`element_name` перед попыткой заполнить `sb`. Если `element_name` не "
+"соответствует имени вашей политики, просто верните 0. Возвращайте ненулевое "
+"значение только в случае ошибки при внешнем представлении данных метки. "
+"После того как политика заполнит `element_data`, `*claimed` должен быть "
+"увеличен."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1220
+#, no-wrap
+msgid "`mpo_externalize_ifnet_label`"
+msgstr "`mpo_externalize_ifnet_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1226
+#, no-wrap
+msgid ""
+"int mpo_externalize_ifnet_label(struct label *label, char *element_name,\n"
+" struct sbuf *sb, int *claimed);\n"
+msgstr ""
+"int mpo_externalize_ifnet_label(struct label *label, char *element_name,\n"
+" struct sbuf *sb, int *claimed);\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1260
+#, no-wrap
+msgid "`mpo_externalize_pipe_label`"
+msgstr "`mpo_externalize_pipe_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1266
+#, no-wrap
+msgid ""
+"int mpo_externalize_pipe_label(struct label *label, char *element_name,\n"
+" struct sbuf *sb, int *claimed);\n"
+msgstr ""
+"int mpo_externalize_pipe_label(struct label *label, char *element_name,\n"
+" struct sbuf *sb, int *claimed);\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1300
+#, no-wrap
+msgid "`mpo_externalize_socket_label`"
+msgstr "`mpo_externalize_socket_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1306
+#, no-wrap
+msgid ""
+"int mpo_externalize_socket_label(struct label *label, char *element_name,\n"
+" struct sbuf *sb, int *claimed);\n"
+msgstr ""
+"int mpo_externalize_socket_label(struct label *label, char *element_name,\n"
+" struct sbuf *sb, int *claimed);\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1340
+#, no-wrap
+msgid "`mpo_externalize_socket_peer_label`"
+msgstr "`mpo_externalize_socket_peer_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1346
+#, no-wrap
+msgid ""
+"int mpo_externalize_socket_peer_label(struct label *label, char *element_name,\n"
+" struct sbuf *sb, int *claimed);\n"
+msgstr ""
+"int mpo_externalize_socket_peer_label(struct label *label, char *element_name,\n"
+" struct sbuf *sb, int *claimed);\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1380
+#, no-wrap
+msgid "`mpo_externalize_vnode_label`"
+msgstr "`mpo_externalize_vnode_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1386
+#, no-wrap
+msgid ""
+"int mpo_externalize_vnode_label(struct label *label, char *element_name,\n"
+" struct sbuf *sb, int *claimed);\n"
+msgstr ""
+"int mpo_externalize_vnode_label(struct label *label, char *element_name,\n"
+" struct sbuf *sb, int *claimed);\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1420
+#, no-wrap
+msgid "`mpo_internalize_cred_label`"
+msgstr "`mpo_internalize_cred_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1426
+#, no-wrap
+msgid ""
+"int mpo_internalize_cred_label(struct label *label, char *element_name,\n"
+" char *element_data, int *claimed);\n"
+msgstr ""
+"int mpo_internalize_cred_label(struct label *label, char *element_name,\n"
+" char *element_data, int *claimed);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1441
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1478
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1515
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1552
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1589
+#, no-wrap
+msgid "Name of the policy whose label should be internalized"
+msgstr "Имя политики, метка которой должна быть приведена к внутреннему представлению"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1444
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1481
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1518
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1555
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1592
+#, no-wrap
+msgid "`element_data`"
+msgstr "`element_data`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1445
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1482
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1519
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1556
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1593
+#, no-wrap
+msgid "Text data to be internalized"
+msgstr "Текстовые данные для преобразования к внутреннему представлению"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1449
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1486
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1523
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1560
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1597
+#, no-wrap
+msgid "Should be incremented when data can be successfully internalized."
+msgstr "Должно увеличиваться, когда данные могут быть успешно преобразовываться вовнутреннее представление."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1455
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1492
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1529
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1566
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1603
+msgid ""
+"Produce an internal label structure based on externalized label data in text "
+"format. Currently, all policies' `internalize` entry points are called when "
+"internalization is requested, so the implementation should compare the "
+"contents of `element_name` to its own name in order to be sure it should be "
+"internalizing the data in `element_data`. Just as in the `externalize` "
+"entry points, the entry point should return 0 if `element_name` does not "
+"match its own name, or when data can successfully be internalized, in which "
+"case `*claimed` should be incremented."
+msgstr ""
+"Создать внутреннюю структуру меток на основе данных метки вов нешнем "
+"представлении в текстовом формате. В настоящее время, при запросе "
+"преобразования во внутреннее представление вызываются точки входа "
+"`internalize` всех политик, поэтому реализация должна сравнивать содержимое "
+"`element_name` со своим именем, чтобы убедиться, что она должна "
+"преобразовывать данные в `element_data`. Как и в точках входа `externalize`, "
+"точка входа должна возвращать 0, если `element_name` не совпадает с её "
+"собственным именем, или когда данные могут быть успешно преобразованы, в "
+"этом случае `*claimed` должен быть увеличен."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1457
+#, no-wrap
+msgid "`mpo_internalize_ifnet_label`"
+msgstr "`mpo_internalize_ifnet_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1463
+#, no-wrap
+msgid ""
+"int mpo_internalize_ifnet_label(struct label *label, char *element_name,\n"
+" char *element_data, int *claimed);\n"
+msgstr ""
+"int mpo_internalize_ifnet_label(struct label *label, char *element_name,\n"
+" char *element_data, int *claimed);\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1494
+#, no-wrap
+msgid "`mpo_internalize_pipe_label`"
+msgstr "`mpo_internalize_pipe_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1500
+#, no-wrap
+msgid ""
+"int mpo_internalize_pipe_label(struct label *label, char *element_name,\n"
+" char *element_data, int *claimed);\n"
+msgstr ""
+"int mpo_internalize_pipe_label(struct label *label, char *element_name,\n"
+" char *element_data, int *claimed);\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1531
+#, no-wrap
+msgid "`mpo_internalize_socket_label`"
+msgstr "`mpo_internalize_socket_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1537
+#, no-wrap
+msgid ""
+"int mpo_internalize_socket_label(struct label *label, char *element_name,\n"
+" char *element_data, int *claimed);\n"
+msgstr ""
+"int mpo_internalize_socket_label(struct label *label, char *element_name,\n"
+" char *element_data, int *claimed);\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1568
+#, no-wrap
+msgid "`mpo_internalize_vnode_label`"
+msgstr "`mpo_internalize_vnode_label`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1574
+#, no-wrap
+msgid ""
+"int mpo_internalize_vnode_label(struct label *label, char *element_name,\n"
+" char *element_data, int *claimed);\n"
+msgstr ""
+"int mpo_internalize_vnode_label(struct label *label, char *element_name,\n"
+" char *element_data, int *claimed);\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1605
+#, no-wrap
+msgid "Label Events"
+msgstr "События метки"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1613
+msgid ""
+"This class of entry points is used by the MAC framework to permit policies "
+"to maintain label information on kernel objects. For each labeled kernel "
+"object of interest to a MAC policy, entry points may be registered for "
+"relevant life cycle events. All objects implement initialization, creation, "
+"and destruction hooks. Some objects will also implement relabeling, "
+"allowing user processes to change the labels on objects. Some objects will "
+"also implement object-specific events, such as label events associated with "
+"IP reassembly. A typical labeled object will have the following life cycle "
+"of entry points:"
+msgstr ""
+"Этот класс точек входа используется фреймворком MAC для разрешения политикам "
+"поддерживать информацию о метках на объектах ядра. Для каждого помеченного "
+"объекта ядра, представляющего интерес для политики MAC, могут быть "
+"зарегистрированы точки входа для соответствующих событий жизненного цикла. "
+"Все объекты реализуют хуки инициализации, создания и уничтожения. Некоторые "
+"объекты также реализуют перемаркировку, позволяя пользовательским процессам "
+"изменять метки на объектах. Некоторые объекты также реализуют специфичные "
+"для объекта события, такие как события меток, связанные с повторной сборкой "
+"IP. Типичный помеченный объект будет иметь следующий жизненный цикл точек "
+"входа:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1625
+#, no-wrap
+msgid ""
+"Label initialization o\n"
+"(object-specific wait) \\\n"
+"Label creation o\n"
+" \\\n"
+"Relabel events, o--<--.\n"
+"Various object-specific, | |\n"
+"Access control events ~-->--o\n"
+" \\\n"
+"Label destruction o\n"
+msgstr ""
+"Label initialization o\n"
+"(object-specific wait) \\\n"
+"Label creation o\n"
+" \\\n"
+"Relabel events, o--<--.\n"
+"Various object-specific, | |\n"
+"Access control events ~-->--o\n"
+" \\\n"
+"Label destruction o\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1629
+msgid ""
+"Label initialization permits policies to allocate memory and set initial "
+"values for labels without context for the use of the object. The label slot "
+"allocated to a policy will be zeroed by default, so some policies may not "
+"need to perform initialization."
+msgstr ""
+"Инициализация меток позволяет политикам выделять память и устанавливать "
+"начальные значения для меток без контекста использования объекта. Слот "
+"метки, выделенный для политики, по умолчанию будет обнулен, поэтому "
+"некоторым политикам может не потребоваться выполнять инициализацию."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1636
+msgid ""
+"Label creation occurs when the kernel structure is associated with an actual "
+"kernel object. For example, Mbufs may be allocated and remain unused in a "
+"pool until they are required. mbuf allocation causes label initialization "
+"on the mbuf to take place, but mbuf creation occurs when the mbuf is "
+"associated with a datagram. Typically, context will be provided for a "
+"creation event, including the circumstances of the creation, and labels of "
+"other relevant objects in the creation process. For example, when an mbuf "
+"is created from a socket, the socket and its label will be presented to "
+"registered policies in addition to the new mbuf and its label. Memory "
+"allocation in creation events is discouraged, as it may occur in performance "
+"sensitive ports of the kernel; in addition, creation calls are not permitted "
+"to fail so a failure to allocate memory cannot be reported."
+msgstr ""
+"Создание метки происходит, когда структура ядра связывается с реальным "
+"объектом ядра. Например, Mbuf могут быть выделены и оставаться "
+"неиспользованными в пуле до тех пор, пока они не понадобятся. Выделение mbuf "
+"приводит к инициализации метки на mbuf, но создание mbuf происходит, когда "
+"mbuf связывается с датаграммой. Обычно для события создания предоставляется "
+"контекст, включая обстоятельства создания и метки других значимых объектов в "
+"процессе создания. Например, когда mbuf создаётся из сокета, сокет и его "
+"метка будут переданы зарегистрированным политикам в дополнение к новому mbuf "
+"и его метке. Выделение памяти в событиях создания не рекомендуется, так как "
+"это может происходить в чувствительных к производительности участках ядра; "
+"кроме того, вызовы создания не могут завершиться неудачей, поэтому "
+"невозможность выделить память не может быть сообщена."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1639
+msgid ""
+"Object specific events do not generally fall into the other broad classes of "
+"label events, but will generally provide an opportunity to modify or update "
+"the label on an object based on additional context. For example, the label "
+"on an IP fragment reassembly queue may be updated during the MAC_UPDATE_IPQ "
+"entry point as a result of the acceptance of an additional mbuf to that "
+"queue."
+msgstr ""
+"События, привящанные к объектам, обычно не попадают в другие классы событий "
+"меток, но, как правило, предоставляют возможность изменить или обновить "
+"метку объекта на основе дополнительного контекста. Например, метка в очереди "
+"сборки IP-фрагментов может быть обновлена во время точки входа "
+"`MAC_UPDATE_IPQ` в результате принятия дополнительного mbuf в эту очередь."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1641
+msgid "Access control events are discussed in detail in the following section."
+msgstr "События контроля доступа подробно рассматриваются в следующем разделе."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1643
+msgid ""
+"Label destruction permits policies to release storage or state associated "
+"with a label during its association with an object so that the kernel data "
+"structures supporting the object may be reused or released."
+msgstr ""
+"Уничтожение метки позволяет политикам освобождать хранилище или состояние, "
+"связанное с меткой во время её ассоциации с объектом, чтобы структуры данных "
+"ядра, поддерживающие объект, могли быть повторно использованы или "
+"освобождены."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1647
+msgid ""
+"In addition to labels associated with specific kernel objects, an additional "
+"class of labels exists: temporary labels. These labels are used to store "
+"update information submitted by user processes. These labels are "
+"initialized and destroyed as with other label types, but the creation event "
+"is MAC_INTERNALIZE, which accepts a user label to be converted to an in-"
+"kernel representation."
+msgstr ""
+"В дополнение к меткам, связанным с определёнными объектами ядра, существует "
+"дополнительный класс меток: временные метки. Эти метки используются для "
+"хранения информации об обновлениях, отправляемых пользовательскими "
+"процессами. Они инициализируются и уничтожаются так же, как и другие типы "
+"меток, но событие создания — это `MAC_INTERNALIZE`, которое принимает "
+"пользовательскую метку для преобразования во внутреннее представление в ядре."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1649
+#, no-wrap
+msgid "File System Object Labeling Event Operations"
+msgstr "Действия с событиями меток объектов файловой системы"
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1652
+#, no-wrap
+msgid "`mpo_associate_vnode_devfs`"
+msgstr "`mpo_associate_vnode_devfs`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1659
+#, no-wrap
+msgid ""
+"void mpo_associate_vnode_devfs(struct mount *mp, struct label *fslabel,\n"
+" struct devfs_dirent *de, struct label *delabel, struct vnode *vp,\n"
+" struct label *vlabel);\n"
+msgstr ""
+"void mpo_associate_vnode_devfs(struct mount *mp, struct label *fslabel,\n"
+" struct devfs_dirent *de, struct label *delabel, struct vnode *vp,\n"
+" struct label *vlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1669
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1712
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1750
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1854
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1951
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3834
+#, no-wrap
+msgid "`mp`"
+msgstr "`mp`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1670
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1855
+#, no-wrap
+msgid "Devfs mount point"
+msgstr "Точка монтирования devfs"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1674
+#, no-wrap
+msgid "Devfs file system label (`mp->mnt_fslabel`)"
+msgstr "Метка файловой системы devfs (`mp->mnt_fslabel`)"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1677
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1866
+#, no-wrap
+msgid "`de`"
+msgstr "`de`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1678
+#, no-wrap
+msgid "Devfs directory entry"
+msgstr "Запись каталога devfs"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1681
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1870
+#, no-wrap
+msgid "`delabel`"
+msgstr "`delabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1682
+#, no-wrap
+msgid "Policy label associated with `de`"
+msgstr "Метка политики, связанная с `de`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1685
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1720
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1758
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1914
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2009
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2044
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2084
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2934
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2968
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3240
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3799
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3897
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4050
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4089
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4126
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4160
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4197
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4252
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4287
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4322
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4357
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4392
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4432
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4476
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4586
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4656
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4690
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4723
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4764
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4812
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4849
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4886
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4928
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5031
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5181
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5282
+#, no-wrap
+msgid "`vp`"
+msgstr "`vp`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1686
+#, no-wrap
+msgid "vnode associated with `de`"
+msgstr "узел vnode, связанный с `de`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1689
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1724
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1762
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1918
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2048
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3244
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5185
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5286
+#, no-wrap
+msgid "`vlabel`"
+msgstr "`vlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1690
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1725
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1763
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1919
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2049
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4257
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4292
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4397
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4437
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4481
+#, no-wrap
+msgid "Policy label associated with `vp`"
+msgstr "Метка политики, связанная с `vp`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1694
+msgid ""
+"Fill in the label (`vlabel`) for a newly created devfs vnode based on the "
+"devfs directory entry passed in `de` and its label."
+msgstr ""
+"Заполнить метку (`vlabel`) для только что созданного devfs vnode на основе "
+"записи каталога devfs, переданной в `de`, и её метки."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1696
+#, no-wrap
+msgid "`mpo_associate_vnode_extattr`"
+msgstr "`mpo_associate_vnode_extattr`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1702
+#, no-wrap
+msgid ""
+"int mpo_associate_vnode_extattr(struct mount *mp, struct label *fslabel,\n"
+" struct vnode *vp, struct label *vlabel);\n"
+msgstr ""
+"int mpo_associate_vnode_extattr(struct mount *mp, struct label *fslabel,\n"
+" struct vnode *vp, struct label *vlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1713
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1751
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1899
+#, no-wrap
+msgid "File system mount point"
+msgstr "Точка монтирования файловой системы"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1717
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1755
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1903
+#, no-wrap
+msgid "File system label"
+msgstr "Метка файловой системы"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1721
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1759
+#, no-wrap
+msgid "Vnode to label"
+msgstr "Узел vnode для метки"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1732
+msgid ""
+"Attempt to retrieve the label for `vp` from the file system extended "
+"attributes. Upon success, the value `0` is returned. Should extended "
+"attribute retrieval not be supported, an accepted fallback is to copy "
+"`fslabel` into `vlabel`. In the event of an error, an appropriate value for "
+"`errno` should be returned."
+msgstr ""
+"Попытка получить метку для `vp` из расширенных атрибутов файловой системы. В "
+"случае успеха возвращается значение `0`. Если получение расширенных "
+"атрибутов не поддерживается, допустимым резервным вариантом является "
+"копирование `fslabel` в `vlabel`. В случае ошибки должно быть возвращено "
+"соответствующее значение `errno`."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1734
+#, no-wrap
+msgid "`mpo_associate_vnode_singlelabel`"
+msgstr "`mpo_associate_vnode_singlelabel`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1740
+#, no-wrap
+msgid ""
+"void mpo_associate_vnode_singlelabel(struct mount *mp, struct label *fslabel,\n"
+" struct vnode *vp, struct label *vlabel);\n"
+msgstr ""
+"void mpo_associate_vnode_singlelabel(struct mount *mp, struct label *fslabel,\n"
+" struct vnode *vp, struct label *vlabel);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1767
+msgid ""
+"On non-multilabel file systems, this entry point is called to set the policy "
+"label for `vp` based on the file system label, `fslabel`."
+msgstr ""
+"На файловых системах без поддержки multilabel эта точка входа вызывается для "
+"установки метки политики для `vp` на основе метки файловой системы `fslabel`."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1769
+#, no-wrap
+msgid "`mpo_create_devfs_device`"
+msgstr "`mpo_create_devfs_device`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1775
+#, no-wrap
+msgid ""
+"void mpo_create_devfs_device(dev_t dev, struct devfs_dirent *devfs_dirent,\n"
+" struct label *label);\n"
+msgstr ""
+"void mpo_create_devfs_device(dev_t dev, struct devfs_dirent *devfs_dirent,\n"
+" struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1785
+#, no-wrap
+msgid "`dev`"
+msgstr "`dev`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1786
+#, no-wrap
+msgid "Device corresponding with `devfs_dirent`"
+msgstr "Устройство, соответствующее `devfs_dirent`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1789
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1825
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2076
+#, no-wrap
+msgid "`devfs_dirent`"
+msgstr "`devfs_dirent`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1790
+#, no-wrap
+msgid "Devfs directory entry to be labeled."
+msgstr "Запись в каталоге devfs, для которой создается метка."
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1794
+#, no-wrap
+msgid "Label for `devfs_dirent` to be filled in."
+msgstr "Метка для `devfs_dirent`, которую нужно заполнить."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1799
+msgid ""
+"Fill out the label on a devfs_dirent being created for the passed device. "
+"This call will be made when the device file system is mounted, regenerated, "
+"or a new device is made available."
+msgstr ""
+"Заполнить метку на devfs_dirent, создаваемом для переданного устройства. "
+"Этот вызов будет выполнен при монтировании файловой системы устройств, её "
+"восстановлении или при появлении нового устройства."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1801
+#, no-wrap
+msgid "`mpo_create_devfs_directory`"
+msgstr "`mpo_create_devfs_directory`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1807
+#, no-wrap
+msgid ""
+"void mpo_create_devfs_directory(char *dirname, int dirnamelen,\n"
+" struct devfs_dirent *devfs_dirent, struct label *label);\n"
+msgstr ""
+"void mpo_create_devfs_directory(char *dirname, int dirnamelen,\n"
+" struct devfs_dirent *devfs_dirent, struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1817
+#, no-wrap
+msgid "`dirname`"
+msgstr "`dirname`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1818
+#, no-wrap
+msgid "Name of directory being created"
+msgstr "Имя создаваемого каталога"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1821
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5317
+#, no-wrap
+msgid "`namelen`"
+msgstr "`namelen`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1822
+#, no-wrap
+msgid "Length of string `dirname`"
+msgstr "Длина строки `dirname`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1826
+#, no-wrap
+msgid "Devfs directory entry for directory being created."
+msgstr "Запись в devfs для создаваемого каталога."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1831
+msgid ""
+"Fill out the label on a devfs_dirent being created for the passed "
+"directory. This call will be made when the device file system is mounted, "
+"regenerated, or a new device requiring a specific directory hierarchy is "
+"made available."
+msgstr ""
+"Заполнить метку на devfs_dirent, создаваемом для переданного каталога. Этот "
+"вызов будет выполнен при монтировании файловой системы устройств, её "
+"восстановлении или при появлении нового устройства, требующего определённой "
+"иерархии каталогов."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1833
+#, no-wrap
+msgid "`mpo_create_devfs_symlink`"
+msgstr "`mpo_create_devfs_symlink`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1840
+#, no-wrap
+msgid ""
+"void mpo_create_devfs_symlink(struct ucred *cred, struct mount *mp,\n"
+" struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de,\n"
+" struct label *delabel);\n"
+msgstr ""
+"void mpo_create_devfs_symlink(struct ucred *cred, struct mount *mp,\n"
+" struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de,\n"
+" struct label *delabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1850
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1894
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1947
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2005
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2040
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2152
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2184
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2252
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2287
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2398
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2824
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2996
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3018
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3040
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3135
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3157
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3183
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3209
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3236
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3266
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3288
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3311
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3350
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3381
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3412
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3447
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3478
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3509
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3542
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3579
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3610
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3670
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3699
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3734
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3768
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3795
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3830
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3863
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3893
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3931
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3964
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3995
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4038
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4085
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4122
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4156
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4193
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4240
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4283
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4318
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4353
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4420
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4464
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4512
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4545
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4582
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4619
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4652
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4686
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4719
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4760
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4808
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4845
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4882
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4924
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4964
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4994
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5027
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5062
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5103
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5144
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5207
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5229
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5255
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5278
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5309
+#, no-wrap
+msgid "`cred`"
+msgstr "`cred`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1851
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1895
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1948
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2006
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2041
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2153
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2185
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2253
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2288
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2399
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2825
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3041
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3136
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3158
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3184
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3210
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3237
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3267
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3289
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3312
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3351
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3382
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3413
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3448
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3479
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3510
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3543
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3580
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3611
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3641
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3671
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3700
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3735
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3769
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3796
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3831
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3864
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3894
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3932
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3965
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3996
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4039
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4086
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4123
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4157
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4194
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4241
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4284
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4354
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4385
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4421
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4465
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4513
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4546
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4583
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4620
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4653
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4687
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4720
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4761
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4809
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4846
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4883
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4925
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4965
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4995
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5028
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5063
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5104
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5145
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5178
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5208
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5230
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5256
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5279
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5310
+#, no-wrap
+msgid "Subject credential"
+msgstr "Учетные данные субъекта"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1858
+#, no-wrap
+msgid "`dd`"
+msgstr "`dd`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1859
+#, no-wrap
+msgid "Link destination"
+msgstr "Назначения cсылки"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1862
+#, no-wrap
+msgid "`ddlabel`"
+msgstr "`ddlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1863
+#, no-wrap
+msgid "Label associated with `dd`"
+msgstr "Метка, связанная с `dd`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1867
+#, no-wrap
+msgid "Symlink entry"
+msgstr "Символьная ссылка записи"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1871
+#, no-wrap
+msgid "Label associated with `de`"
+msgstr "Метка, связанная с `de`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1875
+msgid ""
+"Fill in the label (`delabel`) for a newly created man:devfs[5] symbolic link "
+"entry."
+msgstr ""
+"Заполнить метку (`delabel`) для новой структуры man:devfs[5] символьной "
+"ссылки."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1877
+#, no-wrap
+msgid "`mpo_create_vnode_extattr`"
+msgstr "`mpo_create_vnode_extattr`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1884
+#, no-wrap
+msgid ""
+"int mpo_create_vnode_extattr(struct ucred *cred, struct mount *mp,\n"
+" struct label *fslabel, struct vnode *dvp, struct label *dlabel,\n"
+" struct vnode *vp, struct label *vlabel, struct componentname *cnp);\n"
+msgstr ""
+"int mpo_create_vnode_extattr(struct ucred *cred, struct mount *mp,\n"
+" struct label *fslabel, struct vnode *dvp, struct label *dlabel,\n"
+" struct vnode *vp, struct label *vlabel, struct componentname *cnp);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1898
+#, no-wrap
+msgid "`mount`"
+msgstr "`mount`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1906
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3935
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3968
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3999
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4042
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4244
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4424
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4468
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4549
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4623
+#, no-wrap
+msgid "`dvp`"
+msgstr "`dvp`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1907
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4043
+#, no-wrap
+msgid "Parent directory vnode"
+msgstr "Родительский каталог vnode"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1910
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3939
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3972
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4003
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4046
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4248
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4428
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4472
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4553
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4627
+#, no-wrap
+msgid "`dlabel`"
+msgstr "`dlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1911
+#, no-wrap
+msgid "Label associated with `dvp`"
+msgstr "Метка, связанная с `dvp`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1915
+#, no-wrap
+msgid "Newly created vnode"
+msgstr "Вновь созданная vnode"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1922
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4007
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4058
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4260
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4440
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4488
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4557
+#, no-wrap
+msgid "`cnp`"
+msgstr "`cnp`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1923
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4059
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4441
+#, no-wrap
+msgid "Component name for `vp`"
+msgstr "Название компонента для `vp`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1929
+msgid ""
+"Write out the label for `vp` to the appropriate extended attribute. If the "
+"write succeeds, fill in `vlabel` with the label, and return 0. Otherwise, "
+"return an appropriate error."
+msgstr ""
+"Записать метку для `vp` в соответствующий расширенный атрибут. Если запись "
+"прошла успешно, заполняет `vlabel` меткой и возвращает 0. В противном случае "
+"вернет соответствующую ошибку."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1931
+#, no-wrap
+msgid "`mpo_create_mount`"
+msgstr "`mpo_create_mount`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1937
+#, no-wrap
+msgid ""
+"void mpo_create_mount(struct ucred *cred, struct mount *mp, struct label *mnt,\n"
+" struct label *fslabel);\n"
+msgstr ""
+"void mpo_create_mount(struct ucred *cred, struct mount *mp, struct label *mnt,\n"
+" struct label *fslabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1952
+#, no-wrap
+msgid "Object; file system being mounted"
+msgstr "Объект; файловая система, которая монтируется"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1956
+#, no-wrap
+msgid "Policy label to be filled in for `mp`"
+msgstr "Метка политики для заполнения в `mp`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1960
+#, no-wrap
+msgid "Policy label for the file system `mp` mounts."
+msgstr "Метка политики для файловой системы, монтируемой в `mp`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1965
+msgid ""
+"Fill out the labels on the mount point being created by the passed subject "
+"credential. This call will be made when a new file system is mounted."
+msgstr ""
+"Заполнить метки на точке монтирования, создаваемой переданными учетными "
+"данными субъекта. Этот вызов будет выполнен при монтировании новой файловой "
+"системы."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1967
+#, no-wrap
+msgid "`mpo_create_root_mount`"
+msgstr "`mpo_create_root_mount`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1973
+#, no-wrap
+msgid ""
+"void mpo_create_root_mount(struct ucred *cred, struct mount *mp,\n"
+" struct label *mntlabel, struct label *fslabel);\n"
+msgstr ""
+"void mpo_create_root_mount(struct ucred *cred, struct mount *mp,\n"
+" struct label *mntlabel, struct label *fslabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1983
+#, no-wrap
+msgid "See crossref:mac[mac-mpo-create-mount, `mpo_create_mount`]."
+msgstr "См. crossref:mac[mac-mpo-create-mount, `mpo_create_mount`]."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1987
+msgid ""
+"Fill out the labels on the mount point being created by the passed subject "
+"credential. This call will be made when the root file system is mounted, "
+"after `mpo_create_mount;`."
+msgstr ""
+"Заполнить метки на точке монтирования, создаваемой переданными учетными "
+"данными субъекта. Этот вызов будет выполнен при монтировании корневой "
+"файловой системы после `mpo_create_mount;`."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1989
+#, no-wrap
+msgid "`mpo_relabel_vnode`"
+msgstr "`mpo_relabel_vnode`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:1995
+#, no-wrap
+msgid ""
+"void mpo_relabel_vnode(struct ucred *cred, struct vnode *vp,\n"
+" struct label *vnodelabel, struct label *newlabel);\n"
+msgstr ""
+"void mpo_relabel_vnode(struct ucred *cred, struct vnode *vp,\n"
+" struct label *vnodelabel, struct label *newlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2010
+#, no-wrap
+msgid "vnode to relabel"
+msgstr "vnode для перемаркировки"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2013
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2088
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2938
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2972
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3803
+#, no-wrap
+msgid "`vnodelabel`"
+msgstr "`vnodelabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2014
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3804
+#, no-wrap
+msgid "Existing policy label for `vp`"
+msgstr "Существующая метка политики для `vp`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2017
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2264
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2299
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2334
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2836
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3044
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3424
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3711
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3746
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3772
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3807
+#, no-wrap
+msgid "`newlabel`"
+msgstr "`newlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2018
+#, no-wrap
+msgid "New, possibly partial label to replace `vnodelabel`"
+msgstr "Новая, возможно частичная метка для замены `vnodelabel`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2022
+msgid ""
+"Update the label on the passed vnode given the passed update vnode label and "
+"the passed subject credential."
+msgstr ""
+"Обновить метку на переданном vnode с учетом переданной обновленной метки "
+"vnode и переданных учетных данных субъекта."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2024
+#, no-wrap
+msgid "`mpo_setlabel_vnode_extattr`"
+msgstr "`mpo_setlabel_vnode_extattr`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2030
+#, no-wrap
+msgid ""
+"int mpo_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp,\n"
+" struct label *vlabel, struct label *intlabel);\n"
+msgstr ""
+"int mpo_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp,\n"
+" struct label *vlabel, struct label *intlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2045
+#, no-wrap
+msgid "Vnode for which the label is being written"
+msgstr "vnode, для которой записывается метка"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2052
+#, no-wrap
+msgid "`intlabel`"
+msgstr "`intlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2053
+#, no-wrap
+msgid "Label to write out"
+msgstr "Метка для записи"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2058
+msgid ""
+"Write out the policy from `intlabel` to an extended attribute. This is "
+"called from `vop_stdcreatevnode_ea`."
+msgstr ""
+"Записать политику из `intlabel` в расширенный атрибут. Этот метод вызывается "
+"из `vop_stdcreatevnode_ea`."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2060
+#, no-wrap
+msgid "`mpo_update_devfsdirent`"
+msgstr "`mpo_update_devfsdirent`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2066
+#, no-wrap
+msgid ""
+"void mpo_update_devfsdirent(struct devfs_dirent *devfs_dirent,\n"
+" struct label *direntlabel, struct vnode *vp, struct label *vnodelabel);\n"
+msgstr ""
+"void mpo_update_devfsdirent(struct devfs_dirent *devfs_dirent,\n"
+" struct label *direntlabel, struct vnode *vp, struct label *vnodelabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2077
+#, no-wrap
+msgid "Object; devfs directory entry"
+msgstr "Объект; каталожная запись devfs"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2080
+#, no-wrap
+msgid "`direntlabel`"
+msgstr "`direntlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2081
+#, no-wrap
+msgid "Policy label for `devfs_dirent` to be updated."
+msgstr "Метка политики для `devfs_dirent`, которая будет обновлена."
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2085
+#, no-wrap
+msgid "Parent vnode"
+msgstr "Родительский vnode"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2087
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2937
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3802
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4092
+#, no-wrap
+msgid "Locked"
+msgstr "Заблокирован"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2089
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2939
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2973
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3902
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4055
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4094
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4131
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4165
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4202
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4591
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4661
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4695
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4728
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4769
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4817
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4854
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4891
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4933
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5036
+#, no-wrap
+msgid "Policy label for `vp`"
+msgstr "Метка политики для `vp`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2095
+msgid ""
+"Update the `devfs_dirent` label from the passed devfs vnode label. This "
+"call will be made when a devfs vnode has been successfully relabeled to "
+"commit the label change such that it lasts even if the vnode is recycled. "
+"It will also be made when a symlink is created in devfs, following a call to "
+"`mac_vnode_create_from_vnode` to initialize the vnode label."
+msgstr ""
+"Обновить метку `devfs_dirent` из переданной метки devfs vnode. Этот вызов "
+"будет выполнен, когда devfs vnode успешно перемаркирован, чтобы "
+"зафиксировать изменение метки, чтобы оно сохранилось, даже если vnode будет "
+"переиспользован. Он также будет выполнен при создании символьной ссылки в "
+"devfs после вызова `mac_vnode_create_from_vnode` для инициализации метки "
+"vnode."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2097
+#, no-wrap
+msgid "IPC Object Labeling Event Operations"
+msgstr "Действия с событиями меток объектов IPC"
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2100
+#, no-wrap
+msgid "`mpo_create_mbuf_from_socket`"
+msgstr "`mpo_create_mbuf_from_socket`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2106
+#, no-wrap
+msgid ""
+"void mpo_create_mbuf_from_socket(struct socket *so, struct label *socketlabel,\n"
+" struct mbuf *m, struct label *mbuflabel);\n"
+msgstr ""
+"void mpo_create_mbuf_from_socket(struct socket *so, struct label *socketlabel,\n"
+" struct mbuf *m, struct label *mbuflabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2116
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3513
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3546
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3674
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3738
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4516
+#, no-wrap
+msgid "`socket`"
+msgstr "`socket`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2117
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3584
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3615
+#, no-wrap
+msgid "Socket"
+msgstr "Сокет"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2119
+#, no-wrap
+msgid "Socket locking WIP"
+msgstr "Блокировка сокетов — работа в процессе"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2120
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2192
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3517
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3550
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3587
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3618
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3678
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3742
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4520
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5152
+#, no-wrap
+msgid "`socketlabel`"
+msgstr "`socketlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2121
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3518
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3551
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3679
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4521
+#, no-wrap
+msgid "Policy label for `socket`"
+msgstr "Метка политики для `socket`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2124
+#, no-wrap
+msgid "`m`"
+msgstr "`m`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2125
+#, no-wrap
+msgid "Object; mbuf"
+msgstr "Объект; mbuf"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2128
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2326
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2610
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2646
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2682
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2863
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5078
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5119
+#, no-wrap
+msgid "`mbuflabel`"
+msgstr "`mbuflabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2129
+#, no-wrap
+msgid "Policy label to fill in for `m`"
+msgstr "Метка политики для заполнения для `m`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2134
+msgid ""
+"Set the label on a newly created mbuf header from the passed socket label. "
+"This call is made when a new datagram or message is generated by the socket "
+"and stored in the passed mbuf."
+msgstr ""
+"Установить метку на только что созданном заголовке mbuf из переданной метки "
+"сокета. Этот вызов выполняется, когда новый датаграмма или сообщение "
+"генерируется сокетом и сохраняется в переданном mbuf."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2136
+#, no-wrap
+msgid "`mpo_create_pipe`"
+msgstr "`mpo_create_pipe`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2142
+#, no-wrap
+msgid ""
+"void mpo_create_pipe(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel);\n"
+msgstr ""
+"void mpo_create_pipe(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2156
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2256
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3315
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3354
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3385
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3416
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3451
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3482
+#, no-wrap
+msgid "`pipe`"
+msgstr "`pipe`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2157
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2257
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3316
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3355
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3386
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3417
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3452
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3483
+#, no-wrap
+msgid "Pipe"
+msgstr "Канал"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2160
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3319
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3358
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3389
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3420
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3455
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3486
+#, no-wrap
+msgid "`pipelabel`"
+msgstr "`pipelabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2161
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3320
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3359
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3390
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3456
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3487
+#, no-wrap
+msgid "Policy label associated with `pipe`"
+msgstr "Метка политики, связанная с `pipe`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2166
+msgid ""
+"Set the label on a newly created pipe from the passed subject credential. "
+"This call is made when a new pipe is created."
+msgstr ""
+"Установить метку на только что созданном канале из переданных учетных данных "
+"субъекта. Этот вызов выполняется при создании нового канала."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2168
+#, no-wrap
+msgid "`mpo_create_socket`"
+msgstr "`mpo_create_socket`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2174
+#, no-wrap
+msgid ""
+"void mpo_create_socket(struct ucred *cred, struct socket *so,\n"
+" struct label *socketlabel);\n"
+msgstr ""
+"void mpo_create_socket(struct ucred *cred, struct socket *so,\n"
+" struct label *socketlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2187
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2290
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2401
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2929
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2967
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3798
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3866
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4088
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5147
+#, no-wrap
+msgid "Immutable"
+msgstr "Неизменяемый"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2188
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2291
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3583
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3614
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5148
+#, no-wrap
+msgid "`so`"
+msgstr "`so`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2189
+#, no-wrap
+msgid "Object; socket to label"
+msgstr "Объект; сокет для добавления метки"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2193
+#, no-wrap
+msgid "Label to fill in for `so`"
+msgstr "Метка для заполнения для `so`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2198
+msgid ""
+"Set the label on a newly created socket from the passed subject credential. "
+"This call is made when a socket is created."
+msgstr ""
+"Установить метку на новом сокете из переданных учетных данных субъекта. Этот "
+"вызов выполняется при создании сокета."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2200
+#, no-wrap
+msgid "`mpo_create_socket_from_socket`"
+msgstr "`mpo_create_socket_from_socket`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2207
+#, no-wrap
+msgid ""
+"void mpo_create_socket_from_socket(struct socket *oldsocket,\n"
+" struct label *oldsocketlabel, struct socket *newsocket,\n"
+" struct label *newsocketlabel);\n"
+msgstr ""
+"void mpo_create_socket_from_socket(struct socket *oldsocket,\n"
+" struct label *oldsocketlabel, struct socket *newsocket,\n"
+" struct label *newsocketlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2217
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2359
+#, no-wrap
+msgid "`oldsocket`"
+msgstr "`oldsocket`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2218
+#, no-wrap
+msgid "Listening socket"
+msgstr "Сокет, вызвавший listen"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2221
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2363
+#, no-wrap
+msgid "`oldsocketlabel`"
+msgstr "`oldsocketlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2222
+#, no-wrap
+msgid "Policy label associated with `oldsocket`"
+msgstr "Метка политики, связанная с `oldsocket`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2225
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2367
+#, no-wrap
+msgid "`newsocket`"
+msgstr "`newsocket`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2226
+#, no-wrap
+msgid "New socket"
+msgstr "Новый сокет"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2229
+#, no-wrap
+msgid "`newsocketlabel`"
+msgstr "`newsocketlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2230
+#, no-wrap
+msgid "Policy label associated with `newsocketlabel`"
+msgstr "Метка политики, связанная с `newsocketlabel`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2234
+msgid ""
+"Label a socket, `newsocket`, newly man:accept[2]ed, based on the "
+"man:listen[2] socket, `oldsocket`."
+msgstr ""
+"Создать метку сокета `newsocket`, только что принявшему соединение через "
+"man:accept[2], на основе сокета `oldsocket`, вызвавшего man:listen[2] ."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2236
+#, no-wrap
+msgid "`mpo_relabel_pipe`"
+msgstr "`mpo_relabel_pipe`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2242
+#, no-wrap
+msgid ""
+"void mpo_relabel_pipe(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *oldlabel, struct label *newlabel);\n"
+msgstr ""
+"void mpo_relabel_pipe(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *oldlabel, struct label *newlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2260
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2295
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2330
+#, no-wrap
+msgid "`oldlabel`"
+msgstr "`oldlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2261
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3421
+#, no-wrap
+msgid "Current policy label associated with `pipe`"
+msgstr "Текущая метка политики, связанная с `pipe`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2265
+#, no-wrap
+msgid "Policy label update to apply to `pipe`"
+msgstr "Обновление метки политики для применения к `pipe`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2269
+msgid "Apply a new label, `newlabel`, to `pipe`."
+msgstr "Применить новую метку `newlabel` к `pipe`."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2271
+#, no-wrap
+msgid "`mpo_relabel_socket`"
+msgstr "`mpo_relabel_socket`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2277
+#, no-wrap
+msgid ""
+"void mpo_relabel_socket(struct ucred *cred, struct socket *so,\n"
+" struct label *oldlabel, struct label *newlabel);\n"
+msgstr ""
+"void mpo_relabel_socket(struct ucred *cred, struct socket *so,\n"
+" struct label *oldlabel, struct label *newlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2292
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3675
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3739
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4517
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5149
+#, no-wrap
+msgid "Object; socket"
+msgstr "Объект; сокет"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2296
+#, no-wrap
+msgid "Current label for `so`"
+msgstr "Текущая метка для `so`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2300
+#, no-wrap
+msgid "Label update for `so`"
+msgstr "Метка обновления для `so`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2304
+msgid "Update the label on a socket from the passed socket label update."
+msgstr "Обновить метку на сокете из переданного обновления метки сокета."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2306
+#, no-wrap
+msgid "`mpo_set_socket_peer_from_mbuf`"
+msgstr "`mpo_set_socket_peer_from_mbuf`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2312
+#, no-wrap
+msgid ""
+"void mpo_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct label *mbuflabel,\n"
+" struct label *oldlabel, struct label *newlabel);\n"
+msgstr ""
+"void mpo_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct label *mbuflabel,\n"
+" struct label *oldlabel, struct label *newlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2322
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2606
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2642
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2678
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2859
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5074
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5115
+#, no-wrap
+msgid "`mbuf`"
+msgstr "`mbuf`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2323
+#, no-wrap
+msgid "First datagram received over socket"
+msgstr "Первый датаграмм, полученный через сокет"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2327
+#, no-wrap
+msgid "Label for `mbuf`"
+msgstr "Метка для `mbuf`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2331
+#, no-wrap
+msgid "Current label for the socket"
+msgstr "Текущая метка для сокета"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2335
+#, no-wrap
+msgid "Policy label to be filled out for the socket"
+msgstr "Метка политики для заполнения сокета"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2340
+msgid ""
+"Set the peer label on a stream socket from the passed mbuf label. This call "
+"will be made when the first datagram is received by the stream socket, with "
+"the exception of Unix domain sockets."
+msgstr ""
+"Установить метку однорангового узла на потоковом сокете из переданной метки "
+"mbuf. Этот вызов будет выполнен при получении первого датаграммы потоковым "
+"сокетом, за исключением сокетов домена Unix."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2342
+#, no-wrap
+msgid "`mpo_set_socket_peer_from_socket`"
+msgstr "`mpo_set_socket_peer_from_socket`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2349
+#, no-wrap
+msgid ""
+"void mpo_set_socket_peer_from_socket(struct socket *oldsocket,\n"
+" struct label *oldsocketlabel, struct socket *newsocket,\n"
+" struct label *newsocketpeerlabel);\n"
+msgstr ""
+"void mpo_set_socket_peer_from_socket(struct socket *oldsocket,\n"
+" struct label *oldsocketlabel, struct socket *newsocket,\n"
+" struct label *newsocketpeerlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2360
+#, no-wrap
+msgid "Local socket"
+msgstr "Локальный сокет"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2364
+#, no-wrap
+msgid "Policy label for `oldsocket`"
+msgstr "Метка политики для `oldsocket`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2368
+#, no-wrap
+msgid "Peer socket"
+msgstr "Сокет однорангового узла (peer socket)"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2371
+#, no-wrap
+msgid "`newsocketpeerlabel`"
+msgstr "`newsocketpeerlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2372
+#, no-wrap
+msgid "Policy label to fill in for `newsocket`"
+msgstr "Метка политики для заполнения для `newsocket`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2377
+msgid ""
+"Set the peer label on a stream UNIX domain socket from the passed remote "
+"socket endpoint. This call will be made when the socket pair is connected, "
+"and will be made for both endpoints."
+msgstr ""
+"Установите метку однорангового узла на потоковом UNIX-сокете из переданной "
+"конечной точки удаленного сокета. Этот вызов будет выполнен при соединении "
+"пары сокетов и будет произведен для обеих конечных точек."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2379
+#, no-wrap
+msgid "Network Object Labeling Event Operations"
+msgstr "Действия с событиями меток сетевых объектов"
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2382
+#, no-wrap
+msgid "`mpo_create_bpfdesc`"
+msgstr "`mpo_create_bpfdesc`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2388
+#, no-wrap
+msgid ""
+"void mpo_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d,\n"
+" struct label *bpflabel);\n"
+msgstr ""
+"void mpo_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d,\n"
+" struct label *bpflabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2402
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2634
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3100
+#, no-wrap
+msgid "`bpf_d`"
+msgstr "`bpf_d`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2403
+#, no-wrap
+msgid "Object; bpf descriptor"
+msgstr "Объект; дескриптор bpf"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2406
+#, no-wrap
+msgid "`bpf`"
+msgstr "`bpf`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2407
+#, no-wrap
+msgid "Policy label to be filled in for `bpf_d`"
+msgstr "Метка политики для заполнения для `bpf_d`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2412
+msgid ""
+"Set the label on a newly created BPF descriptor from the passed subject "
+"credential. This call will be made when a BPF device node is opened by a "
+"process with the passed subject credential."
+msgstr ""
+"Установить метку на новом дескрипторе BPF из переданных учётных данных "
+"субъекта. Этот вызов будет выполнен при открытии узла устройства BPF "
+"процессом с переданными учётными данными субъекта."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2414
+#, no-wrap
+msgid "`mpo_create_ifnet`"
+msgstr "`mpo_create_ifnet`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2419
+#, no-wrap
+msgid "void mpo_create_ifnet(struct ifnet *ifnet, struct label *ifnetlabel);\n"
+msgstr "void mpo_create_ifnet(struct ifnet *ifnet, struct label *ifnetlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2429
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2598
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2670
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2714
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2828
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3108
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3703
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5066
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5107
+#, no-wrap
+msgid "`ifnet`"
+msgstr "`ifnet`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2430
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2599
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2671
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2715
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5067
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5108
+#, no-wrap
+msgid "Network interface"
+msgstr "Сетевой интерфейс"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2433
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2602
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2674
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2718
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2832
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3112
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3707
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5070
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5111
+#, no-wrap
+msgid "`ifnetlabel`"
+msgstr "`ifnetlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2434
+#, no-wrap
+msgid "Policy label to fill in for `ifnet`"
+msgstr "Метка политики для заполнения для `ifnet`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2439
+msgid ""
+"Set the label on a newly created interface. This call may be made when a "
+"new physical interface becomes available to the system, or when a pseudo-"
+"interface is instantiated during the boot or as a result of a user action."
+msgstr ""
+"Установить метку на вновь созданном интерфейсе. Этот вызов может быть "
+"выполнен, когда новое физическое устройство становится доступным системе, "
+"или когда псевдо-интерфейс создаётся во время загрузки или в результате "
+"действия пользователя."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2441
+#, no-wrap
+msgid "`mpo_create_ipq`"
+msgstr "`mpo_create_ipq`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2447
+#, no-wrap
+msgid ""
+"void mpo_create_ipq(struct mbuf *fragment, struct label *fragmentlabel,\n"
+" struct ipq *ipq, struct label *ipqlabel);\n"
+msgstr ""
+"void mpo_create_ipq(struct mbuf *fragment, struct label *fragmentlabel,\n"
+" struct ipq *ipq, struct label *ipqlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2457
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2535
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2786
+#, no-wrap
+msgid "`fragment`"
+msgstr "`fragment`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2458
+#, no-wrap
+msgid "First received IP fragment"
+msgstr "Первый полученный IP-фрагмент"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2461
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2539
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2790
+#, no-wrap
+msgid "`fragmentlabel`"
+msgstr "`fragmentlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2462
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2791
+#, no-wrap
+msgid "Policy label for `fragment`"
+msgstr "Метка политики для `fragment`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2465
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2492
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2794
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2867
+#, no-wrap
+msgid "`ipq`"
+msgstr "`ipq`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2466
+#, no-wrap
+msgid "IP reassembly queue to be labeled"
+msgstr "Очередь повторной сборки IP, которой добавляетя метка"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2469
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2496
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2798
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2871
+#, no-wrap
+msgid "`ipqlabel`"
+msgstr "`ipqlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2470
+#, no-wrap
+msgid "Policy label to be filled in for `ipq`"
+msgstr "Метка политики для заполнения в `ipq`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2474
+msgid ""
+"Set the label on a newly created IP fragment reassembly queue from the mbuf "
+"header of the first received fragment."
+msgstr ""
+"Установить метку на вновь созданной очереди сборки IP-фрагментов из "
+"заголовка mbuf первого полученного фрагмента."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2476
+#, no-wrap
+msgid "`mpo_create_datagram_from_ipq`"
+msgstr "`mpo_create_datagram_from_ipq`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2482
+#, no-wrap
+msgid ""
+"void mpo_create_create_datagram_from_ipq(struct ipq *ipq,\n"
+" struct label *ipqlabel, struct mbuf *datagram, struct label *datagramlabel);\n"
+msgstr ""
+"void mpo_create_create_datagram_from_ipq(struct ipq *ipq,\n"
+" struct label *ipqlabel, struct mbuf *datagram, struct label *datagramlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2493
+#, no-wrap
+msgid "IP reassembly queue"
+msgstr "Очередь повторной сборки IP"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2497
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2799
+#, no-wrap
+msgid "Policy label for `ipq`"
+msgstr "Метка политики для `ipq`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2500
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2527
+#, no-wrap
+msgid "`datagram`"
+msgstr "`datagram`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2501
+#, no-wrap
+msgid "Datagram to be labeled"
+msgstr "Датаграмма для добавления метки"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2504
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2531
+#, no-wrap
+msgid "`datagramlabel`"
+msgstr "`datagramlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2505
+#, no-wrap
+msgid "Policy label to be filled in for `datagramlabel`"
+msgstr "Метка политики для заполнения в `datagramlabel`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2509
+msgid ""
+"Set the label on a newly reassembled IP datagram from the IP fragment "
+"reassembly queue from which it was generated."
+msgstr ""
+"Установите метку на вновь собранный IP-датаграмму из очереди сборки IP-"
+"фрагментов, из которой он был сгенерирован."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2511
+#, no-wrap
+msgid "`mpo_create_fragment`"
+msgstr "`mpo_create_fragment`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2517
+#, no-wrap
+msgid ""
+"void mpo_create_fragment(struct mbuf *datagram, struct label *datagramlabel,\n"
+" struct mbuf *fragment, struct label *fragmentlabel);\n"
+msgstr ""
+"void mpo_create_fragment(struct mbuf *datagram, struct label *datagramlabel,\n"
+" struct mbuf *fragment, struct label *fragmentlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2528
+#, no-wrap
+msgid "Datagram"
+msgstr "Датаграмма"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2532
+#, no-wrap
+msgid "Policy label for `datagram`"
+msgstr "Метка политики для `datagram`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2536
+#, no-wrap
+msgid "Fragment to be labeled"
+msgstr "Фрагмент, которому будет установлена метка"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2540
+#, no-wrap
+msgid "Policy label to be filled in for `datagram`"
+msgstr "Метка политики для заполнения для `datagram`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2544
+msgid ""
+"Set the label on the mbuf header of a newly created IP fragment from the "
+"label on the mbuf header of the datagram it was generate from."
+msgstr ""
+"Установить метку на заголовке mbuf вновь созданного IP-фрагмента из метки на "
+"заголовке mbuf датаграммы, из которой он был сгенерирован."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2546
+#, no-wrap
+msgid "`mpo_create_mbuf_from_mbuf`"
+msgstr "`mpo_create_mbuf_from_mbuf`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2552
+#, no-wrap
+msgid ""
+"void mpo_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct label *oldmbuflabel,\n"
+" struct mbuf *newmbuf, struct label *newmbuflabel);\n"
+msgstr ""
+"void mpo_create_mbuf_from_mbuf(struct mbuf *oldmbuf, struct label *oldmbuflabel,\n"
+" struct mbuf *newmbuf, struct label *newmbuflabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2562
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2706
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2750
+#, no-wrap
+msgid "`oldmbuf`"
+msgstr "`oldmbuf`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2563
+#, no-wrap
+msgid "Existing (source) mbuf"
+msgstr "Существующий (исходный) mbuf"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2566
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2710
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2754
+#, no-wrap
+msgid "`oldmbuflabel`"
+msgstr "`oldmbuflabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2567
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2711
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2755
+#, no-wrap
+msgid "Policy label for `oldmbuf`"
+msgstr "Метка политики для `oldmbuf`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2570
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2722
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2758
+#, no-wrap
+msgid "`newmbuf`"
+msgstr "`newmbuf`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2571
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2643
+#, no-wrap
+msgid "New mbuf to be labeled"
+msgstr "Новый mbuf для добавления метки"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2574
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2726
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2762
+#, no-wrap
+msgid "`newmbuflabel`"
+msgstr "`newmbuflabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2575
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2727
+#, no-wrap
+msgid "Policy label to be filled in for `newmbuf`"
+msgstr "Метка политики для заполнения в `newmbuf`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2580
+msgid ""
+"Set the label on the mbuf header of a newly created datagram from the mbuf "
+"header of an existing datagram. This call may be made in a number of "
+"situations, including when an mbuf is re-allocated for alignment purposes."
+msgstr ""
+"Установить метку в заголовке mbuf для вновь созданной датаграммы на основе "
+"заголовка mbuf существующей датаграммы. Этот вызов может быть выполнен в "
+"ряде ситуаций, включая случаи, когда для mbuf заново выделяется память для "
+"целей выравнивания."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2582
+#, no-wrap
+msgid "`mpo_create_mbuf_linklayer`"
+msgstr "`mpo_create_mbuf_linklayer`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2588
+#, no-wrap
+msgid ""
+"void mpo_create_mbuf_linklayer(struct ifnet *ifnet, struct label *ifnetlabel,\n"
+" struct mbuf *mbuf, struct label *mbuflabel);\n"
+msgstr ""
+"void mpo_create_mbuf_linklayer(struct ifnet *ifnet, struct label *ifnetlabel,\n"
+" struct mbuf *mbuf, struct label *mbuflabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2603
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2719
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2833
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3113
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5071
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5112
+#, no-wrap
+msgid "Policy label for `ifnet`"
+msgstr "Метка политики для `ifnet`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2607
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2679
+#, no-wrap
+msgid "mbuf header for new datagram"
+msgstr "заголовок mbuf для новой датаграммы"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2611
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2683
+#, no-wrap
+msgid "Policy label to be filled in for `mbuf`"
+msgstr "Метка политики для заполнения для `mbuf`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2616
+msgid ""
+"Set the label on the mbuf header of a newly created datagram generated for "
+"the purposes of a link layer response for the passed interface. This call "
+"may be made in a number of situations, including for ARP or ND6 responses in "
+"the IPv4 and IPv6 stacks."
+msgstr ""
+"Установить метку в заголовке mbuf для вновь созданной датаграммы, "
+"сгенерированного для целей ответа на канальном уровне для переданного "
+"интерфейса. Этот вызов может быть выполнен в ряде ситуаций, включая ответы "
+"ARP или ND6 в стеках IPv4 и IPv6."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2618
+#, no-wrap
+msgid "`mpo_create_mbuf_from_bpfdesc`"
+msgstr "`mpo_create_mbuf_from_bpfdesc`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2624
+#, no-wrap
+msgid ""
+"void mpo_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct label *bpflabel,\n"
+" struct mbuf *mbuf, struct label *mbuflabel);\n"
+msgstr ""
+"void mpo_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct label *bpflabel,\n"
+" struct mbuf *mbuf, struct label *mbuflabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2635
+#, no-wrap
+msgid "BPF descriptor"
+msgstr "Дескриптор BPF"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2638
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3104
+#, no-wrap
+msgid "`bpflabel`"
+msgstr "`bpflabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2639
+#, no-wrap
+msgid "Policy label for `bpflabel`"
+msgstr "Метка политики для `bpflabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2647
+#, no-wrap
+msgid "Policy label to fill in for `mbuf`"
+msgstr "Метка политики для заполнения `mbuf`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2652
+msgid ""
+"Set the label on the mbuf header of a newly created datagram generated using "
+"the passed BPF descriptor. This call is made when a write is performed to "
+"the BPF device associated with the passed BPF descriptor."
+msgstr ""
+"Установить метку на заголовок mbuf вновь созданной датаграммы, "
+"сгенерированной с использованием переданного дескриптора BPF. Этот вызов "
+"выполняется при записи в устройство BPF, связанное с переданным дескриптором "
+"BPF."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2654
+#, no-wrap
+msgid "`mpo_create_mbuf_from_ifnet`"
+msgstr "`mpo_create_mbuf_from_ifnet`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2660
+#, no-wrap
+msgid ""
+"void mpo_create_mbuf_from_ifnet(struct ifnet *ifnet, struct label *ifnetlabel,\n"
+" struct mbuf *mbuf, struct label *mbuflabel);\n"
+msgstr ""
+"void mpo_create_mbuf_from_ifnet(struct ifnet *ifnet, struct label *ifnetlabel,\n"
+" struct mbuf *mbuf, struct label *mbuflabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2675
+#, no-wrap
+msgid "Policy label for `ifnetlabel`"
+msgstr "Метка политики для `ifnetlabel`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2687
+msgid ""
+"Set the label on the mbuf header of a newly created datagram generated from "
+"the passed network interface."
+msgstr ""
+"Установить метку на заголовке mbuf вновь созданной датаграммы, "
+"сгенерированной из переданного сетевого интерфейса."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2689
+#, no-wrap
+msgid "`mpo_create_mbuf_multicast_encap`"
+msgstr "`mpo_create_mbuf_multicast_encap`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2696
+#, no-wrap
+msgid ""
+"void mpo_create_mbuf_multicast_encap(struct mbuf *oldmbuf,\n"
+" struct label *oldmbuflabel, struct ifnet *ifnet, struct label *ifnetlabel,\n"
+" struct mbuf *newmbuf, struct label *newmbuflabel);\n"
+msgstr ""
+"void mpo_create_mbuf_multicast_encap(struct mbuf *oldmbuf,\n"
+" struct label *oldmbuflabel, struct ifnet *ifnet, struct label *ifnetlabel,\n"
+" struct mbuf *newmbuf, struct label *newmbuflabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2707
+#, no-wrap
+msgid "mbuf header for existing datagram"
+msgstr "Заголовок mbuf для существующего датаграммы"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2723
+#, no-wrap
+msgid "mbuf header to be labeled for new datagram"
+msgstr "Заголовок mbuf для пометки новой датаграммы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2732
+msgid ""
+"Set the label on the mbuf header of a newly created datagram generated from "
+"the existing passed datagram when it is processed by the passed multicast "
+"encapsulation interface. This call is made when an mbuf is to be delivered "
+"using the virtual interface."
+msgstr ""
+"Установить метку в заголовке mbuf для вновь созданной датаграммы, "
+"сгенерированной из существующей переданной датаграммы, при её обработке "
+"переданным интерфейсом мультикастовой инкапсуляции. Этот вызов происходит "
+"при доставке mbuf с использованием виртуального интерфейса."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2734
+#, no-wrap
+msgid "`mpo_create_mbuf_netlayer`"
+msgstr "`mpo_create_mbuf_netlayer`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2740
+#, no-wrap
+msgid ""
+"void mpo_create_mbuf_netlayer(struct mbuf *oldmbuf, struct label *oldmbuflabel,\n"
+" struct mbuf *newmbuf, struct label *newmbuflabel);\n"
+msgstr ""
+"void mpo_create_mbuf_netlayer(struct mbuf *oldmbuf, struct label *oldmbuflabel,\n"
+" struct mbuf *newmbuf, struct label *newmbuflabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2751
+#, no-wrap
+msgid "Received datagram"
+msgstr "Полученная датаграмма"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2759
+#, no-wrap
+msgid "Newly created datagram"
+msgstr "Вновь созданная датаграмма"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2763
+#, no-wrap
+msgid "Policy label for `newmbuf`"
+msgstr "Метка политики для `newmbuf`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2768
+msgid ""
+"Set the label on the mbuf header of a newly created datagram generated by "
+"the IP stack in response to an existing received datagram (`oldmbuf`). This "
+"call may be made in a number of situations, including when responding to "
+"ICMP request datagrams."
+msgstr ""
+"Установить метку на заголовок mbuf вновь созданной датаграммы, "
+"сгенерированной стеком IP в ответ на полученную датаграмму (`oldmbuf`). Этот "
+"вызов может быть выполнен в различных ситуациях, включая ответ на датаграммы "
+"ICMP-запросов."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2770
+#, no-wrap
+msgid "`mpo_fragment_match`"
+msgstr "`mpo_fragment_match`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2776
+#, no-wrap
+msgid ""
+"int mpo_fragment_match(struct mbuf *fragment, struct label *fragmentlabel,\n"
+" struct ipq *ipq, struct label *ipqlabel);\n"
+msgstr ""
+"int mpo_fragment_match(struct mbuf *fragment, struct label *fragmentlabel,\n"
+" struct ipq *ipq, struct label *ipqlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2787
+#, no-wrap
+msgid "IP datagram fragment"
+msgstr "Фрагмент IP-датаграммы"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2795
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2868
+#, no-wrap
+msgid "IP fragment reassembly queue"
+msgstr "Очередь сборки IP-фрагментов"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2806
+msgid ""
+"Determine whether an mbuf header containing an IP datagram (`fragment`) "
+"fragment matches the label of the passed IP fragment reassembly queue "
+"(`ipq`). Return (1) for a successful match, or (0) for no match. This call "
+"is made when the IP stack attempts to find an existing fragment reassembly "
+"queue for a newly received fragment; if this fails, a new fragment "
+"reassembly queue may be instantiated for the fragment. Policies may use "
+"this entry point to prevent the reassembly of otherwise matching IP "
+"fragments if policy does not permit them to be reassembled based on the "
+"label or other information."
+msgstr ""
+"Определить, соответствует ли заголовок mbuf, содержащий фрагмент IP-"
+"датаграммы (`fragment`), метке переданной очереди сборки IP-фрагментов "
+"(`ipq`). Возвращает (1) при успешном совпадении или (0) при отсутствии "
+"совпадения. Этот вызов выполняется, когда IP-стек пытается найти "
+"существующую очередь сборки фрагментов для вновь полученного фрагмента; если "
+"поиск не удаётся, для фрагмента может быть создана новая очередь сборки. "
+"Политики могут использовать эту точку входа, чтобы предотвратить сборку в "
+"остальном подходящих IP-фрагментов, если политика не разрешает их сборку на "
+"основе метки или другой информации."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2808
+#, no-wrap
+msgid "`mpo_relabel_ifnet`"
+msgstr "`mpo_relabel_ifnet`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2814
+#, no-wrap
+msgid ""
+"void mpo_relabel_ifnet(struct ucred *cred, struct ifnet *ifnet,\n"
+" struct label *ifnetlabel, struct label *newlabel);\n"
+msgstr ""
+"void mpo_relabel_ifnet(struct ucred *cred, struct ifnet *ifnet,\n"
+" struct label *ifnetlabel, struct label *newlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2829
+#, no-wrap
+msgid "Object; Network interface"
+msgstr "Объект; Сетевой интерфейс"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2837
+#, no-wrap
+msgid "Label update to apply to `ifnet`"
+msgstr "Метка обновления для применения к `ifnet`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2841
+msgid ""
+"Update the label of network interface, `ifnet`, based on the passed update "
+"label, `newlabel`, and the passed subject credential, `cred`."
+msgstr ""
+"Обновить метку сетевого интерфейса, `ifnet`, на основе переданной новой "
+"метки, `newlabel`, и переданных учетных данных субъекта, `cred`."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2843
+#, no-wrap
+msgid "`mpo_update_ipq`"
+msgstr "`mpo_update_ipq`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2849
+#, no-wrap
+msgid ""
+"void mpo_update_ipq(struct mbuf *fragment, struct label *fragmentlabel,\n"
+" struct ipq *ipq, struct label *ipqlabel);\n"
+msgstr ""
+"void mpo_update_ipq(struct mbuf *fragment, struct label *fragmentlabel,\n"
+" struct ipq *ipq, struct label *ipqlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2860
+#, no-wrap
+msgid "IP fragment"
+msgstr "IP фрагмент"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2864
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5079
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5120
+#, no-wrap
+msgid "Policy label for `mbuf`"
+msgstr "Метка политики для `mbuf`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2872
+#, no-wrap
+msgid "Policy label to be updated for `ipq`"
+msgstr "Метка политики для обновления для `ipq`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2876
+msgid ""
+"Update the label on an IP fragment reassembly queue (`ipq`) based on the "
+"acceptance of the passed IP fragment mbuf header (`mbuf`)."
+msgstr ""
+"Обновить метку в очереди сборки IP-фрагментов (`ipq`) на основе принятия "
+"переданного заголовка IP-фрагмента mbuf (`mbuf`)."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2878
+#, no-wrap
+msgid "Process Labeling Event Operations"
+msgstr "Действия с событиями меток процессов"
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2881
+#, no-wrap
+msgid "`mpo_create_cred`"
+msgstr "`mpo_create_cred`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2886
+#, no-wrap
+msgid "void mpo_create_cred(struct ucred *parent_cred, struct ucred *child_cred);\n"
+msgstr "void mpo_create_cred(struct ucred *parent_cred, struct ucred *child_cred);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2896
+#, no-wrap
+msgid "`parent_cred`"
+msgstr "`parent_cred`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2897
+#, no-wrap
+msgid "Parent subject credential"
+msgstr "Учетные данные субъекта‐родителя"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2900
+#, no-wrap
+msgid "`child_cred`"
+msgstr "`child_cred`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2901
+#, no-wrap
+msgid "Child subject credential"
+msgstr "Учётные данные дочернего субъекта"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2907
+msgid ""
+"Set the label of a newly created subject credential from the passed subject "
+"credential. This call will be made when man:crcopy[9] is invoked on a newly "
+"created `struct ucred`. This call should not be confused with a process "
+"forking or creation event."
+msgstr ""
+"Установить метку вновь созданного субъекта из переданного субъекта. Этот "
+"вызов будет выполнен при вызове man:crcopy[9] для только что созданной "
+"структуры `struct ucred`. Этот вызов не следует путать с событием создания "
+"или ветвления процесса."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2909
+#, no-wrap
+msgid "`mpo_execve_transition`"
+msgstr "`mpo_execve_transition`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2915
+#, no-wrap
+msgid ""
+"void mpo_execve_transition(struct ucred *old, struct ucred *new,\n"
+" struct vnode *vp, struct label *vnodelabel);\n"
+msgstr ""
+"void mpo_execve_transition(struct ucred *old, struct ucred *new,\n"
+" struct vnode *vp, struct label *vnodelabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2926
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2964
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5321
+#, no-wrap
+msgid "`old`"
+msgstr "`old`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2927
+#, no-wrap
+msgid "Existing subject credential"
+msgstr "Учетные данные существующего субъекта"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2930
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5333
+#, no-wrap
+msgid "`new`"
+msgstr "`new`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2931
+#, no-wrap
+msgid "New subject credential to be labeled"
+msgstr "Учетные данные нового субъекта для добавления метки"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2935
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2969
+#, no-wrap
+msgid "File to execute"
+msgstr "Файл для выполнения"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2946
+msgid ""
+"Update the label of a newly created subject credential (`new`) from the "
+"passed existing subject credential (`old`) based on a label transition "
+"caused by executing the passed vnode (`vp`). This call occurs when a "
+"process executes the passed vnode and one of the policies returns a success "
+"from the `mpo_execve_will_transition` entry point. Policies may choose to "
+"implement this call simply by invoking `mpo_create_cred` and passing the two "
+"subject credentials so as not to implement a transitioning event. Policies "
+"should not leave this entry point unimplemented if they implement "
+"`mpo_create_cred`, even if they do not implement "
+"`mpo_execve_will_transition`."
+msgstr ""
+"Обновить метку учетных данных вновь созданного субъекта (`new`) на основе "
+"переданных учетных данных существующего субъекта (`old`) в соответствии с "
+"переходом метки, вызванным выполнением переданного vnode (`vp`). Этот вызов "
+"происходит, когда процесс выполняет переданный vnode, и одна из политик "
+"возвращает успех из точки входа `mpo_execve_will_transition`. Политики могут "
+"выбрать реализацию этого вызова просто путем вызова `mpo_create_cred` и "
+"передачи двух субъектов учетных данных, чтобы не реализовывать событие "
+"перехода. Политики не должны оставлять эту точку входа нереализованной, если "
+"они реализуют `mpo_create_cred`, даже если они не реализуют "
+"`mpo_execve_will_transition`."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2948
+#, no-wrap
+msgid "`mpo_execve_will_transition`"
+msgstr "`mpo_execve_will_transition`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2954
+#, no-wrap
+msgid ""
+"int mpo_execve_will_transition(struct ucred *old, struct vnode *vp,\n"
+" struct label *vnodelabel);\n"
+msgstr ""
+"int mpo_execve_will_transition(struct ucred *old, struct vnode *vp,\n"
+" struct label *vnodelabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2965
+#, no-wrap
+msgid "Subject credential prior to man:execve[2]"
+msgstr "Учетные данные субъекта перед man:execve[2]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2979
+msgid ""
+"Determine whether the policy will want to perform a transition event as a "
+"result of the execution of the passed vnode by the passed subject "
+"credential. Return 1 if a transition is required, 0 if not. Even if a "
+"policy returns 0, it should behave correctly in the presence of an "
+"unexpected invocation of `mpo_execve_transition`, as that call may happen as "
+"a result of another policy requesting a transition."
+msgstr ""
+"Определить, будет ли политика выполнять событие перехода в результате "
+"выполнения переданного vnode с использованием переданных учетных данных "
+"субъекта. Вернуть 1, если переход требуется, и 0, если нет. Даже если "
+"политика возвращает 0, она должна корректно обрабатывать неожиданный вызов "
+"`mpo_execve_transition`, так как этот вызов может произойти из-за запроса "
+"перехода другой политикой."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2981
+#, no-wrap
+msgid "`mpo_create_proc0`"
+msgstr "`mpo_create_proc0`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2986
+#, no-wrap
+msgid "void mpo_create_proc0(struct ucred *cred);\n"
+msgstr "void mpo_create_proc0(struct ucred *cred);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:2997
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3019
+#, no-wrap
+msgid "Subject credential to be filled in"
+msgstr "Учетные данные субъекта для заполнения"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3001
+msgid ""
+"Create the subject credential of process 0, the parent of all kernel "
+"processes."
+msgstr ""
+"Создать учетные данные субъекта процесса 0, родителя всех процессов ядра."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3003
+#, no-wrap
+msgid "`mpo_create_proc1`"
+msgstr "`mpo_create_proc1`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3008
+#, no-wrap
+msgid "void mpo_create_proc1(struct ucred *cred);\n"
+msgstr "void mpo_create_proc1(struct ucred *cred);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3023
+msgid ""
+"Create the subject credential of process 1, the parent of all user processes."
+msgstr ""
+"Создать учетные данные субъекта процесса 1, родителя всех пользовательских "
+"процессов."
+
+#. type: Title =====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3025
+#, no-wrap
+msgid "`mpo_relabel_cred`"
+msgstr "`mpo_relabel_cred`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3030
+#, no-wrap
+msgid "void mpo_relabel_cred(struct ucred *cred, struct label *newlabel);\n"
+msgstr "void mpo_relabel_cred(struct ucred *cred, struct label *newlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3045
+#, no-wrap
+msgid "Label update to apply to `cred`"
+msgstr "Обновление метки для применения к `cred`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3049
+msgid "Update the label on a subject credential from the passed update label."
+msgstr ""
+"Обновить метку на учетных данных субъекта из переданной обновляемой метки."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3051
+#, no-wrap
+msgid "Access Control Checks"
+msgstr "Проверки контроля доступа"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3059
+msgid ""
+"Access control entry points permit policy modules to influence access "
+"control decisions made by the kernel. Generally, although not always, "
+"arguments to an access control entry point will include one or more "
+"authorizing credentials, information (possibly including a label) for any "
+"other objects involved in the operation. An access control entry point may "
+"return 0 to permit the operation, or an man:errno[2] error value. The "
+"results of invoking the entry point across various registered policy modules "
+"will be composed as follows: if all modules permit the operation to succeed, "
+"success will be returned. If one or modules returns a failure, a failure "
+"will be returned. If more than one module returns a failure, the errno "
+"value to return to the user will be selected using the following precedence, "
+"implemented by the `error_select()` function in [.filename]#kern_mac.c#:"
+msgstr ""
+"Точки входа контроля доступа позволяют модулям политики влиять на решения по "
+"контролю доступа, принимаемые ядром. Обычно, хотя и не всегда, аргументы "
+"точки входа контроля доступа включают одно или несколько удостоверяющих "
+"полномочий, информацию (возможно, включая метку) для любых других объектов, "
+"участвующих в операции. Точка входа контроля доступа может вернуть 0 для "
+"разрешения операции или значение ошибки man:errno[2]. Результаты вызова "
+"точки входа через различные зарегистрированные модули политики будут "
+"объединены следующим образом: если все модули разрешают успешное выполнение "
+"операции, будет возвращен успех. Если один или несколько модулей возвращают "
+"ошибку, будет возвращена ошибка. Если более одного модуля возвращают ошибку, "
+"значение errno, которое будет возвращено пользователю, выбирается с "
+"использованием следующего приоритета, реализованного функцией "
+"`error_select()` в [.filename]#kern_mac.c#:"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3065
+#, no-wrap
+msgid "Most precedence"
+msgstr "Наивысший приоритет"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3067
+#, no-wrap
+msgid "EDEADLK"
+msgstr "EDEADLK"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3070
+#, no-wrap
+msgid "EINVAL"
+msgstr "EINVAL"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3073
+#, no-wrap
+msgid "ESRCH"
+msgstr "ESRCH"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3076
+#, no-wrap
+msgid "EACCES"
+msgstr "EACCES"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3077
+#, no-wrap
+msgid "Least precedence"
+msgstr "Наименьший приоритет"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3078
+#, no-wrap
+msgid "EPERM"
+msgstr "EPERM"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3082
+msgid ""
+"If none of the error values returned by all modules are listed in the "
+"precedence chart then an arbitrarily selected value from the set will be "
+"returned. In general, the rules provide precedence to errors in the "
+"following order: kernel failures, invalid arguments, object not present, "
+"access not permitted, other."
+msgstr ""
+"Если ни одно из значений ошибок, возвращаемых всеми модулями, не указано в "
+"таблице приоритетов, будет возвращено произвольно выбранное значение из "
+"набора. В общем случае правила устанавливают следующий порядок приоритетов "
+"ошибок: сбои ядра, неверные аргументы, отсутствие объекта, отсутствие "
+"доступа, прочие."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3084
+#, no-wrap
+msgid "`mpo_check_bpfdesc_receive`"
+msgstr "`mpo_check_bpfdesc_receive`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3090
+#, no-wrap
+msgid ""
+"int mpo_check_bpfdesc_receive(struct bpf_d *bpf_d, struct label *bpflabel,\n"
+" struct ifnet *ifnet, struct label *ifnetlabel);\n"
+msgstr ""
+"int mpo_check_bpfdesc_receive(struct bpf_d *bpf_d, struct label *bpflabel,\n"
+" struct ifnet *ifnet, struct label *ifnetlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3101
+#, no-wrap
+msgid "Subject; BPF descriptor"
+msgstr "Субъект; Дескриптор BPF"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3105
+#, no-wrap
+msgid "Policy label for `bpf_d`"
+msgstr "Метка политики для `bpf_d`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3109
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3704
+#, no-wrap
+msgid "Object; network interface"
+msgstr "Объект; сетевой интерфейс"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3118
+msgid ""
+"Determine whether the MAC framework should permit datagrams from the passed "
+"interface to be delivered to the buffers of the passed BPF descriptor. "
+"Return (0) for success, or an `errno` value for failure Suggested failure: "
+"EACCES for label mismatches, EPERM for lack of privilege."
+msgstr ""
+"Определить, должен ли framework MAC разрешать доставку датаграмм с "
+"переданного интерфейса в буферы переданного BPF-дескриптора. Возвращает (0) "
+"при успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при "
+"несоответствии меток, EPERM при отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3120
+#, no-wrap
+msgid "`mpo_check_kenv_dump`"
+msgstr "`mpo_check_kenv_dump`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3125
+#, no-wrap
+msgid "int mpo_check_kenv_dump(struct ucred *cred);\n"
+msgstr "int mpo_check_kenv_dump(struct ucred *cred);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3140
+msgid ""
+"Determine whether the subject should be allowed to retrieve the kernel "
+"environment (see man:kenv[2])."
+msgstr ""
+"Определить, следует ли разрешить субъекту получать доступ к окружению ядра "
+"(см. man:kenv[2])."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3142
+#, no-wrap
+msgid "`mpo_check_kenv_get`"
+msgstr "`mpo_check_kenv_get`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3147
+#, no-wrap
+msgid "int mpo_check_kenv_get(struct ucred *cred, char *name);\n"
+msgstr "int mpo_check_kenv_get(struct ucred *cred, char *name);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3161
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3187
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3213
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4209
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4776
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5313
+#, no-wrap
+msgid "`name`"
+msgstr "`name`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3162
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3188
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3214
+#, no-wrap
+msgid "Kernel environment variable name"
+msgstr "Имя переменной окружения ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3166
+msgid ""
+"Determine whether the subject should be allowed to retrieve the value of the "
+"specified kernel environment variable."
+msgstr ""
+"Определить, следует ли разрешить субъекту получать значение указанной "
+"переменной окружения ядра."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3168
+#, no-wrap
+msgid "`mpo_check_kenv_set`"
+msgstr "`mpo_check_kenv_set`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3173
+#, no-wrap
+msgid "int mpo_check_kenv_set(struct ucred *cred, char *name);\n"
+msgstr "int mpo_check_kenv_set(struct ucred *cred, char *name);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3192
+msgid ""
+"Determine whether the subject should be allowed to set the specified kernel "
+"environment variable."
+msgstr ""
+"Определить, следует ли разрешить субъекту устанавливать указанную переменную "
+"окружения ядра."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3194
+#, no-wrap
+msgid "`mpo_check_kenv_unset`"
+msgstr "`mpo_check_kenv_unset`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3199
+#, no-wrap
+msgid "int mpo_check_kenv_unset(struct ucred *cred, char *name);\n"
+msgstr "int mpo_check_kenv_unset(struct ucred *cred, char *name);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3218
+msgid ""
+"Determine whether the subject should be allowed to unset the specified "
+"kernel environment variable."
+msgstr ""
+"Определить, следует ли разрешить субъекту сбросить указанную переменную "
+"окружения ядра."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3220
+#, no-wrap
+msgid "`mpo_check_kld_load`"
+msgstr "`mpo_check_kld_load`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3226
+#, no-wrap
+msgid ""
+"int mpo_check_kld_load(struct ucred *cred, struct vnode *vp,\n"
+" struct label *vlabel);\n"
+msgstr ""
+"int mpo_check_kld_load(struct ucred *cred, struct vnode *vp,\n"
+" struct label *vlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3241
+#, no-wrap
+msgid "Kernel module vnode"
+msgstr "vnode модуля ядра"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3245
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5186
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5287
+#, no-wrap
+msgid "Label associated with `vp`"
+msgstr "Метка, связанная с `vp`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3249
+msgid ""
+"Determine whether the subject should be allowed to load the specified module "
+"file."
+msgstr ""
+"Определить, следует ли разрешить субъекту загружать указанный файл модуля."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3251
+#, no-wrap
+msgid "`mpo_check_kld_stat`"
+msgstr "`mpo_check_kld_stat`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3256
+#, no-wrap
+msgid "int mpo_check_kld_stat(struct ucred *cred);\n"
+msgstr "int mpo_check_kld_stat(struct ucred *cred);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3271
+msgid ""
+"Determine whether the subject should be allowed to retrieve a list of loaded "
+"kernel module files and associated statistics."
+msgstr ""
+"Определить, следует ли разрешить субъекту получать список загруженных файлов "
+"модулей ядра и связанную с ними статистику."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3273
+#, no-wrap
+msgid "`mpo_check_kld_unload`"
+msgstr "`mpo_check_kld_unload`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3278
+#, no-wrap
+msgid "int mpo_check_kld_unload(struct ucred *cred);\n"
+msgstr "int mpo_check_kld_unload(struct ucred *cred);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3293
+msgid ""
+"Determine whether the subject should be allowed to unload a kernel module."
+msgstr "Определить, следует ли разрешить субъекту выгружать модуль ядра."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3295
+#, no-wrap
+msgid "`mpo_check_pipe_ioctl`"
+msgstr "`mpo_check_pipe_ioctl`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3301
+#, no-wrap
+msgid ""
+"int mpo_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel, unsigned long cmd, void *data);\n"
+msgstr ""
+"int mpo_check_pipe_ioctl(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel, unsigned long cmd, void *data);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3323
+#, no-wrap
+msgid "`cmd`"
+msgstr "`cmd`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3324
+#, no-wrap
+msgid "man:ioctl[2] command"
+msgstr "команда nman:ioctl[2]"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3327
+#, no-wrap
+msgid "`data`"
+msgstr "`data`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3328
+#, no-wrap
+msgid "man:ioctl[2] data"
+msgstr "данные man:ioctl[2]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3332
+msgid ""
+"Determine whether the subject should be allowed to make the specified "
+"man:ioctl[2] call."
+msgstr ""
+"Определить, следует ли разрешить субъекту выполнять указанный вызов "
+"man:ioctl[2]."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3334
+#, no-wrap
+msgid "`mpo_check_pipe_poll`"
+msgstr "`mpo_check_pipe_poll`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3340
+#, no-wrap
+msgid ""
+"int mpo_check_pipe_poll(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel);\n"
+msgstr ""
+"int mpo_check_pipe_poll(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3363
+msgid "Determine whether the subject should be allowed to poll `pipe`."
+msgstr "Определить, следует ли разрешить субъекту опрашивать `pipe`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3365
+#, no-wrap
+msgid "`mpo_check_pipe_read`"
+msgstr "`mpo_check_pipe_read`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3371
+#, no-wrap
+msgid ""
+"int mpo_check_pipe_read(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel);\n"
+msgstr ""
+"int mpo_check_pipe_read(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3394
+msgid "Determine whether the subject should be allowed read access to `pipe`."
+msgstr "Определить, следует ли разрешить субъекту доступ на чтение к `pipe`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3396
+#, no-wrap
+msgid "`mpo_check_pipe_relabel`"
+msgstr "`mpo_check_pipe_relabel`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3402
+#, no-wrap
+msgid ""
+"int mpo_check_pipe_relabel(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel, struct label *newlabel);\n"
+msgstr ""
+"int mpo_check_pipe_relabel(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel, struct label *newlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3425
+#, no-wrap
+msgid "Label update to `pipelabel`"
+msgstr "Обновление метки до `pipelabel`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3429
+msgid "Determine whether the subject should be allowed to relabel `pipe`."
+msgstr "Определить, следует ли разрешить субъекту изменять метку `pipe`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3431
+#, no-wrap
+msgid "`mpo_check_pipe_stat`"
+msgstr "`mpo_check_pipe_stat`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3437
+#, no-wrap
+msgid ""
+"int mpo_check_pipe_stat(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel);\n"
+msgstr ""
+"int mpo_check_pipe_stat(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3460
+msgid ""
+"Determine whether the subject should be allowed to retrieve statistics "
+"related to `pipe`."
+msgstr ""
+"Определить, следует ли разрешить субъекту получать статистику, связанную с "
+"`pipe`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3462
+#, no-wrap
+msgid "`mpo_check_pipe_write`"
+msgstr "`mpo_check_pipe_write`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3468
+#, no-wrap
+msgid ""
+"int mpo_check_pipe_write(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel);\n"
+msgstr ""
+"int mpo_check_pipe_write(struct ucred *cred, struct pipe *pipe,\n"
+" struct label *pipelabel);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3491
+msgid "Determine whether the subject should be allowed to write to `pipe`."
+msgstr "Определить, следует ли разрешить субъекту запись в `pipe`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3493
+#, no-wrap
+msgid "`mpo_check_socket_bind`"
+msgstr "`mpo_check_socket_bind`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3499
+#, no-wrap
+msgid ""
+"int mpo_check_socket_bind(struct ucred *cred, struct socket *socket,\n"
+" struct label *socketlabel, struct sockaddr *sockaddr);\n"
+msgstr ""
+"int mpo_check_socket_bind(struct ucred *cred, struct socket *socket,\n"
+" struct label *socketlabel, struct sockaddr *sockaddr);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3514
+#, no-wrap
+msgid "Socket to be bound"
+msgstr "Сокет для привязки"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3521
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3554
+#, no-wrap
+msgid "`sockaddr`"
+msgstr "`sockaddr`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3522
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3555
+#, no-wrap
+msgid "Address of `socket`"
+msgstr "Адрес `socket`"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3526
+#, no-wrap
+msgid "`mpo_check_socket_connect`"
+msgstr "`mpo_check_socket_connect`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3532
+#, no-wrap
+msgid ""
+"int mpo_check_socket_connect(struct ucred *cred, struct socket *socket,\n"
+" struct label *socketlabel, struct sockaddr *sockaddr);\n"
+msgstr ""
+"int mpo_check_socket_connect(struct ucred *cred, struct socket *socket,\n"
+" struct label *socketlabel, struct sockaddr *sockaddr);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3547
+#, no-wrap
+msgid "Socket to be connected"
+msgstr "Сокет для подключения"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3561
+msgid ""
+"Determine whether the subject credential (`cred`) can connect the passed "
+"socket (`socket`) to the passed socket address (`sockaddr`). Return 0 for "
+"success, or an `errno` value for failure. Suggested failure: EACCES for "
+"label mismatches, EPERM for lack of privilege."
+msgstr ""
+"Определить, может ли субъект с учётными данными (`cred`) подключить "
+"переданный сокет (`socket`) к переданному адресу сокета (`sockaddr`). "
+"Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые "
+"ошибки: EACCES при несоответствии меток, EPERM при отсутствии прав."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3563
+#, no-wrap
+msgid "`mpo_check_socket_receive`"
+msgstr "`mpo_check_socket_receive`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3569
+#, no-wrap
+msgid ""
+"int mpo_check_socket_receive(struct ucred *cred, struct socket *so,\n"
+" struct label *socketlabel);\n"
+msgstr ""
+"int mpo_check_socket_receive(struct ucred *cred, struct socket *so,\n"
+" struct label *socketlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3588
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3619
+#, no-wrap
+msgid "Policy label associated with `so`"
+msgstr "Метка политики, связанная с `so`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3592
+msgid ""
+"Determine whether the subject should be allowed to receive information from "
+"the socket `so`."
+msgstr ""
+"Определить, следует ли разрешить субъекту получать информацию из сокета `so`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3594
+#, no-wrap
+msgid "`mpo_check_socket_send`"
+msgstr "`mpo_check_socket_send`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3600
+#, no-wrap
+msgid ""
+"int mpo_check_socket_send(struct ucred *cred, struct socket *so,\n"
+" struct label *socketlabel);\n"
+msgstr ""
+"int mpo_check_socket_send(struct ucred *cred, struct socket *so,\n"
+" struct label *socketlabel);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3623
+msgid ""
+"Determine whether the subject should be allowed to send information across "
+"the socket `so`."
+msgstr ""
+"Определить, следует ли разрешить субъекту передавать информацию через сокет "
+"`so`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3625
+#, no-wrap
+msgid "`mpo_check_cred_visible`"
+msgstr "`mpo_check_cred_visible`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3630
+#, no-wrap
+msgid "int mpo_check_cred_visible(struct ucred *u1, struct ucred *u2);\n"
+msgstr "int mpo_check_cred_visible(struct ucred *u1, struct ucred *u2);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3640
+#, no-wrap
+msgid "`u1`"
+msgstr "`u1`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3644
+#, no-wrap
+msgid "`u2`"
+msgstr "`u2`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3645
+#, no-wrap
+msgid "Object credential"
+msgstr "Учетные данные объекта"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3652
+msgid ""
+"Determine whether the subject credential `u1` can \"see\" other subjects "
+"with the passed subject credential `u2`. Return 0 for success, or an "
+"`errno` value for failure. Suggested failure: EACCES for label mismatches, "
+"EPERM for lack of privilege, or ESRCH to hide visibility. This call may be "
+"made in a number of situations, including inter-process status sysctl's used "
+"by `ps`, and in procfs lookups."
+msgstr ""
+"Определить, может ли субъект с учётными данными `u1` \"видеть\" другие "
+"субъекты с переданными учётными данными `u2`. Возвращает 0 при успехе или "
+"значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии "
+"меток, EPERM при отсутствии привилегий или ESRCH для скрытия видимости. Этот "
+"вызов может выполняться в различных ситуациях, включая системные вызовы "
+"состояния межпроцессного взаимодействия, используемые `ps`, и при поиске в "
+"procfs."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3654
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5128
+#, no-wrap
+msgid "`mpo_check_socket_visible`"
+msgstr "`mpo_check_socket_visible`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3660
+#, no-wrap
+msgid ""
+"int mpo_check_socket_visible(struct ucred *cred, struct socket *socket,\n"
+" struct label *socketlabel);\n"
+msgstr ""
+"int mpo_check_socket_visible(struct ucred *cred, struct socket *socket,\n"
+" struct label *socketlabel);\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3683
+#, no-wrap
+msgid "`mpo_check_ifnet_relabel`"
+msgstr "`mpo_check_ifnet_relabel`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3689
+#, no-wrap
+msgid ""
+"int mpo_check_ifnet_relabel(struct ucred *cred, struct ifnet *ifnet,\n"
+" struct label *ifnetlabel, struct label *newlabel);\n"
+msgstr ""
+"int mpo_check_ifnet_relabel(struct ucred *cred, struct ifnet *ifnet,\n"
+" struct label *ifnetlabel, struct label *newlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3708
+#, no-wrap
+msgid "Existing policy label for `ifnet`"
+msgstr "Существующая метка политики для `ifnet`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3712
+#, no-wrap
+msgid "Policy label update to later be applied to `ifnet`"
+msgstr "Обновление метки политики для последующего применения к `ifnet`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3716
+msgid ""
+"Determine whether the subject credential can relabel the passed network "
+"interface to the passed label update."
+msgstr ""
+"Определить, может ли учетные данные субъекта перемаркировать переданный "
+"сетевой интерфейс в соответствии с переданным обновлением метки."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3718
+#, no-wrap
+msgid "`mpo_check_socket_relabel`"
+msgstr "`mpo_check_socket_relabel`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3724
+#, no-wrap
+msgid ""
+"int mpo_check_socket_relabel(struct ucred *cred, struct socket *socket,\n"
+" struct label *socketlabel, struct label *newlabel);\n"
+msgstr ""
+"int mpo_check_socket_relabel(struct ucred *cred, struct socket *socket,\n"
+" struct label *socketlabel, struct label *newlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3743
+#, no-wrap
+msgid "Existing policy label for `socket`"
+msgstr "Метка существующей политики для `socket`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3747
+#, no-wrap
+msgid "Label update to later be applied to `socketlabel`"
+msgstr "Обновление метки для последующего применения к `socketlabel`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3751
+msgid ""
+"Determine whether the subject credential can relabel the passed socket to "
+"the passed label update."
+msgstr ""
+"Определить, могут ли учётные данные субъекта перемаркировать переданный "
+"сокет в соответствии с переданным обновлением метки."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3753
+#, no-wrap
+msgid "`mpo_check_cred_relabel`"
+msgstr "`mpo_check_cred_relabel`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3758
+#, no-wrap
+msgid "int mpo_check_cred_relabel(struct ucred *cred, struct label *newlabel);\n"
+msgstr "int mpo_check_cred_relabel(struct ucred *cred, struct label *newlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3773
+#, no-wrap
+msgid "Label update to later be applied to `cred`"
+msgstr "Обновление метки для последующего применения к `cred`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3777
+msgid ""
+"Determine whether the subject credential can relabel itself to the passed "
+"label update."
+msgstr ""
+"Определить, могут ли учетные данные субъекта перемаркировать себя в "
+"соответствии с переданным обновлением метки."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3779
+#, no-wrap
+msgid "`mpo_check_vnode_relabel`"
+msgstr "`mpo_check_vnode_relabel`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3785
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_relabel(struct ucred *cred, struct vnode *vp,\n"
+" struct label *vnodelabel, struct label *newlabel);\n"
+msgstr ""
+"int mpo_check_vnode_relabel(struct ucred *cred, struct vnode *vp,\n"
+" struct label *vnodelabel, struct label *newlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3800
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3898
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4000
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4090
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4161
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4198
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4550
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4587
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4657
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4691
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4724
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4765
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4813
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4850
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4887
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5032
+#, no-wrap
+msgid "Object; vnode"
+msgstr "Объект; vnode"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3808
+#, no-wrap
+msgid "Policy label update to later be applied to `vp`"
+msgstr "Обновление метки политики для последующего применения к `vp`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3812
+msgid ""
+"Determine whether the subject credential can relabel the passed vnode to the "
+"passed label update."
+msgstr ""
+"Определить, могут ли учётные данные субъекта изменить метку переданного "
+"vnode на переданную обновлённую метку."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3814
+#, no-wrap
+msgid "`mpo_check_mount_stat`"
+msgstr "`mpo_check_mount_stat`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3820
+#, no-wrap
+msgid ""
+"int mpo_check_mount_stat(struct ucred *cred, struct mount *mp,\n"
+" struct label *mountlabel);\n"
+msgstr ""
+"int mpo_check_mount_stat(struct ucred *cred, struct mount *mp,\n"
+" struct label *mountlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3835
+#, no-wrap
+msgid "Object; file system mount"
+msgstr "Объект; точка монтирования файловой системы"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3838
+#, no-wrap
+msgid "`mountlabel`"
+msgstr "`mountlabel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3839
+#, no-wrap
+msgid "Policy label for `mp`"
+msgstr "Метка политики для `mp`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3846
+msgid ""
+"Determine whether the subject credential can see the results of a statfs "
+"performed on the file system. Return 0 for success, or an `errno` value for "
+"failure. Suggested failure: EACCES for label mismatches or EPERM for lack "
+"of privilege. This call may be made in a number of situations, including "
+"during invocations of man:statfs[2] and related calls, as well as to "
+"determine what file systems to exclude from listings of file systems, such "
+"as when man:getfsstat[2] is invoked."
+msgstr ""
+"Определить, могут ли учетные данные субъекта видеть результаты выполнения "
+"statfs для файловой системы. Возвращает 0 при успехе или значение `errno` "
+"при ошибке. Рекомендуемые ошибки: EACCES при несоответствии меток или EPERM "
+"при отсутствии привилегий. Этот вызов может выполняться в различных "
+"ситуациях, включая вызовы man:statfs[2] и связанных функций, а также для "
+"определения, какие файловые системы исключать из списка, например, при "
+"вызове man:getfsstat[2]."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3848
+#, no-wrap
+msgid "`mpo_check_proc_debug`"
+msgstr "`mpo_check_proc_debug`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3853
+#, no-wrap
+msgid "int mpo_check_proc_debug(struct ucred *cred, struct proc *proc);\n"
+msgstr "int mpo_check_proc_debug(struct ucred *cred, struct proc *proc);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3867
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4968
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4998
+#, no-wrap
+msgid "`proc`"
+msgstr "`proc`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3868
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4969
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4999
+#, no-wrap
+msgid "Object; process"
+msgstr "Объект; процесс"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3875
+msgid ""
+"Determine whether the subject credential can debug the passed process. "
+"Return 0 for success, or an `errno` value for failure. Suggested failure: "
+"EACCES for label mismatch, EPERM for lack of privilege, or ESRCH to hide "
+"visibility of the target. This call may be made in a number of situations, "
+"including use of the man:ptrace[2] and man:ktrace[2] APIs, as well as for "
+"some types of procfs operations."
+msgstr ""
+"Определить, могут ли учётные данные субъекта отлаживать переданный процесс. "
+"Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые "
+"ошибки: EACCES при несоответствии метки, EPERM при недостатке прав или ESRCH "
+"для скрытия видимости цели. Этот вызов может использоваться в различных "
+"ситуациях, включая использование API man:ptrace[2] и man:ktrace[2], а также "
+"для некоторых операций с procfs."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3877
+#, no-wrap
+msgid "`mpo_check_vnode_access`"
+msgstr "`mpo_check_vnode_access`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3883
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_access(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int flags);\n"
+msgstr ""
+"int mpo_check_vnode_access(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int flags);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3905
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4820
+#, no-wrap
+msgid "`flags`"
+msgstr "`flags`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3906
+#, no-wrap
+msgid "man:access[2] flags"
+msgstr "флаги man:access[2]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3913
+msgid ""
+"Determine how invocations of man:access[2] and related calls by the subject "
+"credential should return when performed on the passed vnode using the passed "
+"access flags. This should generally be implemented using the same semantics "
+"used in `mpo_check_vnode_open`. Return 0 for success, or an `errno` value "
+"for failure. Suggested failure: EACCES for label mismatches or EPERM for "
+"lack of privilege."
+msgstr ""
+"Определить, как должны возвращаться вызовы man:access[2] и связанные вызовы "
+"для субъекта с указанными учетными данными при выполнении на переданном "
+"vnode с использованием переданных флагов доступа. Обычно это должно быть "
+"реализовано с использованием той же семантики, что и в "
+"`mpo_check_vnode_open`. Возвращает 0 при успехе или значение `errno` при "
+"ошибке. Рекомендуемые ошибки: EACCES при несоответствии меток или EPERM при "
+"отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3915
+#, no-wrap
+msgid "`mpo_check_vnode_chdir`"
+msgstr "`mpo_check_vnode_chdir`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3921
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_chdir(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel);\n"
+msgstr ""
+"int mpo_check_vnode_chdir(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3936
+#, no-wrap
+msgid "Object; vnode to man:chdir[2] into"
+msgstr "Объект; vnode, в который делается man:chdir[2]"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3940
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4004
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4047
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4554
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4628
+#, no-wrap
+msgid "Policy label for `dvp`"
+msgstr "Метка политики для `dvp`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3946
+msgid ""
+"Determine whether the subject credential can change the process working "
+"directory to the passed vnode. Return 0 for success, or an `errno` value "
+"for failure. Suggested failure: EACCES for label mismatch, or EPERM for "
+"lack of privilege."
+msgstr ""
+"Определить, могут ли учётные данные субъекта изменить рабочий каталог "
+"процесса на переданный vnode. Возвращает 0 при успехе или значение `errno` "
+"при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM "
+"при отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3948
+#, no-wrap
+msgid "`mpo_check_vnode_chroot`"
+msgstr "`mpo_check_vnode_chroot`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3954
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_chroot(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel);\n"
+msgstr ""
+"int mpo_check_vnode_chroot(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3969
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4245
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4425
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4469
+#, no-wrap
+msgid "Directory vnode"
+msgstr "vnode каталога"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3973
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4249
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4429
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4473
+#, no-wrap
+msgid "Policy label associated with `dvp`"
+msgstr "Метка политики, связанная с `dvp`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3977
+msgid ""
+"Determine whether the subject should be allowed to man:chroot[2] into the "
+"specified directory (`dvp`)."
+msgstr ""
+"Определить, следует ли разрешить субъекту выполнять man:chroot[2] в "
+"указанный каталог (`dvp`)."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3979
+#, no-wrap
+msgid "`mpo_check_vnode_create`"
+msgstr "`mpo_check_vnode_create`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:3985
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_create(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel, struct componentname *cnp, struct vattr *vap);\n"
+msgstr ""
+"int mpo_check_vnode_create(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel, struct componentname *cnp, struct vattr *vap);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4008
+#, no-wrap
+msgid "Component name for `dvp`"
+msgstr "Название компонента для `dvp`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4011
+#, no-wrap
+msgid "`vap`"
+msgstr "`vap`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4012
+#, no-wrap
+msgid "vnode attributes for `vap`"
+msgstr "атрибуты vnode для `vap`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4019
+msgid ""
+"Determine whether the subject credential can create a vnode with the passed "
+"parent directory, passed name information, and passed attribute "
+"information. Return 0 for success, or an `errno` value for failure. "
+"Suggested failure: EACCES for label mismatch, or EPERM for lack of "
+"privilege. This call may be made in a number of situations, including as a "
+"result of calls to man:open[2] with O_CREAT, man:mkfifo[2], and others."
+msgstr ""
+"Определить, могут ли учетные данные субъекта создать vnode с указанной "
+"родительской директорией, информацией о имени и атрибутами. Возвращает 0 при "
+"успехе или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при "
+"несоответствии метки или EPERM при отсутствии привилегий. Этот вызов может "
+"выполняться в различных ситуациях, включая вызовы man:open[2] с O_CREAT, "
+"man:mkfifo[2] и другие."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4021
+#, no-wrap
+msgid "`mpo_check_vnode_delete`"
+msgstr "`mpo_check_vnode_delete`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4028
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_delete(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel, struct vnode *vp, void *label,\n"
+" struct componentname *cnp);\n"
+msgstr ""
+"int mpo_check_vnode_delete(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel, struct vnode *vp, void *label,\n"
+" struct componentname *cnp);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4051
+#, no-wrap
+msgid "Object; vnode to delete"
+msgstr "Объект; vnode для удаления"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4067
+msgid ""
+"Determine whether the subject credential can delete a vnode from the passed "
+"parent directory and passed name information. Return 0 for success, or an "
+"`errno` value for failure. Suggested failure: EACCES for label mismatch, or "
+"EPERM for lack of privilege. This call may be made in a number of "
+"situations, including as a result of calls to man:unlink[2] and "
+"man:rmdir[2]. Policies implementing this entry point should also implement "
+"`mpo_check_rename_to` to authorize deletion of objects as a result of being "
+"the target of a rename."
+msgstr ""
+"Определить, может ли субъект с данными учетными данными удалить vnode из "
+"переданного родительского каталога и переданной информации о имени. "
+"Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые "
+"ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий. "
+"Этот вызов может быть выполнен в различных ситуациях, включая вызовы "
+"man:unlink[2] и man:rmdir[2]. Политики, реализующие эту точку входа, также "
+"должны реализовывать `mpo_check_rename_to` для авторизации удаления объектов "
+"в результате их переименования."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4069
+#, no-wrap
+msgid "`mpo_check_vnode_deleteacl`"
+msgstr "`mpo_check_vnode_deleteacl`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4075
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, acl_type_t type);\n"
+msgstr ""
+"int mpo_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, acl_type_t type);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4097
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4168
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4731
+#, no-wrap
+msgid "`type`"
+msgstr "`type`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4098
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4169
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4732
+#, no-wrap
+msgid "ACL type"
+msgstr "Тип ACL"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4104
+msgid ""
+"Determine whether the subject credential can delete the ACL of passed type "
+"from the passed vnode. Return 0 for success, or an `errno` value for "
+"failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of "
+"privilege."
+msgstr ""
+"Определить, могут ли учетные данные субъекта удалить ACL указанного типа из "
+"переданного vnode. Возвращает 0 при успехе или значение `errno` при ошибке. "
+"Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при "
+"отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4106
+#, no-wrap
+msgid "`mpo_check_vnode_exec`"
+msgstr "`mpo_check_vnode_exec`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4112
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_exec(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label);\n"
+msgstr ""
+"int mpo_check_vnode_exec(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4127
+#, no-wrap
+msgid "Object; vnode to execute"
+msgstr "Объект; vnode для выполнения"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4138
+msgid ""
+"Determine whether the subject credential can execute the passed vnode. "
+"Determination of execute privilege is made separately from decisions about "
+"any transitioning event. Return 0 for success, or an `errno` value for "
+"failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of "
+"privilege."
+msgstr ""
+"Определить, могут ли учётные данные субъекта выполнить переданный vnode. "
+"Проверка права на выполнение осуществляется отдельно от решений о любом "
+"переходном событии. Возвращает 0 при успехе или значение `errno` при ошибке. "
+"Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при "
+"отсутствии прав."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4140
+#, no-wrap
+msgid "`mpo_check_vnode_getacl`"
+msgstr "`mpo_check_vnode_getacl`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4146
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_getacl(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, acl_type_t type);\n"
+msgstr ""
+"int mpo_check_vnode_getacl(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, acl_type_t type);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4175
+msgid ""
+"Determine whether the subject credential can retrieve the ACL of passed type "
+"from the passed vnode. Return 0 for success, or an `errno` value for "
+"failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of "
+"privilege."
+msgstr ""
+"Определить, может ли учётное данное субъекта получить ACL указанного типа из "
+"переданного vnode. Возвращает 0 при успехе или значение `errno` при ошибке. "
+"Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при "
+"отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4177
+#, no-wrap
+msgid "`mpo_check_vnode_getextattr`"
+msgstr "`mpo_check_vnode_getextattr`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4183
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_getextattr(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int attrnamespace, const char *name, struct uio *uio);\n"
+msgstr ""
+"int mpo_check_vnode_getextattr(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int attrnamespace, const char *name, struct uio *uio);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4205
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4772
+#, no-wrap
+msgid "`attrnamespace`"
+msgstr "`attrnamespace`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4206
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4773
+#, no-wrap
+msgid "Extended attribute namespace"
+msgstr "Пространство имен расширенных атрибутов"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4210
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4777
+#, no-wrap
+msgid "Extended attribute name"
+msgstr "Имя расширенного атрибута"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4213
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4780
+#, no-wrap
+msgid "`uio`"
+msgstr "`uio`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4214
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4781
+#, no-wrap
+msgid "I/O structure pointer; see man:uio[9]"
+msgstr "Указатель структуры ввода-вывода; см. man:uio[9]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4221
+msgid ""
+"Determine whether the subject credential can retrieve the extended attribute "
+"with the passed namespace and name from the passed vnode. Policies "
+"implementing labeling using extended attributes may be interested in special "
+"handling of operations on those extended attributes. Return 0 for success, "
+"or an `errno` value for failure. Suggested failure: EACCES for label "
+"mismatch, or EPERM for lack of privilege."
+msgstr ""
+"Определить, может ли субъект с указанными учетными данными получить "
+"расширенный атрибут с заданным пространством имен и именем из указанного "
+"vnode. Политики, реализующие маркировку с использованием расширенных "
+"атрибутов, могут требовать особой обработки операций с этими атрибутами. "
+"Возвращает 0 при успехе или значение `errno` при ошибке. Рекомендуемые "
+"ошибки: EACCES при несоответствии метки или EPERM при отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4223
+#, no-wrap
+msgid "`mpo_check_vnode_link`"
+msgstr "`mpo_check_vnode_link`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4230
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_link(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel, struct vnode *vp, struct label *label,\n"
+" struct componentname *cnp);\n"
+msgstr ""
+"int mpo_check_vnode_link(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel, struct vnode *vp, struct label *label,\n"
+" struct componentname *cnp);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4253
+#, no-wrap
+msgid "Link destination vnode"
+msgstr "vnode целевого линка"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4261
+#, no-wrap
+msgid "Component name for the link being created"
+msgstr "Имя компонента для создаваемой ссылки"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4265
+msgid ""
+"Determine whether the subject should be allowed to create a link to the "
+"vnode `vp` with the name specified by `cnp`."
+msgstr ""
+"Определить, следует ли разрешить субъекту создавать ссылку на vnode `vp` с "
+"именем, указанным в `cnp`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4267
+#, no-wrap
+msgid "`mpo_check_vnode_mmap`"
+msgstr "`mpo_check_vnode_mmap`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4273
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_mmap(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int prot);\n"
+msgstr ""
+"int mpo_check_vnode_mmap(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int prot);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4288
+#, no-wrap
+msgid "Vnode to map"
+msgstr "vnode для mmap"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4295
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4330
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4361
+#, no-wrap
+msgid "`prot`"
+msgstr "`prot`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4296
+#, no-wrap
+msgid "Mmap protections (see man:mmap[2])"
+msgstr "Защита mmap (см. man:mmap[2])"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4300
+msgid ""
+"Determine whether the subject should be allowed to map the vnode `vp` with "
+"the protections specified in `prot`."
+msgstr ""
+"Определить, следует ли разрешить субъекту отображать vnode `vp` с указанными "
+"в `prot` правами доступа."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4302
+#, no-wrap
+msgid "`mpo_check_vnode_mmap_downgrade`"
+msgstr "`mpo_check_vnode_mmap_downgrade`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4308
+#, no-wrap
+msgid ""
+"void mpo_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int *prot);\n"
+msgstr ""
+"void mpo_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int *prot);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4319
+#, no-wrap
+msgid "See crossref:mac[mac-mpo-check-vnode-mmap, `mpo_check_vnode_mmap`]."
+msgstr "См. crossref:mac[mac-mpo-check-vnode-mmap, `mpo_check_vnode_mmap`]."
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4331
+#, no-wrap
+msgid "Mmap protections to be downgraded"
+msgstr "Защита mmap для понижения уровня"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4335
+msgid "Downgrade the mmap protections based on the subject and object labels."
+msgstr "Понизить уровень защиты mmap на основе меток субъекта и объекта."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4337
+#, no-wrap
+msgid "`mpo_check_vnode_mprotect`"
+msgstr "`mpo_check_vnode_mprotect`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4343
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_mprotect(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int prot);\n"
+msgstr ""
+"int mpo_check_vnode_mprotect(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int prot);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4358
+#, no-wrap
+msgid "Mapped vnode"
+msgstr "Отображенный vnode"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4362
+#, no-wrap
+msgid "Memory protections"
+msgstr "Защита памяти"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4366
+msgid ""
+"Determine whether the subject should be allowed to set the specified memory "
+"protections on memory mapped from the vnode `vp`."
+msgstr ""
+"Определить, следует ли разрешить субъекту устанавливать указанные защиты "
+"памяти для памяти, отображенной из vnode `vp`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4368
+#, no-wrap
+msgid "`mpo_check_vnode_poll`"
+msgstr "`mpo_check_vnode_poll`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4374
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred,\n"
+" struct vnode *vp, struct label *label);\n"
+msgstr ""
+"int mpo_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred,\n"
+" struct vnode *vp, struct label *label);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4384
+#, no-wrap
+msgid "`active_cred`"
+msgstr "`active_cred`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4388
+#, no-wrap
+msgid "`file_cred`"
+msgstr "`file_cred`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4389
+#, no-wrap
+msgid "Credential associated with the struct file"
+msgstr "Учетные данные, связанные со структурой file"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4393
+#, no-wrap
+msgid "Polled vnode"
+msgstr "vnode, на котором вызывается poll"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4401
+msgid "Determine whether the subject should be allowed to poll the vnode `vp`."
+msgstr "Определить, следует ли разрешить субъекту вызывать poll на vnode `vp`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4403
+#, no-wrap
+msgid "`mpo_check_vnode_rename_from`"
+msgstr "`mpo_check_vnode_rename_from`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4410
+#, no-wrap
+msgid ""
+"int mpo_vnode_rename_from(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel, struct vnode *vp, struct label *label,\n"
+" struct componentname *cnp);\n"
+msgstr ""
+"int mpo_vnode_rename_from(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel, struct vnode *vp, struct label *label,\n"
+" struct componentname *cnp);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4433
+#, no-wrap
+msgid "Vnode to be renamed"
+msgstr "vnode для переименования"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4445
+msgid ""
+"Determine whether the subject should be allowed to rename the vnode `vp` to "
+"something else."
+msgstr ""
+"Определить, следует ли разрешить субъекту переименовать vnode `vp` во что-то "
+"другое."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4447
+#, no-wrap
+msgid "`mpo_check_vnode_rename_to`"
+msgstr "`mpo_check_vnode_rename_to`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4454
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel, struct vnode *vp, struct label *label, int samedir,\n"
+" struct componentname *cnp);\n"
+msgstr ""
+"int mpo_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel, struct vnode *vp, struct label *label, int samedir,\n"
+" struct componentname *cnp);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4477
+#, no-wrap
+msgid "Overwritten vnode"
+msgstr "vnode, который будет перезаписан"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4484
+#, no-wrap
+msgid "`samedir`"
+msgstr "`samedir`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4485
+#, no-wrap
+msgid "Boolean; `1` if the source and destination directories are the same"
+msgstr "Логическое значение; `1`, если исходный и целевой каталоги совпадают"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4489
+#, no-wrap
+msgid "Destination component name"
+msgstr "Имя компонента назначения"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4494
+msgid ""
+"Determine whether the subject should be allowed to rename to the vnode `vp`, "
+"into the directory `dvp`, or to the name represented by `cnp`. If there is "
+"no existing file to overwrite, `vp` and `label` will be NULL."
+msgstr ""
+"Определить, следует ли разрешить субъекту переименование vnode `vp` в "
+"директорию `dvp` или в имя, представленное `cnp`. Если не существует файла "
+"для перезаписи, `vp` и `label` будут NULL."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4496
+#, no-wrap
+msgid "`mpo_check_socket_listen`"
+msgstr "`mpo_check_socket_listen`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4502
+#, no-wrap
+msgid ""
+"int mpo_check_socket_listen(struct ucred *cred, struct socket *socket,\n"
+" struct label *socketlabel);\n"
+msgstr ""
+"int mpo_check_socket_listen(struct ucred *cred, struct socket *socket,\n"
+" struct label *socketlabel);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4527
+msgid ""
+"Determine whether the subject credential can listen on the passed socket. "
+"Return 0 for success, or an `errno` value for failure. Suggested failure: "
+"EACCES for label mismatch, or EPERM for lack of privilege."
+msgstr ""
+"Определить, могут ли учётные данные субъекта прослушивать переданный сокет "
+"(вызывать listen). Возвращает 0 при успехе или значение `errno` при ошибке. "
+"Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при "
+"отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4529
+#, no-wrap
+msgid "`mpo_check_vnode_lookup`"
+msgstr "`mpo_check_vnode_lookup`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4535
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_lookup(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel, struct componentname *cnp);\n"
+msgstr ""
+"int mpo_check_vnode_lookup(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel, struct componentname *cnp);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4558
+#, no-wrap
+msgid "Component name being looked up"
+msgstr "Имя компонента, который ищется"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4564
+msgid ""
+"Determine whether the subject credential can perform a lookup in the passed "
+"directory vnode for the passed name. Return 0 for success, or an `errno` "
+"value for failure. Suggested failure: EACCES for label mismatch, or EPERM "
+"for lack of privilege."
+msgstr ""
+"Определить, могут ли учётные данные субъекта выполнить поиск указанного "
+"имени в каталог с переданном vnode. Возвращает 0 при успехе или значение "
+"`errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки "
+"или EPERM при отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4566
+#, no-wrap
+msgid "`mpo_check_vnode_open`"
+msgstr "`mpo_check_vnode_open`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4572
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_open(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int acc_mode);\n"
+msgstr ""
+"int mpo_check_vnode_open(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int acc_mode);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4594
+#, no-wrap
+msgid "`acc_mode`"
+msgstr "`acc_mode`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4595
+#, no-wrap
+msgid "man:open[2] access mode"
+msgstr "режим доступа от man:open[2]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4601
+msgid ""
+"Determine whether the subject credential can perform an open operation on "
+"the passed vnode with the passed access mode. Return 0 for success, or an "
+"errno value for failure. Suggested failure: EACCES for label mismatch, or "
+"EPERM for lack of privilege."
+msgstr ""
+"Определить, могут ли учетные данные субъекта выполнить операцию открытия "
+"переданного vnode с указанным режимом доступа. Возвращает 0 в случае успеха "
+"или значение errno при ошибке. Рекомендуемые ошибки: EACCES при "
+"несоответствии метки или EPERM при отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4603
+#, no-wrap
+msgid "`mpo_check_vnode_readdir`"
+msgstr "`mpo_check_vnode_readdir`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4609
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_readdir(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel);\n"
+msgstr ""
+"int mpo_check_vnode_readdir(struct ucred *cred, struct vnode *dvp,\n"
+" struct label *dlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4624
+#, no-wrap
+msgid "Object; directory vnode"
+msgstr "Объект; vnode каталога"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4634
+msgid ""
+"Determine whether the subject credential can perform a `readdir` operation "
+"on the passed directory vnode. Return 0 for success, or an `errno` value "
+"for failure. Suggested failure: EACCES for label mismatch, or EPERM for "
+"lack of privilege."
+msgstr ""
+"Определить, могут ли учетные данные субъекта выполнить операцию `readdir` "
+"для переданного vnode каталога. Возвращает 0 при успехе или значение `errno` "
+"при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM "
+"при отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4636
+#, no-wrap
+msgid "`mpo_check_vnode_readlink`"
+msgstr "`mpo_check_vnode_readlink`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4642
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_readlink(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label);\n"
+msgstr ""
+"int mpo_check_vnode_readlink(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4668
+msgid ""
+"Determine whether the subject credential can perform a `readlink` operation "
+"on the passed symlink vnode. Return 0 for success, or an `errno` value for "
+"failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of "
+"privilege. This call may be made in a number of situations, including an "
+"explicit `readlink` call by the user process, or as a result of an implicit "
+"`readlink` during a name lookup by the process."
+msgstr ""
+"Определить, могут ли учетные данные субъекта выполнить операцию `readlink` "
+"для переданного символьного vnode. Возвращает 0 в случае успеха или значение "
+"`errno` в случае ошибки. Рекомендуемые ошибки: EACCES при несоответствии "
+"метки или EPERM при отсутствии привилегий. Этот вызов может быть выполнен в "
+"различных ситуациях, включая явный вызов `readlink` пользовательским "
+"процессом или неявный `readlink` во время поиска имени процессом."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4670
+#, no-wrap
+msgid "`mpo_check_vnode_revoke`"
+msgstr "`mpo_check_vnode_revoke`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4676
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_revoke(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label);\n"
+msgstr ""
+"int mpo_check_vnode_revoke(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4701
+msgid ""
+"Determine whether the subject credential can revoke access to the passed "
+"vnode. Return 0 for success, or an `errno` value for failure. Suggested "
+"failure: EACCES for label mismatch, or EPERM for lack of privilege."
+msgstr ""
+"Определить, могут ли учётные данные субъекта отозвать доступ к переданному "
+"vnode. Возвращает 0 при успехе или значение `errno` при ошибке. "
+"Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при "
+"отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4703
+#, no-wrap
+msgid "`mpo_check_vnode_setacl`"
+msgstr "`mpo_check_vnode_setacl`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4709
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_setacl(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, acl_type_t type, struct acl *acl);\n"
+msgstr ""
+"int mpo_check_vnode_setacl(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, acl_type_t type, struct acl *acl);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4735
+#, no-wrap
+msgid "`acl`"
+msgstr "`acl`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4736
+#, no-wrap
+msgid "ACL"
+msgstr "ACL"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4742
+msgid ""
+"Determine whether the subject credential can set the passed ACL of passed "
+"type on the passed vnode. Return 0 for success, or an `errno` value for "
+"failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of "
+"privilege."
+msgstr ""
+"Определить, могут ли учётные данные субъекта установить переданный ACL "
+"указанного типа для переданного vnode. Возвращает 0 при успехе или значение "
+"`errno` при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки "
+"или EPERM при отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4744
+#, no-wrap
+msgid "`mpo_check_vnode_setextattr`"
+msgstr "`mpo_check_vnode_setextattr`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4750
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_setextattr(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int attrnamespace, const char *name, struct uio *uio);\n"
+msgstr ""
+"int mpo_check_vnode_setextattr(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, int attrnamespace, const char *name, struct uio *uio);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4790
+msgid ""
+"Determine whether the subject credential can set the extended attribute of "
+"passed name and passed namespace on the passed vnode. Policies implementing "
+"security labels backed into extended attributes may want to provide "
+"additional protections for those attributes. Additionally, policies should "
+"avoid making decisions based on the data referenced from `uio`, as there is "
+"a potential race condition between this check and the actual operation. The "
+"`uio` may also be `NULL` if a delete operation is being performed. Return 0 "
+"for success, or an `errno` value for failure. Suggested failure: EACCES for "
+"label mismatch, or EPERM for lack of privilege."
+msgstr ""
+"Определить, могут ли учетные данные субъекта установить расширенный атрибут "
+"с переданным именем и пространством имен на переданном vnode. Политики, "
+"реализующие метки безопасности, основанные на расширенных атрибутах, могут "
+"предусматривать дополнительные защиты для этих атрибутов. Кроме того, "
+"политикам следует избегать принятия решений на основе данных, на которые "
+"ссылается `uio`, так как существует потенциальное состояние гонки между этой "
+"проверкой и фактической операцией. `uio` также может быть `NULL`, если "
+"выполняется операция удаления. Возвращает 0 при успехе или значение `errno` "
+"при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM "
+"при отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4792
+#, no-wrap
+msgid "`mpo_check_vnode_setflags`"
+msgstr "`mpo_check_vnode_setflags`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4798
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_setflags(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, u_long flags);\n"
+msgstr ""
+"int mpo_check_vnode_setflags(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, u_long flags);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4821
+#, no-wrap
+msgid "File flags; see man:chflags[2]"
+msgstr "Флаги файла; см. man:chflags[2]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4827
+msgid ""
+"Determine whether the subject credential can set the passed flags on the "
+"passed vnode. Return 0 for success, or an `errno` value for failure. "
+"Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege."
+msgstr ""
+"Определить, могут ли учётные данные субъекта установить переданные флаги на "
+"переданном vnode. Возвращает 0 при успехе или значение `errno` при ошибке. "
+"Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при "
+"отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4829
+#, no-wrap
+msgid "`mpo_check_vnode_setmode`"
+msgstr "`mpo_check_vnode_setmode`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4835
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_setmode(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, mode_t mode);\n"
+msgstr ""
+"int mpo_check_vnode_setmode(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, mode_t mode);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4857
+#, no-wrap
+msgid "`mode`"
+msgstr "`mode`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4858
+#, no-wrap
+msgid "File mode; see man:chmod[2]"
+msgstr "Режим файла; см. man:chmod[2]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4864
+msgid ""
+"Determine whether the subject credential can set the passed mode on the "
+"passed vnode. Return 0 for success, or an `errno` value for failure. "
+"Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege."
+msgstr ""
+"Определить, могут ли учётные данные субъекта установить переданный режим для "
+"переданного vnode. Возвращает 0 при успехе или значение `errno` при ошибке. "
+"Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при "
+"недостатке привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4866
+#, no-wrap
+msgid "`mpo_check_vnode_setowner`"
+msgstr "`mpo_check_vnode_setowner`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4872
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_setowner(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, uid_t uid, gid_t gid);\n"
+msgstr ""
+"int mpo_check_vnode_setowner(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, uid_t uid, gid_t gid);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4894
+#, no-wrap
+msgid "`uid`"
+msgstr "`uid`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4895
+#, no-wrap
+msgid "User ID"
+msgstr "User ID"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4898
+#, no-wrap
+msgid "`gid`"
+msgstr "`gid`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4899
+#, no-wrap
+msgid "Group ID"
+msgstr "Идентификатор группы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4906
+msgid ""
+"Determine whether the subject credential can set the passed uid and passed "
+"gid as file uid and file gid on the passed vnode. The IDs may be set to "
+"(`-1`) to request no update. Return 0 for success, or an `errno` value for "
+"failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of "
+"privilege."
+msgstr ""
+"Определить, могут ли учетные данные субъекта установить переданный uid и "
+"переданный gid в качестве uid файла и gid файла для переданного vnode. "
+"Идентификаторы могут быть установлены в (`-1`) для запроса отсутствия "
+"обновления. Возвращает 0 при успехе или значение `errno` при ошибке. "
+"Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при "
+"отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4908
+#, no-wrap
+msgid "`mpo_check_vnode_setutimes`"
+msgstr "`mpo_check_vnode_setutimes`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4914
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_setutimes(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, struct timespec atime, struct timespec mtime);\n"
+msgstr ""
+"int mpo_check_vnode_setutimes(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label, struct timespec atime, struct timespec mtime);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4929
+#, no-wrap
+msgid "Object; vp"
+msgstr "Объект; vp"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4936
+#, no-wrap
+msgid "`atime`"
+msgstr "`atime`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4937
+#, no-wrap
+msgid "Access time; see man:utimes[2]"
+msgstr "Время доступа; см. man:utimes[2]"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4940
+#, no-wrap
+msgid "`mtime`"
+msgstr "`mtime`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4941
+#, no-wrap
+msgid "Modification time; see man:utimes[2]"
+msgstr "Время изменения; см. man:utimes[2]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4947
+msgid ""
+"Determine whether the subject credential can set the passed access "
+"timestamps on the passed vnode. Return 0 for success, or an `errno` value "
+"for failure. Suggested failure: EACCES for label mismatch, or EPERM for "
+"lack of privilege."
+msgstr ""
+"Определить, могут ли учётные данные субъекта установить переданные времена "
+"доступа на переданном vnode. Возвращает 0 при успехе или значение `errno` "
+"при ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM "
+"при отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4949
+#, no-wrap
+msgid "`mpo_check_proc_sched`"
+msgstr "`mpo_check_proc_sched`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4954
+#, no-wrap
+msgid "int mpo_check_proc_sched(struct ucred *ucred, struct proc *proc);\n"
+msgstr "int mpo_check_proc_sched(struct ucred *ucred, struct proc *proc);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4975
+msgid ""
+"Determine whether the subject credential can change the scheduling "
+"parameters of the passed process. Return 0 for success, or an `errno` value "
+"for failure. Suggested failure: EACCES for label mismatch, EPERM for lack "
+"of privilege, or ESRCH to limit visibility."
+msgstr ""
+"Определить, могут ли учетные данные субъекта изменить параметры планирования "
+"переданного процесса. Возвращает 0 при успехе или значение `errno` при "
+"ошибке. Рекомендуемые ошибки: EACCES при несоответствии метки, EPERM при "
+"отсутствии привилегий или ESRCH для ограничения видимости."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4977
+msgid "See man:setpriority[2] for more information."
+msgstr "См. man:setpriority[2] для получения дополнительной информации."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4979
+#, no-wrap
+msgid "`mpo_check_proc_signal`"
+msgstr "`mpo_check_proc_signal`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:4984
+#, no-wrap
+msgid "int mpo_check_proc_signal(struct ucred *cred, struct proc *proc, int signal);\n"
+msgstr "int mpo_check_proc_signal(struct ucred *cred, struct proc *proc, int signal);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5002
+#, no-wrap
+msgid "`signal`"
+msgstr "`signal`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5003
+#, no-wrap
+msgid "Signal; see man:kill[2]"
+msgstr "Сигнал; см. man:kill[2]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5009
+msgid ""
+"Determine whether the subject credential can deliver the passed signal to "
+"the passed process. Return 0 for success, or an `errno` value for failure. "
+"Suggested failure: EACCES for label mismatch, EPERM for lack of privilege, "
+"or ESRCH to limit visibility."
+msgstr ""
+"Определить, могут ли учётные данные субъекта доставить указанный сигнал "
+"указанному процессу. Возвращает 0 при успехе или значение `errno` при "
+"ошибке. Рекомендуемые коды ошибок: EACCES при несоответствии метки, EPERM "
+"при недостатке прав или ESRCH для ограничения видимости."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5011
+#, no-wrap
+msgid "`mpo_check_vnode_stat`"
+msgstr "`mpo_check_vnode_stat`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5017
+#, no-wrap
+msgid ""
+"int mpo_check_vnode_stat(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label);\n"
+msgstr ""
+"int mpo_check_vnode_stat(struct ucred *cred, struct vnode *vp,\n"
+" struct label *label);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5042
+msgid ""
+"Determine whether the subject credential can `stat` the passed vnode. "
+"Return 0 for success, or an `errno` value for failure. Suggested failure: "
+"EACCES for label mismatch, or EPERM for lack of privilege."
+msgstr ""
+"Определить, могут ли учетные данные субъекта выполнять `stat` для "
+"переданного vnode. Возвращает 0 при успехе или значение `errno` при ошибке. "
+"Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при "
+"отсутствии привилегий."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5044
+msgid "See man:stat[2] for more information."
+msgstr "См. man:stat[2] для получения дополнительной информации."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5046
+#, no-wrap
+msgid "`mpo_check_ifnet_transmit`"
+msgstr "`mpo_check_ifnet_transmit`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5052
+#, no-wrap
+msgid ""
+"int mpo_check_ifnet_transmit(struct ucred *cred, struct ifnet *ifnet,\n"
+" struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel);\n"
+msgstr ""
+"int mpo_check_ifnet_transmit(struct ucred *cred, struct ifnet *ifnet,\n"
+" struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5075
+#, no-wrap
+msgid "Object; mbuf to be sent"
+msgstr "Объект; mbuf для отправки"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5085
+msgid ""
+"Determine whether the network interface can transmit the passed mbuf. "
+"Return 0 for success, or an `errno` value for failure. Suggested failure: "
+"EACCES for label mismatch, or EPERM for lack of privilege."
+msgstr ""
+"Определить, может ли сетевой интерфейс передать mbuf, переданный в качестве "
+"параметра. Возвращает 0 при успехе или значение `errno` при ошибке. "
+"Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при "
+"отсутствии привилегий."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5087
+#, no-wrap
+msgid "`mpo_check_socket_deliver`"
+msgstr "`mpo_check_socket_deliver`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5093
+#, no-wrap
+msgid ""
+"int mpo_check_socket_deliver(struct ucred *cred, struct ifnet *ifnet,\n"
+" struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel);\n"
+msgstr ""
+"int mpo_check_socket_deliver(struct ucred *cred, struct ifnet *ifnet,\n"
+" struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5116
+#, no-wrap
+msgid "Object; mbuf to be delivered"
+msgstr "Объект; mbuf для доставки"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5126
+msgid ""
+"Determine whether the socket may receive the datagram stored in the passed "
+"mbuf header. Return 0 for success, or an `errno` value for failure. "
+"Suggested failures: EACCES for label mismatch, or EPERM for lack of "
+"privilege."
+msgstr ""
+"Определить, может ли сокет принять датаграмму, хранящуюся в переданном "
+"заголовке mbuf. Возвращает 0 при успехе или значение `errno` при ошибке. "
+"Рекомендуемые ошибки: EACCES при несоответствии метки или EPERM при "
+"отсутствии привилегий."
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5134
+#, no-wrap
+msgid ""
+"int mpo_check_socket_visible(struct ucred *cred, struct socket *so,\n"
+" struct label *socketlabel);\n"
+msgstr ""
+"int mpo_check_socket_visible(struct ucred *cred, struct socket *so,\n"
+" struct label *socketlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5153
+#, no-wrap
+msgid "Policy label for `so`"
+msgstr "Метка политики для `so`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5159
+msgid ""
+"Determine whether the subject credential cred can \"see\" the passed socket "
+"(`socket`) using system monitoring functions, such as those employed by "
+"man:netstat[8] and man:sockstat[1]. Return 0 for success, or an `errno` "
+"value for failure. Suggested failure: EACCES for label mismatches, EPERM "
+"for lack of privilege, or ESRCH to hide visibility."
+msgstr ""
+"Определить, могут ли учетные данные субъекта cred \"видеть\" переданный "
+"сокет (`socket`), используя функции системного мониторинга, такие как те, "
+"что применяются в man:netstat[8] и man:sockstat[1]. Возвращает 0 при успехе "
+"или значение `errno` при ошибке. Рекомендуемые ошибки: EACCES при "
+"несоответствии меток, EPERM при отсутствии привилегий или ESRCH для скрытия "
+"видимости."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5161
+#, no-wrap
+msgid "`mpo_check_system_acct`"
+msgstr "`mpo_check_system_acct`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5167
+#, no-wrap
+msgid ""
+"int mpo_check_system_acct(struct ucred *ucred, struct vnode *vp,\n"
+" struct label *vlabel);\n"
+msgstr ""
+"int mpo_check_system_acct(struct ucred *ucred, struct vnode *vp,\n"
+" struct label *vlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5177
+#, no-wrap
+msgid "`ucred`"
+msgstr "`ucred`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5182
+#, no-wrap
+msgid "Accounting file; man:acct[5]"
+msgstr "Файл учёта; man:acct[5]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5190
+msgid ""
+"Determine whether the subject should be allowed to enable accounting, based "
+"on its label and the label of the accounting log file."
+msgstr ""
+"Определить, следует ли разрешить субъекту включение учёта, основываясь на "
+"его метке и метке файла журнала учёта."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5192
+#, no-wrap
+msgid "`mpo_check_system_nfsd`"
+msgstr "`mpo_check_system_nfsd`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5197
+#, no-wrap
+msgid "int mpo_check_system_nfsd(struct ucred *cred);\n"
+msgstr "int mpo_check_system_nfsd(struct ucred *cred);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5212
+msgid "Determine whether the subject should be allowed to call man:nfssvc[2]."
+msgstr "Определить, следует ли разрешить субъекту вызывать man:nfssvc[2]."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5214
+#, no-wrap
+msgid "`mpo_check_system_reboot`"
+msgstr "`mpo_check_system_reboot`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5219
+#, no-wrap
+msgid "int mpo_check_system_reboot(struct ucred *cred, int howto);\n"
+msgstr "int mpo_check_system_reboot(struct ucred *cred, int howto);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5233
+#, no-wrap
+msgid "`howto`"
+msgstr "`howto`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5234
+#, no-wrap
+msgid "`howto` parameter from man:reboot[2]"
+msgstr "Параметр `howto` из man:reboot[2]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5238
+msgid ""
+"Determine whether the subject should be allowed to reboot the system in the "
+"specified manner."
+msgstr ""
+"Определить, следует ли разрешить субъекту перезагружать систему указанным "
+"способом."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5240
+#, no-wrap
+msgid "`mpo_check_system_settime`"
+msgstr "`mpo_check_system_settime`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5245
+#, no-wrap
+msgid "int mpo_check_system_settime(struct ucred *cred);\n"
+msgstr "int mpo_check_system_settime(struct ucred *cred);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5260
+msgid "Determine whether the user should be allowed to set the system clock."
+msgstr "Определить, разрешено ли пользователю устанавливать системные часы."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5262
+#, no-wrap
+msgid "`mpo_check_system_swapon`"
+msgstr "`mpo_check_system_swapon`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5268
+#, no-wrap
+msgid ""
+"int mpo_check_system_swapon(struct ucred *cred, struct vnode *vp,\n"
+" struct label *vlabel);\n"
+msgstr ""
+"int mpo_check_system_swapon(struct ucred *cred, struct vnode *vp,\n"
+" struct label *vlabel);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5283
+#, no-wrap
+msgid "Swap device"
+msgstr "Устройство подкачки"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5291
+msgid ""
+"Determine whether the subject should be allowed to add `vp` as a swap device."
+msgstr ""
+"Определить, следует ли разрешить субъекту добавлять `vp` как устройство "
+"подкачки."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5293
+#, no-wrap
+msgid "`mpo_check_system_sysctl`"
+msgstr "`mpo_check_system_sysctl`"
+
+#. type: delimited block - 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5299
+#, no-wrap
+msgid ""
+"int mpo_check_system_sysctl(struct ucred *cred, int *name, u_int *namelen,\n"
+" void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen);\n"
+msgstr ""
+"int mpo_check_system_sysctl(struct ucred *cred, int *name, u_int *namelen,\n"
+" void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen);\n"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5314
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5334
+#, no-wrap
+msgid "See man:sysctl[3]"
+msgstr "См. man:sysctl[3]"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5325
+#, no-wrap
+msgid "`oldlenp`"
+msgstr "`oldlenp`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5329
+#, no-wrap
+msgid "`inkernel`"
+msgstr "`inkernel`"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5330
+#, no-wrap
+msgid "Boolean; `1` if called from kernel"
+msgstr "Логический; `1`, если вызвано из ядра"
+
+#. type: Table
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5337
+#, no-wrap
+msgid "`newlen`"
+msgstr "`newlen`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5342
+msgid ""
+"Determine whether the subject should be allowed to make the specified "
+"man:sysctl[3] transaction."
+msgstr ""
+"Определить, следует ли разрешить субъекту выполнять указанную транзакцию "
+"man:sysctl[3]."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5344
+#, no-wrap
+msgid "Label Management Calls"
+msgstr "Вызовы при управления метками"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5350
+msgid ""
+"Relabel events occur when a user process has requested that the label on an "
+"object be modified. A two-phase update occurs: first, an access control "
+"check will be performed to determine if the update is both valid and "
+"permitted, and then the update itself is performed via a separate entry "
+"point. Relabel entry points typically accept the object, object label "
+"reference, and an update label submitted by the process. Memory allocation "
+"during relabel is discouraged, as relabel calls are not permitted to fail "
+"(failure should be reported earlier in the relabel check)."
+msgstr ""
+"События изменения метки происходят, когда пользовательский процесс "
+"запрашивает изменение метки на объекте. Происходит двухэтапное обновление: "
+"сначала выполняется проверка контроля доступа, чтобы определить, является ли "
+"обновление допустимым и разрешённым, а затем само обновление выполняется "
+"через отдельную точку входа. Точки входа для изменения метки обычно "
+"принимают объект, ссылку на метку объекта и новую метку, предоставленную "
+"процессом. Выделение памяти во время изменения метки не рекомендуется, так "
+"как вызовы изменения метки не могут завершиться неудачей (ошибка должна быть "
+"обнаружена ранее на этапе проверки изменения метки)."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5352
+#, no-wrap
+msgid "Userland Architecture"
+msgstr "Пользовательская архитектура"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5356
+msgid ""
+"The TrustedBSD MAC Framework includes a number of policy-agnostic elements, "
+"including MAC library interfaces for abstractly managing labels, "
+"modifications to the system credential management and login libraries to "
+"support the assignment of MAC labels to users, and a set of tools to monitor "
+"and modify labels on processes, files, and network interfaces. More details "
+"on the user architecture will be added to this section in the near future."
+msgstr ""
+"В фреймворк TrustedBSD MAC входит ряд элементов, не зависящих от политик, "
+"включая интерфейсы MAC-библиотеки для абстрактного управления метками, "
+"изменения в управлении системными учетными данными и библиотеках входа в "
+"систему для поддержки назначения MAC-меток пользователям, а также набор "
+"инструментов для мониторинга и изменения меток процессов, файлов и сетевых "
+"интерфейсов. Более подробная информация о пользовательской архитектуре будет "
+"добавлена в этот раздел в ближайшее время."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5358
+#, no-wrap
+msgid "APIs for Policy-Agnostic Label Management"
+msgstr "API для управления метками, не зависящими от политики"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5365
+msgid ""
+"The TrustedBSD MAC Framework provides a number of library and system calls "
+"permitting applications to manage MAC labels on objects using a policy-"
+"agnostic interface. This permits applications to manipulate labels for a "
+"variety of policies without being written to support specific policies. "
+"These interfaces are used by general-purpose tools such as man:ifconfig[8], "
+"man:ls[1] and man:ps[1] to view labels on network interfaces, files, and "
+"processes. The APIs also support MAC management tools including "
+"man:getfmac[8], man:getpmac[8], man:setfmac[8], man:setfsmac[8], and "
+"man:setpmac[8]. The MAC APIs are documented in man:mac[3]."
+msgstr ""
+"Фреймворк TrustedBSD MAC предоставляет ряд библиотечных и системных вызовов, "
+"позволяющих приложениям управлять метками MAC на объектах с использованием "
+"политико-независимого интерфейса. Это позволяет приложениям манипулировать "
+"метками для различных политик без необходимости поддержки конкретных "
+"политик. Эти интерфейсы используются универсальными инструментами, такими "
+"как man:ifconfig[8], man:ls[1] и man:ps[1], для просмотра меток на сетевых "
+"интерфейсах, файлах и процессах. API также поддерживают инструменты "
+"управления MAC, включая man:getfmac[8], man:getpmac[8], man:setfmac[8], "
+"man:setfsmac[8] и man:setpmac[8]. API MAC документированы в man:mac[3]."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5373
+msgid ""
+"Applications handle MAC labels in two forms: an internalized form used to "
+"return and set labels on processes and objects (`mac_t`), and externalized "
+"form based on C strings appropriate for storage in configuration files, "
+"display to the user, or input from the user. Each MAC label contains a "
+"number of elements, each consisting of a name and value pair. Policy "
+"modules in the kernel bind to specific names and interpret the values in "
+"policy-specific ways. In the externalized string form, labels are "
+"represented by a comma-delimited list of name and value pairs separated by "
+"the `/` character. Labels may be directly converted to and from text using "
+"provided APIs; when retrieving labels from the kernel, internalized label "
+"storage must first be prepared for the desired label element set. "
+"Typically, this is done in one of two ways: using man:mac_prepare[3] and an "
+"arbitrary list of desired label elements, or one of the variants of the call "
+"that loads a default element set from the man:mac.conf[5] configuration "
+"file. Per-object defaults permit application writers to usefully display "
+"labels associated with objects without being aware of the policies present "
+"in the system."
+msgstr ""
+"Приложения обрабатывают метки MAC в двух формах: внутренней форме, "
+"используемой для возврата и установки меток для процессов и объектов "
+"(`mac_t`), и внешней форме, основанной на строках C, подходящих для хранения "
+"в конфигурационных файлах, отображения пользователю или ввода от "
+"пользователя. Каждая метка MAC содержит ряд элементов, каждый из которых "
+"состоит из пары имя-значение. Модули политик в ядре привязываются к "
+"определённым именам и интерпретируют значения специфичным для политики "
+"образом. Во внешней строковой форме метки представляются списком пар имя-"
+"значение, разделённых запятыми и символом `/`. Метки могут быть напрямую "
+"преобразованы в текст и обратно с использованием предоставленных API; при "
+"извлечении меток из ядра внутреннее хранилище меток должно быть сначала "
+"подготовлено для желаемого набора элементов метки. Обычно это делается одним "
+"из двух способов: с использованием man:mac_prepare[3] и произвольного списка "
+"желаемых элементов метки, или одной из вариаций вызова, который загружает "
+"набор элементов по умолчанию из конфигурационного файла man:mac.conf[5]. "
+"Значения по умолчанию для каждого объекта позволяют разработчикам приложений "
+"удобно отображать метки, связанные с объектами, без необходимости знать о "
+"присутствующих в системе политиках."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5378
+msgid ""
+"Currently, direct manipulation of label elements other than by conversion to "
+"a text string, string editing, and conversion back to an internalized label "
+"is not supported by the MAC library. Such interfaces may be added in the "
+"future if they prove necessary for application writers."
+msgstr ""
+"В настоящее время прямое манипулирование элементами меток, кроме как путем "
+"преобразования в текстовую строку, редактирования строки и обратного "
+"преобразования во внутреннюю метку, не поддерживается библиотекой MAC. Такие "
+"интерфейсы могут быть добавлены в будущем, если окажется, что они необходимы "
+"разработчикам приложений."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5381
+#, no-wrap
+msgid "Binding of Labels to Users"
+msgstr "Привязка меток к пользователям"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5385
+msgid ""
+"The standard user context management interface, man:setusercontext[3], has "
+"been modified to retrieve MAC labels associated with a user's class from "
+"man:login.conf[5]. These labels are then set along with other user context "
+"when either `LOGIN_SETALL` is specified, or when `LOGIN_SETMAC` is "
+"explicitly specified."
+msgstr ""
+"Стандартный интерфейс управления контекстом пользователя, "
+"man:setusercontext[3], был изменён для получения меток MAC, связанных с "
+"классом пользователя, из man:login.conf[5]. Эти метки устанавливаются вместе "
+"с остальным контекстом пользователя, когда указан `LOGIN_SETALL` или явно "
+"указан `LOGIN_SETMAC`."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5390
+msgid ""
+"It is expected that, in a future version of FreeBSD, the MAC label database "
+"will be separated from the [.filename]#login.conf# user class abstraction, "
+"and be maintained in a separate database. However, the "
+"man:setusercontext[3] API should remain the same following such a change."
+msgstr ""
+"Ожидается, что в будущей версии FreeBSD база данных меток MAC будет отделена "
+"от абстракции классов пользователей [.filename]#login.conf# и будет "
+"поддерживаться в отдельной базе данных. Однако API man:setusercontext[3] "
+"должно остаться неизменным после такого изменения."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5393
+#, no-wrap
+msgid "Conclusion"
+msgstr "Заключение"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/mac/_index.adoc:5398
+msgid ""
+"The TrustedBSD MAC framework permits kernel modules to augment the system "
+"security policy in a highly integrated manner. They may do this based on "
+"existing object properties, or based on label data that is maintained with "
+"the assistance of the MAC framework. The framework is sufficiently flexible "
+"to implement a variety of policy types, including information flow security "
+"policies such as MLS and Biba, as well as policies based on existing BSD "
+"credentials or file protections. Policy authors may wish to consult this "
+"documentation as well as existing security modules when implementing a new "
+"security service."
+msgstr ""
+"Фреймворк TrustedBSD MAC позволяет модулям ядра расширять политику "
+"безопасности системы высокоинтегрированным способом. Они могут делать это на "
+"основе существующих свойств объектов или данных меток, которые "
+"поддерживаются с помощью фреймворка MAC. Фреймворк достаточно гибкий для "
+"реализации различных типов политик, включая политики безопасности "
+"информационных потоков, такие как MLS и Biba, а также политики, основанные "
+"на существующих учетных данных BSD или защите файлов. Авторам политик может "
+"быть полезно ознакомиться с этой документацией, а также с существующими "
+"модулями безопасности при реализации новой службы безопасности."
+
+#~ msgid ""
+#~ "The MAC policy entry point vector, `mac__policy__ops` in this example, "
+#~ "associates functions defined in the module with specific entry points. A "
+#~ "complete listing of available entry points and their prototypes may be "
+#~ "found in the MAC entry point reference section. Of specific interest "
+#~ "during module registration are the .mpo_destroy and .mpo_init entry "
+#~ "points."
+#~ msgstr ""
+#~ "Вектор точек входа политики MAC, `mac__policy__ops` в данном примере, "
+#~ "связывает функции, определённые в модуле, с конкретными точками входа. "
+#~ "Полный список доступных точек входа и их прототипов можно найти в разделе "
+#~ "справочника по точкам входа MAC. Особый интерес при регистрации модуля "
+#~ "представляют точки входа .mpo_destroy и .mpo_init."
+
+#, no-wrap
+#~ msgid "mpo_init will be invoked once a policy is successfully registered with the module framework but prior to any other entry points becoming active."
+#~ msgstr "mpo_init будет вызван после успешной регистрации политики в модульной системе, но до активации других точек входа."
+
+#~ msgid ""
+#~ "This permits the policy to perform any policy-specific allocation and "
+#~ "initialization, such as initialization of any data or locks."
+#~ msgstr ""
+#~ "Это позволяет политике выполнять любые выделения и инициализации, "
+#~ "специфичные для данной политики, такие как инициализация данных или "
+#~ "блокировок."
+
+#, no-wrap
+#~ msgid "mpo_destroy will be invoked when a policy module is unloaded to permit releasing of any allocated memory and destruction of locks."
+#~ msgstr "mpo_destroy будет вызван при выгрузке модуля политики для освобождения выделенной памяти и уничтожения блокировок."
+
+#~ msgid ""
+#~ "Currently, these two entry points are invoked with the MAC policy list "
+#~ "mutex held to prevent any other entry points from being invoked: this "
+#~ "will be changed, but in the mean time, policies should be careful about "
+#~ "what kernel primitives they invoke so as to avoid lock ordering or "
+#~ "sleeping problems."
+#~ msgstr ""
+#~ "В настоящее время эти две точки входа вызываются с удержанием мьютекса "
+#~ "списка политик MAC, чтобы предотвратить вызов других точек входа: это "
+#~ "будет изменено, но до тех пор политики должны быть осторожны с "
+#~ "используемыми примитивами ядра, чтобы избежать проблем с порядком "
+#~ "блокировок или сном."
diff --git a/documentation/content/ru/books/arch-handbook/newbus/_index.adoc b/documentation/content/ru/books/arch-handbook/newbus/_index.adoc
new file mode 100644
index 0000000000..7f892c1b1e
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/newbus/_index.adoc
@@ -0,0 +1,213 @@
+---
+authors:
+ -
+ author: 'Jeroen Ruigrok van der Werven (asmodai)'
+ email: asmodai@FreeBSD.org
+ -
+ author: 'Hiten Pandya'
+ email: hiten@uk.FreeBSD.org
+description: Newbus
+next: books/arch-handbook/sound
+params:
+ path: /books/arch-handbook/newbus/
+prev: books/arch-handbook/usb
+showBookMenu: true
+tags: ["Newbus", "overview", "API"]
+title: 'Глава 14. Newbus'
+weight: 16
+---
+
+[[newbus]]
+= Newbus
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 14
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+_Особая благодарность Мэтью Н. Додду, Уорнеру Лошу, Биллу Полу, Дагу Рэбсону, Майку Смиту, Питеру Вемму и Скотту Лонгу_.
+
+Эта глава подробно объясняет фреймворк устройств Newbus.
+
+[[newbus-devdrivers]]
+== Драйверы устройств
+
+=== Назначение драйвера устройства
+
+Драйвер устройства — это программный компонент, который предоставляет интерфейс между обобщённым представлением периферийного устройства (например, диска, сетевого адаптера) в ядре и его фактической реализацией. _Интерфейс драйвера устройства (DDI)_ — это определённый интерфейс между ядром и компонентом драйвера устройства.
+
+=== Типы драйверов устройств
+
+В UNIX(R), а следовательно, и в FreeBSD, были времена, когда определялось четыре типа устройств:
+
+* драйверы блочных устройств
+* драйверы символьных устройств
+* драйверы сетевых устройств
+* драйверы псевдоустройств
+
+_Блочные устройства_ работали таким образом, что использовали блоки [данных] фиксированного размера. Этот тип драйвера зависел от так называемого _буферного кэша_, который кэшировал доступные блоки данных в выделенной части памяти. Часто этот буферный кэш был основан на отложенной записи (write-behind), что означало, что при изменении данных в памяти они синхронизировались с диском во время периодической очистки диска системой, тем самым оптимизируя запись.
+
+=== Символьные устройства
+
+Однако в версиях FreeBSD 4.0 и выше различие между блочными и символьными устройствами перестало существовать.
+
+[[newbus-overview]]
+== Обзор Newbus
+
+_Newbus_ — это реализация новой архитектуры шины, основанной на уровнях абстракции, которая была впервые представлена в FreeBSD 3.0, когда порт для Alpha был добавлен в дерево исходного кода. Однако только в версии 4.0 она стала системой по умолчанию для использования с драйверами устройств. Её цель — предоставить более объектно-ориентированный способ взаимодействия между различными шинами и устройствами, которые хост-система предоставляет _операционной системе_.
+
+Основные функции включают, среди прочего:
+
+* динамическое присоединение
+* простая модуляризация драйверов
+* псевдо-шины
+
+Одним из наиболее заметных изменений является переход от плоской и нерегламентированной системы к структуре дерева устройств.
+
+На верхнем уровне находится устройство _"root"_, которое является родительским для всех остальных устройств. Для каждой архитектуры обычно существует единственный дочерний элемент "root", к которому подключены такие компоненты, как _мосты host-to-PCI_ и т.д. Для x86 этим устройством "root" является устройство _"nexus"_. Для Alpha различные модели Alpha имеют разные устройства верхнего уровня, соответствующие различным аппаратным наборам микросхем, включая _lca_, _apecs_, _cia_ и _tsunami_.
+
+Устройство в контексте Newbus представляет собой отдельную аппаратную сущность в системе. Например, каждое PCI-устройство представлено устройством Newbus. Любое устройство в системе может иметь дочерние устройства; устройство, у которого есть дочерние устройства, часто называют _"шиной"_. Примерами распространённых шин в системе являются ISA и PCI, которые управляют списками устройств, подключённых к шинам ISA и PCI соответственно.
+
+Часто соединение между различными типами шин представлено устройством _"мост"_, которое обычно имеет один дочерний элемент для подключенной шины. Примером этого является _PCI-to-PCI мост_, который представлен устройством _[.filename]#pcibN#_ на родительской PCI-шине и имеет дочерний элемент _[.filename]#pciN#_ для подключенной шины. Такая структура упрощает реализацию дерева PCI-шин, позволяя использовать общий код как для верхнеуровневых, так и для соединенных через мост шин.
+
+Каждое устройство в архитектуре Newbus запрашивает у своего родителя отображение своих ресурсов. Затем родитель запрашивает у своего собственного родителя, пока запрос не достигнет nexus. Таким образом, по сути, nexus - это единственная часть системы Newbus, которая знает обо всех ресурсах.
+
+[TIP]
+====
+Устройство ISA может захотеть отобразить свой порт ввода-вывода по адресу `0x230`, поэтому оно запрашивает у своего родителя, в данном случае — шины ISA. Шина ISA передаёт запрос мосту PCI-to-ISA, который, в свою очередь, запрашивает шину PCI. Запрос доходит до моста host-to-PCI и, наконец, до nexus. Прелесть этого восходящего перехода в том, что есть возможность преобразовывать запросы. Например, запрос порта ввода-вывода `0x230` может быть преобразован в отображение памяти по адресу `0xb0000230` на системе MIPS с помощью моста PCI.
+====
+
+Распределение ресурсов может контролироваться в любом месте дерева устройств. Например, на многих платформах Alpha прерывания ISA управляются отдельно от прерываний PCI, а распределение ресурсов для прерываний ISA осуществляется устройством шины ISA Alpha. На IA-32 прерывания ISA и PCI управляются устройством верхнего уровня nexus. Для обеих архитектур управление пространством памяти и портов осуществляется единым объектом — nexus для IA-32 и соответствующим драйвером чипсета на Alpha (например, CIA или tsunami).
+
+Для стандартизации доступа к памяти и ресурсам, отображённым на порты, Newbus интегрирует API `bus_space` из NetBSD. Они предоставляют единый API для замены inb/outb и прямых операций чтения/записи в память. Преимущество этого подхода в том, что один драйвер может легко использовать либо регистры, отображённые в память, либо регистры, отображённые на порты (некоторое оборудование поддерживает оба варианта).
+
+Эта поддержка интегрирована в механизм распределения ресурсов. При выделении ресурса драйвер может получить связанные `bus_space_tag_t` и `bus_space_handle_t` из этого ресурса.
+
+Newbus также позволяет определять методы интерфейса в файлах, предназначенных для этой цели. Это файлы с расширением [.filename]#.m#, которые находятся в иерархии [.filename]#src/sys#.
+
+Ядро системы Newbus представляет собой расширяемую модель «объектно-ориентированного программирования». Каждое устройство в системе имеет таблицу поддерживаемых методов. Система и другие устройства используют эти методы для управления устройством и запроса услуг. Различные методы, поддерживаемые устройством, определяются рядом «интерфейсов». «Интерфейс» — это просто группа связанных методов, которые могут быть реализованы устройством.
+
+В системе Newbus методы для устройства предоставляются различными драйверами устройств в системе. Когда устройство подключается к драйверу во время _автоконфигурации_, оно использует таблицу методов, объявленную драйвером. Устройство может позже _отключиться_ от своего драйвера и _подключиться_ к новому драйверу с новой таблицей методов. Это позволяет динамически заменять драйверы, что может быть полезно для разработки драйверов.
+
+Интерфейсы описываются языком определения интерфейсов, похожим на язык, используемый для определения операций vnode для файловых систем. Интерфейс хранится в файле методов (который обычно называется [.filename]#foo_if.m#).
+
+.Методы Newbus
+[example]
+====
+[.programlisting]
+....
+ # Foo subsystem/driver (a comment...)
+
+ INTERFACE foo
+
+ METHOD int doit {
+ device_t dev;
+ };
+
+ # DEFAULT is the method that will be used, if a method was not
+ # provided via: DEVMETHOD()
+
+ METHOD void doit_to_child {
+ device_t dev;
+ driver_t child;
+ } DEFAULT doit_generic_to_child;
+....
+====
+
+Когда этот интерфейс компилируется, он генерирует заголовочный файл "[.filename]#foo_if.h#", который содержит объявления функций:
+
+[.programlisting]
+....
+ int FOO_DOIT(device_t dev);
+ int FOO_DOIT_TO_CHILD(device_t dev, device_t child);
+....
+
+Исходный файл `[.filename]#foo_if.c#` также создается для сопровождения автоматически сгенерированного заголовочного файла; он содержит реализации функций, которые ищут расположение соответствующих функций в таблице методов объекта и вызывают эту функцию.
+
+Система определяет два основных интерфейса. Первый фундаментальный интерфейс называется _"device"_ (устройство) и включает методы, которые относятся ко всем устройствам. Методы в интерфейсе _"device"_ включают _"probe"_ (обнаружение), _"attach"_ (присоединение) и _"detach"_ (отсоединение) для управления обнаружением оборудования, а также _"shutdown"_ (выключение), _"suspend"_ (приостановка) и _"resume"_ (возобновление) для уведомления о критических событиях.
+
+Второй, более сложный интерфейс — _"bus"_. Этот интерфейс содержит методы, подходящие для устройств, имеющих дочерние элементы, включая методы доступа к специфичной для шины информации об устройстве footnote:[man:bus_generic_read_ivar[9] и man:bus_generic_write_ivar[9]], уведомления о событиях (`_child_detached_`, `_driver_added_`) и управление ресурсами (`_alloc_resource_`, `_activate_resource_`, `_deactivate_resource_`, `_release_resource_`).
+
+Многие методы в интерфейсе "bus" выполняют сервисы для некоторого дочернего устройства шины. Эти методы обычно используют первые два аргумента для указания шины, предоставляющей сервис, и дочернего устройства, запрашивающего сервис. Для упрощения кода драйвера многие из этих методов имеют вспомогательные функции, которые находят родительское устройство и вызывают метод у родителя. Например, метод `BUS_TEARDOWN_INTR(device_t dev, device_t child, ...)` может быть вызван с помощью функции `bus_teardown_intr(device_t child, ...)`.
+
+Некоторые типы шин в системе определяют дополнительные интерфейсы для предоставления доступа к специфичной для шины функциональности. Например, драйвер шины PCI определяет интерфейс "pci", который имеет два метода `_read_config_` и `_write_config_` для доступа к конфигурационным регистрам устройства PCI.
+
+[[newbus-api]]
+== Newbus API
+
+Поскольку API Newbus очень обширен, в этом разделе предпринята попытка его документирования. Дополнительная информация будет добавлена в следующей версии этого документа.
+
+=== Важные места в иерархии исходного кода
+
+[.filename]#src/sys/[arch]/[arch]# - Код ядра для конкретной аппаратной архитектуры находится в этом каталоге. Например, архитектура `i386` или архитектура `SPARC64`.
+
+[.filename]#src/sys/dev/[bus]# - поддержка устройств для конкретной `[bus]` находится в этом каталоге.
+
+[.filename]#src/sys/dev/pci# - Код поддержки шины PCI находится в этом каталоге.
+
+[.filename]#src/sys/[isa|pci]# - В этом каталоге находятся драйверы устройств PCI/ISA. Код поддержки шины PCI/ISA располагался в этом каталоге в FreeBSD версии `4.0`.
+
+=== Важные структуры и определения типов
+
+`devclass_t` - Это определение типа указателя на `struct devclass`.
+
+`device_method_t` - Это то же самое, что и `kobj_method_t` (см. [.filename]#src/sys/kobj.h#).
+
+`device_t` - Это определение типа указателя на структуру `struct device`. `device_t` представляет устройство в системе. Это объект ядра. Подробности реализации см. в [.filename]#src/sys/sys/bus_private.h#.
+
+`driver_t` - Это определение типа, которое ссылается на `struct driver`. Структура `driver` является классом объекта ядра `device`; она также содержит данные, приватные для драйвера.
+
+*_driver_t_ Implementation*
+[.programlisting]
+....
+ struct driver {
+ KOBJ_CLASS_FIELDS;
+ void *priv; /* driver private data */
+ };
+....
+
+Тип `device_state_t`, который является перечислением, `device_state`. Он содержит возможные состояния устройства Newbus до и после процесса автонастройки.
+
+*Device States _device_state_t*
+[.programlisting]
+....
+ /*
+ * src/sys/sys/bus.h
+ */
+ typedef enum device_state {
+ DS_NOTPRESENT, /* not probed or probe failed */
+ DS_ALIVE, /* probe succeeded */
+ DS_ATTACHED, /* attach method called */
+ DS_BUSY /* device is open */
+ } device_state_t;
+....
diff --git a/documentation/content/ru/books/arch-handbook/newbus/_index.po b/documentation/content/ru/books/arch-handbook/newbus/_index.po
new file mode 100644
index 0000000000..92ae3249ba
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/newbus/_index.po
@@ -0,0 +1,711 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-07-05 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbooknewbus_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:19
+#, no-wrap
+msgid "Newbus"
+msgstr "Newbus"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:1
+#, no-wrap
+msgid "Chapter 14. Newbus"
+msgstr "Глава 14. Newbus"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:57
+msgid ""
+"_Special thanks to Matthew N. Dodd, Warner Losh, Bill Paul, Doug Rabson, "
+"Mike Smith, Peter Wemm and Scott Long_."
+msgstr ""
+"_Особая благодарность Мэтью Н. Додду, Уорнеру Лошу, Биллу Полу, Дагу "
+"Рэбсону, Майку Смиту, Питеру Вемму и Скотту Лонгу_."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:59
+msgid "This chapter explains the Newbus device framework in detail."
+msgstr "Эта глава подробно объясняет фреймворк устройств Newbus."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:61
+#, no-wrap
+msgid "Device Drivers"
+msgstr "Драйверы устройств"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:63
+#, no-wrap
+msgid "Purpose of a Device Driver"
+msgstr "Назначение драйвера устройства"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:66
+msgid ""
+"A device driver is a software component which provides the interface between "
+"the kernel's generic view of a peripheral (e.g., disk, network adapter) and "
+"the actual implementation of the peripheral. The _device driver interface "
+"(DDI)_ is the defined interface between the kernel and the device driver "
+"component."
+msgstr ""
+"Драйвер устройства — это программный компонент, который предоставляет "
+"интерфейс между обобщённым представлением периферийного устройства "
+"(например, диска, сетевого адаптера) в ядре и его фактической реализацией. "
+"_Интерфейс драйвера устройства (DDI)_ — это определённый интерфейс между "
+"ядром и компонентом драйвера устройства."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:67
+#, no-wrap
+msgid "Types of Device Drivers"
+msgstr "Типы драйверов устройств"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:70
+msgid ""
+"There used to be days in UNIX(R), and thus FreeBSD, in which there were four "
+"types of devices defined:"
+msgstr ""
+"В UNIX(R), а следовательно, и в FreeBSD, были времена, когда определялось "
+"четыре типа устройств:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:72
+msgid "block device drivers"
+msgstr "драйверы блочных устройств"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:73
+msgid "character device drivers"
+msgstr "драйверы символьных устройств"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:74
+msgid "network device drivers"
+msgstr "драйверы сетевых устройств"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:75
+msgid "pseudo-device drivers"
+msgstr "драйверы псевдоустройств"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:77
+msgid ""
+"_Block devices_ performed in a way that used fixed size blocks [of data]. "
+"This type of driver depended on the so-called _buffer cache_, which had "
+"cached accessed blocks of data in a dedicated part of memory. Often this "
+"buffer cache was based on write-behind, which meant that when data was "
+"modified in memory it got synced to disk whenever the system did its "
+"periodical disk flushing, thus optimizing writes."
+msgstr ""
+"_Блочные устройства_ работали таким образом, что использовали блоки [данных] "
+"фиксированного размера. Этот тип драйвера зависел от так называемого "
+"_буферного кэша_, который кэшировал доступные блоки данных в выделенной "
+"части памяти. Часто этот буферный кэш был основан на отложенной записи "
+"(write-behind), что означало, что при изменении данных в памяти они "
+"синхронизировались с диском во время периодической очистки диска системой, "
+"тем самым оптимизируя запись."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:78
+#, no-wrap
+msgid "Character Devices"
+msgstr "Символьные устройства"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:81
+msgid ""
+"However, in the versions of FreeBSD 4.0 and onward the distinction between "
+"block and character devices became non-existent."
+msgstr ""
+"Однако в версиях FreeBSD 4.0 и выше различие между блочными и символьными "
+"устройствами перестало существовать."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:83
+#, no-wrap
+msgid "Overview of Newbus"
+msgstr "Обзор Newbus"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:86
+msgid ""
+"_Newbus_ is the implementation of a new bus architecture based on "
+"abstraction layers which saw its introduction in FreeBSD 3.0 when the Alpha "
+"port was imported into the source tree. It was not until 4.0 before it "
+"became the default system to use for device drivers. Its goals are to "
+"provide a more object-oriented means of interconnecting the various busses "
+"and devices which a host system provides to the _Operating System_."
+msgstr ""
+"_Newbus_ — это реализация новой архитектуры шины, основанной на уровнях "
+"абстракции, которая была впервые представлена в FreeBSD 3.0, когда порт для "
+"Alpha был добавлен в дерево исходного кода. Однако только в версии 4.0 она "
+"стала системой по умолчанию для использования с драйверами устройств. Её "
+"цель — предоставить более объектно-ориентированный способ взаимодействия "
+"между различными шинами и устройствами, которые хост-система предоставляет "
+"_операционной системе_."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:88
+msgid "Its main features include amongst others:"
+msgstr "Основные функции включают, среди прочего:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:90
+msgid "dynamic attaching"
+msgstr "динамическое присоединение"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:91
+msgid "easy modularization of drivers"
+msgstr "простая модуляризация драйверов"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:92
+msgid "pseudo-busses"
+msgstr "псевдо-шины"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:94
+msgid ""
+"One of the most prominent changes is the migration from the flat and ad-hoc "
+"system to a device tree layout."
+msgstr ""
+"Одним из наиболее заметных изменений является переход от плоской и "
+"нерегламентированной системы к структуре дерева устройств."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:96
+msgid ""
+"At the top level resides the _\"root\"_ device which is the parent to hang "
+"all other devices on. For each architecture, there is typically a single "
+"child of \"root\" which has such things as _host-to-PCI bridges_, etc. "
+"attached to it. For x86, this \"root\" device is the _\"nexus\"_ device. For "
+"Alpha, various different models of Alpha have different top-level devices "
+"corresponding to the different hardware chipsets, including _lca_, _apecs_, "
+"_cia_ and _tsunami_."
+msgstr ""
+"На верхнем уровне находится устройство _\"root\"_, которое является "
+"родительским для всех остальных устройств. Для каждой архитектуры обычно "
+"существует единственный дочерний элемент \"root\", к которому подключены "
+"такие компоненты, как _мосты host-to-PCI_ и т.д. Для x86 этим устройством "
+"\"root\" является устройство _\"nexus\"_. Для Alpha различные модели Alpha "
+"имеют разные устройства верхнего уровня, соответствующие различным "
+"аппаратным наборам микросхем, включая _lca_, _apecs_, _cia_ и _tsunami_."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:98
+msgid ""
+"A device in the Newbus context represents a single hardware entity in the "
+"system. For instance each PCI device is represented by a Newbus device. Any "
+"device in the system can have children; a device which has children is often "
+"called a _\"bus\"_. Examples of common busses in the system are ISA and PCI, "
+"which manage lists of devices attached to ISA and PCI busses respectively."
+msgstr ""
+"Устройство в контексте Newbus представляет собой отдельную аппаратную "
+"сущность в системе. Например, каждое PCI-устройство представлено устройством "
+"Newbus. Любое устройство в системе может иметь дочерние устройства; "
+"устройство, у которого есть дочерние устройства, часто называют _\"шиной\"_. "
+"Примерами распространённых шин в системе являются ISA и PCI, которые "
+"управляют списками устройств, подключённых к шинам ISA и PCI соответственно."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:100
+msgid ""
+"Often, a connection between different kinds of bus is represented by a "
+"_\"bridge\"_ device, which normally has one child for the attached bus. An "
+"example of this is a _PCI-to-PCI bridge_ which is represented by a device "
+"_[.filename]#pcibN#_ on the parent PCI bus and has a child "
+"_[.filename]#pciN#_ for the attached bus. This layout simplifies the "
+"implementation of the PCI bus tree, allowing common code to be used for both "
+"top-level and bridged busses."
+msgstr ""
+"Часто соединение между различными типами шин представлено устройством "
+"_\"мост\"_, которое обычно имеет один дочерний элемент для подключенной "
+"шины. Примером этого является _PCI-to-PCI мост_, который представлен "
+"устройством _[.filename]#pcibN#_ на родительской PCI-шине и имеет дочерний "
+"элемент _[.filename]#pciN#_ для подключенной шины. Такая структура упрощает "
+"реализацию дерева PCI-шин, позволяя использовать общий код как для "
+"верхнеуровневых, так и для соединенных через мост шин."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:102
+msgid ""
+"Each device in the Newbus architecture asks its parent to map its resources. "
+"The parent then asks its own parent until the nexus is reached. So, "
+"basically the nexus is the only part of the Newbus system which knows about "
+"all resources."
+msgstr ""
+"Каждое устройство в архитектуре Newbus запрашивает у своего родителя "
+"отображение своих ресурсов. Затем родитель запрашивает у своего собственного "
+"родителя, пока запрос не достигнет nexus. Таким образом, по сути, nexus - "
+"это единственная часть системы Newbus, которая знает обо всех ресурсах."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:106
+msgid ""
+"An ISA device might want to map its IO port at `0x230`, so it asks its "
+"parent, in this case the ISA bus. The ISA bus hands it over to the PCI-to-"
+"ISA bridge which in its turn asks the PCI bus, which reaches the host-to-PCI "
+"bridge and finally the nexus. The beauty of this transition upwards is that "
+"there is room to translate the requests. For example, the `0x230` IO port "
+"request might become memory-mapped at `0xb0000230` on a MIPS box by the PCI "
+"bridge."
+msgstr ""
+"Устройство ISA может захотеть отобразить свой порт ввода-вывода по адресу "
+"`0x230`, поэтому оно запрашивает у своего родителя, в данном случае — шины "
+"ISA. Шина ISA передаёт запрос мосту PCI-to-ISA, который, в свою очередь, "
+"запрашивает шину PCI. Запрос доходит до моста host-to-PCI и, наконец, до "
+"nexus. Прелесть этого восходящего перехода в том, что есть возможность "
+"преобразовывать запросы. Например, запрос порта ввода-вывода `0x230` может "
+"быть преобразован в отображение памяти по адресу `0xb0000230` на системе "
+"MIPS с помощью моста PCI."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:109
+msgid ""
+"Resource allocation can be controlled at any place in the device tree. For "
+"instance on many Alpha platforms, ISA interrupts are managed separately from "
+"PCI interrupts and resource allocations for ISA interrupts are managed by "
+"the Alpha's ISA bus device. On IA-32, ISA and PCI interrupts are both "
+"managed by the top-level nexus device. For both ports, memory and port "
+"address space is managed by a single entity - nexus for IA-32 and the "
+"relevant chipset driver on Alpha (e.g., CIA or tsunami)."
+msgstr ""
+"Распределение ресурсов может контролироваться в любом месте дерева "
+"устройств. Например, на многих платформах Alpha прерывания ISA управляются "
+"отдельно от прерываний PCI, а распределение ресурсов для прерываний ISA "
+"осуществляется устройством шины ISA Alpha. На IA-32 прерывания ISA и PCI "
+"управляются устройством верхнего уровня nexus. Для обеих архитектур "
+"управление пространством памяти и портов осуществляется единым объектом — "
+"nexus для IA-32 и соответствующим драйвером чипсета на Alpha (например, CIA "
+"или tsunami)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:111
+msgid ""
+"In order to normalize access to memory and port mapped resources, Newbus "
+"integrates the `bus_space` APIs from NetBSD. These provide a single API to "
+"replace inb/outb and direct memory reads/writes. The advantage of this is "
+"that a single driver can easily use either memory-mapped registers or port-"
+"mapped registers (some hardware supports both)."
+msgstr ""
+"Для стандартизации доступа к памяти и ресурсам, отображённым на порты, "
+"Newbus интегрирует API `bus_space` из NetBSD. Они предоставляют единый API "
+"для замены inb/outb и прямых операций чтения/записи в память. Преимущество "
+"этого подхода в том, что один драйвер может легко использовать либо "
+"регистры, отображённые в память, либо регистры, отображённые на порты "
+"(некоторое оборудование поддерживает оба варианта)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:113
+msgid ""
+"This support is integrated into the resource allocation mechanism. When a "
+"resource is allocated, a driver can retrieve the associated "
+"`bus_space_tag_t` and `bus_space_handle_t` from the resource."
+msgstr ""
+"Эта поддержка интегрирована в механизм распределения ресурсов. При выделении "
+"ресурса драйвер может получить связанные `bus_space_tag_t` и "
+"`bus_space_handle_t` из этого ресурса."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:115
+msgid ""
+"Newbus also allows for definitions of interface methods in files dedicated "
+"to this purpose. These are the [.filename]#.m# files that are found under "
+"the [.filename]#src/sys# hierarchy."
+msgstr ""
+"Newbus также позволяет определять методы интерфейса в файлах, "
+"предназначенных для этой цели. Это файлы с расширением [.filename]#.m#, "
+"которые находятся в иерархии [.filename]#src/sys#."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:117
+msgid ""
+"The core of the Newbus system is an extensible \"object-based programming\" "
+"model. Each device in the system has a table of methods which it supports. "
+"The system and other devices uses those methods to control the device and "
+"request services. The different methods supported by a device are defined by "
+"a number of \"interfaces\". An \"interface\" is simply a group of related "
+"methods which can be implemented by a device."
+msgstr ""
+"Ядро системы Newbus представляет собой расширяемую модель «объектно-"
+"ориентированного программирования». Каждое устройство в системе имеет "
+"таблицу поддерживаемых методов. Система и другие устройства используют эти "
+"методы для управления устройством и запроса услуг. Различные методы, "
+"поддерживаемые устройством, определяются рядом «интерфейсов». «Интерфейс» — "
+"это просто группа связанных методов, которые могут быть реализованы "
+"устройством."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:119
+msgid ""
+"In the Newbus system, the methods for a device are provided by the various "
+"device drivers in the system. When a device is attached to a driver during "
+"_auto-configuration_, it uses the method table declared by the driver. A "
+"device can later _detach_ from its driver and _re-attach_ to a new driver "
+"with a new method table. This allows dynamic replacement of drivers which "
+"can be useful for driver development."
+msgstr ""
+"В системе Newbus методы для устройства предоставляются различными драйверами "
+"устройств в системе. Когда устройство подключается к драйверу во время "
+"_автоконфигурации_, оно использует таблицу методов, объявленную драйвером. "
+"Устройство может позже _отключиться_ от своего драйвера и _подключиться_ к "
+"новому драйверу с новой таблицей методов. Это позволяет динамически заменять "
+"драйверы, что может быть полезно для разработки драйверов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:121
+msgid ""
+"The interfaces are described by an interface definition language similar to "
+"the language used to define vnode operations for file systems. The interface "
+"would be stored in a methods file (which would normally be named "
+"[.filename]#foo_if.m#)."
+msgstr ""
+"Интерфейсы описываются языком определения интерфейсов, похожим на язык, "
+"используемый для определения операций vnode для файловых систем. Интерфейс "
+"хранится в файле методов (который обычно называется [.filename]#foo_if.m#)."
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:122
+#, no-wrap
+msgid "Newbus Methods"
+msgstr "Методы Newbus"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:128
+#, no-wrap
+msgid " # Foo subsystem/driver (a comment...)\n"
+msgstr " # Foo subsystem/driver (a comment...)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:130
+#, no-wrap
+msgid "\t INTERFACE foo\n"
+msgstr "\t INTERFACE foo\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:134
+#, no-wrap
+msgid ""
+"\tMETHOD int doit {\n"
+"\t\tdevice_t dev;\n"
+"\t};\n"
+msgstr ""
+"\tMETHOD int doit {\n"
+"\t\tdevice_t dev;\n"
+"\t};\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:137
+#, no-wrap
+msgid ""
+"\t# DEFAULT is the method that will be used, if a method was not\n"
+"\t# provided via: DEVMETHOD()\n"
+msgstr ""
+"\t# DEFAULT is the method that will be used, if a method was not\n"
+"\t# provided via: DEVMETHOD()\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:142
+#, no-wrap
+msgid ""
+"\tMETHOD void doit_to_child {\n"
+"\t\tdevice_t dev;\n"
+"\t\tdriver_t child;\n"
+"\t} DEFAULT doit_generic_to_child;\n"
+msgstr ""
+"\tMETHOD void doit_to_child {\n"
+"\t\tdevice_t dev;\n"
+"\t\tdriver_t child;\n"
+"\t} DEFAULT doit_generic_to_child;\n"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:146
+msgid ""
+"When this interface is compiled, it generates a header file "
+"\"[.filename]#foo_if.h#\" which contains function declarations:"
+msgstr ""
+"Когда этот интерфейс компилируется, он генерирует заголовочный файл "
+"\"[.filename]#foo_if.h#\", который содержит объявления функций:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:151
+#, no-wrap
+msgid ""
+" int FOO_DOIT(device_t dev);\n"
+" int FOO_DOIT_TO_CHILD(device_t dev, device_t child);\n"
+msgstr ""
+" int FOO_DOIT(device_t dev);\n"
+" int FOO_DOIT_TO_CHILD(device_t dev, device_t child);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:154
+msgid ""
+"A source file, \"[.filename]#foo_if.c#\" is also created to accompany the "
+"automatically generated header file; it contains implementations of those "
+"functions which look up the location of the relevant functions in the "
+"object's method table and call that function."
+msgstr ""
+"Исходный файл `[.filename]#foo_if.c#` также создается для сопровождения "
+"автоматически сгенерированного заголовочного файла; он содержит реализации "
+"функций, которые ищут расположение соответствующих функций в таблице методов "
+"объекта и вызывают эту функцию."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:156
+msgid ""
+"The system defines two main interfaces. The first fundamental interface is "
+"called _\"device\"_ and includes methods which are relevant to all devices. "
+"Methods in the _\"device\"_ interface include _\"probe\"_, _\"attach\"_ and "
+"_\"detach\"_ to control detection of hardware and _\"shutdown\"_, "
+"_\"suspend\"_ and _\"resume\"_ for critical event notification."
+msgstr ""
+"Система определяет два основных интерфейса. Первый фундаментальный интерфейс "
+"называется _\"device\"_ (устройство) и включает методы, которые относятся ко "
+"всем устройствам. Методы в интерфейсе _\"device\"_ включают _\"probe\"_ "
+"(обнаружение), _\"attach\"_ (присоединение) и _\"detach\"_ (отсоединение) "
+"для управления обнаружением оборудования, а также _\"shutdown\"_ "
+"(выключение), _\"suspend\"_ (приостановка) и _\"resume\"_ (возобновление) "
+"для уведомления о критических событиях."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:158
+msgid ""
+"The second, more complex interface is _\"bus\"_. This interface contains "
+"methods suitable for devices which have children, including methods to "
+"access bus specific per-device information footnote:"
+"[man:bus_generic_read_ivar[9] and man:bus_generic_write_ivar[9]], event "
+"notification (`_child_detached_`, `_driver_added_`) and resource management "
+"(`_alloc_resource_`, `_activate_resource_`, `_deactivate_resource_`, "
+"`_release_resource_`)."
+msgstr ""
+"Второй, более сложный интерфейс — _\"bus\"_. Этот интерфейс содержит методы, "
+"подходящие для устройств, имеющих дочерние элементы, включая методы доступа "
+"к специфичной для шины информации об устройстве footnote:"
+"[man:bus_generic_read_ivar[9] и man:bus_generic_write_ivar[9]], уведомления "
+"о событиях (`_child_detached_`, `_driver_added_`) и управление ресурсами "
+"(`_alloc_resource_`, `_activate_resource_`, `_deactivate_resource_`, "
+"`_release_resource_`)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:160
+msgid ""
+"Many methods in the \"bus\" interface are performing services for some child "
+"of the bus device. These methods would normally use the first two arguments "
+"to specify the bus providing the service and the child device which is "
+"requesting the service. To simplify driver code, many of these methods have "
+"accessor functions which lookup the parent and call a method on the parent. "
+"For instance the method `BUS_TEARDOWN_INTR(device_t dev, device_t "
+"child, ...)` can be called using the function `bus_teardown_intr(device_t "
+"child, ...)`."
+msgstr ""
+"Многие методы в интерфейсе \"bus\" выполняют сервисы для некоторого "
+"дочернего устройства шины. Эти методы обычно используют первые два аргумента "
+"для указания шины, предоставляющей сервис, и дочернего устройства, "
+"запрашивающего сервис. Для упрощения кода драйвера многие из этих методов "
+"имеют вспомогательные функции, которые находят родительское устройство и "
+"вызывают метод у родителя. Например, метод `BUS_TEARDOWN_INTR(device_t dev, "
+"device_t child, ...)` может быть вызван с помощью функции "
+"`bus_teardown_intr(device_t child, ...)`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:162
+msgid ""
+"Some bus types in the system define additional interfaces to provide access "
+"to bus-specific functionality. For instance, the PCI bus driver defines the "
+"\"pci\" interface which has two methods `_read_config_` and `_write_config_` "
+"for accessing the configuration registers of a PCI device."
+msgstr ""
+"Некоторые типы шин в системе определяют дополнительные интерфейсы для "
+"предоставления доступа к специфичной для шины функциональности. Например, "
+"драйвер шины PCI определяет интерфейс \"pci\", который имеет два метода "
+"`_read_config_` и `_write_config_` для доступа к конфигурационным регистрам "
+"устройства PCI."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:164
+#, no-wrap
+msgid "Newbus API"
+msgstr "Newbus API"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:167
+msgid ""
+"As the Newbus API is huge, this section makes some effort at documenting it. "
+"More information to come in the next revision of this document."
+msgstr ""
+"Поскольку API Newbus очень обширен, в этом разделе предпринята попытка его "
+"документирования. Дополнительная информация будет добавлена в следующей "
+"версии этого документа."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:168
+#, no-wrap
+msgid "Important Locations in the Source Hierarchy"
+msgstr "Важные места в иерархии исходного кода"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:171
+msgid ""
+"[.filename]#src/sys/[arch]/[arch]# - Kernel code for a specific machine "
+"architecture resides in this directory. For example, the `i386` "
+"architecture, or the `SPARC64` architecture."
+msgstr ""
+"[.filename]#src/sys/[arch]/[arch]# - Код ядра для конкретной аппаратной "
+"архитектуры находится в этом каталоге. Например, архитектура `i386` или "
+"архитектура `SPARC64`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:173
+msgid ""
+"[.filename]#src/sys/dev/[bus]# - device support for a specific `[bus]` "
+"resides in this directory."
+msgstr ""
+"[.filename]#src/sys/dev/[bus]# - поддержка устройств для конкретной `[bus]` "
+"находится в этом каталоге."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:175
+msgid ""
+"[.filename]#src/sys/dev/pci# - PCI bus support code resides in this "
+"directory."
+msgstr ""
+"[.filename]#src/sys/dev/pci# - Код поддержки шины PCI находится в этом "
+"каталоге."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:177
+msgid ""
+"[.filename]#src/sys/[isa|pci]# - PCI/ISA device drivers reside in this "
+"directory. The PCI/ISA bus support code used to exist in this directory in "
+"FreeBSD version `4.0`."
+msgstr ""
+"[.filename]#src/sys/[isa|pci]# - В этом каталоге находятся драйверы "
+"устройств PCI/ISA. Код поддержки шины PCI/ISA располагался в этом каталоге в "
+"FreeBSD версии `4.0`."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:178
+#, no-wrap
+msgid "Important Structures and Type Definitions"
+msgstr "Важные структуры и определения типов"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:181
+msgid ""
+"`devclass_t` - This is a type definition of a pointer to a `struct devclass`."
+msgstr "`devclass_t` - Это определение типа указателя на `struct devclass`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:183
+msgid ""
+"`device_method_t` - This is the same as `kobj_method_t` (see [.filename]#src/"
+"sys/kobj.h#)."
+msgstr ""
+"`device_method_t` - Это то же самое, что и `kobj_method_t` (см. "
+"[.filename]#src/sys/kobj.h#)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:185
+msgid ""
+"`device_t` - This is a type definition of a pointer to a `struct device`. "
+"`device_t` represents a device in the system. It is a kernel object. See "
+"[.filename]#src/sys/sys/bus_private.h# for implementation details."
+msgstr ""
+"`device_t` - Это определение типа указателя на структуру `struct device`. "
+"`device_t` представляет устройство в системе. Это объект ядра. Подробности "
+"реализации см. в [.filename]#src/sys/sys/bus_private.h#."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:187
+msgid ""
+"`driver_t` - This is a type definition which references `struct driver`. The "
+"`driver` struct is a class of the `device` kernel object; it also holds data "
+"private to the driver."
+msgstr ""
+"`driver_t` - Это определение типа, которое ссылается на `struct driver`. "
+"Структура `driver` является классом объекта ядра `device`; она также "
+"содержит данные, приватные для драйвера."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:189
+#, fuzzy
+#| msgid "*_driver_t_ Implementation*\n"
+msgid "*_driver_t_ Implementation*"
+msgstr "* Реализация _driver_t_*\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:195
+#, no-wrap
+msgid ""
+"\t struct driver {\n"
+"\t\tKOBJ_CLASS_FIELDS;\n"
+"\t\tvoid\t*priv;\t\t\t/* driver private data */\n"
+"\t };\n"
+msgstr ""
+"\t struct driver {\n"
+"\t\tKOBJ_CLASS_FIELDS;\n"
+"\t\tvoid\t*priv;\t\t\t/* driver private data */\n"
+"\t };\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:198
+msgid ""
+"A `device_state_t` type, which is an enumeration, `device_state`. It "
+"contains the possible states of a Newbus device before and after the "
+"autoconfiguration process."
+msgstr ""
+"Тип `device_state_t`, который является перечислением, `device_state`. Он "
+"содержит возможные состояния устройства Newbus до и после процесса "
+"автонастройки."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:200
+#, fuzzy
+#| msgid "*Device States _device_state_t*\n"
+msgid "*Device States _device_state_t*"
+msgstr "*Состояния устройств _device_state_t*\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/newbus/_index.adoc:211
+#, no-wrap
+msgid ""
+"\t /*\n"
+"\t * src/sys/sys/bus.h\n"
+"\t */\n"
+"\t typedef enum device_state {\n"
+"\t\tDS_NOTPRESENT,\t/* not probed or probe failed */\n"
+"\t\tDS_ALIVE,\t\t/* probe succeeded */\n"
+"\t\tDS_ATTACHED,\t/* attach method called */\n"
+"\t\tDS_BUSY\t\t\t/* device is open */\n"
+"\t } device_state_t;\n"
+msgstr ""
+"\t /*\n"
+"\t * src/sys/sys/bus.h\n"
+"\t */\n"
+"\t typedef enum device_state {\n"
+"\t\tDS_NOTPRESENT,\t/* not probed or probe failed */\n"
+"\t\tDS_ALIVE,\t\t/* probe succeeded */\n"
+"\t\tDS_ATTACHED,\t/* attach method called */\n"
+"\t\tDS_BUSY\t\t\t/* device is open */\n"
+"\t } device_state_t;\n"
diff --git a/documentation/content/ru/books/arch-handbook/parti.adoc b/documentation/content/ru/books/arch-handbook/parti.adoc
new file mode 100644
index 0000000000..8f04536f14
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/parti.adoc
@@ -0,0 +1,13 @@
+---
+BundleType: branch
+next: books/arch-handbook/boot
+params:
+ path: /books/arch-handbook/parti/
+prev: books/arch-handbook
+showBookMenu: true
+title: 'Глава I. Ядро'
+weight: 1
+---
+
+[[kernel]]
+= Ядро системы
diff --git a/documentation/content/ru/books/arch-handbook/parti.po b/documentation/content/ru/books/arch-handbook/parti.po
new file mode 100644
index 0000000000..ac36191cc2
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/parti.po
@@ -0,0 +1,31 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-06-12 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookparti/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/parti.adoc:1
+#, no-wrap
+msgid "Part I. Kernel"
+msgstr "Глава I. Ядро"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/parti.adoc:13
+#, no-wrap
+msgid "Kernel"
+msgstr "Ядро системы"
diff --git a/documentation/content/ru/books/arch-handbook/partii.adoc b/documentation/content/ru/books/arch-handbook/partii.adoc
new file mode 100644
index 0000000000..b8870299b4
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/partii.adoc
@@ -0,0 +1,12 @@
+---
+next: books/arch-handbook/driverbasics
+params:
+ path: /books/arch-handbook/partii/
+prev: books/arch-handbook/smp
+showBookMenu: true
+title: 'Глава II. Драйверы устройств'
+weight: 10
+---
+
+[[devicedrivers]]
+= Драйверы устройств
diff --git a/documentation/content/ru/books/arch-handbook/partii.po b/documentation/content/ru/books/arch-handbook/partii.po
new file mode 100644
index 0000000000..a0698863d7
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/partii.po
@@ -0,0 +1,31 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-05-29 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookpartii/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/partii.adoc:1
+#, no-wrap
+msgid "Part II. Device Drivers"
+msgstr "Глава II. Драйверы устройств"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/partii.adoc:12
+#, no-wrap
+msgid "Device Drivers"
+msgstr "Драйверы устройств"
diff --git a/documentation/content/ru/books/arch-handbook/partiii.adoc b/documentation/content/ru/books/arch-handbook/partiii.adoc
new file mode 100644
index 0000000000..a219eb1f14
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/partiii.adoc
@@ -0,0 +1,12 @@
+---
+next: books/arch-handbook/bibliography
+params:
+ path: /books/arch-handbook/partiii/
+prev: books/arch-handbook/pccard
+showBookMenu: true
+title: 'Глава III. Приложения'
+weight: 19
+---
+
+[[appendices]]
+= Приложения
diff --git a/documentation/content/ru/books/arch-handbook/partiii.po b/documentation/content/ru/books/arch-handbook/partiii.po
new file mode 100644
index 0000000000..951813b7ca
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/partiii.po
@@ -0,0 +1,31 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-05-29 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookpartiii/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/partiii.adoc:1
+#, no-wrap
+msgid "Part III. Appendices"
+msgstr "Глава III. Приложения"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/partiii.adoc:12
+#, no-wrap
+msgid "Appendices"
+msgstr "Приложения"
diff --git a/documentation/content/ru/books/arch-handbook/pccard/_index.adoc b/documentation/content/ru/books/arch-handbook/pccard/_index.adoc
new file mode 100644
index 0000000000..9f6116121e
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/pccard/_index.adoc
@@ -0,0 +1,209 @@
+---
+description: 'PC Card'
+next: books/arch-handbook/partiii
+params:
+ path: /books/arch-handbook/pccard/
+prev: books/arch-handbook/sound
+showBookMenu: true
+tags: ["pc card", "overview"]
+title: 'Глава 16. PC Card'
+weight: 18
+---
+
+[[pccard]]
+= PC Card
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 16
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+Эта глава расскажет о механизмах FreeBSD для написания драйвера устройства для PC Card или CardBus устройства. Однако в настоящее время она лишь документирует, как добавить новое устройство к существующему драйверу pccard.
+
+[[pccard-adddev]]
+== Добавление устройства
+
+Драйверы устройств знают, какие устройства они поддерживают. В ядре существует таблица поддерживаемых устройств, которую драйверы используют для подключения к устройству.
+
+[[pccard-overview]]
+=== Обзор
+
+PC Cards идентифицируются одним из двух способов, оба основаны на _Card Information Structure_ (CIS), хранящейся на карте. Первый метод — использование числовых идентификаторов производителя и продукта. Второй метод — использование удобочитаемых строк, также содержащихся в CIS. Шина PC Card использует централизованную базу данных и некоторые макросы для упрощения шаблона проектирования, помогающего автору драйвера сопоставлять устройства с его драйвером.
+
+Производители оригинального оборудования (OEM) часто разрабатывают эталонный дизайн для продуктов PC Card, а затем продают этот дизайн другим компаниям для продвижения на рынке. Эти компании дорабатывают дизайн, продвигают продукт для своей целевой аудитории или географического региона и размещают на карте свою собственную торговую марку. Доработки физической карты обычно очень незначительны, если они вообще вносятся. Чтобы усилить свой бренд, такие поставщики указывают название своей компании в читаемых человеком строках в пространстве CIS, но оставляют идентификаторы производителя и продукта без изменений.
+
+Из-за такой практики драйверы FreeBSD обычно полагаются на числовые идентификаторы для распознавания устройств. Использование числовых идентификаторов и централизованной базы данных усложняет добавление ID и поддержку карт в систему. Необходимо тщательно проверять, кто на самом деле произвел карту, особенно когда кажется, что у производителя карты уже может быть другой идентификатор производителя в центральной базе данных. Linksys, D-Link и NetGear — это несколько американских производителей сетевого оборудования, которые часто продают один и тот же дизайн. Эти же дизайны могут продаваться в Японии под такими названиями, как Buffalo и Corega. Часто все эти устройства будут иметь одинаковые идентификаторы производителя и продукта.
+
+Код шины PC Card хранит централизованную базу данных информации о картах, но не о том, какой драйвер с ними связан, в [.filename]#/sys/dev/pccard/pccarddevs#. Он также предоставляет набор макросов, которые позволяют легко создавать простые записи в таблице, используемой драйвером для заявки устройств.
+
+Наконец, некоторые устройства очень низкого уровня вообще не содержат идентификации производителя. Эти устройства должны быть обнаружены путем сопоставления читаемых человеком строк CIS. Хотя было бы хорошо, если бы нам не нужен был этот метод в качестве запасного варианта, он необходим для некоторых очень дешевых CD-плееров и Ethernet-карт. Этот метод, как правило, следует избегать, но ряд устройств перечислен в этом разделе, потому что они были добавлены до осознания OEM-характера бизнеса PC Card. При добавлении новых устройств предпочтительнее использовать числовой метод.
+
+[[pccard-pccarddevs]]
+=== Формат файла [.filename]#pccarddevs#
+
+В файле [.filename]#pccarddevs# есть четыре раздела. Первый раздел содержит номера производителей для вендоров, которые их используют. Этот раздел отсортирован в числовом порядке. Следующий раздел включает все продукты, используемые этими вендорами, вместе с их идентификаторами продуктов и строкой описания. Строка описания обычно не используется (вместо этого мы устанавливаем описание устройства на основе читаемого CIS, даже если совпадение найдено по числовому идентификатору). Затем эти два раздела повторяются для устройств, использующих метод строкового сопоставления. Наконец, C-подобные комментарии, заключенные между символами `/*` и `*/`, допускаются в любом месте файла.
+
+Первая часть файла содержит идентификаторы производителей. Пожалуйста, сохраняйте этот список в числовом порядке. Также, пожалуйста, согласовывайте изменения в этом файле, так как мы делимся им с NetBSD для создания общего центра обработки этой информации. Например, вот первые несколько идентификаторов производителей:
+
+[.programlisting]
+....
+vendor FUJITSU 0x0004 Fujitsu Corporation
+vendor NETGEAR_2 0x000b Netgear
+vendor PANASONIC 0x0032 Matsushita Electric Industrial Co.
+vendor SANDISK 0x0045 Sandisk Corporation
+....
+
+Вероятно, запись `NETGEAR_2` на самом деле относится к OEM-производителю, у которого NETGEAR приобретал карты, и автор поддержки этих карт не знал на тот момент, что Netgear использовал чужой идентификатор. Эти записи довольно просты. Ключевое слово `vendor` обозначает тип строки, за которым следует название производителя. Это название будет повторяться позже в [.filename]#pccarddevs#, а также использоваться в таблицах соответствия драйверов, поэтому оно должно быть коротким и допустимым идентификатором в C. Числовой идентификатор в шестнадцатеричном формате указывает производителя. Не добавляйте идентификаторы вида `0xffffffff` или `0xffff`, так как они зарезервированы (первый означает "идентификатор не установлен", а второй иногда встречается в крайне некачественных картах для указания "отсутствует"). Наконец, следует строковое описание компании, производящей карту. Эта строка в FreeBSD ни для чего не используется, кроме как в комментариях.
+
+Вторая секция файла содержит продукты. Как показано в этом примере, формат аналогичен строкам поставщиков:
+
+[.programlisting]
+....
+/* Allied Telesis K.K. */
+product ALLIEDTELESIS LA_PCM 0x0002 Allied Telesis LA-PCM
+
+/* Archos */
+product ARCHOS ARC_ATAPI 0x0043 MiniCD
+....
+
+Ключевое слово `product` следует за именем производителя, повторяющимся сверху. После него идет название продукта, которое используется драйвером и должно быть допустимым идентификатором в C, но также может начинаться с цифры. Как и в случае с производителями, шестнадцатеричный идентификатор продукта для этой карты следует тем же соглашениям для `0xffffffff` и `0xffff`. Наконец, идет строковое описание самого устройства. Эта строка обычно не используется в FreeBSD, поскольку драйвер шины pccard в FreeBSD формирует строку из читаемых человеком записей CIS, но она может быть использована в редких случаях, когда этого недостаточно. Продукты перечислены в алфавитном порядке по производителю, затем в числовом порядке по идентификатору продукта. Перед записями каждого производителя есть комментарий в C, а между записями — пустая строка.
+
+Третий раздел аналогичен предыдущему разделу производителей, но все числовые идентификаторы производителей установлены в `-1`, что означает "совпадение с любым найденным" в коде шины pccard FreeBSD. Поскольку это идентификаторы C, их имена должны быть уникальными. В остальном формат идентичен первому разделу файла.
+
+Последний раздел содержит записи для тех карт, которые должны быть идентифицированы по строковым значениям. Формат этого раздела немного отличается от общего раздела:
+
+[.programlisting]
+....
+product ADDTRON AWP100 { "Addtron", "AWP-100&spWireless&spPCMCIA", "Version&sp01.02", NULL }
+product ALLIEDTELESIS WR211PCM { "Allied&spTelesis&spK.K.", "WR211PCM", NULL, NULL } Allied Telesis WR211PCM
+....
+
+Знакомое ключевое слово `product` сопровождается названием производителя и именем карты, как и во втором разделе файла. Здесь формат отличается от использованного ранее. Идёт группировка {}, за которой следует несколько строк. Эти строки соответствуют производителю, продукту и дополнительной информации, определённой в кортеже CIS_INFO. Эти строки фильтруются программой, которая генерирует [.filename]#pccarddevs.h#, чтобы заменить &sp на реальный пробел. Строки NULL означают, что соответствующую часть записи следует игнорировать. В приведённом здесь примере есть некорректная запись. Она не должна содержать номер версии, если только он не критичен для работы карты. Иногда у производителей может быть множество различных версий карты в обращении, которые все работают, и в таком случае эта информация только затрудняет использование аналогичной карты с FreeBSD. Иногда это необходимо, когда производитель хочет продавать множество различных компонентов под одним брендом из-за рыночных соображений (доступность, цена и т. д.). Тогда это может быть критично для различения карты в тех редких случаях, когда производитель сохранил ту же пару производитель/продукт. На данный момент использование регулярных выражений для сопоставления недоступно.
+
+[[pccard-probe]]
+=== Пример процедуры обнаружения
+
+Чтобы понять, как добавить устройство в список поддерживаемых, необходимо разобраться в процедурах `probe` (обнаружение) и/или `match` (сопоставление), которые есть во многих драйверах. В FreeBSD 5.x это немного сложнее из-за наличия слоя совместимости с OLDCARD. Поскольку различия лишь косметические, здесь будет представлена идеализированная версия.
+
+[.programlisting]
+....
+static const struct pccard_product wi_pccard_products[] = {
+ PCMCIA_CARD(3COM, 3CRWE737A, 0),
+ PCMCIA_CARD(BUFFALO, WLI_PCM_S11, 0),
+ PCMCIA_CARD(BUFFALO, WLI_CF_S11G, 0),
+ PCMCIA_CARD(TDK, LAK_CD011WL, 0),
+ { NULL }
+};
+
+static int
+wi_pccard_probe(dev)
+ device_t dev;
+{
+ const struct pccard_product *pp;
+
+ if ((pp = pccard_product_lookup(dev, wi_pccard_products,
+ sizeof(wi_pccard_products[0]), NULL)) != NULL) {
+ if (pp->pp_name != NULL)
+ device_set_desc(dev, pp->pp_name);
+ return (0);
+ }
+ return (ENXIO);
+}
+....
+
+Вот простая процедура проверки pccard, которая соответствует нескольким устройствам. Как упоминалось выше, название может отличаться (если это не `foo_pccard_probe()`, то это будет `foo_pccard_match()`). Функция `pccard_product_lookup()` является обобщенной функцией, которая проходит по таблице и возвращает указатель на первую запись, которой соответствует. Некоторые драйверы могут использовать этот механизм для передачи дополнительной информации о некоторых картах остальной части драйвера, поэтому в таблице могут быть вариации. Единственное требование — каждая строка таблицы должна содержать `struct pccard_product` в качестве первого элемента.
+
+Рассматривая таблицу `wi_pccard_products`, можно заметить, что все записи имеют вид `PCMCIA_CARD(_foo_, _bar_, _baz_)`. Часть _foo_ — это идентификатор производителя из [.filename]#pccarddevs#. Часть _bar_ — это идентификатор продукта. _baz_ — ожидаемый номер функции для этой карты. Многие pccard-устройства могут иметь несколько функций, поэтому требуется способ различать функцию 1 и функцию 0. Вы можете встретить `PCMCIA_CARD_D`, который включает описание устройства из [.filename]#pccarddevs#. Также могут встречаться `PCMCIA_CARD2` и `PCMCIA_CARD2_D`, которые используются, когда необходимо сопоставить как строки CIS, так и номера производителей, в вариантах «использовать описание по умолчанию» и «взять описание из pccarddevs».
+
+[[pccard-add]]
+=== Собираем все вместе
+
+Для добавления нового устройства необходимо сначала получить идентификационную информацию от устройства. Проще всего это сделать, вставив устройство в слот PC Card или CF и выполнив команду `devinfo -v`. Пример вывода:
+
+[.programlisting]
+....
+ cbb1 pnpinfo vendor=0x104c device=0xac51 subvendor=0x1265 subdevice=0x0300 class=0x060700 at slot=10 function=1
+ cardbus1
+ pccard1
+ unknown pnpinfo manufacturer=0x026f product=0x030c cisvendor="BUFFALO" cisproduct="WLI2-CF-S11" function_type=6 at function=0
+....
+
+`manufacturer` и `product` являются числовыми идентификаторами данного продукта, в то время как `cisvendor` и `cisproduct` представляют собой строки описания продукта из CIS.
+
+Поскольку мы сначала хотим предпочесть числовой вариант, попробуем сначала создать запись на его основе. Приведённая выше карта была слегка изменена для целей данного примера. Производитель — BUFFALO, у которого, как мы видим, уже есть запись:
+
+[.programlisting]
+....
+vendor BUFFALO 0x026f BUFFALO (Melco Corporation)
+....
+
+Но нет записи для этой конкретной карты. Вместо этого мы видим:
+
+[.programlisting]
+....
+/* BUFFALO */
+product BUFFALO WLI_PCM_S11 0x0305 BUFFALO AirStation 11Mbps WLAN
+product BUFFALO LPC_CF_CLT 0x0307 BUFFALO LPC-CF-CLT
+product BUFFALO LPC3_CLT 0x030a BUFFALO LPC3-CLT Ethernet Adapter
+product BUFFALO WLI_CF_S11G 0x030b BUFFALO AirStation 11Mbps CF WLAN
+....
+
+Чтобы добавить устройство, мы можем просто добавить эту запись в [.filename]#pccarddevs#:
+
+[.programlisting]
+....
+product BUFFALO WLI2_CF_S11G 0x030c BUFFALO AirStation ultra 802.11b CF
+....
+
+После выполнения этих шагов карту можно добавить в драйвер. Это простая операция добавления одной строки:
+
+[.programlisting]
+....
+static const struct pccard_product wi_pccard_products[] = {
+ PCMCIA_CARD(3COM, 3CRWE737A, 0),
+ PCMCIA_CARD(BUFFALO, WLI_PCM_S11, 0),
+ PCMCIA_CARD(BUFFALO, WLI_CF_S11G, 0),
++ PCMCIA_CARD(BUFFALO, WLI_CF2_S11G, 0),
+ PCMCIA_CARD(TDK, LAK_CD011WL, 0),
+ { NULL }
+};
+....
+
+Обратите внимание, что я добавил символ '`+`' перед строкой, которую добавил, но это только для выделения строки. Не добавляйте его в реальный драйвер. После добавления строки вы можете пересобрать ядро или модуль и протестировать его. Если устройство распознано и работает, отправьте патч. Если оно не работает, определите, что необходимо для его работы, и отправьте патч. Если устройство не распознаётся вообще, вы где-то ошиблись и следует перепроверить каждый шаг.
+
+Если вы коммиттер исходного кода FreeBSD, и всё работает корректно, то можете закоммитить изменения в дерево. Однако есть несколько небольших нюансов, которые следует учесть. [.filename]#pccarddevs# должен быть закоммичен в дерево первым. Затем [.filename]#pccarddevs.h# необходимо перегенерировать и закоммитить вторым шагом, убедившись, что правильный тег $FreeBSD$ присутствует в последнем файле. В конце закоммитьте добавления в драйвер.
+
+[[pccard-pr]]
+=== Отправка кода для нового устройства
+
+Пожалуйста, не отправляйте записи о новых устройствах автору напрямую. Вместо этого оформите их как PR и сообщите автору номер PR для учета. Это гарантирует, что записи не будут потеряны. При отправке PR нет необходимости включать в патч diff-файлы [.filename]#pccardevs.h#, так как они будут перегенерированы. Однако необходимо включить описание устройства, а также патчи для клиентского драйвера. Если название устройства неизвестно, используйте имя OEM99, и автор скорректирует OEM99 после изучения. Коммиттеры не должны коммитить OEM99, а вместо этого найти наибольший OEM-номер и закоммитить на единицу больше.
diff --git a/documentation/content/ru/books/arch-handbook/pccard/_index.po b/documentation/content/ru/books/arch-handbook/pccard/_index.po
new file mode 100644
index 0000000000..5248481b16
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/pccard/_index.po
@@ -0,0 +1,710 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-09-05 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookpccard_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:14
+#, no-wrap
+msgid "PC Card"
+msgstr "PC Card"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:1
+#, no-wrap
+msgid "Chapter 16. PC Card"
+msgstr "Глава 16. PC Card"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:52
+msgid ""
+"This chapter will talk about the FreeBSD mechanisms for writing a device "
+"driver for a PC Card or CardBus device. However, at present it just "
+"documents how to add a new device to an existing pccard driver."
+msgstr ""
+"Эта глава расскажет о механизмах FreeBSD для написания драйвера устройства "
+"для PC Card или CardBus устройства. Однако в настоящее время она лишь "
+"документирует, как добавить новое устройство к существующему драйверу pccard."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:54
+#, no-wrap
+msgid "Adding a Device"
+msgstr "Добавление устройства"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:57
+msgid ""
+"Device drivers know what devices they support. There is a table of supported "
+"devices in the kernel that drivers use to attach to a device."
+msgstr ""
+"Драйверы устройств знают, какие устройства они поддерживают. В ядре "
+"существует таблица поддерживаемых устройств, которую драйверы используют для "
+"подключения к устройству."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:59
+#, no-wrap
+msgid "Overview"
+msgstr "Обзор"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:62
+msgid ""
+"PC Cards are identified in one of two ways, both based on the _Card "
+"Information Structure_ (CIS) stored on the card. The first method is to use "
+"numeric manufacturer and product numbers. The second method is to use the "
+"human readable strings that are also contained in the CIS. The PC Card bus "
+"uses a centralized database and some macros to facilitate a design pattern "
+"to help the driver writer match devices to his driver."
+msgstr ""
+"PC Cards идентифицируются одним из двух способов, оба основаны на _Card "
+"Information Structure_ (CIS), хранящейся на карте. Первый метод — "
+"использование числовых идентификаторов производителя и продукта. Второй "
+"метод — использование удобочитаемых строк, также содержащихся в CIS. Шина PC "
+"Card использует централизованную базу данных и некоторые макросы для "
+"упрощения шаблона проектирования, помогающего автору драйвера сопоставлять "
+"устройства с его драйвером."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:64
+msgid ""
+"Original equipment manufacturers (OEMs) often develop a reference design for "
+"a PC Card product, then sell this design to other companies to market. Those "
+"companies refine the design, market the product to their target audience or "
+"geographic area, and put their own name plate onto the card. The refinements "
+"to the physical card are typically very minor, if any changes are made at "
+"all. To strengthen their brand, these vendors place their company name in "
+"the human readable strings in the CIS space, but leave the manufacturer and "
+"product IDs unchanged."
+msgstr ""
+"Производители оригинального оборудования (OEM) часто разрабатывают эталонный "
+"дизайн для продуктов PC Card, а затем продают этот дизайн другим компаниям "
+"для продвижения на рынке. Эти компании дорабатывают дизайн, продвигают "
+"продукт для своей целевой аудитории или географического региона и размещают "
+"на карте свою собственную торговую марку. Доработки физической карты обычно "
+"очень незначительны, если они вообще вносятся. Чтобы усилить свой бренд, "
+"такие поставщики указывают название своей компании в читаемых человеком "
+"строках в пространстве CIS, но оставляют идентификаторы производителя и "
+"продукта без изменений."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:66
+msgid ""
+"Due to this practice, FreeBSD drivers usually rely on numeric IDs for device "
+"identification. Using numeric IDs and a centralized database complicates "
+"adding IDs and support for cards to the system. One must carefully check to "
+"see who really made the card, especially when it appears that the vendor who "
+"made the card might already have a different manufacturer ID listed in the "
+"central database. Linksys, D-Link, and NetGear are a number of US "
+"manufacturers of LAN hardware that often sell the same design. These same "
+"designs can be sold in Japan under names such as Buffalo and Corega. Often, "
+"these devices will all have the same manufacturer and product IDs."
+msgstr ""
+"Из-за такой практики драйверы FreeBSD обычно полагаются на числовые "
+"идентификаторы для распознавания устройств. Использование числовых "
+"идентификаторов и централизованной базы данных усложняет добавление ID и "
+"поддержку карт в систему. Необходимо тщательно проверять, кто на самом деле "
+"произвел карту, особенно когда кажется, что у производителя карты уже может "
+"быть другой идентификатор производителя в центральной базе данных. Linksys, "
+"D-Link и NetGear — это несколько американских производителей сетевого "
+"оборудования, которые часто продают один и тот же дизайн. Эти же дизайны "
+"могут продаваться в Японии под такими названиями, как Buffalo и Corega. "
+"Часто все эти устройства будут иметь одинаковые идентификаторы производителя "
+"и продукта."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:68
+msgid ""
+"The PC Card bus code keeps a central database of card information, but not "
+"which driver is associated with them, in [.filename]#/sys/dev/pccard/"
+"pccarddevs#. It also provides a set of macros that allow one to easily "
+"construct simple entries in the table the driver uses to claim devices."
+msgstr ""
+"Код шины PC Card хранит централизованную базу данных информации о картах, но "
+"не о том, какой драйвер с ними связан, в [.filename]#/sys/dev/pccard/"
+"pccarddevs#. Он также предоставляет набор макросов, которые позволяют легко "
+"создавать простые записи в таблице, используемой драйвером для заявки "
+"устройств."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:70
+msgid ""
+"Finally, some really low end devices do not contain manufacturer "
+"identification at all. These devices must be detected by matching the human "
+"readable CIS strings. While it would be nice if we did not need this method "
+"as a fallback, it is necessary for some very low end CD-ROM players and "
+"Ethernet cards. This method should generally be avoided, but a number of "
+"devices are listed in this section because they were added prior to the "
+"recognition of the OEM nature of the PC Card business. When adding new "
+"devices, prefer using the numeric method."
+msgstr ""
+"Наконец, некоторые устройства очень низкого уровня вообще не содержат "
+"идентификации производителя. Эти устройства должны быть обнаружены путем "
+"сопоставления читаемых человеком строк CIS. Хотя было бы хорошо, если бы нам "
+"не нужен был этот метод в качестве запасного варианта, он необходим для "
+"некоторых очень дешевых CD-плееров и Ethernet-карт. Этот метод, как правило, "
+"следует избегать, но ряд устройств перечислен в этом разделе, потому что они "
+"были добавлены до осознания OEM-характера бизнеса PC Card. При добавлении "
+"новых устройств предпочтительнее использовать числовой метод."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:72
+#, no-wrap
+msgid "Format of [.filename]#pccarddevs#"
+msgstr "Формат файла [.filename]#pccarddevs#"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:75
+msgid ""
+"There are four sections in the [.filename]#pccarddevs# files. The first "
+"section lists the manufacturer numbers for vendors that use them. This "
+"section is sorted in numerical order. The next section has all of the "
+"products that are used by these vendors, along with their product ID numbers "
+"and a description string. The description string typically is not used "
+"(instead we set the device's description based on the human readable CIS, "
+"even if we match on the numeric version). These two sections are then "
+"repeated for devices that use the string matching method. Finally, C-style "
+"comments enclosed in `/*` and `*/` characters are allowed anywhere in the "
+"file."
+msgstr ""
+"В файле [.filename]#pccarddevs# есть четыре раздела. Первый раздел содержит "
+"номера производителей для вендоров, которые их используют. Этот раздел "
+"отсортирован в числовом порядке. Следующий раздел включает все продукты, "
+"используемые этими вендорами, вместе с их идентификаторами продуктов и "
+"строкой описания. Строка описания обычно не используется (вместо этого мы "
+"устанавливаем описание устройства на основе читаемого CIS, даже если "
+"совпадение найдено по числовому идентификатору). Затем эти два раздела "
+"повторяются для устройств, использующих метод строкового сопоставления. "
+"Наконец, C-подобные комментарии, заключенные между символами `/*` и `*/`, "
+"допускаются в любом месте файла."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:77
+msgid ""
+"The first section of the file contains the vendor IDs. Please keep this list "
+"sorted in numeric order. Also, please coordinate changes to this file "
+"because we share it with NetBSD to help facilitate a common clearing house "
+"for this information. For example, here are the first few vendor IDs:"
+msgstr ""
+"Первая часть файла содержит идентификаторы производителей. Пожалуйста, "
+"сохраняйте этот список в числовом порядке. Также, пожалуйста, согласовывайте "
+"изменения в этом файле, так как мы делимся им с NetBSD для создания общего "
+"центра обработки этой информации. Например, вот первые несколько "
+"идентификаторов производителей:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:84
+#, no-wrap
+msgid ""
+"vendor FUJITSU\t\t\t0x0004 Fujitsu Corporation\n"
+"vendor NETGEAR_2\t\t0x000b Netgear\n"
+"vendor PANASONIC\t\t0x0032\tMatsushita Electric Industrial Co.\n"
+"vendor SANDISK\t\t\t0x0045\tSandisk Corporation\n"
+msgstr ""
+"vendor FUJITSU\t\t\t0x0004 Fujitsu Corporation\n"
+"vendor NETGEAR_2\t\t0x000b Netgear\n"
+"vendor PANASONIC\t\t0x0032\tMatsushita Electric Industrial Co.\n"
+"vendor SANDISK\t\t\t0x0045\tSandisk Corporation\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:87
+msgid ""
+"Chances are very good that the `NETGEAR_2` entry is really an OEM that "
+"NETGEAR purchased cards from and the author of support for those cards was "
+"unaware at the time that Netgear was using someone else's ID. These entries "
+"are fairly straightforward. The vendor keyword denotes the kind of line that "
+"this is, followed by the name of the vendor. This name will be repeated "
+"later in [.filename]#pccarddevs#, as well as used in the driver's match "
+"tables, so keep it short and a valid C identifier. A numeric ID in hex "
+"identifies the manufacturer. Do not add IDs of the form `0xffffffff` or "
+"`0xffff` because these are reserved IDs (the former is \"no ID set\" while "
+"the latter is sometimes seen in extremely poor quality cards to try to "
+"indicate \"none\"). Finally there is a string description of the company "
+"that makes the card. This string is not used in FreeBSD for anything but "
+"commentary purposes."
+msgstr ""
+"Вероятно, запись `NETGEAR_2` на самом деле относится к OEM-производителю, у "
+"которого NETGEAR приобретал карты, и автор поддержки этих карт не знал на "
+"тот момент, что Netgear использовал чужой идентификатор. Эти записи довольно "
+"просты. Ключевое слово `vendor` обозначает тип строки, за которым следует "
+"название производителя. Это название будет повторяться позже в "
+"[.filename]#pccarddevs#, а также использоваться в таблицах соответствия "
+"драйверов, поэтому оно должно быть коротким и допустимым идентификатором в "
+"C. Числовой идентификатор в шестнадцатеричном формате указывает "
+"производителя. Не добавляйте идентификаторы вида `0xffffffff` или `0xffff`, "
+"так как они зарезервированы (первый означает \"идентификатор не "
+"установлен\", а второй иногда встречается в крайне некачественных картах для "
+"указания \"отсутствует\"). Наконец, следует строковое описание компании, "
+"производящей карту. Эта строка в FreeBSD ни для чего не используется, кроме "
+"как в комментариях."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:89
+msgid ""
+"The second section of the file contains the products. As shown in this "
+"example, the format is similar to the vendor lines:"
+msgstr ""
+"Вторая секция файла содержит продукты. Как показано в этом примере, формат "
+"аналогичен строкам поставщиков:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:94
+#, no-wrap
+msgid ""
+"/* Allied Telesis K.K. */\n"
+"product ALLIEDTELESIS LA_PCM\t0x0002 Allied Telesis LA-PCM\n"
+msgstr ""
+"/* Allied Telesis K.K. */\n"
+"product ALLIEDTELESIS LA_PCM\t0x0002 Allied Telesis LA-PCM\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:97
+#, no-wrap
+msgid ""
+"/* Archos */\n"
+"product\tARCHOS ARC_ATAPI\t0x0043 MiniCD\n"
+msgstr ""
+"/* Archos */\n"
+"product\tARCHOS ARC_ATAPI\t0x0043 MiniCD\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:100
+msgid ""
+"The `product` keyword is followed by the vendor name, repeated from above. "
+"This is followed by the product name, which is used by the driver and should "
+"be a valid C identifier, but may also start with a number. As with the "
+"vendors, the hex product ID for this card follows the same convention for "
+"`0xffffffff` and `0xffff`. Finally, there is a string description of the "
+"device itself. This string typically is not used in FreeBSD, since FreeBSD's "
+"pccard bus driver will construct a string from the human readable CIS "
+"entries, but it can be used in the rare cases where this is somehow "
+"insufficient. The products are in alphabetical order by manufacturer, then "
+"numerical order by product ID. They have a C comment before each "
+"manufacturer's entries and there is a blank line between entries."
+msgstr ""
+"Ключевое слово `product` следует за именем производителя, повторяющимся "
+"сверху. После него идет название продукта, которое используется драйвером и "
+"должно быть допустимым идентификатором в C, но также может начинаться с "
+"цифры. Как и в случае с производителями, шестнадцатеричный идентификатор "
+"продукта для этой карты следует тем же соглашениям для `0xffffffff` и "
+"`0xffff`. Наконец, идет строковое описание самого устройства. Эта строка "
+"обычно не используется в FreeBSD, поскольку драйвер шины pccard в FreeBSD "
+"формирует строку из читаемых человеком записей CIS, но она может быть "
+"использована в редких случаях, когда этого недостаточно. Продукты "
+"перечислены в алфавитном порядке по производителю, затем в числовом порядке "
+"по идентификатору продукта. Перед записями каждого производителя есть "
+"комментарий в C, а между записями — пустая строка."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:102
+msgid ""
+"The third section is like the previous vendor section, but with all of the "
+"manufacturer numeric IDs set to `-1`, meaning \"match anything found\" in "
+"the FreeBSD pccard bus code. Since these are C identifiers, their names must "
+"be unique. Otherwise the format is identical to the first section of the "
+"file."
+msgstr ""
+"Третий раздел аналогичен предыдущему разделу производителей, но все числовые "
+"идентификаторы производителей установлены в `-1`, что означает \"совпадение "
+"с любым найденным\" в коде шины pccard FreeBSD. Поскольку это идентификаторы "
+"C, их имена должны быть уникальными. В остальном формат идентичен первому "
+"разделу файла."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:104
+msgid ""
+"The final section contains the entries for those cards that must be "
+"identified by string entries. This section's format is a little different "
+"from the generic section:"
+msgstr ""
+"Последний раздел содержит записи для тех карт, которые должны быть "
+"идентифицированы по строковым значениям. Формат этого раздела немного "
+"отличается от общего раздела:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:109
+#, no-wrap
+msgid ""
+"product ADDTRON AWP100\t\t{ \"Addtron\", \"AWP-100&spWireless&spPCMCIA\", \"Version&sp01.02\", NULL }\n"
+"product ALLIEDTELESIS WR211PCM\t{ \"Allied&spTelesis&spK.K.\", \"WR211PCM\", NULL, NULL } Allied Telesis WR211PCM\n"
+msgstr ""
+"product ADDTRON AWP100\t\t{ \"Addtron\", \"AWP-100&spWireless&spPCMCIA\", \"Version&sp01.02\", NULL }\n"
+"product ALLIEDTELESIS WR211PCM\t{ \"Allied&spTelesis&spK.K.\", \"WR211PCM\", NULL, NULL } Allied Telesis WR211PCM\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:112
+msgid ""
+"The familiar `product` keyword is followed by the vendor name and the card "
+"name, just as in the second section of the file. Here the format deviates "
+"from that used earlier. There is a {} grouping, followed by a number of "
+"strings. These strings correspond to the vendor, product, and extra "
+"information that is defined in a CIS_INFO tuple. These strings are filtered "
+"by the program that generates [.filename]#pccarddevs.h# to replace &sp with "
+"a real space. NULL strings mean that the corresponding part of the entry "
+"should be ignored. The example shown here contains a bad entry. It should "
+"not contain the version number unless that is critical for the operation of "
+"the card. Sometimes vendors will have many different versions of the card in "
+"the field that all work, in which case that information only makes it harder "
+"for someone with a similar card to use it with FreeBSD. Sometimes it is "
+"necessary when a vendor wishes to sell many different parts under the same "
+"brand due to market considerations (availability, price, and so forth). Then "
+"it can be critical to disambiguating the card in those rare cases where the "
+"vendor kept the same manufacturer/product pair. Regular expression matching "
+"is not available at this time."
+msgstr ""
+"Знакомое ключевое слово `product` сопровождается названием производителя и "
+"именем карты, как и во втором разделе файла. Здесь формат отличается от "
+"использованного ранее. Идёт группировка {}, за которой следует несколько "
+"строк. Эти строки соответствуют производителю, продукту и дополнительной "
+"информации, определённой в кортеже CIS_INFO. Эти строки фильтруются "
+"программой, которая генерирует [.filename]#pccarddevs.h#, чтобы заменить &sp "
+"на реальный пробел. Строки NULL означают, что соответствующую часть записи "
+"следует игнорировать. В приведённом здесь примере есть некорректная запись. "
+"Она не должна содержать номер версии, если только он не критичен для работы "
+"карты. Иногда у производителей может быть множество различных версий карты в "
+"обращении, которые все работают, и в таком случае эта информация только "
+"затрудняет использование аналогичной карты с FreeBSD. Иногда это необходимо, "
+"когда производитель хочет продавать множество различных компонентов под "
+"одним брендом из-за рыночных соображений (доступность, цена и т. д.). Тогда "
+"это может быть критично для различения карты в тех редких случаях, когда "
+"производитель сохранил ту же пару производитель/продукт. На данный момент "
+"использование регулярных выражений для сопоставления недоступно."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:114
+#, no-wrap
+msgid "Sample Probe Routine"
+msgstr "Пример процедуры обнаружения"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:117
+msgid ""
+"To understand how to add a device to the list of supported devices, one must "
+"understand the probe and/or match routines that many drivers have. It is "
+"complicated a little in FreeBSD 5.x because there is a compatibility layer "
+"for OLDCARD present as well. Since only the window-dressing is different, an "
+"idealized version will be presented here."
+msgstr ""
+"Чтобы понять, как добавить устройство в список поддерживаемых, необходимо "
+"разобраться в процедурах `probe` (обнаружение) и/или `match` "
+"(сопоставление), которые есть во многих драйверах. В FreeBSD 5.x это немного "
+"сложнее из-за наличия слоя совместимости с OLDCARD. Поскольку различия лишь "
+"косметические, здесь будет представлена идеализированная версия."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:127
+#, no-wrap
+msgid ""
+"static const struct pccard_product wi_pccard_products[] = {\n"
+"\tPCMCIA_CARD(3COM, 3CRWE737A, 0),\n"
+"\tPCMCIA_CARD(BUFFALO, WLI_PCM_S11, 0),\n"
+"\tPCMCIA_CARD(BUFFALO, WLI_CF_S11G, 0),\n"
+"\tPCMCIA_CARD(TDK, LAK_CD011WL, 0),\n"
+"\t{ NULL }\n"
+"};\n"
+msgstr ""
+"static const struct pccard_product wi_pccard_products[] = {\n"
+"\tPCMCIA_CARD(3COM, 3CRWE737A, 0),\n"
+"\tPCMCIA_CARD(BUFFALO, WLI_PCM_S11, 0),\n"
+"\tPCMCIA_CARD(BUFFALO, WLI_CF_S11G, 0),\n"
+"\tPCMCIA_CARD(TDK, LAK_CD011WL, 0),\n"
+"\t{ NULL }\n"
+"};\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:133
+#, no-wrap
+msgid ""
+"static int\n"
+"wi_pccard_probe(dev)\n"
+"\tdevice_t\tdev;\n"
+"{\n"
+"\tconst struct pccard_product *pp;\n"
+msgstr ""
+"static int\n"
+"wi_pccard_probe(dev)\n"
+"\tdevice_t\tdev;\n"
+"{\n"
+"\tconst struct pccard_product *pp;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:142
+#, no-wrap
+msgid ""
+"\tif ((pp = pccard_product_lookup(dev, wi_pccard_products,\n"
+"\t sizeof(wi_pccard_products[0]), NULL)) != NULL) {\n"
+"\t\tif (pp->pp_name != NULL)\n"
+"\t\t\tdevice_set_desc(dev, pp->pp_name);\n"
+"\t\treturn (0);\n"
+"\t}\n"
+"\treturn (ENXIO);\n"
+"}\n"
+msgstr ""
+"\tif ((pp = pccard_product_lookup(dev, wi_pccard_products,\n"
+"\t sizeof(wi_pccard_products[0]), NULL)) != NULL) {\n"
+"\t\tif (pp->pp_name != NULL)\n"
+"\t\t\tdevice_set_desc(dev, pp->pp_name);\n"
+"\t\treturn (0);\n"
+"\t}\n"
+"\treturn (ENXIO);\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:145
+msgid ""
+"Here we have a simple pccard probe routine that matches a few devices. As "
+"stated above, the name may vary (if it is not `foo_pccard_probe()` it will "
+"be `foo_pccard_match()`). The function `pccard_product_lookup()` is a "
+"generalized function that walks the table and returns a pointer to the first "
+"entry that it matches. Some drivers may use this mechanism to convey "
+"additional information about some cards to the rest of the driver, so there "
+"may be some variance in the table. The only requirement is that each row of "
+"the table must have a `struct pccard_product` as the first element."
+msgstr ""
+"Вот простая процедура проверки pccard, которая соответствует нескольким "
+"устройствам. Как упоминалось выше, название может отличаться (если это не "
+"`foo_pccard_probe()`, то это будет `foo_pccard_match()`). Функция "
+"`pccard_product_lookup()` является обобщенной функцией, которая проходит по "
+"таблице и возвращает указатель на первую запись, которой соответствует. "
+"Некоторые драйверы могут использовать этот механизм для передачи "
+"дополнительной информации о некоторых картах остальной части драйвера, "
+"поэтому в таблице могут быть вариации. Единственное требование — каждая "
+"строка таблицы должна содержать `struct pccard_product` в качестве первого "
+"элемента."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:147
+msgid ""
+"Looking at the table `wi_pccard_products`, one notices that all the entries "
+"are of the form `PCMCIA_CARD(_foo_, _bar_, _baz_)`. The _foo_ part is the "
+"manufacturer ID from [.filename]#pccarddevs#. The _bar_ part is the product "
+"ID. _baz_ is the expected function number for this card. Many pccards can "
+"have multiple functions, and some way to disambiguate function 1 from "
+"function 0 is needed. You may see `PCMCIA_CARD_D`, which includes the device "
+"description from [.filename]#pccarddevs#. You may also see `PCMCIA_CARD2` "
+"and `PCMCIA_CARD2_D` which are used when you need to match both CIS strings "
+"and manufacturer numbers, in the \"use the default description\" and \"take "
+"the description from pccarddevs\" flavors."
+msgstr ""
+"Рассматривая таблицу `wi_pccard_products`, можно заметить, что все записи "
+"имеют вид `PCMCIA_CARD(_foo_, _bar_, _baz_)`. Часть _foo_ — это "
+"идентификатор производителя из [.filename]#pccarddevs#. Часть _bar_ — это "
+"идентификатор продукта. _baz_ — ожидаемый номер функции для этой карты. "
+"Многие pccard-устройства могут иметь несколько функций, поэтому требуется "
+"способ различать функцию 1 и функцию 0. Вы можете встретить `PCMCIA_CARD_D`, "
+"который включает описание устройства из [.filename]#pccarddevs#. Также могут "
+"встречаться `PCMCIA_CARD2` и `PCMCIA_CARD2_D`, которые используются, когда "
+"необходимо сопоставить как строки CIS, так и номера производителей, в "
+"вариантах «использовать описание по умолчанию» и «взять описание из "
+"pccarddevs»."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:149
+#, no-wrap
+msgid "Putting it All Together"
+msgstr "Собираем все вместе"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:152
+msgid ""
+"To add a new device, one must first obtain the identification information "
+"from the device. The easiest way to do this is to insert the device into a "
+"PC Card or CF slot and issue `devinfo -v`. Sample output:"
+msgstr ""
+"Для добавления нового устройства необходимо сначала получить "
+"идентификационную информацию от устройства. Проще всего это сделать, вставив "
+"устройство в слот PC Card или CF и выполнив команду `devinfo -v`. Пример "
+"вывода:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:159
+#, no-wrap
+msgid ""
+" cbb1 pnpinfo vendor=0x104c device=0xac51 subvendor=0x1265 subdevice=0x0300 class=0x060700 at slot=10 function=1\n"
+" cardbus1\n"
+" pccard1\n"
+" unknown pnpinfo manufacturer=0x026f product=0x030c cisvendor=\"BUFFALO\" cisproduct=\"WLI2-CF-S11\" function_type=6 at function=0\n"
+msgstr ""
+" cbb1 pnpinfo vendor=0x104c device=0xac51 subvendor=0x1265 subdevice=0x0300 class=0x060700 at slot=10 function=1\n"
+" cardbus1\n"
+" pccard1\n"
+" unknown pnpinfo manufacturer=0x026f product=0x030c cisvendor=\"BUFFALO\" cisproduct=\"WLI2-CF-S11\" function_type=6 at function=0\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:162
+msgid ""
+"`manufacturer` and `product` are the numeric IDs for this product, while "
+"`cisvendor` and `cisproduct` are the product description strings from the "
+"CIS."
+msgstr ""
+"`manufacturer` и `product` являются числовыми идентификаторами данного "
+"продукта, в то время как `cisvendor` и `cisproduct` представляют собой "
+"строки описания продукта из CIS."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:164
+msgid ""
+"Since we first want to prefer the numeric option, first try to construct an "
+"entry based on that. The above card has been slightly fictionalized for the "
+"purpose of this example. The vendor is BUFFALO, which we see already has an "
+"entry:"
+msgstr ""
+"Поскольку мы сначала хотим предпочесть числовой вариант, попробуем сначала "
+"создать запись на его основе. Приведённая выше карта была слегка изменена "
+"для целей данного примера. Производитель — BUFFALO, у которого, как мы "
+"видим, уже есть запись:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:168
+#, no-wrap
+msgid "vendor BUFFALO\t\t\t0x026f\tBUFFALO (Melco Corporation)\n"
+msgstr "vendor BUFFALO\t\t\t0x026f\tBUFFALO (Melco Corporation)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:171
+msgid "But there is no entry for this particular card. Instead we find:"
+msgstr "Но нет записи для этой конкретной карты. Вместо этого мы видим:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:179
+#, no-wrap
+msgid ""
+"/* BUFFALO */\n"
+"product BUFFALO WLI_PCM_S11\t0x0305\tBUFFALO AirStation 11Mbps WLAN\n"
+"product BUFFALO LPC_CF_CLT\t0x0307\tBUFFALO LPC-CF-CLT\n"
+"product\tBUFFALO\tLPC3_CLT\t0x030a\tBUFFALO LPC3-CLT Ethernet Adapter\n"
+"product BUFFALO WLI_CF_S11G\t0x030b\tBUFFALO AirStation 11Mbps CF WLAN\n"
+msgstr ""
+"/* BUFFALO */\n"
+"product BUFFALO WLI_PCM_S11\t0x0305\tBUFFALO AirStation 11Mbps WLAN\n"
+"product BUFFALO LPC_CF_CLT\t0x0307\tBUFFALO LPC-CF-CLT\n"
+"product\tBUFFALO\tLPC3_CLT\t0x030a\tBUFFALO LPC3-CLT Ethernet Adapter\n"
+"product BUFFALO WLI_CF_S11G\t0x030b\tBUFFALO AirStation 11Mbps CF WLAN\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:182
+msgid ""
+"To add the device, we can just add this entry to [.filename]#pccarddevs#:"
+msgstr ""
+"Чтобы добавить устройство, мы можем просто добавить эту запись в "
+"[.filename]#pccarddevs#:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:186
+#, no-wrap
+msgid "product BUFFALO WLI2_CF_S11G\t0x030c\tBUFFALO AirStation ultra 802.11b CF\n"
+msgstr "product BUFFALO WLI2_CF_S11G\t0x030c\tBUFFALO AirStation ultra 802.11b CF\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:189
+msgid ""
+"Once these steps are complete, the card can be added to the driver. That is "
+"a simple operation of adding one line:"
+msgstr ""
+"После выполнения этих шагов карту можно добавить в драйвер. Это простая "
+"операция добавления одной строки:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:200
+#, no-wrap
+msgid ""
+"static const struct pccard_product wi_pccard_products[] = {\n"
+"\tPCMCIA_CARD(3COM, 3CRWE737A, 0),\n"
+"\tPCMCIA_CARD(BUFFALO, WLI_PCM_S11, 0),\n"
+"\tPCMCIA_CARD(BUFFALO, WLI_CF_S11G, 0),\n"
+"+\tPCMCIA_CARD(BUFFALO, WLI_CF2_S11G, 0),\n"
+"\tPCMCIA_CARD(TDK, LAK_CD011WL, 0),\n"
+"\t{ NULL }\n"
+"};\n"
+msgstr ""
+"static const struct pccard_product wi_pccard_products[] = {\n"
+"\tPCMCIA_CARD(3COM, 3CRWE737A, 0),\n"
+"\tPCMCIA_CARD(BUFFALO, WLI_PCM_S11, 0),\n"
+"\tPCMCIA_CARD(BUFFALO, WLI_CF_S11G, 0),\n"
+"+\tPCMCIA_CARD(BUFFALO, WLI_CF2_S11G, 0),\n"
+"\tPCMCIA_CARD(TDK, LAK_CD011WL, 0),\n"
+"\t{ NULL }\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:203
+msgid ""
+"Note that I have included a '`+`' in the line before the line that I added, "
+"but that is simply to highlight the line. Do not add it to the actual "
+"driver. Once you have added the line, you can recompile your kernel or "
+"module and test it. If the device is recognized and works, please submit a "
+"patch. If it does not work, please figure out what is needed to make it work "
+"and submit a patch. If the device is not recognized at all, you have done "
+"something wrong and should recheck each step."
+msgstr ""
+"Обратите внимание, что я добавил символ '`+`' перед строкой, которую "
+"добавил, но это только для выделения строки. Не добавляйте его в реальный "
+"драйвер. После добавления строки вы можете пересобрать ядро или модуль и "
+"протестировать его. Если устройство распознано и работает, отправьте патч. "
+"Если оно не работает, определите, что необходимо для его работы, и отправьте "
+"патч. Если устройство не распознаётся вообще, вы где-то ошиблись и следует "
+"перепроверить каждый шаг."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:205
+msgid ""
+"If you are a FreeBSD src committer, and everything appears to be working, "
+"then you can commit the changes to the tree. However, there are some minor "
+"tricky things to be considered. [.filename]#pccarddevs# must be committed to "
+"the tree first. Then [.filename]#pccarddevs.h# must be regenerated and "
+"committed as a second step, ensuring that the right $FreeBSD$ tag is in the "
+"latter file. Finally, commit the additions to the driver."
+msgstr ""
+"Если вы коммиттер исходного кода FreeBSD, и всё работает корректно, то "
+"можете закоммитить изменения в дерево. Однако есть несколько небольших "
+"нюансов, которые следует учесть. [.filename]#pccarddevs# должен быть "
+"закоммичен в дерево первым. Затем [.filename]#pccarddevs.h# необходимо "
+"перегенерировать и закоммитить вторым шагом, убедившись, что правильный тег "
+"$FreeBSD$ присутствует в последнем файле. В конце закоммитьте добавления в "
+"драйвер."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:207
+#, no-wrap
+msgid "Submitting a New Device"
+msgstr "Отправка кода для нового устройства"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pccard/_index.adoc:209
+msgid ""
+"Please do not send entries for new devices to the author directly. Instead, "
+"submit them as a PR and send the author the PR number for his records. This "
+"ensures that entries are not lost. When submitting a PR, it is unnecessary "
+"to include the [.filename]#pccardevs.h# diffs in the patch, since those will "
+"be regenerated. It is necessary to include a description of the device, as "
+"well as the patches to the client driver. If you do not know the name, use "
+"OEM99 as the name, and the author will adjust OEM99 accordingly after "
+"investigation. Committers should not commit OEM99, but instead find the "
+"highest OEM entry and commit one more than that."
+msgstr ""
+"Пожалуйста, не отправляйте записи о новых устройствах автору напрямую. "
+"Вместо этого оформите их как PR и сообщите автору номер PR для учета. Это "
+"гарантирует, что записи не будут потеряны. При отправке PR нет необходимости "
+"включать в патч diff-файлы [.filename]#pccardevs.h#, так как они будут "
+"перегенерированы. Однако необходимо включить описание устройства, а также "
+"патчи для клиентского драйвера. Если название устройства неизвестно, "
+"используйте имя OEM99, и автор скорректирует OEM99 после изучения. "
+"Коммиттеры не должны коммитить OEM99, а вместо этого найти наибольший OEM-"
+"номер и закоммитить на единицу больше."
diff --git a/documentation/content/ru/books/arch-handbook/pci/_index.adoc b/documentation/content/ru/books/arch-handbook/pci/_index.adoc
new file mode 100644
index 0000000000..cf9dffb85e
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/pci/_index.adoc
@@ -0,0 +1,424 @@
+---
+description: 'Устройства PCI'
+next: books/arch-handbook/scsi
+params:
+ path: /books/arch-handbook/pci/
+prev: books/arch-handbook/isa
+showBookMenu: true
+tags: ["PCI", "Devices", "example", "guide"]
+title: 'Глава 11. Устройства PCI'
+weight: 13
+---
+
+[[pci]]
+= Устройства PCI
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 11
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+Эта глава расскажет о механизмах FreeBSD для написания драйвера устройства на шине PCI.
+
+[[pci-probe]]
+== Обнаружение и подключение
+
+Информация о том, как код шины PCI перебирает неприсоединённые устройства и проверяет, сможет ли только что загруженный kld присоединиться к любому из них.
+
+=== Пример исходного кода драйвера ([.filename]#mypci.c#)
+
+[.programlisting]
+....
+/*
+ * Simple KLD to play with the PCI functions.
+ *
+ * Murray Stokely
+ */
+
+#include <sys/param.h> /* defines used in kernel.h */
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/kernel.h> /* types used in module initialization */
+#include <sys/conf.h> /* cdevsw struct */
+#include <sys/uio.h> /* uio struct */
+#include <sys/malloc.h>
+#include <sys/bus.h> /* structs, prototypes for pci bus stuff and DEVMETHOD macros! */
+
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcivar.h> /* For pci_get macros! */
+#include <dev/pci/pcireg.h>
+
+/* The softc holds our per-instance data. */
+struct mypci_softc {
+ device_t my_dev;
+ struct cdev *my_cdev;
+};
+
+/* Function prototypes */
+static d_open_t mypci_open;
+static d_close_t mypci_close;
+static d_read_t mypci_read;
+static d_write_t mypci_write;
+
+/* Character device entry points */
+
+static struct cdevsw mypci_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = mypci_open,
+ .d_close = mypci_close,
+ .d_read = mypci_read,
+ .d_write = mypci_write,
+ .d_name = "mypci",
+};
+
+/*
+ * In the cdevsw routines, we find our softc by using the si_drv1 member
+ * of struct cdev. We set this variable to point to our softc in our
+ * attach routine when we create the /dev entry.
+ */
+
+int
+mypci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+ struct mypci_softc *sc;
+
+ /* Look up our softc. */
+ sc = dev->si_drv1;
+ device_printf(sc->my_dev, "Opened successfully.\n");
+ return (0);
+}
+
+int
+mypci_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
+{
+ struct mypci_softc *sc;
+
+ /* Look up our softc. */
+ sc = dev->si_drv1;
+ device_printf(sc->my_dev, "Closed.\n");
+ return (0);
+}
+
+int
+mypci_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct mypci_softc *sc;
+
+ /* Look up our softc. */
+ sc = dev->si_drv1;
+ device_printf(sc->my_dev, "Asked to read %zd bytes.\n", uio->uio_resid);
+ return (0);
+}
+
+int
+mypci_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct mypci_softc *sc;
+
+ /* Look up our softc. */
+ sc = dev->si_drv1;
+ device_printf(sc->my_dev, "Asked to write %zd bytes.\n", uio->uio_resid);
+ return (0);
+}
+
+/* PCI Support Functions */
+
+/*
+ * Compare the device ID of this device against the IDs that this driver
+ * supports. If there is a match, set the description and return success.
+ */
+static int
+mypci_probe(device_t dev)
+{
+
+ device_printf(dev, "MyPCI Probe\nVendor ID : 0x%x\nDevice ID : 0x%x\n",
+ pci_get_vendor(dev), pci_get_device(dev));
+
+ if (pci_get_vendor(dev) == 0x11c1) {
+ printf("We've got the Winmodem, probe successful!\n");
+ device_set_desc(dev, "WinModem");
+ return (BUS_PROBE_DEFAULT);
+ }
+ return (ENXIO);
+}
+
+/* Attach function is only called if the probe is successful. */
+
+static int
+mypci_attach(device_t dev)
+{
+ struct mypci_softc *sc;
+
+ printf("MyPCI Attach for : deviceID : 0x%x\n", pci_get_devid(dev));
+
+ /* Look up our softc and initialize its fields. */
+ sc = device_get_softc(dev);
+ sc->my_dev = dev;
+
+ /*
+ * Create a /dev entry for this device. The kernel will assign us
+ * a major number automatically. We use the unit number of this
+ * device as the minor number and name the character device
+ * "mypci<unit>".
+ */
+ sc->my_cdev = make_dev(&mypci_cdevsw, device_get_unit(dev),
+ UID_ROOT, GID_WHEEL, 0600, "mypci%u", device_get_unit(dev));
+ sc->my_cdev->si_drv1 = sc;
+ printf("Mypci device loaded.\n");
+ return (0);
+}
+
+/* Detach device. */
+
+static int
+mypci_detach(device_t dev)
+{
+ struct mypci_softc *sc;
+
+ /* Teardown the state in our softc created in our attach routine. */
+ sc = device_get_softc(dev);
+ destroy_dev(sc->my_cdev);
+ printf("Mypci detach!\n");
+ return (0);
+}
+
+/* Called during system shutdown after sync. */
+
+static int
+mypci_shutdown(device_t dev)
+{
+
+ printf("Mypci shutdown!\n");
+ return (0);
+}
+
+/*
+ * Device suspend routine.
+ */
+static int
+mypci_suspend(device_t dev)
+{
+
+ printf("Mypci suspend!\n");
+ return (0);
+}
+
+/*
+ * Device resume routine.
+ */
+static int
+mypci_resume(device_t dev)
+{
+
+ printf("Mypci resume!\n");
+ return (0);
+}
+
+static device_method_t mypci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mypci_probe),
+ DEVMETHOD(device_attach, mypci_attach),
+ DEVMETHOD(device_detach, mypci_detach),
+ DEVMETHOD(device_shutdown, mypci_shutdown),
+ DEVMETHOD(device_suspend, mypci_suspend),
+ DEVMETHOD(device_resume, mypci_resume),
+
+ DEVMETHOD_END
+};
+
+static devclass_t mypci_devclass;
+
+DEFINE_CLASS_0(mypci, mypci_driver, mypci_methods, sizeof(struct mypci_softc));
+DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);
+....
+
+=== [.filename]#Makefile# для примера драйвера
+
+[.programlisting]
+....
+# Makefile for mypci driver
+
+KMOD= mypci
+SRCS= mypci.c
+SRCS+= device_if.h bus_if.h pci_if.h
+
+.include <bsd.kmod.mk>
+....
+
+Если вы поместите исходный файл выше и [.filename]#Makefile# в каталог, вы можете запустить `make` для компиляции примера драйвера. Дополнительно можно выполнить `make load` для загрузки драйвера в текущее ядро и `make unload` для выгрузки драйвера после его загрузки.
+
+=== Дополнительные ресурсы
+
+* http://www.pcisig.org/[Группа по стандартам PCI]
+* PCI System Architecture, Fourth Edition by Tom Shanley, et al.
+
+[[pci-bus]]
+== Ресурсы шины
+
+FreeBSD предоставляет объектно-ориентированный механизм для запроса ресурсов от родительской шины. Почти все устройства будут дочерними элементами какого-либо типа шины (PCI, ISA, USB, SCSI и т.д.), и этим устройствам необходимо получать ресурсы от своей родительской шины (такие как сегменты памяти, линии прерываний или каналы DMA).
+
+=== Регистры базовых адресов
+
+Для выполнения каких-либо полезных действий с устройством PCI необходимо получить _регистры базовых адресов_ (BAR) из конфигурационного пространства PCI. Специфичные для PCI детали получения BAR абстрагированы в функции `bus_alloc_resource()`.
+
+Например, типичный драйвер может содержать что-то подобное в функции `attach()`:
+
+[.programlisting]
+....
+ sc->bar0id = PCIR_BAR(0);
+ sc->bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->bar0id,
+ 0, ~0, 1, RF_ACTIVE);
+ if (sc->bar0res == NULL) {
+ printf("Memory allocation of PCI base register 0 failed!\n");
+ error = ENXIO;
+ goto fail1;
+ }
+
+ sc->bar1id = PCIR_BAR(1);
+ sc->bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->bar1id,
+ 0, ~0, 1, RF_ACTIVE);
+ if (sc->bar1res == NULL) {
+ printf("Memory allocation of PCI base register 1 failed!\n");
+ error = ENXIO;
+ goto fail2;
+ }
+ sc->bar0_bt = rman_get_bustag(sc->bar0res);
+ sc->bar0_bh = rman_get_bushandle(sc->bar0res);
+ sc->bar1_bt = rman_get_bustag(sc->bar1res);
+ sc->bar1_bh = rman_get_bushandle(sc->bar1res);
+....
+
+Дескрипторы для каждого регистра базовых адресов хранятся в структуре `softc`, чтобы их можно было использовать для записи на устройство в дальнейшем.
+
+Эти дескрипторы затем могут быть использованы для чтения или записи из регистров устройства с помощью функций `bus_space_*`. Например, драйвер может содержать сокращённую функцию для чтения из специфичного для платы регистра, как показано ниже:
+
+[.programlisting]
+....
+uint16_t
+board_read(struct ni_softc *sc, uint16_t address)
+{
+ return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);
+}
+....
+
+Аналогично, можно записать в регистры с помощью:
+
+[.programlisting]
+....
+void
+board_write(struct ni_softc *sc, uint16_t address, uint16_t value)
+{
+ bus_space_write_2(sc->bar1_bt, sc->bar1_bh, address, value);
+}
+....
+
+Эти функции существуют в 8-битных, 16-битных и 32-битных версиях, и вам следует использовать `bus_space_{read|write}_{1|2|4}` соответственно.
+
+[NOTE]
+====
+В FreeBSD 7.0 и более поздних версиях вы можете использовать функции `bus_*` вместо `bus_space_*`. Функции `bus_*` принимают указатель на структуру resource * вместо тега шины и дескриптора. Таким образом, вы можете удалить тег шины и дескриптор шины из `softc` и переписать функцию `board_read()` как:
+
+[.programlisting]
+....
+uint16_t
+board_read(struct ni_softc *sc, uint16_t address)
+{
+ return (bus_read(sc->bar1res, address));
+}
+....
+
+====
+
+=== Прерывания
+
+Прерывания выделяются объектно-ориентированным кодом шины аналогично ресурсам памяти. Сначала ресурс IRQ должен быть выделен из родительской шины, а затем должен быть настроен обработчик прерывания для работы с этим IRQ.
+
+Вот пример из функции `attach()` устройства, который скажет больше, чем слова.
+
+[.programlisting]
+....
+/* Get the IRQ resource */
+
+ sc->irqid = 0x0;
+ sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &(sc->irqid),
+ 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
+ if (sc->irqres == NULL) {
+ printf("IRQ allocation failed!\n");
+ error = ENXIO;
+ goto fail3;
+ }
+
+ /* Now we should set up the interrupt handler */
+
+ error = bus_setup_intr(dev, sc->irqres, INTR_TYPE_MISC,
+ my_handler, sc, &(sc->handler));
+ if (error) {
+ printf("Couldn't set up irq\n");
+ goto fail4;
+ }
+....
+
+Некоторые меры предосторожности должны быть приняты в процедуре отключения драйвера. Необходимо остановить поток прерываний устройства и удалить обработчик прерываний. Как только `bus_teardown_intr()` завершится, можно быть уверенным, что обработчик прерываний больше не будет вызываться и все потоки, которые могли выполнять этот обработчик, завершили работу. Поскольку эта функция может засыпать, нельзя удерживать какие-либо мьютексы при её вызове.
+
+=== DMA
+
+Этот раздел устарел и приведён только в исторических целях. Правильный способ решения этих проблем — использование функций `bus_space_dma*()`. Этот абзац можно удалить, когда раздел будет обновлён с учётом данного подхода. Однако на данный момент API находится в состоянии изменения, поэтому, когда он стабилизируется, будет полезно обновить этот раздел соответствующим образом.
+
+На ПК периферийные устройства, которые хотят использовать DMA с управлением шиной, должны работать с физическими адресами. Это проблема, поскольку FreeBSD использует виртуальную память и работает почти исключительно с виртуальными адресами. К счастью, существует функция `vtophys()`, которая поможет.
+
+[.programlisting]
+....
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#define vtophys(virtual_address) (...)
+....
+
+Однако решение немного отличается на alpha, и на самом деле нам нужна функция под названием `vtobus()`.
+
+[.programlisting]
+....
+#if defined(__alpha__)
+#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
+#else
+#define vtobus(va) vtophys(va)
+#endif
+....
+
+=== Освобождение ресурсов
+
+Очень важно освободить все ресурсы, которые были выделены во время `attach()`. Необходимо внимательно следить за освобождением правильных ресурсов даже в случае ошибки, чтобы система оставалась работоспособной при завершении работы вашего драйвера.
diff --git a/documentation/content/ru/books/arch-handbook/pci/_index.po b/documentation/content/ru/books/arch-handbook/pci/_index.po
new file mode 100644
index 0000000000..9c03c178dd
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/pci/_index.po
@@ -0,0 +1,1071 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-07-03 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookpci_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:14
+#, no-wrap
+msgid "PCI Devices"
+msgstr "Устройства PCI"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:1
+#, no-wrap
+msgid "Chapter 11. PCI Devices"
+msgstr "Глава 11. Устройства PCI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:52
+msgid ""
+"This chapter will talk about the FreeBSD mechanisms for writing a device "
+"driver for a device on a PCI bus."
+msgstr ""
+"Эта глава расскажет о механизмах FreeBSD для написания драйвера устройства "
+"на шине PCI."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:54
+#, no-wrap
+msgid "Probe and Attach"
+msgstr "Обнаружение и подключение"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:57
+msgid ""
+"Information here about how the PCI bus code iterates through the unattached "
+"devices and see if a newly loaded kld will attach to any of them."
+msgstr ""
+"Информация о том, как код шины PCI перебирает неприсоединённые устройства и "
+"проверяет, сможет ли только что загруженный kld присоединиться к любому из "
+"них."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:58
+#, no-wrap
+msgid "Sample Driver Source ([.filename]#mypci.c#)"
+msgstr "Пример исходного кода драйвера ([.filename]#mypci.c#)"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:67
+#, no-wrap
+msgid ""
+"/*\n"
+" * Simple KLD to play with the PCI functions.\n"
+" *\n"
+" * Murray Stokely\n"
+" */\n"
+msgstr ""
+"/*\n"
+" * Simple KLD to play with the PCI functions.\n"
+" *\n"
+" * Murray Stokely\n"
+" */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:77
+#, no-wrap
+msgid ""
+"#include <sys/param.h>\t\t/* defines used in kernel.h */\n"
+"#include <sys/module.h>\n"
+"#include <sys/systm.h>\n"
+"#include <sys/errno.h>\n"
+"#include <sys/kernel.h>\t\t/* types used in module initialization */\n"
+"#include <sys/conf.h>\t\t/* cdevsw struct */\n"
+"#include <sys/uio.h>\t\t/* uio struct */\n"
+"#include <sys/malloc.h>\n"
+"#include <sys/bus.h>\t\t/* structs, prototypes for pci bus stuff and DEVMETHOD macros! */\n"
+msgstr ""
+"#include <sys/param.h>\t\t/* defines used in kernel.h */\n"
+"#include <sys/module.h>\n"
+"#include <sys/systm.h>\n"
+"#include <sys/errno.h>\n"
+"#include <sys/kernel.h>\t\t/* types used in module initialization */\n"
+"#include <sys/conf.h>\t\t/* cdevsw struct */\n"
+"#include <sys/uio.h>\t\t/* uio struct */\n"
+"#include <sys/malloc.h>\n"
+"#include <sys/bus.h>\t\t/* structs, prototypes for pci bus stuff and DEVMETHOD macros! */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:81
+#, no-wrap
+msgid ""
+"#include <machine/bus.h>\n"
+"#include <sys/rman.h>\n"
+"#include <machine/resource.h>\n"
+msgstr ""
+"#include <machine/bus.h>\n"
+"#include <sys/rman.h>\n"
+"#include <machine/resource.h>\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:84
+#, no-wrap
+msgid ""
+"#include <dev/pci/pcivar.h>\t/* For pci_get macros! */\n"
+"#include <dev/pci/pcireg.h>\n"
+msgstr ""
+"#include <dev/pci/pcivar.h>\t/* For pci_get macros! */\n"
+"#include <dev/pci/pcireg.h>\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:90
+#, no-wrap
+msgid ""
+"/* The softc holds our per-instance data. */\n"
+"struct mypci_softc {\n"
+"\tdevice_t\tmy_dev;\n"
+"\tstruct cdev\t*my_cdev;\n"
+"};\n"
+msgstr ""
+"/* The softc holds our per-instance data. */\n"
+"struct mypci_softc {\n"
+"\tdevice_t\tmy_dev;\n"
+"\tstruct cdev\t*my_cdev;\n"
+"};\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:96
+#, no-wrap
+msgid ""
+"/* Function prototypes */\n"
+"static d_open_t\t\tmypci_open;\n"
+"static d_close_t\tmypci_close;\n"
+"static d_read_t\t\tmypci_read;\n"
+"static d_write_t\tmypci_write;\n"
+msgstr ""
+"/* Function prototypes */\n"
+"static d_open_t\t\tmypci_open;\n"
+"static d_close_t\tmypci_close;\n"
+"static d_read_t\t\tmypci_read;\n"
+"static d_write_t\tmypci_write;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:98
+#, no-wrap
+msgid "/* Character device entry points */\n"
+msgstr "/* Character device entry points */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:107
+#, no-wrap
+msgid ""
+"static struct cdevsw mypci_cdevsw = {\n"
+"\t.d_version =\tD_VERSION,\n"
+"\t.d_open =\tmypci_open,\n"
+"\t.d_close =\tmypci_close,\n"
+"\t.d_read =\tmypci_read,\n"
+"\t.d_write =\tmypci_write,\n"
+"\t.d_name =\t\"mypci\",\n"
+"};\n"
+msgstr ""
+"static struct cdevsw mypci_cdevsw = {\n"
+"\t.d_version =\tD_VERSION,\n"
+"\t.d_open =\tmypci_open,\n"
+"\t.d_close =\tmypci_close,\n"
+"\t.d_read =\tmypci_read,\n"
+"\t.d_write =\tmypci_write,\n"
+"\t.d_name =\t\"mypci\",\n"
+"};\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:113
+#, no-wrap
+msgid ""
+"/*\n"
+" * In the cdevsw routines, we find our softc by using the si_drv1 member\n"
+" * of struct cdev. We set this variable to point to our softc in our\n"
+" * attach routine when we create the /dev entry.\n"
+" */\n"
+msgstr ""
+"/*\n"
+" * In the cdevsw routines, we find our softc by using the si_drv1 member\n"
+" * of struct cdev. We set this variable to point to our softc in our\n"
+" * attach routine when we create the /dev entry.\n"
+" */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:118
+#, no-wrap
+msgid ""
+"int\n"
+"mypci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)\n"
+"{\n"
+"\tstruct mypci_softc *sc;\n"
+msgstr ""
+"int\n"
+"mypci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)\n"
+"{\n"
+"\tstruct mypci_softc *sc;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:124
+#, no-wrap
+msgid ""
+"\t/* Look up our softc. */\n"
+"\tsc = dev->si_drv1;\n"
+"\tdevice_printf(sc->my_dev, \"Opened successfully.\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+msgstr ""
+"\t/* Look up our softc. */\n"
+"\tsc = dev->si_drv1;\n"
+"\tdevice_printf(sc->my_dev, \"Opened successfully.\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:129
+#, no-wrap
+msgid ""
+"int\n"
+"mypci_close(struct cdev *dev, int fflag, int devtype, struct thread *td)\n"
+"{\n"
+"\tstruct mypci_softc *sc;\n"
+msgstr ""
+"int\n"
+"mypci_close(struct cdev *dev, int fflag, int devtype, struct thread *td)\n"
+"{\n"
+"\tstruct mypci_softc *sc;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:135
+#, no-wrap
+msgid ""
+"\t/* Look up our softc. */\n"
+"\tsc = dev->si_drv1;\n"
+"\tdevice_printf(sc->my_dev, \"Closed.\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+msgstr ""
+"\t/* Look up our softc. */\n"
+"\tsc = dev->si_drv1;\n"
+"\tdevice_printf(sc->my_dev, \"Closed.\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:140
+#, no-wrap
+msgid ""
+"int\n"
+"mypci_read(struct cdev *dev, struct uio *uio, int ioflag)\n"
+"{\n"
+"\tstruct mypci_softc *sc;\n"
+msgstr ""
+"int\n"
+"mypci_read(struct cdev *dev, struct uio *uio, int ioflag)\n"
+"{\n"
+"\tstruct mypci_softc *sc;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:146
+#, no-wrap
+msgid ""
+"\t/* Look up our softc. */\n"
+"\tsc = dev->si_drv1;\n"
+"\tdevice_printf(sc->my_dev, \"Asked to read %zd bytes.\\n\", uio->uio_resid);\n"
+"\treturn (0);\n"
+"}\n"
+msgstr ""
+"\t/* Look up our softc. */\n"
+"\tsc = dev->si_drv1;\n"
+"\tdevice_printf(sc->my_dev, \"Asked to read %zd bytes.\\n\", uio->uio_resid);\n"
+"\treturn (0);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:151
+#, no-wrap
+msgid ""
+"int\n"
+"mypci_write(struct cdev *dev, struct uio *uio, int ioflag)\n"
+"{\n"
+"\tstruct mypci_softc *sc;\n"
+msgstr ""
+"int\n"
+"mypci_write(struct cdev *dev, struct uio *uio, int ioflag)\n"
+"{\n"
+"\tstruct mypci_softc *sc;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:157
+#, no-wrap
+msgid ""
+"\t/* Look up our softc. */\n"
+"\tsc = dev->si_drv1;\n"
+"\tdevice_printf(sc->my_dev, \"Asked to write %zd bytes.\\n\", uio->uio_resid);\n"
+"\treturn (0);\n"
+"}\n"
+msgstr ""
+"\t/* Look up our softc. */\n"
+"\tsc = dev->si_drv1;\n"
+"\tdevice_printf(sc->my_dev, \"Asked to write %zd bytes.\\n\", uio->uio_resid);\n"
+"\treturn (0);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:159
+#, no-wrap
+msgid "/* PCI Support Functions */\n"
+msgstr "/* PCI Support Functions */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:167
+#, no-wrap
+msgid ""
+"/*\n"
+" * Compare the device ID of this device against the IDs that this driver\n"
+" * supports. If there is a match, set the description and return success.\n"
+" */\n"
+"static int\n"
+"mypci_probe(device_t dev)\n"
+"{\n"
+msgstr ""
+"/*\n"
+" * Compare the device ID of this device against the IDs that this driver\n"
+" * supports. If there is a match, set the description and return success.\n"
+" */\n"
+"static int\n"
+"mypci_probe(device_t dev)\n"
+"{\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:170
+#, no-wrap
+msgid ""
+"\tdevice_printf(dev, \"MyPCI Probe\\nVendor ID : 0x%x\\nDevice ID : 0x%x\\n\",\n"
+"\t pci_get_vendor(dev), pci_get_device(dev));\n"
+msgstr ""
+"\tdevice_printf(dev, \"MyPCI Probe\\nVendor ID : 0x%x\\nDevice ID : 0x%x\\n\",\n"
+"\t pci_get_vendor(dev), pci_get_device(dev));\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:178
+#, no-wrap
+msgid ""
+"\tif (pci_get_vendor(dev) == 0x11c1) {\n"
+"\t\tprintf(\"We've got the Winmodem, probe successful!\\n\");\n"
+"\t\tdevice_set_desc(dev, \"WinModem\");\n"
+"\t\treturn (BUS_PROBE_DEFAULT);\n"
+"\t}\n"
+"\treturn (ENXIO);\n"
+"}\n"
+msgstr ""
+"\tif (pci_get_vendor(dev) == 0x11c1) {\n"
+"\t\tprintf(\"We've got the Winmodem, probe successful!\\n\");\n"
+"\t\tdevice_set_desc(dev, \"WinModem\");\n"
+"\t\treturn (BUS_PROBE_DEFAULT);\n"
+"\t}\n"
+"\treturn (ENXIO);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:180
+#, no-wrap
+msgid "/* Attach function is only called if the probe is successful. */\n"
+msgstr "/* Attach function is only called if the probe is successful. */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:185
+#, no-wrap
+msgid ""
+"static int\n"
+"mypci_attach(device_t dev)\n"
+"{\n"
+"\tstruct mypci_softc *sc;\n"
+msgstr ""
+"static int\n"
+"mypci_attach(device_t dev)\n"
+"{\n"
+"\tstruct mypci_softc *sc;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:187
+#, no-wrap
+msgid "\tprintf(\"MyPCI Attach for : deviceID : 0x%x\\n\", pci_get_devid(dev));\n"
+msgstr "\tprintf(\"MyPCI Attach for : deviceID : 0x%x\\n\", pci_get_devid(dev));\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:191
+#, no-wrap
+msgid ""
+"\t/* Look up our softc and initialize its fields. */\n"
+"\tsc = device_get_softc(dev);\n"
+"\tsc->my_dev = dev;\n"
+msgstr ""
+"\t/* Look up our softc and initialize its fields. */\n"
+"\tsc = device_get_softc(dev);\n"
+"\tsc->my_dev = dev;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:204
+#, no-wrap
+msgid ""
+"\t/*\n"
+"\t * Create a /dev entry for this device. The kernel will assign us\n"
+"\t * a major number automatically. We use the unit number of this\n"
+"\t * device as the minor number and name the character device\n"
+"\t * \"mypci<unit>\".\n"
+"\t */\n"
+"\tsc->my_cdev = make_dev(&mypci_cdevsw, device_get_unit(dev),\n"
+"\t UID_ROOT, GID_WHEEL, 0600, \"mypci%u\", device_get_unit(dev));\n"
+"\tsc->my_cdev->si_drv1 = sc;\n"
+"\tprintf(\"Mypci device loaded.\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+msgstr ""
+"\t/*\n"
+"\t * Create a /dev entry for this device. The kernel will assign us\n"
+"\t * a major number automatically. We use the unit number of this\n"
+"\t * device as the minor number and name the character device\n"
+"\t * \"mypci<unit>\".\n"
+"\t */\n"
+"\tsc->my_cdev = make_dev(&mypci_cdevsw, device_get_unit(dev),\n"
+"\t UID_ROOT, GID_WHEEL, 0600, \"mypci%u\", device_get_unit(dev));\n"
+"\tsc->my_cdev->si_drv1 = sc;\n"
+"\tprintf(\"Mypci device loaded.\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:206
+#, no-wrap
+msgid "/* Detach device. */\n"
+msgstr "/* Detach device. */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:211
+#, no-wrap
+msgid ""
+"static int\n"
+"mypci_detach(device_t dev)\n"
+"{\n"
+"\tstruct mypci_softc *sc;\n"
+msgstr ""
+"static int\n"
+"mypci_detach(device_t dev)\n"
+"{\n"
+"\tstruct mypci_softc *sc;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:218
+#, no-wrap
+msgid ""
+"\t/* Teardown the state in our softc created in our attach routine. */\n"
+"\tsc = device_get_softc(dev);\n"
+"\tdestroy_dev(sc->my_cdev);\n"
+"\tprintf(\"Mypci detach!\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+msgstr ""
+"\t/* Teardown the state in our softc created in our attach routine. */\n"
+"\tsc = device_get_softc(dev);\n"
+"\tdestroy_dev(sc->my_cdev);\n"
+"\tprintf(\"Mypci detach!\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:220
+#, no-wrap
+msgid "/* Called during system shutdown after sync. */\n"
+msgstr "/* Called during system shutdown after sync. */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:224
+#, no-wrap
+msgid ""
+"static int\n"
+"mypci_shutdown(device_t dev)\n"
+"{\n"
+msgstr ""
+"static int\n"
+"mypci_shutdown(device_t dev)\n"
+"{\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:228
+#, no-wrap
+msgid ""
+"\tprintf(\"Mypci shutdown!\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+msgstr ""
+"\tprintf(\"Mypci shutdown!\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:235
+#, no-wrap
+msgid ""
+"/*\n"
+" * Device suspend routine.\n"
+" */\n"
+"static int\n"
+"mypci_suspend(device_t dev)\n"
+"{\n"
+msgstr ""
+"/*\n"
+" * Device suspend routine.\n"
+" */\n"
+"static int\n"
+"mypci_suspend(device_t dev)\n"
+"{\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:239
+#, no-wrap
+msgid ""
+"\tprintf(\"Mypci suspend!\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+msgstr ""
+"\tprintf(\"Mypci suspend!\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:246
+#, no-wrap
+msgid ""
+"/*\n"
+" * Device resume routine.\n"
+" */\n"
+"static int\n"
+"mypci_resume(device_t dev)\n"
+"{\n"
+msgstr ""
+"/*\n"
+" * Device resume routine.\n"
+" */\n"
+"static int\n"
+"mypci_resume(device_t dev)\n"
+"{\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:250
+#, no-wrap
+msgid ""
+"\tprintf(\"Mypci resume!\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+msgstr ""
+"\tprintf(\"Mypci resume!\\n\");\n"
+"\treturn (0);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:259
+#, no-wrap
+msgid ""
+"static device_method_t mypci_methods[] = {\n"
+"\t/* Device interface */\n"
+"\tDEVMETHOD(device_probe,\t\tmypci_probe),\n"
+"\tDEVMETHOD(device_attach,\tmypci_attach),\n"
+"\tDEVMETHOD(device_detach,\tmypci_detach),\n"
+"\tDEVMETHOD(device_shutdown,\tmypci_shutdown),\n"
+"\tDEVMETHOD(device_suspend,\tmypci_suspend),\n"
+"\tDEVMETHOD(device_resume,\tmypci_resume),\n"
+msgstr ""
+"static device_method_t mypci_methods[] = {\n"
+"\t/* Device interface */\n"
+"\tDEVMETHOD(device_probe,\t\tmypci_probe),\n"
+"\tDEVMETHOD(device_attach,\tmypci_attach),\n"
+"\tDEVMETHOD(device_detach,\tmypci_detach),\n"
+"\tDEVMETHOD(device_shutdown,\tmypci_shutdown),\n"
+"\tDEVMETHOD(device_suspend,\tmypci_suspend),\n"
+"\tDEVMETHOD(device_resume,\tmypci_resume),\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:262
+#, no-wrap
+msgid ""
+"\tDEVMETHOD_END\n"
+"};\n"
+msgstr ""
+"\tDEVMETHOD_END\n"
+"};\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:264
+#, no-wrap
+msgid "static devclass_t mypci_devclass;\n"
+msgstr "static devclass_t mypci_devclass;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:267
+#, no-wrap
+msgid ""
+"DEFINE_CLASS_0(mypci, mypci_driver, mypci_methods, sizeof(struct mypci_softc));\n"
+"DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);\n"
+msgstr ""
+"DEFINE_CLASS_0(mypci, mypci_driver, mypci_methods, sizeof(struct mypci_softc));\n"
+"DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:269
+#, no-wrap
+msgid "[.filename]#Makefile# for Sample Driver"
+msgstr "[.filename]#Makefile# для примера драйвера"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:274
+#, no-wrap
+msgid "# Makefile for mypci driver\n"
+msgstr "# Makefile for mypci driver\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:278
+#, no-wrap
+msgid ""
+"KMOD=\tmypci\n"
+"SRCS=\tmypci.c\n"
+"SRCS+=\tdevice_if.h bus_if.h pci_if.h\n"
+msgstr ""
+"KMOD=\tmypci\n"
+"SRCS=\tmypci.c\n"
+"SRCS+=\tdevice_if.h bus_if.h pci_if.h\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:280
+#, no-wrap
+msgid ".include <bsd.kmod.mk>\n"
+msgstr ".include <bsd.kmod.mk>\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:283
+msgid ""
+"If you place the above source file and [.filename]#Makefile# into a "
+"directory, you may run `make` to compile the sample driver. Additionally, "
+"you may run `make load` to load the driver into the currently running kernel "
+"and `make unload` to unload the driver after it is loaded."
+msgstr ""
+"Если вы поместите исходный файл выше и [.filename]#Makefile# в каталог, вы "
+"можете запустить `make` для компиляции примера драйвера. Дополнительно можно "
+"выполнить `make load` для загрузки драйвера в текущее ядро и `make unload` "
+"для выгрузки драйвера после его загрузки."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:284
+#, no-wrap
+msgid "Additional Resources"
+msgstr "Дополнительные ресурсы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:287
+msgid "http://www.pcisig.org/[PCI Special Interest Group]"
+msgstr "http://www.pcisig.org/[Группа по стандартам PCI]"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:288
+msgid "PCI System Architecture, Fourth Edition by Tom Shanley, et al."
+msgstr "PCI System Architecture, Fourth Edition by Tom Shanley, et al."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:290
+#, no-wrap
+msgid "Bus Resources"
+msgstr "Ресурсы шины"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:293
+msgid ""
+"FreeBSD provides an object-oriented mechanism for requesting resources from "
+"a parent bus. Almost all devices will be a child member of some sort of bus "
+"(PCI, ISA, USB, SCSI, etc) and these devices need to acquire resources from "
+"their parent bus (such as memory segments, interrupt lines, or DMA channels)."
+msgstr ""
+"FreeBSD предоставляет объектно-ориентированный механизм для запроса ресурсов "
+"от родительской шины. Почти все устройства будут дочерними элементами какого-"
+"либо типа шины (PCI, ISA, USB, SCSI и т.д.), и этим устройствам необходимо "
+"получать ресурсы от своей родительской шины (такие как сегменты памяти, "
+"линии прерываний или каналы DMA)."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:294
+#, no-wrap
+msgid "Base Address Registers"
+msgstr "Регистры базовых адресов"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:297
+msgid ""
+"To do anything particularly useful with a PCI device you will need to obtain "
+"the _Base Address Registers_ (BARs) from the PCI Configuration space. The "
+"PCI-specific details of obtaining the BAR are abstracted in the "
+"`bus_alloc_resource()` function."
+msgstr ""
+"Для выполнения каких-либо полезных действий с устройством PCI необходимо "
+"получить _регистры базовых адресов_ (BAR) из конфигурационного пространства "
+"PCI. Специфичные для PCI детали получения BAR абстрагированы в функции "
+"`bus_alloc_resource()`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:299
+msgid ""
+"For example, a typical driver might have something similar to this in the "
+"`attach()` function:"
+msgstr ""
+"Например, типичный драйвер может содержать что-то подобное в функции "
+"`attach()`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:310
+#, no-wrap
+msgid ""
+" sc->bar0id = PCIR_BAR(0);\n"
+" sc->bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->bar0id,\n"
+"\t\t\t\t 0, ~0, 1, RF_ACTIVE);\n"
+" if (sc->bar0res == NULL) {\n"
+" printf(\"Memory allocation of PCI base register 0 failed!\\n\");\n"
+" error = ENXIO;\n"
+" goto fail1;\n"
+" }\n"
+msgstr ""
+" sc->bar0id = PCIR_BAR(0);\n"
+" sc->bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->bar0id,\n"
+"\t\t\t\t 0, ~0, 1, RF_ACTIVE);\n"
+" if (sc->bar0res == NULL) {\n"
+" printf(\"Memory allocation of PCI base register 0 failed!\\n\");\n"
+" error = ENXIO;\n"
+" goto fail1;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:323
+#, no-wrap
+msgid ""
+" sc->bar1id = PCIR_BAR(1);\n"
+" sc->bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->bar1id,\n"
+"\t\t\t\t 0, ~0, 1, RF_ACTIVE);\n"
+" if (sc->bar1res == NULL) {\n"
+" printf(\"Memory allocation of PCI base register 1 failed!\\n\");\n"
+" error = ENXIO;\n"
+" goto fail2;\n"
+" }\n"
+" sc->bar0_bt = rman_get_bustag(sc->bar0res);\n"
+" sc->bar0_bh = rman_get_bushandle(sc->bar0res);\n"
+" sc->bar1_bt = rman_get_bustag(sc->bar1res);\n"
+" sc->bar1_bh = rman_get_bushandle(sc->bar1res);\n"
+msgstr ""
+" sc->bar1id = PCIR_BAR(1);\n"
+" sc->bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->bar1id,\n"
+"\t\t\t\t 0, ~0, 1, RF_ACTIVE);\n"
+" if (sc->bar1res == NULL) {\n"
+" printf(\"Memory allocation of PCI base register 1 failed!\\n\");\n"
+" error = ENXIO;\n"
+" goto fail2;\n"
+" }\n"
+" sc->bar0_bt = rman_get_bustag(sc->bar0res);\n"
+" sc->bar0_bh = rman_get_bushandle(sc->bar0res);\n"
+" sc->bar1_bt = rman_get_bustag(sc->bar1res);\n"
+" sc->bar1_bh = rman_get_bushandle(sc->bar1res);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:326
+msgid ""
+"Handles for each base address register are kept in the `softc` structure so "
+"that they can be used to write to the device later."
+msgstr ""
+"Дескрипторы для каждого регистра базовых адресов хранятся в структуре "
+"`softc`, чтобы их можно было использовать для записи на устройство в "
+"дальнейшем."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:328
+msgid ""
+"These handles can then be used to read or write from the device registers "
+"with the `bus_space_*` functions. For example, a driver might contain a "
+"shorthand function to read from a board specific register like this:"
+msgstr ""
+"Эти дескрипторы затем могут быть использованы для чтения или записи из "
+"регистров устройства с помощью функций `bus_space_*`. Например, драйвер "
+"может содержать сокращённую функцию для чтения из специфичного для платы "
+"регистра, как показано ниже:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:336
+#, no-wrap
+msgid ""
+"uint16_t\n"
+"board_read(struct ni_softc *sc, uint16_t address)\n"
+"{\n"
+" return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);\n"
+"}\n"
+msgstr ""
+"uint16_t\n"
+"board_read(struct ni_softc *sc, uint16_t address)\n"
+"{\n"
+" return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:339
+msgid "Similarly, one could write to the registers with:"
+msgstr "Аналогично, можно записать в регистры с помощью:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:347
+#, no-wrap
+msgid ""
+"void\n"
+"board_write(struct ni_softc *sc, uint16_t address, uint16_t value)\n"
+"{\n"
+" bus_space_write_2(sc->bar1_bt, sc->bar1_bh, address, value);\n"
+"}\n"
+msgstr ""
+"void\n"
+"board_write(struct ni_softc *sc, uint16_t address, uint16_t value)\n"
+"{\n"
+" bus_space_write_2(sc->bar1_bt, sc->bar1_bh, address, value);\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:350
+msgid ""
+"These functions exist in 8bit, 16bit, and 32bit versions and you should use "
+"`bus_space_{read|write}_{1|2|4}` accordingly."
+msgstr ""
+"Эти функции существуют в 8-битных, 16-битных и 32-битных версиях, и вам "
+"следует использовать `bus_space_{read|write}_{1|2|4}` соответственно."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:354
+msgid ""
+"In FreeBSD 7.0 and later, you can use the `bus_*` functions instead of "
+"`bus_space_*`. The `bus_*` functions take a struct resource * pointer "
+"instead of a bus tag and handle. Thus, you could drop the bus tag and bus "
+"handle members from the `softc` and rewrite the `board_read()` function as:"
+msgstr ""
+"В FreeBSD 7.0 и более поздних версиях вы можете использовать функции `bus_*` "
+"вместо `bus_space_*`. Функции `bus_*` принимают указатель на структуру "
+"resource * вместо тега шины и дескриптора. Таким образом, вы можете удалить "
+"тег шины и дескриптор шины из `softc` и переписать функцию `board_read()` "
+"как:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:362
+#, no-wrap
+msgid ""
+"uint16_t\n"
+"board_read(struct ni_softc *sc, uint16_t address)\n"
+"{\n"
+"\treturn (bus_read(sc->bar1res, address));\n"
+"}\n"
+msgstr ""
+"uint16_t\n"
+"board_read(struct ni_softc *sc, uint16_t address)\n"
+"{\n"
+"\treturn (bus_read(sc->bar1res, address));\n"
+"}\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:366
+#, no-wrap
+msgid "Interrupts"
+msgstr "Прерывания"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:369
+msgid ""
+"Interrupts are allocated from the object-oriented bus code in a way similar "
+"to the memory resources. First an IRQ resource must be allocated from the "
+"parent bus, and then the interrupt handler must be set up to deal with this "
+"IRQ."
+msgstr ""
+"Прерывания выделяются объектно-ориентированным кодом шины аналогично "
+"ресурсам памяти. Сначала ресурс IRQ должен быть выделен из родительской "
+"шины, а затем должен быть настроен обработчик прерывания для работы с этим "
+"IRQ."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:371
+msgid "Again, a sample from a device `attach()` function says more than words."
+msgstr ""
+"Вот пример из функции `attach()` устройства, который скажет больше, чем "
+"слова."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:375
+#, no-wrap
+msgid "/* Get the IRQ resource */\n"
+msgstr "/* Get the IRQ resource */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:384
+#, no-wrap
+msgid ""
+" sc->irqid = 0x0;\n"
+" sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &(sc->irqid),\n"
+"\t\t\t\t 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);\n"
+" if (sc->irqres == NULL) {\n"
+"\tprintf(\"IRQ allocation failed!\\n\");\n"
+"\terror = ENXIO;\n"
+"\tgoto fail3;\n"
+" }\n"
+msgstr ""
+" sc->irqid = 0x0;\n"
+" sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &(sc->irqid),\n"
+"\t\t\t\t 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);\n"
+" if (sc->irqres == NULL) {\n"
+"\tprintf(\"IRQ allocation failed!\\n\");\n"
+"\terror = ENXIO;\n"
+"\tgoto fail3;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:386
+#, no-wrap
+msgid " /* Now we should set up the interrupt handler */\n"
+msgstr " /* Now we should set up the interrupt handler */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:393
+#, no-wrap
+msgid ""
+" error = bus_setup_intr(dev, sc->irqres, INTR_TYPE_MISC,\n"
+"\t\t\t my_handler, sc, &(sc->handler));\n"
+" if (error) {\n"
+"\tprintf(\"Couldn't set up irq\\n\");\n"
+"\tgoto fail4;\n"
+" }\n"
+msgstr ""
+" error = bus_setup_intr(dev, sc->irqres, INTR_TYPE_MISC,\n"
+"\t\t\t my_handler, sc, &(sc->handler));\n"
+" if (error) {\n"
+"\tprintf(\"Couldn't set up irq\\n\");\n"
+"\tgoto fail4;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:396
+msgid ""
+"Some care must be taken in the detach routine of the driver. You must "
+"quiesce the device's interrupt stream, and remove the interrupt handler. "
+"Once `bus_teardown_intr()` has returned, you know that your interrupt "
+"handler will no longer be called and that all threads that might have been "
+"executing this interrupt handler have returned. Since this function can "
+"sleep, you must not hold any mutexes when calling this function."
+msgstr ""
+"Некоторые меры предосторожности должны быть приняты в процедуре отключения "
+"драйвера. Необходимо остановить поток прерываний устройства и удалить "
+"обработчик прерываний. Как только `bus_teardown_intr()` завершится, можно "
+"быть уверенным, что обработчик прерываний больше не будет вызываться и все "
+"потоки, которые могли выполнять этот обработчик, завершили работу. Поскольку "
+"эта функция может засыпать, нельзя удерживать какие-либо мьютексы при её "
+"вызове."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:397
+#, no-wrap
+msgid "DMA"
+msgstr "DMA"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:400
+msgid ""
+"This section is obsolete, and present only for historical reasons. The "
+"proper methods for dealing with these issues is to use the "
+"`bus_space_dma*()` functions instead. This paragraph can be removed when "
+"this section is updated to reflect that usage. However, at the moment, the "
+"API is in a bit of flux, so once that settles down, it would be good to "
+"update this section to reflect that."
+msgstr ""
+"Этот раздел устарел и приведён только в исторических целях. Правильный "
+"способ решения этих проблем — использование функций `bus_space_dma*()`. Этот "
+"абзац можно удалить, когда раздел будет обновлён с учётом данного подхода. "
+"Однако на данный момент API находится в состоянии изменения, поэтому, когда "
+"он стабилизируется, будет полезно обновить этот раздел соответствующим "
+"образом."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:402
+msgid ""
+"On the PC, peripherals that want to do bus-mastering DMA must deal with "
+"physical addresses. This is a problem since FreeBSD uses virtual memory and "
+"deals almost exclusively with virtual addresses. Fortunately, there is a "
+"function, `vtophys()` to help."
+msgstr ""
+"На ПК периферийные устройства, которые хотят использовать DMA с управлением "
+"шиной, должны работать с физическими адресами. Это проблема, поскольку "
+"FreeBSD использует виртуальную память и работает почти исключительно с "
+"виртуальными адресами. К счастью, существует функция `vtophys()`, которая "
+"поможет."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:407
+#, no-wrap
+msgid ""
+"#include <vm/vm.h>\n"
+"#include <vm/pmap.h>\n"
+msgstr ""
+"#include <vm/vm.h>\n"
+"#include <vm/pmap.h>\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:409
+#, no-wrap
+msgid "#define vtophys(virtual_address) (...)\n"
+msgstr "#define vtophys(virtual_address) (...)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:412
+msgid ""
+"The solution is a bit different on the alpha however, and what we really "
+"want is a function called `vtobus()`."
+msgstr ""
+"Однако решение немного отличается на alpha, и на самом деле нам нужна "
+"функция под названием `vtobus()`."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:420
+#, no-wrap
+msgid ""
+"#if defined(__alpha__)\n"
+"#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)\n"
+"#else\n"
+"#define vtobus(va) vtophys(va)\n"
+"#endif\n"
+msgstr ""
+"#if defined(__alpha__)\n"
+"#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)\n"
+"#else\n"
+"#define vtobus(va) vtophys(va)\n"
+"#endif\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:422
+#, no-wrap
+msgid "Deallocating Resources"
+msgstr "Освобождение ресурсов"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/pci/_index.adoc:424
+msgid ""
+"It is very important to deallocate all of the resources that were allocated "
+"during `attach()`. Care must be taken to deallocate the correct stuff even "
+"on a failure condition so that the system will remain usable while your "
+"driver dies."
+msgstr ""
+"Очень важно освободить все ресурсы, которые были выделены во время "
+"`attach()`. Необходимо внимательно следить за освобождением правильных "
+"ресурсов даже в случае ошибки, чтобы система оставалась работоспособной при "
+"завершении работы вашего драйвера."
diff --git a/documentation/content/ru/books/arch-handbook/scsi/_index.adoc b/documentation/content/ru/books/arch-handbook/scsi/_index.adoc
new file mode 100644
index 0000000000..036e2d4888
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/scsi/_index.adoc
@@ -0,0 +1,1367 @@
+---
+description: 'Контроллеры SCSI с общим методом доступа (CAM)'
+next: books/arch-handbook/usb
+params:
+ path: /books/arch-handbook/scsi/
+prev: books/arch-handbook/pci
+showBookMenu: true
+tags: ["SCSI", "Controller", "Architecture"]
+title: 'Глава 12. Контроллеры SCSI с общим методом доступа (CAM)'
+weight: 14
+---
+
+[[scsi]]
+= Контроллеры SCSI с общим методом доступа (CAM)
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 12
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[scsi-synopsis]]
+== Обзор
+
+Этот документ предполагает, что читатель имеет общее представление о драйверах устройств в FreeBSD и о протоколе SCSI. Большая часть информации в этом документе была извлечена из драйверов:
+
+* ncr ([.filename]#/sys/pci/ncr.c#) от Wolfgang Stanglmeier и Stefan Esser
+* sym ([.filename]#/sys/dev/sym/sym_hipd.c#) от Gerard Roudier
+* aic7xxx ([.filename]#/sys/dev/aic7xxx/aic7xxx.c#) от Justin T. Gibbs
+
+и из самого кода CAM (автор Justin T. Gibbs, см. [.filename]#/sys/cam/*#). Когда какое-то решение выглядело наиболее логичным и было практически дословно взято из кода Justin T. Gibbs, я отмечал его как "рекомендуемое".
+
+Документ иллюстрирован примерами на псевдокоде. Хотя иногда примеры содержат много деталей и выглядят как настоящий код, это всё ещё псевдокод. Он был написан, чтобы продемонстрировать концепции в понятной форме. Для реального драйвера могут быть более модульные и эффективные подходы. Также он абстрагируется от деталей оборудования, а также от вопросов, которые могли бы затмить демонстрируемые концепции или которые предполагается описать в других главах руководства разработчика. Такие детали обычно показаны в виде вызовов функций с описательными именами, комментариев или псевдооператоров. К счастью, полные примеры из реальной жизни со всеми деталями можно найти в реальных драйверах.
+
+[[scsi-general]]
+== Общая Архитектура
+
+CAM означает Common Access Method (Общий Метод Доступа). Это универсальный способ адресации шин ввода-вывода в стиле SCSI. Это позволяет отделить общие драйверы устройств от драйверов, управляющих шиной ввода-вывода: например, драйвер диска получает возможность управлять дисками как на SCSI, IDE, так и на любой другой шине, так что часть драйвера диска не нужно переписывать (или копировать и изменять) для каждой новой шины ввода-вывода. Таким образом, двумя наиболее важными активными сущностями являются:
+
+* _Модули периферийных устройств_ - драйвер для периферийных устройств (диски, ленты, CD-ROM и т.д.)
+* _Модули интерфейса SCSI_ (SIM) - драйверы адаптеров шины для подключения к шине ввода-вывода, такой как SCSI или IDE.
+
+Периферийный драйвер получает запросы от ОС, преобразует их в последовательность команд SCSI и передает эти команды SCSI модулю интерфейса SCSI. Модуль интерфейса SCSI отвечает за передачу этих команд реальному оборудованию (или, если оборудование не поддерживает SCSI, а использует, например, IDE, также преобразует команды SCSI в собственные команды оборудования).
+
+Так как мы заинтересованы в написании драйвера адаптера SCSI, с этого момента мы будем рассматривать всё с точки зрения модуля SCSI-интерфейса (SIM).
+
+== Глобальные переменные и Шаблонный код
+
+Типичный драйвер SIM должен включать следующие заголовочные файлы, связанные с CAM:
+
+[.programlisting]
+....
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_debug.h>
+#include <cam/scsi/scsi_all.h>
+....
+
+== Конфигурация устройства: xxx_attach
+
+Первое, что должен сделать каждый драйвер SIM, — это зарегистрироваться в подсистеме CAM. Это выполняется в функции `xxx_attach()` драйвера (здесь и далее xxx_ используется для обозначения уникального префикса имени драйвера). Сама функция `xxx_attach()` вызывается кодом автонастройки системной шины, который мы здесь не описываем.
+
+Это достигается в несколько этапов: сначала необходимо выделить очередь запросов, связанных с этой SIM:
+
+[.programlisting]
+....
+ struct cam_devq *devq;
+
+ if ((devq = cam_simq_alloc(SIZE)) == NULL) {
+ error; /* some code to handle the error */
+ }
+....
+
+Вот `SIZE` — это размер выделяемой очереди, максимальное количество запросов, которые она может содержать. Это количество запросов, которые драйвер SIM может обрабатывать параллельно на одной SCSI-карте. Обычно его можно вычислить как:
+
+[.programlisting]
+....
+SIZE = NUMBER_OF_SUPPORTED_TARGETS * MAX_SIMULTANEOUS_COMMANDS_PER_TARGET
+....
+
+Далее мы создаем описание нашего SIM:
+
+[.programlisting]
+....
+ struct cam_sim *sim;
+
+ if ((sim = cam_sim_alloc(action_func, poll_func, driver_name,
+ softc, unit, mtx, max_dev_transactions,
+ max_tagged_dev_transactions, devq)) == NULL) {
+ cam_simq_free(devq);
+ error; /* some code to handle the error */
+ }
+....
+
+Обратите внимание, что если мы не сможем создать дескриптор SIM, мы также освобождаем `devq`, потому что больше ничего не можем с ним сделать и хотим сэкономить память.
+
+Если SCSI-карта имеет несколько шин SCSI, то каждой шине требуется собственная структура `cam_sim`.
+
+Интересный вопрос: что делать, если SCSI-карта имеет более одной SCSI-шины, нужна ли одна структура `devq` на карту или на SCSI-шину? Ответ, приведённый в комментариях к коду CAM, таков: как угодно, на усмотрение автора драйвера.
+
+Аргументы:
+
+* `action_func` - указатель на функцию `xxx_action` драйвера.
+
+[.programlisting]
+....
+static void xxx_action(struct cam_sim *, union ccb *);
+....
+* `poll_func` - указатель на функцию `xxx_poll()` драйвера
++
+[.programlisting]
+....
+static void xxx_poll(struct cam_sim *);
+....
+* `driver_name` — имя фактического драйвера, например `ncr` или `wds`.
+* `softc` — указатель на внутренний дескриптор драйвера для данной SCSI-карты. Этот указатель будет использоваться драйвером в дальнейшем для получения приватных данных.
+* unit - номер управляющего устройства, например, для контроллера "mps0" это число будет 0
+* mtx - Блокировка, связанная с данной SIM. Для SIM, которые не поддерживают блокировку, передается Giant. Для SIM, которые поддерживают, передается блокировка, используемая для защиты структур данных этой SIM. Эта блокировка будет удерживаться при вызовах xxx_action и xxx_poll.
+* max_dev_transactions - максимальное количество одновременных транзакций на целевом SCSI-устройстве в режиме без тегов. Это значение почти всегда равно 1, за исключением возможных исключений только для не-SCSI карт. Также драйверы, которые надеются получить преимущество, подготавливая одну транзакцию во время выполнения другой, могут установить его в 2, но это не кажется оправданным из-за сложности.
+* max_tagged_dev_transactions - то же самое, но в режиме с тегами. Теги — это способ в SCSI инициировать несколько транзакций на устройстве: каждая транзакция получает уникальный тег и отправляется на устройство. Когда устройство завершает транзакцию, оно возвращает результат вместе с тегом, чтобы SCSI-адаптер (и драйвер) могли определить, какая транзакция была завершена. Этот аргумент также известен как максимальная глубина тега. Он зависит от возможностей SCSI-адаптера.
+
+Наконец, мы регистрируем шины SCSI, связанные с нашим SCSI-адаптером:
+
+[.programlisting]
+....
+ if (xpt_bus_register(sim, softc, bus_number) != CAM_SUCCESS) {
+ cam_sim_free(sim, /*free_devq*/ TRUE);
+ error; /* some code to handle the error */
+ }
+....
+
+Если существует одна структура `devq` на каждую шину SCSI (т.е. мы рассматриваем карту с несколькими шинами как несколько карт с одной шиной каждая), то номер шины всегда будет 0, в противном случае каждая шина на SCSI-карте должна получить уникальный номер. Каждой шине требуется своя отдельная структура `cam_sim`.
+
+После этого наш контроллер полностью подключён к системе CAM. Значение `devq` теперь можно отбросить: sim будет передаваться в качестве аргумента во всех последующих вызовах из CAM, а devq можно получить из него.
+
+CAM предоставляет инфраструктуру для подобных асинхронных событий. Некоторые события возникают на нижних уровнях (драйверы SIM), некоторые — в драйверах периферийных устройств, а некоторые — в самой подсистеме CAM. Любой драйвер может зарегистрировать обработчики для определённых типов асинхронных событий, чтобы получать уведомления при их возникновении.
+
+Типичным примером такого события является сброс устройства. Каждая транзакция и событие идентифицируют устройства, к которым они применяются, с помощью "пути". Специфичные для целевого устройства события обычно происходят во время транзакции с этим устройством. Таким образом, путь из этой транзакции может быть повторно использован для сообщения о данном событии (это безопасно, потому что путь события копируется в процедуре сообщения о событии, но не освобождается и не передаётся дальше). Также безопасно динамически выделять пути в любое время, включая процедуры обработки прерываний, хотя это влечёт определённые накладные расходы, и возможная проблема такого подхода заключается в том, что в этот момент может не быть свободной памяти. Для события сброса шины нам необходимо определить путь-шаблон, включающий все устройства на шине. Поэтому мы можем заранее создать путь для будущих событий сброса шины и избежать проблем с возможной нехваткой памяти в будущем:
+
+[.programlisting]
+....
+ struct cam_path *path;
+
+ if (xpt_create_path(&path, /*periph*/NULL,
+ cam_sim_path(sim), CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ xpt_bus_deregister(cam_sim_path(sim));
+ cam_sim_free(sim, /*free_devq*/TRUE);
+ error; /* some code to handle the error */
+ }
+
+ softc->wpath = path;
+ softc->sim = sim;
+....
+
+Как вы можете видеть, путь включает:
+
+* Идентификатор драйвера периферийного устройства (NULL здесь, так как у нас его нет)
+* Идентификатор драйвера SIM (`cam_sim_path(sim)`)
+* Номер целевого устройства SCSI (CAM_TARGET_WILDCARD означает "все устройства")
+* Номер SCSI LUN подустройства (CAM_LUN_WILDCARD означает "все LUN")
+
+Если драйвер не может выделить этот путь, он не сможет нормально работать, поэтому в таком случае мы демонтируем эту шину SCSI.
+
+И мы сохраняем указатель пути в структуре `softc` для дальнейшего использования. После этого сохраняем значение sim (или можем также отбросить его при выходе из `xxx_probe()`, если захотим).
+
+Вот и всё для минималистичной инициализации. Чтобы сделать всё правильно, остался ещё один вопрос.
+
+Для драйвера SIM есть одно особенно важное событие: когда целевое устройство считается потерянным. В этом случае может быть хорошей идеей сбросить SCSI-переговоры с этим устройством. Поэтому мы регистрируем обратный вызов для этого события в CAM. Запрос передаётся в CAM путём запроса действия CAM в блоке управления CAM для этого типа запроса:
+
+[.programlisting]
+....
+ struct ccb_setasync csa;
+
+ xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = AC_LOST_DEVICE;
+ csa.callback = xxx_async;
+ csa.callback_arg = sim;
+ xpt_action((union ccb *)&csa);
+....
+
+== Обработка сообщений CAM: xxx_action
+
+[.programlisting]
+....
+static void xxx_action(struct cam_sim *sim, union ccb *ccb);
+....
+
+Выполнить некоторое действие по запросу подсистемы CAM. Sim описывает SIM для запроса, CCB — это сам запрос. CCB расшифровывается как "CAM Control Block" (блок управления CAM). Это объединение множества конкретных экземпляров, каждый из которых описывает аргументы для определённого типа транзакций. Все эти экземпляры имеют общий заголовок CCB, в котором хранится общая часть аргументов.
+
+CAM поддерживает SCSI-контроллеры, работающие как в режиме инициатора («обычном»), так и в режиме цели (эмулирующем SCSI-устройство). Здесь мы рассматриваем только часть, относящуюся к режиму инициатора.
+
+Существует несколько функций и макросов (другими словами, методов), определённых для доступа к публичным данным в структуре sim:
+
+* `cam_sim_path(sim)` - идентификатор пути (см. выше)
+* `cam_sim_name(sim)` — имя sim
+* `cam_sim_softc(sim)` - указатель на структуру softc (приватные данные драйвера)
+* `cam_sim_unit(sim)` - номер устройства
+* `cam_sim_bus(sim)` - идентификатор шины
+
+Для идентификации устройства `xxx_action()` может получить номер устройства и указатель на его структуру softc, используя следующие функции.
+
+Тип запроса хранится в `ccb->ccb_h.func_code`. Поэтому, как правило, `xxx_action()` состоит из большого оператора switch:
+
+[.programlisting]
+....
+ struct xxx_softc *softc = (struct xxx_softc *) cam_sim_softc(sim);
+ struct ccb_hdr *ccb_h = &ccb->ccb_h;
+ int unit = cam_sim_unit(sim);
+ int bus = cam_sim_bus(sim);
+
+ switch (ccb_h->func_code) {
+ case ...:
+ ...
+ default:
+ ccb_h->status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+....
+
+Как видно из случая по умолчанию (если получена неизвестная команда) код возврата команды устанавливается в `ccb->ccb_h.status`, а завершённый CCB возвращается обратно в CAM вызовом `xpt_done(ccb)`.
+
+`xpt_done()` не обязательно вызывать из `xxx_action()`: Например, запрос ввода-вывода может быть поставлен в очередь внутри драйвера SIM и/или его SCSI-контроллера. Затем, когда устройство пошлет прерывание, сигнализирующее о завершении обработки этого запроса, `xpt_done()` может быть вызван из процедуры обработки прерывания.
+
+На самом деле, статус CCB не только присваивается в качестве кода возврата, но и CCB всегда имеет какой-то статус. Перед тем как CCB передается в процедуру `xxx_action()`, он получает статус CCB_REQ_INPROG, означающий, что запрос находится в процессе выполнения. В [.filename]#/sys/cam/cam.h# определено удивительно большое количество значений статуса, которые должны детально отражать состояние запроса. Что еще интереснее, статус фактически представляет собой "побитовое ИЛИ" перечисленного значения статуса (младшие 6 бит) и возможных дополнительных флагов (старшие биты). Перечисленные значения будут подробно рассмотрены далее. Их краткое описание можно найти в разделе "Сводка ошибок". Возможные флаги статуса:
+
+* _CAM_DEV_QFRZN_ - если драйвер SIM получает серьёзную ошибку (например, устройство не отвечает на выборку или нарушает протокол SCSI) при обработке CCB, он должен заморозить очередь запросов, вызвав `xpt_freeze_simq()`, вернуть другие поставленные в очередь, но ещё не обработанные CCB для этого устройства обратно в очередь CAM, затем установить этот флаг для проблемного CCB и вызвать `xpt_done()`. Этот флаг заставляет подсистему CAM разморозить очередь после обработки ошибки.
+* _CAM_AUTOSNS_VALID_ - если устройство вернуло состояние ошибки и флаг CAM_DIS_AUTOSENSE не установлен в CCB, драйвер SIM должен автоматически выполнить команду REQUEST SENSE, чтобы извлечь данные sense (расширенную информацию об ошибке) из устройства. Если попытка была успешной, данные sense должны быть сохранены в CCB, а этот флаг установлен.
+* _CAM_RELEASE_SIMQ_ - аналогично CAM_DEV_QFRZN, но используется в случае возникновения проблем (или нехватки ресурсов) с самим SCSI-контроллером. В этом случае все последующие запросы к контроллеру должны быть остановлены с помощью `xpt_freeze_simq()`. Очередь контроллера будет возобновлена после того, как драйвер SIM устранит нехватку и уведомит CAM, вернув некоторый CCB с установленным этим флагом.
+* _CAM_SIM_QUEUED_ - этот флаг должен быть установлен, когда SIM помещает CCB в свою очередь запросов (и снят, когда этот CCB извлекается из очереди перед возвратом в CAM). В настоящее время этот флаг нигде не используется в коде CAM, поэтому его назначение чисто диагностическое.
+* _CAM_QOS_VALID_ - Данные QOS теперь действительны.
+
+Функция `xxx_action()` не может находиться в состоянии ожидания, поэтому вся синхронизация доступа к ресурсам должна выполняться с использованием SIM или заморозки очереди устройств. Помимо упомянутых флагов, подсистема CAM предоставляет функции `xpt_release_simq()` и `xpt_release_devq()` для разморозки очередей напрямую, без передачи CCB в CAM.
+
+Заголовок CCB содержит следующие поля:
+
+* _path_ - идентификатор пути для запроса
+* _target_id_ - идентификатор целевого устройства для запроса
+* _target_lun_ - идентификатор LUN целевого устройства
+* _timeout_ - интервал таймаута для этой команды, в миллисекундах
+* _timeout_ch_ - удобное место для драйвера SIM, чтобы хранить обработчик таймаута (сама подсистема CAM не делает никаких предположений о нём)
+* _flags_ - различные биты информации о запросе spriv_ptr0, spriv_ptr1 — поля, зарезервированные для приватного использования драйвером SIM (например, для связи с очередями SIM или приватными блоками управления SIM); фактически они существуют как объединения: spriv_ptr0 и spriv_ptr1 имеют тип (void *), spriv_field0 и spriv_field1 имеют тип unsigned long, sim_priv.entries[0].bytes и sim_priv.entries[1].bytes - это байтовые массивы размера, согласованного с другими вариантами объединения, а sim_priv.bytes - это один массив, вдвое большего размера.
+
+Рекомендуемый способ использования приватных полей SIM в CCB — это определить для них осмысленные имена и использовать эти осмысленные имена в драйвере, например:
+
+[.programlisting]
+....
+#define ccb_some_meaningful_name sim_priv.entries[0].bytes
+#define ccb_hcb spriv_ptr1 /* for hardware control block */
+....
+
+Наиболее распространенные запросы в режиме инициатора:
+
+=== _XPT_SCSI_IO_ - выполнить транзакцию ввода-вывода
+
+Экземпляр "struct ccb_scsiio csio" объединения ccb используется для передачи аргументов. Они включают:
+
+* _cdb_io_ - указатель на буфер команды SCSI или сам буфер
+* _cdb_len_ - длина команды SCSI
+* _data_ptr_ - указатель на буфер данных (усложняется, если используется scatter/gather)
+* _dxfer_len_ - длина передаваемых данных
+* _sglist_cnt_ - счетчик сегментов scatter/gather
+* _scsi_status_ - место для возврата статуса SCSI
+* _sense_data_ - буфер для информации SCSI sense, если команда возвращает ошибку (драйвер SIM должен автоматически выполнить команду REQUEST SENSE в этом случае, если флаг CCB CAM_DIS_AUTOSENSE не установлен)
+* _sense_len_ - длина этого буфера (если она окажется больше размера sense_data, драйвер SIM должен без уведомления принять меньшее значение)
+* _resid_, _sense_resid_ — если передача данных или SCSI sense вернула ошибку, это счётчики остаточных (не переданных) данных. Они не кажутся особенно значимыми, поэтому в случаях, когда их сложно вычислить (например, подсчёт байтов в FIFO-буфере SCSI-контроллера), подойдёт и приблизительное значение. Для успешно завершённой передачи они должны быть установлены в ноль.
+* _tag_action_ - тип используемого тега:
+** `CAM_TAG_ACTION_NONE` - не использовать теги для данной транзакции
+** MSG_SIMPLE_Q_TAG, MSG_HEAD_OF_Q_TAG, MSG_ORDERED_Q_TAG — значение, соответствующее указанному теговому сообщению (см. /sys/cam/scsi/scsi_message.h); указывает только тип тега, значение тега должно быть назначено самим драйвером SIM
+
+Общая логика обработки этого запроса следующая:
+
+Первое, что нужно сделать, это проверить возможные состояния гонки, чтобы убедиться, что команда не была прервана, пока находилась в очереди:
+
+[.programlisting]
+....
+ struct ccb_scsiio *csio = &ccb->csio;
+
+ if ((ccb_h->status & CAM_STATUS_MASK) != CAM_REQ_INPROG) {
+ xpt_done(ccb);
+ return;
+ }
+....
+
+Также мы проверяем, что устройство вообще поддерживается нашим контроллером:
+
+[.programlisting]
+....
+ if (ccb_h->target_id > OUR_MAX_SUPPORTED_TARGET_ID
+ || cch_h->target_id == OUR_SCSI_CONTROLLERS_OWN_ID) {
+ ccb_h->status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+ if (ccb_h->target_lun > OUR_MAX_SUPPORTED_LUN) {
+ ccb_h->status = CAM_LUN_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+....
+
+Затем выделяем все необходимые структуры данных (такие как зависящий от карты блок управления оборудованием), которые нам нужны для обработки этого запроса. Если мы не можем этого сделать, то замораживаем очередь SIM и запоминаем, что у нас есть отложенная операция, возвращаем CCB обратно и просим CAM поставить его в очередь снова. Позже, когда ресурсы станут доступны, очередь SIM должна быть разморожена путём возврата CCB с установленным битом `CAM_SIMQ_RELEASE` в его статусе. В противном случае, если всё прошло успешно, связываем CCB с блоком управления оборудованием (HCB) и помечаем его как поставленный в очередь.
+
+[.programlisting]
+....
+ struct xxx_hcb *hcb = allocate_hcb(softc, unit, bus);
+
+ if (hcb == NULL) {
+ softc->flags |= RESOURCE_SHORTAGE;
+ xpt_freeze_simq(sim, /*count*/1);
+ ccb_h->status = CAM_REQUEUE_REQ;
+ xpt_done(ccb);
+ return;
+ }
+
+ hcb->ccb = ccb; ccb_h->ccb_hcb = (void *)hcb;
+ ccb_h->status |= CAM_SIM_QUEUED;
+....
+
+Извлечь целевые данные из CCB в аппаратный блок управления. Проверить, запрошено ли назначение тега, и если да, то сгенерировать уникальный тег и построить сообщения тега SCSI. Драйвер SIM также отвечает за согласование с устройствами для установки максимальной взаимно поддерживаемой ширины шины, синхронной скорости и смещения.
+
+[.programlisting]
+....
+ hcb->target = ccb_h->target_id; hcb->lun = ccb_h->target_lun;
+ generate_identify_message(hcb);
+ if (ccb_h->tag_action != CAM_TAG_ACTION_NONE)
+ generate_unique_tag_message(hcb, ccb_h->tag_action);
+ if (!target_negotiated(hcb))
+ generate_negotiation_messages(hcb);
+....
+
+Затем настройте команду SCSI. Хранилище команды может быть указано в CCB различными способами, определяемыми флагами CCB. Буфер команды может содержаться в CCB или указываться на него; в последнем случае указатель может быть физическим или виртуальным. Поскольку оборудованию обычно требуется физический адрес, мы всегда преобразуем адрес в физический, как правило, используя API busdma.
+
+В случае, если запрашивается физический адрес, допустимо вернуть CCB со статусом `CAM_REQ_INVALID`, текущие драйверы так и делают. При необходимости физический адрес также может быть преобразован или отображен обратно в виртуальный, но с большими трудностями, поэтому мы этого не делаем.
+
+[.programlisting]
+....
+ if (ccb_h->flags & CAM_CDB_POINTER) {
+ /* CDB is a pointer */
+ if (!(ccb_h->flags & CAM_CDB_PHYS)) {
+ /* CDB pointer is virtual */
+ hcb->cmd = vtobus(csio->cdb_io.cdb_ptr);
+ } else {
+ /* CDB pointer is physical */
+ hcb->cmd = csio->cdb_io.cdb_ptr ;
+ }
+ } else {
+ /* CDB is in the ccb (buffer) */
+ hcb->cmd = vtobus(csio->cdb_io.cdb_bytes);
+ }
+ hcb->cmdlen = csio->cdb_len;
+....
+
+Теперь настало время настроить данные. Опять же, хранилище данных может быть указано в CCB различными интересными способами, определяемыми флагами CCB. Сначала мы получаем направление передачи данных. Самый простой случай — если нет данных для передачи:
+
+[.programlisting]
+....
+ int dir = (ccb_h->flags & CAM_DIR_MASK);
+
+ if (dir == CAM_DIR_NONE)
+ goto end_data;
+....
+
+Затем мы проверяем, находятся ли данные в одном фрагменте или в списке scatter-gather, а также являются ли адреса физическими или виртуальными. SCSI-контроллер может обрабатывать только ограниченное количество фрагментов ограниченной длины. Если запрос превышает это ограничение, мы возвращаем ошибку. Мы используем специальную функцию для возврата CCB, чтобы в одном месте обрабатывать нехватку ресурсов HCB. Функции для добавления фрагментов зависят от драйвера, и здесь мы оставляем их без детальной реализации. Подробности о проблемах трансляции адресов см. в описании обработки SCSI-команд (CDB). Если какая-то вариация слишком сложна или невозможна для реализации с конкретной картой, допустимо вернуть статус `CAM_REQ_INVALID`. На самом деле, похоже, что возможность scatter-gather нигде в коде CAM сейчас не используется. Но как минимум случай с единичным неразделённым виртуальным буфером должен быть реализован, так как он активно используется CAM.
+
+[.programlisting]
+....
+ int rv;
+
+ initialize_hcb_for_data(hcb);
+
+ if ((!(ccb_h->flags & CAM_SCATTER_VALID)) {
+ /* single buffer */
+ if (!(ccb_h->flags & CAM_DATA_PHYS)) {
+ rv = add_virtual_chunk(hcb, csio->data_ptr, csio->dxfer_len, dir);
+ }
+ } else {
+ rv = add_physical_chunk(hcb, csio->data_ptr, csio->dxfer_len, dir);
+ }
+ } else {
+ int i;
+ struct bus_dma_segment *segs;
+ segs = (struct bus_dma_segment *)csio->data_ptr;
+
+ if ((ccb_h->flags & CAM_SG_LIST_PHYS) != 0) {
+ /* The SG list pointer is physical */
+ rv = setup_hcb_for_physical_sg_list(hcb, segs, csio->sglist_cnt);
+ } else if (!(ccb_h->flags & CAM_DATA_PHYS)) {
+ /* SG buffer pointers are virtual */
+ for (i = 0; i < csio->sglist_cnt; i++) {
+ rv = add_virtual_chunk(hcb, segs[i].ds_addr,
+ segs[i].ds_len, dir);
+ if (rv != CAM_REQ_CMP)
+ break;
+ }
+ } else {
+ /* SG buffer pointers are physical */
+ for (i = 0; i < csio->sglist_cnt; i++) {
+ rv = add_physical_chunk(hcb, segs[i].ds_addr,
+ segs[i].ds_len, dir);
+ if (rv != CAM_REQ_CMP)
+ break;
+ }
+ }
+ }
+ if (rv != CAM_REQ_CMP) {
+ /* we expect that add_*_chunk() functions return CAM_REQ_CMP
+ * if they added a chunk successfully, CAM_REQ_TOO_BIG if
+ * the request is too big (too many bytes or too many chunks),
+ * CAM_REQ_INVALID in case of other troubles
+ */
+ free_hcb_and_ccb_done(hcb, ccb, rv);
+ return;
+ }
+ end_data:
+....
+
+Если отключение запрещено для этого CCB, мы передаем эту информацию в hcb:
+
+[.programlisting]
+....
+ if (ccb_h->flags & CAM_DIS_DISCONNECT)
+ hcb_disable_disconnect(hcb);
+....
+
+Если контроллер способен самостоятельно выполнять команду REQUEST SENSE, то ему также следует передать значение флага CAM_DIS_AUTOSENSE, чтобы предотвратить автоматическое выполнение REQUEST SENSE, если подсистема CAM этого не требует.
+
+Осталось только установить таймаут, передать наш hcb оборудованию и вернуться, остальное будет сделано обработчиком прерывания (или обработчиком таймаута).
+
+[.programlisting]
+....
+ ccb_h->timeout_ch = timeout(xxx_timeout, (caddr_t) hcb,
+ (ccb_h->timeout * hz) / 1000); /* convert milliseconds to ticks */
+ put_hcb_into_hardware_queue(hcb);
+ return;
+....
+
+И вот возможная реализация функции, возвращающей CCB:
+
+[.programlisting]
+....
+ static void
+ free_hcb_and_ccb_done(struct xxx_hcb *hcb, union ccb *ccb, u_int32_t status)
+ {
+ struct xxx_softc *softc = hcb->softc;
+
+ ccb->ccb_h.ccb_hcb = 0;
+ if (hcb != NULL) {
+ untimeout(xxx_timeout, (caddr_t) hcb, ccb->ccb_h.timeout_ch);
+ /* we're about to free a hcb, so the shortage has ended */
+ if (softc->flags & RESOURCE_SHORTAGE) {
+ softc->flags &= ~RESOURCE_SHORTAGE;
+ status |= CAM_RELEASE_SIMQ;
+ }
+ free_hcb(hcb); /* also removes hcb from any internal lists */
+ }
+ ccb->ccb_h.status = status |
+ (ccb->ccb_h.status & ~(CAM_STATUS_MASK|CAM_SIM_QUEUED));
+ xpt_done(ccb);
+ }
+....
+
+=== _XPT_RESET_DEV_ - отправить устройству сообщение SCSI "BUS DEVICE RESET"
+
+В CCB не передаются данные, кроме заголовка, и наиболее интересным аргументом в нём является target_id. В зависимости от аппаратного обеспечения контроллера может быть создан аппаратный блок управления (как для запроса XPT_SCSI_IO, см. описание запроса XPT_SCSI_IO) и отправлен контроллеру, или SCSI-контроллер может быть немедленно запрограммирован на отправку этого сообщения RESET устройству, или этот запрос может просто не поддерживаться (и возвращать статус `CAM_REQ_INVALID`). Также при завершении запроса все отключенные транзакции для этого целевого устройства должны быть прерваны (вероятно, в процедуре прерывания).
+
+Кроме того, все текущие переговоры для цели теряются при сбросе, поэтому они также могут быть очищены. Или их очистка может быть отложена, так как в любом случае цель запросит повторные переговоры при следующей транзакции.
+
+=== _XPT_RESET_BUS_ - отправить сигнал RESET на шину SCSI
+
+В CCB не передаются аргументы, единственный интересный аргумент — это шина SCSI, указанная структурой sim.
+
+Минималистичная реализация могла бы пропустить SCSI-переговоры для всех устройств на шине и вернуть статус CAM_REQ_CMP.
+
+Правильная реализация дополнительно должна фактически сбросить шину SCSI (возможно, также сбросить контроллер SCSI) и пометить все обрабатываемые CCB, как находящиеся в аппаратной очереди, так и отключенные, как завершенные со статусом CAM_SCSI_BUS_RESET. Например:
+[.programlisting]
+....
+ int targ, lun;
+ struct xxx_hcb *h, *hh;
+ struct ccb_trans_settings neg;
+ struct cam_path *path;
+
+ /* The SCSI bus reset may take a long time, in this case its completion
+ * should be checked by interrupt or timeout. But for simplicity
+ * we assume here that it is really fast.
+ */
+ reset_scsi_bus(softc);
+
+ /* drop all enqueued CCBs */
+ for (h = softc->first_queued_hcb; h != NULL; h = hh) {
+ hh = h->next;
+ free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);
+ }
+
+ /* the clean values of negotiations to report */
+ neg.bus_width = 8;
+ neg.sync_period = neg.sync_offset = 0;
+ neg.valid = (CCB_TRANS_BUS_WIDTH_VALID
+ | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);
+
+ /* drop all disconnected CCBs and clean negotiations */
+ for (targ=0; targ <= OUR_MAX_SUPPORTED_TARGET; targ++) {
+ clean_negotiations(softc, targ);
+
+ /* report the event if possible */
+ if (xpt_create_path(&path, /*periph*/NULL,
+ cam_sim_path(sim), targ,
+ CAM_LUN_WILDCARD) == CAM_REQ_CMP) {
+ xpt_async(AC_TRANSFER_NEG, path, &neg);
+ xpt_free_path(path);
+ }
+
+ for (lun=0; lun <= OUR_MAX_SUPPORTED_LUN; lun++)
+ for (h = softc->first_discon_hcb[targ][lun]; h != NULL; h = hh) {
+ hh=h->next;
+ free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);
+ }
+ }
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+
+ /* report the event */
+ xpt_async(AC_BUS_RESET, softc->wpath, NULL);
+ return;
+....
+
+Реализация сброса шины SCSI в виде функции может быть хорошей идеей, так как она может быть повторно использована функцией таймаута в качестве последнего средства, если что-то пойдёт не так.
+
+=== _XPT_ABORT_ - прервать указанный CCB
+
+Аргументы передаются в экземпляре "struct ccb_abort cab" объединения ccb. Единственное поле аргумента в нём:
+
+* _abort_ccb_ — указатель на CCB, который необходимо прервать
+
+Если прерывание не поддерживается, просто верните статус CAM_UA_ABORT. Это также простой способ минимальной реализации этого вызова — в любом случае возвращать CAM_UA_ABORT.
+
+Трудный путь — честно реализовать этот запрос. Сначала проверьте, что прерывание применяется к SCSI-транзакции:
+
+[.programlisting]
+....
+ struct ccb *abort_ccb;
+ abort_ccb = ccb->cab.abort_ccb;
+
+ if (abort_ccb->ccb_h.func_code != XPT_SCSI_IO) {
+ ccb->ccb_h.status = CAM_UA_ABORT;
+ xpt_done(ccb);
+ return;
+ }
+....
+
+Затем необходимо найти этот CCB в нашей очереди. Это можно сделать, пройдясь по списку всех наших блоков управления оборудованием в поисках связанного с этим CCB:
+
+[.programlisting]
+....
+ struct xxx_hcb *hcb, *h;
+
+ hcb = NULL;
+
+ /* We assume that softc->first_hcb is the head of the list of all
+ * HCBs associated with this bus, including those enqueued for
+ * processing, being processed by hardware and disconnected ones.
+ */
+ for (h = softc->first_hcb; h != NULL; h = h->next) {
+ if (h->ccb == abort_ccb) {
+ hcb = h;
+ break;
+ }
+ }
+
+ if (hcb == NULL) {
+ /* no such CCB in our queue */
+ ccb->ccb_h.status = CAM_PATH_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+
+ hcb=found_hcb;
+....
+
+Теперь мы рассмотрим текущее состояние обработки HCB. Он может находиться в очереди, ожидая отправки на шину SCSI, передаваться в данный момент, быть отключенным и ожидать результата команды, или фактически завершённым с точки зрения аппаратуры, но ещё не отмеченным программным обеспечением, как выполненный. Чтобы избежать состояний гонки с аппаратурой, мы помечаем HCB как прерванный, так что если этот HCB вот-вот будет отправлен на шину SCSI, контроллер SCSI увидит этот флаг и пропустит его.
+
+[.programlisting]
+....
+ int hstatus;
+
+ /* shown as a function, in case special action is needed to make
+ * this flag visible to hardware
+ */
+ set_hcb_flags(hcb, HCB_BEING_ABORTED);
+
+ abort_again:
+
+ hstatus = get_hcb_status(hcb);
+ switch (hstatus) {
+ case HCB_SITTING_IN_QUEUE:
+ remove_hcb_from_hardware_queue(hcb);
+ /* FALLTHROUGH */
+ case HCB_COMPLETED:
+ /* this is an easy case */
+ free_hcb_and_ccb_done(hcb, abort_ccb, CAM_REQ_ABORTED);
+ break;
+....
+
+Если CCB передаётся в данный момент, мы хотели бы сигнализировать контроллеру SCSI аппаратно-зависимым способом, что хотим прервать текущую передачу. Контроллер SCSI установит сигнал SCSI ATTENTION, и когда целевое устройство ответит на него, отправит сообщение ABORT. Мы также сбрасываем таймаут, чтобы убедиться, что целевое устройство не засыпает навсегда. Если команда не будет прервана в разумное время, например, за 10 секунд, процедура таймаута продолжит работу и сбросит всю шину SCSI. Поскольку команда будет прервана в разумные сроки, мы можем просто вернуть запрос на прерывание как успешно выполненный и пометить прерванный CCB как прерванный (но пока не помечать его как завершённый).
+
+[.programlisting]
+....
+ case HCB_BEING_TRANSFERRED:
+ untimeout(xxx_timeout, (caddr_t) hcb, abort_ccb->ccb_h.timeout_ch);
+ abort_ccb->ccb_h.timeout_ch =
+ timeout(xxx_timeout, (caddr_t) hcb, 10 * hz);
+ abort_ccb->ccb_h.status = CAM_REQ_ABORTED;
+ /* ask the controller to abort that HCB, then generate
+ * an interrupt and stop
+ */
+ if (signal_hardware_to_abort_hcb_and_stop(hcb) < 0) {
+ /* oops, we missed the race with hardware, this transaction
+ * got off the bus before we aborted it, try again */
+ goto abort_again;
+ }
+
+ break;
+....
+
+Если CCB находится в списке отключенных, то настроить его как запрос прерывания и повторно поставить в начало аппаратной очереди. Сбросить таймаут и сообщить о завершении запроса прерывания.
+
+[.programlisting]
+....
+ case HCB_DISCONNECTED:
+ untimeout(xxx_timeout, (caddr_t) hcb, abort_ccb->ccb_h.timeout_ch);
+ abort_ccb->ccb_h.timeout_ch =
+ timeout(xxx_timeout, (caddr_t) hcb, 10 * hz);
+ put_abort_message_into_hcb(hcb);
+ put_hcb_at_the_front_of_hardware_queue(hcb);
+ break;
+ }
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ return;
+....
+
+Вот и все, что касается запроса ABORT, хотя есть еще один момент. Поскольку сообщение ABORT очищает все текущие транзакции на LUN, нам необходимо пометить все остальные активные транзакции на этом LUN как прерванные. Это должно быть выполнено в процедуре аппаратного прерывания после того, как транзакция будет прервана.
+
+Реализация прерывания CCB в виде функции может быть довольно хорошей идеей, эта функция может быть повторно использована, если транзакция ввода-вывода превысит время ожидания. Единственное различие будет в том, что для транзакции с истекшим временем ожидания будет возвращён статус CAM_CMD_TIMEOUT. Тогда код в case XPT_ABORT будет небольшим, например:
+
+[.programlisting]
+....
+ case XPT_ABORT:
+ struct ccb *abort_ccb;
+ abort_ccb = ccb->cab.abort_ccb;
+
+ if (abort_ccb->ccb_h.func_code != XPT_SCSI_IO) {
+ ccb->ccb_h.status = CAM_UA_ABORT;
+ xpt_done(ccb);
+ return;
+ }
+ if (xxx_abort_ccb(abort_ccb, CAM_REQ_ABORTED) < 0)
+ /* no such CCB in our queue */
+ ccb->ccb_h.status = CAM_PATH_INVALID;
+ else
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ return;
+....
+
+=== _XPT_SET_TRAN_SETTINGS_ - явно установить значения настроек передачи SCSI
+
+Аргументы передаются в экземпляре "struct ccb_trans_setting cts" объединения ccb:
+
+* _valid_ - битовая маска, показывающая, какие настройки должны быть обновлены:
+** _CCB_TRANS_SYNC_RATE_VALID_ - скорость синхронной передачи
+** _CCB_TRANS_SYNC_OFFSET_VALID_ - синхронное смещение
+** _CCB_TRANS_BUS_WIDTH_VALID_ - ширина шины
+** _CCB_TRANS_DISC_VALID_ - установить разрешение/запрет отключения
+** _CCB_TRANS_TQ_VALID_ - установить разрешение/запрет очередей с тегами
+* _flags_ - состоит из двух частей: бинарных аргументов и идентификации подопераций. Бинарные аргументы:
+** _CCB_TRANS_DISC_ENB_ - разрешить отключение
+** _CCB_TRANS_TAG_ENB_ - разрешить тегированную очередь
+* подоперации:
+** _CCB_TRANS_CURRENT_SETTINGS_ - изменить текущие параметры согласования
+** _CCB_TRANS_USER_SETTINGS_ - сохранять желаемые пользовательские значения sync_period, sync_offset - самоочевидные параметры; если sync_offset==0, то запрашивается асинхронный режим bus_width - ширина шины в битах (не в байтах)
+
+Поддерживаются два набора согласованных параметров: пользовательские настройки и текущие настройки. Пользовательские настройки не так часто используются в драйверах SIM, это в основном просто область памяти, где верхние уровни могут сохранять (и позже извлекать) свои представления о параметрах. Установка пользовательских параметров не вызывает повторного согласования скоростей передачи. Однако, когда SCSI-контроллер выполняет согласование, он никогда не должен устанавливать значения выше пользовательских параметров, так что они по сути являются верхней границей.
+
+Текущие настройки, как следует из названия, являются текущими. Их изменение означает, что параметры должны быть повторно согласованы при следующей передаче. Опять же, эти «новые текущие настройки» не предназначены для принудительного применения к устройству, они лишь используются в качестве начального шага переговоров. Кроме того, они должны быть ограничены реальными возможностями SCSI-контроллера: например, если SCSI-контроллер имеет 8-битную шину, а запрос требует установки 16-битных передач, этот параметр должен быть тихо усечён до 8-битных передач перед отправкой на устройство.
+
+Один нюанс заключается в том, что ширина шины и синхронные параметры относятся к цели, тогда как параметры отключения и включения тегов относятся к логическому устройству LUN.
+
+Рекомендуемая реализация заключается в хранении 3 наборов согласованных параметров (ширина шины и синхронная передача):
+
+* _user_ - пользовательский набор, как указано выше
+* _current_ - тот, который фактически действуют
+* _goal_ - тот набор, который запрошен для установки параметров в качестве "текущих"
+
+Код выглядит следующим образом:
+
+[.programlisting]
+....
+ struct ccb_trans_settings *cts;
+ int targ, lun;
+ int flags;
+
+ cts = &ccb->cts;
+ targ = ccb_h->target_id;
+ lun = ccb_h->target_lun;
+ flags = cts->flags;
+ if (flags & CCB_TRANS_USER_SETTINGS) {
+ if (flags & CCB_TRANS_SYNC_RATE_VALID)
+ softc->user_sync_period[targ] = cts->sync_period;
+ if (flags & CCB_TRANS_SYNC_OFFSET_VALID)
+ softc->user_sync_offset[targ] = cts->sync_offset;
+ if (flags & CCB_TRANS_BUS_WIDTH_VALID)
+ softc->user_bus_width[targ] = cts->bus_width;
+
+ if (flags & CCB_TRANS_DISC_VALID) {
+ softc->user_tflags[targ][lun] &= ~CCB_TRANS_DISC_ENB;
+ softc->user_tflags[targ][lun] |= flags & CCB_TRANS_DISC_ENB;
+ }
+ if (flags & CCB_TRANS_TQ_VALID) {
+ softc->user_tflags[targ][lun] &= ~CCB_TRANS_TQ_ENB;
+ softc->user_tflags[targ][lun] |= flags & CCB_TRANS_TQ_ENB;
+ }
+ }
+ if (flags & CCB_TRANS_CURRENT_SETTINGS) {
+ if (flags & CCB_TRANS_SYNC_RATE_VALID)
+ softc->goal_sync_period[targ] =
+ max(cts->sync_period, OUR_MIN_SUPPORTED_PERIOD);
+ if (flags & CCB_TRANS_SYNC_OFFSET_VALID)
+ softc->goal_sync_offset[targ] =
+ min(cts->sync_offset, OUR_MAX_SUPPORTED_OFFSET);
+ if (flags & CCB_TRANS_BUS_WIDTH_VALID)
+ softc->goal_bus_width[targ] = min(cts->bus_width, OUR_BUS_WIDTH);
+
+ if (flags & CCB_TRANS_DISC_VALID) {
+ softc->current_tflags[targ][lun] &= ~CCB_TRANS_DISC_ENB;
+ softc->current_tflags[targ][lun] |= flags & CCB_TRANS_DISC_ENB;
+ }
+ if (flags & CCB_TRANS_TQ_VALID) {
+ softc->current_tflags[targ][lun] &= ~CCB_TRANS_TQ_ENB;
+ softc->current_tflags[targ][lun] |= flags & CCB_TRANS_TQ_ENB;
+ }
+ }
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ return;
+....
+
+Затем, когда следующий запрос ввода-вывода будет обработан, он проверит, нужно ли повторное согласование, например, вызовом функции target_negotiated(hcb). Это может быть реализовано следующим образом:
+
+[.programlisting]
+....
+ int
+ target_negotiated(struct xxx_hcb *hcb)
+ {
+ struct softc *softc = hcb->softc;
+ int targ = hcb->targ;
+
+ if (softc->current_sync_period[targ] != softc->goal_sync_period[targ]
+ || softc->current_sync_offset[targ] != softc->goal_sync_offset[targ]
+ || softc->current_bus_width[targ] != softc->goal_bus_width[targ])
+ return 0; /* FALSE */
+ else
+ return 1; /* TRUE */
+ }
+....
+
+После пересогласования значений полученные значения должны быть присвоены как текущим, так и целевым параметрам, чтобы для будущих операций ввода-вывода текущие и целевые параметры совпадали, и функция `target_negotiated()` возвращала TRUE. При инициализации карты (в `xxx_attach()`) текущие параметры согласования должны быть инициализированы узким асинхронным режимом, а целевые и текущие значения должны быть инициализированы максимальными значениями, поддерживаемыми контроллером.
+
+=== _XPT_GET_TRAN_SETTINGS_ - получить значения настроек передачи SCSI
+
+Эта операция является обратной XPT_SET_TRAN_SETTINGS. Заполните экземпляр CCB "struct ccb_trans_setting cts" данными, запрошенными флагами CCB_TRANS_CURRENT_SETTINGS или CCB_TRANS_USER_SETTINGS (если установлены оба, существующие драйверы возвращают текущие настройки). Установите все биты в поле valid.
+
+=== _XPT_CALC_GEOMETRY_ - вычислить логическую (BIOS) геометрию диска
+
+Аргументы передаются в экземпляре "struct ccb_calc_geometry ccg" объединения ccb:
+
+* _block_size_ - вход, размер блока (также известный как сектор) в байтах
+* _volume_size_ - вход, размер тома в байтах
+* _cylinders_ - выход, логические цилиндры
+* _heads_ - выход, логические головки
+* _secs_per_track_ - выход, логических секторов на дорожку
+
+Если возвращённая геометрия значительно отличается от той, которую предполагает BIOS SCSI-контроллера, и диск на этом SCSI-контроллере используется как загрузочный, система может не загрузиться. Типичный пример расчёта, взятый из драйвера `aic7xxx`, выглядит следующим образом:
+
+[.programlisting]
+....
+ struct ccb_calc_geometry *ccg;
+ u_int32_t size_mb;
+ u_int32_t secs_per_cylinder;
+ int extended;
+
+ ccg = &ccb->ccg;
+ size_mb = ccg->volume_size
+ / ((1024L * 1024L) / ccg->block_size);
+ extended = check_cards_EEPROM_for_extended_geometry(softc);
+
+ if (size_mb > 1024 && extended) {
+ ccg->heads = 255;
+ ccg->secs_per_track = 63;
+ } else {
+ ccg->heads = 64;
+ ccg->secs_per_track = 32;
+ }
+ secs_per_cylinder = ccg->heads * ccg->secs_per_track;
+ ccg->cylinders = ccg->volume_size / secs_per_cylinder;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ return;
+....
+
+Это дает общее представление, точный расчет зависит от особенностей конкретной BIOS. Если BIOS не предоставляет возможности установить флаг "расширенной трансляции" в EEPROM, этот флаг обычно следует считать равным 1. Другие популярные геометрии:
+
+[.programlisting]
+....
+ 128 heads, 63 sectors - Symbios controllers
+ 16 heads, 63 sectors - old controllers
+....
+
+Некоторые системные BIOS и SCSI BIOS конфликтуют друг с другом с переменным успехом. Например, комбинация Symbios 875/895 SCSI и Phoenix BIOS может выдавать геометрию 128/63 после включения питания и 255/63 после жесткого сброса или мягкой перезагрузки.
+
+=== _XPT_PATH_INQ_ - запрос пути, другими словами, получение свойств драйвера SIM и контроллера SCSI (также известного как HBA - Host Bus Adapter)
+
+Свойства возвращаются в экземпляре "struct ccb_pathinq cpi" объединения ccb:
+
+* `version_num` - номер версии драйвера SIM, в настоящее время все драйверы используют 1
+* hba_inquiry - битовая маска функций, поддерживаемых контроллером:
+** PI_MDP_ABLE - поддерживает сообщение MDP (что-то из SCSI3?)
+** PI_WIDE_32 — поддерживает 32-битную широкую SCSI
+** PI_WIDE_16 — поддерживает 16-битную широкую SCSI
+** PI_SDTR_ABLE - может согласовать синхронную скорость передачи
+** PI_LINKED_CDB - поддерживает связанные команды
+** PI_TAG_ABLE - поддерживает помеченные команды
+** PI_SOFT_RST — поддерживает альтернативу мягкого сброса (жесткий сброс и мягкий сброс являются взаимоисключающими в пределах шины SCSI)
+* target_sprt - флаги поддержки целевого режима, 0 если не поддерживается
+* hba_misc - различные функции контроллера:
+** PIM_SCANHILO - сканирование шины от высокого ID к низкому ID
+** PIM_NOREMOVE - съемные устройства не включены в сканирование
+** PIM_NOINITIATOR - роль инициатора не поддерживается
+** PIM_NOBUSRESET - пользователь отключил начальный BUS RESET
+* hba_eng_cnt - загадочное количество движков HBA, что-то связанное со сжатием, в настоящее время всегда устанавливается в 0
+* vuhba_flags - уникальные флаги производителя, в настоящее время не используются
+* max_target - максимальный поддерживаемый идентификатор целевого устройства (7 для 8-битной шины, 15 для 16-битной шины, 127 для Fibre Channel)
+* max_lun - максимально поддерживаемый идентификатор LUN (7 для старых SCSI-контроллеров, 63 для новых)
+* async_flags - битовая маска установленных обработчиков Async, в настоящее время не используется
+* hpath_id - наивысший Path ID в подсистеме, в настоящее время не используется
+* unit_number - номер контроллера, cam_sim_unit(sim)
+* bus_id - номер шины, cam_sim_bus(sim)
+* initiator_id - SCSI ID самого контроллера
+* base_transfer_speed - номинальная скорость передачи в КБ/с для асинхронных узкополосных передач, равна 3300 для SCSI
+* sim_vid - идентификатор производителя драйвера SIM, строка с нулевым окончанием максимальной длины SIM_IDLEN, включая завершающий ноль
+* hba_vid - идентификатор производителя SCSI-контроллера, строка с нулевым окончанием максимальной длины HBA_IDLEN, включая завершающий ноль
+* dev_name - имя драйвера устройства, строка с нулевым окончанием максимальной длины DEV_IDLEN, включая завершающий ноль, эквивалентно cam_sim_name(sim)
+
+Рекомендуемый способ установки строковых полей — использование strncpy, например:
+
+[.programlisting]
+....
+ strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+....
+
+После установки значений установите статус в CAM_REQ_CMP и пометьте CCB как завершённый.
+
+[[scsi-polling]]
+== Опрос xxx_poll
+
+[.programlisting]
+....
+static void xxx_poll(struct cam_sim *);
+....
+
+Функция poll используется для имитации прерываний, когда подсистема прерываний не функционирует (например, когда система аварийно завершила работу и создает дамп памяти). Подсистема CAM устанавливает соответствующий уровень прерывания перед вызовом процедуры poll. Таким образом, все, что ей нужно сделать, — это вызвать процедуру прерывания (или наоборот, процедура poll может выполнять реальные действия, а процедура прерывания просто вызывает процедуру poll). Зачем тогда нужна отдельная функция? Это связано с различными соглашениями о вызовах. Процедура `xxx_poll` получает указатель на структуру cam_sim в качестве аргумента, в то время как процедура прерывания PCI по общему соглашению получает указатель на структуру `xxx_softc`, а процедура прерывания ISA получает только номер устройства. Таким образом, процедура poll обычно выглядит следующим образом:
+
+[.programlisting]
+....
+static void
+xxx_poll(struct cam_sim *sim)
+{
+ xxx_intr((struct xxx_softc *)cam_sim_softc(sim)); /* for PCI device */
+}
+....
+
+или
+
+[.programlisting]
+....
+static void
+xxx_poll(struct cam_sim *sim)
+{
+ xxx_intr(cam_sim_unit(sim)); /* for ISA device */
+}
+....
+
+[[scsi-async]]
+== Асинхронные события
+
+Если была настроена асинхронная callback-функция для события, то callback-функция должна быть определена.
+
+[.programlisting]
+....
+static void
+ahc_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
+....
+
+* callback_arg - значение, переданное при регистрации callback
+* code - определяет тип события
+* path - определяет устройства, к которым применяется событие
+* arg - аргумент, специфичный для события
+
+Реализация для одного типа события, AC_LOST_DEVICE, выглядит следующим образом:
+
+[.programlisting]
+....
+ struct xxx_softc *softc;
+ struct cam_sim *sim;
+ int targ;
+ struct ccb_trans_settings neg;
+
+ sim = (struct cam_sim *)callback_arg;
+ softc = (struct xxx_softc *)cam_sim_softc(sim);
+ switch (code) {
+ case AC_LOST_DEVICE:
+ targ = xpt_path_target_id(path);
+ if (targ <= OUR_MAX_SUPPORTED_TARGET) {
+ clean_negotiations(softc, targ);
+ /* send indication to CAM */
+ neg.bus_width = 8;
+ neg.sync_period = neg.sync_offset = 0;
+ neg.valid = (CCB_TRANS_BUS_WIDTH_VALID
+ | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);
+ xpt_async(AC_TRANSFER_NEG, path, &neg);
+ }
+ break;
+ default:
+ break;
+ }
+....
+
+[[scsi-interrupts]]
+== Прерывания
+
+Точный тип процедуры прерывания зависит от типа периферийной шины (PCI, ISA и так далее), к которой подключен SCSI-контроллер.
+
+Прерывания в драйверах SIM выполняются на уровне прерывания splcam. Поэтому в драйвере следует использовать `splcam()` для синхронизации между обработчиком прерывания и остальной частью драйвера (для драйверов, учитывающих многопроцессорность, ситуация становится ещё сложнее, но здесь мы этот случай не рассматриваем). Псевдокод в этом документе беззаботно игнорирует проблемы синхронизации. Реальный код так делать не должен. Простейший подход — установить `splcam()` при входе в другие функции и сбросить при выходе, защищая их одной большой критической секцией. Чтобы гарантировать восстановление уровня прерывания, можно определить обёрточную функцию, например:
+
+[.programlisting]
+....
+ static void
+ xxx_action(struct cam_sim *sim, union ccb *ccb)
+ {
+ int s;
+ s = splcam();
+ xxx_action1(sim, ccb);
+ splx(s);
+ }
+
+ static void
+ xxx_action1(struct cam_sim *sim, union ccb *ccb)
+ {
+ ... process the request ...
+ }
+....
+
+Этот подход прост и надежен, но проблема в том, что прерывания могут блокироваться на относительно долгое время, что негативно скажется на производительности системы. С другой стороны, функции семейства `spl()` имеют довольно высокие накладные расходы, поэтому большое количество мелких критических секций также может быть нежелательным.
+
+Условия, обрабатываемые процедурой прерывания, и детали сильно зависят от оборудования. Мы рассматриваем набор "типичных" условий.
+
+Сначала проверяем, было ли на шине событие SCSI сброса (вероятно, вызванное другим SCSI-контроллером на той же SCSI-шине). Если это так, мы отменяем все поставленные в очередь и отключенные запросы, сообщаем о событиях и повторно инициализируем наш SCSI-контроллер. Важно, чтобы во время этой инициализации контроллер не инициировал ещё один сброс, иначе два контроллера на одной SCSI-шине могут бесконечно обмениваться сбросами. Случай фатальной ошибки/зависания контроллера может быть обработан в том же месте, но, вероятно, также потребуется отправка сигнала RESET на SCSI-шину для сброса состояния соединений с SCSI-устройствами.
+
+[.programlisting]
+....
+ int fatal=0;
+ struct ccb_trans_settings neg;
+ struct cam_path *path;
+
+ if (detected_scsi_reset(softc)
+ || (fatal = detected_fatal_controller_error(softc))) {
+ int targ, lun;
+ struct xxx_hcb *h, *hh;
+
+ /* drop all enqueued CCBs */
+ for(h = softc->first_queued_hcb; h != NULL; h = hh) {
+ hh = h->next;
+ free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);
+ }
+
+ /* the clean values of negotiations to report */
+ neg.bus_width = 8;
+ neg.sync_period = neg.sync_offset = 0;
+ neg.valid = (CCB_TRANS_BUS_WIDTH_VALID
+ | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);
+
+ /* drop all disconnected CCBs and clean negotiations */
+ for (targ=0; targ <= OUR_MAX_SUPPORTED_TARGET; targ++) {
+ clean_negotiations(softc, targ);
+
+ /* report the event if possible */
+ if (xpt_create_path(&path, /*periph*/NULL,
+ cam_sim_path(sim), targ,
+ CAM_LUN_WILDCARD) == CAM_REQ_CMP) {
+ xpt_async(AC_TRANSFER_NEG, path, &neg);
+ xpt_free_path(path);
+ }
+
+ for (lun=0; lun <= OUR_MAX_SUPPORTED_LUN; lun++)
+ for (h = softc->first_discon_hcb[targ][lun]; h != NULL; h = hh) {
+ hh=h->next;
+ if (fatal)
+ free_hcb_and_ccb_done(h, h->ccb, CAM_UNREC_HBA_ERROR);
+ else
+ free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);
+ }
+ }
+
+ /* report the event */
+ xpt_async(AC_BUS_RESET, softc->wpath, NULL);
+
+ /* re-initialization may take a lot of time, in such case
+ * its completion should be signaled by another interrupt or
+ * checked on timeout - but for simplicity we assume here that
+ * it is really fast
+ */
+ if (!fatal) {
+ reinitialize_controller_without_scsi_reset(softc);
+ } else {
+ reinitialize_controller_with_scsi_reset(softc);
+ }
+ schedule_next_hcb(softc);
+ return;
+ }
+....
+
+Если прерывание не вызвано условием, общим для всего контроллера, то, вероятно, что-то произошло с текущим блоком управления аппаратным обеспечением. В зависимости от оборудования могут быть и другие события, не связанные с HCB, но мы их здесь не рассматриваем. Затем мы анализируем, что произошло с этим HCB:
+
+[.programlisting]
+....
+ struct xxx_hcb *hcb, *h, *hh;
+ int hcb_status, scsi_status;
+ int ccb_status;
+ int targ;
+ int lun_to_freeze;
+
+ hcb = get_current_hcb(softc);
+ if (hcb == NULL) {
+ /* either stray interrupt or something went very wrong
+ * or this is something hardware-dependent
+ */
+ handle as necessary;
+ return;
+ }
+
+ targ = hcb->target;
+ hcb_status = get_status_of_current_hcb(softc);
+....
+
+Сначала мы проверяем, завершился ли HCB, и если да, то проверяем возвращённый статус SCSI.
+
+[.programlisting]
+....
+ if (hcb_status == COMPLETED) {
+ scsi_status = get_completion_status(hcb);
+....
+
+Затем проверьте, связан ли этот статус с командой REQUEST SENSE, и если да, обработайте его простым способом.
+
+[.programlisting]
+....
+ if (hcb->flags & DOING_AUTOSENSE) {
+ if (scsi_status == GOOD) { /* autosense was successful */
+ hcb->ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
+ free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_SCSI_STATUS_ERROR);
+ } else {
+ autosense_failed:
+ free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_AUTOSENSE_FAIL);
+ }
+ schedule_next_hcb(softc);
+ return;
+ }
+....
+
+Иначе сама команда завершена, уделяйте больше внимания деталям. Если автоопределение не отключено для этого CCB и команда завершилась неудачно с данными состояния, выполните команду REQUEST SENSE для получения этих данных.
+
+[.programlisting]
+....
+ hcb->ccb->csio.scsi_status = scsi_status;
+ calculate_residue(hcb);
+
+ if ((hcb->ccb->ccb_h.flags & CAM_DIS_AUTOSENSE)==0
+ && (scsi_status == CHECK_CONDITION
+ || scsi_status == COMMAND_TERMINATED)) {
+ /* start auto-SENSE */
+ hcb->flags |= DOING_AUTOSENSE;
+ setup_autosense_command_in_hcb(hcb);
+ restart_current_hcb(softc);
+ return;
+ }
+ if (scsi_status == GOOD)
+ free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_REQ_CMP);
+ else
+ free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_SCSI_STATUS_ERROR);
+ schedule_next_hcb(softc);
+ return;
+ }
+....
+
+Типичным примером могут быть события согласования: сообщения согласования, полученные от цели SCSI (в ответ на нашу попытку согласования или по инициативе цели), или если цель не может согласовать (отклоняет наши сообщения согласования или не отвечает на них).
+
+[.programlisting]
+....
+ switch (hcb_status) {
+ case TARGET_REJECTED_WIDE_NEG:
+ /* revert to 8-bit bus */
+ softc->current_bus_width[targ] = softc->goal_bus_width[targ] = 8;
+ /* report the event */
+ neg.bus_width = 8;
+ neg.valid = CCB_TRANS_BUS_WIDTH_VALID;
+ xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &neg);
+ continue_current_hcb(softc);
+ return;
+ case TARGET_ANSWERED_WIDE_NEG:
+ {
+ int wd;
+
+ wd = get_target_bus_width_request(softc);
+ if (wd <= softc->goal_bus_width[targ]) {
+ /* answer is acceptable */
+ softc->current_bus_width[targ] =
+ softc->goal_bus_width[targ] = neg.bus_width = wd;
+
+ /* report the event */
+ neg.valid = CCB_TRANS_BUS_WIDTH_VALID;
+ xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &neg);
+ } else {
+ prepare_reject_message(hcb);
+ }
+ }
+ continue_current_hcb(softc);
+ return;
+ case TARGET_REQUESTED_WIDE_NEG:
+ {
+ int wd;
+
+ wd = get_target_bus_width_request(softc);
+ wd = min (wd, OUR_BUS_WIDTH);
+ wd = min (wd, softc->user_bus_width[targ]);
+
+ if (wd != softc->current_bus_width[targ]) {
+ /* the bus width has changed */
+ softc->current_bus_width[targ] =
+ softc->goal_bus_width[targ] = neg.bus_width = wd;
+
+ /* report the event */
+ neg.valid = CCB_TRANS_BUS_WIDTH_VALID;
+ xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &neg);
+ }
+ prepare_width_nego_rsponse(hcb, wd);
+ }
+ continue_current_hcb(softc);
+ return;
+ }
+....
+
+Затем мы обрабатываем любые ошибки, которые могли произойти во время автоопределения, тем же простым способом, что и раньше. В противном случае мы снова внимательно изучаем детали.
+
+[.programlisting]
+....
+ if (hcb->flags & DOING_AUTOSENSE)
+ goto autosense_failed;
+
+ switch (hcb_status) {
+....
+
+Следующее событие, которое мы рассматриваем, — это неожиданное отключение. Оно считается нормальным после сообщения ABORT или BUS DEVICE RESET и аномальным в остальных случаях.
+
+[.programlisting]
+....
+ case UNEXPECTED_DISCONNECT:
+ if (requested_abort(hcb)) {
+ /* abort affects all commands on that target+LUN, so
+ * mark all disconnected HCBs on that target+LUN as aborted too
+ */
+ for (h = softc->first_discon_hcb[hcb->target][hcb->lun];
+ h != NULL; h = hh) {
+ hh=h->next;
+ free_hcb_and_ccb_done(h, h->ccb, CAM_REQ_ABORTED);
+ }
+ ccb_status = CAM_REQ_ABORTED;
+ } else if (requested_bus_device_reset(hcb)) {
+ int lun;
+
+ /* reset affects all commands on that target, so
+ * mark all disconnected HCBs on that target+LUN as reset
+ */
+
+ for (lun=0; lun <= OUR_MAX_SUPPORTED_LUN; lun++)
+ for (h = softc->first_discon_hcb[hcb->target][lun];
+ h != NULL; h = hh) {
+ hh=h->next;
+ free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);
+ }
+
+ /* send event */
+ xpt_async(AC_SENT_BDR, hcb->ccb->ccb_h.path_id, NULL);
+
+ /* this was the CAM_RESET_DEV request itself, it is completed */
+ ccb_status = CAM_REQ_CMP;
+ } else {
+ calculate_residue(hcb);
+ ccb_status = CAM_UNEXP_BUSFREE;
+ /* request the further code to freeze the queue */
+ hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;
+ lun_to_freeze = hcb->lun;
+ }
+ break;
+....
+
+Если цель отказывается принимать теги, мы уведомляем CAM об этом и возвращаем все команды для этого LUN:
+
+[.programlisting]
+....
+ case TAGS_REJECTED:
+ /* report the event */
+ neg.flags = 0 & ~CCB_TRANS_TAG_ENB;
+ neg.valid = CCB_TRANS_TQ_VALID;
+ xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &neg);
+
+ ccb_status = CAM_MSG_REJECT_REC;
+ /* request the further code to freeze the queue */
+ hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;
+ lun_to_freeze = hcb->lun;
+ break;
+....
+
+Затем мы проверяем ряд других условий, при этом обработка в основном ограничивается установкой статуса CCB:
+
+[.programlisting]
+....
+ case SELECTION_TIMEOUT:
+ ccb_status = CAM_SEL_TIMEOUT;
+ /* request the further code to freeze the queue */
+ hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;
+ lun_to_freeze = CAM_LUN_WILDCARD;
+ break;
+ case PARITY_ERROR:
+ ccb_status = CAM_UNCOR_PARITY;
+ break;
+ case DATA_OVERRUN:
+ case ODD_WIDE_TRANSFER:
+ ccb_status = CAM_DATA_RUN_ERR;
+ break;
+ default:
+ /* all other errors are handled in a generic way */
+ ccb_status = CAM_REQ_CMP_ERR;
+ /* request the further code to freeze the queue */
+ hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;
+ lun_to_freeze = CAM_LUN_WILDCARD;
+ break;
+ }
+....
+
+Затем мы проверяем, была ли ошибка достаточно серьёзной, чтобы заморозить очередь ввода до её обработки, и если да, то делаем это:
+
+[.programlisting]
+....
+ if (hcb->ccb->ccb_h.status & CAM_DEV_QFRZN) {
+ /* freeze the queue */
+ xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);
+
+ /* re-queue all commands for this target/LUN back to CAM */
+
+ for (h = softc->first_queued_hcb; h != NULL; h = hh) {
+ hh = h->next;
+
+ if (targ == h->targ
+ && (lun_to_freeze == CAM_LUN_WILDCARD || lun_to_freeze == h->lun))
+ free_hcb_and_ccb_done(h, h->ccb, CAM_REQUEUE_REQ);
+ }
+ }
+ free_hcb_and_ccb_done(hcb, hcb->ccb, ccb_status);
+ schedule_next_hcb(softc);
+ return;
+....
+
+На этом общее описание обработки прерываний завершается, хотя для некоторых контроллеров могут потребоваться дополнительные действия.
+
+[[scsi-errors]]
+== Ошибки (Сводка)
+
+При выполнении запроса ввода-вывода может произойти множество ошибок. Причина ошибки может быть указана в статусе CCB с большим количеством деталей. Примеры использования разбросаны по всему документу. Для полноты изложения приведём сводку рекомендуемых действий при типичных ошибках:
+
+* _CAM_RESRC_UNAVAIL_ — некоторый ресурс временно недоступен, и драйвер SIM не может сгенерировать событие, когда он станет доступен. Примером такого ресурса может быть некоторый внутренний аппаратный ресурс контроллера, для которого контроллер не генерирует прерывание при его доступности.
+* _CAM_UNCOR_PARITY_ - произошла неисправимая ошибка четности
+* _CAM_DATA_RUN_ERR_ - переполнение данных или неожиданная фаза данных (направление передачи не соответствует указанному в CAM_DIR_MASK) или нечётная длина передачи для широкой передачи
+* _CAM_SEL_TIMEOUT_ - произошел таймаут выбора (цель не отвечает)
+* _CAM_CMD_TIMEOUT_ - произошло превышение времени ожидания команды (сработала функция таймаута)
+* _CAM_SCSI_STATUS_ERROR_ - устройство вернуло ошибку
+* _CAM_AUTOSENSE_FAIL_ - устройство вернуло ошибку и команда REQUEST SENSE завершилась неудачно
+* _CAM_MSG_REJECT_REC_ - получено сообщение MESSAGE REJECT
+* _CAM_SCSI_BUS_RESET_ - получен сброс шины SCSI
+* _CAM_REQ_CMP_ERR_ - произошла «невозможная» фаза SCSI или что-то столь же странное, либо это просто общая ошибка, если дополнительная информация недоступна
+* _CAM_UNEXP_BUSFREE_ - произошло неожиданное отключение
+* _CAM_BDR_SENT_ - Сообщение BUS DEVICE RESET было отправлено целевому устройству
+* _CAM_UNREC_HBA_ERROR_ - невосстановимая ошибка адаптера шины хоста
+* _CAM_REQ_TOO_BIG_ - запрос слишком велик для данного контроллера
+* _CAM_REQUEUE_REQ_ - этот запрос должен быть повторно поставлен в очередь для сохранения порядка транзакций. Обычно это происходит, когда SIM обнаруживает ошибку, которая должна заморозить очередь, и необходимо поместить другие запросы в очереди для цели на уровне SIM обратно в очередь XPT. Типичными случаями таких ошибок являются тайм-ауты выбора, тайм-ауты команд и другие подобные условия. В таких случаях проблемная команда возвращает статус, указывающий на ошибку, а другие команды, которые ещё не были отправлены на шину, повторно ставятся в очередь.
+* _CAM_LUN_INVALID_ - идентификатор LUN в запросе не поддерживается контроллером SCSI
+* _CAM_TID_INVALID_ - идентификатор целевого устройства в запросе не поддерживается контроллером SCSI
+
+[[scsi-timeout]]
+== Обработка таймаутов
+
+Когда время ожидания для HCB истекает, этот запрос должен быть прерван, как и в случае с запросом XPT_ABORT. Единственное отличие заключается в том, что возвращаемый статус прерванного запроса должен быть CAM_CMD_TIMEOUT вместо CAM_REQ_ABORTED (вот почему реализацию прерывания лучше сделать в виде функции). Но есть ещё одна возможная проблема: что если сам запрос на прерывание зависнет? В этом случае шина SCSI должна быть сброшена, как и при запросе XPT_RESET_BUS (и идея о реализации этого в виде функции, вызываемой из обоих мест, применима и здесь). Также мы должны сбросить всю шину SCSI, если запрос на сброс устройства завис. В итоге функция обработки таймаута будет выглядеть следующим образом:
+
+[.programlisting]
+....
+static void
+xxx_timeout(void *arg)
+{
+ struct xxx_hcb *hcb = (struct xxx_hcb *)arg;
+ struct xxx_softc *softc;
+ struct ccb_hdr *ccb_h;
+
+ softc = hcb->softc;
+ ccb_h = &hcb->ccb->ccb_h;
+
+ if (hcb->flags & HCB_BEING_ABORTED || ccb_h->func_code == XPT_RESET_DEV) {
+ xxx_reset_bus(softc);
+ } else {
+ xxx_abort_ccb(hcb->ccb, CAM_CMD_TIMEOUT);
+ }
+}
+....
+
+Когда мы прерываем запрос, все остальные отключенные запросы к тому же целевому устройству/LUN также прерываются. Возникает вопрос: следует ли возвращать их со статусом CAM_REQ_ABORTED или CAM_CMD_TIMEOUT? Текущие драйверы используют CAM_CMD_TIMEOUT. Это кажется логичным, потому что если один запрос превысил время ожидания, то, вероятно, с устройством происходит что-то действительно плохое, и если их не трогать, они бы сами превысили время ожидания.
diff --git a/documentation/content/ru/books/arch-handbook/scsi/_index.po b/documentation/content/ru/books/arch-handbook/scsi/_index.po
new file mode 100644
index 0000000000..c992ac0b4e
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/scsi/_index.po
@@ -0,0 +1,4280 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-07-05 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookscsi_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:14
+#, no-wrap
+msgid "Common Access Method SCSI Controllers"
+msgstr "Контроллеры SCSI с общим методом доступа (CAM)"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1
+#, no-wrap
+msgid "Chapter 12. Common Access Method SCSI Controllers"
+msgstr "Глава 12. Контроллеры SCSI с общим методом доступа (CAM)"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:52
+#, no-wrap
+msgid "Synopsis"
+msgstr "Обзор"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:56
+msgid ""
+"This document assumes that the reader has a general understanding of device "
+"drivers in FreeBSD and of the SCSI protocol. Much of the information in "
+"this document was extracted from the drivers:"
+msgstr ""
+"Этот документ предполагает, что читатель имеет общее представление о "
+"драйверах устройств в FreeBSD и о протоколе SCSI. Большая часть информации в "
+"этом документе была извлечена из драйверов:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:58
+msgid ""
+"ncr ([.filename]#/sys/pci/ncr.c#) by Wolfgang Stanglmeier and Stefan Esser"
+msgstr ""
+"ncr ([.filename]#/sys/pci/ncr.c#) от Wolfgang Stanglmeier и Stefan Esser"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:59
+msgid "sym ([.filename]#/sys/dev/sym/sym_hipd.c#) by Gerard Roudier"
+msgstr "sym ([.filename]#/sys/dev/sym/sym_hipd.c#) от Gerard Roudier"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:60
+msgid "aic7xxx ([.filename]#/sys/dev/aic7xxx/aic7xxx.c#) by Justin T. Gibbs"
+msgstr "aic7xxx ([.filename]#/sys/dev/aic7xxx/aic7xxx.c#) от Justin T. Gibbs"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:63
+msgid ""
+"and from the CAM code itself (by Justin T. Gibbs, see [.filename]#/sys/cam/"
+"*#). When some solution looked the most logical and was essentially "
+"verbatim extracted from the code by Justin T. Gibbs, I marked it as "
+"\"recommended\"."
+msgstr ""
+"и из самого кода CAM (автор Justin T. Gibbs, см. [.filename]#/sys/cam/*#). "
+"Когда какое-то решение выглядело наиболее логичным и было практически "
+"дословно взято из кода Justin T. Gibbs, я отмечал его как \"рекомендуемое\"."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:71
+msgid ""
+"The document is illustrated with examples in pseudo-code. Although "
+"sometimes the examples have many details and look like real code, it is "
+"still pseudo-code. It was written to demonstrate the concepts in an "
+"understandable way. For a real driver other approaches may be more modular "
+"and efficient. It also abstracts from the hardware details, as well as "
+"issues that would cloud the demonstrated concepts or that are supposed to be "
+"described in the other chapters of the developers handbook. Such details "
+"are commonly shown as calls to functions with descriptive names, comments or "
+"pseudo-statements. Fortunately real life full-size examples with all the "
+"details can be found in the real drivers."
+msgstr ""
+"Документ иллюстрирован примерами на псевдокоде. Хотя иногда примеры содержат "
+"много деталей и выглядят как настоящий код, это всё ещё псевдокод. Он был "
+"написан, чтобы продемонстрировать концепции в понятной форме. Для реального "
+"драйвера могут быть более модульные и эффективные подходы. Также он "
+"абстрагируется от деталей оборудования, а также от вопросов, которые могли "
+"бы затмить демонстрируемые концепции или которые предполагается описать в "
+"других главах руководства разработчика. Такие детали обычно показаны в виде "
+"вызовов функций с описательными именами, комментариев или псевдооператоров. "
+"К счастью, полные примеры из реальной жизни со всеми деталями можно найти в "
+"реальных драйверах."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:73
+#, no-wrap
+msgid "General Architecture"
+msgstr "Общая Архитектура"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:79
+msgid ""
+"CAM stands for Common Access Method. It is a generic way to address the I/O "
+"buses in a SCSI-like way. This allows a separation of the generic device "
+"drivers from the drivers controlling the I/O bus: for example the disk "
+"driver becomes able to control disks on both SCSI, IDE, and/or any other bus "
+"so the disk driver portion does not have to be rewritten (or copied and "
+"modified) for every new I/O bus. Thus the two most important active "
+"entities are:"
+msgstr ""
+"CAM означает Common Access Method (Общий Метод Доступа). Это универсальный "
+"способ адресации шин ввода-вывода в стиле SCSI. Это позволяет отделить общие "
+"драйверы устройств от драйверов, управляющих шиной ввода-вывода: например, "
+"драйвер диска получает возможность управлять дисками как на SCSI, IDE, так и "
+"на любой другой шине, так что часть драйвера диска не нужно переписывать "
+"(или копировать и изменять) для каждой новой шины ввода-вывода. Таким "
+"образом, двумя наиболее важными активными сущностями являются:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:81
+msgid ""
+"_Peripheral Modules_ - a driver for peripheral devices (disk, tape, CD-ROM, "
+"etc.)"
+msgstr ""
+"_Модули периферийных устройств_ - драйвер для периферийных устройств (диски, "
+"ленты, CD-ROM и т.д.)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:82
+msgid ""
+"_SCSI Interface Modules_ (SIM) - a Host Bus Adapter drivers for connecting "
+"to an I/O bus such as SCSI or IDE."
+msgstr ""
+"_Модули интерфейса SCSI_ (SIM) - драйверы адаптеров шины для подключения к "
+"шине ввода-вывода, такой как SCSI или IDE."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:85
+msgid ""
+"A peripheral driver receives requests from the OS, converts them to a "
+"sequence of SCSI commands and passes these SCSI commands to a SCSI Interface "
+"Module. The SCSI Interface Module is responsible for passing these commands "
+"to the actual hardware (or if the actual hardware is not SCSI but, for "
+"example, IDE then also converting the SCSI commands to the native commands "
+"of the hardware)."
+msgstr ""
+"Периферийный драйвер получает запросы от ОС, преобразует их в "
+"последовательность команд SCSI и передает эти команды SCSI модулю интерфейса "
+"SCSI. Модуль интерфейса SCSI отвечает за передачу этих команд реальному "
+"оборудованию (или, если оборудование не поддерживает SCSI, а использует, "
+"например, IDE, также преобразует команды SCSI в собственные команды "
+"оборудования)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:87
+msgid ""
+"As we are interested in writing a SCSI adapter driver here, from this point "
+"on we will consider everything from the SIM standpoint."
+msgstr ""
+"Так как мы заинтересованы в написании драйвера адаптера SCSI, с этого "
+"момента мы будем рассматривать всё с точки зрения модуля SCSI-интерфейса "
+"(SIM)."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:88
+#, no-wrap
+msgid "Globals and Boilerplate"
+msgstr "Глобальные переменные и Шаблонный код"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:91
+msgid ""
+"A typical SIM driver needs to include the following CAM-related header files:"
+msgstr ""
+"Типичный драйвер SIM должен включать следующие заголовочные файлы, связанные "
+"с CAM:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:100
+#, no-wrap
+msgid ""
+"#include <cam/cam.h>\n"
+"#include <cam/cam_ccb.h>\n"
+"#include <cam/cam_sim.h>\n"
+"#include <cam/cam_xpt_sim.h>\n"
+"#include <cam/cam_debug.h>\n"
+"#include <cam/scsi/scsi_all.h>\n"
+msgstr ""
+"#include <cam/cam.h>\n"
+"#include <cam/cam_ccb.h>\n"
+"#include <cam/cam_sim.h>\n"
+"#include <cam/cam_xpt_sim.h>\n"
+"#include <cam/cam_debug.h>\n"
+"#include <cam/scsi/scsi_all.h>\n"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:102
+#, no-wrap
+msgid "Device configuration: xxx_attach"
+msgstr "Конфигурация устройства: xxx_attach"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:107
+msgid ""
+"The first thing each SIM driver must do is register itself with the CAM "
+"subsystem. This is done during the driver's `xxx_attach()` function (here "
+"and further xxx_ is used to denote the unique driver name prefix). The "
+"`xxx_attach()` function itself is called by the system bus auto-"
+"configuration code which we do not describe here."
+msgstr ""
+"Первое, что должен сделать каждый драйвер SIM, — это зарегистрироваться в "
+"подсистеме CAM. Это выполняется в функции `xxx_attach()` драйвера (здесь и "
+"далее xxx_ используется для обозначения уникального префикса имени "
+"драйвера). Сама функция `xxx_attach()` вызывается кодом автонастройки "
+"системной шины, который мы здесь не описываем."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:109
+msgid ""
+"This is achieved in multiple steps: first it is necessary to allocate the "
+"queue of requests associated with this SIM:"
+msgstr ""
+"Это достигается в несколько этапов: сначала необходимо выделить очередь "
+"запросов, связанных с этой SIM:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:113
+#, no-wrap
+msgid " struct cam_devq *devq;\n"
+msgstr " struct cam_devq *devq;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:117
+#, no-wrap
+msgid ""
+" if ((devq = cam_simq_alloc(SIZE)) == NULL) {\n"
+" error; /* some code to handle the error */\n"
+" }\n"
+msgstr ""
+" if ((devq = cam_simq_alloc(SIZE)) == NULL) {\n"
+" error; /* some code to handle the error */\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:122
+msgid ""
+"Here `SIZE` is the size of the queue to be allocated, maximal number of "
+"requests it could contain. It is the number of requests that the SIM driver "
+"can handle in parallel on one SCSI card. Commonly it can be calculated as:"
+msgstr ""
+"Вот `SIZE` — это размер выделяемой очереди, максимальное количество "
+"запросов, которые она может содержать. Это количество запросов, которые "
+"драйвер SIM может обрабатывать параллельно на одной SCSI-карте. Обычно его "
+"можно вычислить как:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:126
+#, no-wrap
+msgid "SIZE = NUMBER_OF_SUPPORTED_TARGETS * MAX_SIMULTANEOUS_COMMANDS_PER_TARGET\n"
+msgstr "SIZE = NUMBER_OF_SUPPORTED_TARGETS * MAX_SIMULTANEOUS_COMMANDS_PER_TARGET\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:129
+msgid "Next we create a descriptor of our SIM:"
+msgstr "Далее мы создаем описание нашего SIM:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:133
+#, no-wrap
+msgid " struct cam_sim *sim;\n"
+msgstr " struct cam_sim *sim;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:140
+#, no-wrap
+msgid ""
+" if ((sim = cam_sim_alloc(action_func, poll_func, driver_name,\n"
+" softc, unit, mtx, max_dev_transactions,\n"
+" max_tagged_dev_transactions, devq)) == NULL) {\n"
+" cam_simq_free(devq);\n"
+" error; /* some code to handle the error */\n"
+" }\n"
+msgstr ""
+" if ((sim = cam_sim_alloc(action_func, poll_func, driver_name,\n"
+" softc, unit, mtx, max_dev_transactions,\n"
+" max_tagged_dev_transactions, devq)) == NULL) {\n"
+" cam_simq_free(devq);\n"
+" error; /* some code to handle the error */\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:143
+msgid ""
+"Note that if we are not able to create a SIM descriptor we free the `devq` "
+"also because we can do nothing else with it and we want to conserve memory."
+msgstr ""
+"Обратите внимание, что если мы не сможем создать дескриптор SIM, мы также "
+"освобождаем `devq`, потому что больше ничего не можем с ним сделать и хотим "
+"сэкономить память."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:145
+msgid ""
+"If a SCSI card has multiple SCSI buses on it then each bus requires its own "
+"`cam_sim` structure."
+msgstr ""
+"Если SCSI-карта имеет несколько шин SCSI, то каждой шине требуется "
+"собственная структура `cam_sim`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:148
+msgid ""
+"An interesting question is what to do if a SCSI card has more than one SCSI "
+"bus, do we need one `devq` structure per card or per SCSI bus? The answer "
+"given in the comments to the CAM code is: either way, as the driver's author "
+"prefers."
+msgstr ""
+"Интересный вопрос: что делать, если SCSI-карта имеет более одной SCSI-шины, "
+"нужна ли одна структура `devq` на карту или на SCSI-шину? Ответ, приведённый "
+"в комментариях к коду CAM, таков: как угодно, на усмотрение автора драйвера."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:150
+msgid "The arguments are:"
+msgstr "Аргументы:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:152
+msgid "`action_func` - pointer to the driver's `xxx_action` function."
+msgstr "`action_func` - указатель на функцию `xxx_action` драйвера."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:156
+#, no-wrap
+msgid "static void xxx_action(struct cam_sim *, union ccb *);\n"
+msgstr "static void xxx_action(struct cam_sim *, union ccb *);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:158
+msgid "`poll_func` - pointer to the driver's `xxx_poll()`"
+msgstr "`poll_func` - указатель на функцию `xxx_poll()` драйвера"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:162
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1020
+#, no-wrap
+msgid "static void xxx_poll(struct cam_sim *);\n"
+msgstr "static void xxx_poll(struct cam_sim *);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:164
+msgid ""
+"driver_name - the name of the actual driver, such as \"ncr\" or \"wds\"."
+msgstr "`driver_name` — имя фактического драйвера, например `ncr` или `wds`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:166
+msgid ""
+"`softc` - pointer to the driver's internal descriptor for this SCSI card. "
+"This pointer will be used by the driver in future to get private data."
+msgstr ""
+"`softc` — указатель на внутренний дескриптор драйвера для данной SCSI-карты. "
+"Этот указатель будет использоваться драйвером в дальнейшем для получения "
+"приватных данных."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:167
+msgid ""
+"unit - the controller unit number, for example for controller \"mps0\" this "
+"number will be 0"
+msgstr ""
+"unit - номер управляющего устройства, например, для контроллера \"mps0\" это "
+"число будет 0"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:171
+msgid ""
+"mtx - Lock associated with this SIM. For SIMs that don't know about "
+"locking, pass in Giant. For SIMs that do, pass in the lock used to guard "
+"this SIM's data structures. This lock will be held when xxx_action and "
+"xxx_poll are called."
+msgstr ""
+"mtx - Блокировка, связанная с данной SIM. Для SIM, которые не поддерживают "
+"блокировку, передается Giant. Для SIM, которые поддерживают, передается "
+"блокировка, используемая для защиты структур данных этой SIM. Эта блокировка "
+"будет удерживаться при вызовах xxx_action и xxx_poll."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:174
+msgid ""
+"max_dev_transactions - maximal number of simultaneous transactions per SCSI "
+"target in the non-tagged mode. This value will be almost universally equal "
+"to 1, with possible exceptions only for the non-SCSI cards. Also the "
+"drivers that hope to take advantage by preparing one transaction while "
+"another one is executed may set it to 2 but this does not seem to be worth "
+"the complexity."
+msgstr ""
+"max_dev_transactions - максимальное количество одновременных транзакций на "
+"целевом SCSI-устройстве в режиме без тегов. Это значение почти всегда равно "
+"1, за исключением возможных исключений только для не-SCSI карт. Также "
+"драйверы, которые надеются получить преимущество, подготавливая одну "
+"транзакцию во время выполнения другой, могут установить его в 2, но это не "
+"кажется оправданным из-за сложности."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:179
+msgid ""
+"max_tagged_dev_transactions - the same thing, but in the tagged mode. Tags "
+"are the SCSI way to initiate multiple transactions on a device: each "
+"transaction is assigned a unique tag and the transaction is sent to the "
+"device. When the device completes some transaction it sends back the result "
+"together with the tag so that the SCSI adapter (and the driver) can tell "
+"which transaction was completed. This argument is also known as the maximal "
+"tag depth. It depends on the abilities of the SCSI adapter."
+msgstr ""
+"max_tagged_dev_transactions - то же самое, но в режиме с тегами. Теги — это "
+"способ в SCSI инициировать несколько транзакций на устройстве: каждая "
+"транзакция получает уникальный тег и отправляется на устройство. Когда "
+"устройство завершает транзакцию, оно возвращает результат вместе с тегом, "
+"чтобы SCSI-адаптер (и драйвер) могли определить, какая транзакция была "
+"завершена. Этот аргумент также известен как максимальная глубина тега. Он "
+"зависит от возможностей SCSI-адаптера."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:181
+msgid "Finally we register the SCSI buses associated with our SCSI adapter:"
+msgstr "Наконец, мы регистрируем шины SCSI, связанные с нашим SCSI-адаптером:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:188
+#, no-wrap
+msgid ""
+" if (xpt_bus_register(sim, softc, bus_number) != CAM_SUCCESS) {\n"
+" cam_sim_free(sim, /*free_devq*/ TRUE);\n"
+" error; /* some code to handle the error */\n"
+" }\n"
+msgstr ""
+" if (xpt_bus_register(sim, softc, bus_number) != CAM_SUCCESS) {\n"
+" cam_sim_free(sim, /*free_devq*/ TRUE);\n"
+" error; /* some code to handle the error */\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:192
+msgid ""
+"If there is one `devq` structure per SCSI bus (i.e., we consider a card with "
+"multiple buses as multiple cards with one bus each) then the bus number will "
+"always be 0, otherwise each bus on the SCSI card should be get a distinct "
+"number. Each bus needs its own separate structure cam_sim."
+msgstr ""
+"Если существует одна структура `devq` на каждую шину SCSI (т.е. мы "
+"рассматриваем карту с несколькими шинами как несколько карт с одной шиной "
+"каждая), то номер шины всегда будет 0, в противном случае каждая шина на "
+"SCSI-карте должна получить уникальный номер. Каждой шине требуется своя "
+"отдельная структура `cam_sim`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:195
+msgid ""
+"After that our controller is completely hooked to the CAM system. The value "
+"of `devq` can be discarded now: sim will be passed as an argument in all "
+"further calls from CAM and devq can be derived from it."
+msgstr ""
+"После этого наш контроллер полностью подключён к системе CAM. Значение "
+"`devq` теперь можно отбросить: sim будет передаваться в качестве аргумента "
+"во всех последующих вызовах из CAM, а devq можно получить из него."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:199
+msgid ""
+"CAM provides the framework for such asynchronous events. Some events "
+"originate from the lower levels (the SIM drivers), some events originate "
+"from the peripheral drivers, some events originate from the CAM subsystem "
+"itself. Any driver can register callbacks for some types of the "
+"asynchronous events, so that it would be notified if these events occur."
+msgstr ""
+"CAM предоставляет инфраструктуру для подобных асинхронных событий. Некоторые "
+"события возникают на нижних уровнях (драйверы SIM), некоторые — в драйверах "
+"периферийных устройств, а некоторые — в самой подсистеме CAM. Любой драйвер "
+"может зарегистрировать обработчики для определённых типов асинхронных "
+"событий, чтобы получать уведомления при их возникновении."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:207
+msgid ""
+"A typical example of such an event is a device reset. Each transaction and "
+"event identifies the devices to which it applies by the means of \"path\". "
+"The target-specific events normally occur during a transaction with this "
+"device. So the path from that transaction may be re-used to report this "
+"event (this is safe because the event path is copied in the event reporting "
+"routine but not deallocated nor passed anywhere further). Also it is safe "
+"to allocate paths dynamically at any time including the interrupt routines, "
+"although that incurs certain overhead, and a possible problem with this "
+"approach is that there may be no free memory at that time. For a bus reset "
+"event we need to define a wildcard path including all devices on the bus. "
+"So we can create the path for the future bus reset events in advance and "
+"avoid problems with the future memory shortage:"
+msgstr ""
+"Типичным примером такого события является сброс устройства. Каждая "
+"транзакция и событие идентифицируют устройства, к которым они применяются, с "
+"помощью \"пути\". Специфичные для целевого устройства события обычно "
+"происходят во время транзакции с этим устройством. Таким образом, путь из "
+"этой транзакции может быть повторно использован для сообщения о данном "
+"событии (это безопасно, потому что путь события копируется в процедуре "
+"сообщения о событии, но не освобождается и не передаётся дальше). Также "
+"безопасно динамически выделять пути в любое время, включая процедуры "
+"обработки прерываний, хотя это влечёт определённые накладные расходы, и "
+"возможная проблема такого подхода заключается в том, что в этот момент может "
+"не быть свободной памяти. Для события сброса шины нам необходимо определить "
+"путь-шаблон, включающий все устройства на шине. Поэтому мы можем заранее "
+"создать путь для будущих событий сброса шины и избежать проблем с возможной "
+"нехваткой памяти в будущем:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:211
+#, no-wrap
+msgid " struct cam_path *path;\n"
+msgstr " struct cam_path *path;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:219
+#, no-wrap
+msgid ""
+" if (xpt_create_path(&path, /*periph*/NULL,\n"
+" cam_sim_path(sim), CAM_TARGET_WILDCARD,\n"
+" CAM_LUN_WILDCARD) != CAM_REQ_CMP) {\n"
+" xpt_bus_deregister(cam_sim_path(sim));\n"
+" cam_sim_free(sim, /*free_devq*/TRUE);\n"
+" error; /* some code to handle the error */\n"
+" }\n"
+msgstr ""
+" if (xpt_create_path(&path, /*periph*/NULL,\n"
+" cam_sim_path(sim), CAM_TARGET_WILDCARD,\n"
+" CAM_LUN_WILDCARD) != CAM_REQ_CMP) {\n"
+" xpt_bus_deregister(cam_sim_path(sim));\n"
+" cam_sim_free(sim, /*free_devq*/TRUE);\n"
+" error; /* some code to handle the error */\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:222
+#, no-wrap
+msgid ""
+" softc->wpath = path;\n"
+" softc->sim = sim;\n"
+msgstr ""
+" softc->wpath = path;\n"
+" softc->sim = sim;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:225
+msgid "As you can see the path includes:"
+msgstr "Как вы можете видеть, путь включает:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:227
+msgid "ID of the peripheral driver (NULL here because we have none)"
+msgstr ""
+"Идентификатор драйвера периферийного устройства (NULL здесь, так как у нас "
+"его нет)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:228
+msgid "ID of the SIM driver (`cam_sim_path(sim)`)"
+msgstr "Идентификатор драйвера SIM (`cam_sim_path(sim)`)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:229
+msgid ""
+"SCSI target number of the device (CAM_TARGET_WILDCARD means \"all devices\")"
+msgstr ""
+"Номер целевого устройства SCSI (CAM_TARGET_WILDCARD означает \"все "
+"устройства\")"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:230
+msgid "SCSI LUN number of the subdevice (CAM_LUN_WILDCARD means \"all LUNs\")"
+msgstr "Номер SCSI LUN подустройства (CAM_LUN_WILDCARD означает \"все LUN\")"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:232
+msgid ""
+"If the driver can not allocate this path it will not be able to work "
+"normally, so in that case we dismantle that SCSI bus."
+msgstr ""
+"Если драйвер не может выделить этот путь, он не сможет нормально работать, "
+"поэтому в таком случае мы демонтируем эту шину SCSI."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:235
+msgid ""
+"And we save the path pointer in the `softc` structure for future use. After "
+"that we save the value of sim (or we can also discard it on the exit from "
+"`xxx_probe()` if we wish)."
+msgstr ""
+"И мы сохраняем указатель пути в структуре `softc` для дальнейшего "
+"использования. После этого сохраняем значение sim (или можем также отбросить "
+"его при выходе из `xxx_probe()`, если захотим)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:238
+msgid ""
+"That is all for a minimalistic initialization. To do things right there is "
+"one more issue left."
+msgstr ""
+"Вот и всё для минималистичной инициализации. Чтобы сделать всё правильно, "
+"остался ещё один вопрос."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:243
+msgid ""
+"For a SIM driver there is one particularly interesting event: when a target "
+"device is considered lost. In this case resetting the SCSI negotiations "
+"with this device may be a good idea. So we register a callback for this "
+"event with CAM. The request is passed to CAM by requesting CAM action on a "
+"CAM control block for this type of request:"
+msgstr ""
+"Для драйвера SIM есть одно особенно важное событие: когда целевое устройство "
+"считается потерянным. В этом случае может быть хорошей идеей сбросить SCSI-"
+"переговоры с этим устройством. Поэтому мы регистрируем обратный вызов для "
+"этого события в CAM. Запрос передаётся в CAM путём запроса действия CAM в "
+"блоке управления CAM для этого типа запроса:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:247
+#, no-wrap
+msgid " struct ccb_setasync csa;\n"
+msgstr " struct ccb_setasync csa;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:254
+#, no-wrap
+msgid ""
+" xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);\n"
+" csa.ccb_h.func_code = XPT_SASYNC_CB;\n"
+" csa.event_enable = AC_LOST_DEVICE;\n"
+" csa.callback = xxx_async;\n"
+" csa.callback_arg = sim;\n"
+" xpt_action((union ccb *)&csa);\n"
+msgstr ""
+" xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);\n"
+" csa.ccb_h.func_code = XPT_SASYNC_CB;\n"
+" csa.event_enable = AC_LOST_DEVICE;\n"
+" csa.callback = xxx_async;\n"
+" csa.callback_arg = sim;\n"
+" xpt_action((union ccb *)&csa);\n"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:256
+#, no-wrap
+msgid "Processing CAM messages: xxx_action"
+msgstr "Обработка сообщений CAM: xxx_action"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:261
+#, no-wrap
+msgid "static void xxx_action(struct cam_sim *sim, union ccb *ccb);\n"
+msgstr "static void xxx_action(struct cam_sim *sim, union ccb *ccb);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:268
+msgid ""
+"Do some action on request of the CAM subsystem. Sim describes the SIM for "
+"the request, CCB is the request itself. CCB stands for \"CAM Control "
+"Block\". It is a union of many specific instances, each describing "
+"arguments for some type of transactions. All of these instances share the "
+"CCB header where the common part of arguments is stored."
+msgstr ""
+"Выполнить некоторое действие по запросу подсистемы CAM. Sim описывает SIM "
+"для запроса, CCB — это сам запрос. CCB расшифровывается как \"CAM Control "
+"Block\" (блок управления CAM). Это объединение множества конкретных "
+"экземпляров, каждый из которых описывает аргументы для определённого типа "
+"транзакций. Все эти экземпляры имеют общий заголовок CCB, в котором "
+"хранится общая часть аргументов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:271
+msgid ""
+"CAM supports the SCSI controllers working in both initiator (\"normal\") "
+"mode and target (simulating a SCSI device) mode. Here we only consider the "
+"part relevant to the initiator mode."
+msgstr ""
+"CAM поддерживает SCSI-контроллеры, работающие как в режиме инициатора "
+"(«обычном»), так и в режиме цели (эмулирующем SCSI-устройство). Здесь мы "
+"рассматриваем только часть, относящуюся к режиму инициатора."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:273
+msgid ""
+"There are a few function and macros (in other words, methods) defined to "
+"access the public data in the struct sim:"
+msgstr ""
+"Существует несколько функций и макросов (другими словами, методов), "
+"определённых для доступа к публичным данным в структуре sim:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:275
+msgid "`cam_sim_path(sim)` - the path ID (see above)"
+msgstr "`cam_sim_path(sim)` - идентификатор пути (см. выше)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:276
+msgid "`cam_sim_name(sim)` - the name of the sim"
+msgstr "`cam_sim_name(sim)` — имя sim"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:277
+msgid ""
+"`cam_sim_softc(sim)` - the pointer to the softc (driver private data) "
+"structure"
+msgstr ""
+"`cam_sim_softc(sim)` - указатель на структуру softc (приватные данные "
+"драйвера)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:278
+msgid "`cam_sim_unit(sim)` - the unit number"
+msgstr "`cam_sim_unit(sim)` - номер устройства"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:279
+msgid "`cam_sim_bus(sim)` - the bus ID"
+msgstr "`cam_sim_bus(sim)` - идентификатор шины"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:281
+msgid ""
+"To identify the device, `xxx_action()` can get the unit number and pointer "
+"to its structure softc using these functions."
+msgstr ""
+"Для идентификации устройства `xxx_action()` может получить номер устройства "
+"и указатель на его структуру softc, используя следующие функции."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:284
+msgid ""
+"The type of request is stored in `ccb->ccb_h.func_code`. So generally "
+"`xxx_action()` consists of a big switch:"
+msgstr ""
+"Тип запроса хранится в `ccb->ccb_h.func_code`. Поэтому, как правило, "
+"`xxx_action()` состоит из большого оператора switch:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:291
+#, no-wrap
+msgid ""
+" struct xxx_softc *softc = (struct xxx_softc *) cam_sim_softc(sim);\n"
+" struct ccb_hdr *ccb_h = &ccb->ccb_h;\n"
+" int unit = cam_sim_unit(sim);\n"
+" int bus = cam_sim_bus(sim);\n"
+msgstr ""
+" struct xxx_softc *softc = (struct xxx_softc *) cam_sim_softc(sim);\n"
+" struct ccb_hdr *ccb_h = &ccb->ccb_h;\n"
+" int unit = cam_sim_unit(sim);\n"
+" int bus = cam_sim_bus(sim);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:300
+#, no-wrap
+msgid ""
+" switch (ccb_h->func_code) {\n"
+" case ...:\n"
+" ...\n"
+" default:\n"
+" ccb_h->status = CAM_REQ_INVALID;\n"
+" xpt_done(ccb);\n"
+" break;\n"
+" }\n"
+msgstr ""
+" switch (ccb_h->func_code) {\n"
+" case ...:\n"
+" ...\n"
+" default:\n"
+" ccb_h->status = CAM_REQ_INVALID;\n"
+" xpt_done(ccb);\n"
+" break;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:303
+msgid ""
+"As can be seen from the default case (if an unknown command was received) "
+"the return code of the command is set into `ccb->ccb_h.status` and the "
+"completed CCB is returned back to CAM by calling `xpt_done(ccb)`."
+msgstr ""
+"Как видно из случая по умолчанию (если получена неизвестная команда) код "
+"возврата команды устанавливается в `ccb->ccb_h.status`, а завершённый CCB "
+"возвращается обратно в CAM вызовом `xpt_done(ccb)`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:306
+msgid ""
+"`xpt_done()` does not have to be called from `xxx_action()`: For example an "
+"I/O request may be enqueued inside the SIM driver and/or its SCSI "
+"controller. Then when the device would post an interrupt signaling that the "
+"processing of this request is complete `xpt_done()` may be called from the "
+"interrupt handling routine."
+msgstr ""
+"`xpt_done()` не обязательно вызывать из `xxx_action()`: Например, запрос "
+"ввода-вывода может быть поставлен в очередь внутри драйвера SIM и/или его "
+"SCSI-контроллера. Затем, когда устройство пошлет прерывание, сигнализирующее "
+"о завершении обработки этого запроса, `xpt_done()` может быть вызван из "
+"процедуры обработки прерывания."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:314
+msgid ""
+"Actually, the CCB status is not only assigned as a return code but a CCB has "
+"some status all the time. Before CCB is passed to the `xxx_action()` "
+"routine it gets the status CCB_REQ_INPROG meaning that it is in progress. "
+"There are a surprising number of status values defined in [.filename]#/sys/"
+"cam/cam.h# which should be able to represent the status of a request in "
+"great detail. More interesting yet, the status is in fact a \"bitwise or\" "
+"of an enumerated status value (the lower 6 bits) and possible additional "
+"flag-like bits (the upper bits). The enumerated values will be discussed "
+"later in more detail. The summary of them can be found in the Errors "
+"Summary section. The possible status flags are:"
+msgstr ""
+"На самом деле, статус CCB не только присваивается в качестве кода возврата, "
+"но и CCB всегда имеет какой-то статус. Перед тем как CCB передается в "
+"процедуру `xxx_action()`, он получает статус CCB_REQ_INPROG, означающий, что "
+"запрос находится в процессе выполнения. В [.filename]#/sys/cam/cam.h# "
+"определено удивительно большое количество значений статуса, которые должны "
+"детально отражать состояние запроса. Что еще интереснее, статус фактически "
+"представляет собой \"побитовое ИЛИ\" перечисленного значения статуса "
+"(младшие 6 бит) и возможных дополнительных флагов (старшие биты). "
+"Перечисленные значения будут подробно рассмотрены далее. Их краткое описание "
+"можно найти в разделе \"Сводка ошибок\". Возможные флаги статуса:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:317
+msgid ""
+"_CAM_DEV_QFRZN_ - if the SIM driver gets a serious error (for example, the "
+"device does not respond to the selection or breaks the SCSI protocol) when "
+"processing a CCB it should freeze the request queue by calling "
+"`xpt_freeze_simq()`, return the other enqueued but not processed yet CCBs "
+"for this device back to the CAM queue, then set this flag for the "
+"troublesome CCB and call `xpt_done()`. This flag causes the CAM subsystem "
+"to unfreeze the queue after it handles the error."
+msgstr ""
+"_CAM_DEV_QFRZN_ - если драйвер SIM получает серьёзную ошибку (например, "
+"устройство не отвечает на выборку или нарушает протокол SCSI) при обработке "
+"CCB, он должен заморозить очередь запросов, вызвав `xpt_freeze_simq()`, "
+"вернуть другие поставленные в очередь, но ещё не обработанные CCB для этого "
+"устройства обратно в очередь CAM, затем установить этот флаг для проблемного "
+"CCB и вызвать `xpt_done()`. Этот флаг заставляет подсистему CAM разморозить "
+"очередь после обработки ошибки."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:319
+msgid ""
+"_CAM_AUTOSNS_VALID_ - if the device returned an error condition and the flag "
+"CAM_DIS_AUTOSENSE is not set in CCB the SIM driver must execute the REQUEST "
+"SENSE command automatically to extract the sense (extended error "
+"information) data from the device. If this attempt was successful the sense "
+"data should be saved in the CCB and this flag set."
+msgstr ""
+"_CAM_AUTOSNS_VALID_ - если устройство вернуло состояние ошибки и флаг "
+"CAM_DIS_AUTOSENSE не установлен в CCB, драйвер SIM должен автоматически "
+"выполнить команду REQUEST SENSE, чтобы извлечь данные sense (расширенную "
+"информацию об ошибке) из устройства. Если попытка была успешной, данные "
+"sense должны быть сохранены в CCB, а этот флаг установлен."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:322
+msgid ""
+"_CAM_RELEASE_SIMQ_ - like CAM_DEV_QFRZN but used in case there is some "
+"problem (or resource shortage) with the SCSI controller itself. Then all "
+"the future requests to the controller should be stopped by "
+"`xpt_freeze_simq()`. The controller queue will be restarted after the SIM "
+"driver overcomes the shortage and informs CAM by returning some CCB with "
+"this flag set."
+msgstr ""
+"_CAM_RELEASE_SIMQ_ - аналогично CAM_DEV_QFRZN, но используется в случае "
+"возникновения проблем (или нехватки ресурсов) с самим SCSI-контроллером. В "
+"этом случае все последующие запросы к контроллеру должны быть остановлены с "
+"помощью `xpt_freeze_simq()`. Очередь контроллера будет возобновлена после "
+"того, как драйвер SIM устранит нехватку и уведомит CAM, вернув некоторый CCB "
+"с установленным этим флагом."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:324
+msgid ""
+"_CAM_SIM_QUEUED_ - when SIM puts a CCB into its request queue this flag "
+"should be set (and removed when this CCB gets dequeued before being returned "
+"back to CAM). This flag is not used anywhere in the CAM code now, so its "
+"purpose is purely diagnostic."
+msgstr ""
+"_CAM_SIM_QUEUED_ - этот флаг должен быть установлен, когда SIM помещает CCB "
+"в свою очередь запросов (и снят, когда этот CCB извлекается из очереди перед "
+"возвратом в CAM). В настоящее время этот флаг нигде не используется в коде "
+"CAM, поэтому его назначение чисто диагностическое."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:325
+msgid "_CAM_QOS_VALID_ - The QOS data is now valid."
+msgstr "_CAM_QOS_VALID_ - Данные QOS теперь действительны."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:328
+msgid ""
+"The function `xxx_action()` is not allowed to sleep, so all the "
+"synchronization for resource access must be done using SIM or device queue "
+"freezing. Besides the aforementioned flags the CAM subsystem provides "
+"functions `xpt_release_simq()` and `xpt_release_devq()` to unfreeze the "
+"queues directly, without passing a CCB to CAM."
+msgstr ""
+"Функция `xxx_action()` не может находиться в состоянии ожидания, поэтому вся "
+"синхронизация доступа к ресурсам должна выполняться с использованием SIM или "
+"заморозки очереди устройств. Помимо упомянутых флагов, подсистема CAM "
+"предоставляет функции `xpt_release_simq()` и `xpt_release_devq()` для "
+"разморозки очередей напрямую, без передачи CCB в CAM."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:330
+msgid "The CCB header contains the following fields:"
+msgstr "Заголовок CCB содержит следующие поля:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:332
+msgid "_path_ - path ID for the request"
+msgstr "_path_ - идентификатор пути для запроса"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:333
+msgid "_target_id_ - target device ID for the request"
+msgstr "_target_id_ - идентификатор целевого устройства для запроса"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:334
+msgid "_target_lun_ - LUN ID of the target device"
+msgstr "_target_lun_ - идентификатор LUN целевого устройства"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:335
+msgid "_timeout_ - timeout interval for this command, in milliseconds"
+msgstr "_timeout_ - интервал таймаута для этой команды, в миллисекундах"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:336
+msgid ""
+"_timeout_ch_ - a convenience place for the SIM driver to store the timeout "
+"handle (the CAM subsystem itself does not make any assumptions about it)"
+msgstr ""
+"_timeout_ch_ - удобное место для драйвера SIM, чтобы хранить обработчик "
+"таймаута (сама подсистема CAM не делает никаких предположений о нём)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:337
+msgid ""
+"_flags_ - various bits of information about the request spriv_ptr0, "
+"spriv_ptr1 - fields reserved for private use by the SIM driver (such as "
+"linking to the SIM queues or SIM private control blocks); actually, they "
+"exist as unions: spriv_ptr0 and spriv_ptr1 have the type (void *), "
+"spriv_field0 and spriv_field1 have the type unsigned long, "
+"sim_priv.entries[0].bytes and sim_priv.entries[1].bytes are byte arrays of "
+"the size consistent with the other incarnations of the union and "
+"sim_priv.bytes is one array, twice bigger."
+msgstr ""
+"_flags_ - различные биты информации о запросе spriv_ptr0, spriv_ptr1 — поля, "
+"зарезервированные для приватного использования драйвером SIM (например, для "
+"связи с очередями SIM или приватными блоками управления SIM); фактически они "
+"существуют как объединения: spriv_ptr0 и spriv_ptr1 имеют тип (void *), "
+"spriv_field0 и spriv_field1 имеют тип unsigned long, "
+"sim_priv.entries[0].bytes и sim_priv.entries[1].bytes - это байтовые массивы "
+"размера, согласованного с другими вариантами объединения, а sim_priv.bytes - "
+"это один массив, вдвое большего размера."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:339
+msgid ""
+"The recommended way of using the SIM private fields of CCB is to define some "
+"meaningful names for them and use these meaningful names in the driver, like:"
+msgstr ""
+"Рекомендуемый способ использования приватных полей SIM в CCB — это "
+"определить для них осмысленные имена и использовать эти осмысленные имена в "
+"драйвере, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:344
+#, no-wrap
+msgid ""
+"#define ccb_some_meaningful_name sim_priv.entries[0].bytes\n"
+"#define ccb_hcb spriv_ptr1 /* for hardware control block */\n"
+msgstr ""
+"#define ccb_some_meaningful_name sim_priv.entries[0].bytes\n"
+"#define ccb_hcb spriv_ptr1 /* for hardware control block */\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:347
+msgid "The most common initiator mode requests are:"
+msgstr "Наиболее распространенные запросы в режиме инициатора:"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:348
+#, no-wrap
+msgid "_XPT_SCSI_IO_ - execute an I/O transaction"
+msgstr "_XPT_SCSI_IO_ - выполнить транзакцию ввода-вывода"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:352
+msgid ""
+"The instance \"struct ccb_scsiio csio\" of the union ccb is used to transfer "
+"the arguments. They are:"
+msgstr ""
+"Экземпляр \"struct ccb_scsiio csio\" объединения ccb используется для "
+"передачи аргументов. Они включают:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:354
+msgid "_cdb_io_ - pointer to the SCSI command buffer or the buffer itself"
+msgstr "_cdb_io_ - указатель на буфер команды SCSI или сам буфер"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:355
+msgid "_cdb_len_ - SCSI command length"
+msgstr "_cdb_len_ - длина команды SCSI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:356
+msgid ""
+"_data_ptr_ - pointer to the data buffer (gets a bit complicated if scatter/"
+"gather is used)"
+msgstr ""
+"_data_ptr_ - указатель на буфер данных (усложняется, если используется "
+"scatter/gather)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:357
+msgid "_dxfer_len_ - length of the data to transfer"
+msgstr "_dxfer_len_ - длина передаваемых данных"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:358
+msgid "_sglist_cnt_ - counter of the scatter/gather segments"
+msgstr "_sglist_cnt_ - счетчик сегментов scatter/gather"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:359
+msgid "_scsi_status_ - place to return the SCSI status"
+msgstr "_scsi_status_ - место для возврата статуса SCSI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:360
+msgid ""
+"_sense_data_ - buffer for the SCSI sense information if the command returns "
+"an error (the SIM driver is supposed to run the REQUEST SENSE command "
+"automatically in this case if the CCB flag CAM_DIS_AUTOSENSE is not set)"
+msgstr ""
+"_sense_data_ - буфер для информации SCSI sense, если команда возвращает "
+"ошибку (драйвер SIM должен автоматически выполнить команду REQUEST SENSE в "
+"этом случае, если флаг CCB CAM_DIS_AUTOSENSE не установлен)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:361
+msgid ""
+"_sense_len_ - the length of that buffer (if it happens to be higher than "
+"size of sense_data the SIM driver must silently assume the smaller value)"
+msgstr ""
+"_sense_len_ - длина этого буфера (если она окажется больше размера "
+"sense_data, драйвер SIM должен без уведомления принять меньшее значение)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:364
+msgid ""
+"_resid_, _sense_resid_ - if the transfer of data or SCSI sense returned an "
+"error these are the returned counters of the residual (not transferred) "
+"data. They do not seem to be especially meaningful, so in a case when they "
+"are difficult to compute (say, counting bytes in the SCSI controller's FIFO "
+"buffer) an approximate value will do as well. For a successfully completed "
+"transfer they must be set to zero."
+msgstr ""
+"_resid_, _sense_resid_ — если передача данных или SCSI sense вернула ошибку, "
+"это счётчики остаточных (не переданных) данных. Они не кажутся особенно "
+"значимыми, поэтому в случаях, когда их сложно вычислить (например, подсчёт "
+"байтов в FIFO-буфере SCSI-контроллера), подойдёт и приблизительное значение. "
+"Для успешно завершённой передачи они должны быть установлены в ноль."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:365
+msgid "_tag_action_ - the kind of tag to use:"
+msgstr "_tag_action_ - тип используемого тега:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:366
+msgid "CAM_TAG_ACTION_NONE - do not use tags for this transaction"
+msgstr "`CAM_TAG_ACTION_NONE` - не использовать теги для данной транзакции"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:367
+msgid ""
+"MSG_SIMPLE_Q_TAG, MSG_HEAD_OF_Q_TAG, MSG_ORDERED_Q_TAG - value equal to the "
+"appropriate tag message (see /sys/cam/scsi/scsi_message.h); this gives only "
+"the tag type, the SIM driver must assign the tag value itself"
+msgstr ""
+"MSG_SIMPLE_Q_TAG, MSG_HEAD_OF_Q_TAG, MSG_ORDERED_Q_TAG — значение, "
+"соответствующее указанному теговому сообщению (см. /sys/cam/scsi/"
+"scsi_message.h); указывает только тип тега, значение тега должно быть "
+"назначено самим драйвером SIM"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:369
+msgid "The general logic of handling this request is the following:"
+msgstr "Общая логика обработки этого запроса следующая:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:371
+msgid ""
+"The first thing to do is to check for possible races, to make sure that the "
+"command did not get aborted when it was sitting in the queue:"
+msgstr ""
+"Первое, что нужно сделать, это проверить возможные состояния гонки, чтобы "
+"убедиться, что команда не была прервана, пока находилась в очереди:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:375
+#, no-wrap
+msgid " struct ccb_scsiio *csio = &ccb->csio;\n"
+msgstr " struct ccb_scsiio *csio = &ccb->csio;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:380
+#, no-wrap
+msgid ""
+" if ((ccb_h->status & CAM_STATUS_MASK) != CAM_REQ_INPROG) {\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+msgstr ""
+" if ((ccb_h->status & CAM_STATUS_MASK) != CAM_REQ_INPROG) {\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:383
+msgid "Also we check that the device is supported at all by our controller:"
+msgstr ""
+"Также мы проверяем, что устройство вообще поддерживается нашим контроллером:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:397
+#, no-wrap
+msgid ""
+" if (ccb_h->target_id > OUR_MAX_SUPPORTED_TARGET_ID\n"
+" || cch_h->target_id == OUR_SCSI_CONTROLLERS_OWN_ID) {\n"
+" ccb_h->status = CAM_TID_INVALID;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+" if (ccb_h->target_lun > OUR_MAX_SUPPORTED_LUN) {\n"
+" ccb_h->status = CAM_LUN_INVALID;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+msgstr ""
+" if (ccb_h->target_id > OUR_MAX_SUPPORTED_TARGET_ID\n"
+" || cch_h->target_id == OUR_SCSI_CONTROLLERS_OWN_ID) {\n"
+" ccb_h->status = CAM_TID_INVALID;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+" if (ccb_h->target_lun > OUR_MAX_SUPPORTED_LUN) {\n"
+" ccb_h->status = CAM_LUN_INVALID;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:403
+msgid ""
+"Then allocate whatever data structures (such as card-dependent hardware "
+"control block) we need to process this request. If we can not then freeze "
+"the SIM queue and remember that we have a pending operation, return the CCB "
+"back and ask CAM to re-queue it. Later when the resources become available "
+"the SIM queue must be unfrozen by returning a ccb with the "
+"`CAM_SIMQ_RELEASE` bit set in its status. Otherwise, if all went well, link "
+"the CCB with the hardware control block (HCB) and mark it as queued."
+msgstr ""
+"Затем выделяем все необходимые структуры данных (такие как зависящий от "
+"карты блок управления оборудованием), которые нам нужны для обработки этого "
+"запроса. Если мы не можем этого сделать, то замораживаем очередь SIM и "
+"запоминаем, что у нас есть отложенная операция, возвращаем CCB обратно и "
+"просим CAM поставить его в очередь снова. Позже, когда ресурсы станут "
+"доступны, очередь SIM должна быть разморожена путём возврата CCB с "
+"установленным битом `CAM_SIMQ_RELEASE` в его статусе. В противном случае, "
+"если всё прошло успешно, связываем CCB с блоком управления оборудованием "
+"(HCB) и помечаем его как поставленный в очередь."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:407
+#, no-wrap
+msgid " struct xxx_hcb *hcb = allocate_hcb(softc, unit, bus);\n"
+msgstr " struct xxx_hcb *hcb = allocate_hcb(softc, unit, bus);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:415
+#, no-wrap
+msgid ""
+" if (hcb == NULL) {\n"
+" softc->flags |= RESOURCE_SHORTAGE;\n"
+" xpt_freeze_simq(sim, /*count*/1);\n"
+" ccb_h->status = CAM_REQUEUE_REQ;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+msgstr ""
+" if (hcb == NULL) {\n"
+" softc->flags |= RESOURCE_SHORTAGE;\n"
+" xpt_freeze_simq(sim, /*count*/1);\n"
+" ccb_h->status = CAM_REQUEUE_REQ;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:418
+#, no-wrap
+msgid ""
+" hcb->ccb = ccb; ccb_h->ccb_hcb = (void *)hcb;\n"
+" ccb_h->status |= CAM_SIM_QUEUED;\n"
+msgstr ""
+" hcb->ccb = ccb; ccb_h->ccb_hcb = (void *)hcb;\n"
+" ccb_h->status |= CAM_SIM_QUEUED;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:423
+msgid ""
+"Extract the target data from CCB into the hardware control block. Check if "
+"we are asked to assign a tag and if yes then generate an unique tag and "
+"build the SCSI tag messages. The SIM driver is also responsible for "
+"negotiations with the devices to set the maximal mutually supported bus "
+"width, synchronous rate and offset."
+msgstr ""
+"Извлечь целевые данные из CCB в аппаратный блок управления. Проверить, "
+"запрошено ли назначение тега, и если да, то сгенерировать уникальный тег и "
+"построить сообщения тега SCSI. Драйвер SIM также отвечает за согласование с "
+"устройствами для установки максимальной взаимно поддерживаемой ширины шины, "
+"синхронной скорости и смещения."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:432
+#, no-wrap
+msgid ""
+" hcb->target = ccb_h->target_id; hcb->lun = ccb_h->target_lun;\n"
+" generate_identify_message(hcb);\n"
+" if (ccb_h->tag_action != CAM_TAG_ACTION_NONE)\n"
+" generate_unique_tag_message(hcb, ccb_h->tag_action);\n"
+" if (!target_negotiated(hcb))\n"
+" generate_negotiation_messages(hcb);\n"
+msgstr ""
+" hcb->target = ccb_h->target_id; hcb->lun = ccb_h->target_lun;\n"
+" generate_identify_message(hcb);\n"
+" if (ccb_h->tag_action != CAM_TAG_ACTION_NONE)\n"
+" generate_unique_tag_message(hcb, ccb_h->tag_action);\n"
+" if (!target_negotiated(hcb))\n"
+" generate_negotiation_messages(hcb);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:438
+msgid ""
+"Then set up the SCSI command. The command storage may be specified in the "
+"CCB in many interesting ways, specified by the CCB flags. The command "
+"buffer can be contained in CCB or pointed to, in the latter case the pointer "
+"may be physical or virtual. Since the hardware commonly needs physical "
+"address we always convert the address to the physical one, typically using "
+"the busdma API."
+msgstr ""
+"Затем настройте команду SCSI. Хранилище команды может быть указано в CCB "
+"различными способами, определяемыми флагами CCB. Буфер команды может "
+"содержаться в CCB или указываться на него; в последнем случае указатель "
+"может быть физическим или виртуальным. Поскольку оборудованию обычно "
+"требуется физический адрес, мы всегда преобразуем адрес в физический, как "
+"правило, используя API busdma."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:441
+msgid ""
+"In case if a physical address is requested it is OK to return the CCB with "
+"the status `CAM_REQ_INVALID`, the current drivers do that. If necessary a "
+"physical address can be also converted or mapped back to a virtual address "
+"but with big pain, so we do not do that."
+msgstr ""
+"В случае, если запрашивается физический адрес, допустимо вернуть CCB со "
+"статусом `CAM_REQ_INVALID`, текущие драйверы так и делают. При необходимости "
+"физический адрес также может быть преобразован или отображен обратно в "
+"виртуальный, но с большими трудностями, поэтому мы этого не делаем."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:458
+#, no-wrap
+msgid ""
+" if (ccb_h->flags & CAM_CDB_POINTER) {\n"
+" /* CDB is a pointer */\n"
+" if (!(ccb_h->flags & CAM_CDB_PHYS)) {\n"
+" /* CDB pointer is virtual */\n"
+" hcb->cmd = vtobus(csio->cdb_io.cdb_ptr);\n"
+" } else {\n"
+" /* CDB pointer is physical */\n"
+" hcb->cmd = csio->cdb_io.cdb_ptr ;\n"
+" }\n"
+" } else {\n"
+" /* CDB is in the ccb (buffer) */\n"
+" hcb->cmd = vtobus(csio->cdb_io.cdb_bytes);\n"
+" }\n"
+" hcb->cmdlen = csio->cdb_len;\n"
+msgstr ""
+" if (ccb_h->flags & CAM_CDB_POINTER) {\n"
+" /* CDB is a pointer */\n"
+" if (!(ccb_h->flags & CAM_CDB_PHYS)) {\n"
+" /* CDB pointer is virtual */\n"
+" hcb->cmd = vtobus(csio->cdb_io.cdb_ptr);\n"
+" } else {\n"
+" /* CDB pointer is physical */\n"
+" hcb->cmd = csio->cdb_io.cdb_ptr ;\n"
+" }\n"
+" } else {\n"
+" /* CDB is in the ccb (buffer) */\n"
+" hcb->cmd = vtobus(csio->cdb_io.cdb_bytes);\n"
+" }\n"
+" hcb->cmdlen = csio->cdb_len;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:464
+msgid ""
+"Now it is time to set up the data. Again, the data storage may be specified "
+"in the CCB in many interesting ways, specified by the CCB flags. First we "
+"get the direction of the data transfer. The simplest case is if there is no "
+"data to transfer:"
+msgstr ""
+"Теперь настало время настроить данные. Опять же, хранилище данных может быть "
+"указано в CCB различными интересными способами, определяемыми флагами CCB. "
+"Сначала мы получаем направление передачи данных. Самый простой случай — если "
+"нет данных для передачи:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:468
+#, no-wrap
+msgid " int dir = (ccb_h->flags & CAM_DIR_MASK);\n"
+msgstr " int dir = (ccb_h->flags & CAM_DIR_MASK);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:471
+#, no-wrap
+msgid ""
+" if (dir == CAM_DIR_NONE)\n"
+" goto end_data;\n"
+msgstr ""
+" if (dir == CAM_DIR_NONE)\n"
+" goto end_data;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:482
+msgid ""
+"Then we check if the data is in one chunk or in a scatter-gather list, and "
+"the addresses are physical or virtual. The SCSI controller may be able to "
+"handle only a limited number of chunks of limited length. If the request "
+"hits this limitation we return an error. We use a special function to "
+"return the CCB to handle in one place the HCB resource shortages. The "
+"functions to add chunks are driver-dependent, and here we leave them without "
+"detailed implementation. See description of the SCSI command (CDB) handling "
+"for the details on the address-translation issues. If some variation is too "
+"difficult or impossible to implement with a particular card it is OK to "
+"return the status `CAM_REQ_INVALID`. Actually, it seems like the scatter-"
+"gather ability is not used anywhere in the CAM code now. But at least the "
+"case for a single non-scattered virtual buffer must be implemented, it is "
+"actively used by CAM."
+msgstr ""
+"Затем мы проверяем, находятся ли данные в одном фрагменте или в списке "
+"scatter-gather, а также являются ли адреса физическими или виртуальными. "
+"SCSI-контроллер может обрабатывать только ограниченное количество фрагментов "
+"ограниченной длины. Если запрос превышает это ограничение, мы возвращаем "
+"ошибку. Мы используем специальную функцию для возврата CCB, чтобы в одном "
+"месте обрабатывать нехватку ресурсов HCB. Функции для добавления фрагментов "
+"зависят от драйвера, и здесь мы оставляем их без детальной реализации. "
+"Подробности о проблемах трансляции адресов см. в описании обработки SCSI-"
+"команд (CDB). Если какая-то вариация слишком сложна или невозможна для "
+"реализации с конкретной картой, допустимо вернуть статус `CAM_REQ_INVALID`. "
+"На самом деле, похоже, что возможность scatter-gather нигде в коде CAM "
+"сейчас не используется. Но как минимум случай с единичным неразделённым "
+"виртуальным буфером должен быть реализован, так как он активно используется "
+"CAM."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:486
+#, no-wrap
+msgid " int rv;\n"
+msgstr " int rv;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:488
+#, no-wrap
+msgid " initialize_hcb_for_data(hcb);\n"
+msgstr " initialize_hcb_for_data(hcb);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:501
+#, no-wrap
+msgid ""
+" if ((!(ccb_h->flags & CAM_SCATTER_VALID)) {\n"
+" /* single buffer */\n"
+" if (!(ccb_h->flags & CAM_DATA_PHYS)) {\n"
+" rv = add_virtual_chunk(hcb, csio->data_ptr, csio->dxfer_len, dir);\n"
+" }\n"
+" } else {\n"
+" rv = add_physical_chunk(hcb, csio->data_ptr, csio->dxfer_len, dir);\n"
+" }\n"
+" } else {\n"
+" int i;\n"
+" struct bus_dma_segment *segs;\n"
+" segs = (struct bus_dma_segment *)csio->data_ptr;\n"
+msgstr ""
+" if ((!(ccb_h->flags & CAM_SCATTER_VALID)) {\n"
+" /* single buffer */\n"
+" if (!(ccb_h->flags & CAM_DATA_PHYS)) {\n"
+" rv = add_virtual_chunk(hcb, csio->data_ptr, csio->dxfer_len, dir);\n"
+" }\n"
+" } else {\n"
+" rv = add_physical_chunk(hcb, csio->data_ptr, csio->dxfer_len, dir);\n"
+" }\n"
+" } else {\n"
+" int i;\n"
+" struct bus_dma_segment *segs;\n"
+" segs = (struct bus_dma_segment *)csio->data_ptr;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:533
+#, no-wrap
+msgid ""
+" if ((ccb_h->flags & CAM_SG_LIST_PHYS) != 0) {\n"
+" /* The SG list pointer is physical */\n"
+" rv = setup_hcb_for_physical_sg_list(hcb, segs, csio->sglist_cnt);\n"
+" } else if (!(ccb_h->flags & CAM_DATA_PHYS)) {\n"
+" /* SG buffer pointers are virtual */\n"
+" for (i = 0; i < csio->sglist_cnt; i++) {\n"
+" rv = add_virtual_chunk(hcb, segs[i].ds_addr,\n"
+" segs[i].ds_len, dir);\n"
+" if (rv != CAM_REQ_CMP)\n"
+" break;\n"
+" }\n"
+" } else {\n"
+" /* SG buffer pointers are physical */\n"
+" for (i = 0; i < csio->sglist_cnt; i++) {\n"
+" rv = add_physical_chunk(hcb, segs[i].ds_addr,\n"
+" segs[i].ds_len, dir);\n"
+" if (rv != CAM_REQ_CMP)\n"
+" break;\n"
+" }\n"
+" }\n"
+" }\n"
+" if (rv != CAM_REQ_CMP) {\n"
+" /* we expect that add_*_chunk() functions return CAM_REQ_CMP\n"
+" * if they added a chunk successfully, CAM_REQ_TOO_BIG if\n"
+" * the request is too big (too many bytes or too many chunks),\n"
+" * CAM_REQ_INVALID in case of other troubles\n"
+" */\n"
+" free_hcb_and_ccb_done(hcb, ccb, rv);\n"
+" return;\n"
+" }\n"
+" end_data:\n"
+msgstr ""
+" if ((ccb_h->flags & CAM_SG_LIST_PHYS) != 0) {\n"
+" /* The SG list pointer is physical */\n"
+" rv = setup_hcb_for_physical_sg_list(hcb, segs, csio->sglist_cnt);\n"
+" } else if (!(ccb_h->flags & CAM_DATA_PHYS)) {\n"
+" /* SG buffer pointers are virtual */\n"
+" for (i = 0; i < csio->sglist_cnt; i++) {\n"
+" rv = add_virtual_chunk(hcb, segs[i].ds_addr,\n"
+" segs[i].ds_len, dir);\n"
+" if (rv != CAM_REQ_CMP)\n"
+" break;\n"
+" }\n"
+" } else {\n"
+" /* SG buffer pointers are physical */\n"
+" for (i = 0; i < csio->sglist_cnt; i++) {\n"
+" rv = add_physical_chunk(hcb, segs[i].ds_addr,\n"
+" segs[i].ds_len, dir);\n"
+" if (rv != CAM_REQ_CMP)\n"
+" break;\n"
+" }\n"
+" }\n"
+" }\n"
+" if (rv != CAM_REQ_CMP) {\n"
+" /* we expect that add_*_chunk() functions return CAM_REQ_CMP\n"
+" * if they added a chunk successfully, CAM_REQ_TOO_BIG if\n"
+" * the request is too big (too many bytes or too many chunks),\n"
+" * CAM_REQ_INVALID in case of other troubles\n"
+" */\n"
+" free_hcb_and_ccb_done(hcb, ccb, rv);\n"
+" return;\n"
+" }\n"
+" end_data:\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:536
+msgid ""
+"If disconnection is disabled for this CCB we pass this information to the "
+"hcb:"
+msgstr ""
+"Если отключение запрещено для этого CCB, мы передаем эту информацию в hcb:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:541
+#, no-wrap
+msgid ""
+" if (ccb_h->flags & CAM_DIS_DISCONNECT)\n"
+" hcb_disable_disconnect(hcb);\n"
+msgstr ""
+" if (ccb_h->flags & CAM_DIS_DISCONNECT)\n"
+" hcb_disable_disconnect(hcb);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:544
+msgid ""
+"If the controller is able to run REQUEST SENSE command all by itself then "
+"the value of the flag CAM_DIS_AUTOSENSE should also be passed to it, to "
+"prevent automatic REQUEST SENSE if the CAM subsystem does not want it."
+msgstr ""
+"Если контроллер способен самостоятельно выполнять команду REQUEST SENSE, то "
+"ему также следует передать значение флага CAM_DIS_AUTOSENSE, чтобы "
+"предотвратить автоматическое выполнение REQUEST SENSE, если подсистема CAM "
+"этого не требует."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:546
+msgid ""
+"The only thing left is to set up the timeout, pass our hcb to the hardware "
+"and return, the rest will be done by the interrupt handler (or timeout "
+"handler)."
+msgstr ""
+"Осталось только установить таймаут, передать наш hcb оборудованию и "
+"вернуться, остальное будет сделано обработчиком прерывания (или обработчиком "
+"таймаута)."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:553
+#, no-wrap
+msgid ""
+" ccb_h->timeout_ch = timeout(xxx_timeout, (caddr_t) hcb,\n"
+" (ccb_h->timeout * hz) / 1000); /* convert milliseconds to ticks */\n"
+" put_hcb_into_hardware_queue(hcb);\n"
+" return;\n"
+msgstr ""
+" ccb_h->timeout_ch = timeout(xxx_timeout, (caddr_t) hcb,\n"
+" (ccb_h->timeout * hz) / 1000); /* convert milliseconds to ticks */\n"
+" put_hcb_into_hardware_queue(hcb);\n"
+" return;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:556
+msgid "And here is a possible implementation of the function returning CCB:"
+msgstr "И вот возможная реализация функции, возвращающей CCB:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:563
+#, no-wrap
+msgid ""
+" static void\n"
+" free_hcb_and_ccb_done(struct xxx_hcb *hcb, union ccb *ccb, u_int32_t status)\n"
+" {\n"
+" struct xxx_softc *softc = hcb->softc;\n"
+msgstr ""
+" static void\n"
+" free_hcb_and_ccb_done(struct xxx_hcb *hcb, union ccb *ccb, u_int32_t status)\n"
+" {\n"
+" struct xxx_softc *softc = hcb->softc;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:578
+#, no-wrap
+msgid ""
+" ccb->ccb_h.ccb_hcb = 0;\n"
+" if (hcb != NULL) {\n"
+" untimeout(xxx_timeout, (caddr_t) hcb, ccb->ccb_h.timeout_ch);\n"
+" /* we're about to free a hcb, so the shortage has ended */\n"
+" if (softc->flags & RESOURCE_SHORTAGE) {\n"
+" softc->flags &= ~RESOURCE_SHORTAGE;\n"
+" status |= CAM_RELEASE_SIMQ;\n"
+" }\n"
+" free_hcb(hcb); /* also removes hcb from any internal lists */\n"
+" }\n"
+" ccb->ccb_h.status = status |\n"
+" (ccb->ccb_h.status & ~(CAM_STATUS_MASK|CAM_SIM_QUEUED));\n"
+" xpt_done(ccb);\n"
+" }\n"
+msgstr ""
+" ccb->ccb_h.ccb_hcb = 0;\n"
+" if (hcb != NULL) {\n"
+" untimeout(xxx_timeout, (caddr_t) hcb, ccb->ccb_h.timeout_ch);\n"
+" /* we're about to free a hcb, so the shortage has ended */\n"
+" if (softc->flags & RESOURCE_SHORTAGE) {\n"
+" softc->flags &= ~RESOURCE_SHORTAGE;\n"
+" status |= CAM_RELEASE_SIMQ;\n"
+" }\n"
+" free_hcb(hcb); /* also removes hcb from any internal lists */\n"
+" }\n"
+" ccb->ccb_h.status = status |\n"
+" (ccb->ccb_h.status & ~(CAM_STATUS_MASK|CAM_SIM_QUEUED));\n"
+" xpt_done(ccb);\n"
+" }\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:580
+#, no-wrap
+msgid "_XPT_RESET_DEV_ - send the SCSI \"BUS DEVICE RESET\" message to a device"
+msgstr "_XPT_RESET_DEV_ - отправить устройству сообщение SCSI \"BUS DEVICE RESET\""
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:585
+msgid ""
+"There is no data transferred in CCB except the header and the most "
+"interesting argument of it is target_id. Depending on the controller "
+"hardware a hardware control block just like for the XPT_SCSI_IO request may "
+"be constructed (see XPT_SCSI_IO request description) and sent to the "
+"controller or the SCSI controller may be immediately programmed to send this "
+"RESET message to the device or this request may be just not supported (and "
+"return the status `CAM_REQ_INVALID`). Also on completion of the request all "
+"the disconnected transactions for this target must be aborted (probably in "
+"the interrupt routine)."
+msgstr ""
+"В CCB не передаются данные, кроме заголовка, и наиболее интересным "
+"аргументом в нём является target_id. В зависимости от аппаратного "
+"обеспечения контроллера может быть создан аппаратный блок управления (как "
+"для запроса XPT_SCSI_IO, см. описание запроса XPT_SCSI_IO) и отправлен "
+"контроллеру, или SCSI-контроллер может быть немедленно запрограммирован на "
+"отправку этого сообщения RESET устройству, или этот запрос может просто не "
+"поддерживаться (и возвращать статус `CAM_REQ_INVALID`). Также при завершении "
+"запроса все отключенные транзакции для этого целевого устройства должны быть "
+"прерваны (вероятно, в процедуре прерывания)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:588
+msgid ""
+"Also all the current negotiations for the target are lost on reset, so they "
+"might be cleaned too. Or they clearing may be deferred, because anyway the "
+"target would request re-negotiation on the next transaction."
+msgstr ""
+"Кроме того, все текущие переговоры для цели теряются при сбросе, поэтому они "
+"также могут быть очищены. Или их очистка может быть отложена, так как в "
+"любом случае цель запросит повторные переговоры при следующей транзакции."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:589
+#, no-wrap
+msgid "_XPT_RESET_BUS_ - send the RESET signal to the SCSI bus"
+msgstr "_XPT_RESET_BUS_ - отправить сигнал RESET на шину SCSI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:592
+msgid ""
+"No arguments are passed in the CCB, the only interesting argument is the "
+"SCSI bus indicated by the struct sim pointer."
+msgstr ""
+"В CCB не передаются аргументы, единственный интересный аргумент — это шина "
+"SCSI, указанная структурой sim."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:594
+msgid ""
+"A minimalistic implementation would forget the SCSI negotiations for all the "
+"devices on the bus and return the status CAM_REQ_CMP."
+msgstr ""
+"Минималистичная реализация могла бы пропустить SCSI-переговоры для всех "
+"устройств на шине и вернуть статус CAM_REQ_CMP."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:597
+msgid ""
+"The proper implementation would in addition actually reset the SCSI bus "
+"(possible also reset the SCSI controller) and mark all the CCBs being "
+"processed, both those in the hardware queue and those being disconnected, as "
+"done with the status CAM_SCSI_BUS_RESET. Like:"
+msgstr ""
+"Правильная реализация дополнительно должна фактически сбросить шину SCSI "
+"(возможно, также сбросить контроллер SCSI) и пометить все обрабатываемые "
+"CCB, как находящиеся в аппаратной очереди, так и отключенные, как "
+"завершенные со статусом CAM_SCSI_BUS_RESET. Например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:603
+#, no-wrap
+msgid ""
+" int targ, lun;\n"
+" struct xxx_hcb *h, *hh;\n"
+" struct ccb_trans_settings neg;\n"
+" struct cam_path *path;\n"
+msgstr ""
+" int targ, lun;\n"
+" struct xxx_hcb *h, *hh;\n"
+" struct ccb_trans_settings neg;\n"
+" struct cam_path *path;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:609
+#, no-wrap
+msgid ""
+" /* The SCSI bus reset may take a long time, in this case its completion\n"
+" * should be checked by interrupt or timeout. But for simplicity\n"
+" * we assume here that it is really fast.\n"
+" */\n"
+" reset_scsi_bus(softc);\n"
+msgstr ""
+" /* The SCSI bus reset may take a long time, in this case its completion\n"
+" * should be checked by interrupt or timeout. But for simplicity\n"
+" * we assume here that it is really fast.\n"
+" */\n"
+" reset_scsi_bus(softc);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:615
+#, no-wrap
+msgid ""
+" /* drop all enqueued CCBs */\n"
+" for (h = softc->first_queued_hcb; h != NULL; h = hh) {\n"
+" hh = h->next;\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);\n"
+" }\n"
+msgstr ""
+" /* drop all enqueued CCBs */\n"
+" for (h = softc->first_queued_hcb; h != NULL; h = hh) {\n"
+" hh = h->next;\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:621
+#, no-wrap
+msgid ""
+" /* the clean values of negotiations to report */\n"
+" neg.bus_width = 8;\n"
+" neg.sync_period = neg.sync_offset = 0;\n"
+" neg.valid = (CCB_TRANS_BUS_WIDTH_VALID\n"
+" | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);\n"
+msgstr ""
+" /* the clean values of negotiations to report */\n"
+" neg.bus_width = 8;\n"
+" neg.sync_period = neg.sync_offset = 0;\n"
+" neg.valid = (CCB_TRANS_BUS_WIDTH_VALID\n"
+" | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:625
+#, no-wrap
+msgid ""
+" /* drop all disconnected CCBs and clean negotiations */\n"
+" for (targ=0; targ <= OUR_MAX_SUPPORTED_TARGET; targ++) {\n"
+" clean_negotiations(softc, targ);\n"
+msgstr ""
+" /* drop all disconnected CCBs and clean negotiations */\n"
+" for (targ=0; targ <= OUR_MAX_SUPPORTED_TARGET; targ++) {\n"
+" clean_negotiations(softc, targ);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:633
+#, no-wrap
+msgid ""
+" /* report the event if possible */\n"
+" if (xpt_create_path(&path, /*periph*/NULL,\n"
+" cam_sim_path(sim), targ,\n"
+" CAM_LUN_WILDCARD) == CAM_REQ_CMP) {\n"
+" xpt_async(AC_TRANSFER_NEG, path, &neg);\n"
+" xpt_free_path(path);\n"
+" }\n"
+msgstr ""
+" /* report the event if possible */\n"
+" if (xpt_create_path(&path, /*periph*/NULL,\n"
+" cam_sim_path(sim), targ,\n"
+" CAM_LUN_WILDCARD) == CAM_REQ_CMP) {\n"
+" xpt_async(AC_TRANSFER_NEG, path, &neg);\n"
+" xpt_free_path(path);\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:640
+#, no-wrap
+msgid ""
+" for (lun=0; lun <= OUR_MAX_SUPPORTED_LUN; lun++)\n"
+" for (h = softc->first_discon_hcb[targ][lun]; h != NULL; h = hh) {\n"
+" hh=h->next;\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);\n"
+" }\n"
+" }\n"
+msgstr ""
+" for (lun=0; lun <= OUR_MAX_SUPPORTED_LUN; lun++)\n"
+" for (h = softc->first_discon_hcb[targ][lun]; h != NULL; h = hh) {\n"
+" hh=h->next;\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);\n"
+" }\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:643
+#, no-wrap
+msgid ""
+" ccb->ccb_h.status = CAM_REQ_CMP;\n"
+" xpt_done(ccb);\n"
+msgstr ""
+" ccb->ccb_h.status = CAM_REQ_CMP;\n"
+" xpt_done(ccb);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:647
+#, no-wrap
+msgid ""
+" /* report the event */\n"
+" xpt_async(AC_BUS_RESET, softc->wpath, NULL);\n"
+" return;\n"
+msgstr ""
+" /* report the event */\n"
+" xpt_async(AC_BUS_RESET, softc->wpath, NULL);\n"
+" return;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:650
+msgid ""
+"Implementing the SCSI bus reset as a function may be a good idea because it "
+"would be re-used by the timeout function as a last resort if the things go "
+"wrong."
+msgstr ""
+"Реализация сброса шины SCSI в виде функции может быть хорошей идеей, так как "
+"она может быть повторно использована функцией таймаута в качестве последнего "
+"средства, если что-то пойдёт не так."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:651
+#, no-wrap
+msgid "_XPT_ABORT_ - abort the specified CCB"
+msgstr "_XPT_ABORT_ - прервать указанный CCB"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:655
+msgid ""
+"The arguments are transferred in the instance \"struct ccb_abort cab\" of "
+"the union ccb. The only argument field in it is:"
+msgstr ""
+"Аргументы передаются в экземпляре \"struct ccb_abort cab\" объединения ccb. "
+"Единственное поле аргумента в нём:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:657
+msgid "_abort_ccb_ - pointer to the CCB to be aborted"
+msgstr "_abort_ccb_ — указатель на CCB, который необходимо прервать"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:660
+msgid ""
+"If the abort is not supported just return the status CAM_UA_ABORT. This is "
+"also the easy way to minimally implement this call, return CAM_UA_ABORT in "
+"any case."
+msgstr ""
+"Если прерывание не поддерживается, просто верните статус CAM_UA_ABORT. Это "
+"также простой способ минимальной реализации этого вызова — в любом случае "
+"возвращать CAM_UA_ABORT."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:663
+msgid ""
+"The hard way is to implement this request honestly. First check that abort "
+"applies to a SCSI transaction:"
+msgstr ""
+"Трудный путь — честно реализовать этот запрос. Сначала проверьте, что "
+"прерывание применяется к SCSI-транзакции:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:668
+#, no-wrap
+msgid ""
+" struct ccb *abort_ccb;\n"
+" abort_ccb = ccb->cab.abort_ccb;\n"
+msgstr ""
+" struct ccb *abort_ccb;\n"
+" abort_ccb = ccb->cab.abort_ccb;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:674
+#, no-wrap
+msgid ""
+" if (abort_ccb->ccb_h.func_code != XPT_SCSI_IO) {\n"
+" ccb->ccb_h.status = CAM_UA_ABORT;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+msgstr ""
+" if (abort_ccb->ccb_h.func_code != XPT_SCSI_IO) {\n"
+" ccb->ccb_h.status = CAM_UA_ABORT;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:678
+msgid ""
+"Then it is necessary to find this CCB in our queue. This can be done by "
+"walking the list of all our hardware control blocks in search for one "
+"associated with this CCB:"
+msgstr ""
+"Затем необходимо найти этот CCB в нашей очереди. Это можно сделать, пройдясь "
+"по списку всех наших блоков управления оборудованием в поисках связанного с "
+"этим CCB:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:682
+#, no-wrap
+msgid " struct xxx_hcb *hcb, *h;\n"
+msgstr " struct xxx_hcb *hcb, *h;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:684
+#, no-wrap
+msgid " hcb = NULL;\n"
+msgstr " hcb = NULL;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:695
+#, no-wrap
+msgid ""
+" /* We assume that softc->first_hcb is the head of the list of all\n"
+" * HCBs associated with this bus, including those enqueued for\n"
+" * processing, being processed by hardware and disconnected ones.\n"
+" */\n"
+" for (h = softc->first_hcb; h != NULL; h = h->next) {\n"
+" if (h->ccb == abort_ccb) {\n"
+" hcb = h;\n"
+" break;\n"
+" }\n"
+" }\n"
+msgstr ""
+" /* We assume that softc->first_hcb is the head of the list of all\n"
+" * HCBs associated with this bus, including those enqueued for\n"
+" * processing, being processed by hardware and disconnected ones.\n"
+" */\n"
+" for (h = softc->first_hcb; h != NULL; h = h->next) {\n"
+" if (h->ccb == abort_ccb) {\n"
+" hcb = h;\n"
+" break;\n"
+" }\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:702
+#, no-wrap
+msgid ""
+" if (hcb == NULL) {\n"
+" /* no such CCB in our queue */\n"
+" ccb->ccb_h.status = CAM_PATH_INVALID;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+msgstr ""
+" if (hcb == NULL) {\n"
+" /* no such CCB in our queue */\n"
+" ccb->ccb_h.status = CAM_PATH_INVALID;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:704
+#, no-wrap
+msgid " hcb=found_hcb;\n"
+msgstr " hcb=found_hcb;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:709
+msgid ""
+"Now we look at the current processing status of the HCB. It may be either "
+"sitting in the queue waiting to be sent to the SCSI bus, being transferred "
+"right now, or disconnected and waiting for the result of the command, or "
+"actually completed by hardware but not yet marked as done by software. To "
+"make sure that we do not get in any races with hardware we mark the HCB as "
+"being aborted, so that if this HCB is about to be sent to the SCSI bus the "
+"SCSI controller will see this flag and skip it."
+msgstr ""
+"Теперь мы рассмотрим текущее состояние обработки HCB. Он может находиться в "
+"очереди, ожидая отправки на шину SCSI, передаваться в данный момент, быть "
+"отключенным и ожидать результата команды, или фактически завершённым с точки "
+"зрения аппаратуры, но ещё не отмеченным программным обеспечением, как "
+"выполненный. Чтобы избежать состояний гонки с аппаратурой, мы помечаем HCB "
+"как прерванный, так что если этот HCB вот-вот будет отправлен на шину SCSI, "
+"контроллер SCSI увидит этот флаг и пропустит его."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:713
+#, no-wrap
+msgid " int hstatus;\n"
+msgstr " int hstatus;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:718
+#, no-wrap
+msgid ""
+" /* shown as a function, in case special action is needed to make\n"
+" * this flag visible to hardware\n"
+" */\n"
+" set_hcb_flags(hcb, HCB_BEING_ABORTED);\n"
+msgstr ""
+" /* shown as a function, in case special action is needed to make\n"
+" * this flag visible to hardware\n"
+" */\n"
+" set_hcb_flags(hcb, HCB_BEING_ABORTED);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:720
+#, no-wrap
+msgid " abort_again:\n"
+msgstr " abort_again:\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:730
+#, no-wrap
+msgid ""
+" hstatus = get_hcb_status(hcb);\n"
+" switch (hstatus) {\n"
+" case HCB_SITTING_IN_QUEUE:\n"
+" remove_hcb_from_hardware_queue(hcb);\n"
+" /* FALLTHROUGH */\n"
+" case HCB_COMPLETED:\n"
+" /* this is an easy case */\n"
+" free_hcb_and_ccb_done(hcb, abort_ccb, CAM_REQ_ABORTED);\n"
+" break;\n"
+msgstr ""
+" hstatus = get_hcb_status(hcb);\n"
+" switch (hstatus) {\n"
+" case HCB_SITTING_IN_QUEUE:\n"
+" remove_hcb_from_hardware_queue(hcb);\n"
+" /* FALLTHROUGH */\n"
+" case HCB_COMPLETED:\n"
+" /* this is an easy case */\n"
+" free_hcb_and_ccb_done(hcb, abort_ccb, CAM_REQ_ABORTED);\n"
+" break;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:737
+msgid ""
+"If the CCB is being transferred right now we would like to signal to the "
+"SCSI controller in some hardware-dependent way that we want to abort the "
+"current transfer. The SCSI controller would set the SCSI ATTENTION signal "
+"and when the target responds to it send an ABORT message. We also reset the "
+"timeout to make sure that the target is not sleeping forever. If the "
+"command would not get aborted in some reasonable time like 10 seconds the "
+"timeout routine would go ahead and reset the whole SCSI bus. Since the "
+"command will be aborted in some reasonable time we can just return the abort "
+"request now as successfully completed, and mark the aborted CCB as aborted "
+"(but not mark it as done yet)."
+msgstr ""
+"Если CCB передаётся в данный момент, мы хотели бы сигнализировать "
+"контроллеру SCSI аппаратно-зависимым способом, что хотим прервать текущую "
+"передачу. Контроллер SCSI установит сигнал SCSI ATTENTION, и когда целевое "
+"устройство ответит на него, отправит сообщение ABORT. Мы также сбрасываем "
+"таймаут, чтобы убедиться, что целевое устройство не засыпает навсегда. Если "
+"команда не будет прервана в разумное время, например, за 10 секунд, "
+"процедура таймаута продолжит работу и сбросит всю шину SCSI. Поскольку "
+"команда будет прервана в разумные сроки, мы можем просто вернуть запрос на "
+"прерывание как успешно выполненный и пометить прерванный CCB как прерванный "
+"(но пока не помечать его как завершённый)."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:753
+#, no-wrap
+msgid ""
+" case HCB_BEING_TRANSFERRED:\n"
+" untimeout(xxx_timeout, (caddr_t) hcb, abort_ccb->ccb_h.timeout_ch);\n"
+" abort_ccb->ccb_h.timeout_ch =\n"
+" timeout(xxx_timeout, (caddr_t) hcb, 10 * hz);\n"
+" abort_ccb->ccb_h.status = CAM_REQ_ABORTED;\n"
+" /* ask the controller to abort that HCB, then generate\n"
+" * an interrupt and stop\n"
+" */\n"
+" if (signal_hardware_to_abort_hcb_and_stop(hcb) < 0) {\n"
+" /* oops, we missed the race with hardware, this transaction\n"
+" * got off the bus before we aborted it, try again */\n"
+" goto abort_again;\n"
+" }\n"
+msgstr ""
+" case HCB_BEING_TRANSFERRED:\n"
+" untimeout(xxx_timeout, (caddr_t) hcb, abort_ccb->ccb_h.timeout_ch);\n"
+" abort_ccb->ccb_h.timeout_ch =\n"
+" timeout(xxx_timeout, (caddr_t) hcb, 10 * hz);\n"
+" abort_ccb->ccb_h.status = CAM_REQ_ABORTED;\n"
+" /* ask the controller to abort that HCB, then generate\n"
+" * an interrupt and stop\n"
+" */\n"
+" if (signal_hardware_to_abort_hcb_and_stop(hcb) < 0) {\n"
+" /* oops, we missed the race with hardware, this transaction\n"
+" * got off the bus before we aborted it, try again */\n"
+" goto abort_again;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:755
+#, no-wrap
+msgid " break;\n"
+msgstr " break;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:759
+msgid ""
+"If the CCB is in the list of disconnected then set it up as an abort request "
+"and re-queue it at the front of hardware queue. Reset the timeout and "
+"report the abort request to be completed."
+msgstr ""
+"Если CCB находится в списке отключенных, то настроить его как запрос "
+"прерывания и повторно поставить в начало аппаратной очереди. Сбросить "
+"таймаут и сообщить о завершении запроса прерывания."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:773
+#, no-wrap
+msgid ""
+" case HCB_DISCONNECTED:\n"
+" untimeout(xxx_timeout, (caddr_t) hcb, abort_ccb->ccb_h.timeout_ch);\n"
+" abort_ccb->ccb_h.timeout_ch =\n"
+" timeout(xxx_timeout, (caddr_t) hcb, 10 * hz);\n"
+" put_abort_message_into_hcb(hcb);\n"
+" put_hcb_at_the_front_of_hardware_queue(hcb);\n"
+" break;\n"
+" }\n"
+" ccb->ccb_h.status = CAM_REQ_CMP;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+msgstr ""
+" case HCB_DISCONNECTED:\n"
+" untimeout(xxx_timeout, (caddr_t) hcb, abort_ccb->ccb_h.timeout_ch);\n"
+" abort_ccb->ccb_h.timeout_ch =\n"
+" timeout(xxx_timeout, (caddr_t) hcb, 10 * hz);\n"
+" put_abort_message_into_hcb(hcb);\n"
+" put_hcb_at_the_front_of_hardware_queue(hcb);\n"
+" break;\n"
+" }\n"
+" ccb->ccb_h.status = CAM_REQ_CMP;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:778
+msgid ""
+"That is all for the ABORT request, although there is one more issue. As the "
+"ABORT message cleans all the ongoing transactions on a LUN we have to mark "
+"all the other active transactions on this LUN as aborted. That should be "
+"done in the interrupt routine, after the transaction gets aborted."
+msgstr ""
+"Вот и все, что касается запроса ABORT, хотя есть еще один момент. Поскольку "
+"сообщение ABORT очищает все текущие транзакции на LUN, нам необходимо "
+"пометить все остальные активные транзакции на этом LUN как прерванные. Это "
+"должно быть выполнено в процедуре аппаратного прерывания после того, как "
+"транзакция будет прервана."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:782
+msgid ""
+"Implementing the CCB abort as a function may be quite a good idea, this "
+"function can be re-used if an I/O transaction times out. The only "
+"difference would be that the timed out transaction would return the status "
+"CAM_CMD_TIMEOUT for the timed out request. Then the case XPT_ABORT would be "
+"small, like that:"
+msgstr ""
+"Реализация прерывания CCB в виде функции может быть довольно хорошей идеей, "
+"эта функция может быть повторно использована, если транзакция ввода-вывода "
+"превысит время ожидания. Единственное различие будет в том, что для "
+"транзакции с истекшим временем ожидания будет возвращён статус "
+"CAM_CMD_TIMEOUT. Тогда код в case XPT_ABORT будет небольшим, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:788
+#, no-wrap
+msgid ""
+" case XPT_ABORT:\n"
+" struct ccb *abort_ccb;\n"
+" abort_ccb = ccb->cab.abort_ccb;\n"
+msgstr ""
+" case XPT_ABORT:\n"
+" struct ccb *abort_ccb;\n"
+" abort_ccb = ccb->cab.abort_ccb;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:801
+#, no-wrap
+msgid ""
+" if (abort_ccb->ccb_h.func_code != XPT_SCSI_IO) {\n"
+" ccb->ccb_h.status = CAM_UA_ABORT;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+" if (xxx_abort_ccb(abort_ccb, CAM_REQ_ABORTED) < 0)\n"
+" /* no such CCB in our queue */\n"
+" ccb->ccb_h.status = CAM_PATH_INVALID;\n"
+" else\n"
+" ccb->ccb_h.status = CAM_REQ_CMP;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+msgstr ""
+" if (abort_ccb->ccb_h.func_code != XPT_SCSI_IO) {\n"
+" ccb->ccb_h.status = CAM_UA_ABORT;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+" }\n"
+" if (xxx_abort_ccb(abort_ccb, CAM_REQ_ABORTED) < 0)\n"
+" /* no such CCB in our queue */\n"
+" ccb->ccb_h.status = CAM_PATH_INVALID;\n"
+" else\n"
+" ccb->ccb_h.status = CAM_REQ_CMP;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:803
+#, no-wrap
+msgid "_XPT_SET_TRAN_SETTINGS_ - explicitly set values of SCSI transfer settings"
+msgstr "_XPT_SET_TRAN_SETTINGS_ - явно установить значения настроек передачи SCSI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:806
+msgid ""
+"The arguments are transferred in the instance \"struct ccb_trans_setting "
+"cts\" of the union ccb:"
+msgstr ""
+"Аргументы передаются в экземпляре \"struct ccb_trans_setting cts\" "
+"объединения ccb:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:808
+msgid "_valid_ - a bitmask showing which settings should be updated:"
+msgstr ""
+"_valid_ - битовая маска, показывающая, какие настройки должны быть обновлены:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:809
+msgid "_CCB_TRANS_SYNC_RATE_VALID_ - synchronous transfer rate"
+msgstr "_CCB_TRANS_SYNC_RATE_VALID_ - скорость синхронной передачи"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:810
+msgid "_CCB_TRANS_SYNC_OFFSET_VALID_ - synchronous offset"
+msgstr "_CCB_TRANS_SYNC_OFFSET_VALID_ - синхронное смещение"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:811
+msgid "_CCB_TRANS_BUS_WIDTH_VALID_ - bus width"
+msgstr "_CCB_TRANS_BUS_WIDTH_VALID_ - ширина шины"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:812
+msgid "_CCB_TRANS_DISC_VALID_ - set enable/disable disconnection"
+msgstr "_CCB_TRANS_DISC_VALID_ - установить разрешение/запрет отключения"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:813
+msgid "_CCB_TRANS_TQ_VALID_ - set enable/disable tagged queuing"
+msgstr "_CCB_TRANS_TQ_VALID_ - установить разрешение/запрет очередей с тегами"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:815
+msgid ""
+"_flags_ - consists of two parts, binary arguments and identification of sub-"
+"operations. The binary arguments are:"
+msgstr ""
+"_flags_ - состоит из двух частей: бинарных аргументов и идентификации "
+"подопераций. Бинарные аргументы:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:816
+msgid "_CCB_TRANS_DISC_ENB_ - enable disconnection"
+msgstr "_CCB_TRANS_DISC_ENB_ - разрешить отключение"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:817
+msgid "_CCB_TRANS_TAG_ENB_ - enable tagged queuing"
+msgstr "_CCB_TRANS_TAG_ENB_ - разрешить тегированную очередь"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:818
+msgid "the sub-operations are:"
+msgstr "подоперации:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:819
+msgid "_CCB_TRANS_CURRENT_SETTINGS_ - change the current negotiations"
+msgstr "_CCB_TRANS_CURRENT_SETTINGS_ - изменить текущие параметры согласования"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:820
+msgid ""
+"_CCB_TRANS_USER_SETTINGS_ - remember the desired user values sync_period, "
+"sync_offset - self-explanatory, if sync_offset==0 then the asynchronous mode "
+"is requested bus_width - bus width, in bits (not bytes)"
+msgstr ""
+"_CCB_TRANS_USER_SETTINGS_ - сохранять желаемые пользовательские значения "
+"sync_period, sync_offset - самоочевидные параметры; если sync_offset==0, то "
+"запрашивается асинхронный режим bus_width - ширина шины в битах (не в байтах)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:825
+msgid ""
+"Two sets of negotiated parameters are supported, the user settings and the "
+"current settings. The user settings are not really used much in the SIM "
+"drivers, this is mostly just a piece of memory where the upper levels can "
+"store (and later recall) its ideas about the parameters. Setting the user "
+"parameters does not cause re-negotiation of the transfer rates. But when "
+"the SCSI controller does a negotiation it must never set the values higher "
+"than the user parameters, so it is essentially the top boundary."
+msgstr ""
+"Поддерживаются два набора согласованных параметров: пользовательские "
+"настройки и текущие настройки. Пользовательские настройки не так часто "
+"используются в драйверах SIM, это в основном просто область памяти, где "
+"верхние уровни могут сохранять (и позже извлекать) свои представления о "
+"параметрах. Установка пользовательских параметров не вызывает повторного "
+"согласования скоростей передачи. Однако, когда SCSI-контроллер выполняет "
+"согласование, он никогда не должен устанавливать значения выше "
+"пользовательских параметров, так что они по сути являются верхней границей."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:830
+msgid ""
+"The current settings are, as the name says, current. Changing them means "
+"that the parameters must be re-negotiated on the next transfer. Again, "
+"these \"new current settings\" are not supposed to be forced on the device, "
+"just they are used as the initial step of negotiations. Also they must be "
+"limited by actual capabilities of the SCSI controller: for example, if the "
+"SCSI controller has 8-bit bus and the request asks to set 16-bit wide "
+"transfers this parameter must be silently truncated to 8-bit transfers "
+"before sending it to the device."
+msgstr ""
+"Текущие настройки, как следует из названия, являются текущими. Их изменение "
+"означает, что параметры должны быть повторно согласованы при следующей "
+"передаче. Опять же, эти «новые текущие настройки» не предназначены для "
+"принудительного применения к устройству, они лишь используются в качестве "
+"начального шага переговоров. Кроме того, они должны быть ограничены "
+"реальными возможностями SCSI-контроллера: например, если SCSI-контроллер "
+"имеет 8-битную шину, а запрос требует установки 16-битных передач, этот "
+"параметр должен быть тихо усечён до 8-битных передач перед отправкой на "
+"устройство."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:832
+msgid ""
+"One caveat is that the bus width and synchronous parameters are per target "
+"while the disconnection and tag enabling parameters are per lun."
+msgstr ""
+"Один нюанс заключается в том, что ширина шины и синхронные параметры "
+"относятся к цели, тогда как параметры отключения и включения тегов относятся "
+"к логическому устройству LUN."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:834
+msgid ""
+"The recommended implementation is to keep 3 sets of negotiated (bus width "
+"and synchronous transfer) parameters:"
+msgstr ""
+"Рекомендуемая реализация заключается в хранении 3 наборов согласованных "
+"параметров (ширина шины и синхронная передача):"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:836
+msgid "_user_ - the user set, as above"
+msgstr "_user_ - пользовательский набор, как указано выше"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:837
+msgid "_current_ - those actually in effect"
+msgstr "_current_ - тот, который фактически действуют"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:838
+msgid "_goal_ - those requested by setting of the \"current\" parameters"
+msgstr ""
+"_goal_ - тот набор, который запрошен для установки параметров в качестве "
+"\"текущих\""
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:840
+msgid "The code looks like:"
+msgstr "Код выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:846
+#, no-wrap
+msgid ""
+" struct ccb_trans_settings *cts;\n"
+" int targ, lun;\n"
+" int flags;\n"
+msgstr ""
+" struct ccb_trans_settings *cts;\n"
+" int targ, lun;\n"
+" int flags;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:858
+#, no-wrap
+msgid ""
+" cts = &ccb->cts;\n"
+" targ = ccb_h->target_id;\n"
+" lun = ccb_h->target_lun;\n"
+" flags = cts->flags;\n"
+" if (flags & CCB_TRANS_USER_SETTINGS) {\n"
+" if (flags & CCB_TRANS_SYNC_RATE_VALID)\n"
+" softc->user_sync_period[targ] = cts->sync_period;\n"
+" if (flags & CCB_TRANS_SYNC_OFFSET_VALID)\n"
+" softc->user_sync_offset[targ] = cts->sync_offset;\n"
+" if (flags & CCB_TRANS_BUS_WIDTH_VALID)\n"
+" softc->user_bus_width[targ] = cts->bus_width;\n"
+msgstr ""
+" cts = &ccb->cts;\n"
+" targ = ccb_h->target_id;\n"
+" lun = ccb_h->target_lun;\n"
+" flags = cts->flags;\n"
+" if (flags & CCB_TRANS_USER_SETTINGS) {\n"
+" if (flags & CCB_TRANS_SYNC_RATE_VALID)\n"
+" softc->user_sync_period[targ] = cts->sync_period;\n"
+" if (flags & CCB_TRANS_SYNC_OFFSET_VALID)\n"
+" softc->user_sync_offset[targ] = cts->sync_offset;\n"
+" if (flags & CCB_TRANS_BUS_WIDTH_VALID)\n"
+" softc->user_bus_width[targ] = cts->bus_width;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:877
+#, no-wrap
+msgid ""
+" if (flags & CCB_TRANS_DISC_VALID) {\n"
+" softc->user_tflags[targ][lun] &= ~CCB_TRANS_DISC_ENB;\n"
+" softc->user_tflags[targ][lun] |= flags & CCB_TRANS_DISC_ENB;\n"
+" }\n"
+" if (flags & CCB_TRANS_TQ_VALID) {\n"
+" softc->user_tflags[targ][lun] &= ~CCB_TRANS_TQ_ENB;\n"
+" softc->user_tflags[targ][lun] |= flags & CCB_TRANS_TQ_ENB;\n"
+" }\n"
+" }\n"
+" if (flags & CCB_TRANS_CURRENT_SETTINGS) {\n"
+" if (flags & CCB_TRANS_SYNC_RATE_VALID)\n"
+" softc->goal_sync_period[targ] =\n"
+" max(cts->sync_period, OUR_MIN_SUPPORTED_PERIOD);\n"
+" if (flags & CCB_TRANS_SYNC_OFFSET_VALID)\n"
+" softc->goal_sync_offset[targ] =\n"
+" min(cts->sync_offset, OUR_MAX_SUPPORTED_OFFSET);\n"
+" if (flags & CCB_TRANS_BUS_WIDTH_VALID)\n"
+" softc->goal_bus_width[targ] = min(cts->bus_width, OUR_BUS_WIDTH);\n"
+msgstr ""
+" if (flags & CCB_TRANS_DISC_VALID) {\n"
+" softc->user_tflags[targ][lun] &= ~CCB_TRANS_DISC_ENB;\n"
+" softc->user_tflags[targ][lun] |= flags & CCB_TRANS_DISC_ENB;\n"
+" }\n"
+" if (flags & CCB_TRANS_TQ_VALID) {\n"
+" softc->user_tflags[targ][lun] &= ~CCB_TRANS_TQ_ENB;\n"
+" softc->user_tflags[targ][lun] |= flags & CCB_TRANS_TQ_ENB;\n"
+" }\n"
+" }\n"
+" if (flags & CCB_TRANS_CURRENT_SETTINGS) {\n"
+" if (flags & CCB_TRANS_SYNC_RATE_VALID)\n"
+" softc->goal_sync_period[targ] =\n"
+" max(cts->sync_period, OUR_MIN_SUPPORTED_PERIOD);\n"
+" if (flags & CCB_TRANS_SYNC_OFFSET_VALID)\n"
+" softc->goal_sync_offset[targ] =\n"
+" min(cts->sync_offset, OUR_MAX_SUPPORTED_OFFSET);\n"
+" if (flags & CCB_TRANS_BUS_WIDTH_VALID)\n"
+" softc->goal_bus_width[targ] = min(cts->bus_width, OUR_BUS_WIDTH);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:890
+#, no-wrap
+msgid ""
+" if (flags & CCB_TRANS_DISC_VALID) {\n"
+" softc->current_tflags[targ][lun] &= ~CCB_TRANS_DISC_ENB;\n"
+" softc->current_tflags[targ][lun] |= flags & CCB_TRANS_DISC_ENB;\n"
+" }\n"
+" if (flags & CCB_TRANS_TQ_VALID) {\n"
+" softc->current_tflags[targ][lun] &= ~CCB_TRANS_TQ_ENB;\n"
+" softc->current_tflags[targ][lun] |= flags & CCB_TRANS_TQ_ENB;\n"
+" }\n"
+" }\n"
+" ccb->ccb_h.status = CAM_REQ_CMP;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+msgstr ""
+" if (flags & CCB_TRANS_DISC_VALID) {\n"
+" softc->current_tflags[targ][lun] &= ~CCB_TRANS_DISC_ENB;\n"
+" softc->current_tflags[targ][lun] |= flags & CCB_TRANS_DISC_ENB;\n"
+" }\n"
+" if (flags & CCB_TRANS_TQ_VALID) {\n"
+" softc->current_tflags[targ][lun] &= ~CCB_TRANS_TQ_ENB;\n"
+" softc->current_tflags[targ][lun] |= flags & CCB_TRANS_TQ_ENB;\n"
+" }\n"
+" }\n"
+" ccb->ccb_h.status = CAM_REQ_CMP;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:894
+msgid ""
+"Then when the next I/O request will be processed it will check if it has to "
+"re-negotiate, for example by calling the function target_negotiated(hcb). "
+"It can be implemented like this:"
+msgstr ""
+"Затем, когда следующий запрос ввода-вывода будет обработан, он проверит, "
+"нужно ли повторное согласование, например, вызовом функции "
+"target_negotiated(hcb). Это может быть реализовано следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:902
+#, no-wrap
+msgid ""
+" int\n"
+" target_negotiated(struct xxx_hcb *hcb)\n"
+" {\n"
+" struct softc *softc = hcb->softc;\n"
+" int targ = hcb->targ;\n"
+msgstr ""
+" int\n"
+" target_negotiated(struct xxx_hcb *hcb)\n"
+" {\n"
+" struct softc *softc = hcb->softc;\n"
+" int targ = hcb->targ;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:910
+#, no-wrap
+msgid ""
+" if (softc->current_sync_period[targ] != softc->goal_sync_period[targ]\n"
+" || softc->current_sync_offset[targ] != softc->goal_sync_offset[targ]\n"
+" || softc->current_bus_width[targ] != softc->goal_bus_width[targ])\n"
+" return 0; /* FALSE */\n"
+" else\n"
+" return 1; /* TRUE */\n"
+" }\n"
+msgstr ""
+" if (softc->current_sync_period[targ] != softc->goal_sync_period[targ]\n"
+" || softc->current_sync_offset[targ] != softc->goal_sync_offset[targ]\n"
+" || softc->current_bus_width[targ] != softc->goal_bus_width[targ])\n"
+" return 0; /* FALSE */\n"
+" else\n"
+" return 1; /* TRUE */\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:914
+msgid ""
+"After the values are re-negotiated the resulting values must be assigned to "
+"both current and goal parameters, so for future I/O transactions the current "
+"and goal parameters would be the same and `target_negotiated()` would return "
+"TRUE. When the card is initialized (in `xxx_attach()`) the current "
+"negotiation values must be initialized to narrow asynchronous mode, the goal "
+"and current values must be initialized to the maximal values supported by "
+"controller."
+msgstr ""
+"После пересогласования значений полученные значения должны быть присвоены "
+"как текущим, так и целевым параметрам, чтобы для будущих операций ввода-"
+"вывода текущие и целевые параметры совпадали, и функция "
+"`target_negotiated()` возвращала TRUE. При инициализации карты (в "
+"`xxx_attach()`) текущие параметры согласования должны быть инициализированы "
+"узким асинхронным режимом, а целевые и текущие значения должны быть "
+"инициализированы максимальными значениями, поддерживаемыми контроллером."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:915
+#, no-wrap
+msgid "_XPT_GET_TRAN_SETTINGS_ - get values of SCSI transfer settings"
+msgstr "_XPT_GET_TRAN_SETTINGS_ - получить значения настроек передачи SCSI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:920
+msgid ""
+"This operations is the reverse of XPT_SET_TRAN_SETTINGS. Fill up the CCB "
+"instance \"struct ccb_trans_setting cts\" with data as requested by the "
+"flags CCB_TRANS_CURRENT_SETTINGS or CCB_TRANS_USER_SETTINGS (if both are set "
+"then the existing drivers return the current settings). Set all the bits in "
+"the valid field."
+msgstr ""
+"Эта операция является обратной XPT_SET_TRAN_SETTINGS. Заполните экземпляр "
+"CCB \"struct ccb_trans_setting cts\" данными, запрошенными флагами "
+"CCB_TRANS_CURRENT_SETTINGS или CCB_TRANS_USER_SETTINGS (если установлены "
+"оба, существующие драйверы возвращают текущие настройки). Установите все "
+"биты в поле valid."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:921
+#, no-wrap
+msgid "_XPT_CALC_GEOMETRY_ - calculate logical (BIOS) geometry of the disk"
+msgstr "_XPT_CALC_GEOMETRY_ - вычислить логическую (BIOS) геометрию диска"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:924
+msgid ""
+"The arguments are transferred in the instance \"struct ccb_calc_geometry "
+"ccg\" of the union ccb:"
+msgstr ""
+"Аргументы передаются в экземпляре \"struct ccb_calc_geometry ccg\" "
+"объединения ccb:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:926
+msgid "_block_size_ - input, block (A.K.A sector) size in bytes"
+msgstr ""
+"_block_size_ - вход, размер блока (также известный как сектор) в байтах"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:927
+msgid "_volume_size_ - input, volume size in bytes"
+msgstr "_volume_size_ - вход, размер тома в байтах"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:928
+msgid "_cylinders_ - output, logical cylinders"
+msgstr "_cylinders_ - выход, логические цилиндры"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:929
+msgid "_heads_ - output, logical heads"
+msgstr "_heads_ - выход, логические головки"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:930
+msgid "_secs_per_track_ - output, logical sectors per track"
+msgstr "_secs_per_track_ - выход, логических секторов на дорожку"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:933
+msgid ""
+"If the returned geometry differs much enough from what the SCSI controller "
+"BIOS thinks and a disk on this SCSI controller is used as bootable the "
+"system may not be able to boot. The typical calculation example taken from "
+"the aic7xxx driver is:"
+msgstr ""
+"Если возвращённая геометрия значительно отличается от той, которую "
+"предполагает BIOS SCSI-контроллера, и диск на этом SCSI-контроллере "
+"используется как загрузочный, система может не загрузиться. Типичный пример "
+"расчёта, взятый из драйвера `aic7xxx`, выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:940
+#, no-wrap
+msgid ""
+" struct ccb_calc_geometry *ccg;\n"
+" u_int32_t size_mb;\n"
+" u_int32_t secs_per_cylinder;\n"
+" int extended;\n"
+msgstr ""
+" struct ccb_calc_geometry *ccg;\n"
+" u_int32_t size_mb;\n"
+" u_int32_t secs_per_cylinder;\n"
+" int extended;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:945
+#, no-wrap
+msgid ""
+" ccg = &ccb->ccg;\n"
+" size_mb = ccg->volume_size\n"
+" / ((1024L * 1024L) / ccg->block_size);\n"
+" extended = check_cards_EEPROM_for_extended_geometry(softc);\n"
+msgstr ""
+" ccg = &ccb->ccg;\n"
+" size_mb = ccg->volume_size\n"
+" / ((1024L * 1024L) / ccg->block_size);\n"
+" extended = check_cards_EEPROM_for_extended_geometry(softc);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:958
+#, no-wrap
+msgid ""
+" if (size_mb > 1024 && extended) {\n"
+" ccg->heads = 255;\n"
+" ccg->secs_per_track = 63;\n"
+" } else {\n"
+" ccg->heads = 64;\n"
+" ccg->secs_per_track = 32;\n"
+" }\n"
+" secs_per_cylinder = ccg->heads * ccg->secs_per_track;\n"
+" ccg->cylinders = ccg->volume_size / secs_per_cylinder;\n"
+" ccb->ccb_h.status = CAM_REQ_CMP;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+msgstr ""
+" if (size_mb > 1024 && extended) {\n"
+" ccg->heads = 255;\n"
+" ccg->secs_per_track = 63;\n"
+" } else {\n"
+" ccg->heads = 64;\n"
+" ccg->secs_per_track = 32;\n"
+" }\n"
+" secs_per_cylinder = ccg->heads * ccg->secs_per_track;\n"
+" ccg->cylinders = ccg->volume_size / secs_per_cylinder;\n"
+" ccb->ccb_h.status = CAM_REQ_CMP;\n"
+" xpt_done(ccb);\n"
+" return;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:963
+msgid ""
+"This gives the general idea, the exact calculation depends on the quirks of "
+"the particular BIOS. If BIOS provides no way set the \"extended "
+"translation\" flag in EEPROM this flag should normally be assumed equal to "
+"1. Other popular geometries are:"
+msgstr ""
+"Это дает общее представление, точный расчет зависит от особенностей "
+"конкретной BIOS. Если BIOS не предоставляет возможности установить флаг "
+"\"расширенной трансляции\" в EEPROM, этот флаг обычно следует считать равным "
+"1. Другие популярные геометрии:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:968
+#, no-wrap
+msgid ""
+" 128 heads, 63 sectors - Symbios controllers\n"
+" 16 heads, 63 sectors - old controllers\n"
+msgstr ""
+" 128 heads, 63 sectors - Symbios controllers\n"
+" 16 heads, 63 sectors - old controllers\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:971
+msgid ""
+"Some system BIOSes and SCSI BIOSes fight with each other with variable "
+"success, for example a combination of Symbios 875/895 SCSI and Phoenix BIOS "
+"can give geometry 128/63 after power up and 255/63 after a hard reset or "
+"soft reboot."
+msgstr ""
+"Некоторые системные BIOS и SCSI BIOS конфликтуют друг с другом с переменным "
+"успехом. Например, комбинация Symbios 875/895 SCSI и Phoenix BIOS может "
+"выдавать геометрию 128/63 после включения питания и 255/63 после жесткого "
+"сброса или мягкой перезагрузки."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:972
+#, no-wrap
+msgid "_XPT_PATH_INQ_ - path inquiry, in other words get the SIM driver and SCSI controller (also known as HBA - Host Bus Adapter) properties"
+msgstr "_XPT_PATH_INQ_ - запрос пути, другими словами, получение свойств драйвера SIM и контроллера SCSI (также известного как HBA - Host Bus Adapter)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:975
+msgid ""
+"The properties are returned in the instance \"struct ccb_pathinq cpi\" of "
+"the union ccb:"
+msgstr ""
+"Свойства возвращаются в экземпляре \"struct ccb_pathinq cpi\" объединения "
+"ccb:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:977
+msgid "version_num - the SIM driver version number, now all drivers use 1"
+msgstr ""
+"`version_num` - номер версии драйвера SIM, в настоящее время все драйверы "
+"используют 1"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:978
+msgid "hba_inquiry - bitmask of features supported by the controller:"
+msgstr "hba_inquiry - битовая маска функций, поддерживаемых контроллером:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:979
+msgid "PI_MDP_ABLE - supports MDP message (something from SCSI3?)"
+msgstr "PI_MDP_ABLE - поддерживает сообщение MDP (что-то из SCSI3?)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:980
+msgid "PI_WIDE_32 - supports 32 bit wide SCSI"
+msgstr "PI_WIDE_32 — поддерживает 32-битную широкую SCSI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:981
+msgid "PI_WIDE_16 - supports 16 bit wide SCSI"
+msgstr "PI_WIDE_16 — поддерживает 16-битную широкую SCSI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:982
+msgid "PI_SDTR_ABLE - can negotiate synchronous transfer rate"
+msgstr "PI_SDTR_ABLE - может согласовать синхронную скорость передачи"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:983
+msgid "PI_LINKED_CDB - supports linked commands"
+msgstr "PI_LINKED_CDB - поддерживает связанные команды"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:984
+msgid "PI_TAG_ABLE - supports tagged commands"
+msgstr "PI_TAG_ABLE - поддерживает помеченные команды"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:985
+msgid ""
+"PI_SOFT_RST - supports soft reset alternative (hard reset and soft reset are "
+"mutually exclusive within a SCSI bus)"
+msgstr ""
+"PI_SOFT_RST — поддерживает альтернативу мягкого сброса (жесткий сброс и "
+"мягкий сброс являются взаимоисключающими в пределах шины SCSI)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:986
+msgid "target_sprt - flags for target mode support, 0 if unsupported"
+msgstr ""
+"target_sprt - флаги поддержки целевого режима, 0 если не поддерживается"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:987
+msgid "hba_misc - miscellaneous controller features:"
+msgstr "hba_misc - различные функции контроллера:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:988
+msgid "PIM_SCANHILO - bus scans from high ID to low ID"
+msgstr "PIM_SCANHILO - сканирование шины от высокого ID к низкому ID"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:989
+msgid "PIM_NOREMOVE - removable devices not included in scan"
+msgstr "PIM_NOREMOVE - съемные устройства не включены в сканирование"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:990
+msgid "PIM_NOINITIATOR - initiator role not supported"
+msgstr "PIM_NOINITIATOR - роль инициатора не поддерживается"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:991
+msgid "PIM_NOBUSRESET - user has disabled initial BUS RESET"
+msgstr "PIM_NOBUSRESET - пользователь отключил начальный BUS RESET"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:992
+msgid ""
+"hba_eng_cnt - mysterious HBA engine count, something related to compression, "
+"now is always set to 0"
+msgstr ""
+"hba_eng_cnt - загадочное количество движков HBA, что-то связанное со "
+"сжатием, в настоящее время всегда устанавливается в 0"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:993
+msgid "vuhba_flags - vendor-unique flags, unused now"
+msgstr ""
+"vuhba_flags - уникальные флаги производителя, в настоящее время не "
+"используются"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:994
+msgid ""
+"max_target - maximal supported target ID (7 for 8-bit bus, 15 for 16-bit "
+"bus, 127 for Fibre Channel)"
+msgstr ""
+"max_target - максимальный поддерживаемый идентификатор целевого устройства "
+"(7 для 8-битной шины, 15 для 16-битной шины, 127 для Fibre Channel)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:995
+msgid ""
+"max_lun - maximal supported LUN ID (7 for older SCSI controllers, 63 for "
+"newer ones)"
+msgstr ""
+"max_lun - максимально поддерживаемый идентификатор LUN (7 для старых SCSI-"
+"контроллеров, 63 для новых)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:996
+msgid "async_flags - bitmask of installed Async handler, unused now"
+msgstr ""
+"async_flags - битовая маска установленных обработчиков Async, в настоящее "
+"время не используется"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:997
+msgid "hpath_id - highest Path ID in the subsystem, unused now"
+msgstr ""
+"hpath_id - наивысший Path ID в подсистеме, в настоящее время не используется"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:998
+msgid "unit_number - the controller unit number, cam_sim_unit(sim)"
+msgstr "unit_number - номер контроллера, cam_sim_unit(sim)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:999
+msgid "bus_id - the bus number, cam_sim_bus(sim)"
+msgstr "bus_id - номер шины, cam_sim_bus(sim)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1000
+msgid "initiator_id - the SCSI ID of the controller itself"
+msgstr "initiator_id - SCSI ID самого контроллера"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1001
+msgid ""
+"base_transfer_speed - nominal transfer speed in KB/s for asynchronous narrow "
+"transfers, equals to 3300 for SCSI"
+msgstr ""
+"base_transfer_speed - номинальная скорость передачи в КБ/с для асинхронных "
+"узкополосных передач, равна 3300 для SCSI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1002
+msgid ""
+"sim_vid - SIM driver's vendor id, a zero-terminated string of maximal length "
+"SIM_IDLEN including the terminating zero"
+msgstr ""
+"sim_vid - идентификатор производителя драйвера SIM, строка с нулевым "
+"окончанием максимальной длины SIM_IDLEN, включая завершающий ноль"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1003
+msgid ""
+"hba_vid - SCSI controller's vendor id, a zero-terminated string of maximal "
+"length HBA_IDLEN including the terminating zero"
+msgstr ""
+"hba_vid - идентификатор производителя SCSI-контроллера, строка с нулевым "
+"окончанием максимальной длины HBA_IDLEN, включая завершающий ноль"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1004
+msgid ""
+"dev_name - device driver name, a zero-terminated string of maximal length "
+"DEV_IDLEN including the terminating zero, equal to cam_sim_name(sim)"
+msgstr ""
+"dev_name - имя драйвера устройства, строка с нулевым окончанием максимальной "
+"длины DEV_IDLEN, включая завершающий ноль, эквивалентно cam_sim_name(sim)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1006
+msgid ""
+"The recommended way of setting the string fields is using strncpy, like:"
+msgstr ""
+"Рекомендуемый способ установки строковых полей — использование strncpy, "
+"например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1010
+#, no-wrap
+msgid " strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);\n"
+msgstr " strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1013
+msgid ""
+"After setting the values set the status to CAM_REQ_CMP and mark the CCB as "
+"done."
+msgstr ""
+"После установки значений установите статус в CAM_REQ_CMP и пометьте CCB как "
+"завершённый."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1015
+#, no-wrap
+msgid "Polling xxx_poll"
+msgstr "Опрос xxx_poll"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1029
+msgid ""
+"The poll function is used to simulate the interrupts when the interrupt "
+"subsystem is not functioning (for example, when the system has crashed and "
+"is creating the system dump). The CAM subsystem sets the proper interrupt "
+"level before calling the poll routine. So all it needs to do is to call the "
+"interrupt routine (or the other way around, the poll routine may be doing "
+"the real action and the interrupt routine would just call the poll "
+"routine). Why bother about a separate function then? This has to do with "
+"different calling conventions. The `xxx_poll` routine gets the struct "
+"cam_sim pointer as its argument while the PCI interrupt routine by common "
+"convention gets pointer to the struct `xxx_softc` and the ISA interrupt "
+"routine gets just the device unit number. So the poll routine would "
+"normally look as:"
+msgstr ""
+"Функция poll используется для имитации прерываний, когда подсистема "
+"прерываний не функционирует (например, когда система аварийно завершила "
+"работу и создает дамп памяти). Подсистема CAM устанавливает соответствующий "
+"уровень прерывания перед вызовом процедуры poll. Таким образом, все, что ей "
+"нужно сделать, — это вызвать процедуру прерывания (или наоборот, процедура "
+"poll может выполнять реальные действия, а процедура прерывания просто "
+"вызывает процедуру poll). Зачем тогда нужна отдельная функция? Это связано с "
+"различными соглашениями о вызовах. Процедура `xxx_poll` получает указатель "
+"на структуру cam_sim в качестве аргумента, в то время как процедура "
+"прерывания PCI по общему соглашению получает указатель на структуру "
+"`xxx_softc`, а процедура прерывания ISA получает только номер устройства. "
+"Таким образом, процедура poll обычно выглядит следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1037
+#, no-wrap
+msgid ""
+"static void\n"
+"xxx_poll(struct cam_sim *sim)\n"
+"{\n"
+" xxx_intr((struct xxx_softc *)cam_sim_softc(sim)); /* for PCI device */\n"
+"}\n"
+msgstr ""
+"static void\n"
+"xxx_poll(struct cam_sim *sim)\n"
+"{\n"
+" xxx_intr((struct xxx_softc *)cam_sim_softc(sim)); /* for PCI device */\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1040
+msgid "or"
+msgstr "или"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1048
+#, no-wrap
+msgid ""
+"static void\n"
+"xxx_poll(struct cam_sim *sim)\n"
+"{\n"
+" xxx_intr(cam_sim_unit(sim)); /* for ISA device */\n"
+"}\n"
+msgstr ""
+"static void\n"
+"xxx_poll(struct cam_sim *sim)\n"
+"{\n"
+" xxx_intr(cam_sim_unit(sim)); /* for ISA device */\n"
+"}\n"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1051
+#, no-wrap
+msgid "Asynchronous Events"
+msgstr "Асинхронные события"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1054
+msgid ""
+"If an asynchronous event callback has been set up then the callback function "
+"should be defined."
+msgstr ""
+"Если была настроена асинхронная callback-функция для события, то callback-"
+"функция должна быть определена."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1059
+#, no-wrap
+msgid ""
+"static void\n"
+"ahc_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)\n"
+msgstr ""
+"static void\n"
+"ahc_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1062
+msgid "callback_arg - the value supplied when registering the callback"
+msgstr "callback_arg - значение, переданное при регистрации callback"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1063
+msgid "code - identifies the type of event"
+msgstr "code - определяет тип события"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1064
+msgid "path - identifies the devices to which the event applies"
+msgstr "path - определяет устройства, к которым применяется событие"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1065
+msgid "arg - event-specific argument"
+msgstr "arg - аргумент, специфичный для события"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1067
+msgid "Implementation for a single type of event, AC_LOST_DEVICE, looks like:"
+msgstr ""
+"Реализация для одного типа события, AC_LOST_DEVICE, выглядит следующим "
+"образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1074
+#, no-wrap
+msgid ""
+" struct xxx_softc *softc;\n"
+" struct cam_sim *sim;\n"
+" int targ;\n"
+" struct ccb_trans_settings neg;\n"
+msgstr ""
+" struct xxx_softc *softc;\n"
+" struct cam_sim *sim;\n"
+" int targ;\n"
+" struct ccb_trans_settings neg;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1093
+#, no-wrap
+msgid ""
+" sim = (struct cam_sim *)callback_arg;\n"
+" softc = (struct xxx_softc *)cam_sim_softc(sim);\n"
+" switch (code) {\n"
+" case AC_LOST_DEVICE:\n"
+" targ = xpt_path_target_id(path);\n"
+" if (targ <= OUR_MAX_SUPPORTED_TARGET) {\n"
+" clean_negotiations(softc, targ);\n"
+" /* send indication to CAM */\n"
+" neg.bus_width = 8;\n"
+" neg.sync_period = neg.sync_offset = 0;\n"
+" neg.valid = (CCB_TRANS_BUS_WIDTH_VALID\n"
+" | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);\n"
+" xpt_async(AC_TRANSFER_NEG, path, &neg);\n"
+" }\n"
+" break;\n"
+" default:\n"
+" break;\n"
+" }\n"
+msgstr ""
+" sim = (struct cam_sim *)callback_arg;\n"
+" softc = (struct xxx_softc *)cam_sim_softc(sim);\n"
+" switch (code) {\n"
+" case AC_LOST_DEVICE:\n"
+" targ = xpt_path_target_id(path);\n"
+" if (targ <= OUR_MAX_SUPPORTED_TARGET) {\n"
+" clean_negotiations(softc, targ);\n"
+" /* send indication to CAM */\n"
+" neg.bus_width = 8;\n"
+" neg.sync_period = neg.sync_offset = 0;\n"
+" neg.valid = (CCB_TRANS_BUS_WIDTH_VALID\n"
+" | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);\n"
+" xpt_async(AC_TRANSFER_NEG, path, &neg);\n"
+" }\n"
+" break;\n"
+" default:\n"
+" break;\n"
+" }\n"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1096
+#, no-wrap
+msgid "Interrupts"
+msgstr "Прерывания"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1099
+msgid ""
+"The exact type of the interrupt routine depends on the type of the "
+"peripheral bus (PCI, ISA and so on) to which the SCSI controller is "
+"connected."
+msgstr ""
+"Точный тип процедуры прерывания зависит от типа периферийной шины (PCI, ISA "
+"и так далее), к которой подключен SCSI-контроллер."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1106
+msgid ""
+"The interrupt routines of the SIM drivers run at the interrupt level "
+"splcam. So `splcam()` should be used in the driver to synchronize activity "
+"between the interrupt routine and the rest of the driver (for a "
+"multiprocessor-aware driver things get yet more interesting but we ignore "
+"this case here). The pseudo-code in this document happily ignores the "
+"problems of synchronization. The real code must not ignore them. A simple-"
+"minded approach is to set `splcam()` on the entry to the other routines and "
+"reset it on return thus protecting them by one big critical section. To "
+"make sure that the interrupt level will be always restored a wrapper "
+"function can be defined, like:"
+msgstr ""
+"Прерывания в драйверах SIM выполняются на уровне прерывания splcam. Поэтому "
+"в драйвере следует использовать `splcam()` для синхронизации между "
+"обработчиком прерывания и остальной частью драйвера (для драйверов, "
+"учитывающих многопроцессорность, ситуация становится ещё сложнее, но здесь "
+"мы этот случай не рассматриваем). Псевдокод в этом документе беззаботно "
+"игнорирует проблемы синхронизации. Реальный код так делать не должен. "
+"Простейший подход — установить `splcam()` при входе в другие функции и "
+"сбросить при выходе, защищая их одной большой критической секцией. Чтобы "
+"гарантировать восстановление уровня прерывания, можно определить обёрточную "
+"функцию, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1117
+#, no-wrap
+msgid ""
+" static void\n"
+" xxx_action(struct cam_sim *sim, union ccb *ccb)\n"
+" {\n"
+" int s;\n"
+" s = splcam();\n"
+" xxx_action1(sim, ccb);\n"
+" splx(s);\n"
+" }\n"
+msgstr ""
+" static void\n"
+" xxx_action(struct cam_sim *sim, union ccb *ccb)\n"
+" {\n"
+" int s;\n"
+" s = splcam();\n"
+" xxx_action1(sim, ccb);\n"
+" splx(s);\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1123
+#, no-wrap
+msgid ""
+" static void\n"
+" xxx_action1(struct cam_sim *sim, union ccb *ccb)\n"
+" {\n"
+" ... process the request ...\n"
+" }\n"
+msgstr ""
+" static void\n"
+" xxx_action1(struct cam_sim *sim, union ccb *ccb)\n"
+" {\n"
+" ... process the request ...\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1127
+msgid ""
+"This approach is simple and robust but the problem with it is that "
+"interrupts may get blocked for a relatively long time and this would "
+"negatively affect the system's performance. On the other hand the functions "
+"of the `spl()` family have rather high overhead, so vast amount of tiny "
+"critical sections may not be good either."
+msgstr ""
+"Этот подход прост и надежен, но проблема в том, что прерывания могут "
+"блокироваться на относительно долгое время, что негативно скажется на "
+"производительности системы. С другой стороны, функции семейства `spl()` "
+"имеют довольно высокие накладные расходы, поэтому большое количество мелких "
+"критических секций также может быть нежелательным."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1130
+msgid ""
+"The conditions handled by the interrupt routine and the details depend very "
+"much on the hardware. We consider the set of \"typical\" conditions."
+msgstr ""
+"Условия, обрабатываемые процедурой прерывания, и детали сильно зависят от "
+"оборудования. Мы рассматриваем набор \"типичных\" условий."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1135
+msgid ""
+"First, we check if a SCSI reset was encountered on the bus (probably caused "
+"by another SCSI controller on the same SCSI bus). If so we drop all the "
+"enqueued and disconnected requests, report the events and re-initialize our "
+"SCSI controller. It is important that during this initialization the "
+"controller will not issue another reset or else two controllers on the same "
+"SCSI bus could ping-pong resets forever. The case of fatal controller error/"
+"hang could be handled in the same place, but it will probably need also "
+"sending RESET signal to the SCSI bus to reset the status of the connections "
+"with the SCSI devices."
+msgstr ""
+"Сначала проверяем, было ли на шине событие SCSI сброса (вероятно, вызванное "
+"другим SCSI-контроллером на той же SCSI-шине). Если это так, мы отменяем все "
+"поставленные в очередь и отключенные запросы, сообщаем о событиях и повторно "
+"инициализируем наш SCSI-контроллер. Важно, чтобы во время этой инициализации "
+"контроллер не инициировал ещё один сброс, иначе два контроллера на одной "
+"SCSI-шине могут бесконечно обмениваться сбросами. Случай фатальной ошибки/"
+"зависания контроллера может быть обработан в том же месте, но, вероятно, "
+"также потребуется отправка сигнала RESET на SCSI-шину для сброса состояния "
+"соединений с SCSI-устройствами."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1141
+#, no-wrap
+msgid ""
+" int fatal=0;\n"
+" struct ccb_trans_settings neg;\n"
+" struct cam_path *path;\n"
+msgstr ""
+" int fatal=0;\n"
+" struct ccb_trans_settings neg;\n"
+" struct cam_path *path;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1146
+#, no-wrap
+msgid ""
+" if (detected_scsi_reset(softc)\n"
+" || (fatal = detected_fatal_controller_error(softc))) {\n"
+" int targ, lun;\n"
+" struct xxx_hcb *h, *hh;\n"
+msgstr ""
+" if (detected_scsi_reset(softc)\n"
+" || (fatal = detected_fatal_controller_error(softc))) {\n"
+" int targ, lun;\n"
+" struct xxx_hcb *h, *hh;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1152
+#, no-wrap
+msgid ""
+" /* drop all enqueued CCBs */\n"
+" for(h = softc->first_queued_hcb; h != NULL; h = hh) {\n"
+" hh = h->next;\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);\n"
+" }\n"
+msgstr ""
+" /* drop all enqueued CCBs */\n"
+" for(h = softc->first_queued_hcb; h != NULL; h = hh) {\n"
+" hh = h->next;\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1158
+#, no-wrap
+msgid ""
+" /* the clean values of negotiations to report */\n"
+" neg.bus_width = 8;\n"
+" neg.sync_period = neg.sync_offset = 0;\n"
+" neg.valid = (CCB_TRANS_BUS_WIDTH_VALID\n"
+" | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);\n"
+msgstr ""
+" /* the clean values of negotiations to report */\n"
+" neg.bus_width = 8;\n"
+" neg.sync_period = neg.sync_offset = 0;\n"
+" neg.valid = (CCB_TRANS_BUS_WIDTH_VALID\n"
+" | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1162
+#, no-wrap
+msgid ""
+" /* drop all disconnected CCBs and clean negotiations */\n"
+" for (targ=0; targ <= OUR_MAX_SUPPORTED_TARGET; targ++) {\n"
+" clean_negotiations(softc, targ);\n"
+msgstr ""
+" /* drop all disconnected CCBs and clean negotiations */\n"
+" for (targ=0; targ <= OUR_MAX_SUPPORTED_TARGET; targ++) {\n"
+" clean_negotiations(softc, targ);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1170
+#, no-wrap
+msgid ""
+" /* report the event if possible */\n"
+" if (xpt_create_path(&path, /*periph*/NULL,\n"
+" cam_sim_path(sim), targ,\n"
+" CAM_LUN_WILDCARD) == CAM_REQ_CMP) {\n"
+" xpt_async(AC_TRANSFER_NEG, path, &neg);\n"
+" xpt_free_path(path);\n"
+" }\n"
+msgstr ""
+" /* report the event if possible */\n"
+" if (xpt_create_path(&path, /*periph*/NULL,\n"
+" cam_sim_path(sim), targ,\n"
+" CAM_LUN_WILDCARD) == CAM_REQ_CMP) {\n"
+" xpt_async(AC_TRANSFER_NEG, path, &neg);\n"
+" xpt_free_path(path);\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1180
+#, no-wrap
+msgid ""
+" for (lun=0; lun <= OUR_MAX_SUPPORTED_LUN; lun++)\n"
+" for (h = softc->first_discon_hcb[targ][lun]; h != NULL; h = hh) {\n"
+" hh=h->next;\n"
+" if (fatal)\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_UNREC_HBA_ERROR);\n"
+" else\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);\n"
+" }\n"
+" }\n"
+msgstr ""
+" for (lun=0; lun <= OUR_MAX_SUPPORTED_LUN; lun++)\n"
+" for (h = softc->first_discon_hcb[targ][lun]; h != NULL; h = hh) {\n"
+" hh=h->next;\n"
+" if (fatal)\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_UNREC_HBA_ERROR);\n"
+" else\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);\n"
+" }\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1183
+#, no-wrap
+msgid ""
+" /* report the event */\n"
+" xpt_async(AC_BUS_RESET, softc->wpath, NULL);\n"
+msgstr ""
+" /* report the event */\n"
+" xpt_async(AC_BUS_RESET, softc->wpath, NULL);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1197
+#, no-wrap
+msgid ""
+" /* re-initialization may take a lot of time, in such case\n"
+" * its completion should be signaled by another interrupt or\n"
+" * checked on timeout - but for simplicity we assume here that\n"
+" * it is really fast\n"
+" */\n"
+" if (!fatal) {\n"
+" reinitialize_controller_without_scsi_reset(softc);\n"
+" } else {\n"
+" reinitialize_controller_with_scsi_reset(softc);\n"
+" }\n"
+" schedule_next_hcb(softc);\n"
+" return;\n"
+" }\n"
+msgstr ""
+" /* re-initialization may take a lot of time, in such case\n"
+" * its completion should be signaled by another interrupt or\n"
+" * checked on timeout - but for simplicity we assume here that\n"
+" * it is really fast\n"
+" */\n"
+" if (!fatal) {\n"
+" reinitialize_controller_without_scsi_reset(softc);\n"
+" } else {\n"
+" reinitialize_controller_with_scsi_reset(softc);\n"
+" }\n"
+" schedule_next_hcb(softc);\n"
+" return;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1202
+msgid ""
+"If interrupt is not caused by a controller-wide condition then probably "
+"something has happened to the current hardware control block. Depending on "
+"the hardware there may be other non-HCB-related events, we just do not "
+"consider them here. Then we analyze what happened to this HCB:"
+msgstr ""
+"Если прерывание не вызвано условием, общим для всего контроллера, то, "
+"вероятно, что-то произошло с текущим блоком управления аппаратным "
+"обеспечением. В зависимости от оборудования могут быть и другие события, не "
+"связанные с HCB, но мы их здесь не рассматриваем. Затем мы анализируем, что "
+"произошло с этим HCB:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1210
+#, no-wrap
+msgid ""
+" struct xxx_hcb *hcb, *h, *hh;\n"
+" int hcb_status, scsi_status;\n"
+" int ccb_status;\n"
+" int targ;\n"
+" int lun_to_freeze;\n"
+msgstr ""
+" struct xxx_hcb *hcb, *h, *hh;\n"
+" int hcb_status, scsi_status;\n"
+" int ccb_status;\n"
+" int targ;\n"
+" int lun_to_freeze;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1219
+#, no-wrap
+msgid ""
+" hcb = get_current_hcb(softc);\n"
+" if (hcb == NULL) {\n"
+" /* either stray interrupt or something went very wrong\n"
+" * or this is something hardware-dependent\n"
+" */\n"
+" handle as necessary;\n"
+" return;\n"
+" }\n"
+msgstr ""
+" hcb = get_current_hcb(softc);\n"
+" if (hcb == NULL) {\n"
+" /* either stray interrupt or something went very wrong\n"
+" * or this is something hardware-dependent\n"
+" */\n"
+" handle as necessary;\n"
+" return;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1222
+#, no-wrap
+msgid ""
+" targ = hcb->target;\n"
+" hcb_status = get_status_of_current_hcb(softc);\n"
+msgstr ""
+" targ = hcb->target;\n"
+" hcb_status = get_status_of_current_hcb(softc);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1225
+msgid ""
+"First we check if the HCB has completed and if so we check the returned SCSI "
+"status."
+msgstr ""
+"Сначала мы проверяем, завершился ли HCB, и если да, то проверяем "
+"возвращённый статус SCSI."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1230
+#, no-wrap
+msgid ""
+" if (hcb_status == COMPLETED) {\n"
+" scsi_status = get_completion_status(hcb);\n"
+msgstr ""
+" if (hcb_status == COMPLETED) {\n"
+" scsi_status = get_completion_status(hcb);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1233
+msgid ""
+"Then look if this status is related to the REQUEST SENSE command and if so "
+"handle it in a simple way."
+msgstr ""
+"Затем проверьте, связан ли этот статус с командой REQUEST SENSE, и если да, "
+"обработайте его простым способом."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1247
+#, no-wrap
+msgid ""
+" if (hcb->flags & DOING_AUTOSENSE) {\n"
+" if (scsi_status == GOOD) { /* autosense was successful */\n"
+" hcb->ccb->ccb_h.status |= CAM_AUTOSNS_VALID;\n"
+" free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_SCSI_STATUS_ERROR);\n"
+" } else {\n"
+" autosense_failed:\n"
+" free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_AUTOSENSE_FAIL);\n"
+" }\n"
+" schedule_next_hcb(softc);\n"
+" return;\n"
+" }\n"
+msgstr ""
+" if (hcb->flags & DOING_AUTOSENSE) {\n"
+" if (scsi_status == GOOD) { /* autosense was successful */\n"
+" hcb->ccb->ccb_h.status |= CAM_AUTOSNS_VALID;\n"
+" free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_SCSI_STATUS_ERROR);\n"
+" } else {\n"
+" autosense_failed:\n"
+" free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_AUTOSENSE_FAIL);\n"
+" }\n"
+" schedule_next_hcb(softc);\n"
+" return;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1251
+msgid ""
+"Else the command itself has completed, pay more attention to details. If "
+"auto-sense is not disabled for this CCB and the command has failed with "
+"sense data then run REQUEST SENSE command to receive that data."
+msgstr ""
+"Иначе сама команда завершена, уделяйте больше внимания деталям. Если "
+"автоопределение не отключено для этого CCB и команда завершилась неудачно с "
+"данными состояния, выполните команду REQUEST SENSE для получения этих данных."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1256
+#, no-wrap
+msgid ""
+" hcb->ccb->csio.scsi_status = scsi_status;\n"
+" calculate_residue(hcb);\n"
+msgstr ""
+" hcb->ccb->csio.scsi_status = scsi_status;\n"
+" calculate_residue(hcb);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1273
+#, no-wrap
+msgid ""
+" if ((hcb->ccb->ccb_h.flags & CAM_DIS_AUTOSENSE)==0\n"
+" && (scsi_status == CHECK_CONDITION\n"
+" || scsi_status == COMMAND_TERMINATED)) {\n"
+" /* start auto-SENSE */\n"
+" hcb->flags |= DOING_AUTOSENSE;\n"
+" setup_autosense_command_in_hcb(hcb);\n"
+" restart_current_hcb(softc);\n"
+" return;\n"
+" }\n"
+" if (scsi_status == GOOD)\n"
+" free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_REQ_CMP);\n"
+" else\n"
+" free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_SCSI_STATUS_ERROR);\n"
+" schedule_next_hcb(softc);\n"
+" return;\n"
+" }\n"
+msgstr ""
+" if ((hcb->ccb->ccb_h.flags & CAM_DIS_AUTOSENSE)==0\n"
+" && (scsi_status == CHECK_CONDITION\n"
+" || scsi_status == COMMAND_TERMINATED)) {\n"
+" /* start auto-SENSE */\n"
+" hcb->flags |= DOING_AUTOSENSE;\n"
+" setup_autosense_command_in_hcb(hcb);\n"
+" restart_current_hcb(softc);\n"
+" return;\n"
+" }\n"
+" if (scsi_status == GOOD)\n"
+" free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_REQ_CMP);\n"
+" else\n"
+" free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_SCSI_STATUS_ERROR);\n"
+" schedule_next_hcb(softc);\n"
+" return;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1276
+msgid ""
+"One typical thing would be negotiation events: negotiation messages received "
+"from a SCSI target (in answer to our negotiation attempt or by target's "
+"initiative) or the target is unable to negotiate (rejects our negotiation "
+"messages or does not answer them)."
+msgstr ""
+"Типичным примером могут быть события согласования: сообщения согласования, "
+"полученные от цели SCSI (в ответ на нашу попытку согласования или по "
+"инициативе цели), или если цель не может согласовать (отклоняет наши "
+"сообщения согласования или не отвечает на них)."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1292
+#, no-wrap
+msgid ""
+" switch (hcb_status) {\n"
+" case TARGET_REJECTED_WIDE_NEG:\n"
+" /* revert to 8-bit bus */\n"
+" softc->current_bus_width[targ] = softc->goal_bus_width[targ] = 8;\n"
+" /* report the event */\n"
+" neg.bus_width = 8;\n"
+" neg.valid = CCB_TRANS_BUS_WIDTH_VALID;\n"
+" xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &neg);\n"
+" continue_current_hcb(softc);\n"
+" return;\n"
+" case TARGET_ANSWERED_WIDE_NEG:\n"
+" {\n"
+" int wd;\n"
+msgstr ""
+" switch (hcb_status) {\n"
+" case TARGET_REJECTED_WIDE_NEG:\n"
+" /* revert to 8-bit bus */\n"
+" softc->current_bus_width[targ] = softc->goal_bus_width[targ] = 8;\n"
+" /* report the event */\n"
+" neg.bus_width = 8;\n"
+" neg.valid = CCB_TRANS_BUS_WIDTH_VALID;\n"
+" xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &neg);\n"
+" continue_current_hcb(softc);\n"
+" return;\n"
+" case TARGET_ANSWERED_WIDE_NEG:\n"
+" {\n"
+" int wd;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1298
+#, no-wrap
+msgid ""
+" wd = get_target_bus_width_request(softc);\n"
+" if (wd <= softc->goal_bus_width[targ]) {\n"
+" /* answer is acceptable */\n"
+" softc->current_bus_width[targ] =\n"
+" softc->goal_bus_width[targ] = neg.bus_width = wd;\n"
+msgstr ""
+" wd = get_target_bus_width_request(softc);\n"
+" if (wd <= softc->goal_bus_width[targ]) {\n"
+" /* answer is acceptable */\n"
+" softc->current_bus_width[targ] =\n"
+" softc->goal_bus_width[targ] = neg.bus_width = wd;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1311
+#, no-wrap
+msgid ""
+" /* report the event */\n"
+" neg.valid = CCB_TRANS_BUS_WIDTH_VALID;\n"
+" xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &neg);\n"
+" } else {\n"
+" prepare_reject_message(hcb);\n"
+" }\n"
+" }\n"
+" continue_current_hcb(softc);\n"
+" return;\n"
+" case TARGET_REQUESTED_WIDE_NEG:\n"
+" {\n"
+" int wd;\n"
+msgstr ""
+" /* report the event */\n"
+" neg.valid = CCB_TRANS_BUS_WIDTH_VALID;\n"
+" xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &neg);\n"
+" } else {\n"
+" prepare_reject_message(hcb);\n"
+" }\n"
+" }\n"
+" continue_current_hcb(softc);\n"
+" return;\n"
+" case TARGET_REQUESTED_WIDE_NEG:\n"
+" {\n"
+" int wd;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1315
+#, no-wrap
+msgid ""
+" wd = get_target_bus_width_request(softc);\n"
+" wd = min (wd, OUR_BUS_WIDTH);\n"
+" wd = min (wd, softc->user_bus_width[targ]);\n"
+msgstr ""
+" wd = get_target_bus_width_request(softc);\n"
+" wd = min (wd, OUR_BUS_WIDTH);\n"
+" wd = min (wd, softc->user_bus_width[targ]);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1320
+#, no-wrap
+msgid ""
+" if (wd != softc->current_bus_width[targ]) {\n"
+" /* the bus width has changed */\n"
+" softc->current_bus_width[targ] =\n"
+" softc->goal_bus_width[targ] = neg.bus_width = wd;\n"
+msgstr ""
+" if (wd != softc->current_bus_width[targ]) {\n"
+" /* the bus width has changed */\n"
+" softc->current_bus_width[targ] =\n"
+" softc->goal_bus_width[targ] = neg.bus_width = wd;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1330
+#, no-wrap
+msgid ""
+" /* report the event */\n"
+" neg.valid = CCB_TRANS_BUS_WIDTH_VALID;\n"
+" xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &neg);\n"
+" }\n"
+" prepare_width_nego_rsponse(hcb, wd);\n"
+" }\n"
+" continue_current_hcb(softc);\n"
+" return;\n"
+" }\n"
+msgstr ""
+" /* report the event */\n"
+" neg.valid = CCB_TRANS_BUS_WIDTH_VALID;\n"
+" xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &neg);\n"
+" }\n"
+" prepare_width_nego_rsponse(hcb, wd);\n"
+" }\n"
+" continue_current_hcb(softc);\n"
+" return;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1334
+msgid ""
+"Then we handle any errors that could have happened during auto-sense in the "
+"same simple-minded way as before. Otherwise we look closer at the details "
+"again."
+msgstr ""
+"Затем мы обрабатываем любые ошибки, которые могли произойти во время "
+"автоопределения, тем же простым способом, что и раньше. В противном случае "
+"мы снова внимательно изучаем детали."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1339
+#, no-wrap
+msgid ""
+" if (hcb->flags & DOING_AUTOSENSE)\n"
+" goto autosense_failed;\n"
+msgstr ""
+" if (hcb->flags & DOING_AUTOSENSE)\n"
+" goto autosense_failed;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1341
+#, no-wrap
+msgid " switch (hcb_status) {\n"
+msgstr " switch (hcb_status) {\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1345
+msgid ""
+"The next event we consider is unexpected disconnect. Which is considered "
+"normal after an ABORT or BUS DEVICE RESET message and abnormal in other "
+"cases."
+msgstr ""
+"Следующее событие, которое мы рассматриваем, — это неожиданное отключение. "
+"Оно считается нормальным после сообщения ABORT или BUS DEVICE RESET и "
+"аномальным в остальных случаях."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1361
+#, no-wrap
+msgid ""
+" case UNEXPECTED_DISCONNECT:\n"
+" if (requested_abort(hcb)) {\n"
+" /* abort affects all commands on that target+LUN, so\n"
+" * mark all disconnected HCBs on that target+LUN as aborted too\n"
+" */\n"
+" for (h = softc->first_discon_hcb[hcb->target][hcb->lun];\n"
+" h != NULL; h = hh) {\n"
+" hh=h->next;\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_REQ_ABORTED);\n"
+" }\n"
+" ccb_status = CAM_REQ_ABORTED;\n"
+" } else if (requested_bus_device_reset(hcb)) {\n"
+" int lun;\n"
+msgstr ""
+" case UNEXPECTED_DISCONNECT:\n"
+" if (requested_abort(hcb)) {\n"
+" /* abort affects all commands on that target+LUN, so\n"
+" * mark all disconnected HCBs on that target+LUN as aborted too\n"
+" */\n"
+" for (h = softc->first_discon_hcb[hcb->target][hcb->lun];\n"
+" h != NULL; h = hh) {\n"
+" hh=h->next;\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_REQ_ABORTED);\n"
+" }\n"
+" ccb_status = CAM_REQ_ABORTED;\n"
+" } else if (requested_bus_device_reset(hcb)) {\n"
+" int lun;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1365
+#, no-wrap
+msgid ""
+" /* reset affects all commands on that target, so\n"
+" * mark all disconnected HCBs on that target+LUN as reset\n"
+" */\n"
+msgstr ""
+" /* reset affects all commands on that target, so\n"
+" * mark all disconnected HCBs on that target+LUN as reset\n"
+" */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1372
+#, no-wrap
+msgid ""
+" for (lun=0; lun <= OUR_MAX_SUPPORTED_LUN; lun++)\n"
+" for (h = softc->first_discon_hcb[hcb->target][lun];\n"
+" h != NULL; h = hh) {\n"
+" hh=h->next;\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);\n"
+" }\n"
+msgstr ""
+" for (lun=0; lun <= OUR_MAX_SUPPORTED_LUN; lun++)\n"
+" for (h = softc->first_discon_hcb[hcb->target][lun];\n"
+" h != NULL; h = hh) {\n"
+" hh=h->next;\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1375
+#, no-wrap
+msgid ""
+" /* send event */\n"
+" xpt_async(AC_SENT_BDR, hcb->ccb->ccb_h.path_id, NULL);\n"
+msgstr ""
+" /* send event */\n"
+" xpt_async(AC_SENT_BDR, hcb->ccb->ccb_h.path_id, NULL);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1386
+#, no-wrap
+msgid ""
+" /* this was the CAM_RESET_DEV request itself, it is completed */\n"
+" ccb_status = CAM_REQ_CMP;\n"
+" } else {\n"
+" calculate_residue(hcb);\n"
+" ccb_status = CAM_UNEXP_BUSFREE;\n"
+" /* request the further code to freeze the queue */\n"
+" hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;\n"
+" lun_to_freeze = hcb->lun;\n"
+" }\n"
+" break;\n"
+msgstr ""
+" /* this was the CAM_RESET_DEV request itself, it is completed */\n"
+" ccb_status = CAM_REQ_CMP;\n"
+" } else {\n"
+" calculate_residue(hcb);\n"
+" ccb_status = CAM_UNEXP_BUSFREE;\n"
+" /* request the further code to freeze the queue */\n"
+" hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;\n"
+" lun_to_freeze = hcb->lun;\n"
+" }\n"
+" break;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1389
+msgid ""
+"If the target refuses to accept tags we notify CAM about that and return "
+"back all commands for this LUN:"
+msgstr ""
+"Если цель отказывается принимать теги, мы уведомляем CAM об этом и "
+"возвращаем все команды для этого LUN:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1397
+#, no-wrap
+msgid ""
+" case TAGS_REJECTED:\n"
+" /* report the event */\n"
+" neg.flags = 0 & ~CCB_TRANS_TAG_ENB;\n"
+" neg.valid = CCB_TRANS_TQ_VALID;\n"
+" xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &neg);\n"
+msgstr ""
+" case TAGS_REJECTED:\n"
+" /* report the event */\n"
+" neg.flags = 0 & ~CCB_TRANS_TAG_ENB;\n"
+" neg.valid = CCB_TRANS_TQ_VALID;\n"
+" xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &neg);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1403
+#, no-wrap
+msgid ""
+" ccb_status = CAM_MSG_REJECT_REC;\n"
+" /* request the further code to freeze the queue */\n"
+" hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;\n"
+" lun_to_freeze = hcb->lun;\n"
+" break;\n"
+msgstr ""
+" ccb_status = CAM_MSG_REJECT_REC;\n"
+" /* request the further code to freeze the queue */\n"
+" hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;\n"
+" lun_to_freeze = hcb->lun;\n"
+" break;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1406
+msgid ""
+"Then we check a number of other conditions, with processing basically "
+"limited to setting the CCB status:"
+msgstr ""
+"Затем мы проверяем ряд других условий, при этом обработка в основном "
+"ограничивается установкой статуса CCB:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1430
+#, no-wrap
+msgid ""
+" case SELECTION_TIMEOUT:\n"
+" ccb_status = CAM_SEL_TIMEOUT;\n"
+" /* request the further code to freeze the queue */\n"
+" hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;\n"
+" lun_to_freeze = CAM_LUN_WILDCARD;\n"
+" break;\n"
+" case PARITY_ERROR:\n"
+" ccb_status = CAM_UNCOR_PARITY;\n"
+" break;\n"
+" case DATA_OVERRUN:\n"
+" case ODD_WIDE_TRANSFER:\n"
+" ccb_status = CAM_DATA_RUN_ERR;\n"
+" break;\n"
+" default:\n"
+" /* all other errors are handled in a generic way */\n"
+" ccb_status = CAM_REQ_CMP_ERR;\n"
+" /* request the further code to freeze the queue */\n"
+" hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;\n"
+" lun_to_freeze = CAM_LUN_WILDCARD;\n"
+" break;\n"
+" }\n"
+msgstr ""
+" case SELECTION_TIMEOUT:\n"
+" ccb_status = CAM_SEL_TIMEOUT;\n"
+" /* request the further code to freeze the queue */\n"
+" hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;\n"
+" lun_to_freeze = CAM_LUN_WILDCARD;\n"
+" break;\n"
+" case PARITY_ERROR:\n"
+" ccb_status = CAM_UNCOR_PARITY;\n"
+" break;\n"
+" case DATA_OVERRUN:\n"
+" case ODD_WIDE_TRANSFER:\n"
+" ccb_status = CAM_DATA_RUN_ERR;\n"
+" break;\n"
+" default:\n"
+" /* all other errors are handled in a generic way */\n"
+" ccb_status = CAM_REQ_CMP_ERR;\n"
+" /* request the further code to freeze the queue */\n"
+" hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;\n"
+" lun_to_freeze = CAM_LUN_WILDCARD;\n"
+" break;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1433
+msgid ""
+"Then we check if the error was serious enough to freeze the input queue "
+"until it gets proceeded and do so if it is:"
+msgstr ""
+"Затем мы проверяем, была ли ошибка достаточно серьёзной, чтобы заморозить "
+"очередь ввода до её обработки, и если да, то делаем это:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1439
+#, no-wrap
+msgid ""
+" if (hcb->ccb->ccb_h.status & CAM_DEV_QFRZN) {\n"
+" /* freeze the queue */\n"
+" xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);\n"
+msgstr ""
+" if (hcb->ccb->ccb_h.status & CAM_DEV_QFRZN) {\n"
+" /* freeze the queue */\n"
+" xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1441
+#, no-wrap
+msgid " /* re-queue all commands for this target/LUN back to CAM */\n"
+msgstr " /* re-queue all commands for this target/LUN back to CAM */\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1444
+#, no-wrap
+msgid ""
+" for (h = softc->first_queued_hcb; h != NULL; h = hh) {\n"
+" hh = h->next;\n"
+msgstr ""
+" for (h = softc->first_queued_hcb; h != NULL; h = hh) {\n"
+" hh = h->next;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1453
+#, no-wrap
+msgid ""
+" if (targ == h->targ\n"
+" && (lun_to_freeze == CAM_LUN_WILDCARD || lun_to_freeze == h->lun))\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_REQUEUE_REQ);\n"
+" }\n"
+" }\n"
+" free_hcb_and_ccb_done(hcb, hcb->ccb, ccb_status);\n"
+" schedule_next_hcb(softc);\n"
+" return;\n"
+msgstr ""
+" if (targ == h->targ\n"
+" && (lun_to_freeze == CAM_LUN_WILDCARD || lun_to_freeze == h->lun))\n"
+" free_hcb_and_ccb_done(h, h->ccb, CAM_REQUEUE_REQ);\n"
+" }\n"
+" }\n"
+" free_hcb_and_ccb_done(hcb, hcb->ccb, ccb_status);\n"
+" schedule_next_hcb(softc);\n"
+" return;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1456
+msgid ""
+"This concludes the generic interrupt handling although specific controllers "
+"may require some additions."
+msgstr ""
+"На этом общее описание обработки прерываний завершается, хотя для некоторых "
+"контроллеров могут потребоваться дополнительные действия."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1458
+#, no-wrap
+msgid "Errors Summary"
+msgstr "Ошибки (Сводка)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1464
+msgid ""
+"When executing an I/O request many things may go wrong. The reason of error "
+"can be reported in the CCB status with great detail. Examples of use are "
+"spread throughout this document. For completeness here is the summary of "
+"recommended responses for the typical error conditions:"
+msgstr ""
+"При выполнении запроса ввода-вывода может произойти множество ошибок. "
+"Причина ошибки может быть указана в статусе CCB с большим количеством "
+"деталей. Примеры использования разбросаны по всему документу. Для полноты "
+"изложения приведём сводку рекомендуемых действий при типичных ошибках:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1467
+msgid ""
+"_CAM_RESRC_UNAVAIL_ - some resource is temporarily unavailable and the SIM "
+"driver cannot generate an event when it will become available. An example "
+"of this resource would be some intra-controller hardware resource for which "
+"the controller does not generate an interrupt when it becomes available."
+msgstr ""
+"_CAM_RESRC_UNAVAIL_ — некоторый ресурс временно недоступен, и драйвер SIM не "
+"может сгенерировать событие, когда он станет доступен. Примером такого "
+"ресурса может быть некоторый внутренний аппаратный ресурс контроллера, для "
+"которого контроллер не генерирует прерывание при его доступности."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1468
+msgid "_CAM_UNCOR_PARITY_ - unrecovered parity error occurred"
+msgstr "_CAM_UNCOR_PARITY_ - произошла неисправимая ошибка четности"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1469
+msgid ""
+"_CAM_DATA_RUN_ERR_ - data overrun or unexpected data phase (going in other "
+"direction than specified in CAM_DIR_MASK) or odd transfer length for wide "
+"transfer"
+msgstr ""
+"_CAM_DATA_RUN_ERR_ - переполнение данных или неожиданная фаза данных "
+"(направление передачи не соответствует указанному в CAM_DIR_MASK) или "
+"нечётная длина передачи для широкой передачи"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1470
+msgid ""
+"_CAM_SEL_TIMEOUT_ - selection timeout occurred (target does not respond)"
+msgstr "_CAM_SEL_TIMEOUT_ - произошел таймаут выбора (цель не отвечает)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1471
+msgid "_CAM_CMD_TIMEOUT_ - command timeout occurred (the timeout function ran)"
+msgstr ""
+"_CAM_CMD_TIMEOUT_ - произошло превышение времени ожидания команды (сработала "
+"функция таймаута)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1472
+msgid "_CAM_SCSI_STATUS_ERROR_ - the device returned error"
+msgstr "_CAM_SCSI_STATUS_ERROR_ - устройство вернуло ошибку"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1473
+msgid ""
+"_CAM_AUTOSENSE_FAIL_ - the device returned error and the REQUEST SENSE "
+"COMMAND failed"
+msgstr ""
+"_CAM_AUTOSENSE_FAIL_ - устройство вернуло ошибку и команда REQUEST SENSE "
+"завершилась неудачно"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1474
+msgid "_CAM_MSG_REJECT_REC_ - MESSAGE REJECT message was received"
+msgstr "_CAM_MSG_REJECT_REC_ - получено сообщение MESSAGE REJECT"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1475
+msgid "_CAM_SCSI_BUS_RESET_ - received SCSI bus reset"
+msgstr "_CAM_SCSI_BUS_RESET_ - получен сброс шины SCSI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1476
+msgid ""
+"_CAM_REQ_CMP_ERR_ - \"impossible\" SCSI phase occurred or something else as "
+"weird or just a generic error if further detail is not available"
+msgstr ""
+"_CAM_REQ_CMP_ERR_ - произошла «невозможная» фаза SCSI или что-то столь же "
+"странное, либо это просто общая ошибка, если дополнительная информация "
+"недоступна"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1477
+msgid "_CAM_UNEXP_BUSFREE_ - unexpected disconnect occurred"
+msgstr "_CAM_UNEXP_BUSFREE_ - произошло неожиданное отключение"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1478
+msgid "_CAM_BDR_SENT_ - BUS DEVICE RESET message was sent to the target"
+msgstr ""
+"_CAM_BDR_SENT_ - Сообщение BUS DEVICE RESET было отправлено целевому "
+"устройству"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1479
+msgid "_CAM_UNREC_HBA_ERROR_ - unrecoverable Host Bus Adapter Error"
+msgstr "_CAM_UNREC_HBA_ERROR_ - невосстановимая ошибка адаптера шины хоста"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1480
+msgid "_CAM_REQ_TOO_BIG_ - the request was too large for this controller"
+msgstr "_CAM_REQ_TOO_BIG_ - запрос слишком велик для данного контроллера"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1484
+msgid ""
+"_CAM_REQUEUE_REQ_ - this request should be re-queued to preserve transaction "
+"ordering. This typically occurs when the SIM recognizes an error that "
+"should freeze the queue and must place other queued requests for the target "
+"at the sim level back into the XPT queue. Typical cases of such errors are "
+"selection timeouts, command timeouts and other like conditions. In such "
+"cases the troublesome command returns the status indicating the error, the "
+"and the other commands which have not be sent to the bus yet get re-queued."
+msgstr ""
+"_CAM_REQUEUE_REQ_ - этот запрос должен быть повторно поставлен в очередь для "
+"сохранения порядка транзакций. Обычно это происходит, когда SIM обнаруживает "
+"ошибку, которая должна заморозить очередь, и необходимо поместить другие "
+"запросы в очереди для цели на уровне SIM обратно в очередь XPT. Типичными "
+"случаями таких ошибок являются тайм-ауты выбора, тайм-ауты команд и другие "
+"подобные условия. В таких случаях проблемная команда возвращает статус, "
+"указывающий на ошибку, а другие команды, которые ещё не были отправлены на "
+"шину, повторно ставятся в очередь."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1485
+msgid ""
+"_CAM_LUN_INVALID_ - the LUN ID in the request is not supported by the SCSI "
+"controller"
+msgstr ""
+"_CAM_LUN_INVALID_ - идентификатор LUN в запросе не поддерживается "
+"контроллером SCSI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1486
+msgid ""
+"_CAM_TID_INVALID_ - the target ID in the request is not supported by the "
+"SCSI controller"
+msgstr ""
+"_CAM_TID_INVALID_ - идентификатор целевого устройства в запросе не "
+"поддерживается контроллером SCSI"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1488
+#, no-wrap
+msgid "Timeout Handling"
+msgstr "Обработка таймаутов"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1496
+msgid ""
+"When the timeout for an HCB expires that request should be aborted, just "
+"like with an XPT_ABORT request. The only difference is that the returned "
+"status of aborted request should be CAM_CMD_TIMEOUT instead of "
+"CAM_REQ_ABORTED (that is why implementation of the abort better be done as a "
+"function). But there is one more possible problem: what if the abort "
+"request itself will get stuck? In this case the SCSI bus should be reset, "
+"just like with an XPT_RESET_BUS request (and the idea about implementing it "
+"as a function called from both places applies here too). Also we should "
+"reset the whole SCSI bus if a device reset request got stuck. So after all "
+"the timeout function would look like:"
+msgstr ""
+"Когда время ожидания для HCB истекает, этот запрос должен быть прерван, как "
+"и в случае с запросом XPT_ABORT. Единственное отличие заключается в том, что "
+"возвращаемый статус прерванного запроса должен быть CAM_CMD_TIMEOUT вместо "
+"CAM_REQ_ABORTED (вот почему реализацию прерывания лучше сделать в виде "
+"функции). Но есть ещё одна возможная проблема: что если сам запрос на "
+"прерывание зависнет? В этом случае шина SCSI должна быть сброшена, как и при "
+"запросе XPT_RESET_BUS (и идея о реализации этого в виде функции, вызываемой "
+"из обоих мест, применима и здесь). Также мы должны сбросить всю шину SCSI, "
+"если запрос на сброс устройства завис. В итоге функция обработки таймаута "
+"будет выглядеть следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1505
+#, no-wrap
+msgid ""
+"static void\n"
+"xxx_timeout(void *arg)\n"
+"{\n"
+" struct xxx_hcb *hcb = (struct xxx_hcb *)arg;\n"
+" struct xxx_softc *softc;\n"
+" struct ccb_hdr *ccb_h;\n"
+msgstr ""
+"static void\n"
+"xxx_timeout(void *arg)\n"
+"{\n"
+" struct xxx_hcb *hcb = (struct xxx_hcb *)arg;\n"
+" struct xxx_softc *softc;\n"
+" struct ccb_hdr *ccb_h;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1508
+#, no-wrap
+msgid ""
+" softc = hcb->softc;\n"
+" ccb_h = &hcb->ccb->ccb_h;\n"
+msgstr ""
+" softc = hcb->softc;\n"
+" ccb_h = &hcb->ccb->ccb_h;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1515
+#, no-wrap
+msgid ""
+" if (hcb->flags & HCB_BEING_ABORTED || ccb_h->func_code == XPT_RESET_DEV) {\n"
+" xxx_reset_bus(softc);\n"
+" } else {\n"
+" xxx_abort_ccb(hcb->ccb, CAM_CMD_TIMEOUT);\n"
+" }\n"
+"}\n"
+msgstr ""
+" if (hcb->flags & HCB_BEING_ABORTED || ccb_h->func_code == XPT_RESET_DEV) {\n"
+" xxx_reset_bus(softc);\n"
+" } else {\n"
+" xxx_abort_ccb(hcb->ccb, CAM_CMD_TIMEOUT);\n"
+" }\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/scsi/_index.adoc:1520
+msgid ""
+"When we abort a request all the other disconnected requests to the same "
+"target/LUN get aborted too. So there appears a question, should we return "
+"them with status CAM_REQ_ABORTED or CAM_CMD_TIMEOUT? The current drivers use "
+"CAM_CMD_TIMEOUT. This seems logical because if one request got timed out "
+"then probably something really bad is happening to the device, so if they "
+"would not be disturbed they would time out by themselves."
+msgstr ""
+"Когда мы прерываем запрос, все остальные отключенные запросы к тому же "
+"целевому устройству/LUN также прерываются. Возникает вопрос: следует ли "
+"возвращать их со статусом CAM_REQ_ABORTED или CAM_CMD_TIMEOUT? Текущие "
+"драйверы используют CAM_CMD_TIMEOUT. Это кажется логичным, потому что если "
+"один запрос превысил время ожидания, то, вероятно, с устройством происходит "
+"что-то действительно плохое, и если их не трогать, они бы сами превысили "
+"время ожидания."
diff --git a/documentation/content/ru/books/arch-handbook/smp/_index.adoc b/documentation/content/ru/books/arch-handbook/smp/_index.adoc
new file mode 100644
index 0000000000..8c4b741566
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/smp/_index.adoc
@@ -0,0 +1,360 @@
+---
+description: 'Документ по архитектуре SMPng'
+next: books/arch-handbook/partii
+params:
+ path: /books/arch-handbook/smp/
+prev: books/arch-handbook/vm
+showBookMenu: true
+tags: ["SMPng", "introduction", "locks"]
+title: 'Глава 8. Документ по архитектуре SMPng'
+weight: 9
+---
+
+[[smp]]
+= Документ по архитектуре SMPng
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 8
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[smp-intro]]
+== Введение
+
+В этом документе представлены текущая архитектура и реализация SMPng. Сначала вводятся основные примитивы и инструменты. Затем излагается общая архитектура модели синхронизации и выполнения ядра FreeBSD. Далее обсуждаются стратегии блокировок для конкретных подсистем, описывающие подходы к внедрению детализированной синхронизации и параллелизма для каждой подсистемы. В заключение приводятся подробные заметки по реализации, объясняющие выбор проектных решений и информирующие читателя о важных последствиях использования конкретных примитивов.
+
+Этот документ находится в стадии разработки и будет обновляться в соответствии с текущими проектированием и реализацией, связанными с проектом SMPng. Многие разделы в настоящее время существуют только в виде набросков, но будут дополняться по мере продвижения работы. Обновления или предложения по документу могут быть направлены редакторам документа.
+
+Цель SMPng — обеспечить параллелизм в ядре. Ядро представляет собой одну довольно большую и сложную программу. Чтобы сделать ядро многопоточным, мы используем те же инструменты, что и для многопоточности других программ. К ним относятся мьютексы, разделяемые/монопольные блокировки, семафоры и условные переменные. Для определений этих и других терминов, связанных с SMP, см. раздел crossref:smp[smp-glossary, Глоссарий] в этой статье.
+
+[[smp-lock-fundamentals]]
+== Основные инструменты и основы блокировки
+
+=== Атомарные инструкции и барьеры памяти
+
+Можно найти много описаний барьеров памяти и атомарных инструкций, поэтому в этом разделе не будет много деталей. Проще говоря, нельзя читать переменные без блокировки, если блокировка используется для защиты записи в эту переменную. Это становится очевидным, если учесть, что барьеры памяти лишь определяют относительный порядок операций с памятью; они не дают никаких гарантий относительно времени выполнения этих операций. То есть, барьер памяти не принуждает к сбросу содержимого локального кэша или буфера записи процессора. Вместо этого, барьер памяти при освобождении блокировки просто гарантирует, что все записи в защищённые данные будут видны другим процессорам или устройствам, если видна запись, освобождающая блокировку. Процессор может хранить эти данные в своём кэше или буфере записи сколько угодно долго. Однако, если другой процессор выполняет атомарную инструкцию над тем же данным, первый процессор должен гарантировать, что обновлённое значение будет видно второму процессору, наряду с любыми другими операциями, которые могут потребоваться согласно барьерам памяти.
+
+Например, предполагая простую модель, в которой данные считаются видимыми, когда они находятся в основной памяти (или в глобальном кэше), когда начинается выполнение атомарной инструкции на одном процессоре, буферы записи и кэши других процессоров должны выполнить все записи в ту же строку кэша вместе с любыми ожидающими операциями за барьером памяти.
+
+Это требует особой осторожности при использовании элемента, защищённого атомарными инструкциями. Например, в реализации мьютекса сна мы должны использовать `atomic_cmpset` вместо `atomic_set` для установки бита `MTX_CONTESTED`. Причина в том, что мы считываем значение `mtx_lock` в переменную и затем принимаем решение на основе этого чтения. Однако значение, которое мы ранее прочитали, может быть устаревшим или измениться, пока мы принимаем решение. Таким образом, когда выполняется `atomic_set`, это может привести к установке бита на другом значении, отличном от того, на котором мы основывали своё решение. Поэтому мы должны использовать `atomic_cmpset`, чтобы установить значение только в том случае, если значение, на котором мы приняли решение, актуально и действительно.
+
+Наконец, атомарные инструкции позволяют обновить или прочитать только один элемент. Если необходимо атомарно обновить несколько элементов, вместо этого следует использовать блокировку. Например, если требуется прочитать два счётчика и получить их значения, согласованные друг с другом, то эти счётчики должны быть защищены блокировкой, а не отдельными атомарными инструкциями.
+
+=== Блокировки на чтение и блокировки на запись
+
+Блокировки на чтение не требуют такой же строгости, как блокировки на запись. Оба типа блокировок должны гарантировать, что данные, к которым они обращаются, не устарели. Однако запись требует монопольного доступа. Несколько потоков могут безопасно читать значение. Использование разных типов блокировок для чтения и записи может быть реализовано несколькими способами.
+
+Во-первых, блокировки sx могут использоваться таким образом: монопольная блокировка при записи и разделяемая блокировка при чтении. Этот метод достаточно прост.
+
+Второй метод несколько менее очевиден. Вы можете защитить данные несколькими блокировками. Для чтения данных достаточно получить блокировку на чтение одной из блокировок. Однако для записи данных необходимо получить блокировку на запись всех блокировок. Это может сделать запись довольно затратной, но может быть полезно, когда данные доступны различными способами. Например, указатель на родительский процесс защищён как `proctree_lock` sx-блокировкой, так и мьютексом процесса. Иногда блокировка процесса удобнее, так как мы просто проверяем, кто является родителем уже заблокированного процесса. Однако в других случаях, таких как `inferior`, необходимо обходить дерево процессов через указатели на родителя, и блокировка каждого процесса была бы слишком затратной, а также сложной для гарантии того, что проверяемое условие остаётся верным как во время проверки, так и при выполнении действий, основанных на этой проверке.
+
+=== Условия и результаты блокировки
+
+Если вам нужна блокировка для проверки состояния переменной, чтобы можно было выполнить действие на основе прочитанного состояния, вы не можете просто удерживать блокировку во время чтения переменной, а затем снять блокировку перед выполнением действия на основе прочитанного значения. Как только вы снимаете блокировку, переменная может измениться, что сделает ваше решение недействительным. Таким образом, вы должны удерживать блокировку как во время чтения переменной, так и во время выполнения действия в результате проверки.
+
+[[smp-design]]
+== Общая Архитектура и Дизайн
+
+=== Обработка прерываний
+
+Следуя примеру нескольких других многопоточных ядер UNIX(R), FreeBSD реализрвала обработчики прерываний, предоставив им собственный контекст потока. Предоставление контекста для обработчиков прерываний позволяет им блокироваться на блокировках. Однако, чтобы избежать задержек, потоки обработки прерываний выполняются с приоритетом реального времени в ядре. Таким образом, обработчики прерываний не должны выполняться слишком долго, чтобы не лишать ресурсов другие потоки ядра. Кроме того, поскольку несколько обработчиков могут использовать один поток прерываний, обработчики прерываний не должны переходить в режим сна или использовать блокировки, допускающие сон, чтобы не лишать ресурсов другие обработчики прерываний.
+
+Текущие потоки обработки прерываний в FreeBSD называются тяжеловесными потоками обработки прерываний. Они получили такое название, потому что переключение на поток обработки прерывания включает в себя полное переключение контекста. В первоначальной реализации ядро не было вытесняющим, поэтому прерывания, которые прерывали поток ядра, должны были ждать, пока поток ядра не заблокируется или не вернётся в пользовательское пространство, прежде чем у них появится возможность выполниться.
+
+Для решения проблем с задержками ядро FreeBSD стало вытесняющим. В настоящее время вытеснение потока ядра происходит только при освобождении мьютекса сна или при поступлении прерывания. Однако планируется сделать ядро FreeBSD полностью вытесняющим, как описано ниже.
+
+Не все обработчики прерываний выполняются в контексте потока. Вместо этого, некоторые обработчики выполняются непосредственно в основном контексте прерывания. Эти обработчики прерываний в настоящее время ошибочно называются "быстрыми" обработчиками прерываний, поскольку для их обозначения применяется флаг `INTR_FAST`, использовавшийся в более ранних версиях ядра. Единственные прерывания, которые в настоящее время используют такие обработчики прерываний, — это прерывания от часов и последовательных устройств ввода-вывода. Поскольку эти обработчики не имеют собственного контекста, они не могут захватывать блокирующие блокировки и, следовательно, могут использовать только спин-мьютексы.
+
+Наконец, существует одна дополнительная оптимизация, которую можно добавить в код MD, называемая легковесными переключениями контекста. Поскольку поток обработки прерывания выполняется в контексте ядра, он может заимствовать vmspace любого процесса. Таким образом, при легковесном переключении контекста переход к потоку обработки прерывания не меняет vmspace, а заимствует vmspace прерванного потока. Чтобы гарантировать, что vmspace прерванного потока не исчезнет во время работы, прерванному потоку запрещается выполнение до тех пор, пока поток обработки прерывания больше не использует его vmspace. Это может произойти, когда поток обработки прерывания либо блокируется, либо завершается. Если поток обработки прерывания блокируется, то при повторном запуске он будет использовать свой собственный контекст. Таким образом, он может освободить прерванный поток.
+
+Недостатки этой оптимизации заключаются в том, что они очень специфичны для конкретной машины и сложны, поэтому стоят усилий только в случае значительного улучшения производительности. На данный момент, вероятно, ещё рано делать выводы, и, фактически, это может даже ухудшить производительность, так как почти все обработчики прерываний будут немедленно блокироваться на Giant и потребуют исправления потока при блокировке. Кроме того, Майк Смит предложил альтернативный метод обработки прерываний, который работает следующим образом:
+
+. Каждый обработчик прерывания состоит из двух частей: предиката, который выполняется в основном контексте прерывания, и обработчика, который выполняется в контексте собственного потока.
+. Если у обработчика прерывания есть предикат, то при срабатывании прерывания этот предикат выполняется. Если предикат возвращает значение `true`, прерывание считается полностью обработанным, и ядро возвращается из прерывания. Если предикат возвращает `false` или предиката нет, то запланированный обработчик запускается.
+
+Встраивание легковесных переключений контекста в эту схему может оказаться довольно сложным. Поскольку мы, возможно, захотим перейти на эту схему в будущем, вероятно, лучше отложить работу над легковесными переключениями контекста до тех пор, пока мы не определимся с окончательной архитектурой обработки прерываний и не выясним, как легковесные переключения контекста могут (или не могут) в неё вписаться.
+
+=== Ядро с вытеснением и критические секции
+
+==== Ядро и вытеснение вкратце
+
+Вытеснение ядра довольно просто. Основная идея заключается в том, что процессор всегда должен выполнять наиболее приоритетную доступную работу. Ну, это в идеале, по крайней мере. Есть несколько случаев, когда затраты на достижение идеала не стоят совершенства.
+
+Реализация полной вытесняющей многозадачности в ядре очень проста: когда вы планируете выполнение потока, помещая его в очередь выполнения, вы проверяете, является ли его приоритет выше, чем у текущего выполняемого потока. Если да, вы инициируете переключение контекста на этот поток.
+
+Хотя блокировки могут защитить большинство данных в случае вытеснения, не все части ядра безопасны для вытеснения. Например, если поток, удерживающий спин-блокировку, будет вытеснен, а новый поток попытается захватить ту же спин-блокировку, новый поток может вращаться вечно, так как прерванный поток может никогда не получить шанс на выполнение. Кроме того, некоторый код, такой как код для назначения номера адресного пространства процессу во время `exec` на Alpha, не должен быть вытеснен, так как он поддерживает фактический код переключения контекста. Для таких участков кода вытеснение отключается с использованием критической секции.
+
+==== Критические Секции
+
+Ответственность API критической секции заключается в предотвращении переключения контекста внутри критической секции. В полностью вытесняющем ядре каждый вызов `setrunqueue` для потока, отличного от текущего, является точкой вытеснения. Одна из реализаций заключается в том, что `critical_enter` устанавливает флаг для каждого потока, который сбрасывается его парной функцией. Если `setrunqueue` вызывается, когда этот флаг установлен, вытеснение не происходит, независимо от приоритета нового потока относительно текущего. Однако, поскольку критические секции используются в спин-блокировках для предотвращения переключения контекста и может быть захвачено несколько спин-блокировок, API критической секции должен поддерживать вложенность. По этой причине текущая реализация использует счетчик вложенности вместо одиночного флага для каждого потока.
+
+Для минимизации задержек прерывания внутри критической секции откладываются, а не отбрасываются. Если поток, который в обычных условиях должен быть вытеснен, становится готовым к выполнению, пока текущий поток находится в критической секции, то устанавливается флаг для данного потока, указывающий на ожидающее прерывание. При выходе из самой внешней критической секции флаг проверяется. Если флаг установлен, текущий поток вытесняется, чтобы позволить выполниться потоку с более высоким приоритетом.
+
+Прерывания создают проблему для спин-мьютексов. Если обработчик низкоуровневого прерывания требует блокировки, он не должен прерывать любой код, которому нужна эта блокировка, чтобы избежать возможного повреждения структур данных. В настоящее время этот механизм реализован через API критических секций с помощью функций `cpu_critical_enter` и `cpu_critical_exit`. Сейчас этот API отключает и снова включает прерывания на всех текущих платформах FreeBSD. Такой подход может быть не идеально оптимальным, но он прост для понимания и надежен в реализации. Теоретически, этот второй API нужен только для спин-мьютексов, используемых в основном контексте прерываний. Однако, для упрощения кода, он используется для всех спин-мьютексов и даже для всех критических секций. Возможно, стоит отделить MD API от MI API и использовать его только совместно с MI API в реализации спин-мьютексов. Если будет принят такой подход, то MD API, вероятно, потребуется переименовать, чтобы показать, что это отдельный API.
+
+==== Компромиссы проектирования
+
+Как упоминалось ранее, были сделаны некоторые компромиссы, чтобы пожертвовать случаями, когда идеальная вытесняющая многозадачность не всегда обеспечивает наилучшую производительность.
+
+Первый компромисс заключается в том, что код вытеснения не учитывает другие процессоры. Предположим, у нас есть два процессора A и B, где приоритет потока A равен 4, а приоритет потока B равен 2. Если процессор B делает поток с приоритетом 1 готовым к выполнению, то теоретически мы хотим, чтобы процессор A переключился на новый поток, чтобы выполнялись два потока с наивысшим приоритетом. Однако стоимость определения, на какой процессор нужно применить вытеснение, а также фактическая сигнализация этому процессору через IPI вместе с необходимой синхронизацией были бы огромными. Таким образом, текущий код вместо этого заставит процессор B переключиться на поток с более высоким приоритетом. Заметим, что это всё равно улучшает состояние системы, так как процессор B выполняет поток с приоритетом 1, а не поток с приоритетом 2.
+
+Второй компромисс ограничивает немедленное вытеснение ядра только потоками ядра с реальным временем. В простом случае вытеснения, описанном выше, поток всегда вытесняется немедленно (или как только будет покинута критическая секция), если становится доступным поток с более высоким приоритетом. Однако многие потоки, выполняющиеся в ядре, работают в контексте ядра лишь короткое время перед тем, как либо заблокироваться, либо вернуться в пользовательское пространство. Таким образом, если ядро вытеснит эти потоки для выполнения другого потока ядра без реального времени, оно может переключиться с выполняемого потока как раз перед тем, как тот собирается завершиться или перейти в режим ожидания. Кэш процессора должен затем адаптироваться к новому потоку. Когда ядро возвращается к вытесненному потоку, оно должно восстановить все потерянные кэшированные данные. Кроме того, выполняются два дополнительных переключения контекста, которых можно было бы избежать, если бы ядро отложило вытеснение до момента, пока первый поток не заблокируется или не вернётся в пользовательское пространство. Таким образом, по умолчанию код вытеснения будет немедленно вытеснять поток только в том случае, если поток с более высоким приоритетом имеет приоритет реального времени.
+
+Включение полной вытесняющей многозадачности для всех потоков ядра полезно в качестве средства отладки, так как позволяет выявить больше состояний гонки. Это особенно полезно на однопроцессорных системах (UP), где многие гонки сложно воспроизвести другими способами. Таким образом, существует опция ядра `FULL_PREEMPTION` для включения вытеснения для всех потоков ядра, которая может использоваться для целей отладки.
+
+=== Миграция потоков
+
+Простыми словами, поток мигрирует, когда переходит с одного CPU на другой. В неперемещаемом ядре это может происходить только в определённых точках, например, при вызове `msleep` или возврате в пользовательское пространство. Однако в перемещаемом ядре прерывание может вызвать вытеснение и возможную миграцию в любой момент. Это может негативно сказаться на данных, специфичных для CPU, поскольку, за исключением `curthread` и `curpcb`, данные могут изменяться при любой миграции. Поскольку потенциально миграция может произойти в любой момент, это делает незащищённый доступ к данным, специфичным для CPU, практически бесполезным. Поэтому желательно иметь возможность отключать миграцию для участков кода, где требуется стабильность данных, специфичных для CPU.
+
+Критические секции в настоящее время предотвращают миграцию, поскольку они не допускают переключения контекстов. Однако это может быть слишком строгим требованием в некоторых случаях, так как критическая секция также эффективно блокирует потоки прерываний на текущем процессоре. В результате был предоставлен другой API, позволяющий текущему потоку указать, что если он будет вытеснен, он не должен мигрировать на другой CPU.
+
+Этот API известен как закрепление потока и предоставляется планировщиком. API состоит из двух функций: `sched_pin` и `sched_unpin`. Эти функции управляют счетчиком вложенности `td_pinned` для каждого потока. Поток считается закрепленным, когда его счетчик вложенности больше нуля, и прекрашает быть закрепленным с нулевым счетчиком вложенности. Каждая реализация планировщика должна гарантировать, что закрепленные потоки выполняются только на том CPU, на котором они выполнялись при первом вызове `sched_pin`. Поскольку счетчик вложенности изменяется только самим потоком и читается другими потоками только тогда, когда закрепленный поток не выполняется, но удерживается `sched_lock`, то `td_pinned` не требует блокировки. Функция `sched_pin` увеличивает счетчик вложенности, а `sched_unpin` уменьшает его. Обратите внимание, что эти функции работают только с текущим потоком и привязывают текущий поток к CPU, на котором он выполняется в данный момент. Для привязки произвольного потока к определенному CPU следует использовать функции `sched_bind` и `sched_unbind`.
+
+=== Обратные вызовы
+
+Функция ядра `timeout` позволяет службам ядра регистрировать функции для выполнения в рамках программного прерывания `softclock`. События планируются на основе заданного количества тактов часов, и вызовы предоставленной потребителем функции будут происходить приблизительно в нужное время.
+
+Глобальный список ожидающих событий с таймаутом защищен глобальной спин-блокировкой `callout_lock`; любой доступ к списку таймаутов должен выполняться с удержанием этой блокировки. Когда `softclock` пробуждается, он сканирует список ожидающих таймаутов на предмет тех, которые должны сработать. Чтобы избежать инверсии блокировок, поток `softclock` освобождает блокировку `callout_lock` при вызове предоставленной функции обратного вызова `timeout`. Если флаг `CALLOUT_MPSAFE` не был установлен во время регистрации, то `Giant` будет захвачен перед вызовом обратного вызова, а затем освобожден после него. Блокировка `callout_lock` будет повторно захвачена перед продолжением работы. Код `softclock` аккуратно поддерживает список в согласованном состоянии во время освобождения блокировки. Если включен `DIAGNOSTIC`, то измеряется время выполнения каждой функции, и если оно превышает пороговое значение, генерируется предупреждение.
+
+[[smp-lock-strategies]]
+== Конкретные стратегии блокировки
+
+=== Учетные данные
+
+`struct ucred` — это внутренняя структура учетных данных ядра, которая обычно используется в качестве основы для управления доступом на уровне процессов внутри ядра. Системы, производные от BSD, используют модель «копирования при записи» для учетных данных: могут существовать множественные ссылки на структуру учетных данных, и когда требуется внести изменение, структура дублируется, изменяется, а затем ссылка заменяется. Благодаря широко распространенному кэшированию учетных данных для реализации контроля доступа при открытии, это приводит к значительной экономии памяти. С переходом на детализированную SMP (симметричную многопроцессорность), эта модель также существенно экономит на операциях блокировки, требуя, чтобы модификации выполнялись только для неразделяемых учетных данных, избегая необходимости явной синхронизации при использовании известных разделяемых учетных данных.
+
+Структуры учетных данных с единственной ссылкой считаются изменяемыми; разделяемые структуры учетных данных не должны изменяться, иначе возникает риск состояния гонки. Мьютекс `cr_mtxp` защищает счетчик ссылок структуры `struct ucred` для поддержания согласованности. Любое использование структуры требует действительной ссылки на протяжении всего времени использования, иначе структура может быть освобождена из-под нелегитимного потребителя.
+
+Мьютекс `struct ucred` является листовым мьютексом и реализован через пул мьютексов по соображениям производительности.
+
+Обычно учетные данные используются в режиме только для чтения для принятия решений по контролю доступа, и в этом случае `td_ucred`, как правило, предпочтительнее, поскольку не требует блокировки. Когда учетные данные процесса обновляются, блокировка `proc` должна удерживаться на протяжении операций проверки и обновления, чтобы избежать состояний гонки. Учетные данные процесса `p_ucred` должны использоваться для операций проверки и обновления, чтобы предотвратить гонки между временем проверки и временем использования.
+
+Если при системных вызовах будет выполняться контроль доступа после обновления учетных данных процесса, значение `td_ucred` также должно быть обновлено до текущего значения процесса. Это предотвратит использование устаревших учетных данных после изменения. Ядро автоматически обновляет указатель `td_ucred` в структуре потока из `p_ucred` процесса всякий раз, когда процесс входит в ядро, что позволяет использовать свежие учетные данные для контроля доступа в ядре.
+
+=== Дескрипторы файлов и таблицы дескрипторов файлов
+
+Подробности будут позже.
+
+=== Структуры клеток
+
+`struct prison` хранит административные данные, связанные с обслуживанием клеток, созданных с использованием API man:jail[2]. Это включает имя хоста для каждой клетки, IP-адрес и связанные настройки. Эта структура имеет счетчик ссылок, так как указатели на её экземпляры разделяются многими структурами учётных данных. Один мьютекс, `pr_mtx`, защищает чтение и запись счётчика ссылок и всех изменяемых переменных внутри `struct jail`. Некоторые переменные устанавливаются только при создании клетки, и действительной ссылки на `struct prison` достаточно для чтения этих значений. Точная блокировка каждой записи документирована в комментариях файла [.filename]#sys/jail.h#.
+
+=== MAC Framework
+
+Фреймворк TrustedBSD MAC поддерживает данные в различных объектах ядра в виде `struct label`. Как правило, метки в объектах ядра защищаются тем же механизмом блокировки, что и остальная часть объекта ядра. Например, метка `v_label` в `struct vnode` защищается блокировкой vnode.
+
+В дополнение к меткам, поддерживаемым в стандартных объектах ядра, MAC Framework также поддерживает список зарегистрированных и активных политик. Список политик защищен глобальной мьютекс-блокировкой (`mac_policy_list_lock`) и счетчиком использования (также защищенным мьютексом). Поскольку множество проверок контроля доступа может выполняться параллельно, вход в framework для доступа только на чтение к списку политик требует удержания мьютекса во время увеличения (и последующего уменьшения) счетчика использования. Мьютекс не обязательно удерживать на протяжении всей операции входа в MAC — некоторые операции, такие как операции с метками на объектах файловой системы, выполняются длительное время. Для изменения списка политик, например во время регистрации и отмены регистрации политик, мьютекс должен быть удержан, а счетчик ссылок должен быть равен нулю, чтобы предотвратить изменение списка во время его использования.
+
+Условная переменная `mac_policy_list_not_busy` доступна для потоков, которым необходимо дождаться освобождения списка, но ожидание на этой условной переменной допустимо только если вызывающий поток не удерживает других блокировок, иначе может возникнуть нарушение порядка блокировок. Фактически, счетчик занятости действует как форма разделяемой/исключающей блокировки доступа к фреймворку: отличие в том, что, в отличие от sx-блокировки, потребители, ожидающие освобождения списка, могут подвергаться голоданию, вместо того чтобы допускать проблемы порядка блокировок в отношении счетчика занятости и других блокировок, которые могут удерживаться при входе в (или внутри) MAC Framework.
+
+=== Модули
+
+Для подсистемы модулей существует единая блокировка, которая используется для защиты общих данных. Эта блокировка является shared/exclusive (SX) и с высокой вероятностью потребует захвата (разделяемого или исключительного), поэтому были добавлены несколько макросов для упрощения работы с ней. Эти макросы можно найти в [.filename]#sys/module.h#, и их использование довольно простое. Основные структуры, защищаемые этой блокировкой, — это структуры `module_t` (при разделяемом доступе) и глобальная структура `modulelist_t` modules. Для более глубокого понимания стратегии блокировок рекомендуется изучить соответствующий исходный код в [.filename]#kern/kern_module.c#.
+
+=== Дерево устройств Newbus
+
+Система newbus будет использовать одну блокировку sx. Читатели будут удерживать разделяемую (read) блокировку (man:sx_slock[9]), а писатели — эксклюзивную (write) блокировку (man:sx_xlock[9]). Внутренние функции не будут выполнять блокировку вообще. Внешне видимые функции будут блокироваться по мере необходимости. Элементы, для которых не важно, выиграна гонка или проиграна, не будут блокироваться, так как они обычно читаются во многих местах (например, man:device_get_softc[9]). Изменения в структурах данных newbus будут относительно редкими, поэтому одной блокировки должно быть достаточно, и это не приведёт к снижению производительности.
+
+=== Каналы (pipe)
+
+...
+
+=== Процессы и потоки
+
+- иерархия процессов
+
+- блокировки и ссылки proc
+
+- потокоспецифичные копии записей proc для заморозки во время системных вызовов, включая td_ucred
+
+- межпроцессные операции
+
+- группы процессов и сеансы
+
+=== Планировщик
+
+Множество ссылок на `sched_lock` и примечания, указывающие на конкретные примитивы и связанные с ними особенности в других частях документа.
+
+=== Select и Poll
+
+Функции `select` и `poll` позволяют потокам блокироваться в ожидании событий на файловых дескрипторах — чаще всего, доступности файловых дескрипторов для чтения или записи.
+
+...
+
+=== SIGIO
+
+Служба SIGIO позволяет процессам запрашивать доставку сигнала SIGIO своей группе процессов при изменении статуса чтения/записи указанных файловых дескрипторов. Не более одного процесса или группы процессов может зарегистрироваться для получения SIGIO от любого заданного объекта ядра, и такой процесс или группа называется владельцем. Каждый объект, поддерживающий регистрацию SIGIO, содержит поле-указатель, которое имеет значение `NULL`, если объект не зарегистрирован, или указывает на структуру `struct sigio`, описывающую регистрацию. Это поле защищено глобальным мьютексом `sigio_lock`. Вызывающие функции обслуживания SIGIO должны передавать это поле «по ссылке», чтобы локальные копии регистра не создавались без защиты блокировкой.
+
+Один `struct sigio` выделяется для каждого зарегистрированного объекта, связанного с любым процессом или группой процессов, и содержит обратные ссылки на объект, владельца, информацию о сигнале, учетные данные и общее состояние регистрации. Каждый процесс или группа процессов содержит список зарегистрированных структур `struct sigio`: `p_sigiolst` для процессов и `pg_sigiolst` для групп процессов. Эти списки защищены блокировками процесса или группы процессов соответственно. Большинство полей в каждой `struct sigio` остаются постоянными на протяжении регистрации, за исключением поля `sio_pgsigio`, которое связывает `struct sigio` со списком процесса или группы процессов. Разработчикам, реализующим новые объекты ядра с поддержкой SIGIO, как правило, следует избегать удержания блокировок структур при вызове функций поддержки SIGIO, таких как `fsetown` или `funsetown`, чтобы не определять порядок блокировок между блокировками структур и глобальной блокировкой SIGIO. Обычно это возможно за счет использования повышенного счетчика ссылок на структуру, например, путем опоры на ссылку файлового дескриптора на канал во время операции с каналом.
+
+=== Sysctl
+
+Сервис `sysctl` MIB вызывается как из ядра, так и из пользовательских приложений с использованием системного вызова. По крайней мере, два вопроса возникают в отношении блокировок: во-первых, защита структур, поддерживающих пространство имен, и во-вторых, взаимодействие с переменными и функциями ядра, к которым обращается интерфейс `sysctl`. Поскольку `sysctl` позволяет прямое экспортирование (и изменение) статистики ядра и параметров конфигурации, механизм `sysctl` должен учитывать соответствующие семантики блокировок для этих переменных. В настоящее время `sysctl` использует единую глобальную sx-блокировку для сериализации использования `sysctl`; однако предполагается, что он работает под защитой Giant, и другие защиты не предоставляются. Оставшаяся часть этого раздела рассматривает возможные изменения в блокировках и семантике `sysctl`.
+
+- Необходимо изменить порядок операций для sysctl, которые обновляют значения из чтения старого, copyin и copyout, записи нового на copyin, блокировку, чтение старого и запись нового, разблокировку, copyout. Обычные sysctl, которые просто копируют старое значение и устанавливают новое, которое они копируют, могут по-прежнему следовать старой модели. Однако, возможно, будет чище использовать вторую модель для всех обработчиков sysctl, чтобы избежать операций блокировки.
+
+- Для упрощения распространённого случая, sysctl может включать указатель на мьютекс в макросах SYSCTL_FOO и в структуре. Это будет работать для большинства sysctl. Для значений, защищённых sx-блокировками, спин-мьютексами или другими стратегиями синхронизации, отличными от одиночного мьютекса сна, можно использовать узлы SYSCTL_PROC для обеспечения корректной блокировки.
+
+=== Очередь задач
+
+Интерфейс `taskqueue` имеет две основные блокировки, связанные с ним, для защиты соответствующих общих данных. Мьютекс `taskqueue_queues_mutex` предназначен для защиты TAILQ `taskqueue_queues`. Другая блокировка мьютекса, связанная с этой системой, находится в структуре данных `struct taskqueue`. Использование примитива синхронизации здесь необходимо для защиты целостности данных в `struct taskqueue`. Следует отметить, что нет отдельных макросов, помогающих пользователю заблокировать свою собственную работу, поскольку эти блокировки, скорее всего, не будут использоваться за пределами [.filename]#kern/subr_taskqueue.c#.
+
+[[smp-implementation-notes]]
+== Заметки о реализации
+
+=== Очереди сна
+
+Очередь сна — это структура, которая содержит список потоков, ожидающих на канале ожидания. Каждый поток, который не находится в состоянии ожидания на канале ожидания, хранит структуру очереди сна при себе. Когда поток блокируется на канале ожидания, он передаёт свою структуру очереди сна этому каналу. Очереди сна, связанные с каналом ожидания, хранятся в хеш-таблице.
+
+Хеш-таблица очередей сна содержит очереди сна для каналов ожидания, у которых есть хотя бы один заблокированный поток. Каждая запись в хеш-таблице называется цепочкой очереди сна. Цепочка содержит связанный список очередей сна и спин-мьютекс. Спин-мьютекс защищает список очередей сна, а также содержимое структур очередей сна в списке. С каждым каналом ожидания связана только одна очередь сна. Если несколько потоков блокируются на одном канале ожидания, то очереди сна, связанные со всеми потоками, кроме первого, хранятся в списке свободных очередей сна в главной очереди сна. Когда поток удаляется из очереди сна, он получает одну из структур очереди сна из свободного списка главной очереди, если он не является единственным потоком в очереди. Последний поток получает главную очередь сна при возобновлении. Поскольку потоки могут удаляться из очереди сна в порядке, отличном от порядка добавления, поток может покинуть очередь сна с другой структурой очереди сна, чем та, с которой он в неё попал.
+
+Функция `sleepq_lock` блокирует спин-мьютес цепи очереди сна, соответствующей определённому каналу ожидания. Функция `sleepq_lookup` выполняет поиск в хеш-таблице главной очереди сна, связанной с заданным каналом ожидания. Если главная очередь сна не найдена, функция возвращает `NULL`. Функция `sleepq_release` разблокирует спин-мьютес, связанный с заданным каналом ожидания.
+
+Поток добавляется в очередь ожидания с помощью `sleepq_add`. Эта функция принимает канал ожидания, указатель на мьютекс, защищающий канал ожидания, строку описания сообщения ожидания и маску флагов. Цепь очереди ожидания должна быть заблокирована с помощью `sleepq_lock` перед вызовом этой функции. Если канал ожидания не защищен мьютексом (или защищен мьютексом Giant), то аргумент указателя на мьютекс должен быть `NULL`. Аргумент флагов содержит поле типа, указывающее на вид очереди ожидания, в которую добавляется поток, и флаг, указывающий, является ли ожидание прерываемым (`SLEEPQ_INTERRUPTIBLE`). В настоящее время существует только два типа очередей ожидания: традиционные очереди, управляемые через функции `msleep` и `wakeup` (`SLEEPQ_MSLEEP`), и очереди ожидания условных переменных (`SLEEPQ_CONDVAR`). Тип очереди ожидания и аргумент указателя на блокировку используются исключительно для внутренних проверок утверждений. Код, вызывающий `sleepq_add`, должен явно разблокировать любой блокировочный механизм, защищающий канал ожидания, после того как связанная цепь очереди ожидания будет заблокирована через `sleepq_lock` и перед блокировкой в очереди ожидания с помощью одной из функций ожидания.
+
+Таймаут для сна устанавливается вызовом `sleepq_set_timeout`. Функция принимает канал ожидания и время таймаута в виде относительного количества тиков в качестве аргументов. Если сон должен быть прерван поступающими сигналами, следует также вызвать функцию `sleepq_catch_signals`. Эта функция принимает канал ожидания в качестве единственного параметра. Если для данного потока уже есть ожидающий сигнал, то `sleepq_catch_signals` вернёт номер сигнала; в противном случае она вернёт 0.
+
+После добавления потока в очередь ожидания он блокируется с использованием одной из функций `sleepq_wait`. Существует четыре функции ожидания в зависимости от того, хочет ли вызывающий код использовать таймаут, прерывание сна перехваченными сигналами или прерывание от планировщика потоков пользовательского пространства. Функция `sleepq_wait` просто ожидает, пока текущий поток не будет явно возобновлён одной из функций пробуждения. Функция `sleepq_timedwait` ожидает, пока поток не будет явно возобновлён или пока не истечёт таймаут, установленный предыдущим вызовом `sleepq_set_timeout`. Функция `sleepq_wait_sig` ожидает, пока поток не будет явно возобновлён или его сон не будет прерван. Функция `sleepq_timedwait_sig` ожидает, пока поток не будет явно возобновлён, не истечёт таймаут, установленный предыдущим вызовом `sleepq_set_timeout`, или сон потока не будет прерван. Все функции ожидания принимают канал ожидания в качестве первого параметра. Кроме того, функция `sleepq_timedwait_sig` принимает второй логический параметр, указывающий, обнаружил ли предыдущий вызов `sleepq_catch_signals` ожидающий сигнал.
+
+Если поток явно возобновлен или прерван сигналом, функция ожидания возвращает ноль, указывая на успешное завершение сна. Если поток возобновлен по таймауту или прерыванию от планировщика потоков в пользовательском пространстве, вместо этого возвращается соответствующее значение errno. Обратите внимание, что поскольку `sleepq_wait` может возвращать только 0, она ничего не возвращает, и вызывающая сторона должна считать сон успешным. Также, если сон потока прерывается одновременно по таймауту и другим причинам, `sleepq_timedwait_sig` вернет ошибку, указывающую на срабатывание таймаута. Если возвращено значение ошибки 0 и для блокировки использовались `sleepq_wait_sig` или `sleepq_timedwait_sig`, следует вызвать функцию `sleepq_calc_signal_retval` для проверки ожидающих сигналов и вычисления соответствующего возвращаемого значения, если таковые обнаружены. Номер сигнала, полученный при предыдущем вызове `sleepq_catch_signals`, должен быть передан в качестве единственного аргумента в `sleepq_calc_signal_retval`.
+
+Потоки, находящиеся в состоянии ожидания на канале ожидания, явно возобновляются функциями `sleepq_broadcast` и `sleepq_signal`. Обе функции принимают канал ожидания, с которого нужно возобновить потоки, приоритет, на который нужно поднять возобновлённые потоки, и аргумент флагов, указывающий тип очереди ожидания, которую нужно возобновить. Аргумент приоритета трактуется как минимальный приоритет. Если у возобновляемого потока уже есть более высокий приоритет (численно меньший), чем указанный в аргументе, его приоритет не изменяется. Аргумент флагов используется для внутренних проверок, чтобы гарантировать, что очереди ожидания не обрабатываются как неправильный тип. Например, функции условных переменных не должны возобновлять потоки на традиционной очереди ожидания. Функция `sleepq_broadcast` возобновляет все потоки, заблокированные на указанном канале ожидания, тогда как `sleepq_signal` возобновляет только поток с наивысшим приоритетом, заблокированный на канале ожидания. Перед вызовом этих функций цепочка очереди ожидания должна быть заблокирована с помощью функции `sleepq_lock`.
+
+Спящий поток может быть прерван с помощью вызова функции `sleepq_abort`. Эта функция должна вызываться с удержанием `sched_lock`, а поток должен находиться в очереди сна. Поток также может быть удалён из определённой очереди сна с помощью функции `sleepq_remove`. Эта функция принимает как поток, так и канал ожидания в качестве аргументов и пробуждает поток только в том случае, если он находится в очереди сна для указанного канала ожидания. Если поток не находится в очереди сна или находится в очереди сна для другого канала ожидания, эта функция ничего не делает.
+
+=== Турникеты
+
+- Сравнение/сопоставление с очередями сна.
+
+- Поиск/ожидание/освобождение. - Описать состояние гонки TDF_TSNOBLOCK.
+
+- Приоритетное распространение.
+
+=== Подробности реализации мьютекса
+
+- Должны ли мы требовать владения мьютексами для `mtx_destroy()`, так как иначе мы не можем безопасно утверждать, что они не принадлежат кому-либо ещё?
+
+==== Вращающиеся мьютексы
+
+- Использование критической секции...
+
+==== Мьютексы сна (Sleep Mutexes)
+
+- Опишите гонки с оспариваемыми мьютексами
+
+- Почему безопасно читать mtx_lock оспариваемой мьютекса при удержании блокировки цепочки турникета.
+
+=== Witness
+
+- Что это делает
+
+- Как это работает
+
+[[smp-misc]]
+== Разные темы
+
+=== Источники прерываний и абстракции ICU
+
+- struct isrc
+
+- pic драйверы
+
+=== Другие случайные вопросы/темы
+
+- Передавать ли блокировку в `sema_wait`?
+
+- Должны ли мы иметь sx блокировки без возможности сна?
+
+- Добавить некоторую информацию о правильном использовании счетчиков ссылок.
+
+:sectnums!:
+
+[glossary]
+[[smp-glossary]]
+== Глоссарий
+
+[.glosslist]
+atomic (атомарный)::
+Операция является атомарной, если все её эффекты видны другим процессорам одновременно при соблюдении соответствующего протокола доступа. В простейшем случае атомарные инструкции предоставляются непосредственно архитектурой процессора. На более высоком уровне, если несколько элементов структуры защищены блокировкой, то набор операций является атомарным, если все они выполняются при удержании блокировки без её освобождения между любыми из операций.
++
+См. также operation (операция).
+
+block (блокировать)::
+Поток блокируется, когда он ожидает блокировку, ресурс или условие. К сожалению, этот термин в результате немного перегружен.
++
+См. также sleep (спать).
+
+critical section (критическая секция)::
+Фрагмент кода, который не может быть вытеснен. Критическая секция начинается и завершается с использованием API man:critical_enter[9].
+
+MD::
+Машинозависимый (machine dependent).
++
+Смотри также MI.
+
+memory operation (операция с памятью)::
+Операция с памятью выполняет чтение и/или запись в ячейку памяти.
+
+MI::
+Машинно-независимый (machine independent).
++
+См. также MD.
+
+operation (операция)::
+См. memory operation (операция с памятью).
+
+primary interrupt context (основной контекст прерывания)::
+Основной контекст прерывания относится к коду, который выполняется при возникновении прерывания. Этот код может либо напрямую запускать обработчик прерывания, либо планировать выполнение асинхронного потока прерывания для обработчиков прерываний данного источника.
+
+realtime kernel thread (поток ядра реального времени)::
+Высокоприоритетный поток ядра. В настоящее время единственными потоками ядра с реальным приоритетом являются потоки обработки прерываний.
++
+См. также thread (поток).
+
+sleep (спать)::
+Поток находится в состоянии сна, когда он заблокирован на условной переменной или в очереди сна через `msleep` или `tsleep`.
++
+См. также block (блокировать).
+
+sleepable lock (блокировка с возможностью сна)::
+блокировка с возможностью сна — это блокировка, которая может удерживаться потоком, находящимся в состоянии сна. В настоящее время в FreeBSD единственными блокировками с возможностью сна являются lockmgr и sx. В будущем некоторые sx-блокировки, такие как allproc и proctree, могут стать блокировками с возможностью сна.
++
+См. также sleep (спать).
+
+thread (поток)::
+Поток ядра, представленный структурой `struct thread`. Потоки владеют блокировками и содержат единственный на поток контекст выполнения.
+
+wait channel (канал ожидания)::
+Виртуальный адрес ядра, на котором потоки могут переходить в режим ожидания.
+
+:sectnums:
diff --git a/documentation/content/ru/books/arch-handbook/smp/_index.po b/documentation/content/ru/books/arch-handbook/smp/_index.po
new file mode 100644
index 0000000000..8175525d49
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/smp/_index.po
@@ -0,0 +1,1983 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-08-26 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbooksmp_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:14
+#, no-wrap
+msgid "SMPng Design Document"
+msgstr "Документ по архитектуре SMPng"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:1
+#, no-wrap
+msgid "Chapter 8. SMPng Design Document"
+msgstr "Глава 8. Документ по архитектуре SMPng"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:52
+#, no-wrap
+msgid "Introduction"
+msgstr "Введение"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:55
+msgid ""
+"This document presents the current design and implementation of the SMPng "
+"Architecture. First, the basic primitives and tools are introduced. Next, a "
+"general architecture for the FreeBSD kernel's synchronization and execution "
+"model is laid out. Then, locking strategies for specific subsystems are "
+"discussed, documenting the approaches taken to introduce fine-grained "
+"synchronization and parallelism for each subsystem. Finally, detailed "
+"implementation notes are provided to motivate design choices, and make the "
+"reader aware of important implications involving the use of specific "
+"primitives."
+msgstr ""
+"В этом документе представлены текущая архитектура и реализация SMPng. "
+"Сначала вводятся основные примитивы и инструменты. Затем излагается общая "
+"архитектура модели синхронизации и выполнения ядра FreeBSD. Далее "
+"обсуждаются стратегии блокировок для конкретных подсистем, описывающие "
+"подходы к внедрению детализированной синхронизации и параллелизма для каждой "
+"подсистемы. В заключение приводятся подробные заметки по реализации, "
+"объясняющие выбор проектных решений и информирующие читателя о важных "
+"последствиях использования конкретных примитивов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:57
+msgid ""
+"This document is a work-in-progress, and will be updated to reflect on-going "
+"design and implementation activities associated with the SMPng Project. Many "
+"sections currently exist only in outline form, but will be fleshed out as "
+"work proceeds. Updates or suggestions regarding the document may be directed "
+"to the document editors."
+msgstr ""
+"Этот документ находится в стадии разработки и будет обновляться в "
+"соответствии с текущими проектированием и реализацией, связанными с проектом "
+"SMPng. Многие разделы в настоящее время существуют только в виде набросков, "
+"но будут дополняться по мере продвижения работы. Обновления или предложения "
+"по документу могут быть направлены редакторам документа."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:63
+msgid ""
+"The goal of SMPng is to allow concurrency in the kernel. The kernel is "
+"basically one rather large and complex program. To make the kernel multi-"
+"threaded we use some of the same tools used to make other programs multi-"
+"threaded. These include mutexes, shared/exclusive locks, semaphores, and "
+"condition variables. For the definitions of these and other SMP-related "
+"terms, please see the crossref:smp[smp-glossary, Glossary] section of this "
+"article."
+msgstr ""
+"Цель SMPng — обеспечить параллелизм в ядре. Ядро представляет собой одну "
+"довольно большую и сложную программу. Чтобы сделать ядро многопоточным, мы "
+"используем те же инструменты, что и для многопоточности других программ. К "
+"ним относятся мьютексы, разделяемые/монопольные блокировки, семафоры и "
+"условные переменные. Для определений этих и других терминов, связанных с "
+"SMP, см. раздел crossref:smp[smp-glossary, Глоссарий] в этой статье."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:65
+#, no-wrap
+msgid "Basic Tools and Locking Fundamentals"
+msgstr "Основные инструменты и основы блокировки"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:67
+#, no-wrap
+msgid "Atomic Instructions and Memory Barriers"
+msgstr "Атомарные инструкции и барьеры памяти"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:70
+msgid ""
+"There are several existing treatments of memory barriers and atomic "
+"instructions, so this section will not include a lot of detail. To put it "
+"simply, one can not go around reading variables without a lock if a lock is "
+"used to protect writes to that variable. This becomes obvious when you "
+"consider that memory barriers simply determine relative order of memory "
+"operations; they do not make any guarantee about timing of memory "
+"operations. That is, a memory barrier does not force the contents of a CPU's "
+"local cache or store buffer to flush. Instead, the memory barrier at lock "
+"release simply ensures that all writes to the protected data will be visible "
+"to other CPU's or devices if the write to release the lock is visible. The "
+"CPU is free to keep that data in its cache or store buffer as long as it "
+"wants. However, if another CPU performs an atomic instruction on the same "
+"datum, the first CPU must guarantee that the updated value is made visible "
+"to the second CPU along with any other operations that memory barriers may "
+"require."
+msgstr ""
+"Можно найти много описаний барьеров памяти и атомарных инструкций, поэтому в "
+"этом разделе не будет много деталей. Проще говоря, нельзя читать переменные "
+"без блокировки, если блокировка используется для защиты записи в эту "
+"переменную. Это становится очевидным, если учесть, что барьеры памяти лишь "
+"определяют относительный порядок операций с памятью; они не дают никаких "
+"гарантий относительно времени выполнения этих операций. То есть, барьер "
+"памяти не принуждает к сбросу содержимого локального кэша или буфера записи "
+"процессора. Вместо этого, барьер памяти при освобождении блокировки просто "
+"гарантирует, что все записи в защищённые данные будут видны другим "
+"процессорам или устройствам, если видна запись, освобождающая блокировку. "
+"Процессор может хранить эти данные в своём кэше или буфере записи сколько "
+"угодно долго. Однако, если другой процессор выполняет атомарную инструкцию "
+"над тем же данным, первый процессор должен гарантировать, что обновлённое "
+"значение будет видно второму процессору, наряду с любыми другими операциями, "
+"которые могут потребоваться согласно барьерам памяти."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:72
+msgid ""
+"For example, assuming a simple model where data is considered visible when "
+"it is in main memory (or a global cache), when an atomic instruction is "
+"triggered on one CPU, other CPU's store buffers and caches must flush any "
+"writes to that same cache line along with any pending operations behind a "
+"memory barrier."
+msgstr ""
+"Например, предполагая простую модель, в которой данные считаются видимыми, "
+"когда они находятся в основной памяти (или в глобальном кэше), когда "
+"начинается выполнение атомарной инструкции на одном процессоре, буферы "
+"записи и кэши других процессоров должны выполнить все записи в ту же строку "
+"кэша вместе с любыми ожидающими операциями за барьером памяти."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:74
+msgid ""
+"This requires one to take special care when using an item protected by "
+"atomic instructions. For example, in the sleep mutex implementation, we have "
+"to use an `atomic_cmpset` rather than an `atomic_set` to turn on the "
+"`MTX_CONTESTED` bit. The reason is that we read the value of `mtx_lock` into "
+"a variable and then make a decision based on that read. However, the value "
+"we read may be stale, or it may change while we are making our decision. "
+"Thus, when the `atomic_set` executed, it may end up setting the bit on "
+"another value than the one we made the decision on. Thus, we have to use an "
+"`atomic_cmpset` to set the value only if the value we made the decision on "
+"is up-to-date and valid."
+msgstr ""
+"Это требует особой осторожности при использовании элемента, защищённого "
+"атомарными инструкциями. Например, в реализации мьютекса сна мы должны "
+"использовать `atomic_cmpset` вместо `atomic_set` для установки бита "
+"`MTX_CONTESTED`. Причина в том, что мы считываем значение `mtx_lock` в "
+"переменную и затем принимаем решение на основе этого чтения. Однако "
+"значение, которое мы ранее прочитали, может быть устаревшим или измениться, "
+"пока мы принимаем решение. Таким образом, когда выполняется `atomic_set`, "
+"это может привести к установке бита на другом значении, отличном от того, на "
+"котором мы основывали своё решение. Поэтому мы должны использовать "
+"`atomic_cmpset`, чтобы установить значение только в том случае, если "
+"значение, на котором мы приняли решение, актуально и действительно."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:76
+msgid ""
+"Finally, atomic instructions only allow one item to be updated or read. If "
+"one needs to atomically update several items, then a lock must be used "
+"instead. For example, if two counters must be read and have values that are "
+"consistent relative to each other, then those counters must be protected by "
+"a lock rather than by separate atomic instructions."
+msgstr ""
+"Наконец, атомарные инструкции позволяют обновить или прочитать только один "
+"элемент. Если необходимо атомарно обновить несколько элементов, вместо этого "
+"следует использовать блокировку. Например, если требуется прочитать два "
+"счётчика и получить их значения, согласованные друг с другом, то эти "
+"счётчики должны быть защищены блокировкой, а не отдельными атомарными "
+"инструкциями."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:77
+#, no-wrap
+msgid "Read Locks Versus Write Locks"
+msgstr "Блокировки на чтение и блокировки на запись"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:80
+msgid ""
+"Read locks do not need to be as strong as write locks. Both types of locks "
+"need to ensure that the data they are accessing is not stale. However, only "
+"write access requires exclusive access. Multiple threads can safely read a "
+"value. Using different types of locks for reads and writes can be "
+"implemented in a number of ways."
+msgstr ""
+"Блокировки на чтение не требуют такой же строгости, как блокировки на "
+"запись. Оба типа блокировок должны гарантировать, что данные, к которым они "
+"обращаются, не устарели. Однако запись требует монопольного доступа. "
+"Несколько потоков могут безопасно читать значение. Использование разных "
+"типов блокировок для чтения и записи может быть реализовано несколькими "
+"способами."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:82
+msgid ""
+"First, sx locks can be used in this manner by using an exclusive lock when "
+"writing and a shared lock when reading. This method is quite straightforward."
+msgstr ""
+"Во-первых, блокировки sx могут использоваться таким образом: монопольная "
+"блокировка при записи и разделяемая блокировка при чтении. Этот метод "
+"достаточно прост."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:84
+msgid ""
+"A second method is a bit more obscure. You can protect a datum with multiple "
+"locks. Then for reading that data you simply need to have a read lock of one "
+"of the locks. However, to write to the data, you need to have a write lock "
+"of all of the locks. This can make writing rather expensive but can be "
+"useful when data is accessed in various ways. For example, the parent "
+"process pointer is protected by both the `proctree_lock` sx lock and the per-"
+"process mutex. Sometimes the proc lock is easier as we are just checking to "
+"see who a parent of a process is that we already have locked. However, other "
+"places such as `inferior` need to walk the tree of processes via parent "
+"pointers and locking each process would be prohibitive as well as a pain to "
+"guarantee that the condition you are checking remains valid for both the "
+"check and the actions taken as a result of the check."
+msgstr ""
+"Второй метод несколько менее очевиден. Вы можете защитить данные несколькими "
+"блокировками. Для чтения данных достаточно получить блокировку на чтение "
+"одной из блокировок. Однако для записи данных необходимо получить блокировку "
+"на запись всех блокировок. Это может сделать запись довольно затратной, но "
+"может быть полезно, когда данные доступны различными способами. Например, "
+"указатель на родительский процесс защищён как `proctree_lock` sx-"
+"блокировкой, так и мьютексом процесса. Иногда блокировка процесса удобнее, "
+"так как мы просто проверяем, кто является родителем уже заблокированного "
+"процесса. Однако в других случаях, таких как `inferior`, необходимо обходить "
+"дерево процессов через указатели на родителя, и блокировка каждого процесса "
+"была бы слишком затратной, а также сложной для гарантии того, что "
+"проверяемое условие остаётся верным как во время проверки, так и при "
+"выполнении действий, основанных на этой проверке."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:85
+#, no-wrap
+msgid "Locking Conditions and Results"
+msgstr "Условия и результаты блокировки"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:88
+msgid ""
+"If you need a lock to check the state of a variable so that you can take an "
+"action based on the state you read, you can not just hold the lock while "
+"reading the variable and then drop the lock before you act on the value you "
+"read. Once you drop the lock, the variable can change rendering your "
+"decision invalid. Thus, you must hold the lock both while reading the "
+"variable and while performing the action as a result of the test."
+msgstr ""
+"Если вам нужна блокировка для проверки состояния переменной, чтобы можно "
+"было выполнить действие на основе прочитанного состояния, вы не можете "
+"просто удерживать блокировку во время чтения переменной, а затем снять "
+"блокировку перед выполнением действия на основе прочитанного значения. Как "
+"только вы снимаете блокировку, переменная может измениться, что сделает ваше "
+"решение недействительным. Таким образом, вы должны удерживать блокировку как "
+"во время чтения переменной, так и во время выполнения действия в результате "
+"проверки."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:90
+#, no-wrap
+msgid "General Architecture and Design"
+msgstr "Общая Архитектура и Дизайн"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:92
+#, no-wrap
+msgid "Interrupt Handling"
+msgstr "Обработка прерываний"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:95
+msgid ""
+"Following the pattern of several other multi-threaded UNIX(R) kernels, "
+"FreeBSD deals with interrupt handlers by giving them their own thread "
+"context. Providing a context for interrupt handlers allows them to block on "
+"locks. To help avoid latency, however, interrupt threads run at real-time "
+"kernel priority. Thus, interrupt handlers should not execute for very long "
+"to avoid starving other kernel threads. In addition, since multiple handlers "
+"may share an interrupt thread, interrupt handlers should not sleep or use a "
+"sleepable lock to avoid starving another interrupt handler."
+msgstr ""
+"Следуя примеру нескольких других многопоточных ядер UNIX(R), FreeBSD "
+"реализрвала обработчики прерываний, предоставив им собственный контекст "
+"потока. Предоставление контекста для обработчиков прерываний позволяет им "
+"блокироваться на блокировках. Однако, чтобы избежать задержек, потоки "
+"обработки прерываний выполняются с приоритетом реального времени в ядре. "
+"Таким образом, обработчики прерываний не должны выполняться слишком долго, "
+"чтобы не лишать ресурсов другие потоки ядра. Кроме того, поскольку несколько "
+"обработчиков могут использовать один поток прерываний, обработчики "
+"прерываний не должны переходить в режим сна или использовать блокировки, "
+"допускающие сон, чтобы не лишать ресурсов другие обработчики прерываний."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:97
+msgid ""
+"The interrupt threads currently in FreeBSD are referred to as heavyweight "
+"interrupt threads. They are called this because switching to an interrupt "
+"thread involves a full context switch. In the initial implementation, the "
+"kernel was not preemptive and thus interrupts that interrupted a kernel "
+"thread would have to wait until the kernel thread blocked or returned to "
+"userland before they would have an opportunity to run."
+msgstr ""
+"Текущие потоки обработки прерываний в FreeBSD называются тяжеловесными "
+"потоками обработки прерываний. Они получили такое название, потому что "
+"переключение на поток обработки прерывания включает в себя полное "
+"переключение контекста. В первоначальной реализации ядро не было "
+"вытесняющим, поэтому прерывания, которые прерывали поток ядра, должны были "
+"ждать, пока поток ядра не заблокируется или не вернётся в пользовательское "
+"пространство, прежде чем у них появится возможность выполниться."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:99
+msgid ""
+"To deal with the latency problems, the kernel in FreeBSD has been made "
+"preemptive. Currently, we only preempt a kernel thread when we release a "
+"sleep mutex or when an interrupt comes in. However, the plan is to make the "
+"FreeBSD kernel fully preemptive as described below."
+msgstr ""
+"Для решения проблем с задержками ядро FreeBSD стало вытесняющим. В настоящее "
+"время вытеснение потока ядра происходит только при освобождении мьютекса сна "
+"или при поступлении прерывания. Однако планируется сделать ядро FreeBSD "
+"полностью вытесняющим, как описано ниже."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:101
+msgid ""
+"Not all interrupt handlers execute in a thread context. Instead, some "
+"handlers execute directly in primary interrupt context. These interrupt "
+"handlers are currently misnamed \"fast\" interrupt handlers since the "
+"`INTR_FAST` flag used in earlier versions of the kernel is used to mark "
+"these handlers. The only interrupts which currently use these types of "
+"interrupt handlers are clock interrupts and serial I/O device interrupts. "
+"Since these handlers do not have their own context, they may not acquire "
+"blocking locks and thus may only use spin mutexes."
+msgstr ""
+"Не все обработчики прерываний выполняются в контексте потока. Вместо этого, "
+"некоторые обработчики выполняются непосредственно в основном контексте "
+"прерывания. Эти обработчики прерываний в настоящее время ошибочно называются "
+"\"быстрыми\" обработчиками прерываний, поскольку для их обозначения "
+"применяется флаг `INTR_FAST`, использовавшийся в более ранних версиях ядра. "
+"Единственные прерывания, которые в настоящее время используют такие "
+"обработчики прерываний, — это прерывания от часов и последовательных "
+"устройств ввода-вывода. Поскольку эти обработчики не имеют собственного "
+"контекста, они не могут захватывать блокирующие блокировки и, следовательно, "
+"могут использовать только спин-мьютексы."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:103
+msgid ""
+"Finally, there is one optional optimization that can be added in MD code "
+"called lightweight context switches. Since an interrupt thread executes in a "
+"kernel context, it can borrow the vmspace of any process. Thus, in a "
+"lightweight context switch, the switch to the interrupt thread does not "
+"switch vmspaces but borrows the vmspace of the interrupted thread. In order "
+"to ensure that the vmspace of the interrupted thread does not disappear out "
+"from under us, the interrupted thread is not allowed to execute until the "
+"interrupt thread is no longer borrowing its vmspace. This can happen when "
+"the interrupt thread either blocks or finishes. If an interrupt thread "
+"blocks, then it will use its own context when it is made runnable again. "
+"Thus, it can release the interrupted thread."
+msgstr ""
+"Наконец, существует одна дополнительная оптимизация, которую можно добавить "
+"в код MD, называемая легковесными переключениями контекста. Поскольку поток "
+"обработки прерывания выполняется в контексте ядра, он может заимствовать "
+"vmspace любого процесса. Таким образом, при легковесном переключении "
+"контекста переход к потоку обработки прерывания не меняет vmspace, а "
+"заимствует vmspace прерванного потока. Чтобы гарантировать, что vmspace "
+"прерванного потока не исчезнет во время работы, прерванному потоку "
+"запрещается выполнение до тех пор, пока поток обработки прерывания больше не "
+"использует его vmspace. Это может произойти, когда поток обработки "
+"прерывания либо блокируется, либо завершается. Если поток обработки "
+"прерывания блокируется, то при повторном запуске он будет использовать свой "
+"собственный контекст. Таким образом, он может освободить прерванный поток."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:105
+msgid ""
+"The cons of this optimization are that they are very machine specific and "
+"complex and thus only worth the effort if their is a large performance "
+"improvement. At this point it is probably too early to tell, and in fact, "
+"will probably hurt performance as almost all interrupt handlers will "
+"immediately block on Giant and require a thread fix-up when they block. "
+"Also, an alternative method of interrupt handling has been proposed by Mike "
+"Smith that works like so:"
+msgstr ""
+"Недостатки этой оптимизации заключаются в том, что они очень специфичны для "
+"конкретной машины и сложны, поэтому стоят усилий только в случае "
+"значительного улучшения производительности. На данный момент, вероятно, ещё "
+"рано делать выводы, и, фактически, это может даже ухудшить "
+"производительность, так как почти все обработчики прерываний будут "
+"немедленно блокироваться на Giant и потребуют исправления потока при "
+"блокировке. Кроме того, Майк Смит предложил альтернативный метод обработки "
+"прерываний, который работает следующим образом:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:107
+msgid ""
+"Each interrupt handler has two parts: a predicate which runs in primary "
+"interrupt context and a handler which runs in its own thread context."
+msgstr ""
+"Каждый обработчик прерывания состоит из двух частей: предиката, который "
+"выполняется в основном контексте прерывания, и обработчика, который "
+"выполняется в контексте собственного потока."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:108
+msgid ""
+"If an interrupt handler has a predicate, then when an interrupt is "
+"triggered, the predicate is run. If the predicate returns true then the "
+"interrupt is assumed to be fully handled and the kernel returns from the "
+"interrupt. If the predicate returns false or there is no predicate, then the "
+"threaded handler is scheduled to run."
+msgstr ""
+"Если у обработчика прерывания есть предикат, то при срабатывании прерывания "
+"этот предикат выполняется. Если предикат возвращает значение `true`, "
+"прерывание считается полностью обработанным, и ядро возвращается из "
+"прерывания. Если предикат возвращает `false` или предиката нет, то "
+"запланированный обработчик запускается."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:110
+msgid ""
+"Fitting light weight context switches into this scheme might prove rather "
+"complicated. Since we may want to change to this scheme at some point in the "
+"future, it is probably best to defer work on light weight context switches "
+"until we have settled on the final interrupt handling architecture and "
+"determined how light weight context switches might or might not fit into it."
+msgstr ""
+"Встраивание легковесных переключений контекста в эту схему может оказаться "
+"довольно сложным. Поскольку мы, возможно, захотим перейти на эту схему в "
+"будущем, вероятно, лучше отложить работу над легковесными переключениями "
+"контекста до тех пор, пока мы не определимся с окончательной архитектурой "
+"обработки прерываний и не выясним, как легковесные переключения контекста "
+"могут (или не могут) в неё вписаться."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:111
+#, no-wrap
+msgid "Kernel Preemption and Critical Sections"
+msgstr "Ядро с вытеснением и критические секции"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:113
+#, no-wrap
+msgid "Kernel Preemption in a Nutshell"
+msgstr "Ядро и вытеснение вкратце"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:116
+msgid ""
+"Kernel preemption is fairly simple. The basic idea is that a CPU should "
+"always be doing the highest priority work available. Well, that is the ideal "
+"at least. There are a couple of cases where the expense of achieving the "
+"ideal is not worth being perfect."
+msgstr ""
+"Вытеснение ядра довольно просто. Основная идея заключается в том, что "
+"процессор всегда должен выполнять наиболее приоритетную доступную работу. "
+"Ну, это в идеале, по крайней мере. Есть несколько случаев, когда затраты на "
+"достижение идеала не стоят совершенства."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:118
+msgid ""
+"Implementing full kernel preemption is very straightforward: when you "
+"schedule a thread to be executed by putting it on a run queue, you check to "
+"see if its priority is higher than the currently executing thread. If so, "
+"you initiate a context switch to that thread."
+msgstr ""
+"Реализация полной вытесняющей многозадачности в ядре очень проста: когда вы "
+"планируете выполнение потока, помещая его в очередь выполнения, вы "
+"проверяете, является ли его приоритет выше, чем у текущего выполняемого "
+"потока. Если да, вы инициируете переключение контекста на этот поток."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:120
+msgid ""
+"While locks can protect most data in the case of a preemption, not all of "
+"the kernel is preemption safe. For example, if a thread holding a spin mutex "
+"preempted and the new thread attempts to grab the same spin mutex, the new "
+"thread may spin forever as the interrupted thread may never get a chance to "
+"execute. Also, some code such as the code to assign an address space number "
+"for a process during `exec` on the Alpha needs to not be preempted as it "
+"supports the actual context switch code. Preemption is disabled for these "
+"code sections by using a critical section."
+msgstr ""
+"Хотя блокировки могут защитить большинство данных в случае вытеснения, не "
+"все части ядра безопасны для вытеснения. Например, если поток, удерживающий "
+"спин-блокировку, будет вытеснен, а новый поток попытается захватить ту же "
+"спин-блокировку, новый поток может вращаться вечно, так как прерванный поток "
+"может никогда не получить шанс на выполнение. Кроме того, некоторый код, "
+"такой как код для назначения номера адресного пространства процессу во время "
+"`exec` на Alpha, не должен быть вытеснен, так как он поддерживает "
+"фактический код переключения контекста. Для таких участков кода вытеснение "
+"отключается с использованием критической секции."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:121
+#, no-wrap
+msgid "Critical Sections"
+msgstr "Критические Секции"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:124
+msgid ""
+"The responsibility of the critical section API is to prevent context "
+"switches inside of a critical section. With a fully preemptive kernel, every "
+"`setrunqueue` of a thread other than the current thread is a preemption "
+"point. One implementation is for `critical_enter` to set a per-thread flag "
+"that is cleared by its counterpart. If `setrunqueue` is called with this "
+"flag set, it does not preempt regardless of the priority of the new thread "
+"relative to the current thread. However, since critical sections are used in "
+"spin mutexes to prevent context switches and multiple spin mutexes can be "
+"acquired, the critical section API must support nesting. For this reason the "
+"current implementation uses a nesting count instead of a single per-thread "
+"flag."
+msgstr ""
+"Ответственность API критической секции заключается в предотвращении "
+"переключения контекста внутри критической секции. В полностью вытесняющем "
+"ядре каждый вызов `setrunqueue` для потока, отличного от текущего, является "
+"точкой вытеснения. Одна из реализаций заключается в том, что "
+"`critical_enter` устанавливает флаг для каждого потока, который сбрасывается "
+"его парной функцией. Если `setrunqueue` вызывается, когда этот флаг "
+"установлен, вытеснение не происходит, независимо от приоритета нового потока "
+"относительно текущего. Однако, поскольку критические секции используются в "
+"спин-блокировках для предотвращения переключения контекста и может быть "
+"захвачено несколько спин-блокировок, API критической секции должен "
+"поддерживать вложенность. По этой причине текущая реализация использует "
+"счетчик вложенности вместо одиночного флага для каждого потока."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:126
+msgid ""
+"In order to minimize latency, preemptions inside of a critical section are "
+"deferred rather than dropped. If a thread that would normally be preempted "
+"to is made runnable while the current thread is in a critical section, then "
+"a per-thread flag is set to indicate that there is a pending preemption. "
+"When the outermost critical section is exited, the flag is checked. If the "
+"flag is set, then the current thread is preempted to allow the higher "
+"priority thread to run."
+msgstr ""
+"Для минимизации задержек прерывания внутри критической секции откладываются, "
+"а не отбрасываются. Если поток, который в обычных условиях должен быть "
+"вытеснен, становится готовым к выполнению, пока текущий поток находится в "
+"критической секции, то устанавливается флаг для данного потока, указывающий "
+"на ожидающее прерывание. При выходе из самой внешней критической секции флаг "
+"проверяется. Если флаг установлен, текущий поток вытесняется, чтобы "
+"позволить выполниться потоку с более высоким приоритетом."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:128
+msgid ""
+"Interrupts pose a problem with regards to spin mutexes. If a low-level "
+"interrupt handler needs a lock, it needs to not interrupt any code needing "
+"that lock to avoid possible data structure corruption. Currently, providing "
+"this mechanism is piggybacked onto critical section API by means of the "
+"`cpu_critical_enter` and `cpu_critical_exit` functions. Currently this API "
+"disables and re-enables interrupts on all of FreeBSD's current platforms. "
+"This approach may not be purely optimal, but it is simple to understand and "
+"simple to get right. Theoretically, this second API need only be used for "
+"spin mutexes that are used in primary interrupt context. However, to make "
+"the code simpler, it is used for all spin mutexes and even all critical "
+"sections. It may be desirable to split out the MD API from the MI API and "
+"only use it in conjunction with the MI API in the spin mutex implementation. "
+"If this approach is taken, then the MD API likely would need a rename to "
+"show that it is a separate API."
+msgstr ""
+"Прерывания создают проблему для спин-мьютексов. Если обработчик "
+"низкоуровневого прерывания требует блокировки, он не должен прерывать любой "
+"код, которому нужна эта блокировка, чтобы избежать возможного повреждения "
+"структур данных. В настоящее время этот механизм реализован через API "
+"критических секций с помощью функций `cpu_critical_enter` и "
+"`cpu_critical_exit`. Сейчас этот API отключает и снова включает прерывания "
+"на всех текущих платформах FreeBSD. Такой подход может быть не идеально "
+"оптимальным, но он прост для понимания и надежен в реализации. Теоретически, "
+"этот второй API нужен только для спин-мьютексов, используемых в основном "
+"контексте прерываний. Однако, для упрощения кода, он используется для всех "
+"спин-мьютексов и даже для всех критических секций. Возможно, стоит отделить "
+"MD API от MI API и использовать его только совместно с MI API в реализации "
+"спин-мьютексов. Если будет принят такой подход, то MD API, вероятно, "
+"потребуется переименовать, чтобы показать, что это отдельный API."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:129
+#, no-wrap
+msgid "Design Tradeoffs"
+msgstr "Компромиссы проектирования"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:132
+msgid ""
+"As mentioned earlier, a couple of trade-offs have been made to sacrifice "
+"cases where perfect preemption may not always provide the best performance."
+msgstr ""
+"Как упоминалось ранее, были сделаны некоторые компромиссы, чтобы "
+"пожертвовать случаями, когда идеальная вытесняющая многозадачность не всегда "
+"обеспечивает наилучшую производительность."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:134
+msgid ""
+"The first trade-off is that the preemption code does not take other CPUs "
+"into account. Suppose we have a two CPU's A and B with the priority of A's "
+"thread as 4 and the priority of B's thread as 2. If CPU B makes a thread "
+"with priority 1 runnable, then in theory, we want CPU A to switch to the new "
+"thread so that we will be running the two highest priority runnable threads. "
+"However, the cost of determining which CPU to enforce a preemption on as "
+"well as actually signaling that CPU via an IPI along with the "
+"synchronization that would be required would be enormous. Thus, the current "
+"code would instead force CPU B to switch to the higher priority thread. Note "
+"that this still puts the system in a better position as CPU B is executing a "
+"thread of priority 1 rather than a thread of priority 2."
+msgstr ""
+"Первый компромисс заключается в том, что код вытеснения не учитывает другие "
+"процессоры. Предположим, у нас есть два процессора A и B, где приоритет "
+"потока A равен 4, а приоритет потока B равен 2. Если процессор B делает "
+"поток с приоритетом 1 готовым к выполнению, то теоретически мы хотим, чтобы "
+"процессор A переключился на новый поток, чтобы выполнялись два потока с "
+"наивысшим приоритетом. Однако стоимость определения, на какой процессор "
+"нужно применить вытеснение, а также фактическая сигнализация этому "
+"процессору через IPI вместе с необходимой синхронизацией были бы огромными. "
+"Таким образом, текущий код вместо этого заставит процессор B переключиться "
+"на поток с более высоким приоритетом. Заметим, что это всё равно улучшает "
+"состояние системы, так как процессор B выполняет поток с приоритетом 1, а не "
+"поток с приоритетом 2."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:136
+msgid ""
+"The second trade-off limits immediate kernel preemption to real-time "
+"priority kernel threads. In the simple case of preemption defined above, a "
+"thread is always preempted immediately (or as soon as a critical section is "
+"exited) if a higher priority thread is made runnable. However, many threads "
+"executing in the kernel only execute in a kernel context for a short time "
+"before either blocking or returning to userland. Thus, if the kernel "
+"preempts these threads to run another non-realtime kernel thread, the kernel "
+"may switch out the executing thread just before it is about to sleep or "
+"execute. The cache on the CPU must then adjust to the new thread. When the "
+"kernel returns to the preempted thread, it must refill all the cache "
+"information that was lost. In addition, two extra context switches are "
+"performed that could be avoided if the kernel deferred the preemption until "
+"the first thread blocked or returned to userland. Thus, by default, the "
+"preemption code will only preempt immediately if the higher priority thread "
+"is a real-time priority thread."
+msgstr ""
+"Второй компромисс ограничивает немедленное вытеснение ядра только потоками "
+"ядра с реальным временем. В простом случае вытеснения, описанном выше, поток "
+"всегда вытесняется немедленно (или как только будет покинута критическая "
+"секция), если становится доступным поток с более высоким приоритетом. Однако "
+"многие потоки, выполняющиеся в ядре, работают в контексте ядра лишь короткое "
+"время перед тем, как либо заблокироваться, либо вернуться в пользовательское "
+"пространство. Таким образом, если ядро вытеснит эти потоки для выполнения "
+"другого потока ядра без реального времени, оно может переключиться с "
+"выполняемого потока как раз перед тем, как тот собирается завершиться или "
+"перейти в режим ожидания. Кэш процессора должен затем адаптироваться к "
+"новому потоку. Когда ядро возвращается к вытесненному потоку, оно должно "
+"восстановить все потерянные кэшированные данные. Кроме того, выполняются два "
+"дополнительных переключения контекста, которых можно было бы избежать, если "
+"бы ядро отложило вытеснение до момента, пока первый поток не заблокируется "
+"или не вернётся в пользовательское пространство. Таким образом, по умолчанию "
+"код вытеснения будет немедленно вытеснять поток только в том случае, если "
+"поток с более высоким приоритетом имеет приоритет реального времени."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:138
+msgid ""
+"Turning on full kernel preemption for all kernel threads has value as a "
+"debugging aid since it exposes more race conditions. It is especially useful "
+"on UP systems were many races are hard to simulate otherwise. Thus, there is "
+"a kernel option `FULL_PREEMPTION` to enable preemption for all kernel "
+"threads that can be used for debugging purposes."
+msgstr ""
+"Включение полной вытесняющей многозадачности для всех потоков ядра полезно в "
+"качестве средства отладки, так как позволяет выявить больше состояний гонки. "
+"Это особенно полезно на однопроцессорных системах (UP), где многие гонки "
+"сложно воспроизвести другими способами. Таким образом, существует опция ядра "
+"`FULL_PREEMPTION` для включения вытеснения для всех потоков ядра, которая "
+"может использоваться для целей отладки."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:139
+#, no-wrap
+msgid "Thread Migration"
+msgstr "Миграция потоков"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:142
+msgid ""
+"Simply put, a thread migrates when it moves from one CPU to another. In a "
+"non-preemptive kernel this can only happen at well-defined points such as "
+"when calling `msleep` or returning to userland. However, in the preemptive "
+"kernel, an interrupt can force a preemption and possible migration at any "
+"time. This can have negative affects on per-CPU data since with the "
+"exception of `curthread` and `curpcb` the data can change whenever you "
+"migrate. Since you can potentially migrate at any time this renders "
+"unprotected per-CPU data access rather useless. Thus it is desirable to be "
+"able to disable migration for sections of code that need per-CPU data to be "
+"stable."
+msgstr ""
+"Простыми словами, поток мигрирует, когда переходит с одного CPU на другой. В "
+"неперемещаемом ядре это может происходить только в определённых точках, "
+"например, при вызове `msleep` или возврате в пользовательское пространство. "
+"Однако в перемещаемом ядре прерывание может вызвать вытеснение и возможную "
+"миграцию в любой момент. Это может негативно сказаться на данных, "
+"специфичных для CPU, поскольку, за исключением `curthread` и `curpcb`, "
+"данные могут изменяться при любой миграции. Поскольку потенциально миграция "
+"может произойти в любой момент, это делает незащищённый доступ к данным, "
+"специфичным для CPU, практически бесполезным. Поэтому желательно иметь "
+"возможность отключать миграцию для участков кода, где требуется стабильность "
+"данных, специфичных для CPU."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:144
+msgid ""
+"Critical sections currently prevent migration since they do not allow "
+"context switches. However, this may be too strong of a requirement to "
+"enforce in some cases since a critical section also effectively blocks "
+"interrupt threads on the current processor. As a result, another API has "
+"been provided to allow the current thread to indicate that if it preempted "
+"it should not migrate to another CPU."
+msgstr ""
+"Критические секции в настоящее время предотвращают миграцию, поскольку они "
+"не допускают переключения контекстов. Однако это может быть слишком строгим "
+"требованием в некоторых случаях, так как критическая секция также эффективно "
+"блокирует потоки прерываний на текущем процессоре. В результате был "
+"предоставлен другой API, позволяющий текущему потоку указать, что если он "
+"будет вытеснен, он не должен мигрировать на другой CPU."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:146
+msgid ""
+"This API is known as thread pinning and is provided by the scheduler. The "
+"API consists of two functions: `sched_pin` and `sched_unpin`. These "
+"functions manage a per-thread nesting count `td_pinned`. A thread is pinned "
+"when its nesting count is greater than zero and a thread starts off unpinned "
+"with a nesting count of zero. Each scheduler implementation is required to "
+"ensure that pinned threads are only executed on the CPU that they were "
+"executing on when the `sched_pin` was first called. Since the nesting count "
+"is only written to by the thread itself and is only read by other threads "
+"when the pinned thread is not executing but while `sched_lock` is held, then "
+"`td_pinned` does not need any locking. The `sched_pin` function increments "
+"the nesting count and `sched_unpin` decrements the nesting count. Note that "
+"these functions only operate on the current thread and bind the current "
+"thread to the CPU it is executing on at the time. To bind an arbitrary "
+"thread to a specific CPU, the `sched_bind` and `sched_unbind` functions "
+"should be used instead."
+msgstr ""
+"Этот API известен как закрепление потока и предоставляется планировщиком. "
+"API состоит из двух функций: `sched_pin` и `sched_unpin`. Эти функции "
+"управляют счетчиком вложенности `td_pinned` для каждого потока. Поток "
+"считается закрепленным, когда его счетчик вложенности больше нуля, и "
+"прекрашает быть закрепленным с нулевым счетчиком вложенности. Каждая "
+"реализация планировщика должна гарантировать, что закрепленные потоки "
+"выполняются только на том CPU, на котором они выполнялись при первом вызове "
+"`sched_pin`. Поскольку счетчик вложенности изменяется только самим потоком и "
+"читается другими потоками только тогда, когда закрепленный поток не "
+"выполняется, но удерживается `sched_lock`, то `td_pinned` не требует "
+"блокировки. Функция `sched_pin` увеличивает счетчик вложенности, а "
+"`sched_unpin` уменьшает его. Обратите внимание, что эти функции работают "
+"только с текущим потоком и привязывают текущий поток к CPU, на котором он "
+"выполняется в данный момент. Для привязки произвольного потока к "
+"определенному CPU следует использовать функции `sched_bind` и `sched_unbind`."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:147
+#, no-wrap
+msgid "Callouts"
+msgstr "Обратные вызовы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:150
+msgid ""
+"The `timeout` kernel facility permits kernel services to register functions "
+"for execution as part of the `softclock` software interrupt. Events are "
+"scheduled based on a desired number of clock ticks, and callbacks to the "
+"consumer-provided function will occur at approximately the right time."
+msgstr ""
+"Функция ядра `timeout` позволяет службам ядра регистрировать функции для "
+"выполнения в рамках программного прерывания `softclock`. События планируются "
+"на основе заданного количества тактов часов, и вызовы предоставленной "
+"потребителем функции будут происходить приблизительно в нужное время."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:152
+msgid ""
+"The global list of pending timeout events is protected by a global spin "
+"mutex, `callout_lock`; all access to the timeout list must be performed with "
+"this mutex held. When `softclock` is woken up, it scans the list of pending "
+"timeouts for those that should fire. In order to avoid lock order reversal, "
+"the `softclock` thread will release the `callout_lock` mutex when invoking "
+"the provided `timeout` callback function. If the `CALLOUT_MPSAFE` flag was "
+"not set during registration, then Giant will be grabbed before invoking the "
+"callout, and then released afterwards. The `callout_lock` mutex will be re-"
+"grabbed before proceeding. The `softclock` code is careful to leave the list "
+"in a consistent state while releasing the mutex. If `DIAGNOSTIC` is enabled, "
+"then the time taken to execute each function is measured, and a warning is "
+"generated if it exceeds a threshold."
+msgstr ""
+"Глобальный список ожидающих событий с таймаутом защищен глобальной спин-"
+"блокировкой `callout_lock`; любой доступ к списку таймаутов должен "
+"выполняться с удержанием этой блокировки. Когда `softclock` пробуждается, он "
+"сканирует список ожидающих таймаутов на предмет тех, которые должны "
+"сработать. Чтобы избежать инверсии блокировок, поток `softclock` освобождает "
+"блокировку `callout_lock` при вызове предоставленной функции обратного "
+"вызова `timeout`. Если флаг `CALLOUT_MPSAFE` не был установлен во время "
+"регистрации, то `Giant` будет захвачен перед вызовом обратного вызова, а "
+"затем освобожден после него. Блокировка `callout_lock` будет повторно "
+"захвачена перед продолжением работы. Код `softclock` аккуратно поддерживает "
+"список в согласованном состоянии во время освобождения блокировки. Если "
+"включен `DIAGNOSTIC`, то измеряется время выполнения каждой функции, и если "
+"оно превышает пороговое значение, генерируется предупреждение."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:154
+#, no-wrap
+msgid "Specific Locking Strategies"
+msgstr "Конкретные стратегии блокировки"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:156
+#, no-wrap
+msgid "Credentials"
+msgstr "Учетные данные"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:159
+msgid ""
+"`struct ucred` is the kernel's internal credential structure, and is "
+"generally used as the basis for process-driven access control within the "
+"kernel. BSD-derived systems use a \"copy-on-write\" model for credential "
+"data: multiple references may exist for a credential structure, and when a "
+"change needs to be made, the structure is duplicated, modified, and then the "
+"reference replaced. Due to wide-spread caching of the credential to "
+"implement access control on open, this results in substantial memory "
+"savings. With a move to fine-grained SMP, this model also saves "
+"substantially on locking operations by requiring that modification only "
+"occur on an unshared credential, avoiding the need for explicit "
+"synchronization when consuming a known-shared credential."
+msgstr ""
+"`struct ucred` — это внутренняя структура учетных данных ядра, которая "
+"обычно используется в качестве основы для управления доступом на уровне "
+"процессов внутри ядра. Системы, производные от BSD, используют модель "
+"«копирования при записи» для учетных данных: могут существовать "
+"множественные ссылки на структуру учетных данных, и когда требуется внести "
+"изменение, структура дублируется, изменяется, а затем ссылка заменяется. "
+"Благодаря широко распространенному кэшированию учетных данных для реализации "
+"контроля доступа при открытии, это приводит к значительной экономии памяти. "
+"С переходом на детализированную SMP (симметричную многопроцессорность), эта "
+"модель также существенно экономит на операциях блокировки, требуя, чтобы "
+"модификации выполнялись только для неразделяемых учетных данных, избегая "
+"необходимости явной синхронизации при использовании известных разделяемых "
+"учетных данных."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:161
+msgid ""
+"Credential structures with a single reference are considered mutable; shared "
+"credential structures must not be modified or a race condition is risked. A "
+"mutex, `cr_mtxp` protects the reference count of `struct ucred` so as to "
+"maintain consistency. Any use of the structure requires a valid reference "
+"for the duration of the use, or the structure may be released out from under "
+"the illegitimate consumer."
+msgstr ""
+"Структуры учетных данных с единственной ссылкой считаются изменяемыми; "
+"разделяемые структуры учетных данных не должны изменяться, иначе возникает "
+"риск состояния гонки. Мьютекс `cr_mtxp` защищает счетчик ссылок структуры "
+"`struct ucred` для поддержания согласованности. Любое использование "
+"структуры требует действительной ссылки на протяжении всего времени "
+"использования, иначе структура может быть освобождена из-под нелегитимного "
+"потребителя."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:163
+msgid ""
+"The `struct ucred` mutex is a leaf mutex and is implemented via a mutex pool "
+"for performance reasons."
+msgstr ""
+"Мьютекс `struct ucred` является листовым мьютексом и реализован через пул "
+"мьютексов по соображениям производительности."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:165
+msgid ""
+"Usually, credentials are used in a read-only manner for access control "
+"decisions, and in this case `td_ucred` is generally preferred because it "
+"requires no locking. When a process' credential is updated the `proc` lock "
+"must be held across the check and update operations thus avoid races. The "
+"process credential `p_ucred` must be used for check and update operations to "
+"prevent time-of-check, time-of-use races."
+msgstr ""
+"Обычно учетные данные используются в режиме только для чтения для принятия "
+"решений по контролю доступа, и в этом случае `td_ucred`, как правило, "
+"предпочтительнее, поскольку не требует блокировки. Когда учетные данные "
+"процесса обновляются, блокировка `proc` должна удерживаться на протяжении "
+"операций проверки и обновления, чтобы избежать состояний гонки. Учетные "
+"данные процесса `p_ucred` должны использоваться для операций проверки и "
+"обновления, чтобы предотвратить гонки между временем проверки и временем "
+"использования."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:167
+msgid ""
+"If system call invocations will perform access control after an update to "
+"the process credential, the value of `td_ucred` must also be refreshed to "
+"the current process value. This will prevent use of a stale credential "
+"following a change. The kernel automatically refreshes the `td_ucred` "
+"pointer in the thread structure from the process `p_ucred` whenever a "
+"process enters the kernel, permitting use of a fresh credential for kernel "
+"access control."
+msgstr ""
+"Если при системных вызовах будет выполняться контроль доступа после "
+"обновления учетных данных процесса, значение `td_ucred` также должно быть "
+"обновлено до текущего значения процесса. Это предотвратит использование "
+"устаревших учетных данных после изменения. Ядро автоматически обновляет "
+"указатель `td_ucred` в структуре потока из `p_ucred` процесса всякий раз, "
+"когда процесс входит в ядро, что позволяет использовать свежие учетные "
+"данные для контроля доступа в ядре."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:168
+#, no-wrap
+msgid "File Descriptors and File Descriptor Tables"
+msgstr "Дескрипторы файлов и таблицы дескрипторов файлов"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:171
+msgid "Details to follow."
+msgstr "Подробности будут позже."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:172
+#, no-wrap
+msgid "Jail Structures"
+msgstr "Структуры клеток"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:175
+msgid ""
+"`struct prison` stores administrative details pertinent to the maintenance "
+"of jails created using the man:jail[2] API. This includes the per-jail "
+"hostname, IP address, and related settings. This structure is reference-"
+"counted since pointers to instances of the structure are shared by many "
+"credential structures. A single mutex, `pr_mtx` protects read and write "
+"access to the reference count and all mutable variables inside the struct "
+"jail. Some variables are set only when the jail is created, and a valid "
+"reference to the `struct prison` is sufficient to read these values. The "
+"precise locking of each entry is documented via comments in [.filename]#sys/"
+"jail.h#."
+msgstr ""
+"`struct prison` хранит административные данные, связанные с обслуживанием "
+"клеток, созданных с использованием API man:jail[2]. Это включает имя хоста "
+"для каждой клетки, IP-адрес и связанные настройки. Эта структура имеет "
+"счетчик ссылок, так как указатели на её экземпляры разделяются многими "
+"структурами учётных данных. Один мьютекс, `pr_mtx`, защищает чтение и запись "
+"счётчика ссылок и всех изменяемых переменных внутри `struct jail`. Некоторые "
+"переменные устанавливаются только при создании клетки, и действительной "
+"ссылки на `struct prison` достаточно для чтения этих значений. Точная "
+"блокировка каждой записи документирована в комментариях файла "
+"[.filename]#sys/jail.h#."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:176
+#, no-wrap
+msgid "MAC Framework"
+msgstr "MAC Framework"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:179
+msgid ""
+"The TrustedBSD MAC Framework maintains data in a variety of kernel objects, "
+"in the form of `struct label`. In general, labels in kernel objects are "
+"protected by the same lock as the remainder of the kernel object. For "
+"example, the `v_label` label in `struct vnode` is protected by the vnode "
+"lock on the vnode."
+msgstr ""
+"Фреймворк TrustedBSD MAC поддерживает данные в различных объектах ядра в "
+"виде `struct label`. Как правило, метки в объектах ядра защищаются тем же "
+"механизмом блокировки, что и остальная часть объекта ядра. Например, метка "
+"`v_label` в `struct vnode` защищается блокировкой vnode."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:181
+msgid ""
+"In addition to labels maintained in standard kernel objects, the MAC "
+"Framework also maintains a list of registered and active policies. The "
+"policy list is protected by a global mutex (`mac_policy_list_lock`) and a "
+"busy count (also protected by the mutex). Since many access control checks "
+"may occur in parallel, entry to the framework for a read-only access to the "
+"policy list requires holding the mutex while incrementing (and later "
+"decrementing) the busy count. The mutex need not be held for the duration of "
+"the MAC entry operation--some operations, such as label operations on file "
+"system objects--are long-lived. To modify the policy list, such as during "
+"policy registration and de-registration, the mutex must be held and the "
+"reference count must be zero, to prevent modification of the list while it "
+"is in use."
+msgstr ""
+"В дополнение к меткам, поддерживаемым в стандартных объектах ядра, MAC "
+"Framework также поддерживает список зарегистрированных и активных политик. "
+"Список политик защищен глобальной мьютекс-блокировкой "
+"(`mac_policy_list_lock`) и счетчиком использования (также защищенным "
+"мьютексом). Поскольку множество проверок контроля доступа может выполняться "
+"параллельно, вход в framework для доступа только на чтение к списку политик "
+"требует удержания мьютекса во время увеличения (и последующего уменьшения) "
+"счетчика использования. Мьютекс не обязательно удерживать на протяжении всей "
+"операции входа в MAC — некоторые операции, такие как операции с метками на "
+"объектах файловой системы, выполняются длительное время. Для изменения "
+"списка политик, например во время регистрации и отмены регистрации политик, "
+"мьютекс должен быть удержан, а счетчик ссылок должен быть равен нулю, чтобы "
+"предотвратить изменение списка во время его использования."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:183
+msgid ""
+"A condition variable, `mac_policy_list_not_busy`, is available to threads "
+"that need to wait for the list to become unbusy, but this condition variable "
+"must only be waited on if the caller is holding no other locks, or a lock "
+"order violation may be possible. The busy count, in effect, acts as a form "
+"of shared/exclusive lock over access to the framework: the difference is "
+"that, unlike with an sx lock, consumers waiting for the list to become "
+"unbusy may be starved, rather than permitting lock order problems with "
+"regards to the busy count and other locks that may be held on entry to (or "
+"inside) the MAC Framework."
+msgstr ""
+"Условная переменная `mac_policy_list_not_busy` доступна для потоков, которым "
+"необходимо дождаться освобождения списка, но ожидание на этой условной "
+"переменной допустимо только если вызывающий поток не удерживает других "
+"блокировок, иначе может возникнуть нарушение порядка блокировок. Фактически, "
+"счетчик занятости действует как форма разделяемой/исключающей блокировки "
+"доступа к фреймворку: отличие в том, что, в отличие от sx-блокировки, "
+"потребители, ожидающие освобождения списка, могут подвергаться голоданию, "
+"вместо того чтобы допускать проблемы порядка блокировок в отношении счетчика "
+"занятости и других блокировок, которые могут удерживаться при входе в (или "
+"внутри) MAC Framework."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:184
+#, no-wrap
+msgid "Modules"
+msgstr "Модули"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:187
+msgid ""
+"For the module subsystem there exists a single lock that is used to protect "
+"the shared data. This lock is a shared/exclusive (SX) lock and has a good "
+"chance of needing to be acquired (shared or exclusively), therefore there "
+"are a few macros that have been added to make access to the lock more easy. "
+"These macros can be located in [.filename]#sys/module.h# and are quite basic "
+"in terms of usage. The main structures protected under this lock are the "
+"`module_t` structures (when shared) and the global `modulelist_t` structure, "
+"modules. One should review the related source code in [.filename]#kern/"
+"kern_module.c# to further understand the locking strategy."
+msgstr ""
+"Для подсистемы модулей существует единая блокировка, которая используется "
+"для защиты общих данных. Эта блокировка является shared/exclusive (SX) и с "
+"высокой вероятностью потребует захвата (разделяемого или исключительного), "
+"поэтому были добавлены несколько макросов для упрощения работы с ней. Эти "
+"макросы можно найти в [.filename]#sys/module.h#, и их использование довольно "
+"простое. Основные структуры, защищаемые этой блокировкой, — это структуры "
+"`module_t` (при разделяемом доступе) и глобальная структура `modulelist_t` "
+"modules. Для более глубокого понимания стратегии блокировок рекомендуется "
+"изучить соответствующий исходный код в [.filename]#kern/kern_module.c#."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:188
+#, no-wrap
+msgid "Newbus Device Tree"
+msgstr "Дерево устройств Newbus"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:191
+msgid ""
+"The newbus system will have one sx lock. Readers will hold a shared (read) "
+"lock (man:sx_slock[9]) and writers will hold an exclusive (write) lock "
+"(man:sx_xlock[9]). Internal functions will not do locking at all. Externally "
+"visible ones will lock as needed. Those items that do not matter if the race "
+"is won or lost will not be locked, since they tend to be read all over the "
+"place (e.g., man:device_get_softc[9]). There will be relatively few changes "
+"to the newbus data structures, so a single lock should be sufficient and not "
+"impose a performance penalty."
+msgstr ""
+"Система newbus будет использовать одну блокировку sx. Читатели будут "
+"удерживать разделяемую (read) блокировку (man:sx_slock[9]), а писатели — "
+"эксклюзивную (write) блокировку (man:sx_xlock[9]). Внутренние функции не "
+"будут выполнять блокировку вообще. Внешне видимые функции будут "
+"блокироваться по мере необходимости. Элементы, для которых не важно, "
+"выиграна гонка или проиграна, не будут блокироваться, так как они обычно "
+"читаются во многих местах (например, man:device_get_softc[9]). Изменения в "
+"структурах данных newbus будут относительно редкими, поэтому одной "
+"блокировки должно быть достаточно, и это не приведёт к снижению "
+"производительности."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:192
+#, no-wrap
+msgid "Pipes"
+msgstr "Каналы (pipe)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:195
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:217
+msgid "..."
+msgstr "..."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:196
+#, no-wrap
+msgid "Processes and Threads"
+msgstr "Процессы и потоки"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:199
+msgid "process hierarchy"
+msgstr "иерархия процессов"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:201
+msgid "proc locks, references"
+msgstr "блокировки и ссылки proc"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:203
+msgid ""
+"thread-specific copies of proc entries to freeze during system calls, "
+"including td_ucred"
+msgstr ""
+"потокоспецифичные копии записей proc для заморозки во время системных "
+"вызовов, включая td_ucred"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:205
+msgid "inter-process operations"
+msgstr "межпроцессные операции"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:207
+msgid "process groups and sessions"
+msgstr "группы процессов и сеансы"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:208
+#, no-wrap
+msgid "Scheduler"
+msgstr "Планировщик"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:211
+msgid ""
+"Lots of references to `sched_lock` and notes pointing at specific primitives "
+"and related magic elsewhere in the document."
+msgstr ""
+"Множество ссылок на `sched_lock` и примечания, указывающие на конкретные "
+"примитивы и связанные с ними особенности в других частях документа."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:212
+#, no-wrap
+msgid "Select and Poll"
+msgstr "Select и Poll"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:215
+msgid ""
+"The `select` and `poll` functions permit threads to block waiting on events "
+"on file descriptors--most frequently, whether or not the file descriptors "
+"are readable or writable."
+msgstr ""
+"Функции `select` и `poll` позволяют потокам блокироваться в ожидании событий "
+"на файловых дескрипторах — чаще всего, доступности файловых дескрипторов для "
+"чтения или записи."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:218
+#, no-wrap
+msgid "SIGIO"
+msgstr "SIGIO"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:221
+msgid ""
+"The SIGIO service permits processes to request the delivery of a SIGIO "
+"signal to its process group when the read/write status of specified file "
+"descriptors changes. At most one process or process group is permitted to "
+"register for SIGIO from any given kernel object, and that process or group "
+"is referred to as the owner. Each object supporting SIGIO registration "
+"contains pointer field that is `NULL` if the object is not registered, or "
+"points to a `struct sigio` describing the registration. This field is "
+"protected by a global mutex, `sigio_lock`. Callers to SIGIO maintenance "
+"functions must pass in this field \"by reference\" so that local register "
+"copies of the field are not made when unprotected by the lock."
+msgstr ""
+"Служба SIGIO позволяет процессам запрашивать доставку сигнала SIGIO своей "
+"группе процессов при изменении статуса чтения/записи указанных файловых "
+"дескрипторов. Не более одного процесса или группы процессов может "
+"зарегистрироваться для получения SIGIO от любого заданного объекта ядра, и "
+"такой процесс или группа называется владельцем. Каждый объект, "
+"поддерживающий регистрацию SIGIO, содержит поле-указатель, которое имеет "
+"значение `NULL`, если объект не зарегистрирован, или указывает на структуру "
+"`struct sigio`, описывающую регистрацию. Это поле защищено глобальным "
+"мьютексом `sigio_lock`. Вызывающие функции обслуживания SIGIO должны "
+"передавать это поле «по ссылке», чтобы локальные копии регистра не "
+"создавались без защиты блокировкой."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:223
+msgid ""
+"One `struct sigio` is allocated for each registered object associated with "
+"any process or process group, and contains back-pointers to the object, "
+"owner, signal information, a credential, and the general disposition of the "
+"registration. Each process or progress group contains a list of registered "
+"`struct sigio` structures, `p_sigiolst` for processes, and `pg_sigiolst` for "
+"process groups. These lists are protected by the process or process group "
+"locks respectively. Most fields in each `struct sigio` are constant for the "
+"duration of the registration, with the exception of the `sio_pgsigio` field "
+"which links the `struct sigio` into the process or process group list. "
+"Developers implementing new kernel objects supporting SIGIO will, in "
+"general, want to avoid holding structure locks while invoking SIGIO "
+"supporting functions, such as `fsetown` or `funsetown` to avoid defining a "
+"lock order between structure locks and the global SIGIO lock. This is "
+"generally possible through use of an elevated reference count on the "
+"structure, such as reliance on a file descriptor reference to a pipe during "
+"a pipe operation."
+msgstr ""
+"Один `struct sigio` выделяется для каждого зарегистрированного объекта, "
+"связанного с любым процессом или группой процессов, и содержит обратные "
+"ссылки на объект, владельца, информацию о сигнале, учетные данные и общее "
+"состояние регистрации. Каждый процесс или группа процессов содержит список "
+"зарегистрированных структур `struct sigio`: `p_sigiolst` для процессов и "
+"`pg_sigiolst` для групп процессов. Эти списки защищены блокировками процесса "
+"или группы процессов соответственно. Большинство полей в каждой `struct "
+"sigio` остаются постоянными на протяжении регистрации, за исключением поля "
+"`sio_pgsigio`, которое связывает `struct sigio` со списком процесса или "
+"группы процессов. Разработчикам, реализующим новые объекты ядра с поддержкой "
+"SIGIO, как правило, следует избегать удержания блокировок структур при "
+"вызове функций поддержки SIGIO, таких как `fsetown` или `funsetown`, чтобы "
+"не определять порядок блокировок между блокировками структур и глобальной "
+"блокировкой SIGIO. Обычно это возможно за счет использования повышенного "
+"счетчика ссылок на структуру, например, путем опоры на ссылку файлового "
+"дескриптора на канал во время операции с каналом."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:224
+#, no-wrap
+msgid "Sysctl"
+msgstr "Sysctl"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:227
+msgid ""
+"The `sysctl` MIB service is invoked from both within the kernel and from "
+"userland applications using a system call. At least two issues are raised in "
+"locking: first, the protection of the structures maintaining the namespace, "
+"and second, interactions with kernel variables and functions that are "
+"accessed by the sysctl interface. Since sysctl permits the direct export "
+"(and modification) of kernel statistics and configuration parameters, the "
+"sysctl mechanism must become aware of appropriate locking semantics for "
+"those variables. Currently, sysctl makes use of a single global sx lock to "
+"serialize use of `sysctl`; however, it is assumed to operate under Giant and "
+"other protections are not provided. The remainder of this section speculates "
+"on locking and semantic changes to sysctl."
+msgstr ""
+"Сервис `sysctl` MIB вызывается как из ядра, так и из пользовательских "
+"приложений с использованием системного вызова. По крайней мере, два вопроса "
+"возникают в отношении блокировок: во-первых, защита структур, поддерживающих "
+"пространство имен, и во-вторых, взаимодействие с переменными и функциями "
+"ядра, к которым обращается интерфейс `sysctl`. Поскольку `sysctl` позволяет "
+"прямое экспортирование (и изменение) статистики ядра и параметров "
+"конфигурации, механизм `sysctl` должен учитывать соответствующие семантики "
+"блокировок для этих переменных. В настоящее время `sysctl` использует единую "
+"глобальную sx-блокировку для сериализации использования `sysctl`; однако "
+"предполагается, что он работает под защитой Giant, и другие защиты не "
+"предоставляются. Оставшаяся часть этого раздела рассматривает возможные "
+"изменения в блокировках и семантике `sysctl`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:229
+msgid ""
+"Need to change the order of operations for sysctl's that update values from "
+"read old, copyin and copyout, write new to copyin, lock, read old and write "
+"new, unlock, copyout. Normal sysctl's that just copyout the old value and "
+"set a new value that they copyin may still be able to follow the old model. "
+"However, it may be cleaner to use the second model for all of the sysctl "
+"handlers to avoid lock operations."
+msgstr ""
+"Необходимо изменить порядок операций для sysctl, которые обновляют значения "
+"из чтения старого, copyin и copyout, записи нового на copyin, блокировку, "
+"чтение старого и запись нового, разблокировку, copyout. Обычные sysctl, "
+"которые просто копируют старое значение и устанавливают новое, которое они "
+"копируют, могут по-прежнему следовать старой модели. Однако, возможно, будет "
+"чище использовать вторую модель для всех обработчиков sysctl, чтобы избежать "
+"операций блокировки."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:231
+msgid ""
+"To allow for the common case, a sysctl could embed a pointer to a mutex in "
+"the SYSCTL_FOO macros and in the struct. This would work for most sysctl's. "
+"For values protected by sx locks, spin mutexes, or other locking strategies "
+"besides a single sleep mutex, SYSCTL_PROC nodes could be used to get the "
+"locking right."
+msgstr ""
+"Для упрощения распространённого случая, sysctl может включать указатель на "
+"мьютекс в макросах SYSCTL_FOO и в структуре. Это будет работать для "
+"большинства sysctl. Для значений, защищённых sx-блокировками, спин-"
+"мьютексами или другими стратегиями синхронизации, отличными от одиночного "
+"мьютекса сна, можно использовать узлы SYSCTL_PROC для обеспечения корректной "
+"блокировки."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:232
+#, no-wrap
+msgid "Taskqueue"
+msgstr "Очередь задач"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:235
+msgid ""
+"The taskqueue's interface has two basic locks associated with it in order to "
+"protect the related shared data. The `taskqueue_queues_mutex` is meant to "
+"serve as a lock to protect the `taskqueue_queues` TAILQ. The other mutex "
+"lock associated with this system is the one in the `struct taskqueue` data "
+"structure. The use of the synchronization primitive here is to protect the "
+"integrity of the data in the `struct taskqueue`. It should be noted that "
+"there are no separate macros to assist the user in locking down his/her own "
+"work since these locks are most likely not going to be used outside of "
+"[.filename]#kern/subr_taskqueue.c#."
+msgstr ""
+"Интерфейс `taskqueue` имеет две основные блокировки, связанные с ним, для "
+"защиты соответствующих общих данных. Мьютекс `taskqueue_queues_mutex` "
+"предназначен для защиты TAILQ `taskqueue_queues`. Другая блокировка "
+"мьютекса, связанная с этой системой, находится в структуре данных `struct "
+"taskqueue`. Использование примитива синхронизации здесь необходимо для "
+"защиты целостности данных в `struct taskqueue`. Следует отметить, что нет "
+"отдельных макросов, помогающих пользователю заблокировать свою собственную "
+"работу, поскольку эти блокировки, скорее всего, не будут использоваться за "
+"пределами [.filename]#kern/subr_taskqueue.c#."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:237
+#, no-wrap
+msgid "Implementation Notes"
+msgstr "Заметки о реализации"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:239
+#, no-wrap
+msgid "Sleep Queues"
+msgstr "Очереди сна"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:242
+msgid ""
+"A sleep queue is a structure that holds the list of threads asleep on a wait "
+"channel. Each thread that is not asleep on a wait channel carries a sleep "
+"queue structure around with it. When a thread blocks on a wait channel, it "
+"donates its sleep queue structure to that wait channel. Sleep queues "
+"associated with a wait channel are stored in a hash table."
+msgstr ""
+"Очередь сна — это структура, которая содержит список потоков, ожидающих на "
+"канале ожидания. Каждый поток, который не находится в состоянии ожидания на "
+"канале ожидания, хранит структуру очереди сна при себе. Когда поток "
+"блокируется на канале ожидания, он передаёт свою структуру очереди сна этому "
+"каналу. Очереди сна, связанные с каналом ожидания, хранятся в хеш-таблице."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:244
+msgid ""
+"The sleep queue hash table holds sleep queues for wait channels that have at "
+"least one blocked thread. Each entry in the hash table is called a "
+"sleepqueue chain. The chain contains a linked list of sleep queues and a "
+"spin mutex. The spin mutex protects the list of sleep queues as well as the "
+"contents of the sleep queue structures on the list. Only one sleep queue is "
+"associated with a given wait channel. If multiple threads block on a wait "
+"channel than the sleep queues associated with all but the first thread are "
+"stored on a list of free sleep queues in the master sleep queue. When a "
+"thread is removed from the sleep queue it is given one of the sleep queue "
+"structures from the master queue's free list if it is not the only thread "
+"asleep on the queue. The last thread is given the master sleep queue when it "
+"is resumed. Since threads may be removed from the sleep queue in a different "
+"order than they are added, a thread may depart from a sleep queue with a "
+"different sleep queue structure than the one it arrived with."
+msgstr ""
+"Хеш-таблица очередей сна содержит очереди сна для каналов ожидания, у "
+"которых есть хотя бы один заблокированный поток. Каждая запись в хеш-таблице "
+"называется цепочкой очереди сна. Цепочка содержит связанный список очередей "
+"сна и спин-мьютекс. Спин-мьютекс защищает список очередей сна, а также "
+"содержимое структур очередей сна в списке. С каждым каналом ожидания связана "
+"только одна очередь сна. Если несколько потоков блокируются на одном канале "
+"ожидания, то очереди сна, связанные со всеми потоками, кроме первого, "
+"хранятся в списке свободных очередей сна в главной очереди сна. Когда поток "
+"удаляется из очереди сна, он получает одну из структур очереди сна из "
+"свободного списка главной очереди, если он не является единственным потоком "
+"в очереди. Последний поток получает главную очередь сна при возобновлении. "
+"Поскольку потоки могут удаляться из очереди сна в порядке, отличном от "
+"порядка добавления, поток может покинуть очередь сна с другой структурой "
+"очереди сна, чем та, с которой он в неё попал."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:246
+msgid ""
+"The `sleepq_lock` function locks the spin mutex of the sleep queue chain "
+"that maps to a specific wait channel. The `sleepq_lookup` function looks in "
+"the hash table for the master sleep queue associated with a given wait "
+"channel. If no master sleep queue is found, it returns `NULL`. The "
+"`sleepq_release` function unlocks the spin mutex associated with a given "
+"wait channel."
+msgstr ""
+"Функция `sleepq_lock` блокирует спин-мьютес цепи очереди сна, "
+"соответствующей определённому каналу ожидания. Функция `sleepq_lookup` "
+"выполняет поиск в хеш-таблице главной очереди сна, связанной с заданным "
+"каналом ожидания. Если главная очередь сна не найдена, функция возвращает "
+"`NULL`. Функция `sleepq_release` разблокирует спин-мьютес, связанный с "
+"заданным каналом ожидания."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:248
+msgid ""
+"A thread is added to a sleep queue via the `sleepq_add`. This function "
+"accepts the wait channel, a pointer to the mutex that protects the wait "
+"channel, a wait message description string, and a mask of flags. The sleep "
+"queue chain should be locked via `sleepq_lock` before this function is "
+"called. If no mutex protects the wait channel (or it is protected by Giant), "
+"then the mutex pointer argument should be `NULL`. The flags argument "
+"contains a type field that indicates the kind of sleep queue that the thread "
+"is being added to and a flag to indicate if the sleep is interruptible "
+"(`SLEEPQ_INTERRUPTIBLE`). Currently there are only two types of sleep "
+"queues: traditional sleep queues managed via the `msleep` and `wakeup` "
+"functions (`SLEEPQ_MSLEEP`) and condition variable sleep queues "
+"(`SLEEPQ_CONDVAR`). The sleep queue type and lock pointer argument are used "
+"solely for internal assertion checking. Code that calls `sleepq_add` should "
+"explicitly unlock any interlock protecting the wait channel after the "
+"associated sleepqueue chain has been locked via `sleepq_lock` and before "
+"blocking on the sleep queue via one of the waiting functions."
+msgstr ""
+"Поток добавляется в очередь ожидания с помощью `sleepq_add`. Эта функция "
+"принимает канал ожидания, указатель на мьютекс, защищающий канал ожидания, "
+"строку описания сообщения ожидания и маску флагов. Цепь очереди ожидания "
+"должна быть заблокирована с помощью `sleepq_lock` перед вызовом этой "
+"функции. Если канал ожидания не защищен мьютексом (или защищен мьютексом "
+"Giant), то аргумент указателя на мьютекс должен быть `NULL`. Аргумент флагов "
+"содержит поле типа, указывающее на вид очереди ожидания, в которую "
+"добавляется поток, и флаг, указывающий, является ли ожидание прерываемым "
+"(`SLEEPQ_INTERRUPTIBLE`). В настоящее время существует только два типа "
+"очередей ожидания: традиционные очереди, управляемые через функции `msleep` "
+"и `wakeup` (`SLEEPQ_MSLEEP`), и очереди ожидания условных переменных "
+"(`SLEEPQ_CONDVAR`). Тип очереди ожидания и аргумент указателя на блокировку "
+"используются исключительно для внутренних проверок утверждений. Код, "
+"вызывающий `sleepq_add`, должен явно разблокировать любой блокировочный "
+"механизм, защищающий канал ожидания, после того как связанная цепь очереди "
+"ожидания будет заблокирована через `sleepq_lock` и перед блокировкой в "
+"очереди ожидания с помощью одной из функций ожидания."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:250
+msgid ""
+"A timeout for a sleep is set by invoking `sleepq_set_timeout`. The function "
+"accepts the wait channel and the timeout time as a relative tick count as "
+"its arguments. If a sleep should be interrupted by arriving signals, the "
+"`sleepq_catch_signals` function should be called as well. This function "
+"accepts the wait channel as its only parameter. If there is already a signal "
+"pending for this thread, then `sleepq_catch_signals` will return a signal "
+"number; otherwise, it will return 0."
+msgstr ""
+"Таймаут для сна устанавливается вызовом `sleepq_set_timeout`. Функция "
+"принимает канал ожидания и время таймаута в виде относительного количества "
+"тиков в качестве аргументов. Если сон должен быть прерван поступающими "
+"сигналами, следует также вызвать функцию `sleepq_catch_signals`. Эта функция "
+"принимает канал ожидания в качестве единственного параметра. Если для "
+"данного потока уже есть ожидающий сигнал, то `sleepq_catch_signals` вернёт "
+"номер сигнала; в противном случае она вернёт 0."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:252
+msgid ""
+"Once a thread has been added to a sleep queue, it blocks using one of the "
+"`sleepq_wait` functions. There are four wait functions depending on whether "
+"or not the caller wishes to use a timeout or have the sleep aborted by "
+"caught signals or an interrupt from the userland thread scheduler. The "
+"`sleepq_wait` function simply waits until the current thread is explicitly "
+"resumed by one of the wakeup functions. The `sleepq_timedwait` function "
+"waits until either the thread is explicitly resumed or the timeout set by an "
+"earlier call to `sleepq_set_timeout` expires. The `sleepq_wait_sig` function "
+"waits until either the thread is explicitly resumed or its sleep is aborted. "
+"The `sleepq_timedwait_sig` function waits until either the thread is "
+"explicitly resumed, the timeout set by an earlier call to "
+"`sleepq_set_timeout` expires, or the thread's sleep is aborted. All of the "
+"wait functions accept the wait channel as their first parameter. In "
+"addition, the `sleepq_timedwait_sig` function accepts a second boolean "
+"parameter to indicate if the earlier call to `sleepq_catch_signals` found a "
+"pending signal."
+msgstr ""
+"После добавления потока в очередь ожидания он блокируется с использованием "
+"одной из функций `sleepq_wait`. Существует четыре функции ожидания в "
+"зависимости от того, хочет ли вызывающий код использовать таймаут, "
+"прерывание сна перехваченными сигналами или прерывание от планировщика "
+"потоков пользовательского пространства. Функция `sleepq_wait` просто "
+"ожидает, пока текущий поток не будет явно возобновлён одной из функций "
+"пробуждения. Функция `sleepq_timedwait` ожидает, пока поток не будет явно "
+"возобновлён или пока не истечёт таймаут, установленный предыдущим вызовом "
+"`sleepq_set_timeout`. Функция `sleepq_wait_sig` ожидает, пока поток не будет "
+"явно возобновлён или его сон не будет прерван. Функция "
+"`sleepq_timedwait_sig` ожидает, пока поток не будет явно возобновлён, не "
+"истечёт таймаут, установленный предыдущим вызовом `sleepq_set_timeout`, или "
+"сон потока не будет прерван. Все функции ожидания принимают канал ожидания в "
+"качестве первого параметра. Кроме того, функция `sleepq_timedwait_sig` "
+"принимает второй логический параметр, указывающий, обнаружил ли предыдущий "
+"вызов `sleepq_catch_signals` ожидающий сигнал."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:254
+msgid ""
+"If the thread is explicitly resumed or is aborted by a signal, then a value "
+"of zero is returned by the wait function to indicate a successful sleep. If "
+"the thread is resumed by either a timeout or an interrupt from the userland "
+"thread scheduler then an appropriate errno value is returned instead. Note "
+"that since `sleepq_wait` can only return 0 it does not return anything and "
+"the caller should assume a successful sleep. Also, if a thread's sleep times "
+"out and is aborted simultaneously then `sleepq_timedwait_sig` will return an "
+"error indicating that a timeout occurred. If an error value of 0 is returned "
+"and either `sleepq_wait_sig` or `sleepq_timedwait_sig` was used to block, "
+"then the function `sleepq_calc_signal_retval` should be called to check for "
+"any pending signals and calculate an appropriate return value if any are "
+"found. The signal number returned by the earlier call to "
+"`sleepq_catch_signals` should be passed as the sole argument to "
+"`sleepq_calc_signal_retval`."
+msgstr ""
+"Если поток явно возобновлен или прерван сигналом, функция ожидания "
+"возвращает ноль, указывая на успешное завершение сна. Если поток возобновлен "
+"по таймауту или прерыванию от планировщика потоков в пользовательском "
+"пространстве, вместо этого возвращается соответствующее значение errno. "
+"Обратите внимание, что поскольку `sleepq_wait` может возвращать только 0, "
+"она ничего не возвращает, и вызывающая сторона должна считать сон успешным. "
+"Также, если сон потока прерывается одновременно по таймауту и другим "
+"причинам, `sleepq_timedwait_sig` вернет ошибку, указывающую на срабатывание "
+"таймаута. Если возвращено значение ошибки 0 и для блокировки использовались "
+"`sleepq_wait_sig` или `sleepq_timedwait_sig`, следует вызвать функцию "
+"`sleepq_calc_signal_retval` для проверки ожидающих сигналов и вычисления "
+"соответствующего возвращаемого значения, если таковые обнаружены. Номер "
+"сигнала, полученный при предыдущем вызове `sleepq_catch_signals`, должен "
+"быть передан в качестве единственного аргумента в "
+"`sleepq_calc_signal_retval`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:256
+msgid ""
+"Threads asleep on a wait channel are explicitly resumed by the "
+"`sleepq_broadcast` and `sleepq_signal` functions. Both functions accept the "
+"wait channel from which to resume threads, a priority to raise resumed "
+"threads to, and a flags argument to indicate which type of sleep queue is "
+"being resumed. The priority argument is treated as a minimum priority. If a "
+"thread being resumed already has a higher priority (numerically lower) than "
+"the priority argument then its priority is not adjusted. The flags argument "
+"is used for internal assertions to ensure that sleep queues are not being "
+"treated as the wrong type. For example, the condition variable functions "
+"should not resume threads on a traditional sleep queue. The "
+"`sleepq_broadcast` function resumes all threads that are blocked on the "
+"specified wait channel while `sleepq_signal` only resumes the highest "
+"priority thread blocked on the wait channel. The sleep queue chain should "
+"first be locked via the `sleepq_lock` function before calling these "
+"functions."
+msgstr ""
+"Потоки, находящиеся в состоянии ожидания на канале ожидания, явно "
+"возобновляются функциями `sleepq_broadcast` и `sleepq_signal`. Обе функции "
+"принимают канал ожидания, с которого нужно возобновить потоки, приоритет, на "
+"который нужно поднять возобновлённые потоки, и аргумент флагов, указывающий "
+"тип очереди ожидания, которую нужно возобновить. Аргумент приоритета "
+"трактуется как минимальный приоритет. Если у возобновляемого потока уже есть "
+"более высокий приоритет (численно меньший), чем указанный в аргументе, его "
+"приоритет не изменяется. Аргумент флагов используется для внутренних "
+"проверок, чтобы гарантировать, что очереди ожидания не обрабатываются как "
+"неправильный тип. Например, функции условных переменных не должны "
+"возобновлять потоки на традиционной очереди ожидания. Функция "
+"`sleepq_broadcast` возобновляет все потоки, заблокированные на указанном "
+"канале ожидания, тогда как `sleepq_signal` возобновляет только поток с "
+"наивысшим приоритетом, заблокированный на канале ожидания. Перед вызовом "
+"этих функций цепочка очереди ожидания должна быть заблокирована с помощью "
+"функции `sleepq_lock`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:258
+msgid ""
+"A sleeping thread may have its sleep interrupted by calling the "
+"`sleepq_abort` function. This function must be called with `sched_lock` held "
+"and the thread must be queued on a sleep queue. A thread may also be removed "
+"from a specific sleep queue via the `sleepq_remove` function. This function "
+"accepts both a thread and a wait channel as an argument and only awakens the "
+"thread if it is on the sleep queue for the specified wait channel. If the "
+"thread is not on a sleep queue or it is on a sleep queue for a different "
+"wait channel, then this function does nothing."
+msgstr ""
+"Спящий поток может быть прерван с помощью вызова функции `sleepq_abort`. Эта "
+"функция должна вызываться с удержанием `sched_lock`, а поток должен "
+"находиться в очереди сна. Поток также может быть удалён из определённой "
+"очереди сна с помощью функции `sleepq_remove`. Эта функция принимает как "
+"поток, так и канал ожидания в качестве аргументов и пробуждает поток только "
+"в том случае, если он находится в очереди сна для указанного канала "
+"ожидания. Если поток не находится в очереди сна или находится в очереди сна "
+"для другого канала ожидания, эта функция ничего не делает."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:259
+#, no-wrap
+msgid "Turnstiles"
+msgstr "Турникеты"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:262
+msgid "Compare/contrast with sleep queues."
+msgstr "Сравнение/сопоставление с очередями сна."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:264
+msgid "Lookup/wait/release. - Describe TDF_TSNOBLOCK race."
+msgstr "Поиск/ожидание/освобождение. - Описать состояние гонки TDF_TSNOBLOCK."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:266
+msgid "Priority propagation."
+msgstr "Приоритетное распространение."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:267
+#, no-wrap
+msgid "Details of the Mutex Implementation"
+msgstr "Подробности реализации мьютекса"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:270
+msgid ""
+"Should we require mutexes to be owned for mtx_destroy() since we can not "
+"safely assert that they are unowned by anyone else otherwise?"
+msgstr ""
+"Должны ли мы требовать владения мьютексами для `mtx_destroy()`, так как "
+"иначе мы не можем безопасно утверждать, что они не принадлежат кому-либо ещё?"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:271
+#, no-wrap
+msgid "Spin Mutexes"
+msgstr "Вращающиеся мьютексы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:274
+msgid "Use a critical section..."
+msgstr "Использование критической секции..."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:275
+#, no-wrap
+msgid "Sleep Mutexes"
+msgstr "Мьютексы сна (Sleep Mutexes)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:278
+msgid "Describe the races with contested mutexes"
+msgstr "Опишите гонки с оспариваемыми мьютексами"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:280
+msgid ""
+"Why it is safe to read mtx_lock of a contested mutex when holding the "
+"turnstile chain lock."
+msgstr ""
+"Почему безопасно читать mtx_lock оспариваемой мьютекса при удержании "
+"блокировки цепочки турникета."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:281
+#, no-wrap
+msgid "Witness"
+msgstr "Witness"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:284
+msgid "What does it do"
+msgstr "Что это делает"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:286
+msgid "How does it work"
+msgstr "Как это работает"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:288
+#, no-wrap
+msgid "Miscellaneous Topics"
+msgstr "Разные темы"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:290
+#, no-wrap
+msgid "Interrupt Source and ICU Abstractions"
+msgstr "Источники прерываний и абстракции ICU"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:293
+msgid "struct isrc"
+msgstr "struct isrc"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:295
+msgid "pic drivers"
+msgstr "pic драйверы"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:296
+#, no-wrap
+msgid "Other Random Questions/Topics"
+msgstr "Другие случайные вопросы/темы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:299
+msgid "Should we pass an interlock into `sema_wait`?"
+msgstr "Передавать ли блокировку в `sema_wait`?"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:301
+msgid "Should we have non-sleepable sx locks?"
+msgstr "Должны ли мы иметь sx блокировки без возможности сна?"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:303
+msgid "Add some info about proper use of reference counts."
+msgstr ""
+"Добавить некоторую информацию о правильном использовании счетчиков ссылок."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:308
+#, no-wrap
+msgid "Glossary"
+msgstr "Глоссарий"
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:311
+#, no-wrap
+msgid "atomic"
+msgstr "atomic (атомарный)"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:313
+msgid ""
+"An operation is atomic if all of its effects are visible to other CPUs "
+"together when the proper access protocol is followed. In the degenerate case "
+"are atomic instructions provided directly by machine architectures. At a "
+"higher level, if several members of a structure are protected by a lock, "
+"then a set of operations are atomic if they are all performed while holding "
+"the lock without releasing the lock in between any of the operations."
+msgstr ""
+"Операция является атомарной, если все её эффекты видны другим процессорам "
+"одновременно при соблюдении соответствующего протокола доступа. В простейшем "
+"случае атомарные инструкции предоставляются непосредственно архитектурой "
+"процессора. На более высоком уровне, если несколько элементов структуры "
+"защищены блокировкой, то набор операций является атомарным, если все они "
+"выполняются при удержании блокировки без её освобождения между любыми из "
+"операций."
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:315
+msgid "See Also operation."
+msgstr "См. также operation (операция)."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:316
+#, no-wrap
+msgid "block"
+msgstr "block (блокировать)"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:318
+msgid ""
+"A thread is blocked when it is waiting on a lock, resource, or condition. "
+"Unfortunately this term is a bit overloaded as a result."
+msgstr ""
+"Поток блокируется, когда он ожидает блокировку, ресурс или условие. К "
+"сожалению, этот термин в результате немного перегружен."
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:320
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:357
+msgid "See Also sleep."
+msgstr "См. также sleep (спать)."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:321
+#, no-wrap
+msgid "critical section"
+msgstr "critical section (критическая секция)"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:323
+msgid ""
+"A section of code that is not allowed to be preempted. A critical section is "
+"entered and exited using the man:critical_enter[9] API."
+msgstr ""
+"Фрагмент кода, который не может быть вытеснен. Критическая секция начинается "
+"и завершается с использованием API man:critical_enter[9]."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:324
+#, no-wrap
+msgid "MD"
+msgstr "MD"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:326
+msgid "Machine dependent."
+msgstr "Машинозависимый (machine dependent)."
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:328
+msgid "See Also MI."
+msgstr "Смотри также MI."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:329
+#, no-wrap
+msgid "memory operation"
+msgstr "memory operation (операция с памятью)"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:331
+msgid "A memory operation reads and/or writes to a memory location."
+msgstr "Операция с памятью выполняет чтение и/или запись в ячейку памяти."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:332
+#, no-wrap
+msgid "MI"
+msgstr "MI"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:334
+msgid "Machine independent."
+msgstr "Машинно-независимый (machine independent)."
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:336
+msgid "See Also MD."
+msgstr "См. также MD."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:337
+#, no-wrap
+msgid "operation"
+msgstr "operation (операция)"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:339
+msgid "See memory operation."
+msgstr "См. memory operation (операция с памятью)."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:340
+#, no-wrap
+msgid "primary interrupt context"
+msgstr "primary interrupt context (основной контекст прерывания)"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:342
+msgid ""
+"Primary interrupt context refers to the code that runs when an interrupt "
+"occurs. This code can either run an interrupt handler directly or schedule "
+"an asynchronous interrupt thread to execute the interrupt handlers for a "
+"given interrupt source."
+msgstr ""
+"Основной контекст прерывания относится к коду, который выполняется при "
+"возникновении прерывания. Этот код может либо напрямую запускать обработчик "
+"прерывания, либо планировать выполнение асинхронного потока прерывания для "
+"обработчиков прерываний данного источника."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:343
+#, no-wrap
+msgid "realtime kernel thread"
+msgstr "realtime kernel thread (поток ядра реального времени)"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:345
+msgid ""
+"A high priority kernel thread. Currently, the only realtime priority kernel "
+"threads are interrupt threads."
+msgstr ""
+"Высокоприоритетный поток ядра. В настоящее время единственными потоками ядра "
+"с реальным приоритетом являются потоки обработки прерываний."
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:347
+msgid "See Also thread."
+msgstr "См. также thread (поток)."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:348
+#, no-wrap
+msgid "sleep"
+msgstr "sleep (спать)"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:350
+msgid ""
+"A thread is asleep when it is blocked on a condition variable or a sleep "
+"queue via msleep or tsleep."
+msgstr ""
+"Поток находится в состоянии сна, когда он заблокирован на условной "
+"переменной или в очереди сна через `msleep` или `tsleep`."
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:352
+msgid "See Also block."
+msgstr "См. также block (блокировать)."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:353
+#, no-wrap
+msgid "sleepable lock"
+msgstr "sleepable lock (блокировка с возможностью сна)"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:355
+msgid ""
+"A sleepable lock is a lock that can be held by a thread which is asleep. "
+"Lockmgr locks and sx locks are currently the only sleepable locks in "
+"FreeBSD. Eventually, some sx locks such as the allproc and proctree locks "
+"may become non-sleepable locks."
+msgstr ""
+"блокировка с возможностью сна — это блокировка, которая может удерживаться "
+"потоком, находящимся в состоянии сна. В настоящее время в FreeBSD "
+"единственными блокировками с возможностью сна являются lockmgr и sx. В "
+"будущем некоторые sx-блокировки, такие как allproc и proctree, могут стать "
+"блокировками с возможностью сна."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:358
+#, no-wrap
+msgid "thread"
+msgstr "thread (поток)"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:360
+msgid ""
+"A kernel thread represented by a struct thread. Threads own locks and hold a "
+"single execution context."
+msgstr ""
+"Поток ядра, представленный структурой `struct thread`. Потоки владеют "
+"блокировками и содержат единственный на поток контекст выполнения."
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:361
+#, no-wrap
+msgid "wait channel"
+msgstr "wait channel (канал ожидания)"
+
+#. type: .glosslist
+#: documentation/content/en/books/arch-handbook/smp/_index.adoc:363
+msgid "A kernel virtual address that threads may sleep on."
+msgstr ""
+"Виртуальный адрес ядра, на котором потоки могут переходить в режим ожидания."
diff --git a/documentation/content/ru/books/arch-handbook/sound/_index.adoc b/documentation/content/ru/books/arch-handbook/sound/_index.adoc
new file mode 100644
index 0000000000..7079a9cd52
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/sound/_index.adoc
@@ -0,0 +1,351 @@
+---
+description: 'Звуковая подсистема FreeBSD'
+next: books/arch-handbook/pccard
+params:
+ path: /books/arch-handbook/sound/
+prev: books/arch-handbook/newbus
+showBookMenu: true
+tags: ["Sound", "OSS", "pcm", "mixer"]
+title: 'Глава 15. Звуковая подсистема'
+weight: 17
+---
+
+[[oss]]
+= Звуковая подсистема
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 15
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[oss-intro]]
+== Введение
+
+Подсистема звука FreeBSD чётко разделяет общие вопросы обработки звука и детали, специфичные для устройств. Это упрощает добавление поддержки нового оборудования.
+
+man:pcm[4] — это центральный компонент подсистемы звука. В основном он реализует следующие элементы:
+
+* Интерфейс системных вызовов (read, write, ioctls) для работы с оцифрованным звуком и функциями микшера. Набор команд ioctl совместим с устаревшим интерфейсом _OSS_ или _Voxware_, что позволяет портировать распространённые мультимедийные приложения без изменений.
+* Общий код для обработки звуковых данных (преобразование форматов, виртуальные каналы).
+* Единый программный интерфейс к аппаратно-зависимым модулям аудиоинтерфейсов.
+* Дополнительная поддержка некоторых распространённых аппаратных интерфейсов (ac97) или общий код для специфичного оборудования (например: подпрограммы ISA DMA).
+
+Поддержка конкретных звуковых карт реализована аппаратно-специфичными драйверами, которые предоставляют интерфейсы каналов и микшера для подключения к общему коду [.filename]#pcm#.
+
+В этой главе термин [.filename]#pcm# будет относиться к центральной, общей части звукового драйвера, в отличие от аппаратно-зависимых модулей.
+
+Разработчик драйверов, только начинающий свою разработку, конечно, захочет начать с существующего модуля и использовать его код в качестве основного источника информации. Однако, хотя код подсистемы звука чист и аккуратен, он в основном лишён комментариев. Этот документ пытается дать обзор интерфейса фреймворка и ответить на некоторые вопросы, которые могут возникнуть при адаптации существующего кода.
+
+В качестве альтернативы или в дополнение к началу разработки с примера драйвера из кода системы, вы можете найти шаблон драйвера с комментариями по адресу https://people.FreeBSD.org/~cg/template.c[ https://people.FreeBSD.org/~cg/template.c]
+
+[[oss-files]]
+== Файлы
+
+Весь соответствующий код находится в [.filename]#/usr/src/sys/dev/sound/#, за исключением определений публичного интерфейса ioctl, которые можно найти в [.filename]#/usr/src/sys/sys/soundcard.h#
+
+В каталоге [.filename]#/usr/src/sys/dev/sound/#, папка [.filename]#pcm/# содержит основной код, тогда как каталоги [.filename]#pci/#, [.filename]#isa/# и [.filename]#usb/# содержат драйверы для плат PCI и ISA, а также для USB-аудиоустройств.
+
+[[pcm-probe-and-attach]]
+== Обнаружение, присоединение и т.д.
+
+Драйверы звуковых устройств выполняют обнаружение и подключение почти так же, как и любой модуль драйвера оборудования. Возможно, вам будет полезно ознакомиться с разделами руководства, посвящёнными crossref:isa-driver[isa-driver,ISA] или crossref:pci[pci,PCI], для получения дополнительной информации.
+
+Однако драйверы звука отличаются в некоторых аспектах:
+
+* Они объявляют себя как устройства класса [.filename]#pcm#, с приватной структурой устройства `struct snddev_info`:
++
+[.programlisting]
+....
+ static driver_t xxx_driver = {
+ "pcm",
+ xxx_methods,
+ sizeof(struct snddev_info)
+ };
+
+ DRIVER_MODULE(snd_xxxpci, pci, xxx_driver, pcm_devclass, 0, 0);
+ MODULE_DEPEND(snd_xxxpci, snd_pcm, PCM_MINVER, PCM_PREFVER,PCM_MAXVER);
+....
++
+Большинству звуковых драйверов необходимо хранить дополнительную приватную информацию о своём устройстве. Приватная структура данных обычно выделяется в процедуре attach. Её адрес передаётся в [.filename]#pcm# через вызовы `pcm_register()` и `mixer_init()`. [.filename]#pcm# позже передаёт обратно этот адрес в качестве параметра при вызовах интерфейсов звукового драйвера.
+* Подпрограмма подключения звукового драйвера должна объявить свой интерфейс MIXER или AC97 для [.filename]#pcm#, вызвав `mixer_init()`. Для интерфейса MIXER это, в свою очередь, приводит к вызову crossref:sound[xxxmixer-init,`xxxmixer_init()`].
+* Функция подключения драйвера звука объявляет свою общую конфигурацию CHANNEL для [.filename]#pcm#, вызывая `pcm_register(dev, sc, nplay, nrec)`, где `sc` — это адрес структуры данных устройства, используемый при последующих вызовах из [.filename]#pcm#, а `nplay` и `nrec` — количество каналов воспроизведения и записи.
+* Подпрограмма подключения звукового драйвера объявляет каждый из своих каналов вызовами `pcm_addchan()`. Это настраивает связующий слой канала в [.filename]#pcm# и, в свою очередь, вызывает вызов crossref:sound[xxxchannel-init,`xxxchannel_init()`].
+* Драйвер звука должен вызвать `pcm_unregister()` в процедуре отключения перед освобождением своих ресурсов.
+
+Существует два возможных способа работы с устройствами, не поддерживающими PnP:
+
+* Используйте метод `device_identify()` (пример: [.filename]#sound/isa/es1888.c#). Метод `device_identify()` проверяет наличие оборудования по известным адресам и, если находит поддерживаемое устройство, создает новое pcm-устройство, которое затем передается для probe/attach.
+* Используйте пользовательскую конфигурацию ядра с соответствующими подсказками для устройств pcm (пример: [.filename]#sound/isa/mss.c#).
+
+[.filename]#pcm# драйверы должны реализовывать подпрограммы `device_suspend`, `device_resume` и `device_shutdown`, чтобы управление питанием и выгрузка модулей работали корректно.
+
+[[oss-interfaces]]
+== Интерфейсы
+
+Интерфейс между ядром [.filename]#pcm# и звуковыми драйверами определяется в терминах crossref:kobj[kernel-objects,объектов ядра Kobj].
+
+Существует два основных интерфейса, которые обычно предоставляет драйвер звука: _CHANNEL_ и либо _MIXER_, либо _AC97_.
+
+Интерфейс _AC97_ — это очень небольшой интерфейс доступа к оборудованию (чтение/запись регистров), реализованный драйверами для устройств с кодеком AC97. В этом случае фактический интерфейс MIXER предоставляется общим кодом AC97 в [.filename]#pcm#.
+
+=== Интерфейс CHANNEL
+
+==== Общие примечания для параметров функций
+
+Драйверы звука обычно имеют приватную структуру данных для описания своего устройства и по одной структуре для каждого канала воспроизведения и записи, который они поддерживают.
+
+Для всех функций интерфейса CHANNEL первый параметр — это непрозрачный указатель.
+
+Второй параметр представляет собой указатель на приватную структуру данных канала, за исключением `channel_init()`, где передается указатель на приватную структуру устройства (и возвращается указатель на канал для дальнейшего использования [.filename]#pcm#).
+
+==== Обзор операций передачи данных
+
+Для надежной передачи звуковых данных ядро [.filename]#pcm# и драйверы звука взаимодействуют через общую область памяти, описываемую структурой `struct snd_dbuf`.
+
+`struct snd_dbuf` является приватной для [.filename]#pcm#, и драйверы звука получают нужные значения через вызовы функций доступа (`sndbuf_getxxx()`).
+
+Область разделяемой памяти имеет размер `sndbuf_getsize()` и разделена на блоки фиксированного размера по `sndbuf_getblksz()` байт.
+
+При воспроизведении общий механизм передачи выглядит следующим образом (для записи идея обратная):
+
+* [.filename]#pcm# сначала заполняет буфер, затем вызывает функцию crossref:sound[channel-trigger,`xxxchannel_trigger()`] драйвера звука с параметром PCMTRIG_START.
+* Звуковой драйвер затем организует повторяющуюся передачу всей области памяти (`sndbuf_getbuf()`, `sndbuf_getsize()`) на устройство блоками по `sndbuf_getblksz()` байт. Для каждого переданного блока он вызывает функцию `chn_intr()`[.filename]#pcm# (обычно это происходит во время прерывания).
+* `chn_intr()` организует копирование новых данных в область, которая была передана устройству (теперь свободна), и вносит соответствующие обновления в структуру `snd_dbuf`.
+
+[[xxxchannel-init]]
+==== channel_init
+
+`xxxchannel_init()` вызывается для инициализации каждого из каналов воспроизведения или записи. Вызовы инициируются из процедуры присоединения драйвера звука. (См. раздел crossref:sound[pcm-probe-and-attach,зондирование и присоединение]).
+
+[.programlisting]
+....
+ static void *
+ xxxchannel_init(kobj_t obj, void *data,
+ struct snd_dbuf *b, struct pcm_channel *c, int dir) <.>
+ {
+ struct xxx_info *sc = data;
+ struct xxx_chinfo *ch;
+ ...
+ return ch; <.>
+ }
+....
+
+<.> `b` — это адрес для канала `struct snd_dbuf`. Он должен быть инициализирован в функции вызовом `sndbuf_alloc()`. Размер буфера, который следует использовать, обычно представляет собой небольшое кратное от 'типичного' размера единицы передачи данных для вашего устройства. `c` — это указатель на структуру управления каналом [.filename]#pcm#. Это непрозрачный объект. Функция должна сохранить его в локальной структуре канала для использования в последующих вызовах [.filename]#pcm# (например: `chn_intr(c)`). `dir` указывает направление канала (`PCMDIR_PLAY` или `PCMDIR_REC`).
+
+<.> Функция должна возвращать указатель на приватную область, используемую для управления этим каналом. Этот указатель будет передаваться в качестве параметра при других вызовах интерфейса канала.
+
+==== channel_setformat
+
+`xxxchannel_setformat()` должен настроить оборудование для указанного канала под указанный звуковой формат.
+
+[.programlisting]
+....
+ static int
+ xxxchannel_setformat(kobj_t obj, void *data, u_int32_t format) <.>
+ {
+ struct xxx_chinfo *ch = data;
+ ...
+ return 0;
+ }
+....
+
+<.> `format` указывается как значение `AFMT_XXX` ([.filename]#soundcard.h#).
+
+==== channel_setspeed
+
+`xxxchannel_setspeed()` настраивает оборудование канала для указанной скорости дискретизации и возвращает возможно скорректированную скорость.
+
+[.programlisting]
+....
+ static int
+ xxxchannel_setspeed(kobj_t obj, void *data, u_int32_t speed)
+ {
+ struct xxx_chinfo *ch = data;
+ ...
+ return speed;
+ }
+....
+
+==== channel_setblocksize
+
+`xxxchannel_setblocksize()` устанавливает размер блока, который является размером единичных транзакций между [.filename]#pcm# и звуковым драйвером, а также между звуковым драйвером и устройством. Обычно это количество байт, передаваемых до возникновения прерывания. Во время передачи звуковой драйвер должен вызывать `chn_intr()` из [.filename]#pcm# каждый раз, когда передается данный размер.
+
+Большинство драйверов звука здесь учитывают только размер блока, который будет использоваться при начале фактической передачи.
+
+[.programlisting]
+....
+ static int
+ xxxchannel_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
+ {
+ struct xxx_chinfo *ch = data;
+ ...
+ return blocksize; <.>
+ }
+....
+
+<.> Функция возвращает, возможно, скорректированный размер блока. Если размер блока действительно изменён, следует вызвать `sndbuf_resize()` для корректировки буфера.
+
+[[channel-trigger]]
+==== channel_trigger
+
+`xxxchannel_trigger()` вызывается [.filename]#pcm# для управления операциями передачи данных в драйвере.
+
+[.programlisting]
+....
+ static int
+ xxxchannel_trigger(kobj_t obj, void *data, int go) <.>
+ {
+ struct xxx_chinfo *ch = data;
+ ...
+ return 0;
+ }
+....
+
+<.> `go` определяет действие для текущего вызова. Возможные значения:
+
+[NOTE]
+====
+Если драйвер использует ISA DMA, перед выполнением действий с устройством следует вызвать `sndbuf_isadma()`, которая позаботится о том, что делает DMA-чип.
+====
+
+==== channel_getptr
+
+`xxxchannel_getptr()` возвращает текущее смещение в буфере передачи. Обычно этот вызов выполняется функцией `chn_intr()`, и именно так [.filename]#pcm# узнаёт, куда можно передавать новые данные.
+
+==== channel_free
+
+`xxxchannel_free()` вызывается для освобождения ресурсов канала, например, при выгрузке драйвера, и должна быть реализована, если структуры данных канала динамически выделены или если `sndbuf_alloc()` не использовалась для выделения буфера.
+
+==== channel_getcaps
+
+[.programlisting]
+....
+ struct pcmchan_caps *
+ xxxchannel_getcaps(kobj_t obj, void *data)
+ {
+ return &xxx_caps; <.>
+ }
+....
+
+<.> Подпрограмма возвращает указатель на (обычно статически определённую) структуру `pcmchan_caps` (определена в [.filename]#sound/pcm/channel.h#). Эта структура содержит минимальную и максимальную частоты дискретизации, а также поддерживаемые звуковые форматы. Пример можно найти в любом драйвере звукового устройства.
+
+==== Дополнительные функции
+
+`channel_reset()`, `channel_resetdone()` и `channel_notify()` предназначены для специальных целей и не должны реализовываться в драйвере без обсуждения на {freebsd-multimedia}.
+
+`channel_setdir()` устарела.
+
+=== Интерфейс MIXER
+
+[[xxxmixer-init]]
+==== mixer_init
+
+`xxxmixer_init()` инициализирует оборудование и сообщает [.filename]#pcm#, какие устройства микшера доступны для воспроизведения и записи
+
+[.programlisting]
+....
+ static int
+ xxxmixer_init(struct snd_mixer *m)
+ {
+ struct xxx_info *sc = mix_getdevinfo(m);
+ u_int32_t v;
+
+ [Initialize hardware]
+
+ [Set appropriate bits in v for play mixers] <.>
+ mix_setdevs(m, v);
+ [Set appropriate bits in v for record mixers]
+ mix_setrecdevs(m, v)
+
+ return 0;
+ }
+....
+
+<.> Установите биты в целочисленном значении и вызовите `mix_setdevs()` и `mix_setrecdevs()`, чтобы сообщить [.filename]#pcm#, какие устройства существуют.
+
+Определения битов микшера можно найти в [.filename]#soundcard.h# (значения `SOUND_MASK_XXX` и сдвиги битов `SOUND_MIXER_XXX`).
+
+==== mixer_set
+
+`xxxmixer_set()` устанавливает уровень громкости для одного устройства микшера.
+
+[.programlisting]
+....
+ static int
+ xxxmixer_set(struct snd_mixer *m, unsigned dev,
+ unsigned left, unsigned right) <.>
+ {
+ struct sc_info *sc = mix_getdevinfo(m);
+ [set volume level]
+ return left | (right << 8); <.>
+ }
+....
+
+<.> Устройство указывается как значение `SOUND_MIXER_XXX`. Значения громкости задаются в диапазоне [0-100]. Значение ноль должно отключать звук устройства.
+<.> Поскольку уровни оборудования, вероятно, не совпадут с входной шкалой и будет происходить округление, процедура возвращает фактические значения уровней (в диапазоне 0-100), как показано.
+
+==== mixer_setrecsrc
+
+`xxxmixer_setrecsrc()` устанавливает устройство источника записи.
+
+[.programlisting]
+....
+ static int
+ xxxmixer_setrecsrc(struct snd_mixer *m, u_int32_t src) <.>
+ {
+ struct xxx_info *sc = mix_getdevinfo(m);
+
+ [look for non zero bit(s) in src, set up hardware]
+
+ [update src to reflect actual action]
+ return src; <.>
+ }
+....
+
+<.> Желаемые устройства записи указываются в виде битового поля
+<.> Возвращаются фактические устройства, настроенные для записи. Некоторые драйверы могут настраивать только одно устройство для записи. Функция должна возвращать -1 в случае ошибки.
+
+==== mixer_uninit, mixer_reinit
+
+`xxxmixer_uninit()` должен гарантировать, что весь звук отключен, и, если возможно, аппаратный микшер должен быть переведен в режим пониженного энергопотребления.
+
+`xxxmixer_reinit()` должна гарантировать, что аппаратура микшера включена и все настройки, не управляемые `mixer_set()` или `mixer_setrecsrc()`, восстановлены.
+
+=== Интерфейс AC97
+
+Интерфейс _AC97_ реализован драйверами с кодеком AC97. У него есть только три метода:
+
+* `xxxac97_init()` возвращает количество найденных кодеков ac97.
+* `ac97_read()` и `ac97_write()` читают или записывают указанный регистр.
+
+Интерфейс _AC97_ используется кодом AC97 в [.filename]#pcm# для выполнения операций более высокого уровня. В качестве примера можно посмотреть [.filename]#sound/pci/maestro3.c# или другие файлы в [.filename]#sound/pci/#.
diff --git a/documentation/content/ru/books/arch-handbook/sound/_index.po b/documentation/content/ru/books/arch-handbook/sound/_index.po
new file mode 100644
index 0000000000..7a627217f4
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/sound/_index.po
@@ -0,0 +1,1125 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-08-26 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbooksound_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:1
+#, no-wrap
+msgid "FreeBSD Sound Subsystem"
+msgstr "Звуковая подсистема FreeBSD"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:1
+#, no-wrap
+msgid "Chapter 15. Sound Subsystem"
+msgstr "Глава 15. Звуковая подсистема"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:14
+#, no-wrap
+msgid "Sound Subsystem"
+msgstr "Звуковая подсистема"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:52
+#, no-wrap
+msgid "Introduction"
+msgstr "Введение"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:55
+msgid ""
+"The FreeBSD sound subsystem cleanly separates generic sound handling issues "
+"from device-specific ones. This makes it easier to add support for new "
+"hardware."
+msgstr ""
+"Подсистема звука FreeBSD чётко разделяет общие вопросы обработки звука и "
+"детали, специфичные для устройств. Это упрощает добавление поддержки нового "
+"оборудования."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:57
+msgid ""
+"The man:pcm[4] framework is the central piece of the sound subsystem. It "
+"mainly implements the following elements:"
+msgstr ""
+"man:pcm[4] — это центральный компонент подсистемы звука. В основном он "
+"реализует следующие элементы:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:59
+msgid ""
+"A system call interface (read, write, ioctls) to digitized sound and mixer "
+"functions. The ioctl command set is compatible with the legacy _OSS_ or "
+"_Voxware_ interface, allowing common multimedia applications to be ported "
+"without modification."
+msgstr ""
+"Интерфейс системных вызовов (read, write, ioctls) для работы с оцифрованным "
+"звуком и функциями микшера. Набор команд ioctl совместим с устаревшим "
+"интерфейсом _OSS_ или _Voxware_, что позволяет портировать распространённые "
+"мультимедийные приложения без изменений."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:60
+msgid ""
+"Common code for processing sound data (format conversions, virtual channels)."
+msgstr ""
+"Общий код для обработки звуковых данных (преобразование форматов, "
+"виртуальные каналы)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:61
+msgid ""
+"A uniform software interface to hardware-specific audio interface modules."
+msgstr ""
+"Единый программный интерфейс к аппаратно-зависимым модулям аудиоинтерфейсов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:62
+msgid ""
+"Additional support for some common hardware interfaces (ac97), or shared "
+"hardware-specific code (ex: ISA DMA routines)."
+msgstr ""
+"Дополнительная поддержка некоторых распространённых аппаратных интерфейсов "
+"(ac97) или общий код для специфичного оборудования (например: подпрограммы "
+"ISA DMA)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:64
+msgid ""
+"The support for specific sound cards is implemented by hardware-specific "
+"drivers, which provide channel and mixer interfaces to plug into the generic "
+"[.filename]#pcm# code."
+msgstr ""
+"Поддержка конкретных звуковых карт реализована аппаратно-специфичными "
+"драйверами, которые предоставляют интерфейсы каналов и микшера для "
+"подключения к общему коду [.filename]#pcm#."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:66
+msgid ""
+"In this chapter, the term [.filename]#pcm# will refer to the central, common "
+"part of the sound driver, as opposed to the hardware-specific modules."
+msgstr ""
+"В этой главе термин [.filename]#pcm# будет относиться к центральной, общей "
+"части звукового драйвера, в отличие от аппаратно-зависимых модулей."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:68
+msgid ""
+"The prospective driver writer will of course want to start from an existing "
+"module and use the code as the ultimate reference. But, while the sound code "
+"is nice and clean, it is also mostly devoid of comments. This document tries "
+"to give an overview of the framework interface and answer some questions "
+"that may arise while adapting the existing code."
+msgstr ""
+"Разработчик драйверов, только начинающий свою разработку, конечно, захочет "
+"начать с существующего модуля и использовать его код в качестве основного "
+"источника информации. Однако, хотя код подсистемы звука чист и аккуратен, он "
+"в основном лишён комментариев. Этот документ пытается дать обзор интерфейса "
+"фреймворка и ответить на некоторые вопросы, которые могут возникнуть при "
+"адаптации существующего кода."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:70
+msgid ""
+"As an alternative, or in addition to starting from a working example, you "
+"can find a commented driver template at https://people.FreeBSD.org/~cg/"
+"template.c[ https://people.FreeBSD.org/~cg/template.c]"
+msgstr ""
+"В качестве альтернативы или в дополнение к началу разработки с примера "
+"драйвера из кода системы, вы можете найти шаблон драйвера с комментариями по "
+"адресу https://people.FreeBSD.org/~cg/template.c[ https://people.FreeBSD.org/"
+"~cg/template.c]"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:72
+#, no-wrap
+msgid "Files"
+msgstr "Файлы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:75
+msgid ""
+"All the relevant code lives in [.filename]#/usr/src/sys/dev/sound/#, except "
+"for the public ioctl interface definitions, found in [.filename]#/usr/src/"
+"sys/sys/soundcard.h#"
+msgstr ""
+"Весь соответствующий код находится в [.filename]#/usr/src/sys/dev/sound/#, "
+"за исключением определений публичного интерфейса ioctl, которые можно найти "
+"в [.filename]#/usr/src/sys/sys/soundcard.h#"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:77
+msgid ""
+"Under [.filename]#/usr/src/sys/dev/sound/#, the [.filename]#pcm/# directory "
+"holds the central code, while the [.filename]#pci/#, [.filename]#isa/# and "
+"[.filename]#usb/# directories have the drivers for PCI and ISA boards, and "
+"for USB audio devices."
+msgstr ""
+"В каталоге [.filename]#/usr/src/sys/dev/sound/#, папка [.filename]#pcm/# "
+"содержит основной код, тогда как каталоги [.filename]#pci/#, [.filename]#isa/"
+"# и [.filename]#usb/# содержат драйверы для плат PCI и ISA, а также для USB-"
+"аудиоустройств."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:79
+#, no-wrap
+msgid "Probing, Attaching, etc."
+msgstr "Обнаружение, присоединение и т.д."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:82
+msgid ""
+"Sound drivers probe and attach in almost the same way as any hardware driver "
+"module. You might want to look at the crossref:isa-driver[isa-driver,ISA] or "
+"crossref:pci[pci,PCI] specific sections of the handbook for more information."
+msgstr ""
+"Драйверы звуковых устройств выполняют обнаружение и подключение почти так "
+"же, как и любой модуль драйвера оборудования. Возможно, вам будет полезно "
+"ознакомиться с разделами руководства, посвящёнными crossref:isa-driver[isa-"
+"driver,ISA] или crossref:pci[pci,PCI], для получения дополнительной "
+"информации."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:84
+msgid "However, sound drivers differ in some ways:"
+msgstr "Однако драйверы звука отличаются в некоторых аспектах:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:86
+msgid ""
+"They declare themselves as [.filename]#pcm# class devices, with a `struct "
+"snddev_info` device private structure:"
+msgstr ""
+"Они объявляют себя как устройства класса [.filename]#pcm#, с приватной "
+"структурой устройства `struct snddev_info`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:94
+#, no-wrap
+msgid ""
+" static driver_t xxx_driver = {\n"
+" \"pcm\",\n"
+" xxx_methods,\n"
+" sizeof(struct snddev_info)\n"
+" };\n"
+msgstr ""
+" static driver_t xxx_driver = {\n"
+" \"pcm\",\n"
+" xxx_methods,\n"
+" sizeof(struct snddev_info)\n"
+" };\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:97
+#, no-wrap
+msgid ""
+" DRIVER_MODULE(snd_xxxpci, pci, xxx_driver, pcm_devclass, 0, 0);\n"
+" MODULE_DEPEND(snd_xxxpci, snd_pcm, PCM_MINVER, PCM_PREFVER,PCM_MAXVER);\n"
+msgstr ""
+" DRIVER_MODULE(snd_xxxpci, pci, xxx_driver, pcm_devclass, 0, 0);\n"
+" MODULE_DEPEND(snd_xxxpci, snd_pcm, PCM_MINVER, PCM_PREFVER,PCM_MAXVER);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:100
+msgid ""
+"Most sound drivers need to store additional private information about their "
+"device. A private data structure is usually allocated in the attach routine. "
+"Its address is passed to [.filename]#pcm# by the calls to `pcm_register()` "
+"and `mixer_init()`. [.filename]#pcm# later passes back this address as a "
+"parameter in calls to the sound driver interfaces."
+msgstr ""
+"Большинству звуковых драйверов необходимо хранить дополнительную приватную "
+"информацию о своём устройстве. Приватная структура данных обычно выделяется "
+"в процедуре attach. Её адрес передаётся в [.filename]#pcm# через вызовы "
+"`pcm_register()` и `mixer_init()`. [.filename]#pcm# позже передаёт обратно "
+"этот адрес в качестве параметра при вызовах интерфейсов звукового драйвера."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:103
+msgid ""
+"The sound driver attach routine should declare its MIXER or AC97 interface "
+"to [.filename]#pcm# by calling `mixer_init()`. For a MIXER interface, this "
+"causes in turn a call to crossref:sound[xxxmixer-init,`xxxmixer_init()`]."
+msgstr ""
+"Подпрограмма подключения звукового драйвера должна объявить свой интерфейс "
+"MIXER или AC97 для [.filename]#pcm#, вызвав `mixer_init()`. Для интерфейса "
+"MIXER это, в свою очередь, приводит к вызову crossref:sound[xxxmixer-"
+"init,`xxxmixer_init()`]."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:104
+msgid ""
+"The sound driver attach routine declares its general CHANNEL configuration "
+"to [.filename]#pcm# by calling `pcm_register(dev, sc, nplay, nrec)`, where "
+"`sc` is the address for the device data structure, used in further calls "
+"from [.filename]#pcm#, and `nplay` and `nrec` are the number of play and "
+"record channels."
+msgstr ""
+"Функция подключения драйвера звука объявляет свою общую конфигурацию CHANNEL "
+"для [.filename]#pcm#, вызывая `pcm_register(dev, sc, nplay, nrec)`, где `sc` "
+"— это адрес структуры данных устройства, используемый при последующих "
+"вызовах из [.filename]#pcm#, а `nplay` и `nrec` — количество каналов "
+"воспроизведения и записи."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:107
+msgid ""
+"The sound driver attach routine declares each of its channel objects by "
+"calls to `pcm_addchan()`. This sets up the channel glue in [.filename]#pcm# "
+"and causes in turn a call to crossref:sound[xxxchannel-"
+"init,`xxxchannel_init()`]."
+msgstr ""
+"Подпрограмма подключения звукового драйвера объявляет каждый из своих "
+"каналов вызовами `pcm_addchan()`. Это настраивает связующий слой канала в "
+"[.filename]#pcm# и, в свою очередь, вызывает вызов crossref:sound[xxxchannel-"
+"init,`xxxchannel_init()`]."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:108
+msgid ""
+"The sound driver detach routine should call `pcm_unregister()` before "
+"releasing its resources."
+msgstr ""
+"Драйвер звука должен вызвать `pcm_unregister()` в процедуре отключения перед "
+"освобождением своих ресурсов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:110
+msgid "There are two possible methods to handle non-PnP devices:"
+msgstr ""
+"Существует два возможных способа работы с устройствами, не поддерживающими "
+"PnP:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:112
+msgid ""
+"Use a `device_identify()` method (example: [.filename]#sound/isa/es1888.c#). "
+"The `device_identify()` method probes for the hardware at known addresses "
+"and, if it finds a supported device, creates a new pcm device which is then "
+"passed to probe/attach."
+msgstr ""
+"Используйте метод `device_identify()` (пример: [.filename]#sound/isa/"
+"es1888.c#). Метод `device_identify()` проверяет наличие оборудования по "
+"известным адресам и, если находит поддерживаемое устройство, создает новое "
+"pcm-устройство, которое затем передается для probe/attach."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:113
+msgid ""
+"Use a custom kernel configuration with appropriate hints for pcm devices "
+"(example: [.filename]#sound/isa/mss.c#)."
+msgstr ""
+"Используйте пользовательскую конфигурацию ядра с соответствующими "
+"подсказками для устройств pcm (пример: [.filename]#sound/isa/mss.c#)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:115
+msgid ""
+"[.filename]#pcm# drivers should implement `device_suspend`, `device_resume` "
+"and `device_shutdown` routines, so that power management and module "
+"unloading function correctly."
+msgstr ""
+"[.filename]#pcm# драйверы должны реализовывать подпрограммы "
+"`device_suspend`, `device_resume` и `device_shutdown`, чтобы управление "
+"питанием и выгрузка модулей работали корректно."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:117
+#, no-wrap
+msgid "Interfaces"
+msgstr "Интерфейсы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:121
+msgid ""
+"The interface between the [.filename]#pcm# core and the sound drivers is "
+"defined in terms of crossref:kobj[kernel-objects,kernel objects]."
+msgstr ""
+"Интерфейс между ядром [.filename]#pcm# и звуковыми драйверами определяется в "
+"терминах crossref:kobj[kernel-objects,объектов ядра Kobj]."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:123
+msgid ""
+"There are two main interfaces that a sound driver will usually provide: "
+"_CHANNEL_ and either _MIXER_ or _AC97_."
+msgstr ""
+"Существует два основных интерфейса, которые обычно предоставляет драйвер "
+"звука: _CHANNEL_ и либо _MIXER_, либо _AC97_."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:125
+msgid ""
+"The _AC97_ interface is a very small hardware access (register read/write) "
+"interface, implemented by drivers for hardware with an AC97 codec. In this "
+"case, the actual MIXER interface is provided by the shared AC97 code in "
+"[.filename]#pcm#."
+msgstr ""
+"Интерфейс _AC97_ — это очень небольшой интерфейс доступа к оборудованию "
+"(чтение/запись регистров), реализованный драйверами для устройств с кодеком "
+"AC97. В этом случае фактический интерфейс MIXER предоставляется общим кодом "
+"AC97 в [.filename]#pcm#."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:126
+#, no-wrap
+msgid "The CHANNEL Interface"
+msgstr "Интерфейс CHANNEL"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:128
+#, no-wrap
+msgid "Common Notes for Function Parameters"
+msgstr "Общие примечания для параметров функций"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:131
+msgid ""
+"Sound drivers usually have a private data structure to describe their "
+"device, and one structure for each play and record data channel that it "
+"supports."
+msgstr ""
+"Драйверы звука обычно имеют приватную структуру данных для описания своего "
+"устройства и по одной структуре для каждого канала воспроизведения и записи, "
+"который они поддерживают."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:133
+msgid ""
+"For all CHANNEL interface functions, the first parameter is an opaque "
+"pointer."
+msgstr ""
+"Для всех функций интерфейса CHANNEL первый параметр — это непрозрачный "
+"указатель."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:135
+msgid ""
+"The second parameter is a pointer to the private channel data structure, "
+"except for `channel_init()` which has a pointer to the private device "
+"structure (and returns the channel pointer for further use by "
+"[.filename]#pcm#)."
+msgstr ""
+"Второй параметр представляет собой указатель на приватную структуру данных "
+"канала, за исключением `channel_init()`, где передается указатель на "
+"приватную структуру устройства (и возвращается указатель на канал для "
+"дальнейшего использования [.filename]#pcm#)."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:136
+#, no-wrap
+msgid "Overview of Data Transfer Operations"
+msgstr "Обзор операций передачи данных"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:139
+msgid ""
+"For sound data transfers, the [.filename]#pcm# core and the sound drivers "
+"communicate through a shared memory area, described by a `struct snd_dbuf`."
+msgstr ""
+"Для надежной передачи звуковых данных ядро [.filename]#pcm# и драйверы звука "
+"взаимодействуют через общую область памяти, описываемую структурой `struct "
+"snd_dbuf`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:141
+msgid ""
+"`struct snd_dbuf` is private to [.filename]#pcm#, and sound drivers obtain "
+"values of interest by calls to accessor functions (`sndbuf_getxxx()`)."
+msgstr ""
+"`struct snd_dbuf` является приватной для [.filename]#pcm#, и драйверы звука "
+"получают нужные значения через вызовы функций доступа (`sndbuf_getxxx()`)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:143
+msgid ""
+"The shared memory area has a size of `sndbuf_getsize()` and is divided into "
+"fixed size blocks of `sndbuf_getblksz()` bytes."
+msgstr ""
+"Область разделяемой памяти имеет размер `sndbuf_getsize()` и разделена на "
+"блоки фиксированного размера по `sndbuf_getblksz()` байт."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:145
+msgid ""
+"When playing, the general transfer mechanism is as follows (reverse the idea "
+"for recording):"
+msgstr ""
+"При воспроизведении общий механизм передачи выглядит следующим образом (для "
+"записи идея обратная):"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:148
+msgid ""
+"[.filename]#pcm# initially fills up the buffer, then calls the sound "
+"driver's crossref:sound[channel-trigger,`xxxchannel_trigger()`] function "
+"with a parameter of PCMTRIG_START."
+msgstr ""
+"[.filename]#pcm# сначала заполняет буфер, затем вызывает функцию "
+"crossref:sound[channel-trigger,`xxxchannel_trigger()`] драйвера звука с "
+"параметром PCMTRIG_START."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:149
+msgid ""
+"The sound driver then arranges to repeatedly transfer the whole memory area "
+"(`sndbuf_getbuf()`, `sndbuf_getsize()`) to the device, in blocks of "
+"`sndbuf_getblksz()` bytes. It calls back the `chn_intr()`[.filename]#pcm# "
+"function for each transferred block (this will typically happen at interrupt "
+"time)."
+msgstr ""
+"Звуковой драйвер затем организует повторяющуюся передачу всей области памяти "
+"(`sndbuf_getbuf()`, `sndbuf_getsize()`) на устройство блоками по "
+"`sndbuf_getblksz()` байт. Для каждого переданного блока он вызывает функцию "
+"`chn_intr()`[.filename]#pcm# (обычно это происходит во время прерывания)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:150
+msgid ""
+"`chn_intr()` arranges to copy new data to the area that was transferred to "
+"the device (now free), and make appropriate updates to the `snd_dbuf` "
+"structure."
+msgstr ""
+"`chn_intr()` организует копирование новых данных в область, которая была "
+"передана устройству (теперь свободна), и вносит соответствующие обновления в "
+"структуру `snd_dbuf`."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:152
+#, no-wrap
+msgid "channel_init"
+msgstr "channel_init"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:156
+msgid ""
+"`xxxchannel_init()` is called to initialize each of the play or record "
+"channels. The calls are initiated from the sound driver attach routine. "
+"(See the crossref:sound[pcm-probe-and-attach,probe and attach section)."
+msgstr ""
+"`xxxchannel_init()` вызывается для инициализации каждого из каналов "
+"воспроизведения или записи. Вызовы инициируются из процедуры присоединения "
+"драйвера звука. (См. раздел crossref:sound[pcm-probe-and-attach,зондирование "
+"и присоединение])."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:168
+#, no-wrap
+msgid ""
+" static void *\n"
+" xxxchannel_init(kobj_t obj, void *data,\n"
+" struct snd_dbuf *b, struct pcm_channel *c, int dir) <.>\n"
+" {\n"
+" struct xxx_info *sc = data;\n"
+" struct xxx_chinfo *ch;\n"
+" ...\n"
+" return ch; <.>\n"
+" }\n"
+msgstr ""
+" static void *\n"
+" xxxchannel_init(kobj_t obj, void *data,\n"
+" struct snd_dbuf *b, struct pcm_channel *c, int dir) <.>\n"
+" {\n"
+" struct xxx_info *sc = data;\n"
+" struct xxx_chinfo *ch;\n"
+" ...\n"
+" return ch; <.>\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:171
+msgid ""
+"`b` is the address for the channel `struct snd_dbuf`. It should be "
+"initialized in the function by calling `sndbuf_alloc()`. The buffer size to "
+"use is normally a small multiple of the 'typical' unit transfer size for "
+"your device.`c` is the [.filename]#pcm# channel control structure pointer. "
+"This is an opaque object. The function should store it in the local channel "
+"structure, to be used in later calls to [.filename]#pcm# (ie: "
+"`chn_intr(c)`).`dir` indicates the channel direction (`PCMDIR_PLAY` or "
+"`PCMDIR_REC`)."
+msgstr ""
+"`b` — это адрес для канала `struct snd_dbuf`. Он должен быть инициализирован "
+"в функции вызовом `sndbuf_alloc()`. Размер буфера, который следует "
+"использовать, обычно представляет собой небольшое кратное от 'типичного' "
+"размера единицы передачи данных для вашего устройства. `c` — это указатель "
+"на структуру управления каналом [.filename]#pcm#. Это непрозрачный объект. "
+"Функция должна сохранить его в локальной структуре канала для использования "
+"в последующих вызовах [.filename]#pcm# (например: `chn_intr(c)`). `dir` "
+"указывает направление канала (`PCMDIR_PLAY` или `PCMDIR_REC`)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:173
+msgid ""
+"The function should return a pointer to the private area used to control "
+"this channel. This will be passed as a parameter to other channel interface "
+"calls."
+msgstr ""
+"Функция должна возвращать указатель на приватную область, используемую для "
+"управления этим каналом. Этот указатель будет передаваться в качестве "
+"параметра при других вызовах интерфейса канала."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:174
+#, no-wrap
+msgid "channel_setformat"
+msgstr "channel_setformat"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:177
+msgid ""
+"`xxxchannel_setformat()` should set up the hardware for the specified "
+"channel for the specified sound format."
+msgstr ""
+"`xxxchannel_setformat()` должен настроить оборудование для указанного канала "
+"под указанный звуковой формат."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:187
+#, no-wrap
+msgid ""
+" static int\n"
+" xxxchannel_setformat(kobj_t obj, void *data, u_int32_t format) <.>\n"
+" {\n"
+" struct xxx_chinfo *ch = data;\n"
+" ...\n"
+" return 0;\n"
+" }\n"
+msgstr ""
+" static int\n"
+" xxxchannel_setformat(kobj_t obj, void *data, u_int32_t format) <.>\n"
+" {\n"
+" struct xxx_chinfo *ch = data;\n"
+" ...\n"
+" return 0;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:190
+msgid ""
+"`format` is specified as an `AFMT_XXX value` ([.filename]#soundcard.h#)."
+msgstr ""
+"`format` указывается как значение `AFMT_XXX` ([.filename]#soundcard.h#)."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:191
+#, no-wrap
+msgid "channel_setspeed"
+msgstr "channel_setspeed"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:194
+msgid ""
+"`xxxchannel_setspeed()` sets up the channel hardware for the specified "
+"sampling speed, and returns the possibly adjusted speed."
+msgstr ""
+"`xxxchannel_setspeed()` настраивает оборудование канала для указанной "
+"скорости дискретизации и возвращает возможно скорректированную скорость."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:204
+#, no-wrap
+msgid ""
+" static int\n"
+" xxxchannel_setspeed(kobj_t obj, void *data, u_int32_t speed)\n"
+" {\n"
+" struct xxx_chinfo *ch = data;\n"
+" ...\n"
+" return speed;\n"
+" }\n"
+msgstr ""
+" static int\n"
+" xxxchannel_setspeed(kobj_t obj, void *data, u_int32_t speed)\n"
+" {\n"
+" struct xxx_chinfo *ch = data;\n"
+" ...\n"
+" return speed;\n"
+" }\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:206
+#, no-wrap
+msgid "channel_setblocksize"
+msgstr "channel_setblocksize"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:209
+msgid ""
+"`xxxchannel_setblocksize()` sets the block size, which is the size of unit "
+"transactions between [.filename]#pcm# and the sound driver, and between the "
+"sound driver and the device. Typically, this would be the number of bytes "
+"transferred before an interrupt occurs. During a transfer, the sound driver "
+"should call [.filename]#pcm#'s `chn_intr()` every time this size has been "
+"transferred."
+msgstr ""
+"`xxxchannel_setblocksize()` устанавливает размер блока, который является "
+"размером единичных транзакций между [.filename]#pcm# и звуковым драйвером, а "
+"также между звуковым драйвером и устройством. Обычно это количество байт, "
+"передаваемых до возникновения прерывания. Во время передачи звуковой драйвер "
+"должен вызывать `chn_intr()` из [.filename]#pcm# каждый раз, когда "
+"передается данный размер."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:211
+msgid ""
+"Most sound drivers only take note of the block size here, to be used when an "
+"actual transfer will be started."
+msgstr ""
+"Большинство драйверов звука здесь учитывают только размер блока, который "
+"будет использоваться при начале фактической передачи."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:221
+#, no-wrap
+msgid ""
+" static int\n"
+" xxxchannel_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)\n"
+" {\n"
+" struct xxx_chinfo *ch = data;\n"
+" ...\n"
+" return blocksize; <.>\n"
+" }\n"
+msgstr ""
+" static int\n"
+" xxxchannel_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)\n"
+" {\n"
+" struct xxx_chinfo *ch = data;\n"
+" ...\n"
+" return blocksize; <.>\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:224
+msgid ""
+"The function returns the possibly adjusted block size. In case the block "
+"size is indeed changed, `sndbuf_resize()` should be called to adjust the "
+"buffer."
+msgstr ""
+"Функция возвращает, возможно, скорректированный размер блока. Если размер "
+"блока действительно изменён, следует вызвать `sndbuf_resize()` для "
+"корректировки буфера."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:226
+#, no-wrap
+msgid "channel_trigger"
+msgstr "channel_trigger"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:229
+msgid ""
+"`xxxchannel_trigger()` is called by [.filename]#pcm# to control data "
+"transfer operations in the driver."
+msgstr ""
+"`xxxchannel_trigger()` вызывается [.filename]#pcm# для управления операциями "
+"передачи данных в драйвере."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:239
+#, no-wrap
+msgid ""
+" static int\n"
+" xxxchannel_trigger(kobj_t obj, void *data, int go) <.>\n"
+" {\n"
+" struct xxx_chinfo *ch = data;\n"
+" ...\n"
+" return 0;\n"
+" }\n"
+msgstr ""
+" static int\n"
+" xxxchannel_trigger(kobj_t obj, void *data, int go) <.>\n"
+" {\n"
+" struct xxx_chinfo *ch = data;\n"
+" ...\n"
+" return 0;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:242
+msgid "`go` defines the action for the current call. The possible values are:"
+msgstr "`go` определяет действие для текущего вызова. Возможные значения:"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:246
+msgid ""
+"If the driver uses ISA DMA, `sndbuf_isadma()` should be called before "
+"performing actions on the device, and will take care of the DMA chip side of "
+"things."
+msgstr ""
+"Если драйвер использует ISA DMA, перед выполнением действий с устройством "
+"следует вызвать `sndbuf_isadma()`, которая позаботится о том, что делает DMA-"
+"чип."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:248
+#, no-wrap
+msgid "channel_getptr"
+msgstr "channel_getptr"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:251
+msgid ""
+"`xxxchannel_getptr()` returns the current offset in the transfer buffer. "
+"This will typically be called by `chn_intr()`, and this is how "
+"[.filename]#pcm# knows where it can transfer new data."
+msgstr ""
+"`xxxchannel_getptr()` возвращает текущее смещение в буфере передачи. Обычно "
+"этот вызов выполняется функцией `chn_intr()`, и именно так [.filename]#pcm# "
+"узнаёт, куда можно передавать новые данные."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:252
+#, no-wrap
+msgid "channel_free"
+msgstr "channel_free"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:255
+msgid ""
+"`xxxchannel_free()` is called to free up channel resources, for example when "
+"the driver is unloaded, and should be implemented if the channel data "
+"structures are dynamically allocated or if `sndbuf_alloc()` was not used for "
+"buffer allocation."
+msgstr ""
+"`xxxchannel_free()` вызывается для освобождения ресурсов канала, например, "
+"при выгрузке драйвера, и должна быть реализована, если структуры данных "
+"канала динамически выделены или если `sndbuf_alloc()` не использовалась для "
+"выделения буфера."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:256
+#, no-wrap
+msgid "channel_getcaps"
+msgstr "channel_getcaps"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:265
+#, no-wrap
+msgid ""
+" struct pcmchan_caps *\n"
+" xxxchannel_getcaps(kobj_t obj, void *data)\n"
+" {\n"
+" return &xxx_caps; <.>\n"
+" }\n"
+msgstr ""
+" struct pcmchan_caps *\n"
+" xxxchannel_getcaps(kobj_t obj, void *data)\n"
+" {\n"
+" return &xxx_caps; <.>\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:268
+msgid ""
+"The routine returns a pointer to a (usually statically-defined) "
+"`pcmchan_caps` structure (defined in [.filename]#sound/pcm/channel.h#. The "
+"structure holds the minimum and maximum sampling frequencies, and the "
+"accepted sound formats. Look at any sound driver for an example."
+msgstr ""
+"Подпрограмма возвращает указатель на (обычно статически определённую) "
+"структуру `pcmchan_caps` (определена в [.filename]#sound/pcm/channel.h#). "
+"Эта структура содержит минимальную и максимальную частоты дискретизации, а "
+"также поддерживаемые звуковые форматы. Пример можно найти в любом драйвере "
+"звукового устройства."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:269
+#, no-wrap
+msgid "More Functions"
+msgstr "Дополнительные функции"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:272
+msgid ""
+"`channel_reset()`, `channel_resetdone()`, and `channel_notify()` are for "
+"special purposes and should not be implemented in a driver without "
+"discussing it on the {freebsd-multimedia}."
+msgstr ""
+"`channel_reset()`, `channel_resetdone()` и `channel_notify()` предназначены "
+"для специальных целей и не должны реализовываться в драйвере без обсуждения "
+"на {freebsd-multimedia}."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:274
+msgid "`channel_setdir()` is deprecated."
+msgstr "`channel_setdir()` устарела."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:275
+#, no-wrap
+msgid "The MIXER Interface"
+msgstr "Интерфейс MIXER"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:278
+#, no-wrap
+msgid "mixer_init"
+msgstr "mixer_init"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:281
+msgid ""
+"`xxxmixer_init()` initializes the hardware and tells [.filename]#pcm# what "
+"mixer devices are available for playing and recording"
+msgstr ""
+"`xxxmixer_init()` инициализирует оборудование и сообщает [.filename]#pcm#, "
+"какие устройства микшера доступны для воспроизведения и записи"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:289
+#, no-wrap
+msgid ""
+" static int\n"
+" xxxmixer_init(struct snd_mixer *m)\n"
+" {\n"
+" struct xxx_info *sc = mix_getdevinfo(m);\n"
+" u_int32_t v;\n"
+msgstr ""
+" static int\n"
+" xxxmixer_init(struct snd_mixer *m)\n"
+" {\n"
+" struct xxx_info *sc = mix_getdevinfo(m);\n"
+" u_int32_t v;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:291
+#, no-wrap
+msgid " [Initialize hardware]\n"
+msgstr " [Initialize hardware]\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:296
+#, no-wrap
+msgid ""
+" [Set appropriate bits in v for play mixers] <.>\n"
+" mix_setdevs(m, v);\n"
+" [Set appropriate bits in v for record mixers]\n"
+" mix_setrecdevs(m, v)\n"
+msgstr ""
+" [Set appropriate bits in v for play mixers] <.>\n"
+" mix_setdevs(m, v);\n"
+" [Set appropriate bits in v for record mixers]\n"
+" mix_setrecdevs(m, v)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:299
+#, no-wrap
+msgid ""
+" return 0;\n"
+" }\n"
+msgstr ""
+" return 0;\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:302
+msgid ""
+"Set bits in an integer value and call `mix_setdevs()` and `mix_setrecdevs()` "
+"to tell [.filename]#pcm# what devices exist."
+msgstr ""
+"Установите биты в целочисленном значении и вызовите `mix_setdevs()` и "
+"`mix_setrecdevs()`, чтобы сообщить [.filename]#pcm#, какие устройства "
+"существуют."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:304
+msgid ""
+"Mixer bits definitions can be found in [.filename]#soundcard.h# "
+"(`SOUND_MASK_XXX` values and `SOUND_MIXER_XXX` bit shifts)."
+msgstr ""
+"Определения битов микшера можно найти в [.filename]#soundcard.h# (значения "
+"`SOUND_MASK_XXX` и сдвиги битов `SOUND_MIXER_XXX`)."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:305
+#, no-wrap
+msgid "mixer_set"
+msgstr "mixer_set"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:308
+msgid "`xxxmixer_set()` sets the volume level for one mixer device."
+msgstr ""
+"`xxxmixer_set()` устанавливает уровень громкости для одного устройства "
+"микшера."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:319
+#, no-wrap
+msgid ""
+" static int\n"
+" xxxmixer_set(struct snd_mixer *m, unsigned dev,\n"
+" unsigned left, unsigned right) <.>\n"
+" {\n"
+" struct sc_info *sc = mix_getdevinfo(m);\n"
+" [set volume level]\n"
+" return left | (right << 8); <.>\n"
+" }\n"
+msgstr ""
+" static int\n"
+" xxxmixer_set(struct snd_mixer *m, unsigned dev,\n"
+" unsigned left, unsigned right) <.>\n"
+" {\n"
+" struct sc_info *sc = mix_getdevinfo(m);\n"
+" [set volume level]\n"
+" return left | (right << 8); <.>\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:322
+msgid ""
+"The device is specified as a `SOUND_MIXER_XXX` value. The volume values are "
+"specified in range [0-100]. A value of zero should mute the device."
+msgstr ""
+"Устройство указывается как значение `SOUND_MIXER_XXX`. Значения громкости "
+"задаются в диапазоне [0-100]. Значение ноль должно отключать звук устройства."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:323
+msgid ""
+"As the hardware levels probably will not match the input scale, and some "
+"rounding will occur, the routine returns the actual level values (in range "
+"0-100) as shown."
+msgstr ""
+"Поскольку уровни оборудования, вероятно, не совпадут с входной шкалой и "
+"будет происходить округление, процедура возвращает фактические значения "
+"уровней (в диапазоне 0-100), как показано."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:324
+#, no-wrap
+msgid "mixer_setrecsrc"
+msgstr "mixer_setrecsrc"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:327
+msgid "`xxxmixer_setrecsrc()` sets the recording source device."
+msgstr "`xxxmixer_setrecsrc()` устанавливает устройство источника записи."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:334
+#, no-wrap
+msgid ""
+" static int\n"
+" xxxmixer_setrecsrc(struct snd_mixer *m, u_int32_t src) <.>\n"
+" {\n"
+" struct xxx_info *sc = mix_getdevinfo(m);\n"
+msgstr ""
+" static int\n"
+" xxxmixer_setrecsrc(struct snd_mixer *m, u_int32_t src) <.>\n"
+" {\n"
+" struct xxx_info *sc = mix_getdevinfo(m);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:336
+#, no-wrap
+msgid " [look for non zero bit(s) in src, set up hardware]\n"
+msgstr " [look for non zero bit(s) in src, set up hardware]\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:340
+#, no-wrap
+msgid ""
+" [update src to reflect actual action]\n"
+" return src; <.>\n"
+" }\n"
+msgstr ""
+" [update src to reflect actual action]\n"
+" return src; <.>\n"
+" }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:343
+msgid "The desired recording devices are specified as a bit field"
+msgstr "Желаемые устройства записи указываются в виде битового поля"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:344
+msgid ""
+"The actual devices set for recording are returned. Some drivers can only set "
+"one device for recording. The function should return -1 if an error occurs."
+msgstr ""
+"Возвращаются фактические устройства, настроенные для записи. Некоторые "
+"драйверы могут настраивать только одно устройство для записи. Функция должна "
+"возвращать -1 в случае ошибки."
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:345
+#, no-wrap
+msgid "mixer_uninit, mixer_reinit"
+msgstr "mixer_uninit, mixer_reinit"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:348
+msgid ""
+"`xxxmixer_uninit()` should ensure that all sound is muted and if possible "
+"mixer hardware should be powered down."
+msgstr ""
+"`xxxmixer_uninit()` должен гарантировать, что весь звук отключен, и, если "
+"возможно, аппаратный микшер должен быть переведен в режим пониженного "
+"энергопотребления."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:350
+msgid ""
+"`xxxmixer_reinit()` should ensure that the mixer hardware is powered up and "
+"any settings not controlled by `mixer_set()` or `mixer_setrecsrc()` are "
+"restored."
+msgstr ""
+"`xxxmixer_reinit()` должна гарантировать, что аппаратура микшера включена и "
+"все настройки, не управляемые `mixer_set()` или `mixer_setrecsrc()`, "
+"восстановлены."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:351
+#, no-wrap
+msgid "The AC97 Interface"
+msgstr "Интерфейс AC97"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:354
+msgid ""
+"The _AC97_ interface is implemented by drivers with an AC97 codec. It only "
+"has three methods:"
+msgstr ""
+"Интерфейс _AC97_ реализован драйверами с кодеком AC97. У него есть только "
+"три метода:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:356
+msgid "`xxxac97_init()` returns the number of ac97 codecs found."
+msgstr "`xxxac97_init()` возвращает количество найденных кодеков ac97."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:357
+msgid "`ac97_read()` and `ac97_write()` read or write a specified register."
+msgstr ""
+"`ac97_read()` и `ac97_write()` читают или записывают указанный регистр."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sound/_index.adoc:358
+msgid ""
+"The _AC97_ interface is used by the AC97 code in [.filename]#pcm# to perform "
+"higher level operations. Look at [.filename]#sound/pci/maestro3.c# or many "
+"others under [.filename]#sound/pci/# for an example."
+msgstr ""
+"Интерфейс _AC97_ используется кодом AC97 в [.filename]#pcm# для выполнения "
+"операций более высокого уровня. В качестве примера можно посмотреть "
+"[.filename]#sound/pci/maestro3.c# или другие файлы в [.filename]#sound/pci/#."
diff --git a/documentation/content/ru/books/arch-handbook/sysinit/_index.adoc b/documentation/content/ru/books/arch-handbook/sysinit/_index.adoc
new file mode 100644
index 0000000000..2ed3e6c763
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/sysinit/_index.adoc
@@ -0,0 +1,165 @@
+---
+description: 'Фреймворк SYSINIT'
+next: books/arch-handbook/mac
+params:
+ path: /books/arch-handbook/sysinit/
+prev: books/arch-handbook/jail
+showBookMenu: true
+tags: ["SYSINIT", "framework", "Terminology"]
+title: 'Глава 5. Фреймворк SYSINIT'
+weight: 6
+---
+
+[[sysinit]]
+= Фреймворк SYSINIT
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 5
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+SYSINIT — это фреймворк для общего механизма сортировки и диспетчеризации вызовов. В настоящее время FreeBSD использует его для динамической инициализации ядра. SYSINIT позволяет изменять порядок, добавлять, удалять и заменять подсистемы ядра FreeBSD во время линковки ядра при загрузке ядра или его модулей, без необходимости редактировать статически упорядоченные маршруты инициализации и перекомпилировать ядро. Эта система также позволяет модулям ядра (в настоящее время называемым _KLD_) компилироваться, линковаться и инициализироваться отдельно во время загрузки, а также загружаться позже, когда система уже работает. Это достигается с помощью «компоновщика ядра» (kernel linker) и «наборов компоновщика» (linker sets).
+
+[[sysinit-term]]
+== Терминология
+
+Набор компоновщика (Linker Set)::
+Техника компоновщика, при которой компоновщик собирает статически объявленные данные из всех исходных файлов программы в единый непрерывно адресуемый блок данных.
+
+[[sysinit-operation]]
+== Работа механизма SYSINIT
+
+SYSINIT полагается на способность компоновщика объединять статические данные, объявленные в нескольких местах исходного кода программы, в единый непрерывный блок данных. Этот метод компоновщика называется "набором компоновщика" (linker set). SYSINIT использует два набора компоновщика для поддержки двух наборов данных, содержащих порядок вызова, функцию и указатель на данные, передаваемые этой функции для каждого члена этих наборов данных.
+
+SYSINIT использует два приоритета для упорядочивания функций при выполнении. Первый приоритет — это идентификатор подсистемы, задающий общий порядок вызова функций SYSINIT. Предварительно объявленные идентификаторы находятся в [.filename]#<sys/kernel.h># в перечислении `sysinit_sub_id`. Второй используемый приоритет — это порядок элементов внутри подсистемы. Предварительно объявленные порядки элементов подсистемы находятся в [.filename]#<sys/kernel.h># в перечислении `sysinit_elem_order`.
+
+В настоящее время существует два варианта использования `SYSINIT`: вызов функций при загрузке системы и загрузке модулей ядра, а также вызов функций при завершении работы системы и выгрузке модулей ядра. Подсистемы ядра часто используют `SYSINIT` при старте системы для инициализации структур данных. Например, подсистема планирования процессов использует `SYSINIT` для инициализации структуры данных очереди выполнения. Драйверы устройств должны избегать прямого использования `SYSINIT()`. Вместо этого драйверы реальных устройств, входящих в структуру шины, должны использовать `DRIVER_MODULE()`, который предоставляет функцию для обнаружения устройства и, если оно присутствует, его инициализации. Этот макрос выполняет несколько действий, специфичных для устройств, а затем вызывает `SYSINIT()` самостоятельно. Для псевдоустройств, которые не входят в структуру шины, следует использовать `DEV_MODULE()`.
+
+[[sysinit-using]]
+== Использование SYSINIT
+
+=== Интерфейс
+
+==== Заголовки
+
+[.programlisting]
+....
+<sys/kernel.h>
+....
+
+==== Макросы
+
+[.programlisting]
+....
+SYSINIT(uniquifier, subsystem, order, func, ident)
+SYSUNINIT(uniquifier, subsystem, order, func, ident)
+....
+
+=== Запуск
+
+Макрос `SYSINIT()` создает необходимые данные SYSINIT в наборе данных инициализации системы, чтобы SYSINIT мог отсортировать и выполнить функцию при запуске системы и загрузке модуля. `SYSINIT()` принимает уникальный идентификатор, который SYSINIT использует для идентификации конкретных данных вызова функции, порядок подсистемы, порядок элемента подсистемы, функцию для вызова и данные для передачи в функцию. Все функции должны принимать аргумент в виде константного указателя.
+
+.Пример `SYSINIT()`
+[example]
+====
+[.programlisting]
+....
+#include <sys/kernel.h>
+
+void foo_null(void *unused)
+{
+ foo_doo();
+}
+SYSINIT(foo, SI_SUB_FOO, SI_ORDER_FOO, foo_null, NULL);
+
+struct foo foo_voodoo = {
+ FOO_VOODOO;
+}
+
+void foo_arg(void *vdata)
+{
+ struct foo *foo = (struct foo *)vdata;
+ foo_data(foo);
+}
+SYSINIT(bar, SI_SUB_FOO, SI_ORDER_FOO, foo_arg, &foo_voodoo);
+....
+====
+
+Обратите внимание, что `SI_SUB_FOO` и `SI_ORDER_FOO` должны быть в перечислениях `sysinit_sub_id` и `sysinit_elem_order`, как упоминалось выше. Можно использовать существующие значения или добавить свои в эти перечисления. Также можно использовать математические операции для точной настройки порядка выполнения SYSINIT. В этом примере показан SYSINIT, который должен выполняться непосредственно перед SYSINIT, обрабатывающими настройку параметров ядра.
+
+.Пример настройки порядка `SYSINIT()`
+[example]
+====
+[.programlisting]
+....
+static void
+mptable_register(void *dummy __unused)
+{
+
+ apic_register_enumerator(&mptable_enumerator);
+}
+
+SYSINIT(mptable_register, SI_SUB_TUNABLES - 1, SI_ORDER_FIRST,
+ mptable_register, NULL);
+....
+
+====
+
+=== Выключение системы
+
+Макрос `SYSUNINIT()` ведет себя аналогично макросу `SYSINIT()`, за исключением того, что добавляет данные SYSINIT в набор данных завершения работы SYSINIT.
+
+.Пример `SYSUNINIT()`
+[example]
+====
+[.programlisting]
+....
+#include <sys/kernel.h>
+
+void foo_cleanup(void *unused)
+{
+ foo_kill();
+}
+SYSUNINIT(foobar, SI_SUB_FOO, SI_ORDER_FOO, foo_cleanup, NULL);
+
+struct foo_stack foo_stack = {
+ FOO_STACK_VOODOO;
+}
+
+void foo_flush(void *vdata)
+{
+}
+SYSUNINIT(barfoo, SI_SUB_FOO, SI_ORDER_FOO, foo_flush, &foo_stack);
+....
+
+====
diff --git a/documentation/content/ru/books/arch-handbook/sysinit/_index.po b/documentation/content/ru/books/arch-handbook/sysinit/_index.po
new file mode 100644
index 0000000000..6dac0074cb
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/sysinit/_index.po
@@ -0,0 +1,393 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-07-02 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbooksysinit_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:1
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:14
+#, no-wrap
+msgid "The SYSINIT Framework"
+msgstr "Фреймворк SYSINIT"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:1
+#, no-wrap
+msgid "Chapter 5. The SYSINIT Framework"
+msgstr "Глава 5. Фреймворк SYSINIT"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:52
+msgid ""
+"SYSINIT is the framework for a generic call sort and dispatch mechanism. "
+"FreeBSD currently uses it for the dynamic initialization of the kernel. "
+"SYSINIT allows FreeBSD's kernel subsystems to be reordered, and added, "
+"removed, and replaced at kernel link time when the kernel or one of its "
+"modules is loaded without having to edit a statically ordered initialization "
+"routing and recompile the kernel. This system also allows kernel modules, "
+"currently called _KLD's_, to be separately compiled, linked, and initialized "
+"at boot time and loaded even later while the system is already running. This "
+"is accomplished using the \"kernel linker\" and \"linker sets\"."
+msgstr ""
+"SYSINIT — это фреймворк для общего механизма сортировки и диспетчеризации "
+"вызовов. В настоящее время FreeBSD использует его для динамической "
+"инициализации ядра. SYSINIT позволяет изменять порядок, добавлять, удалять и "
+"заменять подсистемы ядра FreeBSD во время линковки ядра при загрузке ядра "
+"или его модулей, без необходимости редактировать статически упорядоченные "
+"маршруты инициализации и перекомпилировать ядро. Эта система также позволяет "
+"модулям ядра (в настоящее время называемым _KLD_) компилироваться, "
+"линковаться и инициализироваться отдельно во время загрузки, а также "
+"загружаться позже, когда система уже работает. Это достигается с помощью "
+"«компоновщика ядра» (kernel linker) и «наборов компоновщика» (linker sets)."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:54
+#, no-wrap
+msgid "Terminology"
+msgstr "Терминология"
+
+#. type: Labeled list
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:56
+#, no-wrap
+msgid "Linker Set"
+msgstr "Набор компоновщика (Linker Set)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:58
+msgid ""
+"A linker technique in which the linker gathers statically declared data "
+"throughout a program's source files into a single contiguously addressable "
+"unit of data."
+msgstr ""
+"Техника компоновщика, при которой компоновщик собирает статически "
+"объявленные данные из всех исходных файлов программы в единый непрерывно "
+"адресуемый блок данных."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:60
+#, no-wrap
+msgid "SYSINIT Operation"
+msgstr "Работа механизма SYSINIT"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:63
+msgid ""
+"SYSINIT relies on the ability of the linker to take static data declared at "
+"multiple locations throughout a program's source and group it together as a "
+"single contiguous chunk of data. This linker technique is called a \"linker "
+"set\". SYSINIT uses two linker sets to maintain two data sets containing "
+"each consumer's call order, function, and a pointer to the data to pass to "
+"that function."
+msgstr ""
+"SYSINIT полагается на способность компоновщика объединять статические "
+"данные, объявленные в нескольких местах исходного кода программы, в единый "
+"непрерывный блок данных. Этот метод компоновщика называется \"набором "
+"компоновщика\" (linker set). SYSINIT использует два набора компоновщика для "
+"поддержки двух наборов данных, содержащих порядок вызова, функцию и "
+"указатель на данные, передаваемые этой функции для каждого члена этих "
+"наборов данных."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:65
+msgid ""
+"SYSINIT uses two priorities when ordering the functions for execution. The "
+"first priority is a subsystem ID giving an overall order for SYSINIT's "
+"dispatch of functions. Current predeclared ID's are in [.filename]#<sys/"
+"kernel.h># in the enum list `sysinit_sub_id`. The second priority used is an "
+"element order within the subsystem. Current predeclared subsystem element "
+"orders are in [.filename]#<sys/kernel.h># in the enum list "
+"`sysinit_elem_order`."
+msgstr ""
+"SYSINIT использует два приоритета для упорядочивания функций при выполнении. "
+"Первый приоритет — это идентификатор подсистемы, задающий общий порядок "
+"вызова функций SYSINIT. Предварительно объявленные идентификаторы находятся "
+"в [.filename]#<sys/kernel.h># в перечислении `sysinit_sub_id`. Второй "
+"используемый приоритет — это порядок элементов внутри подсистемы. "
+"Предварительно объявленные порядки элементов подсистемы находятся в "
+"[.filename]#<sys/kernel.h># в перечислении `sysinit_elem_order`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:67
+msgid ""
+"There are currently two uses for SYSINIT. Function dispatch at system "
+"startup and kernel module loads, and function dispatch at system shutdown "
+"and kernel module unload. Kernel subsystems often use system startup "
+"SYSINIT's to initialize data structures, for example the process scheduling "
+"subsystem uses a SYSINIT to initialize the run queue data structure. Device "
+"drivers should avoid using `SYSINIT()` directly. Instead drivers for real "
+"devices that are part of a bus structure should use `DRIVER_MODULE()` to "
+"provide a function that detects the device and, if it is present, "
+"initializes the device. It will do a few things specific to devices and then "
+"call `SYSINIT()` itself. For pseudo-devices, which are not part of a bus "
+"structure, use `DEV_MODULE()`."
+msgstr ""
+"В настоящее время существует два варианта использования `SYSINIT`: вызов "
+"функций при загрузке системы и загрузке модулей ядра, а также вызов функций "
+"при завершении работы системы и выгрузке модулей ядра. Подсистемы ядра часто "
+"используют `SYSINIT` при старте системы для инициализации структур данных. "
+"Например, подсистема планирования процессов использует `SYSINIT` для "
+"инициализации структуры данных очереди выполнения. Драйверы устройств должны "
+"избегать прямого использования `SYSINIT()`. Вместо этого драйверы реальных "
+"устройств, входящих в структуру шины, должны использовать `DRIVER_MODULE()`, "
+"который предоставляет функцию для обнаружения устройства и, если оно "
+"присутствует, его инициализации. Этот макрос выполняет несколько действий, "
+"специфичных для устройств, а затем вызывает `SYSINIT()` самостоятельно. Для "
+"псевдоустройств, которые не входят в структуру шины, следует использовать "
+"`DEV_MODULE()`."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:69
+#, no-wrap
+msgid "Using SYSINIT"
+msgstr "Использование SYSINIT"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:71
+#, no-wrap
+msgid "Interface"
+msgstr "Интерфейс"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:73
+#, no-wrap
+msgid "Headers"
+msgstr "Заголовки"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:78
+#, no-wrap
+msgid "<sys/kernel.h>\n"
+msgstr "<sys/kernel.h>\n"
+
+#. type: Title ====
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:80
+#, no-wrap
+msgid "Macros"
+msgstr "Макросы"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:86
+#, no-wrap
+msgid ""
+"SYSINIT(uniquifier, subsystem, order, func, ident)\n"
+"SYSUNINIT(uniquifier, subsystem, order, func, ident)\n"
+msgstr ""
+"SYSINIT(uniquifier, subsystem, order, func, ident)\n"
+"SYSUNINIT(uniquifier, subsystem, order, func, ident)\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:88
+#, no-wrap
+msgid "Startup"
+msgstr "Запуск"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:91
+msgid ""
+"The `SYSINIT()` macro creates the necessary SYSINIT data in SYSINIT's "
+"startup data set for SYSINIT to sort and dispatch a function at system "
+"startup and module load. `SYSINIT()` takes a uniquifier that SYSINIT uses to "
+"identify the particular function dispatch data, the subsystem order, the "
+"subsystem element order, the function to call, and the data to pass the "
+"function. All functions must take a constant pointer argument."
+msgstr ""
+"Макрос `SYSINIT()` создает необходимые данные SYSINIT в наборе данных "
+"инициализации системы, чтобы SYSINIT мог отсортировать и выполнить функцию "
+"при запуске системы и загрузке модуля. `SYSINIT()` принимает уникальный "
+"идентификатор, который SYSINIT использует для идентификации конкретных "
+"данных вызова функции, порядок подсистемы, порядок элемента подсистемы, "
+"функцию для вызова и данные для передачи в функцию. Все функции должны "
+"принимать аргумент в виде константного указателя."
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:92
+#, no-wrap
+msgid "Example of a `SYSINIT()`"
+msgstr "Пример `SYSINIT()`"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:98
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:148
+#, no-wrap
+msgid "#include <sys/kernel.h>\n"
+msgstr "#include <sys/kernel.h>\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:104
+#, no-wrap
+msgid ""
+"void foo_null(void *unused)\n"
+"{\n"
+" foo_doo();\n"
+"}\n"
+"SYSINIT(foo, SI_SUB_FOO, SI_ORDER_FOO, foo_null, NULL);\n"
+msgstr ""
+"void foo_null(void *unused)\n"
+"{\n"
+" foo_doo();\n"
+"}\n"
+"SYSINIT(foo, SI_SUB_FOO, SI_ORDER_FOO, foo_null, NULL);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:108
+#, no-wrap
+msgid ""
+"struct foo foo_voodoo = {\n"
+" FOO_VOODOO;\n"
+"}\n"
+msgstr ""
+"struct foo foo_voodoo = {\n"
+" FOO_VOODOO;\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:115
+#, no-wrap
+msgid ""
+"void foo_arg(void *vdata)\n"
+"{\n"
+" struct foo *foo = (struct foo *)vdata;\n"
+" foo_data(foo);\n"
+"}\n"
+"SYSINIT(bar, SI_SUB_FOO, SI_ORDER_FOO, foo_arg, &foo_voodoo);\n"
+msgstr ""
+"void foo_arg(void *vdata)\n"
+"{\n"
+" struct foo *foo = (struct foo *)vdata;\n"
+" foo_data(foo);\n"
+"}\n"
+"SYSINIT(bar, SI_SUB_FOO, SI_ORDER_FOO, foo_arg, &foo_voodoo);\n"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:119
+msgid ""
+"Note that `SI_SUB_FOO` and `SI_ORDER_FOO` need to be in the `sysinit_sub_id` "
+"and `sysinit_elem_order` enum's as mentioned above. Either use existing ones "
+"or add your own to the enum's. You can also use math for fine-tuning the "
+"order a SYSINIT will run in. This example shows a SYSINIT that needs to be "
+"run just barely before the SYSINIT's that handle tuning kernel parameters."
+msgstr ""
+"Обратите внимание, что `SI_SUB_FOO` и `SI_ORDER_FOO` должны быть в "
+"перечислениях `sysinit_sub_id` и `sysinit_elem_order`, как упоминалось выше. "
+"Можно использовать существующие значения или добавить свои в эти "
+"перечисления. Также можно использовать математические операции для точной "
+"настройки порядка выполнения SYSINIT. В этом примере показан SYSINIT, "
+"который должен выполняться непосредственно перед SYSINIT, обрабатывающими "
+"настройку параметров ядра."
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:120
+#, no-wrap
+msgid "Example of Adjusting `SYSINIT()` Order"
+msgstr "Пример настройки порядка `SYSINIT()`"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:128
+#, no-wrap
+msgid ""
+"static void\n"
+"mptable_register(void *dummy __unused)\n"
+"{\n"
+msgstr ""
+"static void\n"
+"mptable_register(void *dummy __unused)\n"
+"{\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:131
+#, no-wrap
+msgid ""
+"\tapic_register_enumerator(&mptable_enumerator);\n"
+"}\n"
+msgstr ""
+"\tapic_register_enumerator(&mptable_enumerator);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:134
+#, no-wrap
+msgid ""
+"SYSINIT(mptable_register, SI_SUB_TUNABLES - 1, SI_ORDER_FIRST,\n"
+" mptable_register, NULL);\n"
+msgstr ""
+"SYSINIT(mptable_register, SI_SUB_TUNABLES - 1, SI_ORDER_FIRST,\n"
+" mptable_register, NULL);\n"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:138
+#, no-wrap
+msgid "Shutdown"
+msgstr "Выключение системы"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:141
+msgid ""
+"The `SYSUNINIT()` macro behaves similarly to the `SYSINIT()` macro except "
+"that it adds the SYSINIT data to SYSINIT's shutdown data set."
+msgstr ""
+"Макрос `SYSUNINIT()` ведет себя аналогично макросу `SYSINIT()`, за "
+"исключением того, что добавляет данные SYSINIT в набор данных завершения "
+"работы SYSINIT."
+
+#. type: Block title
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:142
+#, no-wrap
+msgid "Example of a `SYSUNINIT()`"
+msgstr "Пример `SYSUNINIT()`"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:154
+#, no-wrap
+msgid ""
+"void foo_cleanup(void *unused)\n"
+"{\n"
+" foo_kill();\n"
+"}\n"
+"SYSUNINIT(foobar, SI_SUB_FOO, SI_ORDER_FOO, foo_cleanup, NULL);\n"
+msgstr ""
+"void foo_cleanup(void *unused)\n"
+"{\n"
+" foo_kill();\n"
+"}\n"
+"SYSUNINIT(foobar, SI_SUB_FOO, SI_ORDER_FOO, foo_cleanup, NULL);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:158
+#, no-wrap
+msgid ""
+"struct foo_stack foo_stack = {\n"
+" FOO_STACK_VOODOO;\n"
+"}\n"
+msgstr ""
+"struct foo_stack foo_stack = {\n"
+" FOO_STACK_VOODOO;\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/sysinit/_index.adoc:163
+#, no-wrap
+msgid ""
+"void foo_flush(void *vdata)\n"
+"{\n"
+"}\n"
+"SYSUNINIT(barfoo, SI_SUB_FOO, SI_ORDER_FOO, foo_flush, &foo_stack);\n"
+msgstr ""
+"void foo_flush(void *vdata)\n"
+"{\n"
+"}\n"
+"SYSUNINIT(barfoo, SI_SUB_FOO, SI_ORDER_FOO, foo_flush, &foo_stack);\n"
diff --git a/documentation/content/ru/books/arch-handbook/usb/_index.adoc b/documentation/content/ru/books/arch-handbook/usb/_index.adoc
new file mode 100644
index 0000000000..1694fb305a
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/usb/_index.adoc
@@ -0,0 +1,186 @@
+---
+description: 'Устройства USB в FreeBSD'
+next: books/arch-handbook/newbus
+params:
+ path: /books/arch-handbook/usb/
+prev: books/arch-handbook/scsi
+showBookMenu: true
+tags: ["USB", "Structure", "UHCI", "OHCI"]
+title: 'Глава 13. USB-устройства'
+weight: 15
+---
+
+[[usb]]
+= Устройства USB
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 13
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[usb-intro]]
+== Введение
+
+Универсальная последовательная шина (USB) — это новый способ подключения устройств к персональным компьютерам. Архитектура шины поддерживает двустороннюю связь и была разработана в ответ на усложнение устройств, требующих большего взаимодействия с хостом. Поддержка USB включена во все современные чипсеты ПК и, следовательно, доступна во всех недавно собранных компьютерах. Выпуск Apple iMac только с USB стал серьёзным стимулом для производителей оборудования выпускать USB-версии своих устройств. Согласно будущим спецификациям ПК, все устаревшие разъёмы должны быть заменены одним или несколькими USB-разъёмами, обеспечивающими универсальные возможности plug and play. Поддержка USB-оборудования появилась в NetBSD на очень раннем этапе и была разработана Леннартом Аугустссоном для проекта NetBSD. Код был портирован в FreeBSD, и в настоящее время мы поддерживаем общую кодовая базу. Для реализации подсистемы USB важны некоторые особенности USB.
+
+_Леннарт Аугустссон выполнил большую часть работы по реализации поддержки USB для проекта NetBSD. Огромная благодарность за этот невероятный объем работы. Также большое спасибо Арди и Дирку за их комментарии и вычитку этой статьи._
+
+* Устройства подключаются к портам компьютера напрямую или через устройства, называемые концентраторами, образуя древовидную структуру устройств.
+* Устройства можно подключать и отключать во время работы.
+* Устройства могут приостанавливать свою работу и инициировать возобновление работы основной системы
+* Поскольку устройства могут получать питание от шины, программное обеспечение хоста должно отслеживать энергопотребление для каждого концентратора.
+* Различные требования к качеству обслуживания для разных типов устройств, а также максимум в 126 устройств, которые могут быть подключены к одной шине, требуют правильного планирования передач по общей шине, чтобы полностью использовать доступную пропускную способность в 12 Мбит/с (более 400 Мбит/с для USB 2.0)
+* Устройства являются интеллектуальными и содержат легко доступную информацию о себе
+
+Разработка драйверов для подсистемы USB и подключенных к ней устройств поддерживается спецификациями, которые были разработаны и будут разрабатываться. Эти спецификации общедоступны на домашних страницах USB. Apple активно продвигает стандартизированные драйверы, предоставляя драйверы для универсальных классов в своей операционной системе MacOS и не поощряя использование отдельных драйверов для каждого нового устройства. Эта глава пытается собрать основную информацию для базового понимания реализации стека USB 2.0 в FreeBSD/NetBSD. Однако рекомендуется читать её вместе с соответствующими спецификациями 2.0 и другими ресурсами для разработчиков:
+
+* Спецификация USB 2.0 (http://www.usb.org/developers/docs/usb20_docs/[http://www.usb.org/developers/docs/usb20_docs/])
+* Универсальный интерфейс хост-контроллера (UHCI) Спецификация (link:ftp://ftp.netbsd.org/pub/NetBSD/misc/blymn/uhci11d.pdf[ftp://ftp.netbsd.org/pub/NetBSD/misc/blymn/uhci11d.pdf])
+* Спецификация интерфейса Open Host Controller (OHCI)(link:ftp://ftp.compaq.com/pub/supportinformation/papers/hcir1_0a.pdf[ftp://ftp.compaq.com/pub/supportinformation/papers/hcir1_0a.pdf])
+* Раздел для разработчиков на домашней странице USB (http://www.usb.org/developers/[http://www.usb.org/developers/])
+
+=== Структура стека USB
+
+Поддержка USB в FreeBSD может быть разделена на три уровня. Самый нижний уровень содержит драйвер контроллера хоста, предоставляющий универсальный интерфейс к оборудованию и его механизмам планирования. Он поддерживает инициализацию оборудования, планирование передач и обработку завершённых и/или неудачных передач. Каждый драйвер контроллера хоста реализует виртуальный концентратор, обеспечивающий независимый от оборудования доступ к регистрам, управляющим корневыми портами на задней панели машины.
+
+Средний уровень обрабатывает подключение и отключение устройства, базовую инициализацию устройства, выбор драйвера, каналы связи (pipe) и управляет ресурсами. Этот сервисный уровень также контролирует стандартные каналы и запросы устройств, передаваемые через них.
+
+Верхний уровень содержит отдельные драйверы, поддерживающие конкретные (классы) устройств. Эти драйверы реализуют протокол, используемый в каналах, отличных от стандартного. Они также реализуют дополнительную функциональность для обеспечения доступа к устройству другим частям ядра или пользовательского пространства. Они используют интерфейс драйвера USB (USBDI), предоставляемый уровнем сервисов.
+
+[[usb-hc]]
+== Контроллеры хоста
+
+Хост-контроллер (HC) управляет передачей пакетов на шине. Используются кадры длительностью 1 миллисекунда. В начале каждого кадра хост-контроллер генерирует пакет Start of Frame (SOF).
+
+Пакет SOF используется для синхронизации начала кадра и отслеживания номера кадра. Внутри каждого кадра передаются пакеты, либо от хоста к устройству (исходящие), либо от устройства к хосту (входящие). Передачи всегда инициируются хостом (опросные передачи). Поэтому на каждой шине USB может быть только один хост. Каждая передача пакета имеет стадию статуса, в которой получатель данных может вернуть либо ACK (подтверждение приема), NAK (повторить), STALL (ошибка) либо ничего (искаженная стадия данных, устройство недоступно или отключено). В разделе 8.5 спецификации USB 2.0 подробно объясняются детали пакетов. На шине USB могут происходить четыре различных типа передач: управляющие, массовые, прерывания и изохронные. Типы передач и их характеристики описаны ниже.
+
+Крупные передачи между устройством на шине USB и драйвером устройства разделяются на несколько пакетов хост-контроллером или драйвером HC.
+
+Запросы устройств (управляющие передачи) к конечным точкам по умолчанию являются особыми. Они состоят из двух или трёх фаз: SETUP, DATA (опционально) и STATUS. Пакет настройки отправляется на устройство. Если присутствует фаза данных, направление пакета(ов) данных указывается в пакете настройки. Направление в фазе статуса противоположно направлению во время фазы данных или IN, если фазы данных не было. Аппаратное обеспечение хост-контроллера также предоставляет регистры с текущим состоянием корневых портов и изменениями, произошедшими с момента последнего сброса регистра изменений статуса. Доступ к этим регистрам предоставляется через виртуализированный концентратор, как предложено в спецификации USB. Виртуальный концентратор должен соответствовать классу устройств концентратора, указанному в главе 11 этой спецификации. Он должен предоставлять канал по умолчанию, через который можно отправлять запросы устройств. Он возвращает стандартные и специфичные для класса концентратора наборы дескрипторов. Также он должен предоставлять прерывающий канал, сообщающий об изменениях, происходящих на его портах. В настоящее время доступны две спецификации для хост-контроллеров: - Universal Host Controller Interface (UHCI) от Intel и Open Host Controller Interface (OHCI) от Compaq, Microsoft и National Semiconductor. Спецификация UHCI разработана для уменьшения аппаратной сложности, требуя от драйвера хост-контроллера предоставления полного расписания передач для каждого кадра. Контроллеры типа OHCI гораздо более независимы, предоставляя более абстрактный интерфейс и выполняя большую часть работы самостоятельно.
+
+=== UHCI
+
+Контроллер UHCI поддерживает список кадров (framelist) с 1024 указателями на структуры данных для каждого кадра. Он распознаёт два типа данных: дескрипторы передачи (TD) и головы очередей (QH). Каждый TD представляет пакет для передачи в конечную точку устройства или из неё. QH служат для группировки TD (и других QH) вместе.
+
+Каждая передача состоит из одного или нескольких пакетов. Драйвер UHCI разделяет большие передачи на несколько пакетов. Для каждой передачи, за исключением изохронных, выделяется QH. Для каждого типа передачи эти QH собираются в QH для соответствующего типа. Изохронные передачи должны выполняться первыми из-за требования фиксированной задержки и непосредственно указываются указателем в списке кадров. Последний изохронный TD ссылается на QH для прерывающих передач для этого кадра. Все QH для прерывающих передач указывают на QH для управляющих передач, который, в свою очередь, указывает на QH для массовых передач. Следующая диаграмма дает графическое представление этого:
+
+В результате выполняется следующее расписание в каждом кадре. После получения указателя на текущий кадр из списка кадров контроллер сначала выполняет TDs для всех изохронных пакетов в этом кадре. Последний из этих TDs ссылается на QH для прерывающих передач этого кадра. Хост-контроллер затем переходит от этого QH к QHs для отдельных прерывающих передач. После завершения этой очереди, QH для прерывающих передач ссылается на QH для всех управляющих передач. Он выполняет все подочереди, запланированные там, а затем все передачи, поставленные в очередь в QH для массовых передач. Для упрощения обработки завершенных или неудачных передач аппаратное обеспечение генерирует различные типы прерываний в конце каждого кадра. В последнем TD для передачи бит *Interrupt-On Completion* устанавливается драйвером HC, чтобы вызвать прерывание по завершении передачи. Прерывание ошибки возникает, если TD достигает максимального количества ошибок. Если в TD установлен бит *short packet detect* и передано меньше установленной длины пакета, это прерывание уведомляет драйвер контроллера о завершении передачи. Задача драйвера хост-контроллера — определить, какая передача завершилась или вызвала ошибку. При вызове процедура обслуживания прерывания находит все завершенные передачи и вызывает их callback-функции.
+
+Обратитесь к спецификации UHCI для более подробного описания.
+
+=== OHCI
+
+Программирование OHCI-контроллера значительно проще. Контроллер предполагает, что доступен набор конечных точек, и учитывает приоритеты планирования и порядок типов передач в кадре. Основная структура данных, используемая хост-контроллером, — это дескриптор конечной точки (ED), к которому присоединена очередь дескрипторов передачи (TD). ED содержит максимальный размер пакета, разрешенный для конечной точки, а аппаратное обеспечение контроллера выполняет разделение на пакеты. Указатели на буферы данных обновляются после каждой передачи, и когда начальный и конечный указатели становятся равны, TD перемещается в очередь завершенных (done-queue). Четыре типа конечных точек (прерывание, изохронная, управление и массовая) имеют свои собственные очереди. Управляющие и массовые конечные точки помещаются каждая в свою очередь. ED прерываний организуются в дерево, где уровень в дереве определяет частоту их выполнения.
+
+Порядок действий, выполняемых хост-контроллером в каждом кадре, выглядит следующим образом. Контроллер сначала запускает непериодические очереди управления и массовых передач, вплоть до ограничения времени, установленного драйвером HC. Затем выполняются прерывающие передачи для данного номера кадра, используя младшие пять битов номера кадра в качестве индекса уровня 0 дерева ED прерываний. В конце этого дерева подключены изохронные ED, которые затем обходятся. Изохронные TD содержат номер кадра, в котором должна быть запущена первая передача. После выполнения всех периодических передач очереди управления и массовых передач обходятся снова. Периодически вызывается процедура обслуживания прерывания для обработки очереди завершенных операций и вызова обратных вызовов для каждой передачи, а также для перепланирования прерывающих и изохронных конечных точек.
+
+См. UHCI Specification для более подробного описания. Средний уровень обеспечивает контролируемый доступ к устройству и управляет ресурсами, используемыми различными драйверами и уровнем сервисов. Этот уровень отвечает за следующие аспекты:
+
+* Информация о конфигурации устройства
+* Каналы для взаимодействия с устройством
+* Обнаружение, подключение и отключение от устройства.
+
+[[usb-dev]]
+== Информация об устройстве USB
+
+=== Информация о конфигурации устройства
+
+Каждое устройство предоставляет различные уровни информации о конфигурации. У каждого устройства есть одна или несколько конфигураций, из которых одна выбирается во время обнаружения/подключения. Конфигурация определяет требования к питанию и пропускной способности. В каждой конфигурации может быть несколько интерфейсов. Интерфейс устройства — это набор конечных точек. Например, USB-колонки могут иметь интерфейс для аудиоданных (Audio Class) и интерфейс для регуляторов, ручек и кнопок (HID Class). Все интерфейсы в конфигурации активны одновременно и могут быть подключены разными драйверами. Каждый интерфейс может иметь альтернативные варианты, предоставляющие различные параметры качества обслуживания. Например, в камерах это используется для поддержки разных размеров кадра и количества кадров в секунду.
+
+В каждом интерфейсе может быть указано 0 или более конечных точек. Конечные точки — это однонаправленные точки доступа для связи с устройством. Они предоставляют буферы для временного хранения входящих или исходящих данных устройства. Каждая конечная точка имеет уникальный адрес в конфигурации — номер конечной точки плюс её направление. Конечная точка по умолчанию, конечная точка 0, не является частью какого-либо интерфейса и доступна во всех конфигурациях. Она управляется уровнем сервисов и не доступна напрямую драйверам устройств.
+
+Эта иерархическая конфигурационная информация описывается в устройстве стандартным набором дескрипторов (см. раздел 9.6 спецификации USB). Они могут быть запрошены через Get Descriptor Request. Сервисный уровень кэширует эти дескрипторы, чтобы избежать ненужных передач по шине USB. Доступ к дескрипторам предоставляется через вызовы функций.
+
+* Дескрипторы устройств: Общая информация об устройстве, такая как Vendor (производитель), Product (продукт) и Revision Id (идентификатор ревизии), поддерживаемый класс устройства, подкласс и протокол, если применимо, максимальный размер пакета для конечной точки по умолчанию и т. д.
+* Дескрипторы конфигурации: количество интерфейсов в данной конфигурации, поддержка функций приостановки и возобновления работы, а также требования к питанию.
+* Дескрипторы интерфейса: класс интерфейса, подкласс и протокол (если применимо), количество альтернативных настроек интерфейса и количество конечных точек.
+* Дескрипторы конечных точек: Адрес конечной точки, направление и тип, максимальный поддерживаемый размер пакета и частота опроса, если тип является конечной точкой прерывания. Для конечной точки по умолчанию (конечная точка 0) дескриптора не существует, и она никогда не учитывается в дескрипторе интерфейса.
+* Дескрипторы строк: В остальных дескрипторах для некоторых полей указываются индексы строк. Эти индексы могут использоваться для получения описательных строк, возможно, на нескольких языках.
+
+Спецификации классов могут добавлять свои собственные типы дескрипторов, которые доступны через запрос GetDescriptor.
+
+Канальный обмен данными с конечными точками устройства осуществляется через так называемые *каналы*. Драйверы передают данные конечным точкам через канал и предоставляют функцию обратного вызова, которая вызывается при завершении или сбое передачи (асинхронные передачи) или ожидают завершения (синхронная передача). Передачи данных в конечную точку сериализуются в канале. Передача может завершиться успешно, завершиться с ошибкой или превысить время ожидания (если оно было задано). Существует два типа таймаутов для передач. Таймауты могут происходить из-за истечения времени на шине USB (миллисекунды). Эти таймауты рассматриваются как сбои и могут быть вызваны отключением устройства. Вторая форма таймаута реализована на уровне программного обеспечения и срабатывает, если передача не завершается в течение заданного времени (секунды). Это происходит, когда устройство отрицательно подтверждает (NAK) переданные пакеты. Причинами могут быть неготовность устройства к приему данных, переполнение буфера или пустой буфер, либо ошибки протокола.
+
+Если передача через канал превышает максимальный размер пакета, указанный в соответствующем дескрипторе конечной точки, хост-контроллер (OHCI) или драйвер HC (UHCI) разделит передачу на пакеты максимального размера, при этом последний пакет может быть меньше максимального размера.
+
+Иногда для устройства не является проблемой вернуть меньше данных, чем запрошено. Например, при передаче данных в режиме bulk-in на модем может быть запрошено 200 байт данных, но у модема в данный момент доступно только 5 байт. Драйвер может установить флаг короткого пакета (SPD). Это позволяет хост-контроллеру принять пакет, даже если объем переданных данных меньше запрошенного. Этот флаг действителен только для in-передач, так как объем данных, отправляемых на устройство, всегда известен заранее. Если во время передачи в устройстве происходит неустранимая ошибка, канал останавливается (stalled). Прежде чем принимать или отправлять дополнительные данные, драйвер должен устранить причину остановки и сбросить условие остановки конечной точки, отправив запрос `clear endpoint halt` через канал по умолчанию. Конечная точка по умолчанию никогда не должна останавливаться.
+
+Существует четыре различных типа конечных точек и соответствующих каналов: - Управляющий канал / канал по умолчанию: На каждое устройство приходится один управляющий канал, подключенный к конечной точке по умолчанию (конечная точка 0). Этот канал передает запросы устройства и связанные с ними данные. Разница между передачами через канал по умолчанию и другими каналами заключается в том, что протокол для этих передач описан в спецификации USB. Эти запросы используются для сброса и настройки устройства. Базовый набор команд, который должен поддерживаться каждым устройством, приведен в главе 9 спецификации USB. Команды, поддерживаемые на этом канале, могут быть расширены спецификацией класса устройства для обеспечения дополнительной функциональности.
+
+* Массовый канал: Это USB-эквивалент среды передачи данных в сыром виде.
+* Прерывающий канал: Хост отправляет запрос данных на устройство, и если устройству нечего отправлять, оно отвечает NAK на пакет данных. Прерывающие передачи планируются с частотой, указанной при создании канала.
+* Изохронный канал: Эти каналы предназначены для изохронных данных, например, видео- или аудиопотоков, с фиксированной задержкой, но без гарантированной доставки. В текущей реализации доступна некоторая поддержка каналов этого типа. Пакеты в управляющих, массовых и прерывающих передачах повторяются, если во время передачи возникает ошибка или устройство отрицательно подтверждает пакет (NAK) из-за, например, нехватки буферного пространства для хранения входящих данных. Однако изохронные пакеты не повторяются в случае неудачной доставки или NAK пакета, так как это может нарушить временные ограничения.
+
+Доступность необходимой полосы пропускания рассчитывается во время создания канала. Передачи планируются в рамках интервалов в 1 миллисекунду. Распределение полосы пропускания внутри интервала регламентируется спецификацией USB, раздел 5.6 [2]. Изохронные и прерывающие передачи могут использовать до 90% полосы пропускания в рамках интервала. Пакеты для управляющих и массовых передач планируются после всех изохронных и прерывающих пакетов и используют всю оставшуюся полосу пропускания.
+
+Дополнительная информация о планировании передач и освобождении пропускной способности доступна в главе 5 спецификации USB, разделе 1.3 спецификации UHCI и разделе 3.4.2 спецификации OHCI.
+
+[[usb-devprobe]]
+== Обнаружение устройства и подключение
+
+После уведомления от концентратора о подключении нового устройства сервисный уровень включает порт, предоставляя устройству ток 100 мА. На этом этапе устройство находится в своём исходном состоянии и прослушивает адрес устройства 0. Сервисный уровень продолжит получение различных дескрипторов через стандартный канал. После этого он отправит запрос Set Address, чтобы переместить устройство с исходного адреса (адрес 0). Несколько драйверов устройств могут поддерживать данное устройство. Например, драйвер модема может поддерживать ISDN TA через интерфейс совместимости AT. Однако драйвер, предназначенный для конкретной модели ISDN-адаптера, может обеспечить гораздо лучшую поддержку этого устройства. Для обеспечения такой гибкости пробы возвращают приоритеты, указывающие уровень их поддержки. Поддержка конкретной версии продукта имеет наивысший приоритет, а универсальный драйвер — самый низкий. Также возможно, что несколько драйверов могут быть подключены к одному устройству, если в одной конфигурации присутствует несколько интерфейсов. Каждому драйверу требуется поддерживать только подмножество интерфейсов.
+
+Поиск драйвера для нового подключенного устройства сначала проверяет наличие специфичных для устройства драйверов. Если они не найдены, код проверки перебирает все поддерживаемые конфигурации, пока драйвер не будет подключен в одной из них. Для поддержки устройств с несколькими драйверами на разных интерфейсах проверка перебирает все интерфейсы в конфигурации, которые ещё не были заняты драйвером. Конфигурации, превышающие выделенный бюджет мощности для концентратора, игнорируются. Во время подключения драйвер должен инициализировать устройство в его рабочее состояние, но не сбрасывать его, так как это приведёт к отключению устройства от шины и перезапуску процесса проверки. Чтобы избежать излишнего потребления пропускной способности, не следует запрашивать канал прерывания во время подключения, а отложить его выделение до момента открытия файла и фактического использования данных. При закрытии файла канал следует снова закрыть, даже если устройство остаётся подключенным.
+
+=== Отключение и извлечение устройства
+
+Драйвер устройства должен ожидать получения ошибок во время любой транзакции с устройством. Дизайн USB поддерживает и поощряет отключение устройств в любой момент времени. Драйверы должны гарантировать корректную обработку ситуаций, когда устройство исчезает.
+
+Кроме того, устройство, которое было отключено и снова подключено, не будет повторно присоединено к тому же экземпляру устройства. Это может измениться в будущем, когда больше устройств будут поддерживать серийные номера (см. дескриптор устройства) или будут разработаны другие способы определения идентификатора устройства.
+
+Отключение устройства сигнализируется концентратором в пакете прерывания, передаваемом драйверу концентратора. Информация об изменении состояния указывает, на каком порту произошло изменение подключения. Метод отключения устройства для всех драйверов устройств, подключенных к этому порту, вызывается, и структуры очищаются. Если состояние порта указывает, что за это время к порту было подключено устройство, начнется процедура обнаружения и подключения устройства. Сброс устройства вызовет последовательность отключения-подключения на концентраторе и будет обработан, как описано выше.
+
+[[usb-protocol]]
+== Информация о протоколе драйверов USB
+
+Используемый протокол для каналов, отличных от стандартного, не определен спецификацией USB. Информацию об этом можно найти из различных источников. Наиболее точный источник — раздел для разработчиков на домашних страницах USB. На этих страницах доступно растущее количество спецификаций классов устройств. Эти спецификации определяют, каким должно быть совместимое устройство с точки зрения драйвера, базовую функциональность, которую оно должно предоставлять, и протокол, используемый на каналах связи. Спецификация USB включает описание класса концентраторов (Hub Class). Спецификация класса устройств человеко-машинного интерфейса (HID) была создана для поддержки клавиатур, планшетов, сканеров штрих-кодов, кнопок, регуляторов, переключателей и т. д. Третий пример — спецификация класса устройств хранения данных (Mass Storage). Полный список классов устройств можно найти в разделе для разработчиков на домашних страницах USB.
+
+Для многих устройств информация о протоколе ещё не опубликована. Сведения об используемом протоколе могут быть доступны у компании-производителя устройства. Некоторые компании потребуют подписания соглашения о неразглашении (NDA), прежде чем предоставить спецификации. В большинстве случаев это исключает возможность сделать драйвер открытым.
+
+Еще один хороший источник информации — исходные коды драйверов Linux, так как ряд компаний начал предоставлять драйверы для Linux для своих устройств. Всегда полезно связаться с авторами этих драйверов для получения информации.
+
+Пример: Устройства интерфейса пользователя (HID) — Спецификация для устройств интерфейса пользователя, таких как клавиатуры, мыши, планшеты, кнопки, регуляторы и т.д., упоминается в других спецификациях классов устройств и используется во многих устройствах.
+
+Например, аудиоколонки предоставляют конечные точки для цифро-аналоговых преобразователей и, возможно, дополнительный канал для микрофона. Они также предоставляют конечную точку HID в отдельном интерфейсе для кнопок и регуляторов на передней панели устройства. То же самое справедливо для класса управления монитором. Реализовать поддержку этих интерфейсов достаточно просто с помощью доступных библиотек ядра и пользовательского пространства, а также драйвера класса HID или универсального драйвера. Другим примером устройства с несколькими интерфейсами в одной конфигурации, управляемыми разными драйверами, является недорогая клавиатура со встроенным устаревшим портом для мыши. Чтобы избежать затрат на включение аппаратного обеспечения USB-концентратора в устройство, производители объединили данные мыши, полученные с порта PS/2 на задней панели клавиатуры, и нажатия клавиш в два отдельных интерфейса в одной конфигурации. Драйверы мыши и клавиатуры подключаются к соответствующему интерфейсу и выделяют каналы для двух независимых конечных точек.
+
+Пример: Загрузка микропрограммы — многие разработанные устройства основаны на процессоре общего назначения с добавленным USB-ядром. Поскольку разработка драйверов и микропрограмм для USB-устройств всё ещё очень нова, многие устройства требуют загрузки микропрограммы после подключения.
+
+Процедура выполняется следующим образом. Устройство идентифицирует себя через идентификаторы производителя и продукта. Первый драйвер проверяет его и подключается к нему, затем загружает в него микропрограмму. После этого устройство выполняет мягкую перезагрузку, и драйвер отключается. После небольшой паузы устройство снова появляется на шине. При этом идентификаторы производителя, продукта и версии устройства изменятся, что отражает факт загрузки микропрограммы, и в результате второй драйвер проверит его и подключится к нему.
+
+Примером таких устройств является плата ввода-вывода ActiveWire, основанная на чипе EZ-USB. Для этого чипа доступен универсальный загрузчик микропрограмм. Микропрограмма, загруженная в плату ActiveWire, изменяет идентификатор ревизии. Затем выполняется мягкий сброс USB-части чипа EZ-USB для отключения от USB-шины и повторного подключения.
+
+Пример: Поддержка устройств хранения данных в основном построена на существующих протоколах. Устройство Iomega USB Zipdrive основано на SCSI-версии их накопителя. SCSI-команды и статусные сообщения упаковываются в блоки и передаются через массовые каналы к устройству и от него, эмулируя SCSI-контроллер через USB-соединение. ATAPI и UFI команды поддерживаются аналогичным образом.
+
+Спецификация Mass Storage поддерживает 2 различных типа обёртки командного блока. Первоначальная попытка была основана на отправке команды и состояния через канал по умолчанию с использованием массовых передач для данных, перемещаемых между хостом и устройством. На основе опыта был разработан второй подход, основанный на обёртке командных и статусных блоков и их отправке через конечные точки массовой передачи (bulk out и bulk in). Спецификация точно определяет, что должно происходить и когда, а также что необходимо делать в случае возникновения ошибки. Наибольшую сложность при написании драйверов для таких устройств представляет встраивание USB-ориентированного протокола в существующую поддержку устройств хранения данных. CAM предоставляет механизмы для этого достаточно прямолинейным способом. С ATAPI всё менее просто, так как исторически интерфейс IDE никогда не имел множества различных вариантов реализации.
+
+Поддержка USB-дисковода от Y-E Data также не прямолинейна, так как был разработан новый набор команд.
diff --git a/documentation/content/ru/books/arch-handbook/usb/_index.po b/documentation/content/ru/books/arch-handbook/usb/_index.po
new file mode 100644
index 0000000000..9cb9288b11
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/usb/_index.po
@@ -0,0 +1,1200 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-07-12 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookusb_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:1
+#, no-wrap
+msgid "USB Devices in FreeBSD"
+msgstr "Устройства USB в FreeBSD"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:1
+#, no-wrap
+msgid "Chapter 13. USB Devices"
+msgstr "Глава 13. USB-устройства"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:14
+#, no-wrap
+msgid "USB Devices"
+msgstr "Устройства USB"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:52
+#, no-wrap
+msgid "Introduction"
+msgstr "Введение"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:55
+msgid ""
+"The Universal Serial Bus (USB) is a new way of attaching devices to personal "
+"computers. The bus architecture features two-way communication and has been "
+"developed as a response to devices becoming smarter and requiring more "
+"interaction with the host. USB support is included in all current PC "
+"chipsets and is therefore available in all recently built PCs. Apple's "
+"introduction of the USB-only iMac has been a major incentive for hardware "
+"manufacturers to produce USB versions of their devices. The future PC "
+"specifications specify that all legacy connectors on PCs should be replaced "
+"by one or more USB connectors, providing generic plug and play capabilities. "
+"Support for USB hardware was available at a very early stage in NetBSD and "
+"was developed by Lennart Augustsson for the NetBSD project. The code has "
+"been ported to FreeBSD and we are currently maintaining a shared code base. "
+"For the implementation of the USB subsystem a number of features of USB are "
+"important."
+msgstr ""
+"Универсальная последовательная шина (USB) — это новый способ подключения "
+"устройств к персональным компьютерам. Архитектура шины поддерживает "
+"двустороннюю связь и была разработана в ответ на усложнение устройств, "
+"требующих большего взаимодействия с хостом. Поддержка USB включена во все "
+"современные чипсеты ПК и, следовательно, доступна во всех недавно собранных "
+"компьютерах. Выпуск Apple iMac только с USB стал серьёзным стимулом для "
+"производителей оборудования выпускать USB-версии своих устройств. Согласно "
+"будущим спецификациям ПК, все устаревшие разъёмы должны быть заменены одним "
+"или несколькими USB-разъёмами, обеспечивающими универсальные возможности "
+"plug and play. Поддержка USB-оборудования появилась в NetBSD на очень раннем "
+"этапе и была разработана Леннартом Аугустссоном для проекта NetBSD. Код был "
+"портирован в FreeBSD, и в настоящее время мы поддерживаем общую кодовая "
+"базу. Для реализации подсистемы USB важны некоторые особенности USB."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:57
+msgid ""
+"_Lennart Augustsson has done most of the implementation of the USB support "
+"for the NetBSD project. Many thanks for this incredible amount of work. Many "
+"thanks also to Ardy and Dirk for their comments and proofreading of this "
+"paper._"
+msgstr ""
+"_Леннарт Аугустссон выполнил большую часть работы по реализации поддержки "
+"USB для проекта NetBSD. Огромная благодарность за этот невероятный объем "
+"работы. Также большое спасибо Арди и Дирку за их комментарии и вычитку этой "
+"статьи._"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:59
+msgid ""
+"Devices connect to ports on the computer directly or on devices called hubs, "
+"forming a treelike device structure."
+msgstr ""
+"Устройства подключаются к портам компьютера напрямую или через устройства, "
+"называемые концентраторами, образуя древовидную структуру устройств."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:60
+msgid "The devices can be connected and disconnected at run time."
+msgstr "Устройства можно подключать и отключать во время работы."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:61
+msgid "Devices can suspend themselves and trigger resumes of the host system"
+msgstr ""
+"Устройства могут приостанавливать свою работу и инициировать возобновление "
+"работы основной системы"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:62
+msgid ""
+"As the devices can be powered from the bus, the host software has to keep "
+"track of power budgets for each hub."
+msgstr ""
+"Поскольку устройства могут получать питание от шины, программное обеспечение "
+"хоста должно отслеживать энергопотребление для каждого концентратора."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:63
+msgid ""
+"Different quality of service requirements by the different device types "
+"together with the maximum of 126 devices that can be connected to the same "
+"bus, require proper scheduling of transfers on the shared bus to take full "
+"advantage of the 12Mbps bandwidth available. (over 400Mbps with USB 2.0)"
+msgstr ""
+"Различные требования к качеству обслуживания для разных типов устройств, а "
+"также максимум в 126 устройств, которые могут быть подключены к одной шине, "
+"требуют правильного планирования передач по общей шине, чтобы полностью "
+"использовать доступную пропускную способность в 12 Мбит/с (более 400 Мбит/с "
+"для USB 2.0)"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:64
+msgid ""
+"Devices are intelligent and contain easily accessible information about "
+"themselves"
+msgstr ""
+"Устройства являются интеллектуальными и содержат легко доступную информацию "
+"о себе"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:66
+msgid ""
+"The development of drivers for the USB subsystem and devices connected to it "
+"is supported by the specifications that have been developed and will be "
+"developed. These specifications are publicly available from the USB home "
+"pages. Apple has been very strong in pushing for standards based drivers, by "
+"making drivers for the generic classes available in their operating system "
+"MacOS and discouraging the use of separate drivers for each new device. This "
+"chapter tries to collate essential information for a basic understanding of "
+"the USB 2.0 implementation stack in FreeBSD/NetBSD. It is recommended "
+"however to read it together with the relevant 2.0 specifications and other "
+"developer resources:"
+msgstr ""
+"Разработка драйверов для подсистемы USB и подключенных к ней устройств "
+"поддерживается спецификациями, которые были разработаны и будут "
+"разрабатываться. Эти спецификации общедоступны на домашних страницах USB. "
+"Apple активно продвигает стандартизированные драйверы, предоставляя драйверы "
+"для универсальных классов в своей операционной системе MacOS и не поощряя "
+"использование отдельных драйверов для каждого нового устройства. Эта глава "
+"пытается собрать основную информацию для базового понимания реализации стека "
+"USB 2.0 в FreeBSD/NetBSD. Однако рекомендуется читать её вместе с "
+"соответствующими спецификациями 2.0 и другими ресурсами для разработчиков:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:68
+msgid ""
+"USB 2.0 Specification (http://www.usb.org/developers/docs/usb20_docs/[http://"
+"www.usb.org/developers/docs/usb20_docs/])"
+msgstr ""
+"Спецификация USB 2.0 (http://www.usb.org/developers/docs/usb20_docs/[http://"
+"www.usb.org/developers/docs/usb20_docs/])"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:69
+msgid ""
+"Universal Host Controller Interface (UHCI) Specification (link:ftp://"
+"ftp.netbsd.org/pub/NetBSD/misc/blymn/uhci11d.pdf[ftp://ftp.netbsd.org/pub/"
+"NetBSD/misc/blymn/uhci11d.pdf)]"
+msgstr ""
+"Универсальный интерфейс хост-контроллера (UHCI) Спецификация (link:ftp://"
+"ftp.netbsd.org/pub/NetBSD/misc/blymn/uhci11d.pdf[ftp://ftp.netbsd.org/pub/"
+"NetBSD/misc/blymn/uhci11d.pdf])"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:70
+msgid ""
+"Open Host Controller Interface (OHCI) Specification(link:ftp://"
+"ftp.compaq.com/pub/supportinformation/papers/hcir1_0a.pdf[ftp://"
+"ftp.compaq.com/pub/supportinformation/papers/hcir1_0a.pdf])"
+msgstr ""
+"Спецификация интерфейса Open Host Controller (OHCI)(link:ftp://"
+"ftp.compaq.com/pub/supportinformation/papers/hcir1_0a.pdf[ftp://"
+"ftp.compaq.com/pub/supportinformation/papers/hcir1_0a.pdf])"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:71
+msgid ""
+"Developer section of USB home page (http://www.usb.org/developers/[http://"
+"www.usb.org/developers/])"
+msgstr ""
+"Раздел для разработчиков на домашней странице USB (http://www.usb.org/"
+"developers/[http://www.usb.org/developers/])"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:72
+#, no-wrap
+msgid "Structure of the USB Stack"
+msgstr "Структура стека USB"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:75
+msgid ""
+"The USB support in FreeBSD can be split into three layers. The lowest layer "
+"contains the host controller driver, providing a generic interface to the "
+"hardware and its scheduling facilities. It supports initialisation of the "
+"hardware, scheduling of transfers and handling of completed and/or failed "
+"transfers. Each host controller driver implements a virtual hub providing "
+"hardware independent access to the registers controlling the root ports on "
+"the back of the machine."
+msgstr ""
+"Поддержка USB в FreeBSD может быть разделена на три уровня. Самый нижний "
+"уровень содержит драйвер контроллера хоста, предоставляющий универсальный "
+"интерфейс к оборудованию и его механизмам планирования. Он поддерживает "
+"инициализацию оборудования, планирование передач и обработку завершённых и/"
+"или неудачных передач. Каждый драйвер контроллера хоста реализует "
+"виртуальный концентратор, обеспечивающий независимый от оборудования доступ "
+"к регистрам, управляющим корневыми портами на задней панели машины."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:77
+msgid ""
+"The middle layer handles the device connection and disconnection, basic "
+"initialisation of the device, driver selection, the communication channels "
+"(pipes) and does resource management. This services layer also controls the "
+"default pipes and the device requests transferred over them."
+msgstr ""
+"Средний уровень обрабатывает подключение и отключение устройства, базовую "
+"инициализацию устройства, выбор драйвера, каналы связи (pipe) и управляет "
+"ресурсами. Этот сервисный уровень также контролирует стандартные каналы и "
+"запросы устройств, передаваемые через них."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:79
+msgid ""
+"The top layer contains the individual drivers supporting specific (classes "
+"of) devices. These drivers implement the protocol that is used over the "
+"pipes other than the default pipe. They also implement additional "
+"functionality to make the device available to other parts of the kernel or "
+"userland. They use the USB driver interface (USBDI) exposed by the services "
+"layer."
+msgstr ""
+"Верхний уровень содержит отдельные драйверы, поддерживающие конкретные "
+"(классы) устройств. Эти драйверы реализуют протокол, используемый в каналах, "
+"отличных от стандартного. Они также реализуют дополнительную "
+"функциональность для обеспечения доступа к устройству другим частям ядра или "
+"пользовательского пространства. Они используют интерфейс драйвера USB "
+"(USBDI), предоставляемый уровнем сервисов."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:81
+#, no-wrap
+msgid "Host Controllers"
+msgstr "Контроллеры хоста"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:84
+msgid ""
+"The host controller (HC) controls the transmission of packets on the bus. "
+"Frames of 1 millisecond are used. At the start of each frame the host "
+"controller generates a Start of Frame (SOF) packet."
+msgstr ""
+"Хост-контроллер (HC) управляет передачей пакетов на шине. Используются кадры "
+"длительностью 1 миллисекунда. В начале каждого кадра хост-контроллер "
+"генерирует пакет Start of Frame (SOF)."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:86
+msgid ""
+"The SOF packet is used to synchronise to the start of the frame and to keep "
+"track of the frame number. Within each frame packets are transferred, either "
+"from host to device (out) or from device to host (in). Transfers are always "
+"initiated by the host (polled transfers). Therefore there can only be one "
+"host per USB bus. Each transfer of a packet has a status stage in which the "
+"recipient of the data can return either ACK (acknowledge reception), NAK "
+"(retry), STALL (error condition) or nothing (garbled data stage, device not "
+"available or disconnected). Section 8.5 of the USB 2.0 Specification "
+"explains the details of packets in more detail. Four different types of "
+"transfers can occur on a USB bus: control, bulk, interrupt and isochronous. "
+"The types of transfers and their characteristics are described below."
+msgstr ""
+"Пакет SOF используется для синхронизации начала кадра и отслеживания номера "
+"кадра. Внутри каждого кадра передаются пакеты, либо от хоста к устройству "
+"(исходящие), либо от устройства к хосту (входящие). Передачи всегда "
+"инициируются хостом (опросные передачи). Поэтому на каждой шине USB может "
+"быть только один хост. Каждая передача пакета имеет стадию статуса, в "
+"которой получатель данных может вернуть либо ACK (подтверждение приема), NAK "
+"(повторить), STALL (ошибка) либо ничего (искаженная стадия данных, "
+"устройство недоступно или отключено). В разделе 8.5 спецификации USB 2.0 "
+"подробно объясняются детали пакетов. На шине USB могут происходить четыре "
+"различных типа передач: управляющие, массовые, прерывания и изохронные. Типы "
+"передач и их характеристики описаны ниже."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:88
+msgid ""
+"Large transfers between the device on the USB bus and the device driver are "
+"split up into multiple packets by the host controller or the HC driver."
+msgstr ""
+"Крупные передачи между устройством на шине USB и драйвером устройства "
+"разделяются на несколько пакетов хост-контроллером или драйвером HC."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:90
+msgid ""
+"Device requests (control transfers) to the default endpoints are special. "
+"They consist of two or three phases: SETUP, DATA (optional) and STATUS. The "
+"set-up packet is sent to the device. If there is a data phase, the direction "
+"of the data packet(s) is given in the set-up packet. The direction in the "
+"status phase is the opposite of the direction during the data phase, or IN "
+"if there was no data phase. The host controller hardware also provides "
+"registers with the current status of the root ports and the changes that "
+"have occurred since the last reset of the status change register. Access to "
+"these registers is provided through a virtualised hub as suggested in the "
+"USB specification. The virtual hub must comply with the hub device class "
+"given in chapter 11 of that specification. It must provide a default pipe "
+"through which device requests can be sent to it. It returns the standard "
+"andhub class specific set of descriptors. It should also provide an "
+"interrupt pipe that reports changes happening at its ports. There are "
+"currently two specifications for host controllers available: Universal Host "
+"Controller Interface (UHCI) from Intel and Open Host Controller Interface "
+"(OHCI) from Compaq, Microsoft, and National Semiconductor. The UHCI "
+"specification has been designed to reduce hardware complexity by requiring "
+"the host controller driver to supply a complete schedule of the transfers "
+"for each frame. OHCI type controllers are much more independent by providing "
+"a more abstract interface doing a lot of work themselves."
+msgstr ""
+"Запросы устройств (управляющие передачи) к конечным точкам по умолчанию "
+"являются особыми. Они состоят из двух или трёх фаз: SETUP, DATA "
+"(опционально) и STATUS. Пакет настройки отправляется на устройство. Если "
+"присутствует фаза данных, направление пакета(ов) данных указывается в пакете "
+"настройки. Направление в фазе статуса противоположно направлению во время "
+"фазы данных или IN, если фазы данных не было. Аппаратное обеспечение хост-"
+"контроллера также предоставляет регистры с текущим состоянием корневых "
+"портов и изменениями, произошедшими с момента последнего сброса регистра "
+"изменений статуса. Доступ к этим регистрам предоставляется через "
+"виртуализированный концентратор, как предложено в спецификации USB. "
+"Виртуальный концентратор должен соответствовать классу устройств "
+"концентратора, указанному в главе 11 этой спецификации. Он должен "
+"предоставлять канал по умолчанию, через который можно отправлять запросы "
+"устройств. Он возвращает стандартные и специфичные для класса концентратора "
+"наборы дескрипторов. Также он должен предоставлять прерывающий канал, "
+"сообщающий об изменениях, происходящих на его портах. В настоящее время "
+"доступны две спецификации для хост-контроллеров: - Universal Host Controller "
+"Interface (UHCI) от Intel и Open Host Controller Interface (OHCI) от Compaq, "
+"Microsoft и National Semiconductor. Спецификация UHCI разработана для "
+"уменьшения аппаратной сложности, требуя от драйвера хост-контроллера "
+"предоставления полного расписания передач для каждого кадра. Контроллеры "
+"типа OHCI гораздо более независимы, предоставляя более абстрактный интерфейс "
+"и выполняя большую часть работы самостоятельно."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:91
+#, no-wrap
+msgid "UHCI"
+msgstr "UHCI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:94
+msgid ""
+"The UHCI host controller maintains a framelist with 1024 pointers to per "
+"frame data structures. It understands two different data types: transfer "
+"descriptors (TD) and queue heads (QH). Each TD represents a packet to be "
+"communicated to or from a device endpoint. QHs are a means to groupTDs (and "
+"QHs) together."
+msgstr ""
+"Контроллер UHCI поддерживает список кадров (framelist) с 1024 указателями на "
+"структуры данных для каждого кадра. Он распознаёт два типа данных: "
+"дескрипторы передачи (TD) и головы очередей (QH). Каждый TD представляет "
+"пакет для передачи в конечную точку устройства или из неё. QH служат для "
+"группировки TD (и других QH) вместе."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:96
+msgid ""
+"Each transfer consists of one or more packets. The UHCI driver splits large "
+"transfers into multiple packets. For every transfer, apart from isochronous "
+"transfers, a QH is allocated. For every type of transfer these QHs are "
+"collected at a QH for that type. Isochronous transfers have to be executed "
+"first because of the fixed latency requirement and are directly referred to "
+"by the pointer in the framelist. The last isochronous TD refers to the QH "
+"for interrupt transfers for that frame. All QHs for interrupt transfers "
+"point at the QH for control transfers, which in turn points at the QH for "
+"bulk transfers. The following diagram gives a graphical overview of this:"
+msgstr ""
+"Каждая передача состоит из одного или нескольких пакетов. Драйвер UHCI "
+"разделяет большие передачи на несколько пакетов. Для каждой передачи, за "
+"исключением изохронных, выделяется QH. Для каждого типа передачи эти QH "
+"собираются в QH для соответствующего типа. Изохронные передачи должны "
+"выполняться первыми из-за требования фиксированной задержки и "
+"непосредственно указываются указателем в списке кадров. Последний изохронный "
+"TD ссылается на QH для прерывающих передач для этого кадра. Все QH для "
+"прерывающих передач указывают на QH для управляющих передач, который, в свою "
+"очередь, указывает на QH для массовых передач. Следующая диаграмма дает "
+"графическое представление этого:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:98
+msgid ""
+"This results in the following schedule being run in each frame. After "
+"fetching the pointer for the current frame from the framelist the controller "
+"first executes the TDs for all the isochronous packets in that frame. The "
+"last of these TDs refers to the QH for the interrupt transfers for that "
+"frame. The host controller will then descend from that QH to the QHs for the "
+"individual interrupt transfers. After finishing that queue, the QH for the "
+"interrupt transfers will refer the controller to the QH for all control "
+"transfers. It will execute all the subqueues scheduled there, followed by "
+"all the transfers queued at the bulk QH. To facilitate the handling of "
+"finished or failed transfers different types of interrupts are generated by "
+"the hardware at the end of each frame. In the last TD for a transfer the "
+"Interrupt-On Completion bit is set by the HC driver to flag an interrupt "
+"when the transfer has completed. An error interrupt is flagged if a TD "
+"reaches its maximum error count. If the short packet detect bit is set in a "
+"TD and less than the set packet length is transferred this interrupt is "
+"flagged to notify the controller driver of the completed transfer. It is the "
+"host controller driver's task to find out which transfer has completed or "
+"produced an error. When called the interrupt service routine will locate all "
+"the finished transfers and call their callbacks."
+msgstr ""
+"В результате выполняется следующее расписание в каждом кадре. После "
+"получения указателя на текущий кадр из списка кадров контроллер сначала "
+"выполняет TDs для всех изохронных пакетов в этом кадре. Последний из этих "
+"TDs ссылается на QH для прерывающих передач этого кадра. Хост-контроллер "
+"затем переходит от этого QH к QHs для отдельных прерывающих передач. После "
+"завершения этой очереди, QH для прерывающих передач ссылается на QH для всех "
+"управляющих передач. Он выполняет все подочереди, запланированные там, а "
+"затем все передачи, поставленные в очередь в QH для массовых передач. Для "
+"упрощения обработки завершенных или неудачных передач аппаратное обеспечение "
+"генерирует различные типы прерываний в конце каждого кадра. В последнем TD "
+"для передачи бит *Interrupt-On Completion* устанавливается драйвером HC, "
+"чтобы вызвать прерывание по завершении передачи. Прерывание ошибки "
+"возникает, если TD достигает максимального количества ошибок. Если в TD "
+"установлен бит *short packet detect* и передано меньше установленной длины "
+"пакета, это прерывание уведомляет драйвер контроллера о завершении передачи. "
+"Задача драйвера хост-контроллера — определить, какая передача завершилась "
+"или вызвала ошибку. При вызове процедура обслуживания прерывания находит все "
+"завершенные передачи и вызывает их callback-функции."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:100
+msgid "Refer to the UHCI Specification for a more elaborate description."
+msgstr "Обратитесь к спецификации UHCI для более подробного описания."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:101
+#, no-wrap
+msgid "OHCI"
+msgstr "OHCI"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:104
+msgid ""
+"Programming an OHCI host controller is much simpler. The controller assumes "
+"that a set of endpoints is available, and is aware of scheduling priorities "
+"and the ordering of the types of transfers in a frame. The main data "
+"structure used by the host controller is the endpoint descriptor (ED) to "
+"which a queue of transfer descriptors (TDs) is attached. The ED contains the "
+"maximum packet size allowed for an endpoint and the controller hardware does "
+"the splitting into packets. The pointers to the data buffers are updated "
+"after each transfer and when the start and end pointer are equal, the TD is "
+"retired to the done-queue. The four types of endpoints (interrupt, "
+"isochronous, control, and bulk) have their own queues. Control and bulk "
+"endpoints are queued each at their own queue. Interrupt EDs are queued in a "
+"tree, with the level in the tree defining the frequency at which they run."
+msgstr ""
+"Программирование OHCI-контроллера значительно проще. Контроллер "
+"предполагает, что доступен набор конечных точек, и учитывает приоритеты "
+"планирования и порядок типов передач в кадре. Основная структура данных, "
+"используемая хост-контроллером, — это дескриптор конечной точки (ED), к "
+"которому присоединена очередь дескрипторов передачи (TD). ED содержит "
+"максимальный размер пакета, разрешенный для конечной точки, а аппаратное "
+"обеспечение контроллера выполняет разделение на пакеты. Указатели на буферы "
+"данных обновляются после каждой передачи, и когда начальный и конечный "
+"указатели становятся равны, TD перемещается в очередь завершенных (done-"
+"queue). Четыре типа конечных точек (прерывание, изохронная, управление и "
+"массовая) имеют свои собственные очереди. Управляющие и массовые конечные "
+"точки помещаются каждая в свою очередь. ED прерываний организуются в дерево, "
+"где уровень в дереве определяет частоту их выполнения."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:106
+msgid ""
+"The schedule being run by the host controller in each frame looks as "
+"follows. The controller will first run the non-periodic control and bulk "
+"queues, up to a time limit set by the HC driver. Then the interrupt "
+"transfers for that frame number are run, by using the lower five bits of the "
+"frame number as an index into level 0 of the tree of interrupts EDs. At the "
+"end of this tree the isochronous EDs are connected and these are traversed "
+"subsequently. The isochronous TDs contain the frame number of the first "
+"frame the transfer should be run in. After all the periodic transfers have "
+"been run, the control and bulk queues are traversed again. Periodically the "
+"interrupt service routine is called to process the done queue and call the "
+"callbacks for each transfer and reschedule interrupt and isochronous "
+"endpoints."
+msgstr ""
+"Порядок действий, выполняемых хост-контроллером в каждом кадре, выглядит "
+"следующим образом. Контроллер сначала запускает непериодические очереди "
+"управления и массовых передач, вплоть до ограничения времени, установленного "
+"драйвером HC. Затем выполняются прерывающие передачи для данного номера "
+"кадра, используя младшие пять битов номера кадра в качестве индекса уровня 0 "
+"дерева ED прерываний. В конце этого дерева подключены изохронные ED, которые "
+"затем обходятся. Изохронные TD содержат номер кадра, в котором должна быть "
+"запущена первая передача. После выполнения всех периодических передач "
+"очереди управления и массовых передач обходятся снова. Периодически "
+"вызывается процедура обслуживания прерывания для обработки очереди "
+"завершенных операций и вызова обратных вызовов для каждой передачи, а также "
+"для перепланирования прерывающих и изохронных конечных точек."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:108
+msgid ""
+"See the UHCI Specification for a more elaborate description. The middle "
+"layer provides access to the device in a controlled way and maintains "
+"resources in use by the different drivers and the services layer. The layer "
+"takes care of the following aspects:"
+msgstr ""
+"См. UHCI Specification для более подробного описания. Средний уровень "
+"обеспечивает контролируемый доступ к устройству и управляет ресурсами, "
+"используемыми различными драйверами и уровнем сервисов. Этот уровень "
+"отвечает за следующие аспекты:"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:110
+msgid "The device configuration information"
+msgstr "Информация о конфигурации устройства"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:111
+msgid "The pipes to communicate with a device"
+msgstr "Каналы для взаимодействия с устройством"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:112
+msgid "Probing and attaching and detaching form a device."
+msgstr "Обнаружение, подключение и отключение от устройства."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:114
+#, no-wrap
+msgid "USB Device Information"
+msgstr "Информация об устройстве USB"
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:116
+#, no-wrap
+msgid "Device Configuration Information"
+msgstr "Информация о конфигурации устройства"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:119
+msgid ""
+"Each device provides different levels of configuration information. Each "
+"device has one or more configurations, of which one is selected during probe/"
+"attach. A configuration provides power and bandwidth requirements. Within "
+"each configuration there can be multiple interfaces. A device interface is a "
+"collection of endpoints. For example USB speakers can have an interface for "
+"the audio data (Audio Class) and an interface for the knobs, dials and "
+"buttons (HID Class). All interfaces in a configuration are active at the "
+"same time and can be attached to by different drivers. Each interface can "
+"have alternates, providing different quality of service parameters. In for "
+"example cameras this is used to provide different frame sizes and numbers of "
+"frames per second."
+msgstr ""
+"Каждое устройство предоставляет различные уровни информации о конфигурации. "
+"У каждого устройства есть одна или несколько конфигураций, из которых одна "
+"выбирается во время обнаружения/подключения. Конфигурация определяет "
+"требования к питанию и пропускной способности. В каждой конфигурации может "
+"быть несколько интерфейсов. Интерфейс устройства — это набор конечных точек. "
+"Например, USB-колонки могут иметь интерфейс для аудиоданных (Audio Class) и "
+"интерфейс для регуляторов, ручек и кнопок (HID Class). Все интерфейсы в "
+"конфигурации активны одновременно и могут быть подключены разными "
+"драйверами. Каждый интерфейс может иметь альтернативные варианты, "
+"предоставляющие различные параметры качества обслуживания. Например, в "
+"камерах это используется для поддержки разных размеров кадра и количества "
+"кадров в секунду."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:121
+msgid ""
+"Within each interface, 0 or more endpoints can be specified. Endpoints are "
+"the unidirectional access points for communicating with a device. They "
+"provide buffers to temporarily store incoming or outgoing data from the "
+"device. Each endpoint has a unique address within a configuration, the "
+"endpoint's number plus its direction. The default endpoint, endpoint 0, is "
+"not part of any interface and available in all configurations. It is managed "
+"by the services layer and not directly available to device drivers."
+msgstr ""
+"В каждом интерфейсе может быть указано 0 или более конечных точек. Конечные "
+"точки — это однонаправленные точки доступа для связи с устройством. Они "
+"предоставляют буферы для временного хранения входящих или исходящих данных "
+"устройства. Каждая конечная точка имеет уникальный адрес в конфигурации — "
+"номер конечной точки плюс её направление. Конечная точка по умолчанию, "
+"конечная точка 0, не является частью какого-либо интерфейса и доступна во "
+"всех конфигурациях. Она управляется уровнем сервисов и не доступна напрямую "
+"драйверам устройств."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:123
+msgid ""
+"This hierarchical configuration information is described in the device by a "
+"standard set of descriptors (see section 9.6 of the USB specification). They "
+"can be requested through the Get Descriptor Request. The services layer "
+"caches these descriptors to avoid unnecessary transfers on the USB bus. "
+"Access to the descriptors is provided through function calls."
+msgstr ""
+"Эта иерархическая конфигурационная информация описывается в устройстве "
+"стандартным набором дескрипторов (см. раздел 9.6 спецификации USB). Они "
+"могут быть запрошены через Get Descriptor Request. Сервисный уровень "
+"кэширует эти дескрипторы, чтобы избежать ненужных передач по шине USB. "
+"Доступ к дескрипторам предоставляется через вызовы функций."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:125
+msgid ""
+"Device descriptors: General information about the device, like Vendor, "
+"Product and Revision Id, supported device class, subclass and protocol if "
+"applicable, maximum packet size for the default endpoint, etc."
+msgstr ""
+"Дескрипторы устройств: Общая информация об устройстве, такая как Vendor "
+"(производитель), Product (продукт) и Revision Id (идентификатор ревизии), "
+"поддерживаемый класс устройства, подкласс и протокол, если применимо, "
+"максимальный размер пакета для конечной точки по умолчанию и т. д."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:126
+msgid ""
+"Configuration descriptors: The number of interfaces in this configuration, "
+"suspend and resume functionality supported and power requirements."
+msgstr ""
+"Дескрипторы конфигурации: количество интерфейсов в данной конфигурации, "
+"поддержка функций приостановки и возобновления работы, а также требования к "
+"питанию."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:127
+msgid ""
+"Interface descriptors: interface class, subclass and protocol if applicable, "
+"number of alternate settings for the interface and the number of endpoints."
+msgstr ""
+"Дескрипторы интерфейса: класс интерфейса, подкласс и протокол (если "
+"применимо), количество альтернативных настроек интерфейса и количество "
+"конечных точек."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:128
+msgid ""
+"Endpoint descriptors: Endpoint address, direction and type, maximum packet "
+"size supported and polling frequency if type is interrupt endpoint. There is "
+"no descriptor for the default endpoint (endpoint 0) and it is never counted "
+"in an interface descriptor."
+msgstr ""
+"Дескрипторы конечных точек: Адрес конечной точки, направление и тип, "
+"максимальный поддерживаемый размер пакета и частота опроса, если тип "
+"является конечной точкой прерывания. Для конечной точки по умолчанию "
+"(конечная точка 0) дескриптора не существует, и она никогда не учитывается в "
+"дескрипторе интерфейса."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:129
+msgid ""
+"String descriptors: In the other descriptors string indices are supplied for "
+"some fields.These can be used to retrieve descriptive strings, possibly in "
+"multiple languages."
+msgstr ""
+"Дескрипторы строк: В остальных дескрипторах для некоторых полей указываются "
+"индексы строк. Эти индексы могут использоваться для получения описательных "
+"строк, возможно, на нескольких языках."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:131
+msgid ""
+"Class specifications can add their own descriptor types that are available "
+"through the GetDescriptor Request."
+msgstr ""
+"Спецификации классов могут добавлять свои собственные типы дескрипторов, "
+"которые доступны через запрос GetDescriptor."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:133
+msgid ""
+"Pipes Communication to end points on a device flows through so-called pipes. "
+"Drivers submit transfers to endpoints to a pipe and provide a callback to be "
+"called on completion or failure of the transfer (asynchronous transfers) or "
+"wait for completion (synchronous transfer). Transfers to an endpoint are "
+"serialised in the pipe. A transfer can either complete, fail or time-out (if "
+"a time-out has been set). There are two types of time-outs for transfers. "
+"Time-outs can happen due to time-out on the USBbus (milliseconds). These "
+"time-outs are seen as failures and can be due to disconnection of the "
+"device. A second form of time-out is implemented in software and is "
+"triggered when a transfer does not complete within a specified amount of "
+"time (seconds). These are caused by a device acknowledging negatively (NAK) "
+"the transferred packets. The cause for this is the device not being ready to "
+"receive data, buffer under- or overrun or protocol errors."
+msgstr ""
+"Канальный обмен данными с конечными точками устройства осуществляется через "
+"так называемые *каналы*. Драйверы передают данные конечным точкам через "
+"канал и предоставляют функцию обратного вызова, которая вызывается при "
+"завершении или сбое передачи (асинхронные передачи) или ожидают завершения "
+"(синхронная передача). Передачи данных в конечную точку сериализуются в "
+"канале. Передача может завершиться успешно, завершиться с ошибкой или "
+"превысить время ожидания (если оно было задано). Существует два типа "
+"таймаутов для передач. Таймауты могут происходить из-за истечения времени на "
+"шине USB (миллисекунды). Эти таймауты рассматриваются как сбои и могут быть "
+"вызваны отключением устройства. Вторая форма таймаута реализована на уровне "
+"программного обеспечения и срабатывает, если передача не завершается в "
+"течение заданного времени (секунды). Это происходит, когда устройство "
+"отрицательно подтверждает (NAK) переданные пакеты. Причинами могут быть "
+"неготовность устройства к приему данных, переполнение буфера или пустой "
+"буфер, либо ошибки протокола."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:135
+msgid ""
+"If a transfer over a pipe is larger than the maximum packet size specified "
+"in the associated endpoint descriptor, the host controller (OHCI) or the HC "
+"driver (UHCI) will split the transfer into packets of maximum packet size, "
+"with the last packet possibly smaller than the maximum packet size."
+msgstr ""
+"Если передача через канал превышает максимальный размер пакета, указанный в "
+"соответствующем дескрипторе конечной точки, хост-контроллер (OHCI) или "
+"драйвер HC (UHCI) разделит передачу на пакеты максимального размера, при "
+"этом последний пакет может быть меньше максимального размера."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:137
+msgid ""
+"Sometimes it is not a problem for a device to return less data than "
+"requested. For example abulk-in-transfer to a modem might request 200 bytes "
+"of data, but the modem has only 5 bytes available at that time. The driver "
+"can set the short packet (SPD) flag. It allows the host controller to accept "
+"a packet even if the amount of data transferred is less than requested. This "
+"flag is only valid for in-transfers, as the amount of data to be sent to a "
+"device is always known beforehand. If an unrecoverable error occurs in a "
+"device during a transfer the pipe is stalled. Before any more data is "
+"accepted or sent the driver needs to resolve the cause of the stall and "
+"clear the endpoint stall condition through send the clear endpoint halt "
+"device request over the default pipe. The default endpoint should never "
+"stall."
+msgstr ""
+"Иногда для устройства не является проблемой вернуть меньше данных, чем "
+"запрошено. Например, при передаче данных в режиме bulk-in на модем может "
+"быть запрошено 200 байт данных, но у модема в данный момент доступно только "
+"5 байт. Драйвер может установить флаг короткого пакета (SPD). Это позволяет "
+"хост-контроллеру принять пакет, даже если объем переданных данных меньше "
+"запрошенного. Этот флаг действителен только для in-передач, так как объем "
+"данных, отправляемых на устройство, всегда известен заранее. Если во время "
+"передачи в устройстве происходит неустранимая ошибка, канал останавливается "
+"(stalled). Прежде чем принимать или отправлять дополнительные данные, "
+"драйвер должен устранить причину остановки и сбросить условие остановки "
+"конечной точки, отправив запрос `clear endpoint halt` через канал по "
+"умолчанию. Конечная точка по умолчанию никогда не должна останавливаться."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:139
+msgid ""
+"There are four different types of endpoints and corresponding pipes: - "
+"Control pipe / default pipe: There is one control pipe per device, connected "
+"to the default endpoint (endpoint 0). The pipe carries the device requests "
+"and associated data. The difference between transfers over the default pipe "
+"and other pipes is that the protocol for the transfers is described in the "
+"USB specification. These requests are used to reset and configure the "
+"device. A basic set of commands that must be supported by each device is "
+"provided in chapter 9 of the USB specification. The commands supported on "
+"this pipe can be extended by a device class specification to support "
+"additional functionality."
+msgstr ""
+"Существует четыре различных типа конечных точек и соответствующих каналов: - "
+"Управляющий канал / канал по умолчанию: На каждое устройство приходится один "
+"управляющий канал, подключенный к конечной точке по умолчанию (конечная "
+"точка 0). Этот канал передает запросы устройства и связанные с ними данные. "
+"Разница между передачами через канал по умолчанию и другими каналами "
+"заключается в том, что протокол для этих передач описан в спецификации USB. "
+"Эти запросы используются для сброса и настройки устройства. Базовый набор "
+"команд, который должен поддерживаться каждым устройством, приведен в главе 9 "
+"спецификации USB. Команды, поддерживаемые на этом канале, могут быть "
+"расширены спецификацией класса устройства для обеспечения дополнительной "
+"функциональности."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:141
+msgid "Bulk pipe: This is the USB equivalent to a raw transmission medium."
+msgstr "Массовый канал: Это USB-эквивалент среды передачи данных в сыром виде."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:142
+msgid ""
+"Interrupt pipe: The host sends a request for data to the device and if the "
+"device has nothing to send, it will NAK the data packet. Interrupt transfers "
+"are scheduled at a frequency specified when creating the pipe."
+msgstr ""
+"Прерывающий канал: Хост отправляет запрос данных на устройство, и если "
+"устройству нечего отправлять, оно отвечает NAK на пакет данных. Прерывающие "
+"передачи планируются с частотой, указанной при создании канала."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:143
+msgid ""
+"Isochronous pipe: These pipes are intended for isochronous data, for example "
+"video or audio streams, with fixed latency, but no guaranteed delivery. Some "
+"support for pipes of this type is available in the current implementation. "
+"Packets in control, bulk and interrupt transfers are retried if an error "
+"occurs during transmission or the device acknowledges the packet negatively "
+"(NAK) due to for example lack of buffer space to store the incoming data. "
+"Isochronous packets are however not retried in case of failed delivery or "
+"NAK of a packet as this might violate the timing constraints."
+msgstr ""
+"Изохронный канал: Эти каналы предназначены для изохронных данных, например, "
+"видео- или аудиопотоков, с фиксированной задержкой, но без гарантированной "
+"доставки. В текущей реализации доступна некоторая поддержка каналов этого "
+"типа. Пакеты в управляющих, массовых и прерывающих передачах повторяются, "
+"если во время передачи возникает ошибка или устройство отрицательно "
+"подтверждает пакет (NAK) из-за, например, нехватки буферного пространства "
+"для хранения входящих данных. Однако изохронные пакеты не повторяются в "
+"случае неудачной доставки или NAK пакета, так как это может нарушить "
+"временные ограничения."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:145
+msgid ""
+"The availability of the necessary bandwidth is calculated during the "
+"creation of the pipe. Transfers are scheduled within frames of 1 "
+"millisecond. The bandwidth allocation within a frame is prescribed by the "
+"USB specification, section 5.6 [ 2]. Isochronous and interrupt transfers are "
+"allowed to consume up to 90% of the bandwidth within a frame. Packets for "
+"control and bulk transfers are scheduled after all isochronous and interrupt "
+"packets and will consume all the remaining bandwidth."
+msgstr ""
+"Доступность необходимой полосы пропускания рассчитывается во время создания "
+"канала. Передачи планируются в рамках интервалов в 1 миллисекунду. "
+"Распределение полосы пропускания внутри интервала регламентируется "
+"спецификацией USB, раздел 5.6 [2]. Изохронные и прерывающие передачи могут "
+"использовать до 90% полосы пропускания в рамках интервала. Пакеты для "
+"управляющих и массовых передач планируются после всех изохронных и "
+"прерывающих пакетов и используют всю оставшуюся полосу пропускания."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:147
+msgid ""
+"More information on scheduling of transfers and bandwidth reclamation can be "
+"found in chapter 5 of the USB specification, section 1.3 of the UHCI "
+"specification, and section 3.4.2 of the OHCI specification."
+msgstr ""
+"Дополнительная информация о планировании передач и освобождении пропускной "
+"способности доступна в главе 5 спецификации USB, разделе 1.3 спецификации "
+"UHCI и разделе 3.4.2 спецификации OHCI."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:149
+#, no-wrap
+msgid "Device Probe and Attach"
+msgstr "Обнаружение устройства и подключение"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:152
+msgid ""
+"After the notification by the hub that a new device has been connected, the "
+"service layer switches on the port, providing the device with 100 mA of "
+"current. At this point the device is in its default state and listening to "
+"device address 0. The services layer will proceed to retrieve the various "
+"descriptors through the default pipe. After that it will send a Set Address "
+"request to move the device away from the default device address (address 0). "
+"Multiple device drivers might be able to support the device. For example a "
+"modem driver might be able to support an ISDN TA through the AT "
+"compatibility interface. A driver for that specific model of the ISDN "
+"adapter might however be able to provide much better support for this "
+"device. To support this flexibility, the probes return priorities indicating "
+"their level of support. Support for a specific revision of a product ranks "
+"the highest and the generic driver the lowest priority. It might also be "
+"that multiple drivers could attach to one device if there are multiple "
+"interfaces within one configuration. Each driver only needs to support a "
+"subset of the interfaces."
+msgstr ""
+"После уведомления от концентратора о подключении нового устройства сервисный "
+"уровень включает порт, предоставляя устройству ток 100 мА. На этом этапе "
+"устройство находится в своём исходном состоянии и прослушивает адрес "
+"устройства 0. Сервисный уровень продолжит получение различных дескрипторов "
+"через стандартный канал. После этого он отправит запрос Set Address, чтобы "
+"переместить устройство с исходного адреса (адрес 0). Несколько драйверов "
+"устройств могут поддерживать данное устройство. Например, драйвер модема "
+"может поддерживать ISDN TA через интерфейс совместимости AT. Однако драйвер, "
+"предназначенный для конкретной модели ISDN-адаптера, может обеспечить "
+"гораздо лучшую поддержку этого устройства. Для обеспечения такой гибкости "
+"пробы возвращают приоритеты, указывающие уровень их поддержки. Поддержка "
+"конкретной версии продукта имеет наивысший приоритет, а универсальный "
+"драйвер — самый низкий. Также возможно, что несколько драйверов могут быть "
+"подключены к одному устройству, если в одной конфигурации присутствует "
+"несколько интерфейсов. Каждому драйверу требуется поддерживать только "
+"подмножество интерфейсов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:154
+msgid ""
+"The probing for a driver for a newly attached device checks first for device "
+"specific drivers. If not found, the probe code iterates over all supported "
+"configurations until a driver attaches in a configuration. To support "
+"devices with multiple drivers on different interfaces, the probe iterates "
+"over all interfaces in a configuration that have not yet been claimed by a "
+"driver. Configurations that exceed the power budget for the hub are ignored. "
+"During attach the driver should initialise the device to its proper state, "
+"but not reset it, as this will make the device disconnect itself from the "
+"bus and restart the probing process for it. To avoid consuming unnecessary "
+"bandwidth should not claim the interrupt pipe at attach time, but should "
+"postpone allocating the pipe until the file is opened and the data is "
+"actually used. When the file is closed the pipe should be closed again, even "
+"though the device might still be attached."
+msgstr ""
+"Поиск драйвера для нового подключенного устройства сначала проверяет наличие "
+"специфичных для устройства драйверов. Если они не найдены, код проверки "
+"перебирает все поддерживаемые конфигурации, пока драйвер не будет подключен "
+"в одной из них. Для поддержки устройств с несколькими драйверами на разных "
+"интерфейсах проверка перебирает все интерфейсы в конфигурации, которые ещё "
+"не были заняты драйвером. Конфигурации, превышающие выделенный бюджет "
+"мощности для концентратора, игнорируются. Во время подключения драйвер "
+"должен инициализировать устройство в его рабочее состояние, но не сбрасывать "
+"его, так как это приведёт к отключению устройства от шины и перезапуску "
+"процесса проверки. Чтобы избежать излишнего потребления пропускной "
+"способности, не следует запрашивать канал прерывания во время подключения, а "
+"отложить его выделение до момента открытия файла и фактического "
+"использования данных. При закрытии файла канал следует снова закрыть, даже "
+"если устройство остаётся подключенным."
+
+#. type: Title ===
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:155
+#, no-wrap
+msgid "Device Disconnect and Detach"
+msgstr "Отключение и извлечение устройства"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:158
+msgid ""
+"A device driver should expect to receive errors during any transaction with "
+"the device. The design of USB supports and encourages the disconnection of "
+"devices at any point in time. Drivers should make sure that they do the "
+"right thing when the device disappears."
+msgstr ""
+"Драйвер устройства должен ожидать получения ошибок во время любой транзакции "
+"с устройством. Дизайн USB поддерживает и поощряет отключение устройств в "
+"любой момент времени. Драйверы должны гарантировать корректную обработку "
+"ситуаций, когда устройство исчезает."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:160
+msgid ""
+"Furthermore a device that has been disconnected and reconnected will not be "
+"reattached at the same device instance. This might change in the future when "
+"more devices support serial numbers (see the device descriptor) or other "
+"means of defining an identity for a device have been developed."
+msgstr ""
+"Кроме того, устройство, которое было отключено и снова подключено, не будет "
+"повторно присоединено к тому же экземпляру устройства. Это может измениться "
+"в будущем, когда больше устройств будут поддерживать серийные номера (см. "
+"дескриптор устройства) или будут разработаны другие способы определения "
+"идентификатора устройства."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:162
+msgid ""
+"The disconnection of a device is signaled by a hub in the interrupt packet "
+"delivered to the hub driver. The status change information indicates which "
+"port has seen a connection change. The device detach method for all device "
+"drivers for the device connected on that port are called and the structures "
+"cleaned up. If the port status indicates that in the mean time a device has "
+"been connected to that port, the procedure for probing and attaching the "
+"device will be started. A device reset will produce a disconnect-connect "
+"sequence on the hub and will be handled as described above."
+msgstr ""
+"Отключение устройства сигнализируется концентратором в пакете прерывания, "
+"передаваемом драйверу концентратора. Информация об изменении состояния "
+"указывает, на каком порту произошло изменение подключения. Метод отключения "
+"устройства для всех драйверов устройств, подключенных к этому порту, "
+"вызывается, и структуры очищаются. Если состояние порта указывает, что за "
+"это время к порту было подключено устройство, начнется процедура обнаружения "
+"и подключения устройства. Сброс устройства вызовет последовательность "
+"отключения-подключения на концентраторе и будет обработан, как описано выше."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:164
+#, no-wrap
+msgid "USB Drivers Protocol Information"
+msgstr "Информация о протоколе драйверов USB"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:167
+msgid ""
+"The protocol used over pipes other than the default pipe is undefined by the "
+"USB specification. Information on this can be found from various sources. "
+"The most accurate source is the developer's section on the USB home pages. "
+"From these pages, a growing number of deviceclass specifications are "
+"available. These specifications specify what a compliant device should look "
+"like from a driver perspective, basic functionality it needs to provide and "
+"the protocol that is to be used over the communication channels. The USB "
+"specification includes the description of the Hub Class. A class "
+"specification for Human Interface Devices (HID) has been created to cater "
+"for keyboards, tablets, bar-code readers, buttons, knobs, switches, etc. A "
+"third example is the class specification for mass storage devices. For a "
+"full list of device classes see the developers section on the USB home pages."
+msgstr ""
+"Используемый протокол для каналов, отличных от стандартного, не определен "
+"спецификацией USB. Информацию об этом можно найти из различных источников. "
+"Наиболее точный источник — раздел для разработчиков на домашних страницах "
+"USB. На этих страницах доступно растущее количество спецификаций классов "
+"устройств. Эти спецификации определяют, каким должно быть совместимое "
+"устройство с точки зрения драйвера, базовую функциональность, которую оно "
+"должно предоставлять, и протокол, используемый на каналах связи. "
+"Спецификация USB включает описание класса концентраторов (Hub Class). "
+"Спецификация класса устройств человеко-машинного интерфейса (HID) была "
+"создана для поддержки клавиатур, планшетов, сканеров штрих-кодов, кнопок, "
+"регуляторов, переключателей и т. д. Третий пример — спецификация класса "
+"устройств хранения данных (Mass Storage). Полный список классов устройств "
+"можно найти в разделе для разработчиков на домашних страницах USB."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:169
+msgid ""
+"For many devices the protocol information has not yet been published "
+"however. Information on the protocol being used might be available from the "
+"company making the device. Some companies will require you to sign a Non "
+"-Disclosure Agreement (NDA) before giving you the specifications. This in "
+"most cases precludes making the driver open source."
+msgstr ""
+"Для многих устройств информация о протоколе ещё не опубликована. Сведения об "
+"используемом протоколе могут быть доступны у компании-производителя "
+"устройства. Некоторые компании потребуют подписания соглашения о "
+"неразглашении (NDA), прежде чем предоставить спецификации. В большинстве "
+"случаев это исключает возможность сделать драйвер открытым."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:171
+msgid ""
+"Another good source of information is the Linux driver sources, as a number "
+"of companies have started to provide drivers for Linux for their devices. It "
+"is always a good idea to contact the authors of those drivers for their "
+"source of information."
+msgstr ""
+"Еще один хороший источник информации — исходные коды драйверов Linux, так "
+"как ряд компаний начал предоставлять драйверы для Linux для своих устройств. "
+"Всегда полезно связаться с авторами этих драйверов для получения информации."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:173
+msgid ""
+"Example: Human Interface Devices The specification for the Human Interface "
+"Devices like keyboards, mice, tablets, buttons, dials,etc. is referred to in "
+"other device class specifications and is used in many devices."
+msgstr ""
+"Пример: Устройства интерфейса пользователя (HID) — Спецификация для "
+"устройств интерфейса пользователя, таких как клавиатуры, мыши, планшеты, "
+"кнопки, регуляторы и т.д., упоминается в других спецификациях классов "
+"устройств и используется во многих устройствах."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:175
+msgid ""
+"For example audio speakers provide endpoints to the digital to analogue "
+"converters and possibly an extra pipe for a microphone. They also provide a "
+"HID endpoint in a separate interface for the buttons and dials on the front "
+"of the device. The same is true for the monitor control class. It is "
+"straightforward to build support for these interfaces through the available "
+"kernel and userland libraries together with the HID class driver or the "
+"generic driver. Another device that serves as an example for interfaces "
+"within one configuration driven by different device drivers is a cheap "
+"keyboard with built-in legacy mouse port. To avoid having the cost of "
+"including the hardware for a USB hub in the device, manufacturers combined "
+"the mouse data received from the PS/2 port on the back of the keyboard and "
+"the key presses from the keyboard into two separate interfaces in the same "
+"configuration. The mouse and keyboard drivers each attach to the appropriate "
+"interface and allocate the pipes to the two independent endpoints."
+msgstr ""
+"Например, аудиоколонки предоставляют конечные точки для цифро-аналоговых "
+"преобразователей и, возможно, дополнительный канал для микрофона. Они также "
+"предоставляют конечную точку HID в отдельном интерфейсе для кнопок и "
+"регуляторов на передней панели устройства. То же самое справедливо для "
+"класса управления монитором. Реализовать поддержку этих интерфейсов "
+"достаточно просто с помощью доступных библиотек ядра и пользовательского "
+"пространства, а также драйвера класса HID или универсального драйвера. "
+"Другим примером устройства с несколькими интерфейсами в одной конфигурации, "
+"управляемыми разными драйверами, является недорогая клавиатура со встроенным "
+"устаревшим портом для мыши. Чтобы избежать затрат на включение аппаратного "
+"обеспечения USB-концентратора в устройство, производители объединили данные "
+"мыши, полученные с порта PS/2 на задней панели клавиатуры, и нажатия клавиш "
+"в два отдельных интерфейса в одной конфигурации. Драйверы мыши и клавиатуры "
+"подключаются к соответствующему интерфейсу и выделяют каналы для двух "
+"независимых конечных точек."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:177
+msgid ""
+"Example: Firmware download Many devices that have been developed are based "
+"on a general purpose processor with an additional USB core added to it. "
+"Since the development of drivers and firmware for USB devices is still very "
+"new, many devices require the downloading of the firmware after they have "
+"been connected."
+msgstr ""
+"Пример: Загрузка микропрограммы — многие разработанные устройства основаны "
+"на процессоре общего назначения с добавленным USB-ядром. Поскольку "
+"разработка драйверов и микропрограмм для USB-устройств всё ещё очень нова, "
+"многие устройства требуют загрузки микропрограммы после подключения."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:179
+msgid ""
+"The procedure followed is straightforward. The device identifies itself "
+"through a vendor and product Id. The first driver probes and attaches to it "
+"and downloads the firmware into it. After that the device soft resets itself "
+"and the driver is detached. After a short pause the device announces its "
+"presence on the bus. The device will have changed its vendor/product/"
+"revision Id to reflect the fact that it has been supplied with firmware and "
+"as a consequence a second driver will probe it and attach to it."
+msgstr ""
+"Процедура выполняется следующим образом. Устройство идентифицирует себя "
+"через идентификаторы производителя и продукта. Первый драйвер проверяет его "
+"и подключается к нему, затем загружает в него микропрограмму. После этого "
+"устройство выполняет мягкую перезагрузку, и драйвер отключается. После "
+"небольшой паузы устройство снова появляется на шине. При этом идентификаторы "
+"производителя, продукта и версии устройства изменятся, что отражает факт "
+"загрузки микропрограммы, и в результате второй драйвер проверит его и "
+"подключится к нему."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:181
+msgid ""
+"An example of these types of devices is the ActiveWire I/O board, based on "
+"the EZ-USB chip. For this chip a generic firmware downloader is available. "
+"The firmware downloaded into the ActiveWire board changes the revision Id. "
+"It will then perform a soft reset of the USB part of the EZ-USB chip to "
+"disconnect from the USB bus and again reconnect."
+msgstr ""
+"Примером таких устройств является плата ввода-вывода ActiveWire, основанная "
+"на чипе EZ-USB. Для этого чипа доступен универсальный загрузчик "
+"микропрограмм. Микропрограмма, загруженная в плату ActiveWire, изменяет "
+"идентификатор ревизии. Затем выполняется мягкий сброс USB-части чипа EZ-USB "
+"для отключения от USB-шины и повторного подключения."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:183
+msgid ""
+"Example: Mass Storage Devices Support for mass storage devices is mainly "
+"built around existing protocols. The Iomega USB Zipdrive is based on the "
+"SCSI version of their drive. The SCSI commands and status messages are "
+"wrapped in blocks and transferred over the bulk pipes to and from the "
+"device, emulating a SCSI controller over the USB wire. ATAPI and UFI "
+"commands are supported in a similar fashion."
+msgstr ""
+"Пример: Поддержка устройств хранения данных в основном построена на "
+"существующих протоколах. Устройство Iomega USB Zipdrive основано на SCSI-"
+"версии их накопителя. SCSI-команды и статусные сообщения упаковываются в "
+"блоки и передаются через массовые каналы к устройству и от него, эмулируя "
+"SCSI-контроллер через USB-соединение. ATAPI и UFI команды поддерживаются "
+"аналогичным образом."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:185
+msgid ""
+"The Mass Storage Specification supports 2 different types of wrapping of the "
+"command block.The initial attempt was based on sending the command and "
+"status through the default pipe and using bulk transfers for the data to be "
+"moved between the host and the device. Based on experience a second approach "
+"was designed that was based on wrapping the command and status blocks and "
+"sending them over the bulk out and in endpoint. The specification specifies "
+"exactly what has to happen when and what has to be done in case an error "
+"condition is encountered. The biggest challenge when writing drivers for "
+"these devices is to fit USB based protocol into the existing support for "
+"mass storage devices. CAM provides hooks to do this in a fairly straight "
+"forward way. ATAPI is less simple as historically the IDE interface has "
+"never had many different appearances."
+msgstr ""
+"Спецификация Mass Storage поддерживает 2 различных типа обёртки командного "
+"блока. Первоначальная попытка была основана на отправке команды и состояния "
+"через канал по умолчанию с использованием массовых передач для данных, "
+"перемещаемых между хостом и устройством. На основе опыта был разработан "
+"второй подход, основанный на обёртке командных и статусных блоков и их "
+"отправке через конечные точки массовой передачи (bulk out и bulk in). "
+"Спецификация точно определяет, что должно происходить и когда, а также что "
+"необходимо делать в случае возникновения ошибки. Наибольшую сложность при "
+"написании драйверов для таких устройств представляет встраивание USB-"
+"ориентированного протокола в существующую поддержку устройств хранения "
+"данных. CAM предоставляет механизмы для этого достаточно прямолинейным "
+"способом. С ATAPI всё менее просто, так как исторически интерфейс IDE "
+"никогда не имел множества различных вариантов реализации."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/usb/_index.adoc:186
+msgid ""
+"The support for the USB floppy from Y-E Data is again less straightforward "
+"as a new command set has been designed."
+msgstr ""
+"Поддержка USB-дисковода от Y-E Data также не прямолинейна, так как был "
+"разработан новый набор команд."
diff --git a/documentation/content/ru/books/arch-handbook/vm/_index.adoc b/documentation/content/ru/books/arch-handbook/vm/_index.adoc
new file mode 100644
index 0000000000..25c363a04b
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/vm/_index.adoc
@@ -0,0 +1,127 @@
+---
+description: 'Система управления виртуальной памятью во FreeBSD'
+next: books/arch-handbook/smp
+params:
+ path: /books/arch-handbook/vm/
+prev: books/arch-handbook/mac
+showBookMenu: true
+tags: ["Virtual memory", "vm_page_t", "vm_object_t", "I/O", "KVM"]
+title: 'Глава 7. Система управления виртуальной памятью'
+weight: 8
+---
+
+[[vm]]
+= Система управления виртуальной памятью
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 7
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/arch-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[vm-physmem]]
+== Управление физической памятью `vm_page_t`
+
+Физическая память управляется постранично с использованием структуры `vm_page_t`. Страницы физической памяти классифицируются посредством размещения соответствующих структур `vm_page_t` в одной из нескольких очередей подкачки.
+
+Страница может находиться в одном из следующих состояний: зафиксированном (Wired), активном (Active), неактивном (Inactive), кэшированном (Cache) или свободном (Free). За исключением зафиксированного состояния, страница обычно помещается в двусвязный список, представляющий её текущее состояние. Зафиксированные страницы не помещаются ни в одну из очередей.
+
+FreeBSD реализует более сложную очередь подкачки для кэшированных и свободных страниц с целью реализации раскраски страниц. Каждое из этих состояний включает несколько очередей, организованных в соответствии с размерами кэшей L1 и L2 процессора. Когда требуется выделить новую страницу, FreeBSD пытается получить такую, которая будет достаточно хорошо выровнена с точки зрения кэшей L1 и L2 относительно объекта виртуальной памяти, для которого выделяется страница.
+
+Кроме того, страница может удерживаться с помощью счётчика ссылок или быть заблокированной с помощью счётчика занятости. Система виртуальной памяти также реализует состояние «абсолютной блокировки» страницы с использованием бита PG_BUSY в флагах страницы.
+
+В общем случае каждая из очередей подкачки работает по принципу LRU (наименее недавно использованная). Обычно страница изначально помещается в зафиксированное или активное состояние. В зафиксированном состоянии страница, как правило, ассоциирована с какой-либо таблицей страниц. Система виртуальной памяти «состаривает» страницу, просматривая страницы в более активной очереди страниц (LRU), чтобы переместить их в менее активную очередь. Страницы, перемещённые в кэш, всё ещё связаны с объектом виртуальной памяти, но могут быть немедленно повторно использованы. Страницы в очереди свободных действительно являются полностью свободными. FreeBSD стремится минимизировать количество страниц в очереди свободных, но определённый минимальный объём таких страниц должен поддерживаться для обеспечения возможности выделения памяти во время обработки прерываний.
+
+Если процесс пытается обратиться к странице, которой нет в его таблице страниц, но которая присутствует в одной из очередей подкачки (например, в неактивной или кэшированной), происходит относительно недорогой сбой повторной активации страницы, в результате которого страница повторно активируется. Если же страницы вообще нет в памяти системы, процесс вынужден блокироваться до тех пор, пока страница не будет загружена с диска.
+
+FreeBSD динамически настраивает свои очереди страниц и старается поддерживать разумные пропорции количества страниц в различных очередях, а также сбалансированное соотношение чистых и грязных страниц. Объём перераспределения зависит от текущей нагрузки на память системы. Это перераспределение выполняется демоном выгрузки страниц (pageout daemon) и включает в себя очистку (laundering) грязных страниц (синхронизацию их с резервным хранилищем), отслеживание активности страниц по ссылкам на них (сброс их положения в очередях LRU или перемещение между очередями), миграцию страниц между очередями при разбалансировке очередей и другие действия. Система виртуальной памяти FreeBSD допускает определённое количество сбоев повторной активации страниц, чтобы точнее определить, насколько страница фактически активна или неактивна. Это позволяет принимать более обоснованные решения о том, когда очищать или выгружать страницу в файл подкачки.
+
+[[vm-cache]]
+== Унифицированный буферный кэш `vm_object_t`
+
+FreeBSD реализует концепцию универсального «объекта виртуальной памяти» (VM-объекта). VM-объекты могут быть связаны с резервным хранилищем различных типов: без резервного хранилища, с подкачкой, с физическим устройством или с файловым хранилищем. Поскольку файловая система использует те же VM-объекты для управления данными в оперативной памяти, связанными с файлами, в результате получается унифицированный буферный кэш.
+
+Объекты виртуальной памяти могут быть _затемнены_ (shadowed), то есть размещены друг над другом в виде стека. Например, объект виртуальной памяти с подкачкой может быть размещён поверх объекта, связанного с файловым хранилищем, для реализации отображения mmap() с флагом MAP_PRIVATE. Такое стековое размещение также используется для реализации различных общих свойств, включая копирование при записи (copy-on-write), для адресных пространств созданных при форке процесса.
+
+Следует отметить, что структура `vm_page_t` может быть связана только с одним объектом виртуальной памяти (VM-объектом) в данный момент времени. Механизм затемнения VM-объектов реализует кажущееся совместное использование одной и той же страницы между несколькими экземплярами.
+
+[[vm-fileio]]
+== Ввод-вывод файловой системы `struct buf`
+
+VM-объекты, поддерживаемые vnode, такие как объекты, связанные с файловым хранилищем, обычно должны поддерживать собственную информацию о чистоте/грязности, независимую от представления о чистоте/грязности в системе виртуальной памяти. Например, когда система виртуальной памяти решает синхронизировать физическую страницу с её резервным хранилищем, система виртуальной памяти должна отметить страницу как чистую до того, как она будет фактически записана в резервное хранилище. Кроме того, файловые системы должны иметь возможность отображать части файла или метаданных файла в KVM для выполнения операций с ними.
+
+Сущности, используемые для управления этим процессом, известны как файловые буферы, структуры ``struct buf`` или ``bp``. Когда файловая система должна работать с частью объекта виртуальной памяти, она обычно отображает часть объекта в структуру struct buf, а затем отображает страницы из этой структуры в KVM. Точно так же операции с дисковым вводом-выводом обычно выполняются путём отображения частей объектов в структуры буферов и последующего выполнения ввода-вывода на этих структурах. Соответствующие им страницы vm_page_t обычно блокируются на время выполнения операции ввода-вывода. Файловые буферы также имеют собственное представление о состоянии занятости, что полезно для кода драйвера файловой системы, который предпочитает работать с файловыми буферами, а не с обычными страницами виртуальной памяти.
+
+FreeBSD резервирует ограниченный объем виртуальной памяти ядра для хранения отображений из структур struct buf на физические страницы памяти, но следует подчеркнуть, что эта память используется исключительно для хранения отображений и не ограничивает возможность кэширования данных. Кэширование физических данных строго связано с ``vm_page_t``, а не с файловыми буферами. Однако, поскольку файловые буферы используются для операций ввода-вывода, они ограничивают количество возможных параллельных операций ввода-вывода. Тем не менее, поскольку обычно доступно несколько тысяч файловых буферов, это редко становится проблемой.
+
+[[vm-pagetables]]
+== Отображение таблиц страниц `vm_map_t, vm_entry_t`
+
+FreeBSD разделяет топологию физических таблиц страниц от системы виртуальной памяти. Все основные таблицы страниц, связанные с каждым процессом, могут быть восстановлены динамически и обычно считаются временными (throwaway). Специальные таблицы страниц, такие как те, которые управляют KVM (виртуальной памятью ядра), обычно предварительно выделяются навсегда. Эти таблицы страниц не являются временными.
+
+FreeBSD связывает части vm_object с диапазонами адресов в виртуальной памяти через структуры `vm_map_t` и `vm_entry_t`. Таблицы страниц напрямую создаются из иерархии `vm_map_t` / `vm_entry_t`/ `vm_object_t`. Напоминаем, что физические страницы непосредственно связаны только с `vm_object`, однако это не совсем так. Структуры ``vm_page_t`` также привязаны к таблицами страниц, с которыми они активно ассоциированы. Одна структура `vm_page_t` может быть привязана к нескольким _pmaps_ (так называют таблицы страниц). Тем не менее, иерархическая ассоциация сохраняется, и все ссылки на одну и ту же страницу в одном объекте ссылаются на один и тот же `vm_page_t`, что, в свою очередь, обеспечивает унификацию буферного кэша.
+
+[[vm-kvm]]
+== Отображение виртуальной памяти ядра
+
+FreeBSD использует KVM для хранения различных структур ядра. Самой крупной сущностью, хранящейся в KVM, является кэш файловых буферов. То есть, отображения, связанные с сущностями `struct buf`.
+
+В отличие от Linux, FreeBSD _не_ отображает всю физическую память в KVM. Это означает, что FreeBSD может обрабатывать конфигурации памяти до 4 ГБ на 32-битных платформах. На самом деле, если бы MMU это позволял, FreeBSD теоретически могла бы обрабатывать конфигурации памяти до 8 ТБ на 32-битной платформе. Однако, поскольку большинство 32-битных платформ способны отображать только 4 ГБ оперативной памяти, этот вопрос не имеет практического значения.
+
+KVM управляется через несколько механизмов. Основным механизмом для управления KVM является _аллокатор зон_. Аллокатор зон берет блок KVM и делит его на блоки памяти постоянного размера для выделения структур определённого типа. Вы можете использовать команду `vmstat -m`, чтобы получить обзор текущего использования KVM, разбитого по зонам.
+
+[[vm-tuning]]
+== Настройка системы виртуальной памяти во FreeBSD
+
+Были преложены значительные усилия сделать ядро FreeBSD динамически настраиваемым. Обычно вам не нужно вмешиваться в настройки, за исключением параметров конфигурации ядра `maxusers` и `NMBCLUSTERS`. Параметры компиляции ядра указанны (обычно) в файле [.filename]#/usr/src/sys/i386/conf/CONFIG_FILE#. Описание всех доступных параметров конфигурации ядра можно найти в файле [.filename]#/usr/src/sys/i386/conf/LINT#.
+
+В большой системе конфигурации, возможно, вам потребуется увеличить значение `maxusers`. Обычно значения варьируются от 10 до 128. Обратите внимание, что слишком большое значение для `maxusers` может привести к переполнению доступной KVM, что вызовет непредсказуемую работу системы. Лучше оставить `maxusers` на разумном уровне и добавлять другие параметры, такие как `NMBCLUSTERS`, для увеличения конкретных ресурсов.
+
+Если ваша система будет активно использовать сеть, возможно, вам захочется увеличить значение `NMBCLUSTERS`. Типичные значения варьируются от 1024 до 4096.
+
+Параметр `NBUF` также традиционно используется для масштабирования системы. Этот параметр определяет объем виртуальной памяти ядра, который система может использовать для отображения файловых буферов для ввода-вывода. Обратите внимание, что этот параметр никак не связан с унифицированным буферным кэшем! В ядрах 3.0-CURRENT и позднее этот параметр динамически настраивается, и в целом не следует настраивать его вручную. Мы рекомендуем вам _не_ пытаться задавать параметр NBUF. Позвольте системе выбрать его автоматически. Слишком малое значение может привести к крайне неэффективной работе файловой системы, в то время как слишком большое значение может привести к истощению очередей страниц из-за того, что слишком много страниц будет закреплено в памяти.
+
+По умолчанию ядра FreeBSD не оптимизированы. Вы можете установить флаги отладки и оптимизации с помощью директивы `makeoptions` в конфигурации ядра. Обратите внимание, что не следует использовать флаг `-g`, если вы не можете разместить в системе большие ядра (обычно более 7 МБ), которые получаются в результате.
+
+[.programlisting]
+....
+makeoptions DEBUG="-g"
+makeoptions COPTFLAGS="-O -pipe"
+....
+
+Sysctl предоставляет способ настройки параметров ядра во время работы системы. Обычно вам не нужно изменять какие-либо переменные sysctl, особенно те, что связаны с виртуальной памятью.
+
+Настройка виртуальной памяти (VM) и системы во время работы относительно проста. Во-первых, используйте Soft Updates на ваших файловых системах UFS/FFS, когда это возможно. Файл [.filename]#/usr/src/sys/ufs/ffs/README.softupdates# содержит инструкции (и ограничения) по конфигурации этой функции.
+
+Во-вторых, настройте достаточный объем подкачки (swap). Вы должны создать раздел подкачки на каждом физическом диске, до четырёх, даже на "рабочих" дисках. Объём подкачки должен быть как минимум в 2 раза больше объёма основной памяти, а возможно — и больше, если у вас немного ОЗУ. Размер раздела подкачки следует выбирать с учётом максимальной конфигурации памяти, которую вы когда-либо планируете установить на эту машину, чтобы в будущем не пришлось переразбивать диски. Если вы хотите иметь возможность сохранять дампы при сбое, ваш первый swap-раздел должен быть не меньше объёма основной памяти, а каталог [.filename]#/var/crash# должен иметь достаточно свободного места для хранения дампа.
+
+Подкачка на базе NFS вполне допустима в системах версии 4.X и новее, однако необходимо учитывать, что основная нагрузка на подкачку ляжет на NFS-сервер.
diff --git a/documentation/content/ru/books/arch-handbook/vm/_index.po b/documentation/content/ru/books/arch-handbook/vm/_index.po
new file mode 100644
index 0000000000..182ae77808
--- /dev/null
+++ b/documentation/content/ru/books/arch-handbook/vm/_index.po
@@ -0,0 +1,551 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-14 22:43+0300\n"
+"PO-Revision-Date: 2025-08-26 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksarch-handbookvm_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:1
+#, no-wrap
+msgid "Virtual Memory System in FreeBSD"
+msgstr "Система управления виртуальной памятью во FreeBSD"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:1
+#, no-wrap
+msgid "Chapter 7. Virtual Memory System"
+msgstr "Глава 7. Система управления виртуальной памятью"
+
+#. type: Title =
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:14
+#, no-wrap
+msgid "Virtual Memory System"
+msgstr "Система управления виртуальной памятью"
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:52
+#, no-wrap
+msgid "Management of Physical Memory `vm_page_t`"
+msgstr "Управление физической памятью `vm_page_t`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:55
+msgid ""
+"Physical memory is managed on a page-by-page basis through the `vm_page_t` "
+"structure. Pages of physical memory are categorized through the placement of "
+"their respective `vm_page_t` structures on one of several paging queues."
+msgstr ""
+"Физическая память управляется постранично с использованием структуры "
+"`vm_page_t`. Страницы физической памяти классифицируются посредством "
+"размещения соответствующих структур `vm_page_t` в одной из нескольких "
+"очередей подкачки."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:57
+msgid ""
+"A page can be in a wired, active, inactive, cache, or free state. Except for "
+"the wired state, the page is typically placed in a doubly link list queue "
+"representing the state that it is in. Wired pages are not placed on any "
+"queue."
+msgstr ""
+"Страница может находиться в одном из следующих состояний: зафиксированном "
+"(Wired), активном (Active), неактивном (Inactive), кэшированном (Cache) или "
+"свободном (Free). За исключением зафиксированного состояния, страница обычно "
+"помещается в двусвязный список, представляющий её текущее состояние. "
+"Зафиксированные страницы не помещаются ни в одну из очередей."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:59
+msgid ""
+"FreeBSD implements a more involved paging queue for cached and free pages in "
+"order to implement page coloring. Each of these states involves multiple "
+"queues arranged according to the size of the processor's L1 and L2 caches. "
+"When a new page needs to be allocated, FreeBSD attempts to obtain one that "
+"is reasonably well aligned from the point of view of the L1 and L2 caches "
+"relative to the VM object the page is being allocated for."
+msgstr ""
+"FreeBSD реализует более сложную очередь подкачки для кэшированных и "
+"свободных страниц с целью реализации раскраски страниц. Каждое из этих "
+"состояний включает несколько очередей, организованных в соответствии с "
+"размерами кэшей L1 и L2 процессора. Когда требуется выделить новую страницу, "
+"FreeBSD пытается получить такую, которая будет достаточно хорошо выровнена с "
+"точки зрения кэшей L1 и L2 относительно объекта виртуальной памяти, для "
+"которого выделяется страница."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:61
+msgid ""
+"Additionally, a page may be held with a reference count or locked with a "
+"busy count. The VM system also implements an \"ultimate locked\" state for a "
+"page using the PG_BUSY bit in the page's flags."
+msgstr ""
+"Кроме того, страница может удерживаться с помощью счётчика ссылок или быть "
+"заблокированной с помощью счётчика занятости. Система виртуальной памяти "
+"также реализует состояние «абсолютной блокировки» страницы с использованием "
+"бита PG_BUSY в флагах страницы."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:63
+msgid ""
+"In general terms, each of the paging queues operates in a LRU fashion. A "
+"page is typically placed in a wired or active state initially. When wired, "
+"the page is usually associated with a page table somewhere. The VM system "
+"ages the page by scanning pages in a more active paging queue (LRU) in order "
+"to move them to a less-active paging queue. Pages that get moved into the "
+"cache are still associated with a VM object but are candidates for immediate "
+"reuse. Pages in the free queue are truly free. FreeBSD attempts to minimize "
+"the number of pages in the free queue, but a certain minimum number of truly "
+"free pages must be maintained in order to accommodate page allocation at "
+"interrupt time."
+msgstr ""
+"В общем случае каждая из очередей подкачки работает по принципу LRU "
+"(наименее недавно использованная). Обычно страница изначально помещается в "
+"зафиксированное или активное состояние. В зафиксированном состоянии "
+"страница, как правило, ассоциирована с какой-либо таблицей страниц. Система "
+"виртуальной памяти «состаривает» страницу, просматривая страницы в более "
+"активной очереди страниц (LRU), чтобы переместить их в менее активную "
+"очередь. Страницы, перемещённые в кэш, всё ещё связаны с объектом "
+"виртуальной памяти, но могут быть немедленно повторно использованы. Страницы "
+"в очереди свободных действительно являются полностью свободными. FreeBSD "
+"стремится минимизировать количество страниц в очереди свободных, но "
+"определённый минимальный объём таких страниц должен поддерживаться для "
+"обеспечения возможности выделения памяти во время обработки прерываний."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:65
+msgid ""
+"If a process attempts to access a page that does not exist in its page table "
+"but does exist in one of the paging queues (such as the inactive or cache "
+"queues), a relatively inexpensive page reactivation fault occurs which "
+"causes the page to be reactivated. If the page does not exist in system "
+"memory at all, the process must block while the page is brought in from disk."
+msgstr ""
+"Если процесс пытается обратиться к странице, которой нет в его таблице "
+"страниц, но которая присутствует в одной из очередей подкачки (например, в "
+"неактивной или кэшированной), происходит относительно недорогой сбой "
+"повторной активации страницы, в результате которого страница повторно "
+"активируется. Если же страницы вообще нет в памяти системы, процесс вынужден "
+"блокироваться до тех пор, пока страница не будет загружена с диска."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:67
+msgid ""
+"FreeBSD dynamically tunes its paging queues and attempts to maintain "
+"reasonable ratios of pages in the various queues as well as attempts to "
+"maintain a reasonable breakdown of clean versus dirty pages. The amount of "
+"rebalancing that occurs depends on the system's memory load. This "
+"rebalancing is implemented by the pageout daemon and involves laundering "
+"dirty pages (syncing them with their backing store), noticing when pages are "
+"activity referenced (resetting their position in the LRU queues or moving "
+"them between queues), migrating pages between queues when the queues are out "
+"of balance, and so forth. FreeBSD's VM system is willing to take a "
+"reasonable number of reactivation page faults to determine how active or how "
+"idle a page actually is. This leads to better decisions being made as to "
+"when to launder or swap-out a page."
+msgstr ""
+"FreeBSD динамически настраивает свои очереди страниц и старается "
+"поддерживать разумные пропорции количества страниц в различных очередях, а "
+"также сбалансированное соотношение чистых и грязных страниц. Объём "
+"перераспределения зависит от текущей нагрузки на память системы. Это "
+"перераспределение выполняется демоном выгрузки страниц (pageout daemon) и "
+"включает в себя очистку (laundering) грязных страниц (синхронизацию их с "
+"резервным хранилищем), отслеживание активности страниц по ссылкам на них "
+"(сброс их положения в очередях LRU или перемещение между очередями), "
+"миграцию страниц между очередями при разбалансировке очередей и другие "
+"действия. Система виртуальной памяти FreeBSD допускает определённое "
+"количество сбоев повторной активации страниц, чтобы точнее определить, "
+"насколько страница фактически активна или неактивна. Это позволяет принимать "
+"более обоснованные решения о том, когда очищать или выгружать страницу в "
+"файл подкачки."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:69
+#, no-wrap
+msgid "The Unified Buffer Cache `vm_object_t`"
+msgstr "Унифицированный буферный кэш `vm_object_t`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:72
+msgid ""
+"FreeBSD implements the idea of a generic \"VM object\". VM objects can be "
+"associated with backing store of various typesunbacked, swap-backed, "
+"physical device-backed, or file-backed storage. Since the filesystem uses "
+"the same VM objects to manage in-core data relating to files, the result is "
+"a unified buffer cache."
+msgstr ""
+"FreeBSD реализует концепцию универсального «объекта виртуальной памяти» (VM-"
+"объекта). VM-объекты могут быть связаны с резервным хранилищем различных "
+"типов: без резервного хранилища, с подкачкой, с физическим устройством или с "
+"файловым хранилищем. Поскольку файловая система использует те же VM-объекты "
+"для управления данными в оперативной памяти, связанными с файлами, в "
+"результате получается унифицированный буферный кэш."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:74
+msgid ""
+"VM objects can be _shadowed_. That is, they can be stacked on top of each "
+"other. For example, you might have a swap-backed VM object stacked on top of "
+"a file-backed VM object in order to implement a MAP_PRIVATE mmap()ing. This "
+"stacking is also used to implement various sharing properties, including "
+"copy-on-write, for forked address spaces."
+msgstr ""
+"Объекты виртуальной памяти могут быть _затемнены_ (shadowed), то есть "
+"размещены друг над другом в виде стека. Например, объект виртуальной памяти "
+"с подкачкой может быть размещён поверх объекта, связанного с файловым "
+"хранилищем, для реализации отображения mmap() с флагом MAP_PRIVATE. Такое "
+"стековое размещение также используется для реализации различных общих "
+"свойств, включая копирование при записи (copy-on-write), для адресных "
+"пространств созданных при форке процесса."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:76
+msgid ""
+"It should be noted that a `vm_page_t` can only be associated with one VM "
+"object at a time. The VM object shadowing implements the perceived sharing "
+"of the same page across multiple instances."
+msgstr ""
+"Следует отметить, что структура `vm_page_t` может быть связана только с "
+"одним объектом виртуальной памяти (VM-объектом) в данный момент времени. "
+"Механизм затемнения VM-объектов реализует кажущееся совместное использование "
+"одной и той же страницы между несколькими экземплярами."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:78
+#, no-wrap
+msgid "Filesystem I/O `struct buf`"
+msgstr "Ввод-вывод файловой системы `struct buf`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:81
+msgid ""
+"vnode-backed VM objects, such as file-backed objects, generally need to "
+"maintain their own clean/dirty info independent from the VM system's idea of "
+"clean/dirty. For example, when the VM system decides to synchronize a "
+"physical page to its backing store, the VM system needs to mark the page "
+"clean before the page is actually written to its backing store. "
+"Additionally, filesystems need to be able to map portions of a file or file "
+"metadata into KVM in order to operate on it."
+msgstr ""
+"VM-объекты, поддерживаемые vnode, такие как объекты, связанные с файловым "
+"хранилищем, обычно должны поддерживать собственную информацию о чистоте/"
+"грязности, независимую от представления о чистоте/грязности в системе "
+"виртуальной памяти. Например, когда система виртуальной памяти решает "
+"синхронизировать физическую страницу с её резервным хранилищем, система "
+"виртуальной памяти должна отметить страницу как чистую до того, как она "
+"будет фактически записана в резервное хранилище. Кроме того, файловые "
+"системы должны иметь возможность отображать части файла или метаданных файла "
+"в KVM для выполнения операций с ними."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:83
+msgid ""
+"The entities used to manage this are known as filesystem buffers, ``struct "
+"buf``'s, or ``bp``'s. When a filesystem needs to operate on a portion of a "
+"VM object, it typically maps part of the object into a struct buf and then "
+"maps the pages in the struct buf into KVM. In the same manner, disk I/O is "
+"typically issued by mapping portions of objects into buffer structures and "
+"then issuing the I/O on the buffer structures. The underlying vm_page_t's "
+"are typically busied for the duration of the I/O. Filesystem buffers also "
+"have their own notion of being busy, which is useful to filesystem driver "
+"code which would rather operate on filesystem buffers instead of hard VM "
+"pages."
+msgstr ""
+"Сущности, используемые для управления этим процессом, известны как файловые "
+"буферы, структуры ``struct buf`` или ``bp``. Когда файловая система должна "
+"работать с частью объекта виртуальной памяти, она обычно отображает часть "
+"объекта в структуру struct buf, а затем отображает страницы из этой "
+"структуры в KVM. Точно так же операции с дисковым вводом-выводом обычно "
+"выполняются путём отображения частей объектов в структуры буферов и "
+"последующего выполнения ввода-вывода на этих структурах. Соответствующие им "
+"страницы vm_page_t обычно блокируются на время выполнения операции ввода-"
+"вывода. Файловые буферы также имеют собственное представление о состоянии "
+"занятости, что полезно для кода драйвера файловой системы, который "
+"предпочитает работать с файловыми буферами, а не с обычными страницами "
+"виртуальной памяти."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:85
+msgid ""
+"FreeBSD reserves a limited amount of KVM to hold mappings from struct bufs, "
+"but it should be made clear that this KVM is used solely to hold mappings "
+"and does not limit the ability to cache data. Physical data caching is "
+"strictly a function of ``vm_page_t``'s, not filesystem buffers. However, "
+"since filesystem buffers are used to placehold I/O, they do inherently limit "
+"the amount of concurrent I/O possible. However, as there are usually a few "
+"thousand filesystem buffers available, this is not usually a problem."
+msgstr ""
+"FreeBSD резервирует ограниченный объем виртуальной памяти ядра для хранения "
+"отображений из структур struct buf на физические страницы памяти, но следует "
+"подчеркнуть, что эта память используется исключительно для хранения "
+"отображений и не ограничивает возможность кэширования данных. Кэширование "
+"физических данных строго связано с ``vm_page_t``, а не с файловыми буферами. "
+"Однако, поскольку файловые буферы используются для операций ввода-вывода, "
+"они ограничивают количество возможных параллельных операций ввода-вывода. "
+"Тем не менее, поскольку обычно доступно несколько тысяч файловых буферов, "
+"это редко становится проблемой."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:87
+#, no-wrap
+msgid "Mapping Page Tables `vm_map_t, vm_entry_t`"
+msgstr "Отображение таблиц страниц `vm_map_t, vm_entry_t`"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:90
+msgid ""
+"FreeBSD separates the physical page table topology from the VM system. All "
+"hard per-process page tables can be reconstructed on the fly and are usually "
+"considered throwaway. Special page tables such as those managing KVM are "
+"typically permanently preallocated. These page tables are not throwaway."
+msgstr ""
+"FreeBSD разделяет топологию физических таблиц страниц от системы виртуальной "
+"памяти. Все основные таблицы страниц, связанные с каждым процессом, могут "
+"быть восстановлены динамически и обычно считаются временными (throwaway). "
+"Специальные таблицы страниц, такие как те, которые управляют KVM "
+"(виртуальной памятью ядра), обычно предварительно выделяются навсегда. Эти "
+"таблицы страниц не являются временными."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:92
+msgid ""
+"FreeBSD associates portions of vm_objects with address ranges in virtual "
+"memory through `vm_map_t` and `vm_entry_t` structures. Page tables are "
+"directly synthesized from the `vm_map_t`/`vm_entry_t`/ `vm_object_t` "
+"hierarchy. Recall that I mentioned that physical pages are only directly "
+"associated with a `vm_object`; that is not quite true. ``vm_page_t``'s are "
+"also linked into page tables that they are actively associated with. One "
+"`vm_page_t` can be linked into several _pmaps_, as page tables are called. "
+"However, the hierarchical association holds, so all references to the same "
+"page in the same object reference the same `vm_page_t` and thus give us "
+"buffer cache unification across the board."
+msgstr ""
+"FreeBSD связывает части vm_object с диапазонами адресов в виртуальной памяти "
+"через структуры `vm_map_t` и `vm_entry_t`. Таблицы страниц напрямую "
+"создаются из иерархии `vm_map_t` / `vm_entry_t`/ `vm_object_t`. Напоминаем, "
+"что физические страницы непосредственно связаны только с `vm_object`, однако "
+"это не совсем так. Структуры ``vm_page_t`` также привязаны к таблицами "
+"страниц, с которыми они активно ассоциированы. Одна структура `vm_page_t` "
+"может быть привязана к нескольким _pmaps_ (так называют таблицы страниц). "
+"Тем не менее, иерархическая ассоциация сохраняется, и все ссылки на одну и "
+"ту же страницу в одном объекте ссылаются на один и тот же `vm_page_t`, что, "
+"в свою очередь, обеспечивает унификацию буферного кэша."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:94
+#, no-wrap
+msgid "KVM Memory Mapping"
+msgstr "Отображение виртуальной памяти ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:97
+msgid ""
+"FreeBSD uses KVM to hold various kernel structures. The single largest "
+"entity held in KVM is the filesystem buffer cache. That is, mappings "
+"relating to `struct buf` entities."
+msgstr ""
+"FreeBSD использует KVM для хранения различных структур ядра. Самой крупной "
+"сущностью, хранящейся в KVM, является кэш файловых буферов. То есть, "
+"отображения, связанные с сущностями `struct buf`."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:99
+msgid ""
+"Unlike Linux, FreeBSD does _not_ map all of physical memory into KVM. This "
+"means that FreeBSD can handle memory configurations up to 4G on 32 bit "
+"platforms. In fact, if the mmu were capable of it, FreeBSD could "
+"theoretically handle memory configurations up to 8TB on a 32 bit platform. "
+"However, since most 32 bit platforms are only capable of mapping 4GB of ram, "
+"this is a moot point."
+msgstr ""
+"В отличие от Linux, FreeBSD _не_ отображает всю физическую память в KVM. Это "
+"означает, что FreeBSD может обрабатывать конфигурации памяти до 4 ГБ на 32-"
+"битных платформах. На самом деле, если бы MMU это позволял, FreeBSD "
+"теоретически могла бы обрабатывать конфигурации памяти до 8 ТБ на 32-битной "
+"платформе. Однако, поскольку большинство 32-битных платформ способны "
+"отображать только 4 ГБ оперативной памяти, этот вопрос не имеет "
+"практического значения."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:101
+msgid ""
+"KVM is managed through several mechanisms. The main mechanism used to manage "
+"KVM is the _zone allocator_. The zone allocator takes a chunk of KVM and "
+"splits it up into constant-sized blocks of memory in order to allocate a "
+"specific type of structure. You can use `vmstat -m` to get an overview of "
+"current KVM utilization broken down by zone."
+msgstr ""
+"KVM управляется через несколько механизмов. Основным механизмом для "
+"управления KVM является _аллокатор зон_. Аллокатор зон берет блок KVM и "
+"делит его на блоки памяти постоянного размера для выделения структур "
+"определённого типа. Вы можете использовать команду `vmstat -m`, чтобы "
+"получить обзор текущего использования KVM, разбитого по зонам."
+
+#. type: Title ==
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:103
+#, no-wrap
+msgid "Tuning the FreeBSD VM System"
+msgstr "Настройка системы виртуальной памяти во FreeBSD"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:106
+msgid ""
+"A concerted effort has been made to make the FreeBSD kernel dynamically tune "
+"itself. Typically you do not need to mess with anything beyond the "
+"`maxusers` and `NMBCLUSTERS` kernel config options. That is, kernel "
+"compilation options specified in (typically) [.filename]#/usr/src/sys/i386/"
+"conf/CONFIG_FILE#. A description of all available kernel configuration "
+"options can be found in [.filename]#/usr/src/sys/i386/conf/LINT#."
+msgstr ""
+"Были преложены значительные усилия сделать ядро FreeBSD динамически "
+"настраиваемым. Обычно вам не нужно вмешиваться в настройки, за исключением "
+"параметров конфигурации ядра `maxusers` и `NMBCLUSTERS`. Параметры "
+"компиляции ядра указанны (обычно) в файле [.filename]#/usr/src/sys/i386/conf/"
+"CONFIG_FILE#. Описание всех доступных параметров конфигурации ядра можно "
+"найти в файле [.filename]#/usr/src/sys/i386/conf/LINT#."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:108
+msgid ""
+"In a large system configuration you may wish to increase `maxusers`. Values "
+"typically range from 10 to 128. Note that raising `maxusers` too high can "
+"cause the system to overflow available KVM resulting in unpredictable "
+"operation. It is better to leave `maxusers` at some reasonable number and "
+"add other options, such as `NMBCLUSTERS`, to increase specific resources."
+msgstr ""
+"В большой системе конфигурации, возможно, вам потребуется увеличить значение "
+"`maxusers`. Обычно значения варьируются от 10 до 128. Обратите внимание, что "
+"слишком большое значение для `maxusers` может привести к переполнению "
+"доступной KVM, что вызовет непредсказуемую работу системы. Лучше оставить "
+"`maxusers` на разумном уровне и добавлять другие параметры, такие как "
+"`NMBCLUSTERS`, для увеличения конкретных ресурсов."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:110
+msgid ""
+"If your system is going to use the network heavily, you may want to increase "
+"`NMBCLUSTERS`. Typical values range from 1024 to 4096."
+msgstr ""
+"Если ваша система будет активно использовать сеть, возможно, вам захочется "
+"увеличить значение `NMBCLUSTERS`. Типичные значения варьируются от 1024 до "
+"4096."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:112
+msgid ""
+"The `NBUF` parameter is also traditionally used to scale the system. This "
+"parameter determines the amount of KVA the system can use to map filesystem "
+"buffers for I/O. Note that this parameter has nothing whatsoever to do with "
+"the unified buffer cache! This parameter is dynamically tuned in 3.0-CURRENT "
+"and later kernels and should generally not be adjusted manually. We "
+"recommend that you _not_ try to specify an `NBUF` parameter. Let the system "
+"pick it. Too small a value can result in extremely inefficient filesystem "
+"operation while too large a value can starve the page queues by causing too "
+"many pages to become wired down."
+msgstr ""
+"Параметр `NBUF` также традиционно используется для масштабирования системы. "
+"Этот параметр определяет объем виртуальной памяти ядра, который система "
+"может использовать для отображения файловых буферов для ввода-вывода. "
+"Обратите внимание, что этот параметр никак не связан с унифицированным "
+"буферным кэшем! В ядрах 3.0-CURRENT и позднее этот параметр динамически "
+"настраивается, и в целом не следует настраивать его вручную. Мы рекомендуем "
+"вам _не_ пытаться задавать параметр NBUF. Позвольте системе выбрать его "
+"автоматически. Слишком малое значение может привести к крайне неэффективной "
+"работе файловой системы, в то время как слишком большое значение может "
+"привести к истощению очередей страниц из-за того, что слишком много страниц "
+"будет закреплено в памяти."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:114
+msgid ""
+"By default, FreeBSD kernels are not optimized. You can set debugging and "
+"optimization flags with the `makeoptions` directive in the kernel "
+"configuration. Note that you should not use `-g` unless you can accommodate "
+"the large (typically 7 MB+) kernels that result."
+msgstr ""
+"По умолчанию ядра FreeBSD не оптимизированы. Вы можете установить флаги "
+"отладки и оптимизации с помощью директивы `makeoptions` в конфигурации ядра. "
+"Обратите внимание, что не следует использовать флаг `-g`, если вы не можете "
+"разместить в системе большие ядра (обычно более 7 МБ), которые получаются в "
+"результате."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:119
+#, no-wrap
+msgid ""
+"makeoptions DEBUG=\"-g\"\n"
+"makeoptions COPTFLAGS=\"-O -pipe\"\n"
+msgstr ""
+"makeoptions DEBUG=\"-g\" \n"
+"makeoptions COPTFLAGS=\"-O -pipe\"\n"
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:122
+msgid ""
+"Sysctl provides a way to tune kernel parameters at run-time. You typically "
+"do not need to mess with any of the sysctl variables, especially the VM "
+"related ones."
+msgstr ""
+"Sysctl предоставляет способ настройки параметров ядра во время работы "
+"системы. Обычно вам не нужно изменять какие-либо переменные sysctl, особенно "
+"те, что связаны с виртуальной памятью."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:124
+msgid ""
+"Run time VM and system tuning is relatively straightforward. First, use Soft "
+"Updates on your UFS/FFS filesystems whenever possible. [.filename]#/usr/src/"
+"sys/ufs/ffs/README.softupdates# contains instructions (and restrictions) on "
+"how to configure it."
+msgstr ""
+"Настройка виртуальной памяти (VM) и системы во время работы относительно "
+"проста. Во-первых, используйте Soft Updates на ваших файловых системах UFS/"
+"FFS, когда это возможно. Файл [.filename]#/usr/src/sys/ufs/ffs/"
+"README.softupdates# содержит инструкции (и ограничения) по конфигурации этой "
+"функции."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:126
+msgid ""
+"Second, configure sufficient swap. You should have a swap partition "
+"configured on each physical disk, up to four, even on your \"work\" disks. "
+"You should have at least 2x the swap space as you have main memory, and "
+"possibly even more if you do not have a lot of memory. You should also size "
+"your swap partition based on the maximum memory configuration you ever "
+"intend to put on the machine so you do not have to repartition your disks "
+"later on. If you want to be able to accommodate a crash dump, your first "
+"swap partition must be at least as large as main memory and [.filename]#/var/"
+"crash# must have sufficient free space to hold the dump."
+msgstr ""
+"Во-вторых, настройте достаточный объем подкачки (swap). Вы должны создать "
+"раздел подкачки на каждом физическом диске, до четырёх, даже на \"рабочих\" "
+"дисках. Объём подкачки должен быть как минимум в 2 раза больше объёма "
+"основной памяти, а возможно — и больше, если у вас немного ОЗУ. Размер "
+"раздела подкачки следует выбирать с учётом максимальной конфигурации памяти, "
+"которую вы когда-либо планируете установить на эту машину, чтобы в будущем "
+"не пришлось переразбивать диски. Если вы хотите иметь возможность сохранять "
+"дампы при сбое, ваш первый swap-раздел должен быть не меньше объёма основной "
+"памяти, а каталог [.filename]#/var/crash# должен иметь достаточно свободного "
+"места для хранения дампа."
+
+#. type: Plain text
+#: documentation/content/en/books/arch-handbook/vm/_index.adoc:127
+msgid ""
+"NFS-based swap is perfectly acceptable on 4.X or later systems, but you must "
+"be aware that the NFS server will take the brunt of the paging load."
+msgstr ""
+"Подкачка на базе NFS вполне допустима в системах версии 4.X и новее, однако "
+"необходимо учитывать, что основная нагрузка на подкачку ляжет на NFS-сервер."
diff --git a/documentation/content/ru/books/developers-handbook/_index.adoc b/documentation/content/ru/books/developers-handbook/_index.adoc
index 98a3fbc883..9e83164861 100644
--- a/documentation/content/ru/books/developers-handbook/_index.adoc
+++ b/documentation/content/ru/books/developers-handbook/_index.adoc
@@ -1,24 +1,31 @@
---
-title: Руководство FreeBSD для разработчиков
+add_single_page_link: true
authors:
- - author: The FreeBSD Documentation Project
-copyright: 1995-2020 The FreeBSD Documentation Project
+ -
+ author: 'The FreeBSD Documentation Project'
+bookOrder: 25
+copyright: '1995-2023 The FreeBSD Documentation Project'
+description: 'Для тех, кто хочет разрабатывать программное обеспечение для FreeBSD (а не только для тех, кто разрабатывает саму FreeBSD)'
+next: books/developers-handbook/parti
+params:
+ path: /books/developers-handbook/
+showBookMenu: true
+tags: "[\"FreeBSD Developers' Handbook\"]"
+title: 'Руководство разработчика FreeBSD'
trademarks: ["freebsd", "apple", "ibm", "ieee", "intel", "linux", "microsoft", "opengroup", "sun", "general"]
-isIndex: true
+weight: 0
---
-= Руководство FreeBSD для разработчиков
+= Руководство разработчика FreeBSD
:doctype: book
:toc: macro
-:toclevels: 2
+:toclevels: 1
:icons: font
:sectnums:
:sectnumlevels: 6
:partnums:
:source-highlighter: rouge
:experimental:
-:book: true
-:pdf: false
:images-path: books/developers-handbook/
ifdef::env-beastie[]
@@ -30,245 +37,21 @@ include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
-:chapters-path: content/{{% lang %}}/books/developers-handbook/
endif::[]
ifdef::backend-pdf,backend-epub3[]
-:chapters-path:
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]
ifndef::env-beastie[]
-:chapters-path:
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
[.abstract-title]
Аннотация
-Добро пожаловать в руководство FreeBSD для разработчиков.
+Добро пожаловать в Руководство разработчика. Этот документ находится в _процессе разработки_ и создаётся усилиями многих людей. Многие разделы пока отсутствуют, а существующие нуждаются в обновлении. Если вы хотите помочь с этим проектом, отправьте письмо на {freebsd-doc}.
-'''
-
-toc::[]
-
-[[introduction]]
-= Введение
-
-[[developmentplatform]]
-== Разработка во FreeBSD
-
-Здесь необходимо будет обсудить FreeBSD в качестве платформы для разработки, подход к этому BSD, обзор архитектуры, структура /usr/src, история и так далее.
-
-Спасибо вам за выбор FreeBSD в качестве платформы разработки! Надеемся, что она вас не подведет.
-
-[[bsdvision]]
-== Парадигма BSD
-
-[[archoverview]]
-== Обзор архитектуры
-
-[[sourcelayout]]
-== Структура /usr/src
-
-Полный исходный код FreeBSD имеется на нашем общедоступном хранилище CVS. Как правило, исходный код устанавливается в каталог [.filename]#/usr/src#, содержащий следующие подкаталоги.
-
-[.informaltable]
-[cols="1,1", frame="none", options="header"]
-|===
-| Каталог
-| Описание
-
-|[.filename]#bin/#
-|Исходный код файлов из [.filename]#/bin#
-
-|[.filename]#contrib/#
-|Исходный код файлов программного обеспечения, которое предоставлено третьими лицами.
-
-|[.filename]#crypto/#
-|Исходный код DES
-
-|[.filename]#etc/#
-|Исходный код файлов из [.filename]#/etc#
-
-|[.filename]#games/#
-|Исходный код файлов из [.filename]#/usr/games#
-
-|[.filename]#gnu/#
-|Утилиты, подпадающие под условия GNU Public License
-
-|[.filename]#include/#
-|Исходный код файлов из [.filename]#/usr/include#
-
-|[.filename]#kerberosIV/#
-|Исходный код Kerbereros версии IV
-
-|[.filename]#kerberos5/#
-|Исходный код Kerbereros версии 5
-
-|[.filename]#lib/#
-|Исходный код файлов из [.filename]#/usr/lib#
-
-|[.filename]#libexec/#
-|Исходный код файлов из [.filename]#/usr/libexec#
-
-|[.filename]#release/#
-|Файлы, необходимые для выпуска релиза FreeBSD
-
-|[.filename]#sbin/#
-|Исходный код файлов из [.filename]#/sbin#
-
-|[.filename]#secure/#
-|Исходный код FreeSec
-
-|[.filename]#share/#
-|Исходный код файлов из [.filename]#/sbin#
-
-|[.filename]#sys/#
-|Исходные тексты ядра
-
-|[.filename]#tools/#
-|Инструментальные средства, использемые для обслуживания и тестирования FreeBSD
-
-|[.filename]#usr.bin/#
-|Исходный код файлов из [.filename]#/usr/bin#
-
-|[.filename]#usr.sbin/#
-|Исходный код файлов из [.filename]#/usr/sbin#
-|===
-
-[[Basics]]
-= Основы
-include::{chapters-path}secure/chapter.adoc[leveloffset=+1]
-
-[[kernel]]
-= Ядро
-
-[[kernelhistory]]
-== История ядра Unix
-
-Немного истории о ядре Unix/BSD, системных вызовах, как работают процессы, блокировке, планировке задач, нити (ядра), переключение контекста, сигналы, прерывания, модули и так далее.
-
-[[memory]]
-= Память и виртуальная память
-
-[[virtualmemory]]
-== Виртуальная память
-
-VM, постраничная подкачка и свопирование, выделение памяти, тестирование ошибок утечки памяти, mmap, vnode и так далее.
-
-[[iosystem]]
-= Система ввода/вывода
-
-[[ufs]]
-== UFS
-
-UFS, FFS, Ext2FS, JFS, inodes, buffer cache, labeling, locking, metadata, soft-updates, LFS, portalfs, procfs, vnodes, memory sharing, memory objects, TLBs, caching
-
-[[ipc]]
-= Межпроцессное взаимодействие
+Последняя версия этого документа всегда доступна по ссылке link:https://www.FreeBSD.org[веб-сервер FreeBSD]. Его также можно загрузить в различных форматах и с разными вариантами сжатия с link:https://download.freebsd.org/doc/[сервера загрузки FreeBSD] или одного из многочисленных extref:{handbook}[зеркальных сайтов, mirrors].
-[[signals]]
-== Сигналы
-
-Сигналы, конвейеры, семафоры, очереди сообщений, совместно используемая память, сокеты, двери
-
-[[networking]]
-= Работа в сети
-
-[[sockets]]
-== Сокеты
-
-Сокеты, bpf, IP, TCP, UDP, ICMP, OSI, bridging, firewalling, NAT, коммутация и так далее
-
-[[networkfs]]
-= Сетевые файловые системы
-
-[[afs]]
-== AFS
-
-AFS, NFS, SANs etc]
-
-[[terminal]]
-= Работа с терминалами
-
-[[syscons]]
-== Системные консоли
-
-Syscons, tty, PCVT, последовательная консоль, хранители экрана и так далее
-
-[[sound]]
-= Звук
-
-[[oss]]
-== OSS
-
-OSS, waveforms, etc
-
-[[devicedrivers]]
-= Драйверы устройств
-
-[[usb]]
-== Устройства USB
-
-Эта глава расскажет о механизмах, используемых во FreeBSD для написания драйверов для устройств на шине USB.
-
-[[newbus]]
-== NewBus
-
-Эта глава расскажет об архитектуре NewBus во FreeBSD.
-
-[[architectures]]
-= Аппаратные платформы
-
-[[ia32]]
-== IA-32
-
-Рассказ об архитектурных особенностях FreeBSD/x86.
-
-[[alpha]]
-== Alpha
-
-Рассказ об архитектурных особенностях FreeBSD/alpha.
-
-Описание ошибок выравнивания, как их исправлять и как игнорировать.
-
-Пример ассемблерного кода для FreeBSD/alpha.
-
-[[ia64]]
-== IA-64
-
-Рассказ об архитектурных особенностях FreeBSD/ia64.
-
-[[debuggingpart]]
-= Отладка
-
-[[truss]]
-== Truss
-
-Различные описания того, как отлаживать отдельные компоненты системы при помощи утилит truss, ktrace, gdb, kgdb, etc
-
-[[compatibility]]
-= Обеспечение совместимости
-
-[[linux]]
-== Linux
-
-Linux, SVR4 и так далее
-
-// Appendices
-[[appendices]]
-[bibliography]
-= Приложения
-
-[[COD,1]] [1] Dave A Patterson and John L Hennessy. Copyright(R) 1998 Morgan Kaufmann Publishers, Inc. 1-55860-428-6. Morgan Kaufmann Publishers, Inc. Computer Organization and Design. The Hardware / Software Interface. 1-2.
-
-[[APUE, 2]] [2] W. Richard Stevens. Copyright(R) 1993 Addison Wesley Longman, Inc. 0-201-56317-7. Addison Wesley Longman, Inc. Advanced Programming in the Unix Environment. 1-2.
-
-[[DIFOS, 3]] [3] Marshall Kirk McKusick and George Neville-Neil. Copyright(R) 2004 Addison-Wesley. 0-201-70245-2. Addison-Wesley. The Design and Implementation of the FreeBSD Operating System. 1-2.
-
-[[Phrack, 4]] [4] Aleph One. Phrack 49; "Smashing the Stack for Fun and Profit".
-
-[[StackGuard, 5]] [5] Chrispin Cowan, Calton Pu, and Dave Maier. StackGuard; Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks.
-
-[[OpenBSD, 6]] [6] Todd Miller and Theo de Raadt. strlcpy and strlcat -- consistent, safe string copy and concatenation.
+'''
diff --git a/documentation/content/ru/books/developers-handbook/_index.po b/documentation/content/ru/books/developers-handbook/_index.po
new file mode 100644
index 0000000000..17abcc726d
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/_index.po
@@ -0,0 +1,70 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-07-05 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbook_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/developers-handbook/_index.adoc:1
+#, no-wrap
+msgid "For people who want to develop software for FreeBSD (and not just people who are developing FreeBSD itself)"
+msgstr "Для тех, кто хочет разрабатывать программное обеспечение для FreeBSD (а не только для тех, кто разрабатывает саму FreeBSD)"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/_index.adoc:1
+#: documentation/content/en/books/developers-handbook/_index.adoc:18
+#, no-wrap
+msgid "FreeBSD Developers' Handbook"
+msgstr "Руководство разработчика FreeBSD"
+
+#. type: .abstract-title
+#: documentation/content/en/books/developers-handbook/_index.adoc:51
+msgid "Abstract"
+msgstr "Аннотация"
+
+#. type: .abstract-title
+#: documentation/content/en/books/developers-handbook/_index.adoc:56
+msgid ""
+"Welcome to the Developers' Handbook. This manual is a _work in progress_ "
+"and is the work of many individuals. Many sections do not yet exist and "
+"some of those that do exist need to be updated. If you are interested in "
+"helping with this project, send email to the {freebsd-doc}."
+msgstr ""
+"Добро пожаловать в Руководство разработчика. Этот документ находится в "
+"_процессе разработки_ и создаётся усилиями многих людей. Многие разделы пока "
+"отсутствуют, а существующие нуждаются в обновлении. Если вы хотите помочь с "
+"этим проектом, отправьте письмо на {freebsd-doc}."
+
+#. type: .abstract-title
+#: documentation/content/en/books/developers-handbook/_index.adoc:59
+msgid ""
+"The latest version of this document is always available from the "
+"link:https://www.FreeBSD.org[FreeBSD World Wide Web server]. It may also be "
+"downloaded in a variety of formats and compression options from the "
+"link:https://download.freebsd.org/doc/[FreeBSD download server] or one of "
+"the numerous extref:{handbook}[mirror sites, mirrors]."
+msgstr ""
+"Последняя версия этого документа всегда доступна по ссылке link:https://"
+"www.FreeBSD.org[веб-сервер FreeBSD]. Его также можно загрузить в различных "
+"форматах и с разными вариантами сжатия с link:https://download.freebsd.org/"
+"doc/[сервера загрузки FreeBSD] или одного из многочисленных extref:{handbook}"
+"[зеркальных сайтов, mirrors]."
+
+#. type: .abstract-title
+#: documentation/content/en/books/developers-handbook/_index.adoc:60
+msgid "'''"
+msgstr "'''"
diff --git a/documentation/content/ru/books/developers-handbook/bibliography/_index.adoc b/documentation/content/ru/books/developers-handbook/bibliography/_index.adoc
new file mode 100644
index 0000000000..d107cef3fa
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/bibliography/_index.adoc
@@ -0,0 +1,62 @@
+---
+description: 'Библиография Руководства разработчика FreeBSD'
+params:
+ path: /books/developers-handbook/bibliography/
+prev: books/developers-handbook/partv
+showBookMenu: true
+tags: "[\"FreeBSD Developers' Handbook\", \"bibliography\"]"
+title: Библиография
+weight: 17
+---
+
+[appendix]
+[[bibliography]]
+= Библиография
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: A
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/developers-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[COD,1]] [1] Dave A Patterson and John L Hennessy. Copyright(R) 1998 Morgan Kaufmann Publishers, Inc. 1-55860-428-6. Morgan Kaufmann Publishers, Inc. Computer Organization and Design. The Hardware / Software Interface. 1-2.
+
+[[APUE, 2]] [2] W. Richard Stevens. Copyright(R) 1993 Addison Wesley Longman, Inc. 0-201-56317-7. Addison Wesley Longman, Inc. Advanced Programming in the Unix Environment. 1-2.
+
+[[DIFOS, 3]] [3] Marshall Kirk McKusick and George Neville-Neil. Copyright(R) 2004 Addison-Wesley. 0-201-70245-2. Addison-Wesley. The Design and Implementation of the FreeBSD Operating System. 1-2.
+
+[[Phrack, 4]] [4] Aleph One. Phrack 49; "Smashing the Stack for Fun and Profit".
+
+[[StackGuard, 5]] [5] Chrispin Cowan, Calton Pu, and Dave Maier. StackGuard; Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks.
+
+[[OpenBSD, 6]] [6] Todd Miller and Theo de Raadt. strlcpy and strlcat -- consistent, safe string copy and concatenation.
+
diff --git a/documentation/content/ru/books/developers-handbook/bibliography/_index.po b/documentation/content/ru/books/developers-handbook/bibliography/_index.po
new file mode 100644
index 0000000000..82f48421ac
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/bibliography/_index.po
@@ -0,0 +1,92 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-06-21 19:10+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookbibliography_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/developers-handbook/bibliography/_index.adoc:1
+#, no-wrap
+msgid "FreeBSD Developers Handbook Bibliography"
+msgstr "Библиография Руководства разработчика FreeBSD"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/bibliography/_index.adoc:1
+#: documentation/content/en/books/developers-handbook/bibliography/_index.adoc:14
+#, no-wrap
+msgid "Bibliography"
+msgstr "Библиография"
+
+#. type: appendix
+#: documentation/content/en/books/developers-handbook/bibliography/_index.adoc:52
+msgid ""
+"[[COD,1]] [1] Dave A Patterson and John L Hennessy. Copyright(R) 1998 Morgan "
+"Kaufmann Publishers, Inc. 1-55860-428-6. Morgan Kaufmann Publishers, Inc. "
+"Computer Organization and Design. The Hardware / Software Interface. 1-2."
+msgstr ""
+"[[COD,1]] [1] Dave A Patterson and John L Hennessy. Copyright(R) 1998 Morgan "
+"Kaufmann Publishers, Inc. 1-55860-428-6. Morgan Kaufmann Publishers, Inc. "
+"Computer Organization and Design. The Hardware / Software Interface. 1-2."
+
+#. type: appendix
+#: documentation/content/en/books/developers-handbook/bibliography/_index.adoc:54
+msgid ""
+"[[APUE, 2]] [2] W. Richard Stevens. Copyright(R) 1993 Addison Wesley "
+"Longman, Inc. 0-201-56317-7. Addison Wesley Longman, Inc. Advanced "
+"Programming in the Unix Environment. 1-2."
+msgstr ""
+"[[APUE, 2]] [2] W. Richard Stevens. Copyright(R) 1993 Addison Wesley "
+"Longman, Inc. 0-201-56317-7. Addison Wesley Longman, Inc. Advanced "
+"Programming in the Unix Environment. 1-2."
+
+#. type: appendix
+#: documentation/content/en/books/developers-handbook/bibliography/_index.adoc:56
+msgid ""
+"[[DIFOS, 3]] [3] Marshall Kirk McKusick and George Neville-Neil. "
+"Copyright(R) 2004 Addison-Wesley. 0-201-70245-2. Addison-Wesley. The Design "
+"and Implementation of the FreeBSD Operating System. 1-2."
+msgstr ""
+"[[DIFOS, 3]] [3] Marshall Kirk McKusick and George Neville-Neil. "
+"Copyright(R) 2004 Addison-Wesley. 0-201-70245-2. Addison-Wesley. The Design "
+"and Implementation of the FreeBSD Operating System. 1-2."
+
+#. type: appendix
+#: documentation/content/en/books/developers-handbook/bibliography/_index.adoc:58
+msgid ""
+"[[Phrack, 4]] [4] Aleph One. Phrack 49; \"Smashing the Stack for Fun and "
+"Profit\"."
+msgstr ""
+"[[Phrack, 4]] [4] Aleph One. Phrack 49; \"Smashing the Stack for Fun and "
+"Profit\"."
+
+#. type: appendix
+#: documentation/content/en/books/developers-handbook/bibliography/_index.adoc:60
+msgid ""
+"[[StackGuard, 5]] [5] Chrispin Cowan, Calton Pu, and Dave Maier. StackGuard; "
+"Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks."
+msgstr ""
+"[[StackGuard, 5]] [5] Chrispin Cowan, Calton Pu, and Dave Maier. StackGuard; "
+"Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks."
+
+#. type: appendix
+#: documentation/content/en/books/developers-handbook/bibliography/_index.adoc:62
+msgid ""
+"[[OpenBSD, 6]] [6] Todd Miller and Theo de Raadt. strlcpy and strlcat -- "
+"consistent, safe string copy and concatenation."
+msgstr ""
+"[[OpenBSD, 6]] [6] Todd Miller and Theo de Raadt. strlcpy and strlcat -- "
+"consistent, safe string copy and concatenation."
diff --git a/documentation/content/ru/books/developers-handbook/book.adoc b/documentation/content/ru/books/developers-handbook/book.adoc
new file mode 100644
index 0000000000..1ff909d433
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/book.adoc
@@ -0,0 +1,84 @@
+---
+add_split_page_link: true
+authors:
+ -
+ author: 'The FreeBSD Documentation Project'
+copyright: '1995-2023 The FreeBSD Documentation Project'
+description: 'Для тех, кто хочет разрабатывать программное обеспечение для FreeBSD (а не только для тех, кто разрабатывает саму FreeBSD)'
+tags: "[\"FreeBSD Developers' Handbook\"]"
+title: 'Руководство разработчика FreeBSD'
+trademarks: ["freebsd", "apple", "ibm", "ieee", "intel", "linux", "microsoft", "opengroup", "sun", "general"]
+---
+
+= Руководство разработчика FreeBSD
+:doctype: book
+:toc: macro
+:toclevels: 2
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:book: true
+:pdf: false
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+:chapters-path: content/{{% lang %}}/books/developers-handbook/
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+:chapters-path:
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+:chapters-path:
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[.abstract-title]
+Аннотация
+
+Добро пожаловать в Руководство разработчика. Этот документ находится в _процессе разработки_ и создаётся усилиями многих людей. Многие разделы пока отсутствуют, а существующие нуждаются в обновлении. Если вы хотите помочь с этим проектом, отправьте письмо на {freebsd-doc}.
+
+Последняя версия этого документа всегда доступна по ссылке link:https://www.FreeBSD.org[веб-сервер FreeBSD]. Его также можно загрузить в различных форматах и с разными вариантами сжатия с link:https://download.freebsd.org/doc/[сервера загрузки FreeBSD] или одного из многочисленных extref:{handbook}[зеркальных сайтов, mirrors].
+
+'''
+
+toc::[]
+
+// Section one
+include::{chapters-path}parti.adoc[]
+include::{chapters-path}introduction/_index.adoc[leveloffset=+1]
+include::{chapters-path}tools/_index.adoc[leveloffset=+1]
+include::{chapters-path}secure/_index.adoc[leveloffset=+1]
+include::{chapters-path}l10n/_index.adoc[leveloffset=+1]
+include::{chapters-path}policies/_index.adoc[leveloffset=+1]
+include::{chapters-path}testing/_index.adoc[leveloffset=+1]
+
+// Section two
+include::{chapters-path}partii.adoc[]
+include::{chapters-path}sockets/_index.adoc[leveloffset=+1]
+include::{chapters-path}ipv6/_index.adoc[leveloffset=+1]
+
+// Section three
+include::{chapters-path}partiii.adoc[]
+include::{chapters-path}kernelbuild/_index.adoc[leveloffset=+1]
+include::{chapters-path}kerneldebug/_index.adoc[leveloffset=+1]
+
+// Section four
+include::{chapters-path}partiv.adoc[]
+include::{chapters-path}x86/_index.adoc[leveloffset=+1]
+
+// Appendices
+include::{chapters-path}partv.adoc[]
+include::{chapters-path}bibliography/_index.adoc[leveloffset=+1]
diff --git a/documentation/content/ru/books/developers-handbook/book.po b/documentation/content/ru/books/developers-handbook/book.po
new file mode 100644
index 0000000000..cf8b6b30d0
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/book.po
@@ -0,0 +1,70 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-07-05 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookbook/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/developers-handbook/book.adoc:1
+#, no-wrap
+msgid "For people who want to develop software for FreeBSD (and not just people who are developing FreeBSD itself)"
+msgstr "Для тех, кто хочет разрабатывать программное обеспечение для FreeBSD (а не только для тех, кто разрабатывает саму FreeBSD)"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/book.adoc:1
+#: documentation/content/en/books/developers-handbook/book.adoc:12
+#, no-wrap
+msgid "FreeBSD Developers' Handbook"
+msgstr "Руководство разработчика FreeBSD"
+
+#. type: .abstract-title
+#: documentation/content/en/books/developers-handbook/book.adoc:49
+msgid "Abstract"
+msgstr "Аннотация"
+
+#. type: .abstract-title
+#: documentation/content/en/books/developers-handbook/book.adoc:54
+msgid ""
+"Welcome to the Developers' Handbook. This manual is a _work in progress_ "
+"and is the work of many individuals. Many sections do not yet exist and "
+"some of those that do exist need to be updated. If you are interested in "
+"helping with this project, send email to the {freebsd-doc}."
+msgstr ""
+"Добро пожаловать в Руководство разработчика. Этот документ находится в "
+"_процессе разработки_ и создаётся усилиями многих людей. Многие разделы пока "
+"отсутствуют, а существующие нуждаются в обновлении. Если вы хотите помочь с "
+"этим проектом, отправьте письмо на {freebsd-doc}."
+
+#. type: .abstract-title
+#: documentation/content/en/books/developers-handbook/book.adoc:57
+msgid ""
+"The latest version of this document is always available from the "
+"link:https://www.FreeBSD.org[FreeBSD World Wide Web server]. It may also be "
+"downloaded in a variety of formats and compression options from the "
+"link:https://download.freebsd.org/doc/[FreeBSD download server] or one of "
+"the numerous extref:{handbook}[mirror sites, mirrors]."
+msgstr ""
+"Последняя версия этого документа всегда доступна по ссылке link:https://"
+"www.FreeBSD.org[веб-сервер FreeBSD]. Его также можно загрузить в различных "
+"форматах и с разными вариантами сжатия с link:https://download.freebsd.org/"
+"doc/[сервера загрузки FreeBSD] или одного из многочисленных extref:{handbook}"
+"[зеркальных сайтов, mirrors]."
+
+#. type: .abstract-title
+#: documentation/content/en/books/developers-handbook/book.adoc:59
+msgid "'''"
+msgstr "'''"
diff --git a/documentation/content/ru/books/developers-handbook/introduction/_index.adoc b/documentation/content/ru/books/developers-handbook/introduction/_index.adoc
new file mode 100644
index 0000000000..d212e92f3a
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/introduction/_index.adoc
@@ -0,0 +1,86 @@
+---
+authors:
+ -
+ author: 'Murray Stokely'
+ -
+ author: 'Jeroen Ruigrok van der Werven'
+description: 'Введение в Руководство разработчика FreeBSD'
+next: books/developers-handbook/tools
+params:
+ path: /books/developers-handbook/introduction/
+prev: books/developers-handbook/parti
+showBookMenu: true
+tags: ["introduction", "Developing on FreeBSD", "BSD Vision", "Architectural Guidelines"]
+title: 'Глава 1. Введение'
+weight: 2
+---
+
+[[introduction]]
+= Введение
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 1
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/developers-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[introduction-devel]]
+== Разработка на FreeBSD
+
+Вот мы и здесь. Система установлена, и вы готовы начать программировать. Но с чего начать? Что предоставляет FreeBSD? Что она может сделать для меня как для программиста?
+
+Вот некоторые вопросы, на которые эта глава пытается ответить. Конечно, программирование, как и любое другое ремесло, имеет разные уровни мастерства. Для кого-то это хобби, для других — профессия. Информация в этой главе может быть ориентирована на начинающего программиста; действительно, она может быть полезна программисту, не знакомому с платформой FreeBSD.
+
+[[introduction-bsdvision]]
+== Видение BSD
+
+Создать наилучший пакет операционной системы, подобной UNIX(R), с должным уважением к оригинальной идеологии программных инструментов, а также к удобству использования, производительности и стабильности.
+
+[[introduction-archguide]]
+== Архитектурные рекомендации
+
+Наша идеология может быть описана следующими принципами
+
+* Не добавляйте новую функциональность, если разработчик не может завершить реальное приложение без неё.
+* Важно не только определить, чем является система, но и чем она не является. Не стоит пытаться удовлетворить все возможные потребности; вместо этого сделайте систему расширяемой, чтобы дополнительные требования могли быть реализованы с сохранением совместимости.
+* Худшее, чем обобщение на основе одного примера — это обобщение без примеров вообще.
+* Если проблема не до конца понятна, вероятно, лучше вообще не предоставлять решения.
+* Если вы можете получить 90% желаемого эффекта за 10% работы, используйте более простое решение.
+* Изолируйте сложность настолько, насколько это возможно.
+* Предоставлять механизмы, а не политики. В частности, передайте политику пользовательского интерфейса в руки клиента.
+
+Из Шейфлера и Геттиса: «X Window System»
+
+[[introduction-layout]]
+== Структура каталога /usr/src
+
+Полный исходный код FreeBSD доступен в нашем link:https://cgit.freebsd.org/src/[публичном Git-репозитории]. Исходный код обычно устанавливается в [.filename]#/usr/src#. Структура дерева каталогов исходного кода описана в файле link:https://cgit.freebsd.org/src/tree/README.md[README.md] на верхнем уровне дерева.
diff --git a/documentation/content/ru/books/developers-handbook/introduction/_index.po b/documentation/content/ru/books/developers-handbook/introduction/_index.po
new file mode 100644
index 0000000000..8c010f6a5d
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/introduction/_index.po
@@ -0,0 +1,185 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-07-06 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookintroduction_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:1
+#, no-wrap
+msgid "Introduction to the FreeBSD Developers Handbook"
+msgstr "Введение в Руководство разработчика FreeBSD"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:1
+#, no-wrap
+msgid "Chapter 1. Introduction"
+msgstr "Глава 1. Введение"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:17
+#, no-wrap
+msgid "Introduction"
+msgstr "Введение"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:55
+#, no-wrap
+msgid "Developing on FreeBSD"
+msgstr "Разработка на FreeBSD"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:60
+msgid ""
+"So here we are. System all installed and you are ready to start "
+"programming. But where to start? What does FreeBSD provide? What can it do "
+"for me, as a programmer?"
+msgstr ""
+"Вот мы и здесь. Система установлена, и вы готовы начать программировать. Но "
+"с чего начать? Что предоставляет FreeBSD? Что она может сделать для меня как "
+"для программиста?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:66
+msgid ""
+"These are some questions which this chapter tries to answer. Of course, "
+"programming has different levels of proficiency like any other trade. For "
+"some it is a hobby, for others it is their profession. The information in "
+"this chapter might be aimed toward the beginning programmer; indeed, it "
+"could serve useful for the programmer unfamiliar with the FreeBSD platform."
+msgstr ""
+"Вот некоторые вопросы, на которые эта глава пытается ответить. Конечно, "
+"программирование, как и любое другое ремесло, имеет разные уровни "
+"мастерства. Для кого-то это хобби, для других — профессия. Информация в этой "
+"главе может быть ориентирована на начинающего программиста; действительно, "
+"она может быть полезна программисту, не знакомому с платформой FreeBSD."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:68
+#, no-wrap
+msgid "The BSD Vision"
+msgstr "Видение BSD"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:71
+msgid ""
+"To produce the best UNIX(R) like operating system package possible, with due "
+"respect to the original software tools ideology as well as usability, "
+"performance and stability."
+msgstr ""
+"Создать наилучший пакет операционной системы, подобной UNIX(R), с должным "
+"уважением к оригинальной идеологии программных инструментов, а также к "
+"удобству использования, производительности и стабильности."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:73
+#, no-wrap
+msgid "Architectural Guidelines"
+msgstr "Архитектурные рекомендации"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:76
+msgid "Our ideology can be described by the following guidelines"
+msgstr "Наша идеология может быть описана следующими принципами"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:78
+msgid ""
+"Do not add new functionality unless an implementor cannot complete a real "
+"application without it."
+msgstr ""
+"Не добавляйте новую функциональность, если разработчик не может завершить "
+"реальное приложение без неё."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:79
+msgid ""
+"It is as important to decide what a system is not as to decide what it is. "
+"Do not serve all the world's needs; rather, make the system extensible so "
+"that additional needs can be met in an upwardly compatible fashion."
+msgstr ""
+"Важно не только определить, чем является система, но и чем она не является. "
+"Не стоит пытаться удовлетворить все возможные потребности; вместо этого "
+"сделайте систему расширяемой, чтобы дополнительные требования могли быть "
+"реализованы с сохранением совместимости."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:80
+msgid ""
+"The only thing worse than generalizing from one example is generalizing from "
+"no examples at all."
+msgstr ""
+"Худшее, чем обобщение на основе одного примера — это обобщение без примеров "
+"вообще."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:81
+msgid ""
+"If a problem is not completely understood, it is probably best to provide no "
+"solution at all."
+msgstr ""
+"Если проблема не до конца понятна, вероятно, лучше вообще не предоставлять "
+"решения."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:82
+msgid ""
+"If you can get 90 percent of the desired effect for 10 percent of the work, "
+"use the simpler solution."
+msgstr ""
+"Если вы можете получить 90% желаемого эффекта за 10% работы, используйте "
+"более простое решение."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:83
+msgid "Isolate complexity as much as possible."
+msgstr "Изолируйте сложность настолько, насколько это возможно."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:84
+msgid ""
+"Provide mechanism, rather than policy. In particular, place user interface "
+"policy in the client's hands."
+msgstr ""
+"Предоставлять механизмы, а не политики. В частности, передайте политику "
+"пользовательского интерфейса в руки клиента."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:86
+msgid "From Scheifler & Gettys: \"X Window System\""
+msgstr "Из Шейфлера и Геттиса: «X Window System»"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:88
+#, no-wrap
+msgid "The Layout of /usr/src"
+msgstr "Структура каталога /usr/src"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/introduction/_index.adoc:92
+msgid ""
+"The complete source code for FreeBSD is available from our link:https://"
+"cgit.freebsd.org/src/[public Git repository]. The source code is normally "
+"installed in [.filename]#/usr/src#. The layout of the source tree is "
+"described by the top-level link:https://cgit.freebsd.org/src/tree/"
+"README.md[README.md] file."
+msgstr ""
+"Полный исходный код FreeBSD доступен в нашем link:https://cgit.freebsd.org/"
+"src/[публичном Git-репозитории]. Исходный код обычно устанавливается в "
+"[.filename]#/usr/src#. Структура дерева каталогов исходного кода описана в "
+"файле link:https://cgit.freebsd.org/src/tree/README.md[README.md] на верхнем "
+"уровне дерева."
diff --git a/documentation/content/ru/books/developers-handbook/ipv6/_index.adoc b/documentation/content/ru/books/developers-handbook/ipv6/_index.adoc
new file mode 100644
index 0000000000..70f6853b87
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/ipv6/_index.adoc
@@ -0,0 +1,687 @@
+---
+authors:
+ -
+ author: 'Yoshinobu Inoue'
+description: 'Внутреннее устройство IPv6'
+next: books/developers-handbook/partiii
+params:
+ path: /books/developers-handbook/ipv6/
+prev: books/developers-handbook/sockets
+showBookMenu: true
+tags: ["IPv6", "FreeBSD"]
+title: 'Глава 8. Внутреннее устройство IPv6'
+weight: 10
+---
+
+[[ipv6]]
+= Внутреннее устройство IPv6
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 8
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/developers-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[ipv6-implementation]]
+== Реализация IPv6/IPsec
+
+В этом разделе мы объясним внутреннюю реализацию, связанную с IPv6 и IPsec. Данная функциональность заимствована из http://www.kame.net/[проекта KAME]
+
+[[ipv6details]]
+=== IPv6
+
+==== Соответствие
+
+Функции, связанные с IPv6, соответствуют или пытаются соответствовать последнему набору спецификаций IPv6. Для дальнейшего использования мы приводим некоторые из соответствующих документов ниже (_ПРИМЕЧАНИЕ_: это не полный список — его слишком сложно поддерживать...).
+
+Для подробностей обратитесь к соответствующей главе документа, RFC, страницам Справосника или комментариям в исходном коде.
+
+Тесты на соответствие стандартам были проведены для KAME STABLE в проекте TAHI. Результаты можно посмотреть по ссылке http://www.tahi.org/report/KAME/[http://www.tahi.org/report/KAME/]. Мы также участвовали в тестах IOL Университета Нью-Гэмпшира (http://www.iol.unh.edu/[http://www.iol.unh.edu/]) в прошлом, используя наши предыдущие версии.
+
+* RFC1639: FTP Operation Over Big Address Records (FOOBAR)
+
+** RFC2428 предпочтительнее RFC1639. FTP-клиенты сначала попробуют RFC2428, затем RFC1639 в случае неудачи.
+
+* RFC1886: DNS Extensions to support IPv6
+* RFC1933: Transition Mechanisms for IPv6 Hosts and Routers
+
+** IPv4-совместимый адрес не поддерживается.
+** автоматическое туннелирование (описано в разделе 4.3 данного RFC) не поддерживается.
+** man:gif[4] интерфейс реализует IPv[46]-поверх-IPv[46] туннель в общем виде и включает "настроенный туннель", описанный в спецификации. Подробности см. в crossref:ipv6[gif,23.5.1.5] этого документа.
+
+* RFC1981: Path MTU Discovery for IPv6
+* RFC2080: RIPng for IPv6
+
+** usr.sbin/route6d это поддерживает.
+
+* RFC2292: Advanced Sockets API for IPv6
+
+** Для поддерживаемых функций библиотек/API ядра см. [.filename]#sys/netinet6/ADVAPI#.
+
+* RFC2362: Protocol Independent Multicast-Sparse Mode (PIM-SM)
+
+** RFC2362 определяет форматы пакетов для PIM-SM. [.filename]#draft-ietf-pim-ipv6-01.txt# написан на основе этого.
+
+* RFC2373: IPv6 Addressing Architecture
+
+** поддерживает обязательные адреса узлов и соответствует требованиям области видимости.
+
+* RFC2374: An IPv6 Aggregatable Global Unicast Address Format
+
+** поддерживает 64-битную длину Идентификатора Интерфейса.
+
+* RFC2375: IPv6 Multicast Address Assignments
+
+** Пользовательские приложения используют общеизвестные адреса, назначенные в RFC.
+
+* RFC2428: FTP Extensions for IPv6 and NATs
+
+** RFC2428 предпочтительнее RFC1639. FTP-клиенты сначала попробуют RFC2428, затем RFC1639 в случае неудачи.
+
+* RFC2460: IPv6 specification
+* RFC2461: Neighbor discovery for IPv6
+
+** См. crossref:ipv6[neighbor-discovery,23.5.1.2] в этом документе для получения подробностей.
+
+* RFC2462: IPv6 Stateless Address Autoconfiguration
+
+** См. crossref:ipv6[ipv6-pnp,23.5.1.4] в этом документе для получения подробностей.
+
+* RFC2463: ICMPv6 for IPv6 specification
+
+** См. crossref:ipv6[icmpv6,23.5.1.9] в этом документе для получения подробностей.
+
+* RFC2464: Transmission of IPv6 Packets over Ethernet Networks
+* RFC2465: MIB for IPv6: Textual Conventions and General Group
+
+** Необходимая статистика собирается ядром. Фактическая поддержка MIB для IPv6 предоставляется в виде набора патчей для ucd-snmp.
+
+* RFC2466: MIB for IPv6: ICMPv6 group
+
+** Необходимая статистика собирается ядром. Фактическая поддержка MIB IPv6 предоставляется в виде патча для ucd-snmp.
+
+* RFC2467: Transmission of IPv6 Packets over FDDI Networks
+* RFC2497: Transmission of IPv6 packet over ARCnet Networks
+* RFC2553: Basic Socket Interface Extensions for IPv6
+
+** Отображаемый адрес IPv4 (3.7) и особое поведение сокета с привязкой по шаблону IPv6 (3.8) поддерживаются. Подробности см. в разделе crossref:ipv6[ipv6-wildcard-socket,23.5.1.12] этого документа.
+
+* RFC2675: IPv6 Jumbograms
+
+** См. crossref:ipv6[ipv6-jumbo,23.5.1.7] в этом документе для получения подробностей.
+
+* RFC2710: Multicast Listener Discovery for IPv6
+* RFC2711: IPv6 router alert option
+* [.filename]#draft-ietf-ipngwg-router-renum-08#: Перенумерация маршрутизаторов для IPv6
+* [.filename]#draft-ietf-ipngwg-icmp-namelookups-02#: Поиск имен через ICMP в IPv6
+* [.filename]#draft-ietf-ipngwg-icmp-name-lookups-03#: Поиск имен IPv6 через ICMP
+* [.filename]#draft-ietf-pim-ipv6-01.txt#: PIM for IPv6
+
+** man:pim6dd[8] реализует плотный режим. man:pim6sd[8] реализует разреженный режим.
+
+* [.filename]#draft-itojun-ipv6-tcp-to-anycast-00#: Разрыв TCP-соединения с anycast-адресом IPv6
+* [.filename]#draft-yamamoto-wideipv6-comm-model-00#
+
+** См. crossref:ipv6[ipv6-sas,23.5.1.6] в этом документе для более подробной информации.
+
+* [.filename]#draft-ietf-ipngwg-scopedaddr-format-00.txt#: Расширение формата для адресов с областью действия IPv6
+
+[[neighbor-discovery]]
+==== Функция "Обнаружение соседей"
+
+Обнаружение соседей достаточно стабильно. В настоящее время поддерживаются следующие функции: определение адреса (Address Resolution), обнаружение дублирования адресов (DAD — Duplicated Address Detection) и обнаружение недоступности соседей (Neighbor Unreachability Detection). В ближайшем будущем мы добавим поддержку прокси-объявлений соседей (Proxy Neighbor Advertisement) в ядро и команду передачи непрошенных объявлений соседей (Unsolicited Neighbor Advertisement) в качестве инструмента администратора.
+
+Если DAD завершается неудачно, адрес будет помечен как "дублированный" (duplicated), и сообщение будет записано в syslog (а также обычно выведено на консоль). Метку "дублированный" можно проверить с помощью man:ifconfig[8]. Обязанность администратора — проверять и устранять сбои DAD. В ближайшем будущем поведение должно быть улучшено.
+
+Некоторые сетевые драйверы закольцовывают multicast-пакеты обратно на себя, даже если им указано так не делать (особенно в promiscuous mode). В таких случаях DAD может завершиться неудачей, так как механизм DAD видит входящий NS-пакет (на самом деле от самого узла) и считает его признаком дубликата. В качестве обходного решения можно рассмотреть условие #if с пометкой "heuristics" в sys/netinet6/nd6_nbr.c:nd6_dad_timer() (обратите внимание, что фрагмент кода в разделе "heuristics" не соответствует спецификации).
+
+Спецификация обнаружения соседей (RFC2461) не рассматривает обработку кэша соседей в следующих случаях:
+
+. когда отсутствовала запись в кэше соседей, узел получал нежелательный пакет RS/NS/NA/редирект без адреса канального уровня
+. обработка кэша соседей в среде без адреса канального уровня (нам нужна запись в кэше соседей для бита IsRouter)
+
+Для первого случая мы реализовали временное решение на основе обсуждений в рассылке IETF ipngwg. Подробности можно найти в комментариях исходного кода и ветке электронной почты, начавшейся с (IPng 7155) от 6 февраля 1999 года.
+
+Правило определения локальной IPv6 сети (RFC2461) значительно отличается от предположений в сетевом коде BSD. На данный момент не поддерживается правило определения локальной сети при пустом списке маршрутизаторов по умолчанию (RFC2461, раздел 5.2, последнее предложение во 2-м абзаце - обратите внимание, что в спецификации некорректно используются слова "host" и "node" в нескольких местах раздела).
+
+Во избежание возможных атак типа DoS и бесконечных циклов, сейчас принимается только 10 опций в ND-пакете. Таким образом, если к RA прикреплено 20 опций префиксов, будут распознаны только первые 10 префиксов. Если это вызывает проблемы, пожалуйста, задайте вопрос в рассылке FREEBSD-CURRENT и/или измените nd6_maxndopt в [.filename]#sys/netinet6/nd6.c#. При высоком спросе мы можем предоставить sysctl-параметр для этой переменной.
+
+[[ipv6-scope-index]]
+==== Индекс зоны
+
+В IPv6 используются адреса с областями видимости — зонами. Поэтому очень важно указывать индекс зоны (индекс интерфейса для линк-локального адреса или индекс сайта для сайт-локального адреса) вместе с IPv6 адресом. Без индекса зоны адрес IPv6 с ограниченной областью действия является неоднозначным для ядра, и ядро не сможет определить исходящий интерфейс для пакета.
+
+Обычные пользовательские приложения должны использовать расширенный API (RFC2292) для указания индекса зоны или индекса интерфейса. Для аналогичных целей член sin6_scope_id в структуре sockaddr_in6 определён в RFC2553. Однако семантика sin6_scope_id довольно расплывчата. Если важна переносимость вашего приложения, мы рекомендуем использовать расширенный API вместо sin6_scope_id.
+
+В ядре индекс интерфейса для адреса с областью действия link-local встраивается во второе 16-битное слово (3-й и 4-й байт) в IPv6-адресе. Например, вы можете увидеть что-то вроде:
+
+[source, bash]
+....
+ fe80:1::200:f8ff:fe01:6317
+....
+
+в таблице маршрутизации и структуре адреса интерфейса (struct in6_ifaddr). Указанный выше адрес является линк-локальным уникастным адресом, который принадлежит сетевому интерфейсу с идентификатором интерфейса 1. Встроенный индекс позволяет эффективно идентифицировать локальные адреса IPv6 на нескольких интерфейсах с минимальными изменениями кода.
+
+Демоны маршрутизации и программы настройки, такие как man:route6d[8] и man:ifconfig[8], должны управлять "встроенным" индексом зоны. Эти программы используют сокеты маршрутизации и ioctl (например, SIOCGIFADDR_IN6), и API ядра будет возвращать IPv6-адреса с заполненным вторым 16-битным словом. API предназначены для управления внутренними структурами ядра. Программы, использующие эти API, в любом случае должны быть готовы к различиям в ядрах.
+
+При указании адреса с ограниченной областью действия в командной строке НИКОГДА не используйте встроенную форму (например, ff02:1::1 или fe80:2::fedc). Это не должно работать. Всегда используйте стандартную форму, такую как ff02::1 или fe80::fedc, с параметром командной строки для указания интерфейса (например, `ping -6 -I ne0 ff02::1`). В общем, если команда не имеет параметра командной строки для указания исходящего интерфейса, эта команда не готова принимать адрес с областью действия. Это кажется противоречащим принципу IPv6 поддерживать сценарий "кабинета стоматолога". Мы считаем, что спецификации нуждаются в некоторых улучшениях для этого.
+
+Некоторые пользовательские утилиты поддерживают расширенный числовой синтаксис IPv6, как описано в [.filename]#draft-ietf-ipngwg-scopedaddr-format-00.txt#. Можно указать исходящее соединение, используя имя исходящего интерфейса, например "fe80::1%ne0". Таким образом можно легко указать линк-локальный адрес с ограниченной областью действия.
+
+Для использования этого расширения в вашей программе потребуется использовать man:getaddrinfo[3] и man:getnameinfo[3] с NI_WITHSCOPEID. В текущей реализации предполагается однозначное соответствие между каналом и интерфейсом, что является более строгим условием, чем указано в спецификациях.
+
+[[ipv6-pnp]]
+==== Plug and Play (подключи и работай)
+
+Большая часть автонастройки адресов IPv6 без сохранения состояния реализована в ядре. Функции обнаружения соседей (Neighbor Discovery) реализованы в ядре целиком. Ввод рекламы маршрутизатора (RA) для хостов реализован в ядре. Вывод запроса маршрутизатора (RS) для конечных хостов, ввод RS для маршрутизаторов и вывод RA для маршрутизаторов реализованы в пользовательском пространстве.
+
+===== Назначение линк-локальных и специальных адресов
+
+Линк-локальный адрес IPv6 генерируется из IEEE802 адреса (Ethernet MAC адреса). Каждому интерфейсу автоматически присваивается IPv6 линк-локальный адрес, когда интерфейс поднимается (IFF_UP). Также в таблицу маршрутизации добавляется прямой маршрут для линк-локального адреса.
+
+Вот вывод команды netstat:
+
+[source, bash]
+....
+Internet6:
+Destination Gateway Flags Netif Expire
+fe80:1::%ed0/64 link#1 UC ed0
+fe80:2::%ep0/64 link#2 UC ep0
+....
+
+Интерфейсы, не имеющие адреса IEEE802 (псевдоинтерфейсы, такие как туннельные интерфейсы или интерфейсы ppp), будут заимствовать адрес IEEE802 у других интерфейсов, например, Ethernet-интерфейсов, когда это возможно. Если нет подключенного оборудования IEEE802, в качестве последнего средства будет использовано псевдослучайное значение MD5(hostname) для формирования линк-локального адреса. Если это не подходит для вашего использования, вам потребуется настроить линк-локальный адрес вручную.
+
+Если интерфейс не поддерживает IPv6 (например, отсутствует поддержка multicast), на этот интерфейс не будет назначен линк-локальный адрес. Подробности см. в разделе 2.
+
+Каждый интерфейс присоединяется к запрашиваемому широковещательному адресу и линк-локальным широковещательным адресам всех узлов (например, fe80::1:ff01:6317 и ff02::1 соответственно на соединении, к которому подключен интерфейс). В дополнение к линк-локальному адресу, адрес обратной петли (::1 — loopback) будет назначен интерфейсу обратной петли. Также, ::1/128 и ff01::/32 автоматически добавляются в таблицу маршрутизации, а loopback-интерфейс (интерфейс обратной петли) присоединяется к групповому адресу в пределах узла ff01::1.
+
+===== Автоматическая настройка адресов без состояния на узлах
+
+В спецификации IPv6 узлы разделены на две категории: _маршрутизаторы_ и _хосты_. Маршрутизаторы пересылают пакеты, адресованные другим, хосты не пересылают пакеты. Параметр net.inet6.ip6.forwarding определяет, является ли данный узел маршрутизатором или хостом (маршрутизатор, если значение равно 1, хост, если 0).
+
+Когда хост получает Объявление Маршрутизатора (Router Advertisement) от маршрутизатора, он может автоматически настроить себя с помощью автонастройки адреса без сохранения состояния. Это поведение можно контролировать с помощью параметра net.inet6.ip6.accept_rtadv (хост автонастраивается, если значение равно 1). При автонастройке добавляется префикс сетевого адреса для принимающего интерфейса (обычно префикс глобального адреса). Также настраивается маршрут по умолчанию. Маршрутизаторы периодически генерируют пакеты Router Advertisement. Чтобы запросить соседний маршрутизатор сгенерировать RA-пакет, хост может отправить Router Solicitation. Для генерации RS-пакета в любое время используйте команду _rtsol_. Также доступен демон man:rtsold[8]. man:rtsold[8] генерирует Router Solicitation по мере необходимости и отлично подходит для мобильного использования (ноутбуки/лэптопы). Если необходимо игнорировать Router Advertisements, используйте sysctl для установки net.inet6.ip6.accept_rtadv в 0.
+
+Для генерации Router Advertisement от маршрутизатора используйте демон man:rtadvd[8].
+
+Обратите внимание, что спецификация IPv6 предполагает следующие пункты, а случаи несоответствия остаются неуточнёнными:
+
+* Только хосты будут принимать объявления от маршрутизаторов
+* Узлы имеют один сетевой интерфейс (за исключением loopback)
+
+Поэтому не рекомендуется включать net.inet6.ip6.accept_rtadv на маршрутизаторах или многопортовых хостах. Неправильно настроенный узел может вести себя странно (нестандартная конфигурация разрешена для тех, кто хочет провести эксперименты).
+
+Резюмируя настройку sysctl:
+
+[source, bash]
+....
+ accept_rtadv forwarding role of the node
+ --- --- ---
+ 0 0 host (to be manually configured)
+ 0 1 router
+ 1 0 autoconfigured host
+ (spec assumes that host has single
+ interface only, autoconfigured host
+ with multiple interface is
+ out-of-scope)
+ 1 1 invalid, or experimental
+ (out-of-scope of spec)
+....
+
+В RFC2462 есть правило проверки для входящей информации о префиксе в RA, в разделе 5.5.3 (e). Это защищает хосты от злонамеренных (или неправильно настроенных) маршрутизаторов, которые анонсируют очень короткое время жизни префикса. Было обновление от Джима Баунда в рассылке ipngwg (ищите "(ipng 6712)" в архиве), и это обновление Джима реализовано.
+
+См. crossref:ipv6[neighbor-discovery,23.5.1.2] в документе для информации о взаимосвязи между DAD и автонастройкой.
+
+[[gif]]
+==== Универсальный Туннельный Интерфейс
+
+GIF (Generic InterFace) — это псевдоинтерфейс для настроенного туннеля. Подробности описаны в man:gif[4]. В настоящее время
+
+* v6 в v6
+* v6 в v4
+* v4 в v6
+* v4 в v4
+
+доступны. Используйте man:gifconfig[8] для назначения физических (внешних) исходных и конечных адресов интерфейсам gif. Конфигурация, использующая одно семейство адресов для внутреннего и внешнего IP-заголовка (v4 в v4 или v6 в v6), является опасной. Очень легко настроить интерфейсы и таблицы маршрутизации для выполнения бесконечного уровня туннелирования. _Пожалуйста, будьте осторожны_.
+
+gif можно настроить так, чтобы он был дружественным к ECN. Подробнее о дружелюбности к ECN для туннелей см. crossref:ipv6[ipsec-ecn,23.5.4.5], а о настройке — в man:gif[4].
+
+Если вы хотите настроить туннель IPv4-в-IPv6 с интерфейсом gif, внимательно прочитайте man:gif[4]. Вам потребуется удалить линк-локальный адрес IPv6, автоматически назначенный интерфейсу gif.
+
+[[ipv6-sas]]
+==== Выбор исходящего адреса
+
+Текущее правило выбора источника ориентировано на зону (есть несколько исключений — см. ниже). Для заданного адреса назначения исходящий IPv6-адрес выбирается по следующему правилу:
+
+. Если исходящий адрес явно указан пользователем (например, через расширенный API), используется указанный адрес.
+. Если на исходящем интерфейсе назначен адрес (который обычно определяется путем просмотра таблицы маршрутизации) с той же зоной действия, что и адрес назначения, используется этот адрес.
++
+Это наиболее типичный случай.
+. Если нет адреса, удовлетворяющего указанному выше условию, выберите глобальный адрес, назначенный одному из интерфейсов на отправляющем узле.
+. Если нет адреса, удовлетворяющего указанному выше условию, и адрес назначения имеет сайт-локальную зону, выберите сайт-локальный адрес, назначенный одному из интерфейсов на отправляющем узле.
+. Если нет адреса, удовлетворяющего указанному условию, выберите адрес, связанный с записью таблицы маршрутизации для назначения. Это крайняя мера, которая может нарушить границы зоны действия.
+
+Например, ::1 выбирается для ff01::1, fe80:1::200:f8ff:fe01:6317 для fe80:1::2a0:24ff:feab:839b (обратите внимание, что встроенный индекс интерфейса — описанный в crossref:ipv6[ipv6-scope-index,23.5.1.3] — помогает нам выбрать правильный исходный адрес. Эти встроенные индексы не будут передаваться по сети). Если исходящий интерфейс имеет несколько адресов для данной зоны, исходный адрес выбирается на основе наибольшего соответствия (правило 3). Предположим, что 2001:0DB8:808:1:200:f8ff:fe01:6317 и 2001:0DB8:9:124:200:f8ff:fe01:6317 назначены исходящему интерфейсу. 2001:0DB8:808:1:200:f8ff:fe01:6317 выбирается в качестве исходящего адреса для адреса назначения 2001:0DB8:800::1.
+
+Обратите внимание, что приведенное выше правило не документировано в спецификации IPv6. Оно считается элементом, оставленным "на усмотрение реализации". Существуют случаи, когда мы не используем это правило. Один из примеров — установленное TCP-соединение, где мы используем адрес, сохраненный в tcb, в качестве источника. Другой пример — исходящий адрес для Объявления Соседа (Neighbor Advertisement). Согласно спецификации (RFC2461 7.2.2) источник NA должен быть целевым адресом соответствующего NS. В этом случае мы следуем спецификации, а не приведенному выше правилу наибольшего совпадения.
+
+Для новых соединений (когда правило 1 не применяется), устаревшие адреса (адреса с предпочтительным временем жизни = 0) не будут выбираться в качестве исходящего адреса, если доступны другие варианты. Если других вариантов нет, устаревший адрес будет использован в качестве последнего средства. Если есть несколько устаревших адресов, для выбора между ними будет применено указанное выше правило области видимости. Если вы хотите запретить использование устаревших адресов по какой-либо причине, установите параметр net.inet6.ip6.use_deprecated в значение 0. Проблема, связанная с устаревшими адресами, описана в RFC2462 5.5.4 (ПРИМЕЧАНИЕ: в IETF ipngwg ведутся дебаты о том, как использовать "устаревшие" адреса).
+
+[[ipv6-jumbo]]
+==== Джамбо-пакет (Jumbo Payload)
+
+Опция джамбо-пакет типа "от прыжка к прыжку" реализована и может использоваться для отправки IPv6-пакетов с полезной нагрузкой длиной более 65 535 октетов. Однако в настоящее время не поддерживаются физические интерфейсы с MTU более 65 535, поэтому такие нагрузки могут быть только на интерфейсе loopback (т.е. lo0).
+
+Если вы хотите попробовать джамбо-пакеты, сначала необходимо переконфигурировать ядро, чтобы MTU интерфейса loopback превышал 65 535 байт; добавьте следующее в конфигурационный файл ядра:
+
+`options "LARGE_LOMTU" #To test jumbo payload`
+
+и пересоберите новое ядро.
+
+Затем вы можете проверить работу с большими пакетами с помощью команды man:ping[8] с опциями -6, -b и -s. Опция -b необходима для увеличения размера буфера сокета, а опция -s задает длину пакета, которая должна быть больше 65 535. Например, введите следующее:
+
+[source, bash]
+....
+% ping -6 -b 70000 -s 68000 ::1
+....
+
+Спецификация IPv6 требует, чтобы опция Джамбо-пакет не использовалась в пакете, содержащем заголовок фрагмента. Если это условие нарушено, должно быть отправлено ICMPv6 сообщение Parameter Problem отправителю. Спецификация соблюдается, но обычно вы не можете увидеть ICMPv6 ошибку, вызванную этим требованием.
+
+При получении IPv6-пакета проверяется длина кадра и сравнивается с длиной, указанной в поле длины полезной нагрузки заголовка IPv6 или в значении опции Джамбо-пакета, если она присутствует. Если первое значение меньше второго, пакет отбрасывается, и статистика увеличивается. Статистику можно увидеть в выводе команды man:netstat[8] с опцией `-s -p ip6`:
+
+[source, bash]
+....
+% netstat -s -p ip6
+ ip6:
+ (snip)
+ 1 with data size < data length
+....
+
+Итак, ядро не отправляет ICMPv6 ошибку, если ошибочный пакет не является фактически Джамбо-пакетом, то есть его размер пакета превышает 65 535 байт. Как описано выше, в настоящее время не поддерживаются физические интерфейсы с таким огромным MTU, поэтому ICMPv6 ошибка возвращается редко.
+
+В настоящее время поддержка TCP/UDP через jumbogram не реализована. Это связано с отсутствием среды (кроме loopback) для тестирования данной функциональности. Свяжитесь с нами, если вам это необходимо.
+
+IPsec не работает с jumbogram. Это связано с особенностями спецификации, касающимися поддержки AH для джамбограмм (размер заголовка AH влияет на длину полезной нагрузки, что делает крайне сложной аутентификацию входящего пакета с опцией Джамбо-пакет, а также AH).
+
+Существуют фундаментальные проблемы в поддержке *BSD для jumbogram. Мы хотели бы решить их, но нам нужно больше времени для завершения работы. Вот некоторые из них:
+
+* Поле `mbuf pkthdr.len` имеет тип `int` в 4.4BSD, поэтому оно не сможет содержать джамбограмму с длиной > 2G на 32-битных архитектурах CPU. Если мы хотим правильно поддерживать джамбограммы, это поле необходимо расширить, чтобы оно могло содержать 4G + заголовок IPv6 + заголовок канального уровня. Следовательно, его необходимо расширить как минимум до `int64_t` (`u_int32_t` НЕ достаточно).
+
+* Мы ошибочно используем "int" для хранения длины пакета во многих местах. Нам необходимо преобразовать их в более крупный целочисленный тип. Это требует большой осторожности, так как мы можем столкнуться с переполнением во время вычисления длины пакета.
+* Мы ошибочно проверяем поле ip6_plen заголовка IPv6 для определения длины полезной нагрузки пакета в различных местах. Вместо этого следует проверять mbuf pkthdr.len. Функция ip6_input() выполняет проверку корректности опции Джамбо-пакет при вводе, и после этого можно безопасно использовать mbuf pkthdr.len.
+* Код TCP требует тщательного обновления в ряде мест, разумеется.
+
+==== Предотвращение петель при обработке заголовков
+
+Спецификация IPv6 допускает размещение произвольного количества расширений в заголовках пакетов. Если реализовать код обработки пакетов IPv6 так, как реализован код IPv4 в BSD, может произойти переполнение стека ядра из-за длинной цепочки вызовов функций. Код в sys/netinet6 тщательно спроектирован, чтобы избежать переполнения стека ядра, поэтому он определяет собственную структуру переключения протоколов — "struct ip6protosw" (см. [.filename]#netinet6/ip6protosw.h#). Для IPv4 части (sys/netinet) подобных обновлений не было сделано для сохранения совместимости, но в прототип pr_input() внесено небольшое изменение. Поэтому также определена "struct ipprotosw". В результате, если получен пакет IPsec-over-IPv4 с большим количеством заголовков IPsec, стек ядра может переполниться. С IPsec-over-IPv6 такой проблемы нет. (Разумеется, чтобы все эти заголовки IPsec были обработаны, каждый такой заголовок должен пройти все проверки IPsec. Поэтому анонимный злоумышленник не сможет осуществить подобную атаку.)
+
+[[icmpv6]]
+==== ICMPv6
+
+После публикации RFC2463 IETF ipngwg решил запретить ICMPv6 пакеты ошибок для ICMPv6 перенаправлений, чтобы предотвратить ICMPv6 шторм в сетевой среде. Это уже реализовано в ядре.
+
+==== Приложения (Applications)
+
+Для программирования в пользовательском пространстве мы поддерживаем API сокетов IPv6, как указано в RFC2553, RFC2292 и готовящихся интернет-черновиках.
+
+TCP/UDP поверх IPv6 доступны и достаточно стабильны. Вы можете использовать man:telnet[1], man:ftp[1], man:rlogin[1], man:rsh[1], man:ssh[1] и т.д. Эти приложения не зависят от протокола. То есть они автоматически выбирают IPv4 или IPv6 в соответствии с DNS.
+
+==== Внутреннее устройство ядра
+
+В то время как ip_forward() вызывает ip_output(), ip6_forward() напрямую вызывает if_output(), поскольку маршрутизаторы не должны разделять пакеты IPv6 на фрагменты.
+
+ICMPv6 должен содержать исходный пакет по возможности вплоть до 1280 байт. Например, сообщение "Ошибка недоступности порта UDP6/IP6" должно содержать все расширенные заголовки и *неизменённые* заголовки UDP6 и IP6. Таким образом, все функции IP6, кроме TCP, никогда не преобразуют порядок байтов сети в порядок байтов хоста, чтобы сохранить исходный пакет.
+
+Функции tcp_input(), udp6_input() и icmp6_input() не могут предполагать, что заголовок IP6 предшествует транспортным заголовкам из-за наличия расширенных заголовков. Поэтому была реализована in6_cksum() для обработки пакетов, у которых заголовок IP6 и транспортный заголовок не являются непрерывными. Но ни для TCP/IP6, ни для UDP6/IP6 в заголовке нет структуры для расчёта контрольной суммы.
+
+Для удобной обработки заголовка IP6, дополнительных заголовков и транспортных заголовков, от сетевых драйверов теперь требуется хранить пакеты в одном внутреннем mbuf или одном или нескольких внешних mbuf. Типичный старый драйвер подготавливает два внутренних mbuf для данных размером 96–204 байт, однако теперь такие данные пакета хранятся в одном внешнем mbuf.
+
+`netstat -s -p ip6` показывает, соответствует ли ваш драйвер этому требованию. В следующем примере "cce0" нарушает это требование. (Для получения дополнительной информации обратитесь к разделу 2.)
+
+[source, bash]
+....
+Mbuf statistics:
+ 317 one mbuf
+ two or more mbuf::
+ lo0 = 8
+ cce0 = 10
+ 3282 one ext mbuf
+ 0 two or more ext mbuf
+....
+
+Каждая входная функция вызывает IP6_EXTHDR_CHECK в начале, чтобы проверить, является ли область между IP6 и его заголовком непрерывной. IP6_EXTHDR_CHECK вызывает m_pullup() только если mbuf имеет флаг M_LOOP, то есть пакет пришел с интерфейса loopback. m_pullup() никогда не вызывается для пакетов, приходящих с физических сетевых интерфейсов.
+
+Как функции повторной сборки IP, так и IP6 никогда не вызывают m_pullup().
+
+[[ipv6-wildcard-socket]]
+==== IPv4-отображённые адреса и IPv6-сокет с подстановочным адресом
+
+RFC2553 описывает IPv4 отображённые адреса (3.7) и особое поведение IPv6 сокета с привязкой к любому адресу (3.8). Спецификация позволяет вам:
+
+* Принимать IPv4-подключения через сокет с привязкой к подстановочному адресу AF_INET6.
+* Передача IPv4-пакета через сокет AF_INET6 с использованием специальной формы адреса, например ::ffff:10.1.1.1.
+
+но сама спецификация очень сложна и не определяет, как должен вести себя сокетный уровень. Здесь мы называем первую сторону «слушающей», а вторую — «инициирующей» для удобства ссылок.
+
+Вы можете выполнить привязку к подстановочному адресу для обоих семейств адресов на одном и том же порту.
+
+Следующая таблица показывает поведение FreeBSD 4.x.
+
+[source, bash]
+....
+listening side initiating side
+ (AF_INET6 wildcard (connection to ::ffff:10.1.1.1)
+ socket gets IPv4 conn.)
+ --- ---
+FreeBSD 4.x configurable supported
+ default: enabled
+....
+
+Следующие разделы предоставят вам более подробную информацию и объяснят, как можно настроить поведение.
+
+Комментарии о принимающей стороне:
+
+Похоже, что в RFC2553 слишком мало сказано о проблеме привязки к подстановочному адресу, особенно о вопросе пространства портов, режиме отказа и взаимосвязи между AF_INET/INET6 wildcard bind. Может быть несколько различных интерпретаций этого RFC, которые соответствуют ему, но ведут себя по-разному. Поэтому для создания переносимых приложений не следует делать никаких предположений о поведении в ядре. Использование man:getaddrinfo[3] является наиболее безопасным способом. Вопросы пространства номеров портов и привязки к подстановочному адресу подробно обсуждались в рассылке ipv6imp в середине марта 1999 года, и похоже, что конкретного консенсуса нет (то есть, остается на усмотрение реализаторов). Возможно, вам стоит проверить архивы рассылки.
+
+Если серверное приложение хочет принимать IPv4 и IPv6 соединения, есть два варианта.
+
+Один из способов — использование сокетов AF_INET и AF_INET6 (вам понадобятся два сокета). Используйте man:getaddrinfo[3] с AI_PASSIVE в ai_flags, а также man:socket[2] и man:bind[2] для всех возвращённых адресов. Открыв несколько сокетов, вы можете принимать соединения сокетом соответствующей адресной семьи. IPv4-соединения будут приниматься сокетом AF_INET, а IPv6-соединения — сокетом AF_INET6.
+
+Еще один способ — использование одного сокета с универсальной привязкой AF_INET6. Используйте man:getaddrinfo[3] с AI_PASSIVE в ai_flags и AF_INET6 в ai_family, установив первый аргумент hostname в NULL. Затем используйте man:socket[2] и man:bind[2] для адреса, который был возвращен. (должен быть неспецифицированный адрес IPv6). Через этот один сокет можно принимать пакеты как IPv4, так и IPv6.
+
+Для поддержки только IPv6-трафика на AF_INET6-сокете с привязкой к любому адресу переносимым способом всегда проверяйте адрес узла при установке соединения с AF_INET6-сокетом в режиме прослушивания. Если адрес является IPv4-отображённым, возможно, стоит отклонить соединение. Это условие можно проверить с помощью макроса IN6_IS_ADDR_V4MAPPED().
+
+Для более простого решения этой задачи существует зависящий от системы параметр man:setsockopt[2] под названием IPV6_BINDV6ONLY, используемый следующим образом.
+
+[.programlisting]
+....
+ int on;
+
+ setsockopt(s, IPPROTO_IPV6, IPV6_BINDV6ONLY,
+ (char *)&on, sizeof (on)) < 0));
+....
+
+При успешном вызове этот сокет будет принимать только IPv6-пакеты.
+
+Комментарии о стороне инициатора:
+
+Совет разработчикам приложений: для создания переносимого IPv6-приложения (которое работает на различных IPv6-ядрах), мы считаем, что следующие моменты являются ключом к успеху:
+
+* НИКОГДА не используйте жёстко заданные AF_INET или AF_INET6.
+* Используйте man:getaddrinfo[3] и man:getnameinfo[3] во всей системе. Никогда не используйте gethostby*(), getaddrby*(), inet_*() или getipnodeby*(). (Для облегчения обновления существующих приложений для поддержки IPv6 иногда может быть полезен getipnodeby*(). Но по возможности старайтесь переписать код для использования man:getaddrinfo[3] и man:getnameinfo[3].)
+* Если вы хотите подключиться к назначению, используйте man:getaddrinfo[3] и попробуйте все возвращённые назначения, как это делает man:telnet[1].
+* Некоторые реализации стека IPv6 поставляются с некорректной man:getaddrinfo[3]. Включите минимально рабочую версию в ваше приложение и используйте её в крайнем случае.
+
+Если вы хотите использовать сокет AF_INET6 для исходящих подключений как IPv4, так и IPv6, вам потребуется использовать man:getipnodebyname[3]. Если вы хотите обновить существующее приложение для поддержки IPv6 с минимальными усилиями, можно выбрать этот подход. Однако учтите, что это временное решение, поскольку man:getipnodebyname[3] сам по себе не рекомендуется, так как он вообще не обрабатывает IPv6-адреса с зоной. Для разрешения IPv6-имён предпочтительным API является man:getaddrinfo[3]. Поэтому вам следует переписать ваше приложение для использования man:getaddrinfo[3], когда у вас будет время это сделать.
+
+При написании приложений, которые устанавливают исходящие соединения, история становится намного проще, если рассматривать AF_INET и AF_INET6 как совершенно отдельные семейства адресов. Проблемы с {set,get}sockopt упрощаются, проблемы с DNS также станут проще. Мы не рекомендуем полагаться на IPv4-отображённые адреса.
+
+===== унифицированный код tcp и inpcb
+
+FreeBSD 4.x использует общий код tcp для IPv4 и IPv6 (из sys/netinet/tcp*) и раздельный код udp4/6. В нем используется унифицированная структура inpcb.
+
+Платформа может быть настроена для поддержки IPv4-отображённых адресов. Конфигурация ядра кратко описана ниже:
+
+* По умолчанию сокет AF_INET6 может принимать IPv4-соединения при определённых условиях и инициировать соединение с IPv4-адресами, встроенными в IPv4-отображённые IPv6-адреса.
+* Вы можете отключить это во всей системе с помощью sysctl, как показано ниже.
++
+`sysctl net.inet6.ip6.mapped_addr=0`
+
+====== Сторона, принимающая соединения
+
+Каждый сокет может быть настроен для поддержки специальной привязки к подстановочному адресу AF_INET6 (включено по умолчанию). Это можно отключить для каждого отдельного сокета с помощью man:setsockopt[2], как показано ниже.
+
+[.programlisting]
+....
+ int on;
+
+ setsockopt(s, IPPROTO_IPV6, IPV6_BINDV6ONLY,
+ (char *)&on, sizeof (on)) < 0));
+....
+
+Сокет с универсальной привязкой AF_INET6 перехватывает IPv4-подключение тогда и только тогда, когда выполнены следующие условия:
+
+* нет AF_INET сокета, соответствующего IPv4-подключению
+* Сокет AF_INET6 настроен на прием IPv4-трафика, т.е., getsockopt(IPV6_BINDV6ONLY) возвращает 0.
+
+Нет проблем с порядком открытия/закрытия.
+
+====== Инициирующая сторона
+
+FreeBSD 4.x поддерживает исходящее соединение с IPv4-отображённым адресом (::ffff:10.1.1.1), если узел настроен на поддержку IPv4-отображённых адресов.
+
+==== sockaddr_storage
+
+Когда RFC2553 был близок к завершению, велись дискуссии о том, как называть элементы структуры `sockaddr_storage`. Одно предложение заключалось в добавлении "__" перед именами элементов (например, "__ss_len"), так как к ним не следует обращаться напрямую. Другое предложение было не добавлять префикс (например, "ss_len"), поскольку необходимо прямое обращение к этим элементам. Четкого консенсуса по этому вопросу достигнуто не было.
+
+В результате, RFC2553 определяет структуру sockaddr_storage следующим образом:
+
+[.programlisting]
+....
+ struct sockaddr_storage {
+ u_char __ss_len; /* address length */
+ u_char __ss_family; /* address family */
+ /* and bunch of padding */
+ };
+....
+
+Напротив, черновик XNET определяет следующее:
+
+[.programlisting]
+....
+ struct sockaddr_storage {
+ u_char ss_len; /* address length */
+ u_char ss_family; /* address family */
+ /* and bunch of padding */
+ };
+....
+
+В декабре 1999 года было согласовано, что RFC2553bis должен принять последнее (XNET) определение.
+
+Текущая реализация соответствует определению XNET, основанному на обсуждении RFC2553bis.
+
+Если вы рассмотрите несколько реализаций IPv6, то сможете увидеть оба определения. Для программиста в пользовательском пространстве наиболее переносимый способ работы с этим:
+
+. с помощью GNU autoconf сконфигурировать доступ к `ss_family` и/или `ss_len` на целевой платформе,
+. добавить -Dss_family=__ss_family для унификации всех использований (включая заголовочный файл) __ss_family, или
+. никогда не трогайте __ss_family. Приводите к sockaddr * и используйте sa_family, например:
++
+[.programlisting]
+....
+ struct sockaddr_storage ss;
+ family = ((struct sockaddr *)&ss)->sa_family
+....
+
+=== Драйверы сетевых устройств
+
+В настоящее время следующие два пункта должны поддерживаться стандартными драйверами:
+
+. Требование к кластеризации mbuf. В этом стабильном выпуске мы изменили MINCLSIZE на MHLEN+1 для всех операционных систем, чтобы все драйверы работали так, как мы ожидаем.
+. многоадресная рассылка (multicast). Если man:ifmcstat[8] не выводит ни одной многоадресной группы для интерфейса, этот интерфейс необходимо исправить.
+
+Если какие-либо драйверы не поддерживают требования, то их нельзя использовать для IPv6 и/или IPsec-связи. Если вы обнаружили проблему с вашей картой при использовании IPv6/IPsec, пожалуйста, сообщите об этом в {freebsd-bugs}.
+
+(NOTE: Раньше мы требовали, чтобы все драйверы PCMCIA содержали вызов in6_ifattach(). Теперь такого требования нет)
+
+=== Транслятор
+
+Мы классифицируем трансляторы IPv4/IPv6 на 4 типа:
+
+* _Транслятор А_ --- Он используется на раннем этапе перехода, чтобы позволить установить соединение с IPv6-хоста на IPv6-острове к IPv4-хосту в IPv4-океане.
+* _Транслятор Б_ --- Он используется на раннем этапе перехода, чтобы обеспечить возможность установления соединения с IPv6-узлом на IPv6-острове от IPv4-узла в IPv4-океане.
+* _Транслятор C_ --- Он используется на позднем этапе перехода, чтобы сделать возможным установление соединения с IPv6-узлом в IPv6-океане от IPv4-узла на IPv4-острове.
+* _Транслятор D_ --- Он используется на позднем этапе перехода, чтобы сделать возможным установление соединения с IPv6-хоста в IPv6-океане на IPv4-хост на IPv4-острове.
+
+[[ipsec-implementation]]
+=== IPsec
+
+IPsec состоит в основном из трех компонент.
+
+. Управление политиками
+. Управление ключами
+. Обработка AH и ESP
+
+==== Управление политиками
+
+Ядро реализует экспериментальный код управления политикой безопасности. Существует два способа управления политикой безопасности. Первый — настройка политики для каждого сокета с помощью man:setsockopt[2]. В этом случае конфигурация политики описана в man:ipsec_set_policy[3]. Второй способ — настройка политики на основе фильтра пакетов ядра с использованием интерфейса PF_KEY через man:setkey[8].
+
+Запись политики не переупорядочивается вместе со своими индексами, поэтому порядок добавления записей очень важен.
+
+==== Управление ключами
+
+Код управления ключами, реализованный в этом наборе (sys/netkey), представляет собой собственную реализацию PFKEY v2. Это соответствует RFC2367.
+
+В комплект включена "домашняя" реализация демона IKE — "racoon" (kame/kame/racoon). Обычно вам потребуется запустить racoon в качестве демона, затем настроить политику для требования ключей (например, `ping -P 'out ipsec esp/transport//use'`). Ядро будет связываться с демоном racoon по мере необходимости для обмена ключами.
+
+==== Обработка AH и ESP
+
+Модуль IPsec реализован в виде "хуков" к стандартной обработке IPv4/IPv6. При отправке пакета функция ip{,6}_output() проверяет, требуется ли обработка ESP/AH, путем поиска соответствующей базы данных политик безопасности (SPD — Security Policy Database). Если ESP/AH необходим, вызывается {esp,ah}{4,6}_output(), и mbuf соответствующим образом обновляется. При получении пакета функция {esp,ah}4_input() вызывается на основе номера протокола, т.е. (*inetsw[proto])(). {esp,ah}4_input() расшифровывает/проверяет подлинность пакета, а также удаляет цепочку заголовков и выравнивание для ESP/AH. Безопасно удалять заголовок ESP/AH при получении пакета, так как полученный пакет никогда не будет использоваться в "сыром" виде.
+
+Использование ESP/AH влияет на эффективный размер сегмента данных TCP4/6 из-за дополнительных цепочечных заголовков, вставляемых ESP/AH. Наш код учитывает этот случай.
+
+Основные криптографические функции можно найти в директории `sys/crypto`. Преобразования ESP/AH перечислены в `{esp,ah}_core.c` с обёрточными функциями. Если вы хотите добавить какой-либо алгоритм, добавьте обёрточную функцию в `{esp,ah}_core.c` и поместите код вашего криптографического алгоритма в `sys/crypto`.
+
+Режим туннеля частично поддерживается в этом выпуске со следующими ограничениями:
+
+* Туннель IPsec не объединён с универсальным туннельным интерфейсом GIF. Это требует особой осторожности, так как может возникнуть бесконечный цикл между `ip_output()` и `tunnelifp->if_output()`. Мнения расходятся относительно того, лучше ли их объединить или нет.
+* MTU и бит Don't Fragment (IPv4) требуют дополнительной проверки, но в основном работают нормально.
+* Модель аутентификации для туннеля AH должна быть пересмотрена. Нам потребуется улучшить механизм управления политиками в конечном итоге.
+
+==== Соответствие RFC и ID
+
+Код IPsec в ядре соответствует (или пытается соответствовать) следующим стандартам:
+
+Спецификация "старого IPsec", описанная в [.filename]#rfc182[5-9].txt#
+
+Спецификация "new IPsec" описана в [.filename]#rfc240[1-6].txt#, [.filename]#rfc241[01].txt#, [.filename]#rfc2451.txt# и [.filename]#draft-mcdonald-simple-ipsec-api-01.txt# (черновик устарел, но его можно взять по ссылке: link:ftp://ftp.kame.net/pub/internet-drafts/[ ftp://ftp.kame.net/pub/internet-drafts/]). (ПРИМЕЧАНИЕ: Спецификации IKE, [.filename]#rfc241[7-9].txt#, реализованы в пользовательском пространстве в виде демона IKE "racoon")
+
+В настоящее время поддерживаются следующие алгоритмы:
+
+* old IPsec AH
+
+** нулевая криптографическая контрольная сумма (нет документа, только для отладки)
+** MD5 с ключом и с 128-битной криптографической контрольной суммой ([.filename]#rfc1828.txt#)
+** SHA1 с ключом и с 128-битной криптографической контрольной суммой (без документа)
+** HMAC MD5 с 128-битной криптографической контрольной суммой ([.filename]#rfc2085.txt#)
+** HMAC SHA1 с 128-битной криптографической контрольной суммой (без документа)
+
+* old IPsec ESP
+
+** нулевое шифрование (нет документа, аналогично [.filename]#rfc2410.txt#)
+** Режим DES-CBC ([.filename]#rfc1829.txt#)
+
+* new IPsec AH
+
+** нулевая криптографическая контрольная сумма (нет документа, только для отладки)
+** MD5 с ключом и с 96-битной криптографической контрольной суммой (нет документа)
+** SHA1 с ключом и с 96-битной криптографической контрольной суммой (без документа)
+** HMAC MD5 с 96-битной криптографической контрольной суммой ([.filename]#rfc2403.txt#)
+** HMAC SHA1 с 96-битной криптографической контрольной суммой ([.filename]#rfc2404.txt#)
+
+* new IPsec ESP
+
+** нулевое шифрование ([.filename]#rfc2410.txt#)
+** DES-CBC с производным IV ([.filename]#draft-ietf-ipsec-ciph-des-derived-01.txt#, черновик истек)
+** DES-CBC с явным вектором инициализации ([.filename]#rfc2405.txt#)
+** 3DES-CBC с явным вектором инициализации ([.filename]#rfc2451.txt#)
+** BLOWFISH CBC ([.filename]#rfc2451.txt#)
+** CAST128 CBC ([.filename]#rfc2451.txt#)
+** RC5 CBC ([.filename]#rfc2451.txt#)
+** каждый из вышеперечисленных может быть объединён с:
+
+*** Аутентификация ESP с HMAC-MD5 (96 бит)
+*** Аутентификация ESP с HMAC-SHA1(96 бит)
+
+Следующие алгоритмы НЕ поддерживаются:
+
+* old IPsec AH
+
+** HMAC MD5 с 128-битной криптографической контрольной суммой + 64-битная защита от повторного воспроизведения ([.filename]#rfc2085.txt#)
+** SHA1 с ключом и с 160-битной криптографической контрольной суммой +
+32-битное дополнение ([.filename]#rfc1852.txt#)
+
+IPsec (в ядре) и IKE (в пользовательском пространстве как "racoon") были протестированы на нескольких мероприятиях по тестированию взаимодействия и известно, что они хорошо работают со многими другими реализациями. Кроме того, текущая реализация IPsec поддерживает довольно широкий спектр криптографических алгоритмов IPsec, описанных в RFC (мы поддерживаем только алгоритмы без проблем с интеллектуальной собственностью).
+
+[[ipsec-ecn]]
+==== Учет ECN в IPsec-туннелях
+
+Поддерживается ECN-совместимый IPsec-туннель, как описано в [.filename]#draft-ipsec-ecn-00.txt#.
+
+Обычный IPsec-туннель описан в RFC2401. При инкапсуляции поле TOS IPv4 (или поле класса трафика IPv6) копируется из внутреннего IP-заголовка во внешний IP-заголовок. При декапсуляции внешний IP-заголовок просто отбрасывается. Правило декапсуляции несовместимо с ECN, так как бит ECN в поле TOS/класса трафика внешнего IP-заголовка будет потерян.
+
+Чтобы сделать IPsec-туннель дружественным к ECN, следует изменить процедуры инкапсуляции и декапсуляции. Это описано в http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt[ http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt], глава 3.
+
+Реализация туннеля IPsec может обеспечить три варианта поведения, в зависимости от значения параметра `net.inet.ipsec.ecn` (или `net.inet6.ipsec6.ecn`):
+
+* RFC2401: отсутствие учета ECN (значение sysctl -1)
+* ECN запрещён (значение sysctl 0)
+* ECN разрешён (значение sysctl 1)
+
+Обратите внимание, что поведение настраивается для каждого узла, а не для каждой SA (в draft-ipsec-ecn-00 предлагается настройка для каждой SA, но это кажется излишним).
+
+Поведение можно обобщить следующим образом (подробности см. в исходном коде):
+
+[source, bash]
+....
+encapsulate decapsulate
+ --- ---
+RFC2401 copy all TOS bits drop TOS bits on outer
+ from inner to outer. (use inner TOS bits as is)
+
+ECN forbidden copy TOS bits except for ECN drop TOS bits on outer
+ (masked with 0xfc) from inner (use inner TOS bits as is)
+ to outer. set ECN bits to 0.
+
+ECN allowed copy TOS bits except for ECN use inner TOS bits with some
+ CE (masked with 0xfe) from change. if outer ECN CE bit
+ inner to outer. is 1, enable ECN CE bit on
+ set ECN CE bit to 0. the inner.
+....
+
+Общая стратегия настройки выглядит следующим образом:
+
+* если оба конечных пункта туннеля IPsec поддерживают поведение, дружественное к ECN, лучше настроить оба конца на "разрешено ECN" (значение sysctl 1).
+* если другая сторона очень строга к биту TOS, используйте "RFC2401" (значение sysctl -1).
+* в остальных случаях используйте "ECN запрещено" (значение sysctl 0).
+
+Поведение по умолчанию — "ECN запрещён" (значение sysctl 0).
+
+Для получения дополнительной информации обратитесь к:
+
+http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt[ http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt], RFC2481 (Явное Уведомление о Перегрузке), src/sys/netinet6/{ah,esp}_input.c
+
+(Благодарности Kenjiro Cho mailto:kjc@csl.sony.co.jp[kjc@csl.sony.co.jp] за детальный анализ)
+
+==== Совместимость
+
+Вот некоторые из платформ, на которых код KAME тестировал взаимодействие IPsec/IKE в прошлом. Обратите внимание, что обе стороны могли изменить свои реализации, поэтому используйте следующий список только в справочных целях.
+
+Altiga, Ashley-laurent (vpcom.com), Data Fellows (F-Secure), Ericsson ACC, FreeS/WAN, HITACHI, IBM AIX(R), IIJ, Intel, Microsoft(R) Windows NT(R), NIST (linux IPsec + plutoplus), Netscreen, OpenBSD, RedCreek, Routerware, SSH, Secure Computing, Soliton, Toshiba, VPNet, Yamaha RT100i
diff --git a/documentation/content/ru/books/developers-handbook/ipv6/_index.po b/documentation/content/ru/books/developers-handbook/ipv6/_index.po
new file mode 100644
index 0000000000..d842459698
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/ipv6/_index.po
@@ -0,0 +1,2818 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-09-23 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookipv6_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:1
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:16
+#, no-wrap
+msgid "IPv6 Internals"
+msgstr "Внутреннее устройство IPv6"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:1
+#, no-wrap
+msgid "Chapter 8. IPv6 Internals"
+msgstr "Глава 8. Внутреннее устройство IPv6"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:54
+#, no-wrap
+msgid "IPv6/IPsec Implementation"
+msgstr "Реализация IPv6/IPsec"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:58
+msgid ""
+"This section should explain IPv6 and IPsec related implementation "
+"internals. These functionalities are derived from http://www.kame.net/[KAME "
+"project]"
+msgstr ""
+"В этом разделе мы объясним внутреннюю реализацию, связанную с IPv6 и IPsec. "
+"Данная функциональность заимствована из http://www.kame.net/[проекта KAME]"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:60
+#, no-wrap
+msgid "IPv6"
+msgstr "IPv6"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:62
+#, no-wrap
+msgid "Conformance"
+msgstr "Соответствие"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:66
+msgid ""
+"The IPv6 related functions conforms, or tries to conform to the latest set "
+"of IPv6 specifications. For future reference we list some of the relevant "
+"documents below (_NOTE_: this is not a complete list - this is too hard to "
+"maintain...)."
+msgstr ""
+"Функции, связанные с IPv6, соответствуют или пытаются соответствовать "
+"последнему набору спецификаций IPv6. Для дальнейшего использования мы "
+"приводим некоторые из соответствующих документов ниже (_ПРИМЕЧАНИЕ_: это не "
+"полный список — его слишком сложно поддерживать...)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:68
+msgid ""
+"For details please refer to specific chapter in the document, RFCs, manual "
+"pages, or comments in the source code."
+msgstr ""
+"Для подробностей обратитесь к соответствующей главе документа, RFC, "
+"страницам Справосника или комментариям в исходном коде."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:72
+msgid ""
+"Conformance tests have been performed on the KAME STABLE kit at TAHI "
+"project. Results can be viewed at http://www.tahi.org/report/KAME/[http://"
+"www.tahi.org/report/KAME/]. We also attended University of New Hampshire "
+"IOL tests (http://www.iol.unh.edu/[http://www.iol.unh.edu/]) in the past, "
+"with our past snapshots."
+msgstr ""
+"Тесты на соответствие стандартам были проведены для KAME STABLE в проекте "
+"TAHI. Результаты можно посмотреть по ссылке http://www.tahi.org/report/KAME/"
+"[http://www.tahi.org/report/KAME/]. Мы также участвовали в тестах IOL "
+"Университета Нью-Гэмпшира (http://www.iol.unh.edu/[http://www.iol.unh.edu/]) "
+"в прошлом, используя наши предыдущие версии."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:74
+msgid "RFC1639: FTP Operation Over Big Address Records (FOOBAR)"
+msgstr "RFC1639: FTP Operation Over Big Address Records (FOOBAR)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:76
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:113
+msgid ""
+"RFC2428 is preferred over RFC1639. FTP clients will first try RFC2428, then "
+"RFC1639 if failed."
+msgstr ""
+"RFC2428 предпочтительнее RFC1639. FTP-клиенты сначала попробуют RFC2428, "
+"затем RFC1639 в случае неудачи."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:78
+msgid "RFC1886: DNS Extensions to support IPv6"
+msgstr "RFC1886: DNS Extensions to support IPv6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:79
+msgid "RFC1933: Transition Mechanisms for IPv6 Hosts and Routers"
+msgstr "RFC1933: Transition Mechanisms for IPv6 Hosts and Routers"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:81
+msgid "IPv4 compatible address is not supported."
+msgstr "IPv4-совместимый адрес не поддерживается."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:82
+msgid "automatic tunneling (described in 4.3 of this RFC) is not supported."
+msgstr ""
+"автоматическое туннелирование (описано в разделе 4.3 данного RFC) не "
+"поддерживается."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:84
+msgid ""
+"man:gif[4] interface implements IPv[46]-over-IPv[46] tunnel in a generic "
+"way, and it covers \"configured tunnel\" described in the spec. See "
+"crossref:ipv6[gif,23.5.1.5] in this document for details."
+msgstr ""
+"man:gif[4] интерфейс реализует IPv[46]-поверх-IPv[46] туннель в общем виде и "
+"включает \"настроенный туннель\", описанный в спецификации. Подробности см. "
+"в crossref:ipv6[gif,23.5.1.5] этого документа."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:86
+msgid "RFC1981: Path MTU Discovery for IPv6"
+msgstr "RFC1981: Path MTU Discovery for IPv6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:87
+msgid "RFC2080: RIPng for IPv6"
+msgstr "RFC2080: RIPng for IPv6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:89
+msgid "usr.sbin/route6d support this."
+msgstr "usr.sbin/route6d это поддерживает."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:91
+msgid "RFC2292: Advanced Sockets API for IPv6"
+msgstr "RFC2292: Advanced Sockets API for IPv6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:93
+msgid ""
+"For supported library functions/kernel APIs, see [.filename]#sys/netinet6/"
+"ADVAPI#."
+msgstr ""
+"Для поддерживаемых функций библиотек/API ядра см. [.filename]#sys/netinet6/"
+"ADVAPI#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:95
+msgid "RFC2362: Protocol Independent Multicast-Sparse Mode (PIM-SM)"
+msgstr "RFC2362: Protocol Independent Multicast-Sparse Mode (PIM-SM)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:97
+msgid ""
+"RFC2362 defines packet formats for PIM-SM. [.filename]#draft-ietf-pim-"
+"ipv6-01.txt# is written based on this."
+msgstr ""
+"RFC2362 определяет форматы пакетов для PIM-SM. [.filename]#draft-ietf-pim-"
+"ipv6-01.txt# написан на основе этого."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:99
+msgid "RFC2373: IPv6 Addressing Architecture"
+msgstr "RFC2373: IPv6 Addressing Architecture"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:101
+msgid ""
+"supports node required addresses, and conforms to the scope requirement."
+msgstr ""
+"поддерживает обязательные адреса узлов и соответствует требованиям области "
+"видимости."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:103
+msgid "RFC2374: An IPv6 Aggregatable Global Unicast Address Format"
+msgstr "RFC2374: An IPv6 Aggregatable Global Unicast Address Format"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:105
+msgid "supports 64-bit length of Interface ID."
+msgstr "поддерживает 64-битную длину Идентификатора Интерфейса."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:107
+msgid "RFC2375: IPv6 Multicast Address Assignments"
+msgstr "RFC2375: IPv6 Multicast Address Assignments"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:109
+msgid "Userland applications use the well-known addresses assigned in the RFC."
+msgstr ""
+"Пользовательские приложения используют общеизвестные адреса, назначенные в "
+"RFC."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:111
+msgid "RFC2428: FTP Extensions for IPv6 and NATs"
+msgstr "RFC2428: FTP Extensions for IPv6 and NATs"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:115
+msgid "RFC2460: IPv6 specification"
+msgstr "RFC2460: IPv6 specification"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:116
+msgid "RFC2461: Neighbor discovery for IPv6"
+msgstr "RFC2461: Neighbor discovery for IPv6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:118
+msgid ""
+"See crossref:ipv6[neighbor-discovery,23.5.1.2] in this document for details."
+msgstr ""
+"См. crossref:ipv6[neighbor-discovery,23.5.1.2] в этом документе для "
+"получения подробностей."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:120
+msgid "RFC2462: IPv6 Stateless Address Autoconfiguration"
+msgstr "RFC2462: IPv6 Stateless Address Autoconfiguration"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:122
+msgid "See crossref:ipv6[ipv6-pnp,23.5.1.4] in this document for details."
+msgstr ""
+"См. crossref:ipv6[ipv6-pnp,23.5.1.4] в этом документе для получения "
+"подробностей."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:124
+msgid "RFC2463: ICMPv6 for IPv6 specification"
+msgstr "RFC2463: ICMPv6 for IPv6 specification"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:126
+msgid "See crossref:ipv6[icmpv6,23.5.1.9] in this document for details."
+msgstr ""
+"См. crossref:ipv6[icmpv6,23.5.1.9] в этом документе для получения "
+"подробностей."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:128
+msgid "RFC2464: Transmission of IPv6 Packets over Ethernet Networks"
+msgstr "RFC2464: Transmission of IPv6 Packets over Ethernet Networks"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:129
+msgid "RFC2465: MIB for IPv6: Textual Conventions and General Group"
+msgstr "RFC2465: MIB for IPv6: Textual Conventions and General Group"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:131
+msgid ""
+"Necessary statistics are gathered by the kernel. Actual IPv6 MIB support is "
+"provided as a patchkit for ucd-snmp."
+msgstr ""
+"Необходимая статистика собирается ядром. Фактическая поддержка MIB для IPv6 "
+"предоставляется в виде набора патчей для ucd-snmp."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:133
+msgid "RFC2466: MIB for IPv6: ICMPv6 group"
+msgstr "RFC2466: MIB for IPv6: ICMPv6 group"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:135
+msgid ""
+"Necessary statistics are gathered by the kernel. Actual IPv6 MIB support is "
+"provided as patchkit for ucd-snmp."
+msgstr ""
+"Необходимая статистика собирается ядром. Фактическая поддержка MIB IPv6 "
+"предоставляется в виде патча для ucd-snmp."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:137
+msgid "RFC2467: Transmission of IPv6 Packets over FDDI Networks"
+msgstr "RFC2467: Transmission of IPv6 Packets over FDDI Networks"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:138
+msgid "RFC2497: Transmission of IPv6 packet over ARCnet Networks"
+msgstr "RFC2497: Transmission of IPv6 packet over ARCnet Networks"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:139
+msgid "RFC2553: Basic Socket Interface Extensions for IPv6"
+msgstr "RFC2553: Basic Socket Interface Extensions for IPv6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:142
+msgid ""
+"IPv4 mapped address (3.7) and special behavior of IPv6 wildcard bind socket "
+"(3.8) are supported. See crossref:ipv6[ipv6-wildcard-socket,23.5.1.12] in "
+"this document for details."
+msgstr ""
+"Отображаемый адрес IPv4 (3.7) и особое поведение сокета с привязкой по "
+"шаблону IPv6 (3.8) поддерживаются. Подробности см. в разделе "
+"crossref:ipv6[ipv6-wildcard-socket,23.5.1.12] этого документа."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:144
+msgid "RFC2675: IPv6 Jumbograms"
+msgstr "RFC2675: IPv6 Jumbograms"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:146
+msgid "See crossref:ipv6[ipv6-jumbo,23.5.1.7] in this document for details."
+msgstr ""
+"См. crossref:ipv6[ipv6-jumbo,23.5.1.7] в этом документе для получения "
+"подробностей."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:148
+msgid "RFC2710: Multicast Listener Discovery for IPv6"
+msgstr "RFC2710: Multicast Listener Discovery for IPv6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:149
+msgid "RFC2711: IPv6 router alert option"
+msgstr "RFC2711: IPv6 router alert option"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:150
+msgid ""
+"[.filename]#draft-ietf-ipngwg-router-renum-08#: Router renumbering for IPv6"
+msgstr ""
+"[.filename]#draft-ietf-ipngwg-router-renum-08#: Перенумерация "
+"маршрутизаторов для IPv6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:151
+msgid ""
+"[.filename]#draft-ietf-ipngwg-icmp-namelookups-02#: IPv6 Name Lookups "
+"Through ICMP"
+msgstr ""
+"[.filename]#draft-ietf-ipngwg-icmp-namelookups-02#: Поиск имен через ICMP в "
+"IPv6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:152
+msgid ""
+"[.filename]#draft-ietf-ipngwg-icmp-name-lookups-03#: IPv6 Name Lookups "
+"Through ICMP"
+msgstr ""
+"[.filename]#draft-ietf-ipngwg-icmp-name-lookups-03#: Поиск имен IPv6 через "
+"ICMP"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:153
+msgid "[.filename]#draft-ietf-pim-ipv6-01.txt#: PIM for IPv6"
+msgstr "[.filename]#draft-ietf-pim-ipv6-01.txt#: PIM for IPv6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:155
+msgid ""
+"man:pim6dd[8] implements dense mode. man:pim6sd[8] implements sparse mode."
+msgstr ""
+"man:pim6dd[8] реализует плотный режим. man:pim6sd[8] реализует разреженный "
+"режим."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:157
+msgid ""
+"[.filename]#draft-itojun-ipv6-tcp-to-anycast-00#: Disconnecting TCP "
+"connection toward IPv6 anycast address"
+msgstr ""
+"[.filename]#draft-itojun-ipv6-tcp-to-anycast-00#: Разрыв TCP-соединения с "
+"anycast-адресом IPv6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:158
+msgid "[.filename]#draft-yamamoto-wideipv6-comm-model-00#"
+msgstr "[.filename]#draft-yamamoto-wideipv6-comm-model-00#"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:160
+msgid "See crossref:ipv6[ipv6-sas,23.5.1.6] in this document for details."
+msgstr ""
+"См. crossref:ipv6[ipv6-sas,23.5.1.6] в этом документе для более подробной "
+"информации."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:162
+msgid ""
+"[.filename]#draft-ietf-ipngwg-scopedaddr-format-00.txt#: An Extension of "
+"Format for IPv6 Scoped Addresses"
+msgstr ""
+"[.filename]#draft-ietf-ipngwg-scopedaddr-format-00.txt#: Расширение формата "
+"для адресов с областью действия IPv6"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:164
+#, no-wrap
+msgid "Neighbor Discovery"
+msgstr "Функция \"Обнаружение соседей\""
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:169
+msgid ""
+"Neighbor Discovery is fairly stable. Currently Address Resolution, "
+"Duplicated Address Detection, and Neighbor Unreachability Detection are "
+"supported. In the near future we will be adding Proxy Neighbor "
+"Advertisement support in the kernel and Unsolicited Neighbor Advertisement "
+"transmission command as admin tool."
+msgstr ""
+"Обнаружение соседей достаточно стабильно. В настоящее время поддерживаются "
+"следующие функции: определение адреса (Address Resolution), обнаружение "
+"дублирования адресов (DAD — Duplicated Address Detection) и обнаружение "
+"недоступности соседей (Neighbor Unreachability Detection). В ближайшем "
+"будущем мы добавим поддержку прокси-объявлений соседей (Proxy Neighbor "
+"Advertisement) в ядро и команду передачи непрошенных объявлений соседей "
+"(Unsolicited Neighbor Advertisement) в качестве инструмента администратора."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:174
+msgid ""
+"If DAD fails, the address will be marked \"duplicated\" and message will be "
+"generated to syslog (and usually to console). The \"duplicated\" mark can "
+"be checked with man:ifconfig[8]. It is administrators' responsibility to "
+"check for and recover from DAD failures. The behavior should be improved in "
+"the near future."
+msgstr ""
+"Если DAD завершается неудачно, адрес будет помечен как \"дублированный\" "
+"(duplicated), и сообщение будет записано в syslog (а также обычно выведено "
+"на консоль). Метку \"дублированный\" можно проверить с помощью "
+"man:ifconfig[8]. Обязанность администратора — проверять и устранять сбои "
+"DAD. В ближайшем будущем поведение должно быть улучшено."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:178
+msgid ""
+"Some of the network driver loops multicast packets back to itself, even if "
+"instructed not to do so (especially in promiscuous mode). In such cases DAD "
+"may fail, because DAD engine sees inbound NS packet (actually from the node "
+"itself) and considers it as a sign of duplicate. You may want to look at "
+"#if condition marked \"heuristics\" in sys/netinet6/"
+"nd6_nbr.c:nd6_dad_timer() as workaround (note that the code fragment in "
+"\"heuristics\" section is not spec conformant)."
+msgstr ""
+"Некоторые сетевые драйверы закольцовывают multicast-пакеты обратно на себя, "
+"даже если им указано так не делать (особенно в promiscuous mode). В таких "
+"случаях DAD может завершиться неудачей, так как механизм DAD видит входящий "
+"NS-пакет (на самом деле от самого узла) и считает его признаком дубликата. В "
+"качестве обходного решения можно рассмотреть условие #if с пометкой "
+"\"heuristics\" в sys/netinet6/nd6_nbr.c:nd6_dad_timer() (обратите внимание, "
+"что фрагмент кода в разделе \"heuristics\" не соответствует спецификации)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:180
+msgid ""
+"Neighbor Discovery specification (RFC2461) does not talk about neighbor "
+"cache handling in the following cases:"
+msgstr ""
+"Спецификация обнаружения соседей (RFC2461) не рассматривает обработку кэша "
+"соседей в следующих случаях:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:182
+msgid ""
+"when there was no neighbor cache entry, node received unsolicited RS/NS/NA/"
+"redirect packet without link-layer address"
+msgstr ""
+"когда отсутствовала запись в кэше соседей, узел получал нежелательный пакет "
+"RS/NS/NA/редирект без адреса канального уровня"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:183
+msgid ""
+"neighbor cache handling on medium without link-layer address (we need a "
+"neighbor cache entry for IsRouter bit)"
+msgstr ""
+"обработка кэша соседей в среде без адреса канального уровня (нам нужна "
+"запись в кэше соседей для бита IsRouter)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:186
+msgid ""
+"For first case, we implemented workaround based on discussions on IETF "
+"ipngwg mailing list. For more details, see the comments in the source code "
+"and email thread started from (IPng 7155), dated Feb 6 1999."
+msgstr ""
+"Для первого случая мы реализовали временное решение на основе обсуждений в "
+"рассылке IETF ipngwg. Подробности можно найти в комментариях исходного кода "
+"и ветке электронной почты, начавшейся с (IPng 7155) от 6 февраля 1999 года."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:189
+msgid ""
+"IPv6 on-link determination rule (RFC2461) is quite different from "
+"assumptions in BSD network code. At this moment, no on-link determination "
+"rule is supported where default router list is empty (RFC2461, section 5.2, "
+"last sentence in 2nd paragraph - note that the spec misuse the word \"host\" "
+"and \"node\" in several places in the section)."
+msgstr ""
+"Правило определения локальной IPv6 сети (RFC2461) значительно отличается от "
+"предположений в сетевом коде BSD. На данный момент не поддерживается правило "
+"определения локальной сети при пустом списке маршрутизаторов по умолчанию "
+"(RFC2461, раздел 5.2, последнее предложение во 2-м абзаце - обратите "
+"внимание, что в спецификации некорректно используются слова \"host\" и "
+"\"node\" в нескольких местах раздела)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:194
+msgid ""
+"To avoid possible DoS attacks and infinite loops, only 10 options on ND "
+"packet is accepted now. Therefore, if you have 20 prefix options attached "
+"to RA, only the first 10 prefixes will be recognized. If this troubles you, "
+"please ask it on FREEBSD-CURRENT mailing list and/or modify nd6_maxndopt in "
+"[.filename]#sys/netinet6/nd6.c#. If there are high demands we may provide "
+"sysctl knob for the variable."
+msgstr ""
+"Во избежание возможных атак типа DoS и бесконечных циклов, сейчас "
+"принимается только 10 опций в ND-пакете. Таким образом, если к RA "
+"прикреплено 20 опций префиксов, будут распознаны только первые 10 префиксов. "
+"Если это вызывает проблемы, пожалуйста, задайте вопрос в рассылке FREEBSD-"
+"CURRENT и/или измените nd6_maxndopt в [.filename]#sys/netinet6/nd6.c#. При "
+"высоком спросе мы можем предоставить sysctl-параметр для этой переменной."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:196
+#, no-wrap
+msgid "Scope Index"
+msgstr "Индекс зоны"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:201
+msgid ""
+"IPv6 uses scoped addresses. Therefore, it is very important to specify "
+"scope index (interface index for link-local address, or site index for site-"
+"local address) with an IPv6 address. Without scope index, scoped IPv6 "
+"address is ambiguous to the kernel, and kernel will not be able to determine "
+"the outbound interface for a packet."
+msgstr ""
+"В IPv6 используются адреса с областями видимости — зонами. Поэтому очень "
+"важно указывать индекс зоны (индекс интерфейса для линк-локального адреса "
+"или индекс сайта для сайт-локального адреса) вместе с IPv6 адресом. Без "
+"индекса зоны адрес IPv6 с ограниченной областью действия является "
+"неоднозначным для ядра, и ядро не сможет определить исходящий интерфейс для "
+"пакета."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:206
+msgid ""
+"Ordinary userland applications should use advanced API (RFC2292) to specify "
+"scope index, or interface index. For similar purpose, sin6_scope_id member "
+"in sockaddr_in6 structure is defined in RFC2553. However, the semantics for "
+"sin6_scope_id is rather vague. If you care about portability of your "
+"application, we suggest you to use advanced API rather than sin6_scope_id."
+msgstr ""
+"Обычные пользовательские приложения должны использовать расширенный API "
+"(RFC2292) для указания индекса зоны или индекса интерфейса. Для аналогичных "
+"целей член sin6_scope_id в структуре sockaddr_in6 определён в RFC2553. "
+"Однако семантика sin6_scope_id довольно расплывчата. Если важна "
+"переносимость вашего приложения, мы рекомендуем использовать расширенный API "
+"вместо sin6_scope_id."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:209
+msgid ""
+"In the kernel, an interface index for link-local scoped address is embedded "
+"into 2nd 16bit-word (3rd and 4th byte) in IPv6 address. For example, you "
+"may see something like:"
+msgstr ""
+"В ядре индекс интерфейса для адреса с областью действия link-local "
+"встраивается во второе 16-битное слово (3-й и 4-й байт) в IPv6-адресе. "
+"Например, вы можете увидеть что-то вроде:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:213
+#, no-wrap
+msgid "\tfe80:1::200:f8ff:fe01:6317\n"
+msgstr "\tfe80:1::200:f8ff:fe01:6317\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:218
+msgid ""
+"in the routing table and interface address structure (struct in6_ifaddr). "
+"The address above is a link-local unicast address which belongs to a network "
+"interface whose interface identifier is 1. The embedded index enables us to "
+"identify IPv6 link local addresses over multiple interfaces effectively and "
+"with only a little code change."
+msgstr ""
+"в таблице маршрутизации и структуре адреса интерфейса (struct in6_ifaddr). "
+"Указанный выше адрес является линк-локальным уникастным адресом, который "
+"принадлежит сетевому интерфейсу с идентификатором интерфейса 1. Встроенный "
+"индекс позволяет эффективно идентифицировать локальные адреса IPv6 на "
+"нескольких интерфейсах с минимальными изменениями кода."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:223
+msgid ""
+"Routing daemons and configuration programs, like man:route6d[8] and "
+"man:ifconfig[8], will need to manipulate the \"embedded\" scope index. "
+"These programs use routing sockets and ioctls (like SIOCGIFADDR_IN6) and the "
+"kernel API will return IPv6 addresses with 2nd 16bit-word filled in. The "
+"APIs are for manipulating kernel internal structure. Programs that use "
+"these APIs have to be prepared about differences in kernels anyway."
+msgstr ""
+"Демоны маршрутизации и программы настройки, такие как man:route6d[8] и "
+"man:ifconfig[8], должны управлять \"встроенным\" индексом зоны. Эти "
+"программы используют сокеты маршрутизации и ioctl (например, "
+"SIOCGIFADDR_IN6), и API ядра будет возвращать IPv6-адреса с заполненным "
+"вторым 16-битным словом. API предназначены для управления внутренними "
+"структурами ядра. Программы, использующие эти API, в любом случае должны "
+"быть готовы к различиям в ядрах."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:230
+msgid ""
+"When you specify scoped address to the command line, NEVER write the "
+"embedded form (such as ff02:1::1 or fe80:2::fedc). This is not supposed to "
+"work. Always use standard form, like ff02::1 or fe80::fedc, with command "
+"line option for specifying interface (like `ping -6 -I ne0 ff02::1`). In "
+"general, if a command does not have command line option to specify outgoing "
+"interface, that command is not ready to accept scoped address. This may "
+"seem to be opposite from IPv6's premise to support \"dentist office\" "
+"situation. We believe that specifications need some improvements for this."
+msgstr ""
+"При указании адреса с ограниченной областью действия в командной строке "
+"НИКОГДА не используйте встроенную форму (например, ff02:1::1 или "
+"fe80:2::fedc). Это не должно работать. Всегда используйте стандартную форму, "
+"такую как ff02::1 или fe80::fedc, с параметром командной строки для указания "
+"интерфейса (например, `ping -6 -I ne0 ff02::1`). В общем, если команда не "
+"имеет параметра командной строки для указания исходящего интерфейса, эта "
+"команда не готова принимать адрес с областью действия. Это кажется "
+"противоречащим принципу IPv6 поддерживать сценарий \"кабинета стоматолога\". "
+"Мы считаем, что спецификации нуждаются в некоторых улучшениях для этого."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:234
+msgid ""
+"Some of the userland tools support extended numeric IPv6 syntax, as "
+"documented in [.filename]#draft-ietf-ipngwg-scopedaddr-format-00.txt#. You "
+"can specify outgoing link, by using name of the outgoing interface like "
+"\"fe80::1%ne0\". This way you will be able to specify link-local scoped "
+"address without much trouble."
+msgstr ""
+"Некоторые пользовательские утилиты поддерживают расширенный числовой "
+"синтаксис IPv6, как описано в [.filename]#draft-ietf-ipngwg-scopedaddr-"
+"format-00.txt#. Можно указать исходящее соединение, используя имя исходящего "
+"интерфейса, например \"fe80::1%ne0\". Таким образом можно легко указать линк-"
+"локальный адрес с ограниченной областью действия."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:237
+msgid ""
+"To use this extension in your program, you will need to use "
+"man:getaddrinfo[3], and man:getnameinfo[3] with NI_WITHSCOPEID. The "
+"implementation currently assumes 1-to-1 relationship between a link and an "
+"interface, which is stronger than what specs say."
+msgstr ""
+"Для использования этого расширения в вашей программе потребуется "
+"использовать man:getaddrinfo[3] и man:getnameinfo[3] с NI_WITHSCOPEID. В "
+"текущей реализации предполагается однозначное соответствие между каналом и "
+"интерфейсом, что является более строгим условием, чем указано в "
+"спецификациях."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:239
+#, no-wrap
+msgid "Plug and Play"
+msgstr "Plug and Play (подключи и работай)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:245
+msgid ""
+"Most of the IPv6 stateless address autoconfiguration is implemented in the "
+"kernel. Neighbor Discovery functions are implemented in the kernel as a "
+"whole. Router Advertisement (RA) input for hosts is implemented in the "
+"kernel. Router Solicitation (RS) output for endhosts, RS input for routers, "
+"and RA output for routers are implemented in the userland."
+msgstr ""
+"Большая часть автонастройки адресов IPv6 без сохранения состояния "
+"реализована в ядре. Функции обнаружения соседей (Neighbor Discovery) "
+"реализованы в ядре целиком. Ввод рекламы маршрутизатора (RA) для хостов "
+"реализован в ядре. Вывод запроса маршрутизатора (RS) для конечных хостов, "
+"ввод RS для маршрутизаторов и вывод RA для маршрутизаторов реализованы в "
+"пользовательском пространстве."
+
+#. type: Title =====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:246
+#, no-wrap
+msgid "Assignment of link-local, and special addresses"
+msgstr "Назначение линк-локальных и специальных адресов"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:251
+msgid ""
+"IPv6 link-local address is generated from IEEE802 address (Ethernet MAC "
+"address). Each of interface is assigned an IPv6 link-local address "
+"automatically, when the interface becomes up (IFF_UP). Also, direct route "
+"for the link-local address is added to routing table."
+msgstr ""
+"Линк-локальный адрес IPv6 генерируется из IEEE802 адреса (Ethernet MAC "
+"адреса). Каждому интерфейсу автоматически присваивается IPv6 линк-локальный "
+"адрес, когда интерфейс поднимается (IFF_UP). Также в таблицу маршрутизации "
+"добавляется прямой маршрут для линк-локального адреса."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:253
+msgid "Here is an output of netstat command:"
+msgstr "Вот вывод команды netstat:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:260
+#, no-wrap
+msgid ""
+"Internet6:\n"
+"Destination Gateway Flags Netif Expire\n"
+"fe80:1::%ed0/64 link#1 UC ed0\n"
+"fe80:2::%ep0/64 link#2 UC ep0\n"
+msgstr ""
+"Internet6:\n"
+"Destination Gateway Flags Netif Expire\n"
+"fe80:1::%ed0/64 link#1 UC ed0\n"
+"fe80:2::%ep0/64 link#2 UC ep0\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:265
+msgid ""
+"Interfaces that has no IEEE802 address (pseudo interfaces like tunnel "
+"interfaces, or ppp interfaces) will borrow IEEE802 address from other "
+"interfaces, such as Ethernet interfaces, whenever possible. If there is no "
+"IEEE802 hardware attached, a last resort pseudo-random value, MD5(hostname), "
+"will be used as source of link-local address. If it is not suitable for "
+"your usage, you will need to configure the link-local address manually."
+msgstr ""
+"Интерфейсы, не имеющие адреса IEEE802 (псевдоинтерфейсы, такие как "
+"туннельные интерфейсы или интерфейсы ppp), будут заимствовать адрес IEEE802 "
+"у других интерфейсов, например, Ethernet-интерфейсов, когда это возможно. "
+"Если нет подключенного оборудования IEEE802, в качестве последнего средства "
+"будет использовано псевдослучайное значение MD5(hostname) для формирования "
+"линк-локального адреса. Если это не подходит для вашего использования, вам "
+"потребуется настроить линк-локальный адрес вручную."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:268
+msgid ""
+"If an interface is not capable of handling IPv6 (such as lack of multicast "
+"support), link-local address will not be assigned to that interface. See "
+"section 2 for details."
+msgstr ""
+"Если интерфейс не поддерживает IPv6 (например, отсутствует поддержка "
+"multicast), на этот интерфейс не будет назначен линк-локальный адрес. "
+"Подробности см. в разделе 2."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:272
+msgid ""
+"Each interface joins the solicited multicast address and the link-local all-"
+"nodes multicast addresses (e.g., fe80::1:ff01:6317 and ff02::1, "
+"respectively, on the link the interface is attached). In addition to a link-"
+"local address, the loopback address (::1) will be assigned to the loopback "
+"interface. Also, ::1/128 and ff01::/32 are automatically added to routing "
+"table, and loopback interface joins node-local multicast group ff01::1."
+msgstr ""
+"Каждый интерфейс присоединяется к запрашиваемому широковещательному адресу и "
+"линк-локальным широковещательным адресам всех узлов (например, "
+"fe80::1:ff01:6317 и ff02::1 соответственно на соединении, к которому "
+"подключен интерфейс). В дополнение к линк-локальному адресу, адрес обратной "
+"петли (::1 — loopback) будет назначен интерфейсу обратной петли. "
+"Также, ::1/128 и ff01::/32 автоматически добавляются в таблицу "
+"маршрутизации, а loopback-интерфейс (интерфейс обратной петли) "
+"присоединяется к групповому адресу в пределах узла ff01::1."
+
+#. type: Title =====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:273
+#, no-wrap
+msgid "Stateless address autoconfiguration on Hosts"
+msgstr "Автоматическая настройка адресов без состояния на узлах"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:277
+msgid ""
+"In IPv6 specification, nodes are separated into two categories: _routers_ "
+"and _hosts_. Routers forward packets addressed to others, hosts does not "
+"forward the packets. net.inet6.ip6.forwarding defines whether this node is "
+"router or host (router if it is 1, host if it is 0)."
+msgstr ""
+"В спецификации IPv6 узлы разделены на две категории: _маршрутизаторы_ и "
+"_хосты_. Маршрутизаторы пересылают пакеты, адресованные другим, хосты не "
+"пересылают пакеты. Параметр net.inet6.ip6.forwarding определяет, является ли "
+"данный узел маршрутизатором или хостом (маршрутизатор, если значение равно "
+"1, хост, если 0)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:286
+msgid ""
+"When a host hears Router Advertisement from the router, a host may "
+"autoconfigure itself by stateless address autoconfiguration. This behavior "
+"can be controlled by net.inet6.ip6.accept_rtadv (host autoconfigures itself "
+"if it is set to 1). By autoconfiguration, network address prefix for the "
+"receiving interface (usually global address prefix) is added. Default route "
+"is also configured. Routers periodically generate Router Advertisement "
+"packets. To request an adjacent router to generate RA packet, a host can "
+"transmit Router Solicitation. To generate a RS packet at any time, use the "
+"_rtsol_ command. man:rtsold[8] daemon is also available. man:rtsold[8] "
+"generates Router Solicitation whenever necessary, and it works great for "
+"nomadic usage (notebooks/laptops). If one wishes to ignore Router "
+"Advertisements, use sysctl to set net.inet6.ip6.accept_rtadv to 0."
+msgstr ""
+"Когда хост получает Объявление Маршрутизатора (Router Advertisement) от "
+"маршрутизатора, он может автоматически настроить себя с помощью "
+"автонастройки адреса без сохранения состояния. Это поведение можно "
+"контролировать с помощью параметра net.inet6.ip6.accept_rtadv (хост "
+"автонастраивается, если значение равно 1). При автонастройке добавляется "
+"префикс сетевого адреса для принимающего интерфейса (обычно префикс "
+"глобального адреса). Также настраивается маршрут по умолчанию. "
+"Маршрутизаторы периодически генерируют пакеты Router Advertisement. Чтобы "
+"запросить соседний маршрутизатор сгенерировать RA-пакет, хост может "
+"отправить Router Solicitation. Для генерации RS-пакета в любое время "
+"используйте команду _rtsol_. Также доступен демон man:rtsold[8]. "
+"man:rtsold[8] генерирует Router Solicitation по мере необходимости и отлично "
+"подходит для мобильного использования (ноутбуки/лэптопы). Если необходимо "
+"игнорировать Router Advertisements, используйте sysctl для установки "
+"net.inet6.ip6.accept_rtadv в 0."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:288
+msgid ""
+"To generate Router Advertisement from a router, use the man:rtadvd[8] daemon."
+msgstr ""
+"Для генерации Router Advertisement от маршрутизатора используйте демон "
+"man:rtadvd[8]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:290
+msgid ""
+"Note that, IPv6 specification assumes the following items, and nonconforming "
+"cases are left unspecified:"
+msgstr ""
+"Обратите внимание, что спецификация IPv6 предполагает следующие пункты, а "
+"случаи несоответствия остаются неуточнёнными:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:292
+msgid "Only hosts will listen to router advertisements"
+msgstr "Только хосты будут принимать объявления от маршрутизаторов"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:293
+msgid "Hosts have single network interface (except loopback)"
+msgstr "Узлы имеют один сетевой интерфейс (за исключением loopback)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:296
+msgid ""
+"Therefore, this is unwise to enable net.inet6.ip6.accept_rtadv on routers, "
+"or multi-interface host. A misconfigured node can behave strange "
+"(nonconforming configuration allowed for those who would like to do some "
+"experiments)."
+msgstr ""
+"Поэтому не рекомендуется включать net.inet6.ip6.accept_rtadv на "
+"маршрутизаторах или многопортовых хостах. Неправильно настроенный узел может "
+"вести себя странно (нестандартная конфигурация разрешена для тех, кто хочет "
+"провести эксперименты)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:298
+msgid "To summarize the sysctl knob:"
+msgstr "Резюмируя настройку sysctl:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:312
+#, no-wrap
+msgid ""
+"\taccept_rtadv\tforwarding\trole of the node\n"
+"\t---\t\t---\t\t---\n"
+"\t0\t\t0\t\thost (to be manually configured)\n"
+"\t0\t\t1\t\trouter\n"
+"\t1\t\t0\t\tautoconfigured host\n"
+"\t\t\t\t\t(spec assumes that host has single\n"
+"\t\t\t\t\tinterface only, autoconfigured host\n"
+"\t\t\t\t\twith multiple interface is\n"
+"\t\t\t\t\tout-of-scope)\n"
+"\t1\t\t1\t\tinvalid, or experimental\n"
+"\t\t\t\t\t(out-of-scope of spec)\n"
+msgstr ""
+"\taccept_rtadv\tforwarding\trole of the node\n"
+"\t---\t\t---\t\t---\n"
+"\t0\t\t0\t\thost (to be manually configured)\n"
+"\t0\t\t1\t\trouter\n"
+"\t1\t\t0\t\tautoconfigured host\n"
+"\t\t\t\t\t(spec assumes that host has single\n"
+"\t\t\t\t\tinterface only, autoconfigured host\n"
+"\t\t\t\t\twith multiple interface is\n"
+"\t\t\t\t\tout-of-scope)\n"
+"\t1\t\t1\t\tinvalid, or experimental\n"
+"\t\t\t\t\t(out-of-scope of spec)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:317
+msgid ""
+"RFC2462 has validation rule against incoming RA prefix information option, "
+"in 5.5.3 (e). This is to protect hosts from malicious (or misconfigured) "
+"routers that advertise very short prefix lifetime. There was an update from "
+"Jim Bound to ipngwg mailing list (look for \"(ipng 6712)\" in the archive) "
+"and it is implemented Jim's update."
+msgstr ""
+"В RFC2462 есть правило проверки для входящей информации о префиксе в RA, в "
+"разделе 5.5.3 (e). Это защищает хосты от злонамеренных (или неправильно "
+"настроенных) маршрутизаторов, которые анонсируют очень короткое время жизни "
+"префикса. Было обновление от Джима Баунда в рассылке ipngwg (ищите \"(ipng "
+"6712)\" в архиве), и это обновление Джима реализовано."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:319
+msgid ""
+"See crossref:ipv6[neighbor-discovery,23.5.1.2] in the document for "
+"relationship between DAD and autoconfiguration."
+msgstr ""
+"См. crossref:ipv6[neighbor-discovery,23.5.1.2] в документе для информации о "
+"взаимосвязи между DAD и автонастройкой."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:321
+#, no-wrap
+msgid "Generic Tunnel Interface"
+msgstr "Универсальный Туннельный Интерфейс"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:325
+msgid ""
+"GIF (Generic InterFace) is a pseudo interface for configured tunnel. "
+"Details are described in man:gif[4]. Currently"
+msgstr ""
+"GIF (Generic InterFace) — это псевдоинтерфейс для настроенного туннеля. "
+"Подробности описаны в man:gif[4]. В настоящее время"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:327
+msgid "v6 in v6"
+msgstr "v6 в v6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:328
+msgid "v6 in v4"
+msgstr "v6 в v4"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:329
+msgid "v4 in v6"
+msgstr "v4 в v6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:330
+msgid "v4 in v4"
+msgstr "v4 в v4"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:335
+msgid ""
+"are available. Use man:gifconfig[8] to assign physical (outer) source and "
+"destination address to gif interfaces. Configuration that uses same address "
+"family for inner and outer IP header (v4 in v4, or v6 in v6) is dangerous. "
+"It is very easy to configure interfaces and routing tables to perform "
+"infinite level of tunneling. _Please be warned_."
+msgstr ""
+"доступны. Используйте man:gifconfig[8] для назначения физических (внешних) "
+"исходных и конечных адресов интерфейсам gif. Конфигурация, использующая одно "
+"семейство адресов для внутреннего и внешнего IP-заголовка (v4 в v4 или v6 в "
+"v6), является опасной. Очень легко настроить интерфейсы и таблицы "
+"маршрутизации для выполнения бесконечного уровня туннелирования. "
+"_Пожалуйста, будьте осторожны_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:338
+msgid ""
+"gif can be configured to be ECN-friendly. See crossref:ipv6[ipsec-"
+"ecn,23.5.4.5] for ECN-friendliness of tunnels, and man:gif[4] for how to "
+"configure."
+msgstr ""
+"gif можно настроить так, чтобы он был дружественным к ECN. Подробнее о "
+"дружелюбности к ECN для туннелей см. crossref:ipv6[ipsec-ecn,23.5.4.5], а о "
+"настройке — в man:gif[4]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:341
+msgid ""
+"If you would like to configure an IPv4-in-IPv6 tunnel with gif interface, "
+"read man:gif[4] carefully. You will need to remove IPv6 link-local address "
+"automatically assigned to the gif interface."
+msgstr ""
+"Если вы хотите настроить туннель IPv4-в-IPv6 с интерфейсом gif, внимательно "
+"прочитайте man:gif[4]. Вам потребуется удалить линк-локальный адрес IPv6, "
+"автоматически назначенный интерфейсу gif."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:343
+#, no-wrap
+msgid "Source Address Selection"
+msgstr "Выбор исходящего адреса"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:347
+msgid ""
+"Current source selection rule is scope oriented (there are some exceptions - "
+"see below). For a given destination, a source IPv6 address is selected by "
+"the following rule:"
+msgstr ""
+"Текущее правило выбора источника ориентировано на зону (есть несколько "
+"исключений — см. ниже). Для заданного адреса назначения исходящий IPv6-адрес "
+"выбирается по следующему правилу:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:349
+msgid ""
+"If the source address is explicitly specified by the user (e.g., via the "
+"advanced API), the specified address is used."
+msgstr ""
+"Если исходящий адрес явно указан пользователем (например, через расширенный "
+"API), используется указанный адрес."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:350
+msgid ""
+"If there is an address assigned to the outgoing interface (which is usually "
+"determined by looking up the routing table) that has the same scope as the "
+"destination address, the address is used."
+msgstr ""
+"Если на исходящем интерфейсе назначен адрес (который обычно определяется "
+"путем просмотра таблицы маршрутизации) с той же зоной действия, что и адрес "
+"назначения, используется этот адрес."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:352
+msgid "This is the most typical case."
+msgstr "Это наиболее типичный случай."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:353
+msgid ""
+"If there is no address that satisfies the above condition, choose a global "
+"address assigned to one of the interfaces on the sending node."
+msgstr ""
+"Если нет адреса, удовлетворяющего указанному выше условию, выберите "
+"глобальный адрес, назначенный одному из интерфейсов на отправляющем узле."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:354
+msgid ""
+"If there is no address that satisfies the above condition, and destination "
+"address is site local scope, choose a site local address assigned to one of "
+"the interfaces on the sending node."
+msgstr ""
+"Если нет адреса, удовлетворяющего указанному выше условию, и адрес "
+"назначения имеет сайт-локальную зону, выберите сайт-локальный адрес, "
+"назначенный одному из интерфейсов на отправляющем узле."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:355
+msgid ""
+"If there is no address that satisfies the above condition, choose the "
+"address associated with the routing table entry for the destination. This is "
+"the last resort, which may cause scope violation."
+msgstr ""
+"Если нет адреса, удовлетворяющего указанному условию, выберите адрес, "
+"связанный с записью таблицы маршрутизации для назначения. Это крайняя мера, "
+"которая может нарушить границы зоны действия."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:361
+msgid ""
+"For instance, ::1 is selected for ff01::1, fe80:1::200:f8ff:fe01:6317 for "
+"fe80:1::2a0:24ff:feab:839b (note that embedded interface index - described "
+"in crossref:ipv6[ipv6-scope-index,23.5.1.3] - helps us choose the right "
+"source address. Those embedded indices will not be on the wire). If the "
+"outgoing interface has multiple address for the scope, a source is selected "
+"longest match basis (rule 3). Suppose 2001:0DB8:808:1:200:f8ff:fe01:6317 "
+"and 2001:0DB8:9:124:200:f8ff:fe01:6317 are given to the outgoing interface. "
+"2001:0DB8:808:1:200:f8ff:fe01:6317 is chosen as the source for the "
+"destination 2001:0DB8:800::1."
+msgstr ""
+"Например, ::1 выбирается для ff01::1, fe80:1::200:f8ff:fe01:6317 для "
+"fe80:1::2a0:24ff:feab:839b (обратите внимание, что встроенный индекс "
+"интерфейса — описанный в crossref:ipv6[ipv6-scope-index,23.5.1.3] — помогает "
+"нам выбрать правильный исходный адрес. Эти встроенные индексы не будут "
+"передаваться по сети). Если исходящий интерфейс имеет несколько адресов для "
+"данной зоны, исходный адрес выбирается на основе наибольшего соответствия "
+"(правило 3). Предположим, что 2001:0DB8:808:1:200:f8ff:fe01:6317 и "
+"2001:0DB8:9:124:200:f8ff:fe01:6317 назначены исходящему интерфейсу. "
+"2001:0DB8:808:1:200:f8ff:fe01:6317 выбирается в качестве исходящего адреса "
+"для адреса назначения 2001:0DB8:800::1."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:368
+msgid ""
+"Note that the above rule is not documented in the IPv6 spec. It is "
+"considered \"up to implementation\" item. There are some cases where we do "
+"not use the above rule. One example is connected TCP session, and we use "
+"the address kept in tcb as the source. Another example is source address "
+"for Neighbor Advertisement. Under the spec (RFC2461 7.2.2) NA's source "
+"should be the target address of the corresponding NS's target. In this case "
+"we follow the spec rather than the above longest-match rule."
+msgstr ""
+"Обратите внимание, что приведенное выше правило не документировано в "
+"спецификации IPv6. Оно считается элементом, оставленным \"на усмотрение "
+"реализации\". Существуют случаи, когда мы не используем это правило. Один из "
+"примеров — установленное TCP-соединение, где мы используем адрес, "
+"сохраненный в tcb, в качестве источника. Другой пример — исходящий адрес для "
+"Объявления Соседа (Neighbor Advertisement). Согласно спецификации (RFC2461 "
+"7.2.2) источник NA должен быть целевым адресом соответствующего NS. В этом "
+"случае мы следуем спецификации, а не приведенному выше правилу наибольшего "
+"совпадения."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:374
+msgid ""
+"For new connections (when rule 1 does not apply), deprecated addresses "
+"(addresses with preferred lifetime = 0) will not be chosen as source address "
+"if other choices are available. If no other choices are available, "
+"deprecated address will be used as a last resort. If there are multiple "
+"choice of deprecated addresses, the above scope rule will be used to choose "
+"from those deprecated addresses. If you would like to prohibit the use of "
+"deprecated address for some reason, configure net.inet6.ip6.use_deprecated "
+"to 0. The issue related to deprecated address is described in RFC2462 5.5.4 "
+"(NOTE: there is some debate underway in IETF ipngwg on how to use "
+"\"deprecated\" address)."
+msgstr ""
+"Для новых соединений (когда правило 1 не применяется), устаревшие адреса "
+"(адреса с предпочтительным временем жизни = 0) не будут выбираться в "
+"качестве исходящего адреса, если доступны другие варианты. Если других "
+"вариантов нет, устаревший адрес будет использован в качестве последнего "
+"средства. Если есть несколько устаревших адресов, для выбора между ними "
+"будет применено указанное выше правило области видимости. Если вы хотите "
+"запретить использование устаревших адресов по какой-либо причине, установите "
+"параметр net.inet6.ip6.use_deprecated в значение 0. Проблема, связанная с "
+"устаревшими адресами, описана в RFC2462 5.5.4 (ПРИМЕЧАНИЕ: в IETF ipngwg "
+"ведутся дебаты о том, как использовать \"устаревшие\" адреса)."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:376
+#, no-wrap
+msgid "Jumbo Payload"
+msgstr "Джамбо-пакет (Jumbo Payload)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:380
+msgid ""
+"The Jumbo Payload hop-by-hop option is implemented and can be used to send "
+"IPv6 packets with payloads longer than 65,535 octets. But currently no "
+"physical interface whose MTU is more than 65,535 is supported, so such "
+"payloads can be seen only on the loopback interface (i.e., lo0)."
+msgstr ""
+"Опция джамбо-пакет типа \"от прыжка к прыжку\" реализована и может "
+"использоваться для отправки IPv6-пакетов с полезной нагрузкой длиной более "
+"65 535 октетов. Однако в настоящее время не поддерживаются физические "
+"интерфейсы с MTU более 65 535, поэтому такие нагрузки могут быть только на "
+"интерфейсе loopback (т.е. lo0)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:382
+msgid ""
+"If you want to try jumbo payloads, you first have to reconfigure the kernel "
+"so that the MTU of the loopback interface is more than 65,535 bytes; add the "
+"following to the kernel configuration file:"
+msgstr ""
+"Если вы хотите попробовать джамбо-пакеты, сначала необходимо "
+"переконфигурировать ядро, чтобы MTU интерфейса loopback превышал 65 535 "
+"байт; добавьте следующее в конфигурационный файл ядра:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:384
+msgid "`options \"LARGE_LOMTU\" #To test jumbo payload`"
+msgstr "`options \"LARGE_LOMTU\" #To test jumbo payload`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:386
+msgid "and recompile the new kernel."
+msgstr "и пересоберите новое ядро."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:390
+msgid ""
+"Then you can test jumbo payloads by the man:ping[8] command with -6, -b and "
+"-s options. The -b option must be specified to enlarge the size of the "
+"socket buffer and the -s option specifies the length of the packet, which "
+"should be more than 65,535. For example, type as follows:"
+msgstr ""
+"Затем вы можете проверить работу с большими пакетами с помощью команды "
+"man:ping[8] с опциями -6, -b и -s. Опция -b необходима для увеличения "
+"размера буфера сокета, а опция -s задает длину пакета, которая должна быть "
+"больше 65 535. Например, введите следующее:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:394
+#, no-wrap
+msgid "% ping -6 -b 70000 -s 68000 ::1\n"
+msgstr "% ping -6 -b 70000 -s 68000 ::1\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:399
+msgid ""
+"The IPv6 specification requires that the Jumbo Payload option must not be "
+"used in a packet that carries a fragment header. If this condition is "
+"broken, an ICMPv6 Parameter Problem message must be sent to the sender. "
+"specification is followed, but you cannot usually see an ICMPv6 error caused "
+"by this requirement."
+msgstr ""
+"Спецификация IPv6 требует, чтобы опция Джамбо-пакет не использовалась в "
+"пакете, содержащем заголовок фрагмента. Если это условие нарушено, должно "
+"быть отправлено ICMPv6 сообщение Parameter Problem отправителю. Спецификация "
+"соблюдается, но обычно вы не можете увидеть ICMPv6 ошибку, вызванную этим "
+"требованием."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:403
+msgid ""
+"When an IPv6 packet is received, the frame length is checked and compared to "
+"the length specified in the payload length field of the IPv6 header or in "
+"the value of the Jumbo Payload option, if any. If the former is shorter "
+"than the latter, the packet is discarded and statistics are incremented. "
+"You can see the statistics as output of man:netstat[8] command with `-s -p "
+"ip6' option:"
+msgstr ""
+"При получении IPv6-пакета проверяется длина кадра и сравнивается с длиной, "
+"указанной в поле длины полезной нагрузки заголовка IPv6 или в значении опции "
+"Джамбо-пакета, если она присутствует. Если первое значение меньше второго, "
+"пакет отбрасывается, и статистика увеличивается. Статистику можно увидеть в "
+"выводе команды man:netstat[8] с опцией `-s -p ip6`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:410
+#, no-wrap
+msgid ""
+"% netstat -s -p ip6\n"
+"\t ip6:\n"
+"\t\t(snip)\n"
+"\t\t1 with data size < data length\n"
+msgstr ""
+"% netstat -s -p ip6\n"
+"\t ip6:\n"
+"\t\t(snip)\n"
+"\t\t1 with data size < data length\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:414
+msgid ""
+"So, kernel does not send an ICMPv6 error unless the erroneous packet is an "
+"actual Jumbo Payload, that is, its packet size is more than 65,535 bytes. "
+"As described above, currently no physical interface with such a huge MTU is "
+"supported, so it rarely returns an ICMPv6 error."
+msgstr ""
+"Итак, ядро не отправляет ICMPv6 ошибку, если ошибочный пакет не является "
+"фактически Джамбо-пакетом, то есть его размер пакета превышает 65 535 байт. "
+"Как описано выше, в настоящее время не поддерживаются физические интерфейсы "
+"с таким огромным MTU, поэтому ICMPv6 ошибка возвращается редко."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:417
+msgid ""
+"TCP/UDP over jumbogram is not supported at this moment. This is because we "
+"have no medium (other than loopback) to test this. Contact us if you need "
+"this."
+msgstr ""
+"В настоящее время поддержка TCP/UDP через jumbogram не реализована. Это "
+"связано с отсутствием среды (кроме loopback) для тестирования данной "
+"функциональности. Свяжитесь с нами, если вам это необходимо."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:420
+msgid ""
+"IPsec does not work on jumbograms. This is due to some specification twists "
+"in supporting AH with jumbograms (AH header size influences payload length, "
+"and this makes it real hard to authenticate inbound packet with jumbo "
+"payload option as well as AH)."
+msgstr ""
+"IPsec не работает с jumbogram. Это связано с особенностями спецификации, "
+"касающимися поддержки AH для джамбограмм (размер заголовка AH влияет на "
+"длину полезной нагрузки, что делает крайне сложной аутентификацию входящего "
+"пакета с опцией Джамбо-пакет, а также AH)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:424
+msgid ""
+"There are fundamental issues in *BSD support for jumbograms. We would like "
+"to address those, but we need more time to finalize these. To name a few:"
+msgstr ""
+"Существуют фундаментальные проблемы в поддержке *BSD для jumbogram. Мы "
+"хотели бы решить их, но нам нужно больше времени для завершения работы. Вот "
+"некоторые из них:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:428
+msgid ""
+"mbuf pkthdr.len field is typed as \"int\" in 4.4BSD, so it will not hold "
+"jumbogram with len > 2G on 32bit architecture CPUs. If we would like to "
+"support jumbogram properly, the field must be expanded to hold 4G + IPv6 "
+"header + link-layer header. Therefore, it must be expanded to at least "
+"int64_t (u_int32_t is NOT enough)."
+msgstr ""
+"Поле `mbuf pkthdr.len` имеет тип `int` в 4.4BSD, поэтому оно не сможет "
+"содержать джамбограмму с длиной > 2G на 32-битных архитектурах CPU. Если мы "
+"хотим правильно поддерживать джамбограммы, это поле необходимо расширить, "
+"чтобы оно могло содержать 4G + заголовок IPv6 + заголовок канального уровня. "
+"Следовательно, его необходимо расширить как минимум до `int64_t` "
+"(`u_int32_t` НЕ достаточно)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:430
+msgid ""
+"We mistakingly use \"int\" to hold packet length in many places. We need to "
+"convert them into larger integral type. It needs a great care, as we may "
+"experience overflow during packet length computation."
+msgstr ""
+"Мы ошибочно используем \"int\" для хранения длины пакета во многих местах. "
+"Нам необходимо преобразовать их в более крупный целочисленный тип. Это "
+"требует большой осторожности, так как мы можем столкнуться с переполнением "
+"во время вычисления длины пакета."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:431
+msgid ""
+"We mistakingly check for ip6_plen field of IPv6 header for packet payload "
+"length in various places. We should be checking mbuf pkthdr.len instead. "
+"ip6_input() will perform sanity check on jumbo payload option on input, and "
+"we can safely use mbuf pkthdr.len afterwards."
+msgstr ""
+"Мы ошибочно проверяем поле ip6_plen заголовка IPv6 для определения длины "
+"полезной нагрузки пакета в различных местах. Вместо этого следует проверять "
+"mbuf pkthdr.len. Функция ip6_input() выполняет проверку корректности опции "
+"Джамбо-пакет при вводе, и после этого можно безопасно использовать mbuf "
+"pkthdr.len."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:432
+msgid "TCP code needs a careful update in bunch of places, of course."
+msgstr "Код TCP требует тщательного обновления в ряде мест, разумеется."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:433
+#, no-wrap
+msgid "Loop Prevention in Header Processing"
+msgstr "Предотвращение петель при обработке заголовков"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:444
+msgid ""
+"IPv6 specification allows arbitrary number of extension headers to be placed "
+"onto packets. If we implement IPv6 packet processing code in the way BSD "
+"IPv4 code is implemented, kernel stack may overflow due to long function "
+"call chain. sys/netinet6 code is carefully designed to avoid kernel stack "
+"overflow, so sys/netinet6 code defines its own protocol switch structure, as "
+"\"struct ip6protosw\" (see [.filename]#netinet6/ip6protosw.h#). There is no "
+"such update to IPv4 part (sys/netinet) for compatibility, but small change "
+"is added to its pr_input() prototype. So \"struct ipprotosw\" is also "
+"defined. As a result, if you receive IPsec-over-IPv4 packet with massive "
+"number of IPsec headers, kernel stack may blow up. IPsec-over-IPv6 is "
+"okay. (Of-course, for those all IPsec headers to be processed, each such "
+"IPsec header must pass each IPsec check. So an anonymous attacker will not "
+"be able to do such an attack.)"
+msgstr ""
+"Спецификация IPv6 допускает размещение произвольного количества расширений в "
+"заголовках пакетов. Если реализовать код обработки пакетов IPv6 так, как "
+"реализован код IPv4 в BSD, может произойти переполнение стека ядра из-за "
+"длинной цепочки вызовов функций. Код в sys/netinet6 тщательно спроектирован, "
+"чтобы избежать переполнения стека ядра, поэтому он определяет собственную "
+"структуру переключения протоколов — \"struct ip6protosw\" (см. "
+"[.filename]#netinet6/ip6protosw.h#). Для IPv4 части (sys/netinet) подобных "
+"обновлений не было сделано для сохранения совместимости, но в прототип "
+"pr_input() внесено небольшое изменение. Поэтому также определена \"struct "
+"ipprotosw\". В результате, если получен пакет IPsec-over-IPv4 с большим "
+"количеством заголовков IPsec, стек ядра может переполниться. С IPsec-over-"
+"IPv6 такой проблемы нет. (Разумеется, чтобы все эти заголовки IPsec были "
+"обработаны, каждый такой заголовок должен пройти все проверки IPsec. Поэтому "
+"анонимный злоумышленник не сможет осуществить подобную атаку.)"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:446
+#, no-wrap
+msgid "ICMPv6"
+msgstr "ICMPv6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:450
+msgid ""
+"After RFC2463 was published, IETF ipngwg has decided to disallow ICMPv6 "
+"error packet against ICMPv6 redirect, to prevent ICMPv6 storm on a network "
+"medium. This is already implemented into the kernel."
+msgstr ""
+"После публикации RFC2463 IETF ipngwg решил запретить ICMPv6 пакеты ошибок "
+"для ICMPv6 перенаправлений, чтобы предотвратить ICMPv6 шторм в сетевой "
+"среде. Это уже реализовано в ядре."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:451
+#, no-wrap
+msgid "Applications"
+msgstr "Приложения (Applications)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:454
+msgid ""
+"For userland programming, we support IPv6 socket API as specified in "
+"RFC2553, RFC2292 and upcoming Internet drafts."
+msgstr ""
+"Для программирования в пользовательском пространстве мы поддерживаем API "
+"сокетов IPv6, как указано в RFC2553, RFC2292 и готовящихся интернет-"
+"черновиках."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:459
+msgid ""
+"TCP/UDP over IPv6 is available and quite stable. You can enjoy "
+"man:telnet[1], man:ftp[1], man:rlogin[1], man:rsh[1], man:ssh[1], etc. "
+"These applications are protocol independent. That is, they automatically "
+"chooses IPv4 or IPv6 according to DNS."
+msgstr ""
+"TCP/UDP поверх IPv6 доступны и достаточно стабильны. Вы можете использовать "
+"man:telnet[1], man:ftp[1], man:rlogin[1], man:rsh[1], man:ssh[1] и т.д. Эти "
+"приложения не зависят от протокола. То есть они автоматически выбирают IPv4 "
+"или IPv6 в соответствии с DNS."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:460
+#, no-wrap
+msgid "Kernel Internals"
+msgstr "Внутреннее устройство ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:463
+msgid ""
+"While ip_forward() calls ip_output(), ip6_forward() directly calls "
+"if_output() since routers must not divide IPv6 packets into fragments."
+msgstr ""
+"В то время как ip_forward() вызывает ip_output(), ip6_forward() напрямую "
+"вызывает if_output(), поскольку маршрутизаторы не должны разделять пакеты "
+"IPv6 на фрагменты."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:467
+msgid ""
+"ICMPv6 should contain the original packet as long as possible up to 1280. "
+"UDP6/IP6 port unreach, for instance, should contain all extension headers "
+"and the *unchanged* UDP6 and IP6 headers. So, all IP6 functions except TCP "
+"never convert network byte order into host byte order, to save the original "
+"packet."
+msgstr ""
+"ICMPv6 должен содержать исходный пакет по возможности вплоть до 1280 байт. "
+"Например, сообщение \"Ошибка недоступности порта UDP6/IP6\" должно содержать "
+"все расширенные заголовки и *неизменённые* заголовки UDP6 и IP6. Таким "
+"образом, все функции IP6, кроме TCP, никогда не преобразуют порядок байтов "
+"сети в порядок байтов хоста, чтобы сохранить исходный пакет."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:471
+msgid ""
+"tcp_input(), udp6_input() and icmp6_input() can not assume that IP6 header "
+"is preceding the transport headers due to extension headers. So, "
+"in6_cksum() was implemented to handle packets whose IP6 header and transport "
+"header is not continuous. TCP/IP6 nor UDP6/IP6 header structures do not "
+"exist for checksum calculation."
+msgstr ""
+"Функции tcp_input(), udp6_input() и icmp6_input() не могут предполагать, что "
+"заголовок IP6 предшествует транспортным заголовкам из-за наличия расширенных "
+"заголовков. Поэтому была реализована in6_cksum() для обработки пакетов, у "
+"которых заголовок IP6 и транспортный заголовок не являются непрерывными. Но "
+"ни для TCP/IP6, ни для UDP6/IP6 в заголовке нет структуры для расчёта "
+"контрольной суммы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:474
+msgid ""
+"To process IP6 header, extension headers and transport headers easily, "
+"network drivers are now required to store packets in one internal mbuf or "
+"one or more external mbufs. A typical old driver prepares two internal "
+"mbufs for 96 - 204 bytes data, however, now such packet data is stored in "
+"one external mbuf."
+msgstr ""
+"Для удобной обработки заголовка IP6, дополнительных заголовков и "
+"транспортных заголовков, от сетевых драйверов теперь требуется хранить "
+"пакеты в одном внутреннем mbuf или одном или нескольких внешних mbuf. "
+"Типичный старый драйвер подготавливает два внутренних mbuf для данных "
+"размером 96–204 байт, однако теперь такие данные пакета хранятся в одном "
+"внешнем mbuf."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:478
+msgid ""
+"`netstat -s -p ip6` tells you whether or not your driver conforms such "
+"requirement. In the following example, \"cce0\" violates the requirement. "
+"(For more information, refer to Section 2.)"
+msgstr ""
+"`netstat -s -p ip6` показывает, соответствует ли ваш драйвер этому "
+"требованию. В следующем примере \"cce0\" нарушает это требование. (Для "
+"получения дополнительной информации обратитесь к разделу 2.)"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:488
+#, no-wrap
+msgid ""
+"Mbuf statistics:\n"
+" 317 one mbuf\n"
+" two or more mbuf::\n"
+" lo0 = 8\n"
+"\t\t\tcce0 = 10\n"
+" 3282 one ext mbuf\n"
+" 0 two or more ext mbuf\n"
+msgstr ""
+"Mbuf statistics:\n"
+" 317 one mbuf\n"
+" two or more mbuf::\n"
+" lo0 = 8\n"
+"\t\t\tcce0 = 10\n"
+" 3282 one ext mbuf\n"
+" 0 two or more ext mbuf\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:493
+msgid ""
+"Each input function calls IP6_EXTHDR_CHECK in the beginning to check if the "
+"region between IP6 and its header is continuous. IP6_EXTHDR_CHECK calls "
+"m_pullup() only if the mbuf has M_LOOP flag, that is, the packet comes from "
+"the loopback interface. m_pullup() is never called for packets coming from "
+"physical network interfaces."
+msgstr ""
+"Каждая входная функция вызывает IP6_EXTHDR_CHECK в начале, чтобы проверить, "
+"является ли область между IP6 и его заголовком непрерывной. IP6_EXTHDR_CHECK "
+"вызывает m_pullup() только если mbuf имеет флаг M_LOOP, то есть пакет пришел "
+"с интерфейса loopback. m_pullup() никогда не вызывается для пакетов, "
+"приходящих с физических сетевых интерфейсов."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:495
+msgid "Both IP and IP6 reassemble functions never call m_pullup()."
+msgstr ""
+"Как функции повторной сборки IP, так и IP6 никогда не вызывают m_pullup()."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:497
+#, no-wrap
+msgid "IPv4 Mapped Address and IPv6 Wildcard Socket"
+msgstr "IPv4-отображённые адреса и IPv6-сокет с подстановочным адресом"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:501
+msgid ""
+"RFC2553 describes IPv4 mapped address (3.7) and special behavior of IPv6 "
+"wildcard bind socket (3.8). The spec allows you to:"
+msgstr ""
+"RFC2553 описывает IPv4 отображённые адреса (3.7) и особое поведение IPv6 "
+"сокета с привязкой к любому адресу (3.8). Спецификация позволяет вам:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:503
+msgid "Accept IPv4 connections by AF_INET6 wildcard bind socket."
+msgstr ""
+"Принимать IPv4-подключения через сокет с привязкой к подстановочному адресу "
+"AF_INET6."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:504
+msgid ""
+"Transmit IPv4 packet over AF_INET6 socket by using special form of the "
+"address like ::ffff:10.1.1.1."
+msgstr ""
+"Передача IPv4-пакета через сокет AF_INET6 с использованием специальной формы "
+"адреса, например ::ffff:10.1.1.1."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:507
+msgid ""
+"but the spec itself is very complicated and does not specify how the socket "
+"layer should behave. Here we call the former one \"listening side\" and the "
+"latter one \"initiating side\", for reference purposes."
+msgstr ""
+"но сама спецификация очень сложна и не определяет, как должен вести себя "
+"сокетный уровень. Здесь мы называем первую сторону «слушающей», а вторую — "
+"«инициирующей» для удобства ссылок."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:509
+msgid ""
+"You can perform wildcard bind on both of the address families, on the same "
+"port."
+msgstr ""
+"Вы можете выполнить привязку к подстановочному адресу для обоих семейств "
+"адресов на одном и том же порту."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:511
+msgid "The following table show the behavior of FreeBSD 4.x."
+msgstr "Следующая таблица показывает поведение FreeBSD 4.x."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:520
+#, no-wrap
+msgid ""
+"listening side initiating side\n"
+" (AF_INET6 wildcard (connection to ::ffff:10.1.1.1)\n"
+" socket gets IPv4 conn.)\n"
+" --- ---\n"
+"FreeBSD 4.x configurable supported\n"
+" default: enabled\n"
+msgstr ""
+"listening side initiating side\n"
+" (AF_INET6 wildcard (connection to ::ffff:10.1.1.1)\n"
+" socket gets IPv4 conn.)\n"
+" --- ---\n"
+"FreeBSD 4.x configurable supported\n"
+" default: enabled\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:523
+msgid ""
+"The following sections will give you more details, and how you can configure "
+"the behavior."
+msgstr ""
+"Следующие разделы предоставят вам более подробную информацию и объяснят, как "
+"можно настроить поведение."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:525
+msgid "Comments on listening side:"
+msgstr "Комментарии о принимающей стороне:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:532
+msgid ""
+"It looks that RFC2553 talks too little on wildcard bind issue, especially on "
+"the port space issue, failure mode and relationship between AF_INET/INET6 "
+"wildcard bind. There can be several separate interpretation for this RFC "
+"which conform to it but behaves differently. So, to implement portable "
+"application you should assume nothing about the behavior in the kernel. "
+"Using man:getaddrinfo[3] is the safest way. Port number space and wildcard "
+"bind issues were discussed in detail on ipv6imp mailing list, in mid March "
+"1999 and it looks that there is no concrete consensus (means, up to "
+"implementers). You may want to check the mailing list archives."
+msgstr ""
+"Похоже, что в RFC2553 слишком мало сказано о проблеме привязки к "
+"подстановочному адресу, особенно о вопросе пространства портов, режиме "
+"отказа и взаимосвязи между AF_INET/INET6 wildcard bind. Может быть "
+"несколько различных интерпретаций этого RFC, которые соответствуют ему, но "
+"ведут себя по-разному. Поэтому для создания переносимых приложений не "
+"следует делать никаких предположений о поведении в ядре. Использование "
+"man:getaddrinfo[3] является наиболее безопасным способом. Вопросы "
+"пространства номеров портов и привязки к подстановочному адресу подробно "
+"обсуждались в рассылке ipv6imp в середине марта 1999 года, и похоже, что "
+"конкретного консенсуса нет (то есть, остается на усмотрение реализаторов). "
+"Возможно, вам стоит проверить архивы рассылки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:534
+msgid ""
+"If a server application would like to accept IPv4 and IPv6 connections, "
+"there will be two alternatives."
+msgstr ""
+"Если серверное приложение хочет принимать IPv4 и IPv6 соединения, есть два "
+"варианта."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:539
+msgid ""
+"One is using AF_INET and AF_INET6 socket (you will need two sockets). Use "
+"man:getaddrinfo[3] with AI_PASSIVE into ai_flags, and man:socket[2] and "
+"man:bind[2] to all the addresses returned. By opening multiple sockets, you "
+"can accept connections onto the socket with proper address family. IPv4 "
+"connections will be accepted by AF_INET socket, and IPv6 connections will be "
+"accepted by AF_INET6 socket."
+msgstr ""
+"Один из способов — использование сокетов AF_INET и AF_INET6 (вам понадобятся "
+"два сокета). Используйте man:getaddrinfo[3] с AI_PASSIVE в ai_flags, а также "
+"man:socket[2] и man:bind[2] для всех возвращённых адресов. Открыв несколько "
+"сокетов, вы можете принимать соединения сокетом соответствующей адресной "
+"семьи. IPv4-соединения будут приниматься сокетом AF_INET, а IPv6-соединения "
+"— сокетом AF_INET6."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:545
+msgid ""
+"Another way is using one AF_INET6 wildcard bind socket. Use "
+"man:getaddrinfo[3] with AI_PASSIVE into ai_flags and with AF_INET6 into "
+"ai_family, and set the 1st argument hostname to NULL. And man:socket[2] and "
+"man:bind[2] to the address returned. (should be IPv6 unspecified addr). "
+"You can accept either of IPv4 and IPv6 packet via this one socket."
+msgstr ""
+"Еще один способ — использование одного сокета с универсальной привязкой "
+"AF_INET6. Используйте man:getaddrinfo[3] с AI_PASSIVE в ai_flags и AF_INET6 "
+"в ai_family, установив первый аргумент hostname в NULL. Затем используйте "
+"man:socket[2] и man:bind[2] для адреса, который был возвращен. (должен быть "
+"неспецифицированный адрес IPv6). Через этот один сокет можно принимать "
+"пакеты как IPv4, так и IPv6."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:549
+msgid ""
+"To support only IPv6 traffic on AF_INET6 wildcard binded socket portably, "
+"always check the peer address when a connection is made toward AF_INET6 "
+"listening socket. If the address is IPv4 mapped address, you may want to "
+"reject the connection. You can check the condition by using "
+"IN6_IS_ADDR_V4MAPPED() macro."
+msgstr ""
+"Для поддержки только IPv6-трафика на AF_INET6-сокете с привязкой к любому "
+"адресу переносимым способом всегда проверяйте адрес узла при установке "
+"соединения с AF_INET6-сокетом в режиме прослушивания. Если адрес является "
+"IPv4-отображённым, возможно, стоит отклонить соединение. Это условие можно "
+"проверить с помощью макроса IN6_IS_ADDR_V4MAPPED()."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:551
+msgid ""
+"To resolve this issue more easily, there is system dependent "
+"man:setsockopt[2] option, IPV6_BINDV6ONLY, used like below."
+msgstr ""
+"Для более простого решения этой задачи существует зависящий от системы "
+"параметр man:setsockopt[2] под названием IPV6_BINDV6ONLY, используемый "
+"следующим образом."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:555
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:602
+#, no-wrap
+msgid "\tint on;\n"
+msgstr "\tint on;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:558
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:605
+#, no-wrap
+msgid ""
+"\tsetsockopt(s, IPPROTO_IPV6, IPV6_BINDV6ONLY,\n"
+"\t\t (char *)&on, sizeof (on)) < 0));\n"
+msgstr ""
+"\tsetsockopt(s, IPPROTO_IPV6, IPV6_BINDV6ONLY,\n"
+"\t\t (char *)&on, sizeof (on)) < 0));\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:561
+msgid "When this call succeed, then this socket only receive IPv6 packets."
+msgstr "При успешном вызове этот сокет будет принимать только IPv6-пакеты."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:563
+msgid "Comments on initiating side:"
+msgstr "Комментарии о стороне инициатора:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:565
+msgid ""
+"Advise to application implementers: to implement a portable IPv6 application "
+"(which works on multiple IPv6 kernels), we believe that the following is the "
+"key to the success:"
+msgstr ""
+"Совет разработчикам приложений: для создания переносимого IPv6-приложения "
+"(которое работает на различных IPv6-ядрах), мы считаем, что следующие "
+"моменты являются ключом к успеху:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:567
+msgid "NEVER hardcode AF_INET nor AF_INET6."
+msgstr "НИКОГДА не используйте жёстко заданные AF_INET или AF_INET6."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:568
+msgid ""
+"Use man:getaddrinfo[3] and man:getnameinfo[3] throughout the system. Never "
+"use gethostby*(), getaddrby*(), inet_*() or getipnodeby*(). (To update "
+"existing applications to be IPv6 aware easily, sometime getipnodeby*() will "
+"be useful. But if possible, try to rewrite the code to use "
+"man:getaddrinfo[3] and man:getnameinfo[3].)"
+msgstr ""
+"Используйте man:getaddrinfo[3] и man:getnameinfo[3] во всей системе. Никогда "
+"не используйте gethostby*(), getaddrby*(), inet_*() или getipnodeby*(). (Для "
+"облегчения обновления существующих приложений для поддержки IPv6 иногда "
+"может быть полезен getipnodeby*(). Но по возможности старайтесь переписать "
+"код для использования man:getaddrinfo[3] и man:getnameinfo[3].)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:569
+msgid ""
+"If you would like to connect to destination, use man:getaddrinfo[3] and try "
+"all the destination returned, like man:telnet[1] does."
+msgstr ""
+"Если вы хотите подключиться к назначению, используйте man:getaddrinfo[3] и "
+"попробуйте все возвращённые назначения, как это делает man:telnet[1]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:570
+msgid ""
+"Some of the IPv6 stack is shipped with buggy man:getaddrinfo[3]. Ship a "
+"minimal working version with your application and use that as last resort."
+msgstr ""
+"Некоторые реализации стека IPv6 поставляются с некорректной "
+"man:getaddrinfo[3]. Включите минимально рабочую версию в ваше приложение и "
+"используйте её в крайнем случае."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:576
+msgid ""
+"If you would like to use AF_INET6 socket for both IPv4 and IPv6 outgoing "
+"connection, you will need to use man:getipnodebyname[3]. When you would "
+"like to update your existing application to be IPv6 aware with minimal "
+"effort, this approach might be chosen. But please note that it is a "
+"temporal solution, because man:getipnodebyname[3] itself is not recommended "
+"as it does not handle scoped IPv6 addresses at all. For IPv6 name "
+"resolution, man:getaddrinfo[3] is the preferred API. So you should rewrite "
+"your application to use man:getaddrinfo[3], when you get the time to do it."
+msgstr ""
+"Если вы хотите использовать сокет AF_INET6 для исходящих подключений как "
+"IPv4, так и IPv6, вам потребуется использовать man:getipnodebyname[3]. Если "
+"вы хотите обновить существующее приложение для поддержки IPv6 с минимальными "
+"усилиями, можно выбрать этот подход. Однако учтите, что это временное "
+"решение, поскольку man:getipnodebyname[3] сам по себе не рекомендуется, так "
+"как он вообще не обрабатывает IPv6-адреса с зоной. Для разрешения IPv6-имён "
+"предпочтительным API является man:getaddrinfo[3]. Поэтому вам следует "
+"переписать ваше приложение для использования man:getaddrinfo[3], когда у вас "
+"будет время это сделать."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:580
+msgid ""
+"When writing applications that make outgoing connections, story goes much "
+"simpler if you treat AF_INET and AF_INET6 as totally separate address "
+"family. {set,get}sockopt issue goes simpler, DNS issue will be made "
+"simpler. We do not recommend you to rely upon IPv4 mapped address."
+msgstr ""
+"При написании приложений, которые устанавливают исходящие соединения, "
+"история становится намного проще, если рассматривать AF_INET и AF_INET6 как "
+"совершенно отдельные семейства адресов. Проблемы с {set,get}sockopt "
+"упрощаются, проблемы с DNS также станут проще. Мы не рекомендуем полагаться "
+"на IPv4-отображённые адреса."
+
+#. type: Title =====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:581
+#, no-wrap
+msgid "unified tcp and inpcb code"
+msgstr "унифицированный код tcp и inpcb"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:585
+msgid ""
+"FreeBSD 4.x uses shared tcp code between IPv4 and IPv6 (from sys/netinet/"
+"tcp*) and separate udp4/6 code. It uses unified inpcb structure."
+msgstr ""
+"FreeBSD 4.x использует общий код tcp для IPv4 и IPv6 (из sys/netinet/tcp*) и "
+"раздельный код udp4/6. В нем используется унифицированная структура inpcb."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:588
+msgid ""
+"The platform can be configured to support IPv4 mapped address. Kernel "
+"configuration is summarized as follows:"
+msgstr ""
+"Платформа может быть настроена для поддержки IPv4-отображённых адресов. "
+"Конфигурация ядра кратко описана ниже:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:590
+msgid ""
+"By default, AF_INET6 socket will grab IPv4 connections in certain condition, "
+"and can initiate connection to IPv4 destination embedded in IPv4 mapped IPv6 "
+"address."
+msgstr ""
+"По умолчанию сокет AF_INET6 может принимать IPv4-соединения при определённых "
+"условиях и инициировать соединение с IPv4-адресами, встроенными в IPv4-"
+"отображённые IPv6-адреса."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:591
+msgid "You can disable it on entire system with sysctl like below."
+msgstr ""
+"Вы можете отключить это во всей системе с помощью sysctl, как показано ниже."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:593
+msgid "`sysctl net.inet6.ip6.mapped_addr=0`"
+msgstr "`sysctl net.inet6.ip6.mapped_addr=0`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:595
+msgid "====== Listening Side"
+msgstr "====== Сторона, принимающая соединения"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:598
+msgid ""
+"Each socket can be configured to support special AF_INET6 wildcard bind "
+"(enabled by default). You can disable it on each socket basis with "
+"man:setsockopt[2] like below."
+msgstr ""
+"Каждый сокет может быть настроен для поддержки специальной привязки к "
+"подстановочному адресу AF_INET6 (включено по умолчанию). Это можно отключить "
+"для каждого отдельного сокета с помощью man:setsockopt[2], как показано ниже."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:608
+msgid ""
+"Wildcard AF_INET6 socket grabs IPv4 connection if and only if the following "
+"conditions are satisfied:"
+msgstr ""
+"Сокет с универсальной привязкой AF_INET6 перехватывает IPv4-подключение "
+"тогда и только тогда, когда выполнены следующие условия:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:610
+msgid "there is no AF_INET socket that matches the IPv4 connection"
+msgstr "нет AF_INET сокета, соответствующего IPv4-подключению"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:611
+msgid ""
+"the AF_INET6 socket is configured to accept IPv4 traffic, i.e., "
+"getsockopt(IPV6_BINDV6ONLY) returns 0."
+msgstr ""
+"Сокет AF_INET6 настроен на прием IPv4-трафика, т.е., "
+"getsockopt(IPV6_BINDV6ONLY) возвращает 0."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:613
+msgid "There is no problem with open/close ordering."
+msgstr "Нет проблем с порядком открытия/закрытия."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:615
+msgid "====== Initiating Side"
+msgstr "====== Инициирующая сторона"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:617
+msgid ""
+"FreeBSD 4.x supports outgoing connection to IPv4 mapped address "
+"(::ffff:10.1.1.1), if the node is configured to support IPv4 mapped address."
+msgstr ""
+"FreeBSD 4.x поддерживает исходящее соединение с IPv4-отображённым адресом "
+"(::ffff:10.1.1.1), если узел настроен на поддержку IPv4-отображённых адресов."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:618
+#, no-wrap
+msgid "sockaddr_storage"
+msgstr "sockaddr_storage"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:624
+msgid ""
+"When RFC2553 was about to be finalized, there was discussion on how struct "
+"sockaddr_storage members are named. One proposal is to prepend \"__\" to "
+"the members (like \"__ss_len\") as they should not be touched. The other "
+"proposal was not to prepend it (like \"ss_len\") as we need to touch those "
+"members directly. There was no clear consensus on it."
+msgstr ""
+"Когда RFC2553 был близок к завершению, велись дискуссии о том, как называть "
+"элементы структуры `sockaddr_storage`. Одно предложение заключалось в "
+"добавлении \"__\" перед именами элементов (например, \"__ss_len\"), так как "
+"к ним не следует обращаться напрямую. Другое предложение было не добавлять "
+"префикс (например, \"ss_len\"), поскольку необходимо прямое обращение к этим "
+"элементам. Четкого консенсуса по этому вопросу достигнуто не было."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:626
+msgid "As a result, RFC2553 defines struct sockaddr_storage as follows:"
+msgstr ""
+"В результате, RFC2553 определяет структуру sockaddr_storage следующим "
+"образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:634
+#, no-wrap
+msgid ""
+"\tstruct sockaddr_storage {\n"
+"\t\tu_char\t__ss_len;\t/* address length */\n"
+"\t\tu_char\t__ss_family;\t/* address family */\n"
+"\t\t/* and bunch of padding */\n"
+"\t};\n"
+msgstr ""
+"\tstruct sockaddr_storage {\n"
+"\t\tu_char\t__ss_len;\t/* address length */\n"
+"\t\tu_char\t__ss_family;\t/* address family */\n"
+"\t\t/* and bunch of padding */\n"
+"\t};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:637
+msgid "On the contrary, XNET draft defines as follows:"
+msgstr "Напротив, черновик XNET определяет следующее:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:645
+#, no-wrap
+msgid ""
+"\tstruct sockaddr_storage {\n"
+"\t\tu_char\tss_len;\t\t/* address length */\n"
+"\t\tu_char\tss_family;\t/* address family */\n"
+"\t\t/* and bunch of padding */\n"
+"\t};\n"
+msgstr ""
+"\tstruct sockaddr_storage {\n"
+"\t\tu_char\tss_len;\t\t/* address length */\n"
+"\t\tu_char\tss_family;\t/* address family */\n"
+"\t\t/* and bunch of padding */\n"
+"\t};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:648
+msgid ""
+"In December 1999, it was agreed that RFC2553bis should pick the latter "
+"(XNET) definition."
+msgstr ""
+"В декабре 1999 года было согласовано, что RFC2553bis должен принять "
+"последнее (XNET) определение."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:650
+msgid ""
+"Current implementation conforms to XNET definition, based on RFC2553bis "
+"discussion."
+msgstr ""
+"Текущая реализация соответствует определению XNET, основанному на обсуждении "
+"RFC2553bis."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:653
+msgid ""
+"If you look at multiple IPv6 implementations, you will be able to see both "
+"definitions. As an userland programmer, the most portable way of dealing "
+"with it is to:"
+msgstr ""
+"Если вы рассмотрите несколько реализаций IPv6, то сможете увидеть оба "
+"определения. Для программиста в пользовательском пространстве наиболее "
+"переносимый способ работы с этим:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:655
+msgid ""
+"ensure ss_family and/or ss_len are available on the platform, by using GNU "
+"autoconf,"
+msgstr ""
+"с помощью GNU autoconf сконфигурировать доступ к `ss_family` и/или `ss_len` "
+"на целевой платформе,"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:656
+msgid ""
+"have -Dss_family=__ss_family to unify all occurrences (including header "
+"file) into __ss_family, or"
+msgstr ""
+"добавить -Dss_family=__ss_family для унификации всех использований (включая "
+"заголовочный файл) __ss_family, или"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:657
+msgid "never touch __ss_family. cast to sockaddr * and use sa_family like:"
+msgstr ""
+"никогда не трогайте __ss_family. Приводите к sockaddr * и используйте "
+"sa_family, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:662
+#, no-wrap
+msgid ""
+"\tstruct sockaddr_storage ss;\n"
+"\tfamily = ((struct sockaddr *)&ss)->sa_family\n"
+msgstr ""
+"\tstruct sockaddr_storage ss;\n"
+"\tfamily = ((struct sockaddr *)&ss)->sa_family\n"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:664
+#, no-wrap
+msgid "Network Drivers"
+msgstr "Драйверы сетевых устройств"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:667
+msgid ""
+"Now following two items are required to be supported by standard drivers:"
+msgstr ""
+"В настоящее время следующие два пункта должны поддерживаться стандартными "
+"драйверами:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:669
+msgid ""
+"mbuf clustering requirement. In this stable release, we changed MINCLSIZE "
+"into MHLEN+1 for all the operating systems in order to make all the drivers "
+"behave as we expect."
+msgstr ""
+"Требование к кластеризации mbuf. В этом стабильном выпуске мы изменили "
+"MINCLSIZE на MHLEN+1 для всех операционных систем, чтобы все драйверы "
+"работали так, как мы ожидаем."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:670
+msgid ""
+"multicast. If man:ifmcstat[8] yields no multicast group for a interface, "
+"that interface has to be patched."
+msgstr ""
+"многоадресная рассылка (multicast). Если man:ifmcstat[8] не выводит ни одной "
+"многоадресной группы для интерфейса, этот интерфейс необходимо исправить."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:673
+msgid ""
+"If any of the drivers do not support the requirements, then the drivers "
+"cannot be used for IPv6 and/or IPsec communication. If you find any problem "
+"with your card using IPv6/IPsec, then, please report it to the {freebsd-"
+"bugs}."
+msgstr ""
+"Если какие-либо драйверы не поддерживают требования, то их нельзя "
+"использовать для IPv6 и/или IPsec-связи. Если вы обнаружили проблему с вашей "
+"картой при использовании IPv6/IPsec, пожалуйста, сообщите об этом в {freebsd-"
+"bugs}."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:676
+msgid ""
+"(NOTE: In the past we required all PCMCIA drivers to have a call to "
+"in6_ifattach(). We have no such requirement any more)"
+msgstr ""
+"(NOTE: Раньше мы требовали, чтобы все драйверы PCMCIA содержали вызов "
+"in6_ifattach(). Теперь такого требования нет)"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:677
+#, no-wrap
+msgid "Translator"
+msgstr "Транслятор"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:680
+msgid "We categorize IPv4/IPv6 translator into 4 types:"
+msgstr "Мы классифицируем трансляторы IPv4/IPv6 на 4 типа:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:682
+msgid ""
+"_Translator A_ --- It is used in the early stage of transition to make it "
+"possible to establish a connection from an IPv6 host in an IPv6 island to an "
+"IPv4 host in the IPv4 ocean."
+msgstr ""
+"_Транслятор А_ --- Он используется на раннем этапе перехода, чтобы позволить "
+"установить соединение с IPv6-хоста на IPv6-острове к IPv4-хосту в IPv4-"
+"океане."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:683
+msgid ""
+"_Translator B_ --- It is used in the early stage of transition to make it "
+"possible to establish a connection from an IPv4 host in the IPv4 ocean to an "
+"IPv6 host in an IPv6 island."
+msgstr ""
+"_Транслятор Б_ --- Он используется на раннем этапе перехода, чтобы "
+"обеспечить возможность установления соединения с IPv6-узлом на IPv6-острове "
+"от IPv4-узла в IPv4-океане."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:684
+msgid ""
+"_Translator C_ --- It is used in the late stage of transition to make it "
+"possible to establish a connection from an IPv4 host in an IPv4 island to an "
+"IPv6 host in the IPv6 ocean."
+msgstr ""
+"_Транслятор C_ --- Он используется на позднем этапе перехода, чтобы сделать "
+"возможным установление соединения с IPv6-узлом в IPv6-океане от IPv4-узла на "
+"IPv4-острове."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:685
+msgid ""
+"_Translator D_ --- It is used in the late stage of transition to make it "
+"possible to establish a connection from an IPv6 host in the IPv6 ocean to an "
+"IPv4 host in an IPv4 island."
+msgstr ""
+"_Транслятор D_ --- Он используется на позднем этапе перехода, чтобы сделать "
+"возможным установление соединения с IPv6-хоста в IPv6-океане на IPv4-хост на "
+"IPv4-острове."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:687
+#, no-wrap
+msgid "IPsec"
+msgstr "IPsec"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:690
+msgid "IPsec is mainly organized by three components."
+msgstr "IPsec состоит в основном из трех компонент."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:692
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:695
+#, no-wrap
+msgid "Policy Management"
+msgstr "Управление политиками"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:693
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:705
+#, no-wrap
+msgid "Key Management"
+msgstr "Управление ключами"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:694
+msgid "AH and ESP handling"
+msgstr "Обработка AH и ESP"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:702
+msgid ""
+"The kernel implements experimental policy management code. There are two "
+"way to manage security policy. One is to configure per-socket policy using "
+"man:setsockopt[2]. In this cases, policy configuration is described in "
+"man:ipsec_set_policy[3]. The other is to configure kernel packet filter-"
+"based policy using PF_KEY interface, via man:setkey[8]."
+msgstr ""
+"Ядро реализует экспериментальный код управления политикой безопасности. "
+"Существует два способа управления политикой безопасности. Первый — настройка "
+"политики для каждого сокета с помощью man:setsockopt[2]. В этом случае "
+"конфигурация политики описана в man:ipsec_set_policy[3]. Второй способ — "
+"настройка политики на основе фильтра пакетов ядра с использованием "
+"интерфейса PF_KEY через man:setkey[8]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:704
+msgid ""
+"The policy entry is not re-ordered with its indexes, so the order of entry "
+"when you add is very significant."
+msgstr ""
+"Запись политики не переупорядочивается вместе со своими индексами, поэтому "
+"порядок добавления записей очень важен."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:709
+msgid ""
+"The key management code implemented in this kit (sys/netkey) is a home-brew "
+"PFKEY v2 implementation. This conforms to RFC2367."
+msgstr ""
+"Код управления ключами, реализованный в этом наборе (sys/netkey), "
+"представляет собой собственную реализацию PFKEY v2. Это соответствует "
+"RFC2367."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:713
+msgid ""
+"The home-brew IKE daemon, \"racoon\" is included in the kit (kame/kame/"
+"racoon). Basically you will need to run racoon as daemon, then set up a "
+"policy to require keys (like `ping -P 'out ipsec esp/transport//use'`). The "
+"kernel will contact racoon daemon as necessary to exchange keys."
+msgstr ""
+"В комплект включена \"домашняя\" реализация демона IKE — \"racoon\" (kame/"
+"kame/racoon). Обычно вам потребуется запустить racoon в качестве демона, "
+"затем настроить политику для требования ключей (например, `ping -P 'out "
+"ipsec esp/transport//use'`). Ядро будет связываться с демоном racoon по мере "
+"необходимости для обмена ключами."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:714
+#, no-wrap
+msgid "AH and ESP Handling"
+msgstr "Обработка AH и ESP"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:722
+msgid ""
+"IPsec module is implemented as \"hooks\" to the standard IPv4/IPv6 "
+"processing. When sending a packet, ip{,6}_output() checks if ESP/AH "
+"processing is required by checking if a matching SPD (Security Policy "
+"Database) is found. If ESP/AH is needed, {esp,ah}{4,6}_output() will be "
+"called and mbuf will be updated accordingly. When a packet is received, "
+"{esp,ah}4_input() will be called based on protocol number, i.e., "
+"(*inetsw[proto])(). {esp,ah}4_input() will decrypt/check authenticity of "
+"the packet, and strips off daisy-chained header and padding for ESP/AH. It "
+"is safe to strip off the ESP/AH header on packet reception, since we will "
+"never use the received packet in \"as is\" form."
+msgstr ""
+"Модуль IPsec реализован в виде \"хуков\" к стандартной обработке IPv4/IPv6. "
+"При отправке пакета функция ip{,6}_output() проверяет, требуется ли "
+"обработка ESP/AH, путем поиска соответствующей базы данных политик "
+"безопасности (SPD — Security Policy Database). Если ESP/AH необходим, "
+"вызывается {esp,ah}{4,6}_output(), и mbuf соответствующим образом "
+"обновляется. При получении пакета функция {esp,ah}4_input() вызывается на "
+"основе номера протокола, т.е. (*inetsw[proto])(). {esp,ah}4_input() "
+"расшифровывает/проверяет подлинность пакета, а также удаляет цепочку "
+"заголовков и выравнивание для ESP/AH. Безопасно удалять заголовок ESP/AH при "
+"получении пакета, так как полученный пакет никогда не будет использоваться в "
+"\"сыром\" виде."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:725
+msgid ""
+"By using ESP/AH, TCP4/6 effective data segment size will be affected by "
+"extra daisy-chained headers inserted by ESP/AH. Our code takes care of the "
+"case."
+msgstr ""
+"Использование ESP/AH влияет на эффективный размер сегмента данных TCP4/6 из-"
+"за дополнительных цепочечных заголовков, вставляемых ESP/AH. Наш код "
+"учитывает этот случай."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:729
+msgid ""
+"Basic crypto functions can be found in directory \"sys/crypto\". ESP/AH "
+"transform are listed in {esp,ah}_core.c with wrapper functions. If you wish "
+"to add some algorithm, add wrapper function in {esp,ah}_core.c, and add your "
+"crypto algorithm code into sys/crypto."
+msgstr ""
+"Основные криптографические функции можно найти в директории `sys/crypto`. "
+"Преобразования ESP/AH перечислены в `{esp,ah}_core.c` с обёрточными "
+"функциями. Если вы хотите добавить какой-либо алгоритм, добавьте обёрточную "
+"функцию в `{esp,ah}_core.c` и поместите код вашего криптографического "
+"алгоритма в `sys/crypto`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:731
+msgid ""
+"Tunnel mode is partially supported in this release, with the following "
+"restrictions:"
+msgstr ""
+"Режим туннеля частично поддерживается в этом выпуске со следующими "
+"ограничениями:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:733
+msgid ""
+"IPsec tunnel is not combined with GIF generic tunneling interface. It needs "
+"a great care because we may create an infinite loop between ip_output() and "
+"tunnelifp->if_output(). Opinion varies if it is better to unify them, or not."
+msgstr ""
+"Туннель IPsec не объединён с универсальным туннельным интерфейсом GIF. Это "
+"требует особой осторожности, так как может возникнуть бесконечный цикл между "
+"`ip_output()` и `tunnelifp->if_output()`. Мнения расходятся относительно "
+"того, лучше ли их объединить или нет."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:734
+msgid ""
+"MTU and Don't Fragment bit (IPv4) considerations need more checking, but "
+"basically works fine."
+msgstr ""
+"MTU и бит Don't Fragment (IPv4) требуют дополнительной проверки, но в "
+"основном работают нормально."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:735
+msgid ""
+"Authentication model for AH tunnel must be revisited. We will need to "
+"improve the policy management engine, eventually."
+msgstr ""
+"Модель аутентификации для туннеля AH должна быть пересмотрена. Нам "
+"потребуется улучшить механизм управления политиками в конечном итоге."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:736
+#, no-wrap
+msgid "Conformance to RFCs and IDs"
+msgstr "Соответствие RFC и ID"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:739
+msgid ""
+"The IPsec code in the kernel conforms (or, tries to conform) to the "
+"following standards:"
+msgstr ""
+"Код IPsec в ядре соответствует (или пытается соответствовать) следующим "
+"стандартам:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:741
+msgid "\"old IPsec\" specification documented in [.filename]#rfc182[5-9].txt#"
+msgstr ""
+"Спецификация \"старого IPsec\", описанная в [.filename]#rfc182[5-9].txt#"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:744
+msgid ""
+"\"new IPsec\" specification documented in [.filename]#rfc240[1-6].txt#, "
+"[.filename]#rfc241[01].txt#, [.filename]#rfc2451.txt# and [.filename]#draft-"
+"mcdonald-simple-ipsec-api-01.txt# (draft expired, but you can take from "
+"link:ftp://ftp.kame.net/pub/internet-drafts/[ ftp://ftp.kame.net/pub/"
+"internet-drafts/]). (NOTE: IKE specifications, [.filename]#rfc241[7-9].txt# "
+"are implemented in userland, as \"racoon\" IKE daemon)"
+msgstr ""
+"Спецификация \"new IPsec\" описана в [.filename]#rfc240[1-6].txt#, "
+"[.filename]#rfc241[01].txt#, [.filename]#rfc2451.txt# и [.filename]#draft-"
+"mcdonald-simple-ipsec-api-01.txt# (черновик устарел, но его можно взять по "
+"ссылке: link:ftp://ftp.kame.net/pub/internet-drafts/[ ftp://ftp.kame.net/pub/"
+"internet-drafts/]). (ПРИМЕЧАНИЕ: Спецификации IKE, "
+"[.filename]#rfc241[7-9].txt#, реализованы в пользовательском пространстве в "
+"виде демона IKE \"racoon\")"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:746
+msgid "Currently supported algorithms are:"
+msgstr "В настоящее время поддерживаются следующие алгоритмы:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:748
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:785
+msgid "old IPsec AH"
+msgstr "old IPsec AH"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:750
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:763
+msgid "null crypto checksum (no document, just for debugging)"
+msgstr ""
+"нулевая криптографическая контрольная сумма (нет документа, только для "
+"отладки)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:751
+msgid "keyed MD5 with 128bit crypto checksum ([.filename]#rfc1828.txt#)"
+msgstr ""
+"MD5 с ключом и с 128-битной криптографической контрольной суммой "
+"([.filename]#rfc1828.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:752
+msgid "keyed SHA1 with 128bit crypto checksum (no document)"
+msgstr ""
+"SHA1 с ключом и с 128-битной криптографической контрольной суммой (без "
+"документа)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:753
+msgid "HMAC MD5 with 128bit crypto checksum ([.filename]#rfc2085.txt#)"
+msgstr ""
+"HMAC MD5 с 128-битной криптографической контрольной суммой "
+"([.filename]#rfc2085.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:754
+msgid "HMAC SHA1 with 128bit crypto checksum (no document)"
+msgstr ""
+"HMAC SHA1 с 128-битной криптографической контрольной суммой (без документа)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:756
+msgid "old IPsec ESP"
+msgstr "old IPsec ESP"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:758
+msgid "null encryption (no document, similar to [.filename]#rfc2410.txt#)"
+msgstr ""
+"нулевое шифрование (нет документа, аналогично [.filename]#rfc2410.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:759
+msgid "DES-CBC mode ([.filename]#rfc1829.txt#)"
+msgstr "Режим DES-CBC ([.filename]#rfc1829.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:761
+msgid "new IPsec AH"
+msgstr "new IPsec AH"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:764
+msgid "keyed MD5 with 96bit crypto checksum (no document)"
+msgstr ""
+"MD5 с ключом и с 96-битной криптографической контрольной суммой (нет "
+"документа)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:765
+msgid "keyed SHA1 with 96bit crypto checksum (no document)"
+msgstr ""
+"SHA1 с ключом и с 96-битной криптографической контрольной суммой (без "
+"документа)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:766
+msgid "HMAC MD5 with 96bit crypto checksum ([.filename]#rfc2403.txt#)"
+msgstr ""
+"HMAC MD5 с 96-битной криптографической контрольной суммой "
+"([.filename]#rfc2403.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:767
+msgid "HMAC SHA1 with 96bit crypto checksum ([.filename]#rfc2404.txt#)"
+msgstr ""
+"HMAC SHA1 с 96-битной криптографической контрольной суммой "
+"([.filename]#rfc2404.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:769
+msgid "new IPsec ESP"
+msgstr "new IPsec ESP"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:771
+msgid "null encryption ([.filename]#rfc2410.txt#)"
+msgstr "нулевое шифрование ([.filename]#rfc2410.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:772
+msgid ""
+"DES-CBC with derived IV ([.filename]#draft-ietf-ipsec-ciph-des-"
+"derived-01.txt#, draft expired)"
+msgstr ""
+"DES-CBC с производным IV ([.filename]#draft-ietf-ipsec-ciph-des-"
+"derived-01.txt#, черновик истек)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:773
+msgid "DES-CBC with explicit IV ([.filename]#rfc2405.txt#)"
+msgstr "DES-CBC с явным вектором инициализации ([.filename]#rfc2405.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:774
+msgid "3DES-CBC with explicit IV ([.filename]#rfc2451.txt#)"
+msgstr "3DES-CBC с явным вектором инициализации ([.filename]#rfc2451.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:775
+msgid "BLOWFISH CBC ([.filename]#rfc2451.txt#)"
+msgstr "BLOWFISH CBC ([.filename]#rfc2451.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:776
+msgid "CAST128 CBC ([.filename]#rfc2451.txt#)"
+msgstr "CAST128 CBC ([.filename]#rfc2451.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:777
+msgid "RC5 CBC ([.filename]#rfc2451.txt#)"
+msgstr "RC5 CBC ([.filename]#rfc2451.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:778
+msgid "each of the above can be combined with:"
+msgstr "каждый из вышеперечисленных может быть объединён с:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:780
+msgid "ESP authentication with HMAC-MD5(96bit)"
+msgstr "Аутентификация ESP с HMAC-MD5 (96 бит)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:781
+msgid "ESP authentication with HMAC-SHA1(96bit)"
+msgstr "Аутентификация ESP с HMAC-SHA1(96 бит)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:783
+msgid "The following algorithms are NOT supported:"
+msgstr "Следующие алгоритмы НЕ поддерживаются:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:787
+msgid ""
+"HMAC MD5 with 128bit crypto checksum + 64bit replay prevention "
+"([.filename]#rfc2085.txt#)"
+msgstr ""
+"HMAC MD5 с 128-битной криптографической контрольной суммой + 64-битная "
+"защита от повторного воспроизведения ([.filename]#rfc2085.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:788
+msgid ""
+"keyed SHA1 with 160bit crypto checksum + 32bit padding "
+"([.filename]#rfc1852.txt#)"
+msgstr ""
+"SHA1 с ключом и с 160-битной криптографической контрольной суммой + 32-"
+"битное дополнение ([.filename]#rfc1852.txt#)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:791
+msgid ""
+"IPsec (in kernel) and IKE (in userland as \"racoon\") has been tested at "
+"several interoperability test events, and it is known to interoperate with "
+"many other implementations well. Also, current IPsec implementation as "
+"quite wide coverage for IPsec crypto algorithms documented in RFC (we cover "
+"algorithms without intellectual property issues only)."
+msgstr ""
+"IPsec (в ядре) и IKE (в пользовательском пространстве как \"racoon\") были "
+"протестированы на нескольких мероприятиях по тестированию взаимодействия и "
+"известно, что они хорошо работают со многими другими реализациями. Кроме "
+"того, текущая реализация IPsec поддерживает довольно широкий спектр "
+"криптографических алгоритмов IPsec, описанных в RFC (мы поддерживаем только "
+"алгоритмы без проблем с интеллектуальной собственностью)."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:793
+#, no-wrap
+msgid "ECN Consideration on IPsec Tunnels"
+msgstr "Учет ECN в IPsec-туннелях"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:796
+msgid ""
+"ECN-friendly IPsec tunnel is supported as described in [.filename]#draft-"
+"ipsec-ecn-00.txt#."
+msgstr ""
+"Поддерживается ECN-совместимый IPsec-туннель, как описано в "
+"[.filename]#draft-ipsec-ecn-00.txt#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:801
+msgid ""
+"Normal IPsec tunnel is described in RFC2401. On encapsulation, IPv4 TOS "
+"field (or, IPv6 traffic class field) will be copied from inner IP header to "
+"outer IP header. On decapsulation outer IP header will be simply dropped. "
+"The decapsulation rule is not compatible with ECN, since ECN bit on the "
+"outer IP TOS/traffic class field will be lost."
+msgstr ""
+"Обычный IPsec-туннель описан в RFC2401. При инкапсуляции поле TOS IPv4 (или "
+"поле класса трафика IPv6) копируется из внутреннего IP-заголовка во внешний "
+"IP-заголовок. При декапсуляции внешний IP-заголовок просто отбрасывается. "
+"Правило декапсуляции несовместимо с ECN, так как бит ECN в поле TOS/класса "
+"трафика внешнего IP-заголовка будет потерян."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:804
+msgid ""
+"To make IPsec tunnel ECN-friendly, we should modify encapsulation and "
+"decapsulation procedure. This is described in http://www.aciri.org/floyd/"
+"papers/draft-ipsec-ecn-00.txt[ http://www.aciri.org/floyd/papers/draft-ipsec-"
+"ecn-00.txt], chapter 3."
+msgstr ""
+"Чтобы сделать IPsec-туннель дружественным к ECN, следует изменить процедуры "
+"инкапсуляции и декапсуляции. Это описано в http://www.aciri.org/floyd/papers/"
+"draft-ipsec-ecn-00.txt[ http://www.aciri.org/floyd/papers/draft-ipsec-"
+"ecn-00.txt], глава 3."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:806
+msgid ""
+"IPsec tunnel implementation can give you three behaviors, by setting "
+"net.inet.ipsec.ecn (or net.inet6.ipsec6.ecn) to some value:"
+msgstr ""
+"Реализация туннеля IPsec может обеспечить три варианта поведения, в "
+"зависимости от значения параметра `net.inet.ipsec.ecn` (или "
+"`net.inet6.ipsec6.ecn`):"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:808
+msgid "RFC2401: no consideration for ECN (sysctl value -1)"
+msgstr "RFC2401: отсутствие учета ECN (значение sysctl -1)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:809
+msgid "ECN forbidden (sysctl value 0)"
+msgstr "ECN запрещён (значение sysctl 0)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:810
+msgid "ECN allowed (sysctl value 1)"
+msgstr "ECN разрешён (значение sysctl 1)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:812
+msgid ""
+"Note that the behavior is configurable in per-node manner, not per-SA manner "
+"(draft-ipsec-ecn-00 wants per-SA configuration, but it looks too much for "
+"me)."
+msgstr ""
+"Обратите внимание, что поведение настраивается для каждого узла, а не для "
+"каждой SA (в draft-ipsec-ecn-00 предлагается настройка для каждой SA, но это "
+"кажется излишним)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:814
+msgid ""
+"The behavior is summarized as follows (see source code for more detail):"
+msgstr ""
+"Поведение можно обобщить следующим образом (подробности см. в исходном коде):"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:821
+#, no-wrap
+msgid ""
+"encapsulate decapsulate\n"
+" --- ---\n"
+"RFC2401 copy all TOS bits drop TOS bits on outer\n"
+" from inner to outer. (use inner TOS bits as is)\n"
+msgstr ""
+"encapsulate decapsulate\n"
+" --- ---\n"
+"RFC2401 copy all TOS bits drop TOS bits on outer\n"
+" from inner to outer. (use inner TOS bits as is)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:825
+#, no-wrap
+msgid ""
+"ECN forbidden copy TOS bits except for ECN drop TOS bits on outer\n"
+" (masked with 0xfc) from inner (use inner TOS bits as is)\n"
+" to outer. set ECN bits to 0.\n"
+msgstr ""
+"ECN forbidden copy TOS bits except for ECN drop TOS bits on outer\n"
+" (masked with 0xfc) from inner (use inner TOS bits as is)\n"
+" to outer. set ECN bits to 0.\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:830
+#, no-wrap
+msgid ""
+"ECN allowed copy TOS bits except for ECN use inner TOS bits with some\n"
+" CE (masked with 0xfe) from change. if outer ECN CE bit\n"
+" inner to outer. is 1, enable ECN CE bit on\n"
+" set ECN CE bit to 0. the inner.\n"
+msgstr ""
+"ECN allowed copy TOS bits except for ECN use inner TOS bits with some\n"
+" CE (masked with 0xfe) from change. if outer ECN CE bit\n"
+" inner to outer. is 1, enable ECN CE bit on\n"
+" set ECN CE bit to 0. the inner.\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:833
+msgid "General strategy for configuration is as follows:"
+msgstr "Общая стратегия настройки выглядит следующим образом:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:835
+msgid ""
+"if both IPsec tunnel endpoint are capable of ECN-friendly behavior, you "
+"should better configure both end to \"ECN allowed\" (sysctl value 1)."
+msgstr ""
+"если оба конечных пункта туннеля IPsec поддерживают поведение, дружественное "
+"к ECN, лучше настроить оба конца на \"разрешено ECN\" (значение sysctl 1)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:836
+msgid ""
+"if the other end is very strict about TOS bit, use \"RFC2401\" (sysctl value "
+"-1)."
+msgstr ""
+"если другая сторона очень строга к биту TOS, используйте \"RFC2401\" "
+"(значение sysctl -1)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:837
+msgid "in other cases, use \"ECN forbidden\" (sysctl value 0)."
+msgstr "в остальных случаях используйте \"ECN запрещено\" (значение sysctl 0)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:839
+msgid "The default behavior is \"ECN forbidden\" (sysctl value 0)."
+msgstr "Поведение по умолчанию — \"ECN запрещён\" (значение sysctl 0)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:841
+msgid "For more information, please refer to:"
+msgstr "Для получения дополнительной информации обратитесь к:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:843
+msgid ""
+"http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt[ http://"
+"www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt], RFC2481 (Explicit "
+"Congestion Notification), src/sys/netinet6/{ah,esp}_input.c"
+msgstr ""
+"http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt[ http://"
+"www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt], RFC2481 (Явное "
+"Уведомление о Перегрузке), src/sys/netinet6/{ah,esp}_input.c"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:845
+msgid ""
+"(Thanks goes to Kenjiro Cho mailto:kjc@csl.sony.co.jp[kjc@csl.sony.co.jp] "
+"for detailed analysis)"
+msgstr ""
+"(Благодарности Kenjiro Cho mailto:kjc@csl.sony.co.jp[kjc@csl.sony.co.jp] за "
+"детальный анализ)"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:846
+#, no-wrap
+msgid "Interoperability"
+msgstr "Совместимость"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:850
+msgid ""
+"Here are (some of) platforms that KAME code have tested IPsec/IKE "
+"interoperability in the past. Note that both ends may have modified their "
+"implementation, so use the following list just for reference purposes."
+msgstr ""
+"Вот некоторые из платформ, на которых код KAME тестировал взаимодействие "
+"IPsec/IKE в прошлом. Обратите внимание, что обе стороны могли изменить свои "
+"реализации, поэтому используйте следующий список только в справочных целях."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/ipv6/_index.adoc:851
+msgid ""
+"Altiga, Ashley-laurent (vpcom.com), Data Fellows (F-Secure), Ericsson ACC, "
+"FreeS/WAN, HITACHI, IBM AIX(R), IIJ, Intel, Microsoft(R) Windows NT(R), NIST "
+"(linux IPsec + plutoplus), Netscreen, OpenBSD, RedCreek, Routerware, SSH, "
+"Secure Computing, Soliton, Toshiba, VPNet, Yamaha RT100i"
+msgstr ""
+"Altiga, Ashley-laurent (vpcom.com), Data Fellows (F-Secure), Ericsson ACC, "
+"FreeS/WAN, HITACHI, IBM AIX(R), IIJ, Intel, Microsoft(R) Windows NT(R), NIST "
+"(linux IPsec + plutoplus), Netscreen, OpenBSD, RedCreek, Routerware, SSH, "
+"Secure Computing, Soliton, Toshiba, VPNet, Yamaha RT100i"
diff --git a/documentation/content/ru/books/developers-handbook/kernelbuild/_index.adoc b/documentation/content/ru/books/developers-handbook/kernelbuild/_index.adoc
new file mode 100644
index 0000000000..a020ccd898
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/kernelbuild/_index.adoc
@@ -0,0 +1,94 @@
+---
+authors:
+description: 'Сборка и установка ядра FreeBSD'
+next: books/developers-handbook/kerneldebug
+params:
+ path: /books/developers-handbook/kernelbuild/
+prev: books/developers-handbook/partiii
+showBookMenu: true
+tags: ["building", "installing", "kernel", "FreeBSD"]
+title: 'Глава 9. Сборка и установка ядра FreeBSD'
+weight: 12
+---
+
+[[kernelbuild]]
+= Сборка и установка ядра FreeBSD
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 9
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/developers-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+Чтобы быть разработчиком ядра, требуется понимания процесса сборки ядра. Для отладки ядра FreeBSD необходимо уметь его собирать. Существует два известных способа сделать это:
+
+Поддерживаемая процедура сборки и установки ядра описана в главе extref:{handbook}kernelconfig/[Сборка и установка пользовательского ядра, kernelconfig-building] Руководства FreeBSD.
+
+[NOTE]
+====
+Предполагается, что читатель этой главы знаком с информацией, изложенной в главе extref:{handbook}kernelconfig/[Сборка и установка пользовательского ядра, kernelconfig-building] Руководства FreeBSD. Если это не так, пожалуйста, ознакомьтесь с упомянутой главой, чтобы понять, как работает процесс сборки.
+====
+
+[[kernelbuild-traditional]]
+== Построение более быстрым, но менее надежным способом
+
+Сборка ядра таким способом может быть полезной при работе с кодом ядра и может оказаться быстрее, чем описанная процедура, если в конфигурационном файле ядра были изменены только одна или две опции. С другой стороны, это может привести к неожиданным сбоям при сборке ядра.
+
+[.procedure]
+. Выполните man:config[8] для генерации исходного кода ядра:
++
+[source, bash]
+....
+# /usr/sbin/config MYKERNEL
+....
+
+. Перейдите в каталог сборки. man:config[8] выведет имя этого каталога после выполнения, как указано выше.
++
+[source, bash]
+....
+# cd ../compile/MYKERNEL
+....
+
+. Скомпилируйте ядро:
++
+[source, bash]
+....
+# make depend
+# make
+....
+
+. Установите новое ядро:
++
+[source, bash]
+....
+# make install
+....
diff --git a/documentation/content/ru/books/developers-handbook/kernelbuild/_index.po b/documentation/content/ru/books/developers-handbook/kernelbuild/_index.po
new file mode 100644
index 0000000000..0230e59c60
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/kernelbuild/_index.po
@@ -0,0 +1,140 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-06-24 20:10+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookkernelbuild_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:1
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:15
+#, no-wrap
+msgid "Building and Installing a FreeBSD Kernel"
+msgstr "Сборка и установка ядра FreeBSD"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:1
+#, no-wrap
+msgid "Chapter 9. Building and Installing a FreeBSD Kernel"
+msgstr "Глава 9. Сборка и установка ядра FreeBSD"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:55
+msgid ""
+"Being a kernel developer requires understanding of the kernel build "
+"process. To debug the FreeBSD kernel it is required to be able to build "
+"one. There are two known ways to do so:"
+msgstr ""
+"Чтобы быть разработчиком ядра, требуется понимания процесса сборки ядра. Для "
+"отладки ядра FreeBSD необходимо уметь его собирать. Существует два известных "
+"способа сделать это:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:57
+msgid ""
+"The supported procedure to build and install a kernel is documented in the "
+"extref:{handbook}kernelconfig/[Building and Installing a Custom Kernel, "
+"kernelconfig-building] chapter of the FreeBSD Handbook."
+msgstr ""
+"Поддерживаемая процедура сборки и установки ядра описана в главе extref:"
+"{handbook}kernelconfig/[Сборка и установка пользовательского ядра, "
+"kernelconfig-building] Руководства FreeBSD."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:62
+msgid ""
+"It is supposed that the reader of this chapter is familiar with the "
+"information described in the extref:{handbook}kernelconfig/[Building and "
+"Installing a Custom Kernel, kernelconfig-building] chapter of the FreeBSD "
+"Handbook. If this is not the case, please read through the above mentioned "
+"chapter to understand how the build process works."
+msgstr ""
+"Предполагается, что читатель этой главы знаком с информацией, изложенной в "
+"главе extref:{handbook}kernelconfig/[Сборка и установка пользовательского "
+"ядра, kernelconfig-building] Руководства FreeBSD. Если это не так, "
+"пожалуйста, ознакомьтесь с упомянутой главой, чтобы понять, как работает "
+"процесс сборки."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:65
+#, no-wrap
+msgid "Building the Faster but Brittle Way"
+msgstr "Построение более быстрым, но менее надежным способом"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:69
+msgid ""
+"Building the kernel this way may be useful when working on the kernel code "
+"and it may actually be faster than the documented procedure when only a "
+"single option or two were tweaked in the kernel configuration file. On the "
+"other hand, it might lead to unexpected kernel build breakage."
+msgstr ""
+"Сборка ядра таким способом может быть полезной при работе с кодом ядра и "
+"может оказаться быстрее, чем описанная процедура, если в конфигурационном "
+"файле ядра были изменены только одна или две опции. С другой стороны, это "
+"может привести к неожиданным сбоям при сборке ядра."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:72
+msgid "Run man:config[8] to generate the kernel source code:"
+msgstr "Выполните man:config[8] для генерации исходного кода ядра:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:76
+#, no-wrap
+msgid "# /usr/sbin/config MYKERNEL\n"
+msgstr "# /usr/sbin/config MYKERNEL\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:79
+msgid ""
+"Change into the build directory. man:config[8] will print the name of this "
+"directory after being run as above."
+msgstr ""
+"Перейдите в каталог сборки. man:config[8] выведет имя этого каталога после "
+"выполнения, как указано выше."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:83
+#, no-wrap
+msgid "# cd ../compile/MYKERNEL\n"
+msgstr "# cd ../compile/MYKERNEL\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:86
+msgid "Compile the kernel:"
+msgstr "Скомпилируйте ядро:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:91
+#, no-wrap
+msgid ""
+"# make depend\n"
+"# make\n"
+msgstr ""
+"# make depend\n"
+"# make\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:94
+msgid "Install the new kernel:"
+msgstr "Установите новое ядро:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kernelbuild/_index.adoc:98
+#, no-wrap
+msgid "# make install\n"
+msgstr "# make install\n"
diff --git a/documentation/content/ru/books/developers-handbook/kerneldebug/_index.adoc b/documentation/content/ru/books/developers-handbook/kerneldebug/_index.adoc
new file mode 100644
index 0000000000..5ac6d8df9f
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/kerneldebug/_index.adoc
@@ -0,0 +1,773 @@
+---
+authors:
+ -
+ author: 'Paul Richards'
+ -
+ author: 'Jörg Wunsch'
+ -
+ author: 'Robert Watson'
+description: 'Отладка ядра FreeBSD'
+next: books/developers-handbook/partiv
+params:
+ path: /books/developers-handbook/kerneldebug/
+prev: books/developers-handbook/kernelbuild
+showBookMenu: true
+tags: ["Debugging", "Dump", "kgdb", "DDB", "GDB"]
+title: 'Глава 10. Отладка ядра'
+weight: 13
+---
+
+[[kerneldebug]]
+= Отладка ядра
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 10
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/developers-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[kerneldebug-obtain]]
+== Получение аварийного дампа ядра
+
+При работе с разрабатываемым ядром (например, FreeBSD-CURRENT), особенно в экстремальных условиях (например, при очень высокой загрузке, десятках тысяч соединений, чрезвычайно большом количестве одновременных пользователей, сотнях man:jail[8] и т.д.), или при использовании новой функции или драйвера устройства в FreeBSD-STABLE (например, PAE), иногда может возникнуть паника ядра. В случае, если это произойдет, данная глава покажет, как извлечь полезную информацию из аварийного дампа.
+
+Перезагрузка системы неизбежна после паники ядра. После перезагрузки системы содержимое физической памяти (RAM) теряется, как и любые данные на устройстве подкачки перед паникой. Чтобы сохранить данные в физической памяти, ядро использует устройство подкачки как временное хранилище для данных из RAM после сбоя и перезагрузки. Благодаря этому, когда FreeBSD загружается после сбоя, образ ядра может быть извлечен и проведена отладка.
+
+[NOTE]
+====
+Устройство подкачки, настроенное как устройство для дампа, продолжает функционировать как устройство подкачки. В настоящее время дампы на устройства, не являющиеся устройствами подкачки (например, на ленты или CDRW), не поддерживаются. Термин "устройство подкачки" является синонимом термина "раздел подкачки".
+====
+
+Есть несколько типов аварийных дампов ядра:
+
+Полные дампы памяти::
+Содержат полное содержимое физической памяти.
+
+Минидампы::
+Содержат только страницы памяти, используемые ядром (FreeBSD 6.2 и выше).
+
+Текстовые дампы::
+Содержать захваченные, записанные или интерактивные выходные данные отладчика (FreeBSD 7.1 и выше).
+
+Минидампы являются типом дампа по умолчанию, начиная с FreeBSD 7.0, и в большинстве случаев они сохраняют всю необходимую информацию, присутствующую в полном дампе памяти, так как большинство проблем можно изолировать, используя только состояние ядра.
+
+[[config-dumpdev]]
+=== Настройка устройства дампа
+
+Прежде чем ядро запишет содержимое своей физической памяти на устройство дампа, необходимо настроить это устройство. Устройство дампа указывается с помощью команды man:dumpon[8], чтобы сообщить ядру, куда сохранять аварийные дампы. Программа man:dumpon[8] должна быть вызвана после настройки раздела подкачки с помощью man:swapon[8]. Обычно это обрабатывается установкой переменной `dumpdev` в man:rc.conf[5] в путь к устройству подкачки (рекомендуемый способ извлечения дампа ядра) или в значение `AUTO` для использования первого настроенного устройства подкачки. По умолчанию `dumpdev` имеет значение `AUTO` в HEAD и изменено на `NO` в ветках RELENG_* (за исключением RELENG_7, где оставлено значение `AUTO`). Начиная с FreeBSD 9.0-RELEASE и более поздних версий, bsdinstall будет спрашивать, следует ли включить аварийные дампы на целевой системе во время процесса установки.
+
+[TIP]
+====
+Проверьте [.filename]#/etc/fstab# или man:swapinfo[8] для получения списка устройств подкачки.
+====
+
+[IMPORTANT]
+====
+Убедитесь, что каталог `dumpdir`, указанный в man:rc.conf[5], существует перед аварией ядра!
+
+[source, bash]
+....
+# mkdir /var/crash
+# chmod 700 /var/crash
+....
+
+Также помните, что содержимое [.filename]#/var/crash# является конфиденциальным и, скорее всего, содержит секретную информацию, такую как пароли.
+====
+
+[[extract-dump]]
+=== Извлечение дампа ядра
+
+После записи дампа на устройство дампа, дамп должен быть извлечен до монтирования устройства подкачки. Для извлечения дампа с устройства дампа используйте программу man:savecore[8]. Если в man:rc.conf[5] установлен параметр `dumpdev`, man:savecore[8] будет автоматически вызван при первой загрузке в многопользовательском режиме после сбоя и до монтирования устройства подкачки. Расположение извлеченного ядра указывается в параметре `dumpdir` файла man:rc.conf[5], по умолчанию это [.filename]#/var/crash#, а имя файла будет [.filename]#vmcore.0#.
+
+В случае, если файл с именем [.filename]#vmcore.0# уже существует в [.filename]#/var/crash# (или в каталоге, указанном в параметре `dumpdir`), ядро будет увеличивать завершающее число при каждом сбое, чтобы избежать перезаписи существующего файла [.filename]#vmcore# (например, [.filename]#vmcore.1#). man:savecore[8] всегда создает символическую ссылку с именем [.filename]#vmcore.last# в [.filename]#/var/crash# после сохранения дампа. Эта символическая ссылка может быть использована для определения имени последнего дампа.
+
+Утилита man:crashinfo[8] создаёт текстовый файл, содержащий сводную информацию из полного дампа памяти или минидампа. Если параметр `dumpdev` установлен в man:rc.conf[5], man:crashinfo[8] будет автоматически вызван после man:savecore[8]. Результат сохраняется в файл с именем [.filename]#core.txt.N# в директории `dumpdir`.
+
+[TIP]
+====
+Если вы тестируете новое ядро, но вам нужно загрузить другое, чтобы снова запустить систему, загрузите его только в однопользовательском режиме, используя флаг `-s` при загрузке, а затем выполните следующие шаги:
+
+[source, bash]
+....
+# fsck -p
+# mount -a -t ufs # make sure /var/crash is writable
+# savecore /var/crash /dev/ad0s1b
+# exit # exit to multi-user
+....
+
+Это указывает man:savecore[8] извлечь дамп ядра из [.filename]#/dev/ad0s1b# и поместить содержимое в [.filename]#/var/crash#. Не забудьте убедиться, что целевой каталог [.filename]#/var/crash# имеет достаточно места для дампа. Также не забудьте указать правильный путь к вашему swap-устройству, так как он, скорее всего, отличается от [.filename]#/dev/ad0s1b#!
+====
+
+=== Тестирование конфигурации дампа ядра
+
+Ядро включает узел man:sysctl[8], который вызывает панику ядра. Это можно использовать для проверки того, что ваша система правильно настроена для сохранения дампов аварийного завершения работы ядра. Возможно, вы захотите перемонтировать существующие файловые системы в режиме только для чтения в однопользовательском режиме перед тем, как вызвать панику, чтобы избежать потери данных.
+
+[source, bash]
+....
+# shutdown now
+...
+Enter full pathname of shell or RETURN for /bin/sh:
+# mount -a -u -r
+# sysctl debug.kdb.panic=1
+debug.kdb.panic:panic: kdb_sysctl_panic
+...
+....
+
+После перезагрузки система должна сохранить дамп в [.filename]#/var/crash# вместе с соответствующим отчетом из man:crashinfo[8].
+
+[[kerneldebug-gdb]]
+== Отладка аварийного дампа ядра с помощью `kgdb`
+
+[NOTE]
+====
+Этот раздел посвящен man:kgdb[1]. Последняя версия включена в пакет package:devel/gdb[]. Более старая версия также присутствует в FreeBSD 11 и более ранних версиях.
+====
+
+Чтобы войти в отладчик и начать получение информации из дампа, запустите kgdb:
+
+[source, bash]
+....
+# kgdb -n N
+....
+
+Где _N_ — это суффикс файла [.filename]#vmcore.N#, который нужно изучить. Чтобы открыть последний дамп, используйте:
+
+[source, bash]
+....
+# kgdb -n last
+....
+
+Обычно man:kgdb[1] должен быть способен найти ядро, работавшее в момент создания дампа. Если он не может найти нужное ядро, передайте путь к ядру и дампу в качестве двух аргументов для kgdb:
+
+[source, bash]
+....
+# kgdb /boot/kernel/kernel /var/crash/vmcore.0
+....
+
+Вы можете отлаживать дамп аварийного завершения, используя исходные коды ядра, так же, как и для любой другой программы.
+
+Этот дамп получен из ядра версии 5.2-BETA, а крах произошел глубоко внутри ядра. Приведенный ниже вывод был изменен для добавления номеров строк слева. Первый трассировочный вывод проверяет указатель инструкции и получает обратную трассировку. Адрес, используемый в строке 41 для команды `list`, является указателем инструкции и может быть найден в строке 17. Большинство разработчиков запросят как минимум эту информацию, если вы не сможете отладить проблему самостоятельно. Однако, если вы решите проблему, убедитесь, что ваш патч попадет в дерево исходников через отчет о проблеме, списки рассылки, или, может быть, у вас есть возможность его закоммитить!
+
+[source, bash]
+....
+ 1:# cd /usr/obj/usr/src/sys/KERNCONF
+ 2:# kgdb kernel.debug /var/crash/vmcore.0
+ 3:GNU gdb 5.2.1 (FreeBSD)
+ 4:Copyright 2002 Free Software Foundation, Inc.
+ 5:GDB is free software, covered by the GNU General Public License, and you are
+ 6:welcome to change it and/or distribute copies of it under certain conditions.
+ 7:Type "show copying" to see the conditions.
+ 8:There is absolutely no warranty for GDB. Type "show warranty" for details.
+ 9:This GDB was configured as "i386-undermydesk-freebsd"...
+10:panic: page fault
+11:panic messages:
+12:---
+13:Fatal trap 12: page fault while in kernel mode
+14:cpuid = 0; apic id = 00
+15:fault virtual address = 0x300
+16:fault code: = supervisor read, page not present
+17:instruction pointer = 0x8:0xc0713860
+18:stack pointer = 0x10:0xdc1d0b70
+19:frame pointer = 0x10:0xdc1d0b7c
+20:code segment = base 0x0, limit 0xfffff, type 0x1b
+21: = DPL 0, pres 1, def32 1, gran 1
+22:processor eflags = resume, IOPL = 0
+23:current process = 14394 (uname)
+24:trap number = 12
+25:panic: page fault
+26 cpuid = 0;
+27:Stack backtrace:
+28
+29:syncing disks, buffers remaining... 2199 2199 panic: mi_switch: switch in a critical section
+30:cpuid = 0;
+31:Uptime: 2h43m19s
+32:Dumping 255 MB
+33: 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240
+34:---
+35:Reading symbols from /boot/kernel/snd_maestro3.ko...done.
+36:Loaded symbols for /boot/kernel/snd_maestro3.ko
+37:Reading symbols from /boot/kernel/snd_pcm.ko...done.
+38:Loaded symbols for /boot/kernel/snd_pcm.ko
+39:#0 doadump () at /usr/src/sys/kern/kern_shutdown.c:240
+40:240 dumping++;
+41:(kgdb) list *0xc0713860
+42:0xc0713860 is in lapic_ipi_wait (/usr/src/sys/i386/i386/local_apic.c:663).
+43:658 incr = 0;
+44:659 delay = 1;
+45:660 } else
+46:661 incr = 1;
+47:662 for (x = 0; x < delay; x += incr) {
+48:663 if ((lapic->icr_lo & APIC_DELSTAT_MASK) == APIC_DELSTAT_IDLE)
+49:664 return (1);
+50:665 ia32_pause();
+51:666 }
+52:667 return (0);
+53:(kgdb) backtrace
+54:#0 doadump () at /usr/src/sys/kern/kern_shutdown.c:240
+55:#1 0xc055fd9b in boot (howto=260) at /usr/src/sys/kern/kern_shutdown.c:372
+56:#2 0xc056019d in panic () at /usr/src/sys/kern/kern_shutdown.c:550
+57:#3 0xc0567ef5 in mi_switch () at /usr/src/sys/kern/kern_synch.c:470
+58:#4 0xc055fa87 in boot (howto=256) at /usr/src/sys/kern/kern_shutdown.c:312
+59:#5 0xc056019d in panic () at /usr/src/sys/kern/kern_shutdown.c:550
+60:#6 0xc0720c66 in trap_fatal (frame=0xdc1d0b30, eva=0)
+61: at /usr/src/sys/i386/i386/trap.c:821
+62:#7 0xc07202b3 in trap (frame=
+63: {tf_fs = -1065484264, tf_es = -1065484272, tf_ds = -1065484272, tf_edi = 1, tf_esi = 0, tf_ebp = -602076292, tf_isp = -602076324, tf_ebx = 0, tf_edx = 0, tf_ecx = 1000000, tf_eax = 243, tf_trapno = 12, tf_err = 0, tf_eip = -1066321824, tf_cs = 8, tf_eflags = 65671, tf_esp = 243, tf_ss = 0})
+64: at /usr/src/sys/i386/i386/trap.c:250
+65:#8 0xc070c9f8 in calltrap () at {standard input}:94
+66:#9 0xc07139f3 in lapic_ipi_vectored (vector=0, dest=0)
+67: at /usr/src/sys/i386/i386/local_apic.c:733
+68:#10 0xc0718b23 in ipi_selected (cpus=1, ipi=1)
+69: at /usr/src/sys/i386/i386/mp_machdep.c:1115
+70:#11 0xc057473e in kseq_notify (ke=0xcc05e360, cpu=0)
+71: at /usr/src/sys/kern/sched_ule.c:520
+72:#12 0xc0575cad in sched_add (td=0xcbcf5c80)
+73: at /usr/src/sys/kern/sched_ule.c:1366
+74:#13 0xc05666c6 in setrunqueue (td=0xcc05e360)
+75: at /usr/src/sys/kern/kern_switch.c:422
+76:#14 0xc05752f4 in sched_wakeup (td=0xcbcf5c80)
+77: at /usr/src/sys/kern/sched_ule.c:999
+78:#15 0xc056816c in setrunnable (td=0xcbcf5c80)
+79: at /usr/src/sys/kern/kern_synch.c:570
+80:#16 0xc0567d53 in wakeup (ident=0xcbcf5c80)
+81: at /usr/src/sys/kern/kern_synch.c:411
+82:#17 0xc05490a8 in exit1 (td=0xcbcf5b40, rv=0)
+83: at /usr/src/sys/kern/kern_exit.c:509
+84:#18 0xc0548011 in sys_exit () at /usr/src/sys/kern/kern_exit.c:102
+85:#19 0xc0720fd0 in syscall (frame=
+86: {tf_fs = 47, tf_es = 47, tf_ds = 47, tf_edi = 0, tf_esi = -1, tf_ebp = -1077940712, tf_isp = -602075788, tf_ebx = 672411944, tf_edx = 10, tf_ecx = 672411600, tf_eax = 1, tf_trapno = 12, tf_err = 2, tf_eip = 671899563, tf_cs = 31, tf_eflags = 642, tf_esp = -1077940740, tf_ss = 47})
+87: at /usr/src/sys/i386/i386/trap.c:1010
+88:#20 0xc070ca4d in Xint0x80_syscall () at {standard input}:136
+89:---Can't read userspace from dump, or kernel process---
+90:(kgdb) quit
+....
+
+[TIP]
+====
+Если ваша система регулярно завершается аварийно и у вас заканчивается место на диске, удаление старых файлов [.filename]#vmcore# в [.filename]#/var/crash# может освободить значительное количество дискового пространства!
+====
+
+[[kerneldebug-online-ddb]]
+== Онлайн-отладка ядра с использованием DDB
+
+В то время как `kgdb` как автономный отладчик предоставляет очень высокий уровень пользовательского интерфейса, есть некоторые вещи, которые он не может выполнить. Наиболее важные из них — установка точек останова и пошаговое выполнение кода ядра.
+
+Если вам требуется выполнить низкоуровневую отладку ядра, доступен отладчик DDB, работающий в режиме реального времени. Он позволяет устанавливать точки останова, выполнять пошаговое выполнение функций ядра, проверять и изменять переменные ядра и т.д. Однако он не имеет доступа к исходным файлам ядра и работает только с глобальными и статическими символами, без доступа к полной отладочной информации, как это делает `kgdb`.
+
+Для настройки ядра с включенной поддержкой DDB добавьте параметры
+[.programlisting]
+....
+options KDB
+....
+
+[.programlisting]
+....
+options DDB
+....
+
+в ваш конфигурационный файл, и пересоберите. (Подробности о настройке ядра FreeBSD см. в extref:{handbook}[Руководстве FreeBSD]).
+
+После загрузки ядра DDB существует несколько способов войти в него. Первый и самый ранний способ — использовать флаг загрузки `-d`. Ядро запустится в режиме отладки и перейдет в DDB до начала обнаружения любого из устройств. Таким образом, можно отлаживать даже функции обнаружить (probe)/ присоединить (attach) устройств. Для использования этого метода выйдите из меню загрузки загрузчика и введите `boot -d` в командной строке загрузчика.
+
+Второй сценарий — перейти в отладчик после загрузки системы. Есть два простых способа это сделать. Если вы хотите перейти в отладчик из командной строки, просто введите команду:
+
+[source, bash]
+....
+# sysctl debug.kdb.enter=1
+....
+
+В качестве альтернативы, если вы находитесь за системной консолью, можно использовать горячую клавишу на клавиатуре. Стандартной комбинацией для перехода в отладчик является kbd:[Ctrl+Alt+ESC]. В syscons эта последовательность может быть переназначена, и некоторые распространённые раскладки клавиатуры делают это, поэтому убедитесь, что знаете правильную комбинацию. Для последовательных консолей доступна опция, позволяющая использовать сигнал BREAK на линии консоли для входа в DDB (`options BREAK_TO_DEBUGGER` в конфигурационном файле ядра). Это не установлено по умолчанию, так как существует множество последовательных адаптеров, которые излишне генерируют условие BREAK, например, при отключении кабеля.
+
+Третий способ заключается в том, чтобы любое условие паники переходило в DDB, если ядро настроено на его использование. По этой причине не рекомендуется настраивать ядро с DDB для машины, работающей без присмотра.
+
+Для получения неинтерактивной функциональности добавьте:
+
+[.programlisting]
+....
+options KDB_UNATTENDED
+....
+
+в файл конфигурации ядра и пересоберите/переустановите ядро.
+
+Команды DDB примерно напоминают некоторые команды `gdb`. Первое, что вам, вероятно, нужно сделать, это установить точку останова:
+
+[source, bash]
+....
+ break function-name address
+....
+
+Числа по умолчанию интерпретируются как шестнадцатеричные, но чтобы отличить
+их от символьных имен, шестнадцатеричные числа, начинающиеся с букв `a-f`,
+должны предваряться префиксом `0x` (для остальных чисел это
+необязательно). Допускаются простые выражения, например: `function-name +
+0x103`.
+
+Для выхода из отладчика и продолжения выполнения введите:
+
+[source, bash]
+....
+ continue
+....
+
+Для получения трассировки стека текущего потока используйте:
+
+[source, bash]
+....
+ trace
+....
+
+Для получения трассировки стека произвольного потока укажите идентификатор процесса или идентификатор потока в качестве второго аргумента команды `trace`.
+
+Если вы хотите удалить точку останова, используйте
+
+[source, bash]
+....
+ del
+ del address-expression
+....
+
+Первая форма будет принята сразу после срабатывания точки останова и удаляет текущую точку останова. Вторая форма может удалить любую точку останова, но необходимо указать точный адрес; его можно получить из:
+
+[source, bash]
+....
+ show b
+....
+
+или:
+
+[source, bash]
+....
+ show break
+....
+
+Для пошагового выполнения ядра попробуйте:
+
+[source, bash]
+....
+ s
+....
+
+Это позволит войти в функции, но вы можете заставить DDB отслеживать их до достижения соответствующего оператора return с помощью:
+
+[source, bash]
+....
+ n
+....
+
+[NOTE]
+====
+Это отличается от оператора `next` в ``gdb``; это похоже на `finish` в ``gdb``. Нажатие kbd:[n] более одного раза приведёт к продолжению.
+====
+
+Для просмотра данных в памяти используйте (например):
+
+[source, bash]
+....
+ x/wx 0xf0133fe0,40
+ x/hd db_symtab_space
+ x/bc termbuf,10
+ x/s stringbuf
+....
+
+для доступа к словам/полусловам/байтам и отображения в шестнадцатеричном/десятичном/символьном/строковом формате. Число после запятой указывает количество объектов. Для отображения следующих 0x10 элементов просто введите:
+
+[source, bash]
+....
+ x ,10
+....
+
+Аналогично, используйте
+
+[source, bash]
+....
+ x/ia foofunc,10
+....
+
+для дизассемблирования первых 0x10 инструкций функции `foofunc` и их отображения вместе с их смещением от начала `foofunc`.
+
+Для записи в память используйте команду write:
+
+[source, bash]
+....
+ w/b termbuf 0xa 0xb 0
+ w/w 0xf0010030 0 0
+....
+
+Модификатор команды (`b`/`h`/`w`) определяет размер данных для записи, первое следующее выражение — это адрес для записи, а остальное интерпретируется как данные для записи в последующие ячейки памяти.
+
+Если вам необходимо узнать текущее содержимое регистров, введите:
+
+[source, bash]
+....
+ show reg
+....
+
+Также можно отобразить значение одного регистра, например:
+
+[source, bash]
+....
+ p $eax
+....
+
+и изменить его с помощью:
+
+[source, bash]
+....
+ set $eax new-value
+....
+
+Если вам потребуется вызвать некоторые функции ядра из DDB, просто напишите:
+
+[source, bash]
+....
+ call func(arg1, arg2, ...)
+....
+
+Будет выведено возвращаемое значение.
+
+Для вывода информации о всех запущенных процессах в стиле man:ps[1] используйте:
+
+[source, bash]
+....
+ ps
+....
+
+Теперь вы выяснили причину сбоя ядра и хотите выполнить перезагрузку. Помните, что в зависимости от серьезности предыдущего сбоя не все части ядра могут работать корректно. Выполните одно из следующих действий для завершения работы и перезагрузки системы:
+
+[source, bash]
+....
+ panic
+....
+
+Это приведёт к дампу ядра и перезагрузке, чтобы позже можно было проанализировать дамп на более высоком уровне с помощью man:kgdb[1].
+
+[source, bash]
+....
+ call boot(0)
+....
+
+Может быть хорошим способом чисто завершить работу работающей системы, `sync()` все диски и, наконец, в некоторых случаях перезагрузиться. Пока интерфейсы дисков и файловых систем ядра не повреждены, это может быть хорошим способом для почти чистого завершения работы.
+
+[source, bash]
+....
+ reset
+....
+
+Это последний способ избежать катастрофы, и он почти такой же, как нажатие на Большую Красную Кнопку.
+
+Если вам нужна краткая сводка команд, просто введите:
+
+[source, bash]
+....
+ help
+....
+
+Настоятельно рекомендуется иметь распечатанную копию страницы руководства man:ddb[4] для сеанса отладки. Помните, что читать онлайн-руководство во время пошагового выполнения ядра сложно.
+
+[[kerneldebug-online-gdb]]
+== Онлайн-отладка ядра с использованием удаленного GDB
+
+Ядро FreeBSD предоставляет второй бэкенд KDB для отладки в реальном времени: man:gdb[4]. Эта возможность поддерживается с FreeBSD 2.2 и является действительно очень удобной.
+
+GDB давно поддерживает _удалённую отладку_. Это осуществляется с помощью очень простого протокола через последовательное соединение. В отличие от других методов отладки, описанных выше, для этого потребуются две машины. Одна — это хост, предоставляющий среду отладки, включая все исходные тексты и копию бинарного файла ядра со всеми символами. Другая — целевая машина, на которой запущена копия того же самого ядра (возможно, без отладочной информации).
+
+Чтобы использовать удалённый GDB, убедитесь, что следующие параметры присутствуют в конфигурации вашего ядра:
+[.programlisting]
+....
+makeoptions DEBUG=-g
+options KDB
+options GDB
+....
+
+Обратите внимание, что опция `GDB` отключена по умолчанию в ядрах `GENERIC` для веток -STABLE и -RELEASE, но включена в -CURRENT.
+
+После сборки скопируйте ядро на целевую машину и загрузите его. Подключите последовательный порт целевой машины, у которого на устройстве uart установлены флаги "080", к любому последовательному порту отладочной машины. Подробности о настройке флагов на устройстве uart смотрите в man:uart[4].
+
+Целевая машина должна быть переведена в режим отладчика GDB, либо из-за паники, либо путем преднамеренного перехода в отладчик. Перед этим выберите бэкенд отладчика GDB:
+[source, bash]
+....
+# sysctl debug.kdb.current=gdb
+debug.kdb.current: ddb -> gdb
+....
+
+[NOTE]
+====
+Поддерживаемые бэкенды можно вывести с помощью sysctl `debug.kdb.available`. Если конфигурация ядра включает `options DDB`, то man:ddb[4] будет выбран по умолчанию. Если `gdb` не отображается в списке доступных бэкендов, значит, последовательный порт отладки может быть настроен неправильно.
+====
+
+Затем принудительно войдите в отладчик:
+[source, bash]
+....
+# sysctl debug.kdb.enter=1
+debug.kdb.enter: 0KDB: enter: sysctl debug.kdb.enter
+....
+
+Целевая машина теперь ожидает подключения от удалённого клиента GDB. На машине для отладки перейдите в каталог сборки целевого ядра и запустите `gdb`:
+
+[source, bash]
+....
+# cd /usr/obj/usr/src/amd64.amd64/sys/GENERIC/
+# kgdb kernel
+GNU gdb (GDB) 10.2 [GDB v10.2 for FreeBSD]
+Copyright (C) 2021 Free Software Foundation, Inc.
+...
+Reading symbols from kernel...
+Reading symbols from /usr/obj/usr/src/amd64.amd64/sys/GENERIC/kernel.debug...
+(kgdb)
+....
+
+Инициализируйте сеанс удаленной отладки (предполагая, что используется первый последовательный порт) с помощью:
+
+[source, bash]
+....
+(kgdb) target remote /dev/cuau0
+....
+
+Ваш хостинг GDB теперь получит контроль над целевым ядром:
+
+[source, bash]
+....
+Remote debugging using /dev/cuau0
+kdb_enter (why=<optimized out>, msg=<optimized out>) at /usr/src/sys/kern/subr_kdb.c:506
+506 kdb_why = KDB_WHY_UNSET;
+(kgdb)
+....
+
+[TIP]
+====
+В зависимости от используемого компилятора, некоторые локальные переменные могут отображаться как `<optimized out>`, что не позволяет их напрямую исследовать с помощью `gdb`. Если это вызывает проблемы при отладке, можно собрать ядро с пониженным уровнем оптимизации, что может улучшить видимость некоторых переменных. Это можно сделать, передав `COPTFLAGS=-O1` в man:make[1]. Однако определённые классы ошибок в ядре могут проявляться иначе (или вообще не проявляться) при изменении уровня оптимизации.
+====
+
+Вы можете использовать этот сеанс почти как любой другой сеанс GDB, включая полный доступ к исходному коду, запуск в режиме gud (Grand Unified Debugger) внутри окна Emacs (что дает автоматическое отображение исходного кода в другом окне Emacs) и т.д.
+
+[[kerneldebug-console]]
+== Отладка драйвера консоли
+
+Поскольку для работы DDB требуется драйвер консоли, ситуация усложняется, если сам драйвер консоли неисправен. Возможно, вы вспомните о возможности использования последовательной консоли (либо с модифицированными загрузочными блоками, либо указав `-h` в строке `Boot:`), подключив стандартный терминал к первому последовательному порту. DDB работает с любым настроенным драйвером консоли, включая последовательную консоль.
+
+[[kerneldebug-deadlocks]]
+== Отладка взаимоблокировок
+
+Вы можете столкнуться с так называемыми взаимоблокировками — ситуацией, когда система перестает выполнять полезную работу. Чтобы предоставить полезный отчет об ошибке в такой ситуации, используйте man:ddb[4], как описано в предыдущем разделе. Включите в отчет вывод команд `ps` и `trace` для подозрительных процессов.
+
+Если возможно, рассмотрите проведение дополнительного исследования. Приведенный ниже рецепт особенно полезен, если вы подозреваете, что взаимная блокировка происходит на уровне VFS. Добавьте следующие параметры в файл конфигурации ядра.
+
+[.programlisting]
+....
+makeoptions DEBUG=-g
+options INVARIANTS
+options INVARIANT_SUPPORT
+options WITNESS
+options WITNESS_SKIPSPIN
+options DEBUG_LOCKS
+options DEBUG_VFS_LOCKS
+options DIAGNOSTIC
+....
+
+При возникновении взаимоблокировки, помимо вывода команды `ps`, предоставьте информацию из `show pcpu`, `show allpcpu`, `show locks`, `show alllocks`, `show lockedvnods` и `alltrace`.
+
+Для получения осмысленных трассировок стека для потоковых процессов используйте `thread thread-id` для переключения на стек потока и выполните трассировку с помощью `where`.
+
+[[kerneldebug-dcons]]
+== Отладка ядра с помощью Dcons
+
+man:dcons[4] — это очень простой драйвер консоли, который не связан напрямую с какими-либо физическими устройствами. Он просто читает и записывает символы из буфера в ядре или загрузчике и обратно. Благодаря своей простоте он очень полезен для отладки ядра, особенно с устройством FireWire(R). В настоящее время FreeBSD предоставляет два способа взаимодействия с буфером извне ядра с помощью man:dconschat[8].
+
+=== Dcons через FireWire(R)
+
+Большинство контроллеров FireWire(R) (IEEE1394) основаны на спецификации OHCI, которая поддерживает физический доступ к памяти хоста. Это означает, что после инициализации контроллера хоста мы можем получить доступ к памяти хоста без помощи программного обеспечения (ядра). Мы можем использовать эту возможность для взаимодействия с man:dcons[4]. man:dcons[4] предоставляет функциональность, аналогичную последовательной консоли. Он эмулирует два последовательных порта: один для консоли и DDB, другой для GDB. Поскольку удалённый доступ к памяти полностью обрабатывается аппаратным обеспечением, буфер man:dcons[4] остаётся доступным даже при крахе системы.
+
+Устройства FireWire(R) не только встраиваются в материнские платы. Для настольных компьютеров существуют PCI-карты, а для ноутбуков можно приобрести интерфейс CardBus.
+
+==== Включение поддержки FireWire(R) и Dcons на целевой машине
+
+Чтобы включить поддержку FireWire(R) и Dcons в ядре _целевой машины_:
+
+* Убедитесь, что ваше ядро поддерживает `dcons`, `dcons_crom` и `firewire`. `Dcons` должен быть статически связан с ядром. Для `dcons_crom` и `firewire` модули должны подойти.
+* Убедитесь, что физический DMA включен. Возможно, потребуется добавить `hw.firewire.phydma_enable=1` в [.filename]#/boot/loader.conf#.
+* Добавьте параметры для отладки.
+* Добавьте `dcons_gdb=1` в [.filename]#/boot/loader.conf#, если вы используете GDB через FireWire(R).
+* Включите `dcons` в [.filename]#/etc/ttys#.
+* Это необязательно: чтобы принудительно сделать `dcons` высокоуровневой консолью, добавьте `hw.firewire.dcons_crom.force_console=1` в [.filename]#loader.conf#.
+
+Чтобы включить поддержку FireWire(R) и Dcons в man:loader[8] на i386 или amd64:
+
+Добавьте `LOADER_FIREWIRE_SUPPORT=YES` в [.filename]#/etc/make.conf# и пересоберите man:loader[8]:
+
+[source, bash]
+....
+# cd /sys/boot/i386 && make clean && make && make install
+....
+
+Чтобы включить man:dcons[4] в качестве активной низкоуровневой консоли, добавьте `boot_multicons="YES"` в [.filename]#/boot/loader.conf#.
+
+Вот несколько примеров конфигурации. Образец файла конфигурации ядра может содержать:
+
+[source, bash]
+....
+device dcons
+device dcons_crom
+options KDB
+options DDB
+options GDB
+options ALT_BREAK_TO_DEBUGGER
+....
+
+И образец [.filename]#/boot/loader.conf# может содержать:
+
+[source, bash]
+....
+dcons_crom_load="YES"
+dcons_gdb=1
+boot_multicons="YES"
+hw.firewire.phydma_enable=1
+hw.firewire.dcons_crom.force_console=1
+....
+
+==== Включение поддержки FireWire(R) и Dcons на главной машине
+
+Чтобы включить поддержку FireWire(R) в ядре на _основной машине_:
+
+[source, bash]
+....
+# kldload firewire
+....
+
+Определите EUI64 (уникальный 64-битный идентификатор) контроллера FireWire(R) и используйте man:fwcontrol[8] или `dmesg`, чтобы найти EUI64 целевой машины.
+
+Запустите man:dconschat[8], с:
+
+[source, bash]
+....
+# dconschat -e \# -br -G 12345 -t 00-11-22-33-44-55-66-77
+....
+
+Следующие комбинации клавиш могут быть использованы после запуска man:dconschat[8]:
+
+[.informaltable]
+[cols="1,1"]
+|===
+
+|kbd:[~+.]
+|Отсоединиться
+
+|kbd:[~]
+|ALT BREAK
+
+|kbd:[~]
+|ПЕРЕЗАГРУЗИТЬ (RESET) целевую машину
+
+|kbd:[~]
+|Приостановить dconschat
+|===
+
+Присоедините удаленный GDB, запустив man:kgdb[1] с сеансом удаленной отладки:
+
+[source, bash]
+....
+ kgdb -r :12345 kernel
+....
+
+==== Некоторые общие рекомендации
+
+Вот несколько общих советов:
+
+Чтобы в полной мере использовать скорость FireWire(R), отключите другие медленные драйверы консоли:
+
+[source, bash]
+....
+# conscontrol delete ttyd0 # serial console
+# conscontrol delete consolectl # video/keyboard
+....
+
+Существует режим GDB для man:emacs[1]; вот что нужно добавить в ваш [.filename]#.emacs#:
+
+[source, bash]
+....
+(setq gud-gdba-command-name "kgdb -a -a -a -r :12345")
+(setq gdb-many-windows t)
+(xterm-mouse-mode 1)
+M-x gdba
+....
+
+=== Dcons с KVM
+
+Мы можем напрямую читать буфер man:dcons[4] через [.filename]#/dev/mem# для работающих систем и в дампе памяти для систем после аварии. Это даёт аналогичный вывод команде `dmesg -a`, но буфер man:dcons[4] содержит больше информации.
+
+==== Использование Dcons с KVM
+
+Для использования man:dcons[4] с KVM:
+
+Дамп буфера man:dcons[4] работающей системы:
+
+[source, bash]
+....
+# dconschat -1
+....
+
+Дамп буфера man:dcons[4] аварийного дампа:
+
+[source, bash]
+....
+# dconschat -1 -M vmcore.XX
+....
+
+Отладка ядра в реальном времени может быть выполнена через:
+
+[source, bash]
+....
+# fwcontrol -m target_eui64
+# kgdb kernel /dev/fwmem0.2
+....
+
+[[kerneldebug-options]]
+== Глоссарий параметров ядра для отладки
+
+В этом разделе представлен краткий глоссарий параметров ядра, указываемых при компиляции и относящихся к отладке:
+
+* `options KDB`: включает фреймворк отладки ядра. Необходим для `options DDB` и `options GDB`. Практически не влияет на производительность. По умолчанию отладчик будет запущен при панике вместо автоматической перезагрузки.
+* `options KDB_UNATTENDED`: изменяет значение по умолчанию системной настройки `debug.debugger_on_panic` на 0, что управляет входом в отладчик при панике. Если `options KDB` не вкомпилировано в ядро, поведение по умолчанию — автоматическая перезагрузка при панике; если оно вкомпилировано в ядро, поведение по умолчанию — переход в отладчик, если не вкомпилирована опция `options KDB_UNATTENDED`. Если вы хотите оставить отладчик ядра вкомпилированным в ядро, но желаете, чтобы система перезагружалась, пока вы не готовы использовать отладчик для диагностики, используйте эту опцию.
+* `options KDB_TRACE`: изменяет значение по умолчанию системной настройки `debug.trace_on_panic` на 1, что управляет автоматическим выводом трассировки стека при панике. Особенно полезно при использовании с `options KDB_UNATTENDED`, так как позволяет собрать базовую отладочную информацию на последовательной консоли или консоли FireWire, продолжая перезагрузку для восстановления.
+* `options DDB`: включает поддержку консольного отладчика DDB. Этот интерактивный отладчик работает на активной низкоуровневой консоли системы, включая видеоконсоль, последовательную консоль или консоль FireWire. Он предоставляет базовые встроенные средства отладки, такие как трассировка стека, список процессов и потоков, вывод состояния блокировок, состояния виртуальной памяти, состояния файловой системы и управления ядром памяти. DDB не требует работы программного обеспечения на второй машине или возможности создания дампа памяти или полных символов отладки ядра, а также предоставляет детальную диагностику ядра во время выполнения. Многие ошибки могут быть полностью диагностированы с использованием только вывода DDB. Эта опция зависит от `options KDB`.
+* `options GDB`: включает поддержку удалённого отладчика GDB, который может работать через последовательный кабель или FireWire. При входе в отладчик можно подключить GDB для проверки содержимого структур, генерации трассировки стека и т.д. Некоторые состояния ядра сложнее исследовать, чем в DDB, который способен автоматически создавать полезные сводки состояния ядра, например, автоматически обходить структуры отладки блокировок или управления памятью ядра, но для этого требуется вторая машина с запущенным отладчиком. С другой стороны, GDB объединяет информацию из исходного кода ядра и полных отладочных символов, знает полные определения структур данных, локальные переменные и поддерживает написание скриптов. Эта опция не требуется для запуска GDB на дампе памяти ядра. Данная опция зависит от `options KDB`.
+* `options BREAK_TO_DEBUGGER`, `options ALT_BREAK_TO_DEBUGGER`: позволяют сигналу прерывания или альтернативному сигналу на консоли войти в отладчик. Если система зависает без паники, это полезный способ попасть в отладчик. Из-за текущей блокировки ядра сигнал прерывания, сгенерированный на последовательной консоли, значительно надежнее для входа в отладчик и обычно рекомендуется. Данная опция оказывает незначительное или нулевое влияние на производительность.
+* `options INVARIANTS`: включает в ядро большое количество проверок и тестов во время выполнения, которые постоянно проверяют целостность структур данных ядра и инварианты алгоритмов ядра. Эти тесты могут быть затратными, поэтому по умолчанию не включены, но они помогают обеспечить полезное поведение "fail stop", при котором определённые классы нежелательного поведения попадают в отладчик до возникновения повреждения данных ядра, что упрощает их отладку. Тесты включают в себя очистку памяти и проверку использования после освобождения, что является одним из наиболее значимых источников накладных расходов. Эта опция зависит от `options INVARIANT_SUPPORT`.
+* `options INVARIANT_SUPPORT`: многие тесты, присутствующие в `options INVARIANTS`, требуют модифицированных структур данных или определения дополнительных символов ядра.
+* `options WITNESS`: эта опция включает отслеживание и проверку порядка блокировок во время выполнения, что является неоценимым инструментом для диагностики взаимоблокировок. WITNESS поддерживает граф полученных порядков блокировок по типам блокировок и проверяет граф на каждом получении на наличие циклов (явных или неявных). Если цикл обнаружен, на консоль выводится предупреждение и трассировка стека, указывающие на возможное возникновение взаимоблокировки. WITNESS необходим для использования команд DDB `show locks`, `show witness` и `show alllocks`. Эта отладочная опция создает значительную нагрузку на производительность, которую можно несколько уменьшить с помощью `options WITNESS_SKIPSPIN`. Подробная документация доступна в man:witness[4].
+* `options WITNESS_SKIPSPIN`: отключает проверку порядка блокировки spinlock во время выполнения с WITNESS. Поскольку spin-блокировки чаще всего захватываются в планировщике, а события планировщика происходят часто, эта опция может значительно ускорить системы, работающие с WITNESS. Эта опция зависит от `options WITNESS`.
+* `options WITNESS_KDB`: изменяет значение по умолчанию системной настройки `debug.witness.kdb` на 1, что приводит к входу в отладчик при обнаружении нарушения порядка блокировок вместо простого вывода предупреждения. Эта опция зависит от `options WITNESS`.
+* `options SOCKBUF_DEBUG`: выполнять расширенную проверку согласованности сокетных буферов во время выполнения, что может быть полезно для отладки как ошибок в сокетах, так и состояний гонки в протоколах и драйверах устройств, взаимодействующих с сокетами. Данная опция значительно влияет на производительность сети и может изменить временные параметры в состояниях гонки драйверов устройств.
+* `options DEBUG_VFS_LOCKS`: отслеживает точки получения блокировок для lockmgr/vnode, расширяя объем информации, отображаемой командой `show lockedvnods` в DDB. Данная опция оказывает заметное влияние на производительность.
+* `options DEBUG_MEMGUARD`: замена для man:malloc[9], аллокатор памяти ядра, который использует систему VM для обнаружения чтения или записи в освобождённую память. Подробности можно найти в man:memguard[9]. Данная опция значительно влияет на производительность, но может быть очень полезна при отладке ошибок повреждения памяти ядра.
+* `options DIAGNOSTIC`: включает дополнительные, более затратные диагностические тесты, аналогичные `options INVARIANTS`.
+* `options KASAN`: включает отладчик адресов ядра (Kernel Address Sanitizer). Это включает инструментирование компилятора, которое может использоваться для обнаружения недопустимых обращений к памяти в ядре, таких как использование после освобождения и переполнение буфера. В значительной степени заменяет `options DEBUG_MEMGUARD`. Подробности и список поддерживаемых платформ см. в man:kasan[9].
+* `options KMSAN`: включить отладчик использования памяти ядра (Kernel Memory Sanitizer). Это включает инструментирование компилятора, которое может использоваться для обнаружения использования неинициализированной памяти. Подробности и список поддерживаемых платформ см. в man:kmsan[9].
diff --git a/documentation/content/ru/books/developers-handbook/kerneldebug/_index.po b/documentation/content/ru/books/developers-handbook/kerneldebug/_index.po
new file mode 100644
index 0000000000..dd46c18a3c
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/kerneldebug/_index.po
@@ -0,0 +1,2243 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-09-05 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookkerneldebug_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:1
+#, no-wrap
+msgid "FreeBSD Kernel Debugging"
+msgstr "Отладка ядра FreeBSD"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:1
+#, no-wrap
+msgid "Chapter 10. Kernel Debugging"
+msgstr "Глава 10. Отладка ядра"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:18
+#, no-wrap
+msgid "Kernel Debugging"
+msgstr "Отладка ядра"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:56
+#, no-wrap
+msgid "Obtaining a Kernel Crash Dump"
+msgstr "Получение аварийного дампа ядра"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:61
+msgid ""
+"When running a development kernel (e.g., FreeBSD-CURRENT), such as a kernel "
+"under extreme conditions (e.g., very high load averages, tens of thousands "
+"of connections, exceedingly high number of concurrent users, hundreds of "
+"man:jail[8]s, etc.), or using a new feature or device driver on FreeBSD-"
+"STABLE (e.g., PAE), sometimes a kernel will panic. In the event that it "
+"does, this chapter will demonstrate how to extract useful information out of "
+"a crash."
+msgstr ""
+"При работе с разрабатываемым ядром (например, FreeBSD-CURRENT), особенно в "
+"экстремальных условиях (например, при очень высокой загрузке, десятках тысяч "
+"соединений, чрезвычайно большом количестве одновременных пользователей, "
+"сотнях man:jail[8] и т.д.), или при использовании новой функции или драйвера "
+"устройства в FreeBSD-STABLE (например, PAE), иногда может возникнуть паника "
+"ядра. В случае, если это произойдет, данная глава покажет, как извлечь "
+"полезную информацию из аварийного дампа."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:66
+msgid ""
+"A system reboot is inevitable once a kernel panics. Once a system is "
+"rebooted, the contents of a system's physical memory (RAM) is lost, as well "
+"as any bits that are on the swap device before the panic. To preserve the "
+"bits in physical memory, the kernel makes use of the swap device as a "
+"temporary place to store the bits that are in RAM across a reboot after a "
+"crash. In doing this, when FreeBSD boots after a crash, a kernel image can "
+"now be extracted and debugging can take place."
+msgstr ""
+"Перезагрузка системы неизбежна после паники ядра. После перезагрузки системы "
+"содержимое физической памяти (RAM) теряется, как и любые данные на "
+"устройстве подкачки перед паникой. Чтобы сохранить данные в физической "
+"памяти, ядро использует устройство подкачки как временное хранилище для "
+"данных из RAM после сбоя и перезагрузки. Благодаря этому, когда FreeBSD "
+"загружается после сбоя, образ ядра может быть извлечен и проведена отладка."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:72
+msgid ""
+"A swap device that has been configured as a dump device still acts as a swap "
+"device. Dumps to non-swap devices (such as tapes or CDRWs, for example) are "
+"not supported at this time. A \"swap device\" is synonymous with a \"swap "
+"partition.\""
+msgstr ""
+"Устройство подкачки, настроенное как устройство для дампа, продолжает "
+"функционировать как устройство подкачки. В настоящее время дампы на "
+"устройства, не являющиеся устройствами подкачки (например, на ленты или "
+"CDRW), не поддерживаются. Термин \"устройство подкачки\" является синонимом "
+"термина \"раздел подкачки\"."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:75
+msgid "Several types of kernel crash dumps are available:"
+msgstr "Есть несколько типов аварийных дампов ядра:"
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:76
+#, no-wrap
+msgid "Full memory dumps"
+msgstr "Полные дампы памяти"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:78
+msgid "Hold the complete contents of physical memory."
+msgstr "Содержат полное содержимое физической памяти."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:79
+#, no-wrap
+msgid "Minidumps"
+msgstr "Минидампы"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:81
+msgid "Hold only memory pages in use by the kernel (FreeBSD 6.2 and higher)."
+msgstr ""
+"Содержат только страницы памяти, используемые ядром (FreeBSD 6.2 и выше)."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:82
+#, no-wrap
+msgid "Textdumps"
+msgstr "Текстовые дампы"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:84
+msgid ""
+"Hold captured, scripted, or interactive debugger output (FreeBSD 7.1 and "
+"higher)."
+msgstr ""
+"Содержать захваченные, записанные или интерактивные выходные данные "
+"отладчика (FreeBSD 7.1 и выше)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:86
+msgid ""
+"Minidumps are the default dump type as of FreeBSD 7.0, and in most cases "
+"will capture all necessary information present in a full memory dump, as "
+"most problems can be isolated only using kernel state."
+msgstr ""
+"Минидампы являются типом дампа по умолчанию, начиная с FreeBSD 7.0, и в "
+"большинстве случаев они сохраняют всю необходимую информацию, присутствующую "
+"в полном дампе памяти, так как большинство проблем можно изолировать, "
+"используя только состояние ядра."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:88
+#, no-wrap
+msgid "Configuring the Dump Device"
+msgstr "Настройка устройства дампа"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:96
+msgid ""
+"Before the kernel will dump the contents of its physical memory to a dump "
+"device, a dump device must be configured. A dump device is specified by "
+"using the man:dumpon[8] command to tell the kernel where to save kernel "
+"crash dumps. The man:dumpon[8] program must be called after the swap "
+"partition has been configured with man:swapon[8]. This is normally handled "
+"by setting the `dumpdev` variable in man:rc.conf[5] to the path of the swap "
+"device (the recommended way to extract a kernel dump) or `AUTO` to use the "
+"first configured swap device. The default for `dumpdev` is `AUTO` in HEAD, "
+"and changed to `NO` on RELENG_* branches (except for RELENG_7, which was "
+"left set to `AUTO`). On FreeBSD 9.0-RELEASE and later versions, bsdinstall "
+"will ask whether crash dumps should be enabled on the target system during "
+"the install process."
+msgstr ""
+"Прежде чем ядро запишет содержимое своей физической памяти на устройство "
+"дампа, необходимо настроить это устройство. Устройство дампа указывается с "
+"помощью команды man:dumpon[8], чтобы сообщить ядру, куда сохранять аварийные "
+"дампы. Программа man:dumpon[8] должна быть вызвана после настройки раздела "
+"подкачки с помощью man:swapon[8]. Обычно это обрабатывается установкой "
+"переменной `dumpdev` в man:rc.conf[5] в путь к устройству подкачки "
+"(рекомендуемый способ извлечения дампа ядра) или в значение `AUTO` для "
+"использования первого настроенного устройства подкачки. По умолчанию "
+"`dumpdev` имеет значение `AUTO` в HEAD и изменено на `NO` в ветках RELENG_* "
+"(за исключением RELENG_7, где оставлено значение `AUTO`). Начиная с FreeBSD "
+"9.0-RELEASE и более поздних версий, bsdinstall будет спрашивать, следует ли "
+"включить аварийные дампы на целевой системе во время процесса установки."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:100
+msgid ""
+"Check [.filename]#/etc/fstab# or man:swapinfo[8] for a list of swap devices."
+msgstr ""
+"Проверьте [.filename]#/etc/fstab# или man:swapinfo[8] для получения списка "
+"устройств подкачки."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:105
+msgid ""
+"Make sure the `dumpdir` specified in man:rc.conf[5] exists before a kernel "
+"crash!"
+msgstr ""
+"Убедитесь, что каталог `dumpdir`, указанный в man:rc.conf[5], существует "
+"перед аварией ядра!"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:110
+#, no-wrap
+msgid ""
+"# mkdir /var/crash\n"
+"# chmod 700 /var/crash\n"
+msgstr ""
+"# mkdir /var/crash\n"
+"# chmod 700 /var/crash\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:113
+msgid ""
+"Also, remember that the contents of [.filename]#/var/crash# is sensitive and "
+"very likely contains confidential information such as passwords."
+msgstr ""
+"Также помните, что содержимое [.filename]#/var/crash# является "
+"конфиденциальным и, скорее всего, содержит секретную информацию, такую как "
+"пароли."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:116
+#, no-wrap
+msgid "Extracting a Kernel Dump"
+msgstr "Извлечение дампа ядра"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:122
+msgid ""
+"Once a dump has been written to a dump device, the dump must be extracted "
+"before the swap device is mounted. To extract a dump from a dump device, "
+"use the man:savecore[8] program. If `dumpdev` has been set in "
+"man:rc.conf[5], man:savecore[8] will be called automatically on the first "
+"multi-user boot after the crash and before the swap device is mounted. The "
+"location of the extracted core is placed in the man:rc.conf[5] value "
+"`dumpdir`, by default [.filename]#/var/crash# and will be named "
+"[.filename]#vmcore.0#."
+msgstr ""
+"После записи дампа на устройство дампа, дамп должен быть извлечен до "
+"монтирования устройства подкачки. Для извлечения дампа с устройства дампа "
+"используйте программу man:savecore[8]. Если в man:rc.conf[5] установлен "
+"параметр `dumpdev`, man:savecore[8] будет автоматически вызван при первой "
+"загрузке в многопользовательском режиме после сбоя и до монтирования "
+"устройства подкачки. Расположение извлеченного ядра указывается в параметре "
+"`dumpdir` файла man:rc.conf[5], по умолчанию это [.filename]#/var/crash#, а "
+"имя файла будет [.filename]#vmcore.0#."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:126
+msgid ""
+"In the event that there is already a file called [.filename]#vmcore.0# in "
+"[.filename]#/var/crash# (or whatever `dumpdir` is set to), the kernel will "
+"increment the trailing number for every crash to avoid overwriting an "
+"existing [.filename]#vmcore# (e.g., [.filename]#vmcore.1#). man:savecore[8] "
+"will always create a symbolic link to named [.filename]#vmcore.last# in "
+"[.filename]#/var/crash# after a dump is saved. This symbolic link can be "
+"used to locate the name of the most recent dump."
+msgstr ""
+"В случае, если файл с именем [.filename]#vmcore.0# уже существует в "
+"[.filename]#/var/crash# (или в каталоге, указанном в параметре `dumpdir`), "
+"ядро будет увеличивать завершающее число при каждом сбое, чтобы избежать "
+"перезаписи существующего файла [.filename]#vmcore# (например, "
+"[.filename]#vmcore.1#). man:savecore[8] всегда создает символическую ссылку "
+"с именем [.filename]#vmcore.last# в [.filename]#/var/crash# после сохранения "
+"дампа. Эта символическая ссылка может быть использована для определения "
+"имени последнего дампа."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:130
+msgid ""
+"The man:crashinfo[8] utility generates a text file containing a summary of "
+"information from a full memory dump or minidump. If `dumpdev` has been set "
+"in man:rc.conf[5], man:crashinfo[8] will be invoked automatically after "
+"man:savecore[8]. The output is saved to a file in `dumpdir` named "
+"[.filename]#core.txt.N#."
+msgstr ""
+"Утилита man:crashinfo[8] создаёт текстовый файл, содержащий сводную "
+"информацию из полного дампа памяти или минидампа. Если параметр `dumpdev` "
+"установлен в man:rc.conf[5], man:crashinfo[8] будет автоматически вызван "
+"после man:savecore[8]. Результат сохраняется в файл с именем "
+"[.filename]#core.txt.N# в директории `dumpdir`."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:135
+msgid ""
+"If you are testing a new kernel but need to boot a different one in order to "
+"get your system up and running again, boot it only into single user mode "
+"using the `-s` flag at the boot prompt, and then perform the following steps:"
+msgstr ""
+"Если вы тестируете новое ядро, но вам нужно загрузить другое, чтобы снова "
+"запустить систему, загрузите его только в однопользовательском режиме, "
+"используя флаг `-s` при загрузке, а затем выполните следующие шаги:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:142
+#, no-wrap
+msgid ""
+"# fsck -p\n"
+"# mount -a -t ufs # make sure /var/crash is writable\n"
+"# savecore /var/crash /dev/ad0s1b\n"
+"# exit # exit to multi-user\n"
+msgstr ""
+"# fsck -p\n"
+"# mount -a -t ufs # make sure /var/crash is writable\n"
+"# savecore /var/crash /dev/ad0s1b\n"
+"# exit # exit to multi-user\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:147
+msgid ""
+"This instructs man:savecore[8] to extract a kernel dump from [.filename]#/"
+"dev/ad0s1b# and place the contents in [.filename]#/var/crash#. Do not "
+"forget to make sure the destination directory [.filename]#/var/crash# has "
+"enough space for the dump. Also, do not forget to specify the correct path "
+"to your swap device as it is likely different than [.filename]#/dev/ad0s1b#!"
+msgstr ""
+"Это указывает man:savecore[8] извлечь дамп ядра из [.filename]#/dev/ad0s1b# "
+"и поместить содержимое в [.filename]#/var/crash#. Не забудьте убедиться, что "
+"целевой каталог [.filename]#/var/crash# имеет достаточно места для дампа. "
+"Также не забудьте указать правильный путь к вашему swap-устройству, так как "
+"он, скорее всего, отличается от [.filename]#/dev/ad0s1b#!"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:149
+#, no-wrap
+msgid "Testing Kernel Dump Configuration"
+msgstr "Тестирование конфигурации дампа ядра"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:154
+msgid ""
+"The kernel includes a man:sysctl[8] node that requests a kernel panic. This "
+"can be used to verify that your system is properly configured to save kernel "
+"crash dumps. You may wish to remount existing file systems as read-only in "
+"single user mode before triggering the crash to avoid data loss."
+msgstr ""
+"Ядро включает узел man:sysctl[8], который вызывает панику ядра. Это можно "
+"использовать для проверки того, что ваша система правильно настроена для "
+"сохранения дампов аварийного завершения работы ядра. Возможно, вы захотите "
+"перемонтировать существующие файловые системы в режиме только для чтения в "
+"однопользовательском режиме перед тем, как вызвать панику, чтобы избежать "
+"потери данных."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:164
+#, no-wrap
+msgid ""
+"# shutdown now\n"
+"...\n"
+"Enter full pathname of shell or RETURN for /bin/sh:\n"
+"# mount -a -u -r\n"
+"# sysctl debug.kdb.panic=1\n"
+"debug.kdb.panic:panic: kdb_sysctl_panic\n"
+"...\n"
+msgstr ""
+"# shutdown now\n"
+"...\n"
+"Enter full pathname of shell or RETURN for /bin/sh:\n"
+"# mount -a -u -r\n"
+"# sysctl debug.kdb.panic=1\n"
+"debug.kdb.panic:panic: kdb_sysctl_panic\n"
+"...\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:167
+msgid ""
+"After rebooting, your system should save a dump in [.filename]#/var/crash# "
+"along with a matching summary from man:crashinfo[8]."
+msgstr ""
+"После перезагрузки система должна сохранить дамп в [.filename]#/var/crash# "
+"вместе с соответствующим отчетом из man:crashinfo[8]."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:169
+#, no-wrap
+msgid "Debugging a Kernel Crash Dump with `kgdb`"
+msgstr "Отладка аварийного дампа ядра с помощью `kgdb`"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:176
+msgid ""
+"This section covers man:kgdb[1]. The latest version is included in the "
+"package:devel/gdb[]. An older version is also present in FreeBSD 11 and "
+"earlier."
+msgstr ""
+"Этот раздел посвящен man:kgdb[1]. Последняя версия включена в пакет "
+"package:devel/gdb[]. Более старая версия также присутствует в FreeBSD 11 и "
+"более ранних версиях."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:179
+msgid ""
+"To enter into the debugger and begin getting information from the dump, "
+"start kgdb:"
+msgstr ""
+"Чтобы войти в отладчик и начать получение информации из дампа, запустите "
+"kgdb:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:183
+#, no-wrap
+msgid "# kgdb -n N\n"
+msgstr "# kgdb -n N\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:187
+msgid ""
+"Where _N_ is the suffix of the [.filename]#vmcore.N# to examine. To open "
+"the most recent dump use:"
+msgstr ""
+"Где _N_ — это суффикс файла [.filename]#vmcore.N#, который нужно изучить. "
+"Чтобы открыть последний дамп, используйте:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:191
+#, no-wrap
+msgid "# kgdb -n last\n"
+msgstr "# kgdb -n last\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:195
+msgid ""
+"Normally, man:kgdb[1] should be able to locate the kernel running at the "
+"time the dump was generated. If it is not able to locate the correct "
+"kernel, pass the pathname of the kernel and dump as two arguments to kgdb:"
+msgstr ""
+"Обычно man:kgdb[1] должен быть способен найти ядро, работавшее в момент "
+"создания дампа. Если он не может найти нужное ядро, передайте путь к ядру и "
+"дампу в качестве двух аргументов для kgdb:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:199
+#, no-wrap
+msgid "# kgdb /boot/kernel/kernel /var/crash/vmcore.0\n"
+msgstr "# kgdb /boot/kernel/kernel /var/crash/vmcore.0\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:202
+msgid ""
+"You can debug the crash dump using the kernel sources just like you can for "
+"any other program."
+msgstr ""
+"Вы можете отлаживать дамп аварийного завершения, используя исходные коды "
+"ядра, так же, как и для любой другой программы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:209
+msgid ""
+"This dump is from a 5.2-BETA kernel and the crash comes from deep within the "
+"kernel. The output below has been modified to include line numbers on the "
+"left. This first trace inspects the instruction pointer and obtains a back "
+"trace. The address that is used on line 41 for the `list` command is the "
+"instruction pointer and can be found on line 17. Most developers will "
+"request having at least this information sent to them if you are unable to "
+"debug the problem yourself. If, however, you do solve the problem, make "
+"sure that your patch winds its way into the source tree via a problem "
+"report, mailing lists, or by being able to commit it!"
+msgstr ""
+"Этот дамп получен из ядра версии 5.2-BETA, а крах произошел глубоко внутри "
+"ядра. Приведенный ниже вывод был изменен для добавления номеров строк слева. "
+"Первый трассировочный вывод проверяет указатель инструкции и получает "
+"обратную трассировку. Адрес, используемый в строке 41 для команды `list`, "
+"является указателем инструкции и может быть найден в строке 17. Большинство "
+"разработчиков запросят как минимум эту информацию, если вы не сможете "
+"отладить проблему самостоятельно. Однако, если вы решите проблему, "
+"убедитесь, что ваш патч попадет в дерево исходников через отчет о проблеме, "
+"списки рассылки, или, может быть, у вас есть возможность его закоммитить!"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:302
+#, no-wrap
+msgid ""
+" 1:# cd /usr/obj/usr/src/sys/KERNCONF\n"
+" 2:# kgdb kernel.debug /var/crash/vmcore.0\n"
+" 3:GNU gdb 5.2.1 (FreeBSD)\n"
+" 4:Copyright 2002 Free Software Foundation, Inc.\n"
+" 5:GDB is free software, covered by the GNU General Public License, and you are\n"
+" 6:welcome to change it and/or distribute copies of it under certain conditions.\n"
+" 7:Type \"show copying\" to see the conditions.\n"
+" 8:There is absolutely no warranty for GDB. Type \"show warranty\" for details.\n"
+" 9:This GDB was configured as \"i386-undermydesk-freebsd\"...\n"
+"10:panic: page fault\n"
+"11:panic messages:\n"
+"12:---\n"
+"13:Fatal trap 12: page fault while in kernel mode\n"
+"14:cpuid = 0; apic id = 00\n"
+"15:fault virtual address = 0x300\n"
+"16:fault code: = supervisor read, page not present\n"
+"17:instruction pointer = 0x8:0xc0713860\n"
+"18:stack pointer = 0x10:0xdc1d0b70\n"
+"19:frame pointer = 0x10:0xdc1d0b7c\n"
+"20:code segment = base 0x0, limit 0xfffff, type 0x1b\n"
+"21: = DPL 0, pres 1, def32 1, gran 1\n"
+"22:processor eflags = resume, IOPL = 0\n"
+"23:current process = 14394 (uname)\n"
+"24:trap number = 12\n"
+"25:panic: page fault\n"
+"26 cpuid = 0;\n"
+"27:Stack backtrace:\n"
+"28\n"
+"29:syncing disks, buffers remaining... 2199 2199 panic: mi_switch: switch in a critical section\n"
+"30:cpuid = 0;\n"
+"31:Uptime: 2h43m19s\n"
+"32:Dumping 255 MB\n"
+"33: 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240\n"
+"34:---\n"
+"35:Reading symbols from /boot/kernel/snd_maestro3.ko...done.\n"
+"36:Loaded symbols for /boot/kernel/snd_maestro3.ko\n"
+"37:Reading symbols from /boot/kernel/snd_pcm.ko...done.\n"
+"38:Loaded symbols for /boot/kernel/snd_pcm.ko\n"
+"39:#0 doadump () at /usr/src/sys/kern/kern_shutdown.c:240\n"
+"40:240 dumping++;\n"
+"41:(kgdb) list *0xc0713860\n"
+"42:0xc0713860 is in lapic_ipi_wait (/usr/src/sys/i386/i386/local_apic.c:663).\n"
+"43:658 incr = 0;\n"
+"44:659 delay = 1;\n"
+"45:660 } else\n"
+"46:661 incr = 1;\n"
+"47:662 for (x = 0; x < delay; x += incr) {\n"
+"48:663 if ((lapic->icr_lo & APIC_DELSTAT_MASK) == APIC_DELSTAT_IDLE)\n"
+"49:664 return (1);\n"
+"50:665 ia32_pause();\n"
+"51:666 }\n"
+"52:667 return (0);\n"
+"53:(kgdb) backtrace\n"
+"54:#0 doadump () at /usr/src/sys/kern/kern_shutdown.c:240\n"
+"55:#1 0xc055fd9b in boot (howto=260) at /usr/src/sys/kern/kern_shutdown.c:372\n"
+"56:#2 0xc056019d in panic () at /usr/src/sys/kern/kern_shutdown.c:550\n"
+"57:#3 0xc0567ef5 in mi_switch () at /usr/src/sys/kern/kern_synch.c:470\n"
+"58:#4 0xc055fa87 in boot (howto=256) at /usr/src/sys/kern/kern_shutdown.c:312\n"
+"59:#5 0xc056019d in panic () at /usr/src/sys/kern/kern_shutdown.c:550\n"
+"60:#6 0xc0720c66 in trap_fatal (frame=0xdc1d0b30, eva=0)\n"
+"61: at /usr/src/sys/i386/i386/trap.c:821\n"
+"62:#7 0xc07202b3 in trap (frame=\n"
+"63: {tf_fs = -1065484264, tf_es = -1065484272, tf_ds = -1065484272, tf_edi = 1, tf_esi = 0, tf_ebp = -602076292, tf_isp = -602076324, tf_ebx = 0, tf_edx = 0, tf_ecx = 1000000, tf_eax = 243, tf_trapno = 12, tf_err = 0, tf_eip = -1066321824, tf_cs = 8, tf_eflags = 65671, tf_esp = 243, tf_ss = 0})\n"
+"64: at /usr/src/sys/i386/i386/trap.c:250\n"
+"65:#8 0xc070c9f8 in calltrap () at {standard input}:94\n"
+"66:#9 0xc07139f3 in lapic_ipi_vectored (vector=0, dest=0)\n"
+"67: at /usr/src/sys/i386/i386/local_apic.c:733\n"
+"68:#10 0xc0718b23 in ipi_selected (cpus=1, ipi=1)\n"
+"69: at /usr/src/sys/i386/i386/mp_machdep.c:1115\n"
+"70:#11 0xc057473e in kseq_notify (ke=0xcc05e360, cpu=0)\n"
+"71: at /usr/src/sys/kern/sched_ule.c:520\n"
+"72:#12 0xc0575cad in sched_add (td=0xcbcf5c80)\n"
+"73: at /usr/src/sys/kern/sched_ule.c:1366\n"
+"74:#13 0xc05666c6 in setrunqueue (td=0xcc05e360)\n"
+"75: at /usr/src/sys/kern/kern_switch.c:422\n"
+"76:#14 0xc05752f4 in sched_wakeup (td=0xcbcf5c80)\n"
+"77: at /usr/src/sys/kern/sched_ule.c:999\n"
+"78:#15 0xc056816c in setrunnable (td=0xcbcf5c80)\n"
+"79: at /usr/src/sys/kern/kern_synch.c:570\n"
+"80:#16 0xc0567d53 in wakeup (ident=0xcbcf5c80)\n"
+"81: at /usr/src/sys/kern/kern_synch.c:411\n"
+"82:#17 0xc05490a8 in exit1 (td=0xcbcf5b40, rv=0)\n"
+"83: at /usr/src/sys/kern/kern_exit.c:509\n"
+"84:#18 0xc0548011 in sys_exit () at /usr/src/sys/kern/kern_exit.c:102\n"
+"85:#19 0xc0720fd0 in syscall (frame=\n"
+"86: {tf_fs = 47, tf_es = 47, tf_ds = 47, tf_edi = 0, tf_esi = -1, tf_ebp = -1077940712, tf_isp = -602075788, tf_ebx = 672411944, tf_edx = 10, tf_ecx = 672411600, tf_eax = 1, tf_trapno = 12, tf_err = 2, tf_eip = 671899563, tf_cs = 31, tf_eflags = 642, tf_esp = -1077940740, tf_ss = 47})\n"
+"87: at /usr/src/sys/i386/i386/trap.c:1010\n"
+"88:#20 0xc070ca4d in Xint0x80_syscall () at {standard input}:136\n"
+"89:---Can't read userspace from dump, or kernel process---\n"
+"90:(kgdb) quit\n"
+msgstr ""
+" 1:# cd /usr/obj/usr/src/sys/KERNCONF\n"
+" 2:# kgdb kernel.debug /var/crash/vmcore.0\n"
+" 3:GNU gdb 5.2.1 (FreeBSD)\n"
+" 4:Copyright 2002 Free Software Foundation, Inc.\n"
+" 5:GDB is free software, covered by the GNU General Public License, and you are\n"
+" 6:welcome to change it and/or distribute copies of it under certain conditions.\n"
+" 7:Type \"show copying\" to see the conditions.\n"
+" 8:There is absolutely no warranty for GDB. Type \"show warranty\" for details.\n"
+" 9:This GDB was configured as \"i386-undermydesk-freebsd\"...\n"
+"10:panic: page fault\n"
+"11:panic messages:\n"
+"12:---\n"
+"13:Fatal trap 12: page fault while in kernel mode\n"
+"14:cpuid = 0; apic id = 00\n"
+"15:fault virtual address = 0x300\n"
+"16:fault code: = supervisor read, page not present\n"
+"17:instruction pointer = 0x8:0xc0713860\n"
+"18:stack pointer = 0x10:0xdc1d0b70\n"
+"19:frame pointer = 0x10:0xdc1d0b7c\n"
+"20:code segment = base 0x0, limit 0xfffff, type 0x1b\n"
+"21: = DPL 0, pres 1, def32 1, gran 1\n"
+"22:processor eflags = resume, IOPL = 0\n"
+"23:current process = 14394 (uname)\n"
+"24:trap number = 12\n"
+"25:panic: page fault\n"
+"26 cpuid = 0;\n"
+"27:Stack backtrace:\n"
+"28\n"
+"29:syncing disks, buffers remaining... 2199 2199 panic: mi_switch: switch in a critical section\n"
+"30:cpuid = 0;\n"
+"31:Uptime: 2h43m19s\n"
+"32:Dumping 255 MB\n"
+"33: 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240\n"
+"34:---\n"
+"35:Reading symbols from /boot/kernel/snd_maestro3.ko...done.\n"
+"36:Loaded symbols for /boot/kernel/snd_maestro3.ko\n"
+"37:Reading symbols from /boot/kernel/snd_pcm.ko...done.\n"
+"38:Loaded symbols for /boot/kernel/snd_pcm.ko\n"
+"39:#0 doadump () at /usr/src/sys/kern/kern_shutdown.c:240\n"
+"40:240 dumping++;\n"
+"41:(kgdb) list *0xc0713860\n"
+"42:0xc0713860 is in lapic_ipi_wait (/usr/src/sys/i386/i386/local_apic.c:663).\n"
+"43:658 incr = 0;\n"
+"44:659 delay = 1;\n"
+"45:660 } else\n"
+"46:661 incr = 1;\n"
+"47:662 for (x = 0; x < delay; x += incr) {\n"
+"48:663 if ((lapic->icr_lo & APIC_DELSTAT_MASK) == APIC_DELSTAT_IDLE)\n"
+"49:664 return (1);\n"
+"50:665 ia32_pause();\n"
+"51:666 }\n"
+"52:667 return (0);\n"
+"53:(kgdb) backtrace\n"
+"54:#0 doadump () at /usr/src/sys/kern/kern_shutdown.c:240\n"
+"55:#1 0xc055fd9b in boot (howto=260) at /usr/src/sys/kern/kern_shutdown.c:372\n"
+"56:#2 0xc056019d in panic () at /usr/src/sys/kern/kern_shutdown.c:550\n"
+"57:#3 0xc0567ef5 in mi_switch () at /usr/src/sys/kern/kern_synch.c:470\n"
+"58:#4 0xc055fa87 in boot (howto=256) at /usr/src/sys/kern/kern_shutdown.c:312\n"
+"59:#5 0xc056019d in panic () at /usr/src/sys/kern/kern_shutdown.c:550\n"
+"60:#6 0xc0720c66 in trap_fatal (frame=0xdc1d0b30, eva=0)\n"
+"61: at /usr/src/sys/i386/i386/trap.c:821\n"
+"62:#7 0xc07202b3 in trap (frame=\n"
+"63: {tf_fs = -1065484264, tf_es = -1065484272, tf_ds = -1065484272, tf_edi = 1, tf_esi = 0, tf_ebp = -602076292, tf_isp = -602076324, tf_ebx = 0, tf_edx = 0, tf_ecx = 1000000, tf_eax = 243, tf_trapno = 12, tf_err = 0, tf_eip = -1066321824, tf_cs = 8, tf_eflags = 65671, tf_esp = 243, tf_ss = 0})\n"
+"64: at /usr/src/sys/i386/i386/trap.c:250\n"
+"65:#8 0xc070c9f8 in calltrap () at {standard input}:94\n"
+"66:#9 0xc07139f3 in lapic_ipi_vectored (vector=0, dest=0)\n"
+"67: at /usr/src/sys/i386/i386/local_apic.c:733\n"
+"68:#10 0xc0718b23 in ipi_selected (cpus=1, ipi=1)\n"
+"69: at /usr/src/sys/i386/i386/mp_machdep.c:1115\n"
+"70:#11 0xc057473e in kseq_notify (ke=0xcc05e360, cpu=0)\n"
+"71: at /usr/src/sys/kern/sched_ule.c:520\n"
+"72:#12 0xc0575cad in sched_add (td=0xcbcf5c80)\n"
+"73: at /usr/src/sys/kern/sched_ule.c:1366\n"
+"74:#13 0xc05666c6 in setrunqueue (td=0xcc05e360)\n"
+"75: at /usr/src/sys/kern/kern_switch.c:422\n"
+"76:#14 0xc05752f4 in sched_wakeup (td=0xcbcf5c80)\n"
+"77: at /usr/src/sys/kern/sched_ule.c:999\n"
+"78:#15 0xc056816c in setrunnable (td=0xcbcf5c80)\n"
+"79: at /usr/src/sys/kern/kern_synch.c:570\n"
+"80:#16 0xc0567d53 in wakeup (ident=0xcbcf5c80)\n"
+"81: at /usr/src/sys/kern/kern_synch.c:411\n"
+"82:#17 0xc05490a8 in exit1 (td=0xcbcf5b40, rv=0)\n"
+"83: at /usr/src/sys/kern/kern_exit.c:509\n"
+"84:#18 0xc0548011 in sys_exit () at /usr/src/sys/kern/kern_exit.c:102\n"
+"85:#19 0xc0720fd0 in syscall (frame=\n"
+"86: {tf_fs = 47, tf_es = 47, tf_ds = 47, tf_edi = 0, tf_esi = -1, tf_ebp = -1077940712, tf_isp = -602075788, tf_ebx = 672411944, tf_edx = 10, tf_ecx = 672411600, tf_eax = 1, tf_trapno = 12, tf_err = 2, tf_eip = 671899563, tf_cs = 31, tf_eflags = 642, tf_esp = -1077940740, tf_ss = 47})\n"
+"87: at /usr/src/sys/i386/i386/trap.c:1010\n"
+"88:#20 0xc070ca4d in Xint0x80_syscall () at {standard input}:136\n"
+"89:---Can't read userspace from dump, or kernel process---\n"
+"90:(kgdb) quit\n"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:308
+msgid ""
+"If your system is crashing regularly and you are running out of disk space, "
+"deleting old [.filename]#vmcore# files in [.filename]#/var/crash# could save "
+"a considerable amount of disk space!"
+msgstr ""
+"Если ваша система регулярно завершается аварийно и у вас заканчивается место "
+"на диске, удаление старых файлов [.filename]#vmcore# в [.filename]#/var/"
+"crash# может освободить значительное количество дискового пространства!"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:311
+#, no-wrap
+msgid "On-Line Kernel Debugging Using DDB"
+msgstr "Онлайн-отладка ядра с использованием DDB"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:315
+msgid ""
+"While `kgdb` as an off-line debugger provides a very high level of user "
+"interface, there are some things it cannot do. The most important ones "
+"being breakpointing and single-stepping kernel code."
+msgstr ""
+"В то время как `kgdb` как автономный отладчик предоставляет очень высокий "
+"уровень пользовательского интерфейса, есть некоторые вещи, которые он не "
+"может выполнить. Наиболее важные из них — установка точек останова и "
+"пошаговое выполнение кода ядра."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:319
+msgid ""
+"If you need to do low-level debugging on your kernel, there is an on-line "
+"debugger available called DDB. It allows setting of breakpoints, single-"
+"stepping kernel functions, examining and changing kernel variables, etc. "
+"However, it cannot access kernel source files, and only has access to the "
+"global and static symbols, not to the full debug information like `kgdb` "
+"does."
+msgstr ""
+"Если вам требуется выполнить низкоуровневую отладку ядра, доступен отладчик "
+"DDB, работающий в режиме реального времени. Он позволяет устанавливать точки "
+"останова, выполнять пошаговое выполнение функций ядра, проверять и изменять "
+"переменные ядра и т.д. Однако он не имеет доступа к исходным файлам ядра и "
+"работает только с глобальными и статическими символами, без доступа к полной "
+"отладочной информации, как это делает `kgdb`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:321
+msgid "To configure your kernel to include DDB, add the options"
+msgstr "Для настройки ядра с включенной поддержкой DDB добавьте параметры"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:324
+#, no-wrap
+msgid "options KDB\n"
+msgstr "options KDB\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:329
+#, no-wrap
+msgid "options DDB\n"
+msgstr "options DDB\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:333
+msgid ""
+"to your config file, and rebuild. (See extref:{handbook}[The FreeBSD "
+"Handbook] for details on configuring the FreeBSD kernel)."
+msgstr ""
+"в ваш конфигурационный файл, и пересоберите. (Подробности о настройке ядра "
+"FreeBSD см. в extref:{handbook}[Руководстве FreeBSD])."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:339
+msgid ""
+"Once your DDB kernel is running, there are several ways to enter DDB. The "
+"first, and earliest way is to use the boot flag `-d`. The kernel will start "
+"up in debug mode and enter DDB prior to any device probing. Hence you can "
+"even debug the device probe/attach functions. To use this, exit the "
+"loader's boot menu and enter `boot -d` at the loader prompt."
+msgstr ""
+"После загрузки ядра DDB существует несколько способов войти в него. Первый и "
+"самый ранний способ — использовать флаг загрузки `-d`. Ядро запустится в "
+"режиме отладки и перейдет в DDB до начала обнаружения любого из устройств. "
+"Таким образом, можно отлаживать даже функции обнаружить (probe)/ "
+"присоединить (attach) устройств. Для использования этого метода выйдите из "
+"меню загрузки загрузчика и введите `boot -d` в командной строке загрузчика."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:343
+msgid ""
+"The second scenario is to drop to the debugger once the system has booted. "
+"There are two simple ways to accomplish this. If you would like to break to "
+"the debugger from the command prompt, simply type the command:"
+msgstr ""
+"Второй сценарий — перейти в отладчик после загрузки системы. Есть два "
+"простых способа это сделать. Если вы хотите перейти в отладчик из командной "
+"строки, просто введите команду:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:347
+#, no-wrap
+msgid "# sysctl debug.kdb.enter=1\n"
+msgstr "# sysctl debug.kdb.enter=1\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:354
+msgid ""
+"Alternatively, if you are at the system console, you may use a hot-key on "
+"the keyboard. The default break-to-debugger sequence is kbd:"
+"[Ctrl+Alt+ESC]. For syscons, this sequence can be remapped and some of the "
+"distributed maps out there do this, so check to make sure you know the right "
+"sequence to use. There is an option available for serial consoles that "
+"allows the use of a serial line BREAK on the console line to enter DDB "
+"(`options BREAK_TO_DEBUGGER` in the kernel config file). It is not the "
+"default since there are a lot of serial adapters around that gratuitously "
+"generate a BREAK condition, for example when pulling the cable."
+msgstr ""
+"В качестве альтернативы, если вы находитесь за системной консолью, можно "
+"использовать горячую клавишу на клавиатуре. Стандартной комбинацией для "
+"перехода в отладчик является kbd:[Ctrl+Alt+ESC]. В syscons эта "
+"последовательность может быть переназначена, и некоторые распространённые "
+"раскладки клавиатуры делают это, поэтому убедитесь, что знаете правильную "
+"комбинацию. Для последовательных консолей доступна опция, позволяющая "
+"использовать сигнал BREAK на линии консоли для входа в DDB (`options "
+"BREAK_TO_DEBUGGER` в конфигурационном файле ядра). Это не установлено по "
+"умолчанию, так как существует множество последовательных адаптеров, которые "
+"излишне генерируют условие BREAK, например, при отключении кабеля."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:357
+msgid ""
+"The third way is that any panic condition will branch to DDB if the kernel "
+"is configured to use it. For this reason, it is not wise to configure a "
+"kernel with DDB for a machine running unattended."
+msgstr ""
+"Третий способ заключается в том, чтобы любое условие паники переходило в "
+"DDB, если ядро настроено на его использование. По этой причине не "
+"рекомендуется настраивать ядро с DDB для машины, работающей без присмотра."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:359
+msgid "To obtain the unattended functionality, add:"
+msgstr "Для получения неинтерактивной функциональности добавьте:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:363
+#, no-wrap
+msgid "options\tKDB_UNATTENDED\n"
+msgstr "options\tKDB_UNATTENDED\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:366
+msgid "to the kernel configuration file and rebuild/reinstall."
+msgstr "в файл конфигурации ядра и пересоберите/переустановите ядро."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:369
+msgid ""
+"The DDB commands roughly resemble some `gdb` commands. The first thing you "
+"probably need to do is to set a breakpoint:"
+msgstr ""
+"Команды DDB примерно напоминают некоторые команды `gdb`. Первое, что вам, "
+"вероятно, нужно сделать, это установить точку останова:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:373
+#, no-wrap
+msgid " break function-name address\n"
+msgstr " break function-name address\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:378
+msgid ""
+"Numbers are taken hexadecimal by default, but to make them distinct from "
+"symbol names; hexadecimal numbers starting with the letters `a-f` need to be "
+"preceded with `0x` (this is optional for other numbers). Simple expressions "
+"are allowed, for example: `function-name + 0x103`."
+msgstr ""
+"Числа по умолчанию интерпретируются как шестнадцатеричные, но чтобы отличить "
+"их от символьных имен, шестнадцатеричные числа, начинающиеся с букв `a-f`, "
+"должны предваряться префиксом `0x` (для остальных чисел это необязательно). "
+"Допускаются простые выражения, например: `function-name + 0x103`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:380
+msgid "To exit the debugger and continue execution, type:"
+msgstr "Для выхода из отладчика и продолжения выполнения введите:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:384
+#, no-wrap
+msgid " continue\n"
+msgstr " continue\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:387
+msgid "To get a stack trace of the current thread, use:"
+msgstr "Для получения трассировки стека текущего потока используйте:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:391
+#, no-wrap
+msgid " trace\n"
+msgstr " trace\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:394
+msgid ""
+"To get a stack trace of an arbitrary thread, specify a process ID or thread "
+"ID as a second argument to `trace`."
+msgstr ""
+"Для получения трассировки стека произвольного потока укажите идентификатор "
+"процесса или идентификатор потока в качестве второго аргумента команды "
+"`trace`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:396
+msgid "If you want to remove a breakpoint, use"
+msgstr "Если вы хотите удалить точку останова, используйте"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:401
+#, no-wrap
+msgid ""
+" del\n"
+" del address-expression\n"
+msgstr ""
+" del\n"
+" del address-expression\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:405
+msgid ""
+"The first form will be accepted immediately after a breakpoint hit, and "
+"deletes the current breakpoint. The second form can remove any breakpoint, "
+"but you need to specify the exact address; this can be obtained from:"
+msgstr ""
+"Первая форма будет принята сразу после срабатывания точки останова и удаляет "
+"текущую точку останова. Вторая форма может удалить любую точку останова, но "
+"необходимо указать точный адрес; его можно получить из:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:409
+#, no-wrap
+msgid " show b\n"
+msgstr " show b\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:412
+msgid "or:"
+msgstr "или:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:416
+#, no-wrap
+msgid " show break\n"
+msgstr " show break\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:419
+msgid "To single-step the kernel, try:"
+msgstr "Для пошагового выполнения ядра попробуйте:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:423
+#, no-wrap
+msgid " s\n"
+msgstr " s\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:426
+msgid ""
+"This will step into functions, but you can make DDB trace them until the "
+"matching return statement is reached by:"
+msgstr ""
+"Это позволит войти в функции, но вы можете заставить DDB отслеживать их до "
+"достижения соответствующего оператора return с помощью:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:430
+#, no-wrap
+msgid " n\n"
+msgstr " n\n"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:436
+msgid ""
+"This is different from ``gdb``'s `next` statement; it is like ``gdb``'s "
+"`finish`. Pressing kbd:[n] more than once will cause a continue."
+msgstr ""
+"Это отличается от оператора `next` в ``gdb``; это похоже на `finish` в "
+"``gdb``. Нажатие kbd:[n] более одного раза приведёт к продолжению."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:439
+msgid "To examine data from memory, use (for example):"
+msgstr "Для просмотра данных в памяти используйте (например):"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:446
+#, no-wrap
+msgid ""
+" x/wx 0xf0133fe0,40\n"
+" x/hd db_symtab_space\n"
+" x/bc termbuf,10\n"
+" x/s stringbuf\n"
+msgstr ""
+" x/wx 0xf0133fe0,40\n"
+" x/hd db_symtab_space\n"
+" x/bc termbuf,10\n"
+" x/s stringbuf\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:451
+msgid ""
+"for word/halfword/byte access, and hexadecimal/decimal/character/ string "
+"display. The number after the comma is the object count. To display the "
+"next 0x10 items, simply use:"
+msgstr ""
+"для доступа к словам/полусловам/байтам и отображения в шестнадцатеричном/"
+"десятичном/символьном/строковом формате. Число после запятой указывает "
+"количество объектов. Для отображения следующих 0x10 элементов просто введите:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:455
+#, no-wrap
+msgid " x ,10\n"
+msgstr " x ,10\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:458
+msgid "Similarly, use"
+msgstr "Аналогично, используйте"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:462
+#, no-wrap
+msgid " x/ia foofunc,10\n"
+msgstr " x/ia foofunc,10\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:465
+msgid ""
+"to disassemble the first 0x10 instructions of `foofunc`, and display them "
+"along with their offset from the beginning of `foofunc`."
+msgstr ""
+"для дизассемблирования первых 0x10 инструкций функции `foofunc` и их "
+"отображения вместе с их смещением от начала `foofunc`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:467
+msgid "To modify memory, use the write command:"
+msgstr "Для записи в память используйте команду write:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:472
+#, no-wrap
+msgid ""
+" w/b termbuf 0xa 0xb 0\n"
+" w/w 0xf0010030 0 0\n"
+msgstr ""
+" w/b termbuf 0xa 0xb 0\n"
+" w/w 0xf0010030 0 0\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:476
+msgid ""
+"The command modifier (`b`/`h`/`w`) specifies the size of the data to be "
+"written, the first following expression is the address to write to and the "
+"remainder is interpreted as data to write to successive memory locations."
+msgstr ""
+"Модификатор команды (`b`/`h`/`w`) определяет размер данных для записи, "
+"первое следующее выражение — это адрес для записи, а остальное "
+"интерпретируется как данные для записи в последующие ячейки памяти."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:478
+msgid "If you need to know the current registers, use:"
+msgstr "Если вам необходимо узнать текущее содержимое регистров, введите:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:482
+#, no-wrap
+msgid " show reg\n"
+msgstr " show reg\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:485
+msgid "Alternatively, you can display a single register value by e.g."
+msgstr "Также можно отобразить значение одного регистра, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:489
+#, no-wrap
+msgid " p $eax\n"
+msgstr " p $eax\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:492
+msgid "and modify it by:"
+msgstr "и изменить его с помощью:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:496
+#, no-wrap
+msgid " set $eax new-value\n"
+msgstr " set $eax new-value\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:499
+msgid "Should you need to call some kernel functions from DDB, simply say:"
+msgstr ""
+"Если вам потребуется вызвать некоторые функции ядра из DDB, просто напишите:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:503
+#, no-wrap
+msgid " call func(arg1, arg2, ...)\n"
+msgstr " call func(arg1, arg2, ...)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:506
+msgid "The return value will be printed."
+msgstr "Будет выведено возвращаемое значение."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:508
+msgid "For a man:ps[1] style summary of all running processes, use:"
+msgstr ""
+"Для вывода информации о всех запущенных процессах в стиле man:ps[1] "
+"используйте:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:512
+#, no-wrap
+msgid " ps\n"
+msgstr " ps\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:517
+msgid ""
+"Now you have examined why your kernel failed, and you wish to reboot. "
+"Remember that, depending on the severity of previous malfunctioning, not all "
+"parts of the kernel might still be working as expected. Perform one of the "
+"following actions to shut down and reboot your system:"
+msgstr ""
+"Теперь вы выяснили причину сбоя ядра и хотите выполнить перезагрузку. "
+"Помните, что в зависимости от серьезности предыдущего сбоя не все части ядра "
+"могут работать корректно. Выполните одно из следующих действий для "
+"завершения работы и перезагрузки системы:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:521
+#, no-wrap
+msgid " panic\n"
+msgstr " panic\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:524
+msgid ""
+"This will cause your kernel to dump core and reboot, so you can later "
+"analyze the core on a higher level with man:kgdb[1]."
+msgstr ""
+"Это приведёт к дампу ядра и перезагрузке, чтобы позже можно было "
+"проанализировать дамп на более высоком уровне с помощью man:kgdb[1]."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:528
+#, no-wrap
+msgid " call boot(0)\n"
+msgstr " call boot(0)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:532
+msgid ""
+"Might be a good way to cleanly shut down the running system, `sync()` all "
+"disks, and finally, in some cases, reboot. As long as the disk and "
+"filesystem interfaces of the kernel are not damaged, this could be a good "
+"way for an almost clean shutdown."
+msgstr ""
+"Может быть хорошим способом чисто завершить работу работающей системы, "
+"`sync()` все диски и, наконец, в некоторых случаях перезагрузиться. Пока "
+"интерфейсы дисков и файловых систем ядра не повреждены, это может быть "
+"хорошим способом для почти чистого завершения работы."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:536
+#, no-wrap
+msgid " reset\n"
+msgstr " reset\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:539
+msgid ""
+"This is the final way out of disaster and almost the same as hitting the Big "
+"Red Button."
+msgstr ""
+"Это последний способ избежать катастрофы, и он почти такой же, как нажатие "
+"на Большую Красную Кнопку."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:541
+msgid "If you need a short command summary, simply type:"
+msgstr "Если вам нужна краткая сводка команд, просто введите:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:545
+#, no-wrap
+msgid " help\n"
+msgstr " help\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:549
+msgid ""
+"It is highly recommended to have a printed copy of the man:ddb[4] manual "
+"page ready for a debugging session. Remember that it is hard to read the on-"
+"line manual while single-stepping the kernel."
+msgstr ""
+"Настоятельно рекомендуется иметь распечатанную копию страницы руководства "
+"man:ddb[4] для сеанса отладки. Помните, что читать онлайн-руководство во "
+"время пошагового выполнения ядра сложно."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:551
+#, no-wrap
+msgid "On-Line Kernel Debugging Using Remote GDB"
+msgstr "Онлайн-отладка ядра с использованием удаленного GDB"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:555
+msgid ""
+"The FreeBSD kernel provides a second KDB backend for on-line debugging: "
+"man:gdb[4]. This feature has been supported since FreeBSD 2.2, and it is "
+"actually a very neat one."
+msgstr ""
+"Ядро FreeBSD предоставляет второй бэкенд KDB для отладки в реальном времени: "
+"man:gdb[4]. Эта возможность поддерживается с FreeBSD 2.2 и является "
+"действительно очень удобной."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:561
+msgid ""
+"GDB has supported _remote debugging_ for a long time. This is done using a "
+"very simple protocol along a serial line. Unlike the other debugging "
+"methods described above, you will need two machines for doing this. One is "
+"the host providing the debugging environment, including all the sources, and "
+"a copy of the kernel binary with all the symbols in it. The other is the "
+"target machine that runs a copy of the very same kernel (optionally stripped "
+"of the debugging information)."
+msgstr ""
+"GDB давно поддерживает _удалённую отладку_. Это осуществляется с помощью "
+"очень простого протокола через последовательное соединение. В отличие от "
+"других методов отладки, описанных выше, для этого потребуются две машины. "
+"Одна — это хост, предоставляющий среду отладки, включая все исходные тексты "
+"и копию бинарного файла ядра со всеми символами. Другая — целевая машина, на "
+"которой запущена копия того же самого ядра (возможно, без отладочной "
+"информации)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:563
+msgid ""
+"In order to use remote GDB, ensure that the following options are present in "
+"your kernel configuration:"
+msgstr ""
+"Чтобы использовать удалённый GDB, убедитесь, что следующие параметры "
+"присутствуют в конфигурации вашего ядра:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:568
+#, no-wrap
+msgid ""
+"makeoptions DEBUG=-g\n"
+"options KDB\n"
+"options GDB\n"
+msgstr ""
+"makeoptions DEBUG=-g\n"
+"options KDB\n"
+"options GDB\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:571
+msgid ""
+"Note that the `GDB` option is turned off by default in `GENERIC` kernels on "
+"-STABLE and -RELEASE branches, but enabled on -CURRENT."
+msgstr ""
+"Обратите внимание, что опция `GDB` отключена по умолчанию в ядрах `GENERIC` "
+"для веток -STABLE и -RELEASE, но включена в -CURRENT."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:575
+msgid ""
+"Once built, copy the kernel to the target machine, and boot it. Connect the "
+"serial line of the target machine that has \"flags 080\" set on its uart "
+"device to any serial line of the debugging host. See man:uart[4] for "
+"information on how to set the flags on a uart device."
+msgstr ""
+"После сборки скопируйте ядро на целевую машину и загрузите его. Подключите "
+"последовательный порт целевой машины, у которого на устройстве uart "
+"установлены флаги \"080\", к любому последовательному порту отладочной "
+"машины. Подробности о настройке флагов на устройстве uart смотрите в "
+"man:uart[4]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:578
+msgid ""
+"The target machine must be made to enter the GDB backend, either due to a "
+"panic or by taking a purposeful trap into the debugger. Before doing this, "
+"select the GDB debugger backend:"
+msgstr ""
+"Целевая машина должна быть переведена в режим отладчика GDB, либо из-за "
+"паники, либо путем преднамеренного перехода в отладчик. Перед этим выберите "
+"бэкенд отладчика GDB:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:582
+#, no-wrap
+msgid ""
+"# sysctl debug.kdb.current=gdb\n"
+"debug.kdb.current: ddb -> gdb\n"
+msgstr ""
+"# sysctl debug.kdb.current=gdb\n"
+"debug.kdb.current: ddb -> gdb\n"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:589
+msgid ""
+"The supported backends can be listed by the `debug.kdb.available` sysctl. "
+"If the kernel configuration includes `options DDB`, then man:ddb[4] will be "
+"selected by default. If `gdb` does not appear in the list of available "
+"backends, then the debug serial port may not have been configured correctly."
+msgstr ""
+"Поддерживаемые бэкенды можно вывести с помощью sysctl `debug.kdb.available`. "
+"Если конфигурация ядра включает `options DDB`, то man:ddb[4] будет выбран по "
+"умолчанию. Если `gdb` не отображается в списке доступных бэкендов, значит, "
+"последовательный порт отладки может быть настроен неправильно."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:592
+msgid "Then, force entry to the debugger:"
+msgstr "Затем принудительно войдите в отладчик:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:596
+#, no-wrap
+msgid ""
+"# sysctl debug.kdb.enter=1\n"
+"debug.kdb.enter: 0KDB: enter: sysctl debug.kdb.enter\n"
+msgstr ""
+"# sysctl debug.kdb.enter=1\n"
+"debug.kdb.enter: 0KDB: enter: sysctl debug.kdb.enter\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:600
+msgid ""
+"The target machine now awaits connection from a remote GDB client. On the "
+"debugging machine, go to the compile directory of the target kernel, and "
+"start `gdb`:"
+msgstr ""
+"Целевая машина теперь ожидает подключения от удалённого клиента GDB. На "
+"машине для отладки перейдите в каталог сборки целевого ядра и запустите "
+"`gdb`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:611
+#, no-wrap
+msgid ""
+"# cd /usr/obj/usr/src/amd64.amd64/sys/GENERIC/\n"
+"# kgdb kernel\n"
+"GNU gdb (GDB) 10.2 [GDB v10.2 for FreeBSD]\n"
+"Copyright (C) 2021 Free Software Foundation, Inc.\n"
+"...\n"
+"Reading symbols from kernel...\n"
+"Reading symbols from /usr/obj/usr/src/amd64.amd64/sys/GENERIC/kernel.debug...\n"
+"(kgdb)\n"
+msgstr ""
+"# cd /usr/obj/usr/src/amd64.amd64/sys/GENERIC/\n"
+"# kgdb kernel\n"
+"GNU gdb (GDB) 10.2 [GDB v10.2 for FreeBSD]\n"
+"Copyright (C) 2021 Free Software Foundation, Inc.\n"
+"...\n"
+"Reading symbols from kernel...\n"
+"Reading symbols from /usr/obj/usr/src/amd64.amd64/sys/GENERIC/kernel.debug...\n"
+"(kgdb)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:614
+msgid ""
+"Initialize the remote debugging session (assuming the first serial port is "
+"being used) by:"
+msgstr ""
+"Инициализируйте сеанс удаленной отладки (предполагая, что используется "
+"первый последовательный порт) с помощью:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:618
+#, no-wrap
+msgid "(kgdb) target remote /dev/cuau0\n"
+msgstr "(kgdb) target remote /dev/cuau0\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:621
+msgid "Your hosting GDB will now gain control over the target kernel:"
+msgstr "Ваш хостинг GDB теперь получит контроль над целевым ядром:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:628
+#, no-wrap
+msgid ""
+"Remote debugging using /dev/cuau0\n"
+"kdb_enter (why=<optimized out>, msg=<optimized out>) at /usr/src/sys/kern/subr_kdb.c:506\n"
+"506 kdb_why = KDB_WHY_UNSET;\n"
+"(kgdb)\n"
+msgstr ""
+"Remote debugging using /dev/cuau0\n"
+"kdb_enter (why=<optimized out>, msg=<optimized out>) at /usr/src/sys/kern/subr_kdb.c:506\n"
+"506 kdb_why = KDB_WHY_UNSET;\n"
+"(kgdb)\n"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:636
+msgid ""
+"Depending on the compiler used, some local variables may appear as "
+"`<optimized out>`, preventing them from being inspected directly by `gdb`. "
+"If this causes problems while debugging, it is possible to build the kernel "
+"at a decreased optimization level, which may improve the visibility of some "
+"variables. This can be done by passing `COPTFLAGS=-O1` to man:make[1]. "
+"However, certain classes of kernel bugs may manifest differently (or not at "
+"all) when the optimization level is changed."
+msgstr ""
+"В зависимости от используемого компилятора, некоторые локальные переменные "
+"могут отображаться как `<optimized out>`, что не позволяет их напрямую "
+"исследовать с помощью `gdb`. Если это вызывает проблемы при отладке, можно "
+"собрать ядро с пониженным уровнем оптимизации, что может улучшить видимость "
+"некоторых переменных. Это можно сделать, передав `COPTFLAGS=-O1` в "
+"man:make[1]. Однако определённые классы ошибок в ядре могут проявляться "
+"иначе (или вообще не проявляться) при изменении уровня оптимизации."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:640
+msgid ""
+"You can use this session almost as any other GDB session, including full "
+"access to the source, running it in gud-mode inside an Emacs window (which "
+"gives you an automatic source code display in another Emacs window), etc."
+msgstr ""
+"Вы можете использовать этот сеанс почти как любой другой сеанс GDB, включая "
+"полный доступ к исходному коду, запуск в режиме gud (Grand Unified Debugger) "
+"внутри окна Emacs (что дает автоматическое отображение исходного кода в "
+"другом окне Emacs) и т.д."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:642
+#, no-wrap
+msgid "Debugging a Console Driver"
+msgstr "Отладка драйвера консоли"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:648
+msgid ""
+"Since you need a console driver to run DDB on, things are more complicated "
+"if the console driver itself is failing. You might remember the use of a "
+"serial console (either with modified boot blocks, or by specifying `-h` at "
+"the `Boot:` prompt), and hook up a standard terminal onto your first serial "
+"port. DDB works on any configured console driver, including a serial "
+"console."
+msgstr ""
+"Поскольку для работы DDB требуется драйвер консоли, ситуация усложняется, "
+"если сам драйвер консоли неисправен. Возможно, вы вспомните о возможности "
+"использования последовательной консоли (либо с модифицированными "
+"загрузочными блоками, либо указав `-h` в строке `Boot:`), подключив "
+"стандартный терминал к первому последовательному порту. DDB работает с любым "
+"настроенным драйвером консоли, включая последовательную консоль."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:650
+#, no-wrap
+msgid "Debugging Deadlocks"
+msgstr "Отладка взаимоблокировок"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:655
+msgid ""
+"You may experience so called deadlocks, a situation where a system stops "
+"doing useful work. To provide a helpful bug report in this situation, use "
+"man:ddb[4] as described in the previous section. Include the output of `ps` "
+"and `trace` for suspected processes in the report."
+msgstr ""
+"Вы можете столкнуться с так называемыми взаимоблокировками — ситуацией, "
+"когда система перестает выполнять полезную работу. Чтобы предоставить "
+"полезный отчет об ошибке в такой ситуации, используйте man:ddb[4], как "
+"описано в предыдущем разделе. Включите в отчет вывод команд `ps` и `trace` "
+"для подозрительных процессов."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:659
+msgid ""
+"If possible, consider doing further investigation. The recipe below is "
+"especially useful if you suspect that a deadlock occurs in the VFS layer. "
+"Add these options to the kernel configuration file."
+msgstr ""
+"Если возможно, рассмотрите проведение дополнительного исследования. "
+"Приведенный ниже рецепт особенно полезен, если вы подозреваете, что взаимная "
+"блокировка происходит на уровне VFS. Добавьте следующие параметры в файл "
+"конфигурации ядра."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:670
+#, no-wrap
+msgid ""
+"makeoptions \tDEBUG=-g\n"
+"options \tINVARIANTS\n"
+"options \tINVARIANT_SUPPORT\n"
+"options \tWITNESS\n"
+"options \tWITNESS_SKIPSPIN\n"
+"options \tDEBUG_LOCKS\n"
+"options \tDEBUG_VFS_LOCKS\n"
+"options \tDIAGNOSTIC\n"
+msgstr ""
+"makeoptions \tDEBUG=-g\n"
+"options \tINVARIANTS\n"
+"options \tINVARIANT_SUPPORT\n"
+"options \tWITNESS\n"
+"options \tWITNESS_SKIPSPIN\n"
+"options \tDEBUG_LOCKS\n"
+"options \tDEBUG_VFS_LOCKS\n"
+"options \tDIAGNOSTIC\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:673
+msgid ""
+"When a deadlock occurs, in addition to the output of the `ps` command, "
+"provide information from the `show pcpu`, `show allpcpu`, `show locks`, "
+"`show alllocks`, `show lockedvnods` and `alltrace`."
+msgstr ""
+"При возникновении взаимоблокировки, помимо вывода команды `ps`, предоставьте "
+"информацию из `show pcpu`, `show allpcpu`, `show locks`, `show alllocks`, "
+"`show lockedvnods` и `alltrace`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:675
+msgid ""
+"To obtain meaningful backtraces for threaded processes, use `thread thread-"
+"id` to switch to the thread stack, and do a backtrace with `where`."
+msgstr ""
+"Для получения осмысленных трассировок стека для потоковых процессов "
+"используйте `thread thread-id` для переключения на стек потока и выполните "
+"трассировку с помощью `where`."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:677
+#, no-wrap
+msgid "Kernel debugging with Dcons"
+msgstr "Отладка ядра с помощью Dcons"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:683
+msgid ""
+"man:dcons[4] is a very simple console driver that is not directly connected "
+"with any physical devices. It just reads and writes characters from and to "
+"a buffer in a kernel or loader. Due to its simple nature, it is very useful "
+"for kernel debugging, especially with a FireWire(R) device. Currently, "
+"FreeBSD provides two ways to interact with the buffer from outside of the "
+"kernel using man:dconschat[8]."
+msgstr ""
+"man:dcons[4] — это очень простой драйвер консоли, который не связан напрямую "
+"с какими-либо физическими устройствами. Он просто читает и записывает "
+"символы из буфера в ядре или загрузчике и обратно. Благодаря своей простоте "
+"он очень полезен для отладки ядра, особенно с устройством FireWire(R). В "
+"настоящее время FreeBSD предоставляет два способа взаимодействия с буфером "
+"извне ядра с помощью man:dconschat[8]."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:684
+#, no-wrap
+msgid "Dcons over FireWire(R)"
+msgstr "Dcons через FireWire(R)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:692
+msgid ""
+"Most FireWire(R) (IEEE1394) host controllers are based on the OHCI "
+"specification that supports physical access to the host memory. This means "
+"that once the host controller is initialized, we can access the host memory "
+"without the help of software (kernel). We can exploit this facility for "
+"interaction with man:dcons[4]. man:dcons[4] provides similar functionality "
+"as a serial console. It emulates two serial ports, one for the console and "
+"DDB, the other for GDB. Since remote memory access is fully handled by the "
+"hardware, the man:dcons[4] buffer is accessible even when the system crashes."
+msgstr ""
+"Большинство контроллеров FireWire(R) (IEEE1394) основаны на спецификации "
+"OHCI, которая поддерживает физический доступ к памяти хоста. Это означает, "
+"что после инициализации контроллера хоста мы можем получить доступ к памяти "
+"хоста без помощи программного обеспечения (ядра). Мы можем использовать эту "
+"возможность для взаимодействия с man:dcons[4]. man:dcons[4] предоставляет "
+"функциональность, аналогичную последовательной консоли. Он эмулирует два "
+"последовательных порта: один для консоли и DDB, другой для GDB. Поскольку "
+"удалённый доступ к памяти полностью обрабатывается аппаратным обеспечением, "
+"буфер man:dcons[4] остаётся доступным даже при крахе системы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:695
+msgid ""
+"FireWire(R) devices are not limited to those integrated into motherboards. "
+"PCI cards exist for desktops, and a cardbus interface can be purchased for "
+"laptops."
+msgstr ""
+"Устройства FireWire(R) не только встраиваются в материнские платы. Для "
+"настольных компьютеров существуют PCI-карты, а для ноутбуков можно "
+"приобрести интерфейс CardBus."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:696
+#, no-wrap
+msgid "Enabling FireWire(R) and Dcons support on the target machine"
+msgstr "Включение поддержки FireWire(R) и Dcons на целевой машине"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:699
+msgid ""
+"To enable FireWire(R) and Dcons support in the kernel of the _target "
+"machine_:"
+msgstr "Чтобы включить поддержку FireWire(R) и Dcons в ядре _целевой машины_:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:701
+msgid ""
+"Make sure your kernel supports `dcons`, `dcons_crom` and `firewire`. `Dcons` "
+"should be statically linked with the kernel. For `dcons_crom` and "
+"`firewire`, modules should be OK."
+msgstr ""
+"Убедитесь, что ваше ядро поддерживает `dcons`, `dcons_crom` и `firewire`. "
+"`Dcons` должен быть статически связан с ядром. Для `dcons_crom` и `firewire` "
+"модули должны подойти."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:702
+msgid ""
+"Make sure physical DMA is enabled. You may need to add "
+"`hw.firewire.phydma_enable=1` to [.filename]#/boot/loader.conf#."
+msgstr ""
+"Убедитесь, что физический DMA включен. Возможно, потребуется добавить "
+"`hw.firewire.phydma_enable=1` в [.filename]#/boot/loader.conf#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:703
+msgid "Add options for debugging."
+msgstr "Добавьте параметры для отладки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:704
+msgid ""
+"Add `dcons_gdb=1` in [.filename]#/boot/loader.conf# if you use GDB over "
+"FireWire(R)."
+msgstr ""
+"Добавьте `dcons_gdb=1` в [.filename]#/boot/loader.conf#, если вы используете "
+"GDB через FireWire(R)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:705
+msgid "Enable `dcons` in [.filename]#/etc/ttys#."
+msgstr "Включите `dcons` в [.filename]#/etc/ttys#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:706
+msgid ""
+"Optionally, to force `dcons` to be the high-level console, add "
+"`hw.firewire.dcons_crom.force_console=1` to [.filename]#loader.conf#."
+msgstr ""
+"Это необязательно: чтобы принудительно сделать `dcons` высокоуровневой "
+"консолью, добавьте `hw.firewire.dcons_crom.force_console=1` в "
+"[.filename]#loader.conf#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:708
+msgid ""
+"To enable FireWire(R) and Dcons support in man:loader[8] on i386 or amd64:"
+msgstr ""
+"Чтобы включить поддержку FireWire(R) и Dcons в man:loader[8] на i386 или "
+"amd64:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:710
+msgid ""
+"Add `LOADER_FIREWIRE_SUPPORT=YES` in [.filename]#/etc/make.conf# and rebuild "
+"man:loader[8]:"
+msgstr ""
+"Добавьте `LOADER_FIREWIRE_SUPPORT=YES` в [.filename]#/etc/make.conf# и "
+"пересоберите man:loader[8]:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:714
+#, no-wrap
+msgid "# cd /sys/boot/i386 && make clean && make && make install\n"
+msgstr "# cd /sys/boot/i386 && make clean && make && make install\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:717
+msgid ""
+"To enable man:dcons[4] as an active low-level console, add "
+"`boot_multicons=\"YES\"` to [.filename]#/boot/loader.conf#."
+msgstr ""
+"Чтобы включить man:dcons[4] в качестве активной низкоуровневой консоли, "
+"добавьте `boot_multicons=\"YES\"` в [.filename]#/boot/loader.conf#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:720
+msgid ""
+"Here are a few configuration examples. A sample kernel configuration file "
+"would contain:"
+msgstr ""
+"Вот несколько примеров конфигурации. Образец файла конфигурации ядра может "
+"содержать:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:729
+#, no-wrap
+msgid ""
+"device dcons\n"
+"device dcons_crom\n"
+"options KDB\n"
+"options DDB\n"
+"options GDB\n"
+"options ALT_BREAK_TO_DEBUGGER\n"
+msgstr ""
+"device dcons\n"
+"device dcons_crom\n"
+"options KDB\n"
+"options DDB\n"
+"options GDB\n"
+"options ALT_BREAK_TO_DEBUGGER\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:732
+msgid "And a sample [.filename]#/boot/loader.conf# would contain:"
+msgstr "И образец [.filename]#/boot/loader.conf# может содержать:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:740
+#, no-wrap
+msgid ""
+"dcons_crom_load=\"YES\"\n"
+"dcons_gdb=1\n"
+"boot_multicons=\"YES\"\n"
+"hw.firewire.phydma_enable=1\n"
+"hw.firewire.dcons_crom.force_console=1\n"
+msgstr ""
+"dcons_crom_load=\"YES\"\n"
+"dcons_gdb=1\n"
+"boot_multicons=\"YES\"\n"
+"hw.firewire.phydma_enable=1\n"
+"hw.firewire.dcons_crom.force_console=1\n"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:742
+#, no-wrap
+msgid "Enabling FireWire(R) and Dcons support on the host machine"
+msgstr "Включение поддержки FireWire(R) и Dcons на главной машине"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:745
+msgid "To enable FireWire(R) support in the kernel on the _host machine_:"
+msgstr "Чтобы включить поддержку FireWire(R) в ядре на _основной машине_:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:749
+#, no-wrap
+msgid "# kldload firewire\n"
+msgstr "# kldload firewire\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:752
+msgid ""
+"Find out the EUI64 (the unique 64 bit identifier) of the FireWire(R) host "
+"controller, and use man:fwcontrol[8] or `dmesg` to find the EUI64 of the "
+"target machine."
+msgstr ""
+"Определите EUI64 (уникальный 64-битный идентификатор) контроллера "
+"FireWire(R) и используйте man:fwcontrol[8] или `dmesg`, чтобы найти EUI64 "
+"целевой машины."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:754
+msgid "Run man:dconschat[8], with:"
+msgstr "Запустите man:dconschat[8], с:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:758
+#, no-wrap
+msgid "# dconschat -e \\# -br -G 12345 -t 00-11-22-33-44-55-66-77\n"
+msgstr "# dconschat -e \\# -br -G 12345 -t 00-11-22-33-44-55-66-77\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:761
+msgid ""
+"The following key combinations can be used once man:dconschat[8] is running:"
+msgstr ""
+"Следующие комбинации клавиш могут быть использованы после запуска "
+"man:dconschat[8]:"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:767
+#, no-wrap
+msgid "kbd:[~+.]"
+msgstr "kbd:[~+.]"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:769
+#, no-wrap
+msgid "Disconnect"
+msgstr "Отсоединиться"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:770
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:773
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:776
+#, no-wrap
+msgid "kbd:[~]"
+msgstr "kbd:[~]"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:772
+#, no-wrap
+msgid "ALT BREAK"
+msgstr "ALT BREAK"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:775
+#, no-wrap
+msgid "RESET target"
+msgstr "ПЕРЕЗАГРУЗИТЬ (RESET) целевую машину"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:777
+#, no-wrap
+msgid "Suspend dconschat"
+msgstr "Приостановить dconschat"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:780
+msgid ""
+"Attach remote GDB by starting man:kgdb[1] with a remote debugging session:"
+msgstr ""
+"Присоедините удаленный GDB, запустив man:kgdb[1] с сеансом удаленной отладки:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:784
+#, no-wrap
+msgid " kgdb -r :12345 kernel\n"
+msgstr " kgdb -r :12345 kernel\n"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:786
+#, no-wrap
+msgid "Some general tips"
+msgstr "Некоторые общие рекомендации"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:789
+msgid "Here are some general tips:"
+msgstr "Вот несколько общих советов:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:791
+msgid ""
+"To take full advantage of the speed of FireWire(R), disable other slow "
+"console drivers:"
+msgstr ""
+"Чтобы в полной мере использовать скорость FireWire(R), отключите другие "
+"медленные драйверы консоли:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:796
+#, no-wrap
+msgid ""
+"# conscontrol delete ttyd0\t # serial console\n"
+"# conscontrol delete consolectl\t# video/keyboard\n"
+msgstr ""
+"# conscontrol delete ttyd0\t # serial console\n"
+"# conscontrol delete consolectl\t# video/keyboard\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:799
+msgid ""
+"There exists a GDB mode for man:emacs[1]; this is what you will need to add "
+"to your [.filename]#.emacs#:"
+msgstr ""
+"Существует режим GDB для man:emacs[1]; вот что нужно добавить в ваш "
+"[.filename]#.emacs#:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:806
+#, no-wrap
+msgid ""
+"(setq gud-gdba-command-name \"kgdb -a -a -a -r :12345\")\n"
+"(setq gdb-many-windows t)\n"
+"(xterm-mouse-mode 1)\n"
+"M-x gdba\n"
+msgstr ""
+"(setq gud-gdba-command-name \"kgdb -a -a -a -r :12345\")\n"
+"(setq gdb-many-windows t)\n"
+"(xterm-mouse-mode 1)\n"
+"M-x gdba\n"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:808
+#, no-wrap
+msgid "Dcons with KVM"
+msgstr "Dcons с KVM"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:812
+msgid ""
+"We can directly read the man:dcons[4] buffer via [.filename]#/dev/mem# for "
+"live systems, and in the core dump for crashed systems. These give you "
+"similar output to `dmesg -a`, but the man:dcons[4] buffer includes more "
+"information."
+msgstr ""
+"Мы можем напрямую читать буфер man:dcons[4] через [.filename]#/dev/mem# для "
+"работающих систем и в дампе памяти для систем после аварии. Это даёт "
+"аналогичный вывод команде `dmesg -a`, но буфер man:dcons[4] содержит больше "
+"информации."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:813
+#, no-wrap
+msgid "Using Dcons with KVM"
+msgstr "Использование Dcons с KVM"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:816
+msgid "To use man:dcons[4] with KVM:"
+msgstr "Для использования man:dcons[4] с KVM:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:818
+msgid "Dump a man:dcons[4] buffer of a live system:"
+msgstr "Дамп буфера man:dcons[4] работающей системы:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:822
+#, no-wrap
+msgid "# dconschat -1\n"
+msgstr "# dconschat -1\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:825
+msgid "Dump a man:dcons[4] buffer of a crash dump:"
+msgstr "Дамп буфера man:dcons[4] аварийного дампа:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:829
+#, no-wrap
+msgid "# dconschat -1 -M vmcore.XX\n"
+msgstr "# dconschat -1 -M vmcore.XX\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:832
+msgid "Live core debugging can be done via:"
+msgstr "Отладка ядра в реальном времени может быть выполнена через:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:837
+#, no-wrap
+msgid ""
+"# fwcontrol -m target_eui64\n"
+"# kgdb kernel /dev/fwmem0.2\n"
+msgstr ""
+"# fwcontrol -m target_eui64\n"
+"# kgdb kernel /dev/fwmem0.2\n"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:840
+#, no-wrap
+msgid "Glossary of Kernel Options for Debugging"
+msgstr "Глоссарий параметров ядра для отладки"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:843
+msgid ""
+"This section provides a brief glossary of compile-time kernel options used "
+"for debugging:"
+msgstr ""
+"В этом разделе представлен краткий глоссарий параметров ядра, указываемых "
+"при компиляции и относящихся к отладке:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:845
+msgid ""
+"`options KDB`: compiles in the kernel debugger framework. Required for "
+"`options DDB` and `options GDB`. Little or no performance overhead. By "
+"default, the debugger will be entered on panic instead of an automatic "
+"reboot."
+msgstr ""
+"`options KDB`: включает фреймворк отладки ядра. Необходим для `options DDB` "
+"и `options GDB`. Практически не влияет на производительность. По умолчанию "
+"отладчик будет запущен при панике вместо автоматической перезагрузки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:846
+msgid ""
+"`options KDB_UNATTENDED`: change the default value of the "
+"`debug.debugger_on_panic` sysctl to 0, which controls whether the debugger "
+"is entered on panic. When `options KDB` is not compiled into the kernel, the "
+"behavior is to automatically reboot on panic; when it is compiled into the "
+"kernel, the default behavior is to drop into the debugger unless `options "
+"KDB_UNATTENDED` is compiled in. If you want to leave the kernel debugger "
+"compiled into the kernel but want the system to come back up unless you're "
+"on-hand to use the debugger for diagnostics, use this option."
+msgstr ""
+"`options KDB_UNATTENDED`: изменяет значение по умолчанию системной настройки "
+"`debug.debugger_on_panic` на 0, что управляет входом в отладчик при панике. "
+"Если `options KDB` не вкомпилировано в ядро, поведение по умолчанию — "
+"автоматическая перезагрузка при панике; если оно вкомпилировано в ядро, "
+"поведение по умолчанию — переход в отладчик, если не вкомпилирована опция "
+"`options KDB_UNATTENDED`. Если вы хотите оставить отладчик ядра "
+"вкомпилированным в ядро, но желаете, чтобы система перезагружалась, пока вы "
+"не готовы использовать отладчик для диагностики, используйте эту опцию."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:847
+msgid ""
+"`options KDB_TRACE`: change the default value of the `debug.trace_on_panic` "
+"sysctl to 1, which controls whether the debugger automatically prints a "
+"stack trace on panic. Especially if running with `options KDB_UNATTENDED`, "
+"this can be helpful to gather basic debugging information on the serial or "
+"firewire console while still rebooting to recover."
+msgstr ""
+"`options KDB_TRACE`: изменяет значение по умолчанию системной настройки "
+"`debug.trace_on_panic` на 1, что управляет автоматическим выводом "
+"трассировки стека при панике. Особенно полезно при использовании с `options "
+"KDB_UNATTENDED`, так как позволяет собрать базовую отладочную информацию на "
+"последовательной консоли или консоли FireWire, продолжая перезагрузку для "
+"восстановления."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:848
+msgid ""
+"`options DDB`: compile in support for the console debugger, DDB. This "
+"interactive debugger runs on whatever the active low-level console of the "
+"system is, which includes the video console, serial console, or firewire "
+"console. It provides basic integrated debugging facilities, such as stack "
+"tracing, process and thread listing, dumping of lock state, VM state, file "
+"system state, and kernel memory management. DDB does not require software "
+"running on a second machine or being able to generate a core dump or full "
+"debugging kernel symbols, and provides detailed diagnostics of the kernel at "
+"run-time. Many bugs can be fully diagnosed using only DDB output. This "
+"option depends on `options KDB`."
+msgstr ""
+"`options DDB`: включает поддержку консольного отладчика DDB. Этот "
+"интерактивный отладчик работает на активной низкоуровневой консоли системы, "
+"включая видеоконсоль, последовательную консоль или консоль FireWire. Он "
+"предоставляет базовые встроенные средства отладки, такие как трассировка "
+"стека, список процессов и потоков, вывод состояния блокировок, состояния "
+"виртуальной памяти, состояния файловой системы и управления ядром памяти. "
+"DDB не требует работы программного обеспечения на второй машине или "
+"возможности создания дампа памяти или полных символов отладки ядра, а также "
+"предоставляет детальную диагностику ядра во время выполнения. Многие ошибки "
+"могут быть полностью диагностированы с использованием только вывода DDB. Эта "
+"опция зависит от `options KDB`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:849
+msgid ""
+"`options GDB`: compile in support for the remote debugger, GDB, which can "
+"operate over serial cable or firewire. When the debugger is entered, GDB may "
+"be attached to inspect structure contents, generate stack traces, etc. Some "
+"kernel state is more awkward to access than in DDB, which is able to "
+"generate useful summaries of kernel state automatically, such as "
+"automatically walking lock debugging or kernel memory management structures, "
+"and a second machine running the debugger is required. On the other hand, "
+"GDB combines information from the kernel source and full debugging symbols, "
+"and is aware of full data structure definitions, local variables, and is "
+"scriptable. This option is not required to run GDB on a kernel core dump. "
+"This option depends on `options KDB`."
+msgstr ""
+"`options GDB`: включает поддержку удалённого отладчика GDB, который может "
+"работать через последовательный кабель или FireWire. При входе в отладчик "
+"можно подключить GDB для проверки содержимого структур, генерации "
+"трассировки стека и т.д. Некоторые состояния ядра сложнее исследовать, чем в "
+"DDB, который способен автоматически создавать полезные сводки состояния "
+"ядра, например, автоматически обходить структуры отладки блокировок или "
+"управления памятью ядра, но для этого требуется вторая машина с запущенным "
+"отладчиком. С другой стороны, GDB объединяет информацию из исходного кода "
+"ядра и полных отладочных символов, знает полные определения структур данных, "
+"локальные переменные и поддерживает написание скриптов. Эта опция не "
+"требуется для запуска GDB на дампе памяти ядра. Данная опция зависит от "
+"`options KDB`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:850
+msgid ""
+"`options BREAK_TO_DEBUGGER`, `options ALT_BREAK_TO_DEBUGGER`: allow a break "
+"signal or alternative signal on the console to enter the debugger. If the "
+"system hangs without a panic, this is a useful way to reach the debugger. "
+"Due to the current kernel locking, a break signal generated on a serial "
+"console is significantly more reliable at getting into the debugger, and is "
+"generally recommended. This option has little or no performance impact."
+msgstr ""
+"`options BREAK_TO_DEBUGGER`, `options ALT_BREAK_TO_DEBUGGER`: позволяют "
+"сигналу прерывания или альтернативному сигналу на консоли войти в отладчик. "
+"Если система зависает без паники, это полезный способ попасть в отладчик. Из-"
+"за текущей блокировки ядра сигнал прерывания, сгенерированный на "
+"последовательной консоли, значительно надежнее для входа в отладчик и обычно "
+"рекомендуется. Данная опция оказывает незначительное или нулевое влияние на "
+"производительность."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:851
+msgid ""
+"`options INVARIANTS`: compile into the kernel a large number of run-time "
+"assertion checks and tests, which constantly test the integrity of kernel "
+"data structures and the invariants of kernel algorithms. These tests can be "
+"expensive, so are not compiled in by default, but help provide useful \"fail "
+"stop\" behavior, in which certain classes of undesired behavior enter the "
+"debugger before kernel data corruption occurs, making them easier to debug. "
+"Tests include memory scrubbing and use-after-free testing, which is one of "
+"the more significant sources of overhead. This option depends on `options "
+"INVARIANT_SUPPORT`."
+msgstr ""
+"`options INVARIANTS`: включает в ядро большое количество проверок и тестов "
+"во время выполнения, которые постоянно проверяют целостность структур данных "
+"ядра и инварианты алгоритмов ядра. Эти тесты могут быть затратными, поэтому "
+"по умолчанию не включены, но они помогают обеспечить полезное поведение "
+"\"fail stop\", при котором определённые классы нежелательного поведения "
+"попадают в отладчик до возникновения повреждения данных ядра, что упрощает "
+"их отладку. Тесты включают в себя очистку памяти и проверку использования "
+"после освобождения, что является одним из наиболее значимых источников "
+"накладных расходов. Эта опция зависит от `options INVARIANT_SUPPORT`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:852
+msgid ""
+"`options INVARIANT_SUPPORT`: many of the tests present in `options "
+"INVARIANTS` require modified data structures or additional kernel symbols to "
+"be defined."
+msgstr ""
+"`options INVARIANT_SUPPORT`: многие тесты, присутствующие в `options "
+"INVARIANTS`, требуют модифицированных структур данных или определения "
+"дополнительных символов ядра."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:853
+msgid ""
+"`options WITNESS`: this option enables run-time lock order tracking and "
+"verification, and is an invaluable tool for deadlock diagnosis. WITNESS "
+"maintains a graph of acquired lock orders by lock type, and checks the graph "
+"at each acquire for cycles (implicit or explicit). If a cycle is detected, a "
+"warning and stack trace are generated to the console, indicating that a "
+"potential deadlock might have occurred. WITNESS is required in order to use "
+"the `show locks`, `show witness` and `show alllocks` DDB commands. This "
+"debug option has significant performance overhead, which may be somewhat "
+"mitigated through the use of `options WITNESS_SKIPSPIN`. Detailed "
+"documentation may be found in man:witness[4]."
+msgstr ""
+"`options WITNESS`: эта опция включает отслеживание и проверку порядка "
+"блокировок во время выполнения, что является неоценимым инструментом для "
+"диагностики взаимоблокировок. WITNESS поддерживает граф полученных порядков "
+"блокировок по типам блокировок и проверяет граф на каждом получении на "
+"наличие циклов (явных или неявных). Если цикл обнаружен, на консоль "
+"выводится предупреждение и трассировка стека, указывающие на возможное "
+"возникновение взаимоблокировки. WITNESS необходим для использования команд "
+"DDB `show locks`, `show witness` и `show alllocks`. Эта отладочная опция "
+"создает значительную нагрузку на производительность, которую можно несколько "
+"уменьшить с помощью `options WITNESS_SKIPSPIN`. Подробная документация "
+"доступна в man:witness[4]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:854
+msgid ""
+"`options WITNESS_SKIPSPIN`: disable run-time checking of spinlock lock order "
+"with WITNESS. As spin locks are acquired most frequently in the scheduler, "
+"and scheduler events occur often, this option can significantly speed up "
+"systems running with WITNESS. This option depends on `options WITNESS`."
+msgstr ""
+"`options WITNESS_SKIPSPIN`: отключает проверку порядка блокировки spinlock "
+"во время выполнения с WITNESS. Поскольку spin-блокировки чаще всего "
+"захватываются в планировщике, а события планировщика происходят часто, эта "
+"опция может значительно ускорить системы, работающие с WITNESS. Эта опция "
+"зависит от `options WITNESS`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:855
+msgid ""
+"`options WITNESS_KDB`: change the default value of the `debug.witness.kdb` "
+"sysctl to 1, which causes WITNESS to enter the debugger when a lock order "
+"violation is detected, rather than simply printing a warning. This option "
+"depends on `options WITNESS`."
+msgstr ""
+"`options WITNESS_KDB`: изменяет значение по умолчанию системной настройки "
+"`debug.witness.kdb` на 1, что приводит к входу в отладчик при обнаружении "
+"нарушения порядка блокировок вместо простого вывода предупреждения. Эта "
+"опция зависит от `options WITNESS`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:856
+msgid ""
+"`options SOCKBUF_DEBUG`: perform extensive run-time consistency checking on "
+"socket buffers, which can be useful for debugging both socket bugs and race "
+"conditions in protocols and device drivers that interact with sockets. This "
+"option significantly impacts network performance, and may change the timing "
+"in device driver races."
+msgstr ""
+"`options SOCKBUF_DEBUG`: выполнять расширенную проверку согласованности "
+"сокетных буферов во время выполнения, что может быть полезно для отладки как "
+"ошибок в сокетах, так и состояний гонки в протоколах и драйверах устройств, "
+"взаимодействующих с сокетами. Данная опция значительно влияет на "
+"производительность сети и может изменить временные параметры в состояниях "
+"гонки драйверов устройств."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:857
+msgid ""
+"`options DEBUG_VFS_LOCKS`: track lock acquisition points for lockmgr/vnode "
+"locks, expanding the amount of information displayed by `show lockedvnods` "
+"in DDB. This option has a measurable performance impact."
+msgstr ""
+"`options DEBUG_VFS_LOCKS`: отслеживает точки получения блокировок для "
+"lockmgr/vnode, расширяя объем информации, отображаемой командой `show "
+"lockedvnods` в DDB. Данная опция оказывает заметное влияние на "
+"производительность."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:858
+msgid ""
+"`options DEBUG_MEMGUARD`: a replacement for the man:malloc[9] kernel memory "
+"allocator that uses the VM system to detect reads or writes from allocated "
+"memory after free. Details may be found in man:memguard[9]. This option has "
+"a significant performance impact, but can be very helpful in debugging "
+"kernel memory corruption bugs."
+msgstr ""
+"`options DEBUG_MEMGUARD`: замена для man:malloc[9], аллокатор памяти ядра, "
+"который использует систему VM для обнаружения чтения или записи в "
+"освобождённую память. Подробности можно найти в man:memguard[9]. Данная "
+"опция значительно влияет на производительность, но может быть очень полезна "
+"при отладке ошибок повреждения памяти ядра."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:859
+msgid ""
+"`options DIAGNOSTIC`: enable additional, more expensive diagnostic tests "
+"along the lines of `options INVARIANTS`."
+msgstr ""
+"`options DIAGNOSTIC`: включает дополнительные, более затратные "
+"диагностические тесты, аналогичные `options INVARIANTS`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:860
+msgid ""
+"`options KASAN`: enable the Kernel Address Sanitizer. This enables compiler "
+"instrumentation which can be used to detect invalid memory accesses in the "
+"kernel, such as use-after-frees and buffer overflows. This largely "
+"supersedes `options DEBUG_MEMGUARD`. See man:kasan[9] for details, and for "
+"the currently supported platforms."
+msgstr ""
+"`options KASAN`: включает отладчик адресов ядра (Kernel Address Sanitizer). "
+"Это включает инструментирование компилятора, которое может использоваться "
+"для обнаружения недопустимых обращений к памяти в ядре, таких как "
+"использование после освобождения и переполнение буфера. В значительной "
+"степени заменяет `options DEBUG_MEMGUARD`. Подробности и список "
+"поддерживаемых платформ см. в man:kasan[9]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/kerneldebug/_index.adoc:860
+msgid ""
+"`options KMSAN`: enable the Kernel Memory Sanitizer. This enables compiler "
+"instrumentation which can be used to detect uses of uninitialized memory. "
+"See man:kmsan[9] for details, and for the currently supported platforms."
+msgstr ""
+"`options KMSAN`: включить отладчик использования памяти ядра (Kernel Memory "
+"Sanitizer). Это включает инструментирование компилятора, которое может "
+"использоваться для обнаружения использования неинициализированной памяти. "
+"Подробности и список поддерживаемых платформ см. в man:kmsan[9]."
+
+#~ msgid "And for DDD ([.filename]#devel/ddd#):"
+#~ msgstr "И для DDD ([.filename]#devel/ddd#):"
+
+#, no-wrap
+#~ msgid ""
+#~ "# remote serial protocol\n"
+#~ "LANG=C ddd --debugger kgdb -r :12345 kernel\n"
+#~ "# live core debug\n"
+#~ "LANG=C ddd --debugger kgdb kernel /dev/fwmem0.2\n"
+#~ msgstr ""
+#~ "# remote serial protocol\n"
+#~ "LANG=C ddd --debugger kgdb -r :12345 kernel\n"
+#~ "# live core debug\n"
+#~ "LANG=C ddd --debugger kgdb kernel /dev/fwmem0.2\n"
diff --git a/documentation/content/ru/books/developers-handbook/l10n/_index.adoc b/documentation/content/ru/books/developers-handbook/l10n/_index.adoc
new file mode 100644
index 0000000000..7b21ea51f3
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/l10n/_index.adoc
@@ -0,0 +1,228 @@
+---
+authors:
+description: 'Локализация и интернационализация - L10N и I18N в FreeBSD'
+next: books/developers-handbook/policies
+params:
+ path: /books/developers-handbook/l10n/
+prev: books/developers-handbook/secure
+showBookMenu: true
+tags: ["L10N", "I18N", "Localization", "Internationalization", "FreeBSD"]
+title: 'Глава 4. Локализация и интернационализация - L10N и I18N'
+weight: 5
+---
+
+[[l10n]]
+= Локализация и интернационализация - L10N и I18N
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 4
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/developers-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[l10n-programming]]
+== Программирование приложений с поддержкой I18N
+
+Чтобы сделать ваше приложение более полезным для пользователей, говорящих на других языках, мы надеемся, что вы будете разрабатывать его с поддержкой интернационализации (I18N). Компилятор GNU gcc и библиотеки для графического интерфейса, такие как QT и GTK, поддерживают I18N за счёт специальной обработки строк. Создание программы с поддержкой I18N очень просто. Это позволяет участникам быстро адаптировать ваше приложение для других языков. Для получения более подробной информации обратитесь к документации по I18N для конкретной библиотеки.
+
+Вопреки распространённому мнению, написание кода, соответствующего стандарту I18N, является простой задачей. Обычно оно заключается лишь в оборачивании строк в специфичные для библиотеки функции. Кроме того, убедитесь, что поддерживаются широкие или многобайтовые символы.
+
+=== Призыв к объединению усилий по интернационализации
+
+Нам стало известно, что индивидуальные усилия по интернационализации (I18N) и локализации (L10N) в каждой стране дублируют работу друг друга. Многие из нас снова и снова неэффективно изобретают велосипед. Мы надеемся, что различные крупные группы разработчиков в области I18N смогут объединится для совместной работы и ответственности, подобно Core Team в FreeBSD.
+
+В настоящее время мы надеемся, что при написании или портировании I18N-программ вы будете отправлять их в соответствующие списки рассылки FreeBSD каждой страны для тестирования. В будущем мы надеемся создать приложения, которые будут работать на всех языках "из коробки" без грязных хаков.
+
+Создана группа {freebsd-i18n}. Если вы разработчик в области I18N/L10N, присылайте свои комментарии, идеи, вопросы и всё, что относится к этой теме по вашему мнению.
+
+=== Perl и Python
+
+В Perl и Python есть библиотеки для I18N и работы с широкими символами. Пожалуйста, используйте их для соответствия требованиям I18N.
+
+[[posix-nls]]
+== Локализованные сообщения с поддержкой родного языка POSIX.1 (NLS — Native Language Support)
+
+Помимо основных функций интернационализации (I18N), таких как поддержка различных кодировок ввода или национальных соглашений, например, различных разделителей десятичных знаков, на более высоком уровне I18N можно локализовать сообщения, выводимые различными программами. Распространённый способ сделать это — использовать функции NLS POSIX.1, которые предоставляются как часть базовой системы FreeBSD.
+
+[[nls-catalogs]]
+=== Организация локализованных сообщений в файлы каталогов
+
+POSIX.1 NLS использует файлы каталогов. Эти файлы содержат локализованные сообщения в желаемой кодировке. Сообщения организованы в наборы, и каждое сообщение идентифицируется целым числом в соответствующем наборе. Файлы каталогов традиционно называются по имени локали, для которой они содержат локализованные сообщения, с добавлением расширения `.msg`. Например, венгерские сообщения для кодировки ISO8859-2 должны храниться в файле с именем [.filename]#hu_HU.ISO8859-2#.
+
+Эти файлы каталогов представляют собой обычные текстовые файлы, содержащие нумерованные сообщения. Можно добавлять комментарии, начиная строку со знака `$`. Границы наборов также разделяются специальными комментариями, где ключевое слово `set` должно следовать непосредственно за знаком `$`. После ключевого слова `set` указывается номер набора. Например:
+
+[.programlisting]
+....
+$set 1
+....
+
+Фактические записи сообщений начинаются с номера сообщения, за которым следует локализованное сообщение. Допускаются известные модификаторы из man:printf[3]:
+
+[.programlisting]
+....
+15 "File not found: %s\n"
+....
+
+Файлы языкового каталога должны быть скомпилированы в бинарный формат перед тем, как они могут быть открыты программой. Это преобразование выполняется с помощью утилиты man:gencat[1]. Её первый аргумент — это имя файла скомпилированного каталога, а последующие аргументы — входные каталоги. Локализованные сообщения также могут быть организованы в несколько файлов каталогов, и затем все они могут быть обработаны с помощью man:gencat[1].
+
+[[nls-using]]
+=== Использование файлов каталога из исходного кода
+
+Использование файлов каталогов простое. Чтобы вызвать функции, работающие с ними, необходимо включить файл [.filename]#nl_types.h#. Перед использованием каталога его нужно открыть с помощью man:catopen[3]. Функция принимает два аргумента. Первый параметр — это имя установленного и скомпилированного каталога. Обычно используется имя программы, например, grep. Это имя будет использоваться при поиске скомпилированного файла каталога. Вызов man:catopen[3] ищет этот файл в [.filename]#/usr/share/nls/locale/catname# и в [.filename]#/usr/local/share/nls/locale/catname#, где `locale` — установленная локализация, а `catname` — имя обсуждаемого каталога. Второй параметр — это константа, которая может принимать два значения:
+
+* `NL_CAT_LOCALE`, что означает, что используемый файл каталога будет основан на `LC_MESSAGES`.
+* `0`, что означает, что для открытия соответствующего каталога необходимо использовать `LANG`.
+
+Вызов man:catopen[3] возвращает идентификатор каталога типа `nl_catd`. Обратитесь к справочной странице для получения списка возможных кодов ошибок.
+
+После открытия каталога man:catgets[3] может быть использована для извлечения сообщения. Первый параметр — это идентификатор каталога, возвращаемый man:catopen[3], второй — номер набора, третий — номер сообщения, а четвертый — резервное сообщение, которое будет возвращено, если запрошенное сообщение не может быть извлечено из файла каталога.
+
+После использования файла каталога его необходимо закрыть, вызвав man:catclose[3], которая принимает один аргумент — идентификатор каталога.
+
+[[nls-example]]
+=== Практический пример
+
+Следующий пример демонстрирует простое решение по гибкому использованию каталогов NLS.
+
+Нижеследующие строки необходимо поместить в общий заголовочный файл программы, который включается во все исходные файлы, где необходимы локализованные сообщения:
+
+[.programlisting]
+....
+#ifdef WITHOUT_NLS
+#define getstr(n) nlsstr[n]
+#else
+#include nl_types.h
+
+extern nl_catd catalog;
+#define getstr(n) catgets(catalog, 1, n, nlsstr[n])
+#endif
+
+extern char *nlsstr[];
+....
+
+Далее, добавьте эти строки в глобальную секцию объявлений основного исходного файла:
+
+[.programlisting]
+....
+#ifndef WITHOUT_NLS
+#include nl_types.h
+nl_catd catalog;
+#endif
+
+/*
+ * Default messages to use when NLS is disabled or no catalog
+ * is found.
+ */
+char *nlsstr[] = {
+ "",
+/* 1*/ "some random message",
+/* 2*/ "some other message"
+};
+....
+
+Далее следуют реальные фрагменты кода, которые открывают, читают и закрывают каталог:
+
+[.programlisting]
+....
+#ifndef WITHOUT_NLS
+ catalog = catopen("myapp", NL_CAT_LOCALE);
+#endif
+
+...
+
+printf(getstr(1));
+
+...
+
+#ifndef WITHOUT_NLS
+ catclose(catalog);
+#endif
+....
+
+==== Уменьшение количества строк для локализации
+
+Хороший способ уменьшить количество строк, требующих локализации, — использовать сообщения об ошибках из libc. Это также полезно для избежания дублирования и обеспечения единообразия сообщений об ошибках, с которыми может столкнуться множество программ.
+
+Вот пример, который не использует сообщения об ошибках из libc:
+
+[.programlisting]
+....
+#include err.h
+...
+if (!S_ISDIR(st.st_mode))
+ errx(1, "argument is not a directory");
+....
+
+Это можно преобразовать для вывода сообщения об ошибке, считывая `errno` и выводя соответствующее сообщение об ошибке:
+
+[.programlisting]
+....
+#include err.h
+#include errno.h
+...
+if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ err(1, NULL);
+}
+....
+
+В этом примере пользовательская строка исключена, что упростит работу переводчиков при локализации программы, а пользователи увидят стандартное сообщение об ошибке "Not a directory" при возникновении данной ошибки. Это сообщение, вероятно, будет выглядеть более привычным для них. Обратите внимание, что для прямого доступа к `errno` потребовалось включить [.filename]#errno.h#.
+
+Стоит отметить, что бывают случаи, когда `errno` устанавливается автоматически предыдущим вызовом, поэтому нет необходимости устанавливать его явно:
+
+[.programlisting]
+....
+#include err.h
+...
+if ((p = malloc(size)) == NULL)
+ err(1, NULL);
+....
+
+[[nls-mk]]
+=== Использование [.filename]#bsd.nls.mk#
+
+Использование файлов каталогов требует нескольких повторяемых шагов, таких как компиляция каталогов и их установка в нужное место. Чтобы ещё больше упростить этот процесс, [.filename]#bsd.nls.mk# вводит некоторые макросы. Нет необходимости явно включать [.filename]#bsd.nls.mk#, он подключается автоматически из общих Makefiles, таких как [.filename]#bsd.prog.mk# или [.filename]#bsd.lib.mk#.
+
+Обычно достаточно определить `NLSNAME`, которое должно содержать имя каталога, указанное в качестве первого аргумента man:catopen[3], и перечислить файлы каталогов в `NLS` без расширения `.msg`. Вот пример, который позволяет отключить NLS при использовании с предыдущими примерами кода. Для сборки программы без поддержки NLS необходимо определить переменную `WITHOUT_NLS` man:make[1].
+
+[.programlisting]
+....
+.if !defined(WITHOUT_NLS)
+NLS= es_ES.ISO8859-1
+NLS+= hu_HU.ISO8859-2
+NLS+= pt_BR.ISO8859-1
+.else
+CFLAGS+= -DWITHOUT_NLS
+.endif
+....
+
+Обычно файлы каталогов размещаются в подкаталоге [.filename]#nls#, и это поведение по умолчанию для [.filename]#bsd.nls.mk#. Однако можно переопределить расположение каталогов с помощью переменной `NLSSRCDIR` man:make[1]. Имя по умолчанию для предварительно скомпилированных файлов каталогов также следует упомянутому ранее соглашению об именовании. Его можно переопределить, установив переменную `NLSNAME`. Существуют и другие параметры для точной настройки обработки файлов каталогов, но обычно в этом нет необходимости, поэтому они здесь не описаны. Для получения дополнительной информации о [.filename]#bsd.nls.mk# обратитесь к самому файлу — он короткий и легко понятен.
diff --git a/documentation/content/ru/books/developers-handbook/l10n/_index.po b/documentation/content/ru/books/developers-handbook/l10n/_index.po
new file mode 100644
index 0000000000..3a8d1dfcbc
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/l10n/_index.po
@@ -0,0 +1,659 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-06-24 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookl10n_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:1
+#, no-wrap
+msgid "Localization and Internationalization - L10N and I18N in FreeBSD"
+msgstr "Локализация и интернационализация - L10N и I18N в FreeBSD"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:1
+#, no-wrap
+msgid "Chapter 4. Localization and Internationalization - L10N and I18N"
+msgstr "Глава 4. Локализация и интернационализация - L10N и I18N"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:15
+#, no-wrap
+msgid "Localization and Internationalization - L10N and I18N"
+msgstr "Локализация и интернационализация - L10N и I18N"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:53
+#, no-wrap
+msgid "Programming I18N Compliant Applications"
+msgstr "Программирование приложений с поддержкой I18N"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:60
+msgid ""
+"To make your application more useful for speakers of other languages, we "
+"hope that you will program I18N compliant. The GNU gcc compiler and GUI "
+"libraries like QT and GTK support I18N through special handling of strings. "
+"Making a program I18N compliant is very easy. It allows contributors to "
+"port your application to other languages quickly. Refer to the library "
+"specific I18N documentation for more details."
+msgstr ""
+"Чтобы сделать ваше приложение более полезным для пользователей, говорящих на "
+"других языках, мы надеемся, что вы будете разрабатывать его с поддержкой "
+"интернационализации (I18N). Компилятор GNU gcc и библиотеки для графического "
+"интерфейса, такие как QT и GTK, поддерживают I18N за счёт специальной "
+"обработки строк. Создание программы с поддержкой I18N очень просто. Это "
+"позволяет участникам быстро адаптировать ваше приложение для других языков. "
+"Для получения более подробной информации обратитесь к документации по I18N "
+"для конкретной библиотеки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:64
+msgid ""
+"In contrast with common perception, I18N compliant code is easy to write. "
+"Usually, it only involves wrapping your strings with library specific "
+"functions. In addition, please be sure to allow for wide or multibyte "
+"character support."
+msgstr ""
+"Вопреки распространённому мнению, написание кода, соответствующего стандарту "
+"I18N, является простой задачей. Обычно оно заключается лишь в оборачивании "
+"строк в специфичные для библиотеки функции. Кроме того, убедитесь, что "
+"поддерживаются широкие или многобайтовые символы."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:65
+#, no-wrap
+msgid "A Call to Unify the I18N Effort"
+msgstr "Призыв к объединению усилий по интернационализации"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:70
+msgid ""
+"It has come to our attention that the individual I18N/L10N efforts for each "
+"country has been repeating each others' efforts. Many of us have been "
+"reinventing the wheel repeatedly and inefficiently. We hope that the "
+"various major groups in I18N could congregate into a group effort similar to "
+"the Core Team's responsibility."
+msgstr ""
+"Нам стало известно, что индивидуальные усилия по интернационализации (I18N) "
+"и локализации (L10N) в каждой стране дублируют работу друг друга. Многие из "
+"нас снова и снова неэффективно изобретают велосипед. Мы надеемся, что "
+"различные крупные группы разработчиков в области I18N смогут объединится для "
+"совместной работы и ответственности, подобно Core Team в FreeBSD."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:73
+msgid ""
+"Currently, we hope that, when you write or port I18N programs, you would "
+"send it out to each country's related FreeBSD mailing list for testing. In "
+"the future, we hope to create applications that work in all the languages "
+"out-of-the-box without dirty hacks."
+msgstr ""
+"В настоящее время мы надеемся, что при написании или портировании I18N-"
+"программ вы будете отправлять их в соответствующие списки рассылки FreeBSD "
+"каждой страны для тестирования. В будущем мы надеемся создать приложения, "
+"которые будут работать на всех языках \"из коробки\" без грязных хаков."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:76
+msgid ""
+"The {freebsd-i18n} has been established. If you are an I18N/L10N developer, "
+"please send your comments, ideas, questions, and anything you deem related "
+"to it."
+msgstr ""
+"Создана группа {freebsd-i18n}. Если вы разработчик в области I18N/L10N, "
+"присылайте свои комментарии, идеи, вопросы и всё, что относится к этой теме "
+"по вашему мнению."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:77
+#, no-wrap
+msgid "Perl and Python"
+msgstr "Perl и Python"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:81
+msgid ""
+"Perl and Python have I18N and wide character handling libraries. Please use "
+"them for I18N compliance."
+msgstr ""
+"В Perl и Python есть библиотеки для I18N и работы с широкими символами. "
+"Пожалуйста, используйте их для соответствия требованиям I18N."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:83
+#, no-wrap
+msgid "Localized Messages with POSIX.1 Native Language Support (NLS)"
+msgstr "Локализованные сообщения с поддержкой родного языка POSIX.1 (NLS — Native Language Support)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:87
+msgid ""
+"Beyond the basic I18N functions, like supporting various input encodings or "
+"supporting national conventions, such as the different decimal separators, "
+"at a higher level of I18N, it is possible to localize the messages written "
+"to the output by the various programs. A common way of doing this is using "
+"the POSIX.1 NLS functions, which are provided as a part of the FreeBSD base "
+"system."
+msgstr ""
+"Помимо основных функций интернационализации (I18N), таких как поддержка "
+"различных кодировок ввода или национальных соглашений, например, различных "
+"разделителей десятичных знаков, на более высоком уровне I18N можно "
+"локализовать сообщения, выводимые различными программами. Распространённый "
+"способ сделать это — использовать функции NLS POSIX.1, которые "
+"предоставляются как часть базовой системы FreeBSD."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:89
+#, no-wrap
+msgid "Organizing Localized Messages into Catalog Files"
+msgstr "Организация локализованных сообщений в файлы каталогов"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:95
+msgid ""
+"POSIX.1 NLS is based on catalog files, which contain the localized messages "
+"in the desired encoding. The messages are organized into sets and each "
+"message is identified by an integer number in the containing set. The "
+"catalog files are conventionally named after the locale they contain "
+"localized messages for, followed by the `.msg` extension. For instance, the "
+"Hungarian messages for ISO8859-2 encoding should be stored in a file called "
+"[.filename]#hu_HU.ISO8859-2#."
+msgstr ""
+"POSIX.1 NLS использует файлы каталогов. Эти файлы содержат локализованные "
+"сообщения в желаемой кодировке. Сообщения организованы в наборы, и каждое "
+"сообщение идентифицируется целым числом в соответствующем наборе. Файлы "
+"каталогов традиционно называются по имени локали, для которой они содержат "
+"локализованные сообщения, с добавлением расширения `.msg`. Например, "
+"венгерские сообщения для кодировки ISO8859-2 должны храниться в файле с "
+"именем [.filename]#hu_HU.ISO8859-2#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:100
+msgid ""
+"These catalog files are common text files that contain the numbered "
+"messages. It is possible to write comments by starting the line with a `$` "
+"sign. Set boundaries are also separated by special comments, where the "
+"keyword `set` must directly follow the `$` sign. The `set` keyword is then "
+"followed by the set number. For example:"
+msgstr ""
+"Эти файлы каталогов представляют собой обычные текстовые файлы, содержащие "
+"нумерованные сообщения. Можно добавлять комментарии, начиная строку со знака "
+"`$`. Границы наборов также разделяются специальными комментариями, где "
+"ключевое слово `set` должно следовать непосредственно за знаком `$`. После "
+"ключевого слова `set` указывается номер набора. Например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:104
+#, no-wrap
+msgid "$set 1\n"
+msgstr "$set 1\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:108
+msgid ""
+"The actual message entries start with the message number and followed by the "
+"localized message. The well-known modifiers from man:printf[3] are accepted:"
+msgstr ""
+"Фактические записи сообщений начинаются с номера сообщения, за которым "
+"следует локализованное сообщение. Допускаются известные модификаторы из "
+"man:printf[3]:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:112
+#, no-wrap
+msgid "15 \"File not found: %s\\n\"\n"
+msgstr "15 \"File not found: %s\\n\"\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:118
+msgid ""
+"The language catalog files have to be compiled into a binary form before "
+"they can be opened from the program. This conversion is done with the "
+"man:gencat[1] utility. Its first argument is the filename of the compiled "
+"catalog and its further arguments are the input catalogs. The localized "
+"messages can also be organized into more catalog files and then all of them "
+"can be processed with man:gencat[1]."
+msgstr ""
+"Файлы языкового каталога должны быть скомпилированы в бинарный формат перед "
+"тем, как они могут быть открыты программой. Это преобразование выполняется с "
+"помощью утилиты man:gencat[1]. Её первый аргумент — это имя файла "
+"скомпилированного каталога, а последующие аргументы — входные каталоги. "
+"Локализованные сообщения также могут быть организованы в несколько файлов "
+"каталогов, и затем все они могут быть обработаны с помощью man:gencat[1]."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:120
+#, no-wrap
+msgid "Using the Catalog Files from the Source Code"
+msgstr "Использование файлов каталога из исходного кода"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:131
+msgid ""
+"Using the catalog files is simple. To use the related functions, "
+"[.filename]#nl_types.h# must be included. Before using a catalog, it has to "
+"be opened with man:catopen[3]. The function takes two arguments. The first "
+"parameter is the name of the installed and compiled catalog. Usually, the "
+"name of the program is used, such as grep. This name will be used when "
+"looking for the compiled catalog file. The man:catopen[3] call looks for "
+"this file in [.filename]#/usr/share/nls/locale/catname# and in [.filename]#/"
+"usr/local/share/nls/locale/catname#, where `locale` is the locale set and "
+"`catname` is the catalog name being discussed. The second parameter is a "
+"constant, which can have two values:"
+msgstr ""
+"Использование файлов каталогов простое. Чтобы вызвать функции, работающие с "
+"ними, необходимо включить файл [.filename]#nl_types.h#. Перед использованием "
+"каталога его нужно открыть с помощью man:catopen[3]. Функция принимает два "
+"аргумента. Первый параметр — это имя установленного и скомпилированного "
+"каталога. Обычно используется имя программы, например, grep. Это имя будет "
+"использоваться при поиске скомпилированного файла каталога. Вызов "
+"man:catopen[3] ищет этот файл в [.filename]#/usr/share/nls/locale/catname# и "
+"в [.filename]#/usr/local/share/nls/locale/catname#, где `locale` — "
+"установленная локализация, а `catname` — имя обсуждаемого каталога. Второй "
+"параметр — это константа, которая может принимать два значения:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:133
+msgid ""
+"`NL_CAT_LOCALE`, which means that the used catalog file will be based on "
+"`LC_MESSAGES`."
+msgstr ""
+"`NL_CAT_LOCALE`, что означает, что используемый файл каталога будет основан "
+"на `LC_MESSAGES`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:134
+msgid "`0`, which means that `LANG` has to be used to open the proper catalog."
+msgstr ""
+"`0`, что означает, что для открытия соответствующего каталога необходимо "
+"использовать `LANG`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:137
+msgid ""
+"The man:catopen[3] call returns a catalog identifier of type `nl_catd`. "
+"Please refer to the manual page for a list of possible returned error codes."
+msgstr ""
+"Вызов man:catopen[3] возвращает идентификатор каталога типа `nl_catd`. "
+"Обратитесь к справочной странице для получения списка возможных кодов ошибок."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:140
+msgid ""
+"After opening a catalog man:catgets[3] can be used to retrieve a message. "
+"The first parameter is the catalog identifier returned by man:catopen[3], "
+"the second one is the number of the set, the third one is the number of the "
+"messages, and the fourth one is a fallback message, which will be returned "
+"if the requested message cannot be retrieved from the catalog file."
+msgstr ""
+"После открытия каталога man:catgets[3] может быть использована для "
+"извлечения сообщения. Первый параметр — это идентификатор каталога, "
+"возвращаемый man:catopen[3], второй — номер набора, третий — номер "
+"сообщения, а четвертый — резервное сообщение, которое будет возвращено, если "
+"запрошенное сообщение не может быть извлечено из файла каталога."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:142
+msgid ""
+"After using the catalog file, it must be closed by calling man:catclose[3], "
+"which has one argument, the catalog id."
+msgstr ""
+"После использования файла каталога его необходимо закрыть, вызвав "
+"man:catclose[3], которая принимает один аргумент — идентификатор каталога."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:144
+#, no-wrap
+msgid "A Practical Example"
+msgstr "Практический пример"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:147
+msgid ""
+"The following example will demonstrate an easy solution on how to use NLS "
+"catalogs in a flexible way."
+msgstr ""
+"Следующий пример демонстрирует простое решение по гибкому использованию "
+"каталогов NLS."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:149
+msgid ""
+"The below lines need to be put into a common header file of the program, "
+"which is included into all source files where localized messages are "
+"necessary:"
+msgstr ""
+"Нижеследующие строки необходимо поместить в общий заголовочный файл "
+"программы, который включается во все исходные файлы, где необходимы "
+"локализованные сообщения:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:156
+#, no-wrap
+msgid ""
+"#ifdef WITHOUT_NLS\n"
+"#define getstr(n)\t nlsstr[n]\n"
+"#else\n"
+"#include nl_types.h\n"
+msgstr ""
+"#ifdef WITHOUT_NLS\n"
+"#define getstr(n)\t nlsstr[n]\n"
+"#else\n"
+"#include nl_types.h\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:160
+#, no-wrap
+msgid ""
+"extern nl_catd\t\t catalog;\n"
+"#define getstr(n)\t catgets(catalog, 1, n, nlsstr[n])\n"
+"#endif\n"
+msgstr ""
+"extern nl_catd\t\t catalog;\n"
+"#define getstr(n)\t catgets(catalog, 1, n, nlsstr[n])\n"
+"#endif\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:162
+#, no-wrap
+msgid "extern char\t\t*nlsstr[];\n"
+msgstr "extern char\t\t*nlsstr[];\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:165
+msgid ""
+"Next, put these lines into the global declaration part of the main source "
+"file:"
+msgstr ""
+"Далее, добавьте эти строки в глобальную секцию объявлений основного "
+"исходного файла:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:172
+#, no-wrap
+msgid ""
+"#ifndef WITHOUT_NLS\n"
+"#include nl_types.h\n"
+"nl_catd\t catalog;\n"
+"#endif\n"
+msgstr ""
+"#ifndef WITHOUT_NLS\n"
+"#include nl_types.h\n"
+"nl_catd\t catalog;\n"
+"#endif\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:182
+#, no-wrap
+msgid ""
+"/*\n"
+" * Default messages to use when NLS is disabled or no catalog\n"
+" * is found.\n"
+" */\n"
+"char *nlsstr[] = {\n"
+" \"\",\n"
+"/* 1*/ \"some random message\",\n"
+"/* 2*/ \"some other message\"\n"
+"};\n"
+msgstr ""
+"/*\n"
+" * Default messages to use when NLS is disabled or no catalog\n"
+" * is found.\n"
+" */\n"
+"char *nlsstr[] = {\n"
+" \"\",\n"
+"/* 1*/ \"some random message\",\n"
+"/* 2*/ \"some other message\"\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:185
+msgid ""
+"Next come the real code snippets, which open, read, and close the catalog:"
+msgstr ""
+"Далее следуют реальные фрагменты кода, которые открывают, читают и закрывают "
+"каталог:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:191
+#, no-wrap
+msgid ""
+"#ifndef WITHOUT_NLS\n"
+"\tcatalog = catopen(\"myapp\", NL_CAT_LOCALE);\n"
+"#endif\n"
+msgstr ""
+"#ifndef WITHOUT_NLS\n"
+"\tcatalog = catopen(\"myapp\", NL_CAT_LOCALE);\n"
+"#endif\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:193
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:197
+#, no-wrap
+msgid "...\n"
+msgstr "...\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:195
+#, no-wrap
+msgid "printf(getstr(1));\n"
+msgstr "printf(getstr(1));\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:201
+#, no-wrap
+msgid ""
+"#ifndef WITHOUT_NLS\n"
+"\tcatclose(catalog);\n"
+"#endif\n"
+msgstr ""
+"#ifndef WITHOUT_NLS\n"
+"\tcatclose(catalog);\n"
+"#endif\n"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:203
+#, no-wrap
+msgid "Reducing Strings to Localize"
+msgstr "Уменьшение количества строк для локализации"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:207
+msgid ""
+"There is a good way of reducing the strings that need to be localized by "
+"using libc error messages. This is also useful to just avoid duplication "
+"and provide consistent error messages for the common errors that can be "
+"encountered by a great many of programs."
+msgstr ""
+"Хороший способ уменьшить количество строк, требующих локализации, — "
+"использовать сообщения об ошибках из libc. Это также полезно для избежания "
+"дублирования и обеспечения единообразия сообщений об ошибках, с которыми "
+"может столкнуться множество программ."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:209
+msgid "First, here is an example that does not use libc error messages:"
+msgstr "Вот пример, который не использует сообщения об ошибках из libc:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:216
+#, no-wrap
+msgid ""
+"#include err.h\n"
+"...\n"
+"if (!S_ISDIR(st.st_mode))\n"
+"\terrx(1, \"argument is not a directory\");\n"
+msgstr ""
+"#include err.h\n"
+"...\n"
+"if (!S_ISDIR(st.st_mode))\n"
+"\terrx(1, \"argument is not a directory\");\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:219
+msgid ""
+"This can be transformed to print an error message by reading `errno` and "
+"printing an error message accordingly:"
+msgstr ""
+"Это можно преобразовать для вывода сообщения об ошибке, считывая `errno` и "
+"выводя соответствующее сообщение об ошибке:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:229
+#, no-wrap
+msgid ""
+"#include err.h\n"
+"#include errno.h\n"
+"...\n"
+"if (!S_ISDIR(st.st_mode)) {\n"
+"\terrno = ENOTDIR;\n"
+"\terr(1, NULL);\n"
+"}\n"
+msgstr ""
+"#include err.h\n"
+"#include errno.h\n"
+"...\n"
+"if (!S_ISDIR(st.st_mode)) {\n"
+"\terrno = ENOTDIR;\n"
+"\terr(1, NULL);\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:234
+msgid ""
+"In this example, the custom string is eliminated, thus translators will have "
+"less work when localizing the program and users will see the usual \"Not a "
+"directory\" error message when they encounter this error. This message will "
+"probably seem more familiar to them. Please note that it was necessary to "
+"include [.filename]#errno.h# in order to directly access `errno`."
+msgstr ""
+"В этом примере пользовательская строка исключена, что упростит работу "
+"переводчиков при локализации программы, а пользователи увидят стандартное "
+"сообщение об ошибке \"Not a directory\" при возникновении данной ошибки. Это "
+"сообщение, вероятно, будет выглядеть более привычным для них. Обратите "
+"внимание, что для прямого доступа к `errno` потребовалось включить "
+"[.filename]#errno.h#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:236
+msgid ""
+"It is worth to note that there are cases when `errno` is set automatically "
+"by a preceding call, so it is not necessary to set it explicitly:"
+msgstr ""
+"Стоит отметить, что бывают случаи, когда `errno` устанавливается "
+"автоматически предыдущим вызовом, поэтому нет необходимости устанавливать "
+"его явно:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:243
+#, no-wrap
+msgid ""
+"#include err.h\n"
+"...\n"
+"if ((p = malloc(size)) == NULL)\n"
+"\terr(1, NULL);\n"
+msgstr ""
+"#include err.h\n"
+"...\n"
+"if ((p = malloc(size)) == NULL)\n"
+"\terr(1, NULL);\n"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:246
+#, no-wrap
+msgid "Making use of [.filename]#bsd.nls.mk#"
+msgstr "Использование [.filename]#bsd.nls.mk#"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:251
+msgid ""
+"Using the catalog files requires few repeatable steps, such as compiling the "
+"catalogs and installing them to the proper location. In order to simplify "
+"this process even more, [.filename]#bsd.nls.mk# introduces some macros. It "
+"is not necessary to include [.filename]#bsd.nls.mk# explicitly, it is pulled "
+"in from the common Makefiles, such as [.filename]#bsd.prog.mk# or "
+"[.filename]#bsd.lib.mk#."
+msgstr ""
+"Использование файлов каталогов требует нескольких повторяемых шагов, таких "
+"как компиляция каталогов и их установка в нужное место. Чтобы ещё больше "
+"упростить этот процесс, [.filename]#bsd.nls.mk# вводит некоторые макросы. "
+"Нет необходимости явно включать [.filename]#bsd.nls.mk#, он подключается "
+"автоматически из общих Makefiles, таких как [.filename]#bsd.prog.mk# или "
+"[.filename]#bsd.lib.mk#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:255
+msgid ""
+"Usually it is enough to define `NLSNAME`, which should have the catalog name "
+"mentioned as the first argument of man:catopen[3] and list the catalog files "
+"in `NLS` without their `.msg` extension. Here is an example, which makes it "
+"possible to to disable NLS when used with the code examples before. The "
+"`WITHOUT_NLS` man:make[1] variable has to be defined in order to build the "
+"program without NLS support."
+msgstr ""
+"Обычно достаточно определить `NLSNAME`, которое должно содержать имя "
+"каталога, указанное в качестве первого аргумента man:catopen[3], и "
+"перечислить файлы каталогов в `NLS` без расширения `.msg`. Вот пример, "
+"который позволяет отключить NLS при использовании с предыдущими примерами "
+"кода. Для сборки программы без поддержки NLS необходимо определить "
+"переменную `WITHOUT_NLS` man:make[1]."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:265
+#, no-wrap
+msgid ""
+".if !defined(WITHOUT_NLS)\n"
+"NLS=\tes_ES.ISO8859-1\n"
+"NLS+=\thu_HU.ISO8859-2\n"
+"NLS+=\tpt_BR.ISO8859-1\n"
+".else\n"
+"CFLAGS+=\t-DWITHOUT_NLS\n"
+".endif\n"
+msgstr ""
+".if !defined(WITHOUT_NLS)\n"
+"NLS=\tes_ES.ISO8859-1\n"
+"NLS+=\thu_HU.ISO8859-2\n"
+"NLS+=\tpt_BR.ISO8859-1\n"
+".else\n"
+"CFLAGS+=\t-DWITHOUT_NLS\n"
+".endif\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/l10n/_index.adoc:272
+msgid ""
+"Conventionally, the catalog files are placed under the [.filename]#nls# "
+"subdirectory and this is the default behavior of [.filename]#bsd.nls.mk#. "
+"It is possible, though to override the location of the catalogs with the "
+"`NLSSRCDIR` man:make[1] variable. The default name of the precompiled "
+"catalog files also follow the naming convention mentioned before. It can be "
+"overridden by setting the `NLSNAME` variable. There are other options to "
+"fine tune the processing of the catalog files but usually it is not needed, "
+"thus they are not described here. For further information on "
+"[.filename]#bsd.nls.mk#, please refer to the file itself, it is short and "
+"easy to understand."
+msgstr ""
+"Обычно файлы каталогов размещаются в подкаталоге [.filename]#nls#, и это "
+"поведение по умолчанию для [.filename]#bsd.nls.mk#. Однако можно "
+"переопределить расположение каталогов с помощью переменной `NLSSRCDIR` "
+"man:make[1]. Имя по умолчанию для предварительно скомпилированных файлов "
+"каталогов также следует упомянутому ранее соглашению об именовании. Его "
+"можно переопределить, установив переменную `NLSNAME`. Существуют и другие "
+"параметры для точной настройки обработки файлов каталогов, но обычно в этом "
+"нет необходимости, поэтому они здесь не описаны. Для получения "
+"дополнительной информации о [.filename]#bsd.nls.mk# обратитесь к самому "
+"файлу — он короткий и легко понятен."
diff --git a/documentation/content/ru/books/developers-handbook/parti.adoc b/documentation/content/ru/books/developers-handbook/parti.adoc
new file mode 100644
index 0000000000..adce578cf4
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/parti.adoc
@@ -0,0 +1,12 @@
+---
+next: books/developers-handbook/introduction
+params:
+ path: /books/developers-handbook/parti/
+prev: books/developers-handbook
+showBookMenu: true
+title: 'Часть I. Основы'
+weight: 1
+---
+
+[[basics]]
+= Основы
diff --git a/documentation/content/ru/books/developers-handbook/parti.po b/documentation/content/ru/books/developers-handbook/parti.po
new file mode 100644
index 0000000000..1b1ac9c56c
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/parti.po
@@ -0,0 +1,31 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-06-22 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookparti/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/parti.adoc:1
+#, no-wrap
+msgid "Part I. Basics"
+msgstr "Часть I. Основы"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/parti.adoc:12
+#, no-wrap
+msgid "Basics"
+msgstr "Основы"
diff --git a/documentation/content/ru/books/developers-handbook/partii.adoc b/documentation/content/ru/books/developers-handbook/partii.adoc
new file mode 100644
index 0000000000..90b195599b
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/partii.adoc
@@ -0,0 +1,12 @@
+---
+next: books/developers-handbook/sockets
+params:
+ path: /books/developers-handbook/partii/
+prev: books/developers-handbook/testing
+showBookMenu: true
+title: 'Часть II. Межпроцессное взаимодействие'
+weight: 8
+---
+
+[[ipc]]
+= Межпроцессное взаимодействие
diff --git a/documentation/content/ru/books/developers-handbook/partii.po b/documentation/content/ru/books/developers-handbook/partii.po
new file mode 100644
index 0000000000..e6ef620442
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/partii.po
@@ -0,0 +1,31 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-06-22 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookpartii/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/partii.adoc:1
+#, no-wrap
+msgid "Part II. Interprocess Communication"
+msgstr "Часть II. Межпроцессное взаимодействие"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/partii.adoc:12
+#, no-wrap
+msgid "Interprocess Communication"
+msgstr "Межпроцессное взаимодействие"
diff --git a/documentation/content/ru/books/developers-handbook/partiii.adoc b/documentation/content/ru/books/developers-handbook/partiii.adoc
new file mode 100644
index 0000000000..af235ae481
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/partiii.adoc
@@ -0,0 +1,12 @@
+---
+next: books/developers-handbook/kernelbuild
+params:
+ path: /books/developers-handbook/partiii/
+prev: books/developers-handbook/ipv6
+showBookMenu: true
+title: 'Часть III. Ядро системы'
+weight: 11
+---
+
+[[kernel]]
+= Ядро системы
diff --git a/documentation/content/ru/books/developers-handbook/partiii.po b/documentation/content/ru/books/developers-handbook/partiii.po
new file mode 100644
index 0000000000..784ac2476a
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/partiii.po
@@ -0,0 +1,31 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-06-12 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookpartiii/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/partiii.adoc:1
+#, no-wrap
+msgid "Part III. Kernel"
+msgstr "Часть III. Ядро системы"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/partiii.adoc:12
+#, no-wrap
+msgid "Kernel"
+msgstr "Ядро системы"
diff --git a/documentation/content/ru/books/developers-handbook/partiv.adoc b/documentation/content/ru/books/developers-handbook/partiv.adoc
new file mode 100644
index 0000000000..e97b97bcaa
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/partiv.adoc
@@ -0,0 +1,12 @@
+---
+next: books/developers-handbook/x86
+params:
+ path: /books/developers-handbook/partiv/
+prev: books/developers-handbook/kerneldebug
+showBookMenu: true
+title: 'Часть IV. Архитектуры'
+weight: 14
+---
+
+[[architectures]]
+= Архитектуры
diff --git a/documentation/content/ru/books/developers-handbook/partiv.po b/documentation/content/ru/books/developers-handbook/partiv.po
new file mode 100644
index 0000000000..729cc84399
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/partiv.po
@@ -0,0 +1,31 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-06-22 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookpartiv/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/partiv.adoc:1
+#, no-wrap
+msgid "Part IV. Architectures"
+msgstr "Часть IV. Архитектуры"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/partiv.adoc:12
+#, no-wrap
+msgid "Architectures"
+msgstr "Архитектуры"
diff --git a/documentation/content/ru/books/developers-handbook/partv.adoc b/documentation/content/ru/books/developers-handbook/partv.adoc
new file mode 100644
index 0000000000..a5f2aec1f5
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/partv.adoc
@@ -0,0 +1,12 @@
+---
+next: books/developers-handbook/bibliography
+params:
+ path: /books/developers-handbook/partv/
+prev: books/developers-handbook/x86
+showBookMenu: true
+title: 'Часть V. Приложения'
+weight: 16
+---
+
+[[appendices]]
+= Приложения
diff --git a/documentation/content/ru/books/developers-handbook/partv.po b/documentation/content/ru/books/developers-handbook/partv.po
new file mode 100644
index 0000000000..1871f3f359
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/partv.po
@@ -0,0 +1,31 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-06-07 17:10+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookpartv/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/partv.adoc:1
+#, no-wrap
+msgid "Part V. Appendices"
+msgstr "Часть V. Приложения"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/partv.adoc:12
+#, no-wrap
+msgid "Appendices"
+msgstr "Приложения"
diff --git a/documentation/content/ru/books/developers-handbook/policies/_index.adoc b/documentation/content/ru/books/developers-handbook/policies/_index.adoc
new file mode 100644
index 0000000000..7264035d7e
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/policies/_index.adoc
@@ -0,0 +1,148 @@
+---
+authors:
+ -
+ author: 'Poul-Henning Kamp'
+ -
+ author: 'Giorgos Keramidas'
+description: 'Руководство и политика работы с деревом исходного кода'
+next: books/developers-handbook/testing
+params:
+ path: /books/developers-handbook/policies/
+prev: books/developers-handbook/l10n
+showBookMenu: true
+tags: ["Style Guidelines", "MAINTAINER", "Makefiles", "Contributed Software", "Shared libraries"]
+title: 'Глава 5. Руководство и политика работы с деревом исходного кода'
+weight: 6
+---
+
+[[policies]]
+= Руководство и политика работы с деревом исходного кода
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 5
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/developers-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+Эта глава документирует различные руководства и политики, действующие для дерева исходных кодов FreeBSD.
+
+[[policies-style]]
+== Рекомендации по стилю
+
+Соблюдение единого стиля написания кода чрезвычайно важно, особенно в крупных проектах, таких как FreeBSD. Код должен соответствовать стилям программирования FreeBSD, описанным в man:style[9] и man:style.Makefile[5].
+
+[[policies-maintainer]]
+== `MAINTAINER` в Makefile-ах
+
+Если определённая часть дистрибутива FreeBSD [.filename]#src/# поддерживается человеком или группой лиц, это указывается в файле [.filename]#src/MAINTAINERS#. Сопровождающие портов в Коллекции портов указывают свою ответственность, добавляя строку `MAINTAINER` в [.filename]#Makefile# соответствующего порта:
+
+[.programlisting]
+....
+MAINTAINER= email-addresses
+....
+
+[TIP]
+====
+Для других частей репозитория или для разделов, в которых не указан сопровождающий, или если вы не уверены, кто является активным сопровождающим, попробуйте посмотреть историю последних коммитов соответствующих частей дерева исходного кода. Довольно часто сопровождающий явно не указан, но люди, которые активно работали с частью дерева исходного кода, скажем, последние пару лет, заинтересованы в проверке изменений. Даже если это не указано явно в документации или в самом исходном коде, запросить проверку из вежливости — вполне разумное действие.
+====
+
+Роль сопровождающего заключается в следующем:
+
+* Сопровождающий является владельцем и ответственным за этот код. Это означает, что он или она отвечает за исправление ошибок и решение проблем, связанных с этой частью кода, а в случае с предоставленным программным обеспечением — за отслеживание новых версий, если это необходимо.
+* Изменения в каталогах, для которых определен сопровождающий, должны быть отправлены сопровождающему на проверку и рецензирование перед коммитом. Только если сопровождающий не отвечает в течение недопустимо долгого времени на несколько писем, допустимо закоммитить изменения без его проверки. Тем не менее, рекомендуется по возможности попытаться получить рецензирование изменений у кого-нибудь ещё.
+* Конечно, недопустимо добавлять человека или группу в качестве сопровождающего, если они не согласны взять на себя эти обязанности. С другой стороны, это не обязательно должен быть один коммиттер, и это может быть и группа людей.
+
+[[policies-contributed]]
+== Стороннее программное обеспечение
+
+Некоторые части дистрибутива FreeBSD состоят из программного обеспечения, которое активно поддерживается за пределами проекта FreeBSD. По историческим причинам мы называем это _сторонним_ программным обеспечением. Некоторые примеры: LLVM, man:zlib[3] и man:awk[1].
+
+Принятая процедура управления вносимым программным обеспечением включает создание _ветки поставщика_ (_vendor branch_), где программное обеспечение может быть импортировано в чистом виде (без изменений), а обновления могут отслеживаться с учётом версий. Затем содержимое ветки поставщика применяется к дереву исходного кода, возможно, с локальными изменениями. Специфичные для FreeBSD элементы сборки поддерживаются в дереве исходного кода, а не в ветке поставщика.
+
+В зависимости от потребностей и сложности, отдельные программные проекты могут отклоняться от этой процедуры по усмотрению сопровождающего. Точные шаги, необходимые для обновления конкретного программного обеспечения, должны быть записаны в файле с именем `FREEBSD-upgrade`; например, link:https://cgit.freebsd.org/src/tree/contrib/libarchive/FREEBSD-upgrade[файл FREEBSD-upgrade libarchive].
+
+Стороннее программное обеспечение обычно размещается в подкаталоге [.filepath]#contrib/# дерева исходных кодов, за некоторыми исключениями. Стороннее программное обеспечение, используемое только ядром, находится в [.filepath]#sys/contrib/#.
+
+[NOTE]
+====
+Поскольку это затрудняет импорт будущих версий, незначительные, тривиальные и/или косметические изменения _настоятельно не рекомендуются_ для файлов, которые всё ещё отслеживают ветку поставщика.
+====
+
+[[vendor-import]]
+=== Импорт веток поставщика
+
+Стандартный процесс управления сторонним программным обеспечением и ветками поставщиков подробно описан в extref:{committers-guide}#vendor-import-git[Руководстве коммиттера].
+
+[[policies-encumbered]]
+== Файлы с правовыми ограничениями
+
+Время от времени может возникнуть необходимость включить файл с правовыми ограничениями (обремененными лицензиями, патентами) в дерево исходного кода FreeBSD. Например, если устройство требует загрузки небольшого бинарного кода перед началом работы, а у нас нет исходного кода для него, то такой бинарный файл считается обремененным. Следующие политики применяются к включению обремененных файлов в дерево исходного кода FreeBSD.
+
+. Любой файл, который интерпретируется или выполняется процессором(-ами) системы и не представлен в исходном формате, является обременённым.
+. Любой файл с лицензией более ограничительной, чем BSD или GNU, является обременённым.
+. Файл, содержащий загружаемые двоичные данные для использования оборудованием, не является обремененным, если к нему не применяется пункт (1) или (2).
+. Любой файл с правовыми ограничениями требует специального одобрения от link:https://www.FreeBSD.org/administration/#t-core[Core Team] перед добавлением в репозиторий.
+. Обремененные файлы помещаются в [.filename]#src/contrib# или [.filename]#src/sys/contrib#.
+. Весь модуль должен храниться вместе. Нет смысла разделять его, если только нет совместного использования кода с необременённой частью кода.
+. В прошлом бинарные файлы обычно кодировались с помощью uuencode и назывались [.filename]#arch/filename.o.uu#. Теперь в этом нет необходимости, и бинарные файлы могут добавляться в репозиторий без изменений.
+. Файлы ядра системы:
+.. Всегда должны быть указана в [.filename]#conf/files.*# (для упрощения сборки).
+.. Всегда должны быть в [.filename]#LINT#, но link:https://www.FreeBSD.org/administration/#t-core[Core Team] решает в каждом конкретном случае, следует ли их закомментировать или нет. link:https://www.FreeBSD.org/administration/#t-core[Core Team] может, конечно, позже изменить свое решение.
+.. _Инженер по выпуску_ решает, будет ли это включено в выпуск.
+
+. Пользовательские файлы:
+.. Команда link:https://www.FreeBSD.org/administration/#t-core[Core team] принимает решение о включении кода в базовую устанавливаемую систему.
+.. link:https://www.FreeBSD.org/administration/#t-re[Отдел разработки релизов] решает, войдет ли это в релиз.
+
+[[policies-shlib]]
+== Динамические библиотеки
+
+Если вы добавляете поддержку динамических библиотек в порт или другое программное обеспечение, у которого её нет, номера версий библиотек должны следовать этим правилам. Обычно итоговые номера не будут иметь ничего общего с версией выпуска программного обеспечения.
+
+Для портов:
+
+* Предпочитайте использовать номер, уже выбранный вышестоящим проектом
+* Если вышестоящий источник предоставляет управление версиями символов, убедитесь, что мы используем их скрипт
+
+Для базовой системы:
+
+* Начните версии библиотеки с 1
+* Настоятельно рекомендуется добавить контроль версий символов в новую библиотеку
+* Если есть несовместимое изменение, обработайте его с помощью версионирования символов, сохраняя обратную совместимость ABI
+* Если это невозможно или библиотека не использует версионирование символов, увеличьте версию библиотеки
+* Прежде чем даже рассматривать увеличение версии библиотеки для библиотеки с версионированием символов, проконсультируйтесь с командой Release Engineering, предоставив причины, почему изменение настолько важно, что его следует разрешить, несмотря на нарушение ABI
+
+Например, добавленные функции и исправления ошибок, не изменяющие интерфейсы, допустимы, тогда как удалённые функции, изменённый синтаксис вызовов и т.д. должны либо предоставлять обратно-совместимые символы, либо приведут к изменению старшего номера версии.
+
+Обязанность коммиттера, вносящего изменения, — управлять версионированием библиотек.
+
+Динамический загрузчик ELF сопоставляет имена библиотек буквально. Существует популярное соглашение, согласно которому версия библиотеки записывается в виде `libexample.so.x.y`, где x — это мажорная версия, а y — минорная. Общепринятой практикой является установка поля soname у библиотеки (тег ELF `DT_SONAME`) в `libexample.so.x`, а также создание символических ссылок `libexample.so.x->libexample.so.x.y`, `libexample.so->libexample.so.x` при установке библиотеки для последней минорной версии y. Таким образом, поскольку статический компоновщик ищет `libexample.so`, когда указана опция командной строки `-lexample`, объекты, скомпонованные с libexample, получают информацию о зависимости от правильной библиотеки. Почти все популярные системы сборки автоматически используют эту схему.
diff --git a/documentation/content/ru/books/developers-handbook/policies/_index.po b/documentation/content/ru/books/developers-handbook/policies/_index.po
new file mode 100644
index 0000000000..a5bda85033
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/policies/_index.po
@@ -0,0 +1,523 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-09-05 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookpolicies_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:1
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:17
+#, no-wrap
+msgid "Source Tree Guidelines and Policies"
+msgstr "Руководство и политика работы с деревом исходного кода"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:1
+#, no-wrap
+msgid "Chapter 5. Source Tree Guidelines and Policies"
+msgstr "Глава 5. Руководство и политика работы с деревом исходного кода"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:55
+msgid ""
+"This chapter documents various guidelines and policies in force for the "
+"FreeBSD source tree."
+msgstr ""
+"Эта глава документирует различные руководства и политики, действующие для "
+"дерева исходных кодов FreeBSD."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:57
+#, no-wrap
+msgid "Style Guidelines"
+msgstr "Рекомендации по стилю"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:61
+msgid ""
+"Consistent coding style is extremely important, particularly with large "
+"projects like FreeBSD. Code should follow the FreeBSD coding styles "
+"described in man:style[9] and man:style.Makefile[5]."
+msgstr ""
+"Соблюдение единого стиля написания кода чрезвычайно важно, особенно в "
+"крупных проектах, таких как FreeBSD. Код должен соответствовать стилям "
+"программирования FreeBSD, описанным в man:style[9] и man:style.Makefile[5]."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:63
+#, no-wrap
+msgid "`MAINTAINER` on Makefiles"
+msgstr "`MAINTAINER` в Makefile-ах"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:67
+msgid ""
+"If a particular portion of the FreeBSD [.filename]#src/# distribution is "
+"being maintained by a person or group of persons, this is communicated "
+"through an entry in [.filename]#src/MAINTAINERS#. Maintainers of ports "
+"within the Ports Collection express their maintainership to the world by "
+"adding a `MAINTAINER` line to the [.filename]#Makefile# of the port in "
+"question:"
+msgstr ""
+"Если определённая часть дистрибутива FreeBSD [.filename]#src/# "
+"поддерживается человеком или группой лиц, это указывается в файле "
+"[.filename]#src/MAINTAINERS#. Сопровождающие портов в Коллекции портов "
+"указывают свою ответственность, добавляя строку `MAINTAINER` в "
+"[.filename]#Makefile# соответствующего порта:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:71
+#, no-wrap
+msgid "MAINTAINER= email-addresses\n"
+msgstr "MAINTAINER= email-addresses\n"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:78
+msgid ""
+"For other parts of the repository, or for sections not listed as having a "
+"maintainer, or when you are unsure who the active maintainer is, try looking "
+"at the recent commit history of the relevant parts of the source tree. It "
+"is quite often the case that a maintainer is not explicitly named, but the "
+"people who are actively working in a part of the source tree for, say, the "
+"last couple of years are interested in reviewing changes. Even if this is "
+"not specifically mentioned in the documentation or the source itself, asking "
+"for a review as a form of courtesy is a very reasonable thing to do."
+msgstr ""
+"Для других частей репозитория или для разделов, в которых не указан "
+"сопровождающий, или если вы не уверены, кто является активным "
+"сопровождающим, попробуйте посмотреть историю последних коммитов "
+"соответствующих частей дерева исходного кода. Довольно часто сопровождающий "
+"явно не указан, но люди, которые активно работали с частью дерева исходного "
+"кода, скажем, последние пару лет, заинтересованы в проверке изменений. Даже "
+"если это не указано явно в документации или в самом исходном коде, запросить "
+"проверку из вежливости — вполне разумное действие."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:81
+msgid "The role of the maintainer is as follows:"
+msgstr "Роль сопровождающего заключается в следующем:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:83
+msgid ""
+"The maintainer owns and is responsible for that code. This means that he or "
+"she is responsible for fixing bugs and answering problem reports pertaining "
+"to that piece of the code, and in the case of contributed software, for "
+"tracking new versions, as appropriate."
+msgstr ""
+"Сопровождающий является владельцем и ответственным за этот код. Это "
+"означает, что он или она отвечает за исправление ошибок и решение проблем, "
+"связанных с этой частью кода, а в случае с предоставленным программным "
+"обеспечением — за отслеживание новых версий, если это необходимо."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:84
+msgid ""
+"Changes to directories which have a maintainer defined shall be sent to the "
+"maintainer for review before being committed. Only if the maintainer does "
+"not respond for an unacceptable period of time, to several emails, will it "
+"be acceptable to commit changes without review by the maintainer. However, "
+"it is suggested that you try to have the changes reviewed by someone else if "
+"at all possible."
+msgstr ""
+"Изменения в каталогах, для которых определен сопровождающий, должны быть "
+"отправлены сопровождающему на проверку и рецензирование перед коммитом. "
+"Только если сопровождающий не отвечает в течение недопустимо долгого времени "
+"на несколько писем, допустимо закоммитить изменения без его проверки. Тем не "
+"менее, рекомендуется по возможности попытаться получить рецензирование "
+"изменений у кого-нибудь ещё."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:85
+msgid ""
+"It is of course not acceptable to add a person or group as maintainer unless "
+"they agree to assume this duty. On the other hand it does not have to be a "
+"committer and it can easily be a group of people."
+msgstr ""
+"Конечно, недопустимо добавлять человека или группу в качестве "
+"сопровождающего, если они не согласны взять на себя эти обязанности. С "
+"другой стороны, это не обязательно должен быть один коммиттер, и это может "
+"быть и группа людей."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:87
+#, no-wrap
+msgid "Contributed Software"
+msgstr "Стороннее программное обеспечение"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:92
+msgid ""
+"Some parts of the FreeBSD distribution consist of software that is actively "
+"being maintained outside the FreeBSD project. For historical reasons, we "
+"call this _contributed_ software. Some examples are LLVM, man:zlib[3], and "
+"man:awk[1]."
+msgstr ""
+"Некоторые части дистрибутива FreeBSD состоят из программного обеспечения, "
+"которое активно поддерживается за пределами проекта FreeBSD. По историческим "
+"причинам мы называем это _сторонним_ программным обеспечением. Некоторые "
+"примеры: LLVM, man:zlib[3] и man:awk[1]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:96
+msgid ""
+"The accepted procedure for managing contributed software involves creating a "
+"_vendor branch_, where the software can be imported cleanly (without "
+"modification) and updates can be tracked in a versioned manner. Then, the "
+"content of the vendor branch is applied to the source tree, possibly with "
+"local modifications. FreeBSD-specific build glue is maintained in the "
+"source tree, not in the vendor branch."
+msgstr ""
+"Принятая процедура управления вносимым программным обеспечением включает "
+"создание _ветки поставщика_ (_vendor branch_), где программное обеспечение "
+"может быть импортировано в чистом виде (без изменений), а обновления могут "
+"отслеживаться с учётом версий. Затем содержимое ветки поставщика применяется "
+"к дереву исходного кода, возможно, с локальными изменениями. Специфичные для "
+"FreeBSD элементы сборки поддерживаются в дереве исходного кода, а не в ветке "
+"поставщика."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:100
+msgid ""
+"Depending on their needs and complexity, individual software projects may "
+"deviate from this procedure, at the discretion of the maintainer. The exact "
+"steps required to update a particular piece of contributed software should "
+"be recorded in a file named `FREEBSD-upgrade`; for example, link:https://"
+"cgit.freebsd.org/src/tree/contrib/libarchive/FREEBSD-upgrade[libarchive's "
+"FREEBSD-upgrade file]."
+msgstr ""
+"В зависимости от потребностей и сложности, отдельные программные проекты "
+"могут отклоняться от этой процедуры по усмотрению сопровождающего. Точные "
+"шаги, необходимые для обновления конкретного программного обеспечения, "
+"должны быть записаны в файле с именем `FREEBSD-upgrade`; например, "
+"link:https://cgit.freebsd.org/src/tree/contrib/libarchive/FREEBSD-"
+"upgrade[файл FREEBSD-upgrade libarchive]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:103
+msgid ""
+"Contributed software is usually placed in the [.filepath]#contrib/# "
+"subdirectory of the source tree, with some exceptions. Contributed software "
+"used only by the kernel lives under [.filepath]#sys/contrib/#."
+msgstr ""
+"Стороннее программное обеспечение обычно размещается в подкаталоге "
+"[.filepath]#contrib/# дерева исходных кодов, за некоторыми исключениями. "
+"Стороннее программное обеспечение, используемое только ядром, находится в "
+"[.filepath]#sys/contrib/#."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:107
+msgid ""
+"Because it makes it harder to import future versions minor, trivial and/or "
+"cosmetic changes are _strongly discouraged_ on files that are still tracking "
+"the vendor branch."
+msgstr ""
+"Поскольку это затрудняет импорт будущих версий, незначительные, тривиальные "
+"и/или косметические изменения _настоятельно не рекомендуются_ для файлов, "
+"которые всё ещё отслеживают ветку поставщика."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:110
+#, no-wrap
+msgid "Vendor Imports"
+msgstr "Импорт веток поставщика"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:113
+msgid ""
+"The standard process for managing contributed software and vendor branches "
+"is described in detail by the extref:{committers-guide}#vendor-import-"
+"git[Committer's Guide]."
+msgstr ""
+"Стандартный процесс управления сторонним программным обеспечением и ветками "
+"поставщиков подробно описан в extref:{committers-guide}#vendor-import-"
+"git[Руководстве коммиттера]."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:115
+#, no-wrap
+msgid "Encumbered Files"
+msgstr "Файлы с правовыми ограничениями"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:121
+msgid ""
+"It might occasionally be necessary to include an encumbered file in the "
+"FreeBSD source tree. For example, if a device requires a small piece of "
+"binary code to be loaded to it before the device will operate, and we do not "
+"have the source to that code, then the binary file is said to be "
+"encumbered. The following policies apply to including encumbered files in "
+"the FreeBSD source tree."
+msgstr ""
+"Время от времени может возникнуть необходимость включить файл с правовыми "
+"ограничениями (обремененными лицензиями, патентами) в дерево исходного кода "
+"FreeBSD. Например, если устройство требует загрузки небольшого бинарного "
+"кода перед началом работы, а у нас нет исходного кода для него, то такой "
+"бинарный файл считается обремененным. Следующие политики применяются к "
+"включению обремененных файлов в дерево исходного кода FreeBSD."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:123
+msgid ""
+"Any file which is interpreted or executed by the system CPU(s) and not in "
+"source format is encumbered."
+msgstr ""
+"Любой файл, который интерпретируется или выполняется процессором(-ами) "
+"системы и не представлен в исходном формате, является обременённым."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:124
+msgid "Any file with a license more restrictive than BSD or GNU is encumbered."
+msgstr ""
+"Любой файл с лицензией более ограничительной, чем BSD или GNU, является "
+"обременённым."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:125
+msgid ""
+"A file which contains downloadable binary data for use by the hardware is "
+"not encumbered, unless (1) or (2) apply to it."
+msgstr ""
+"Файл, содержащий загружаемые двоичные данные для использования "
+"оборудованием, не является обремененным, если к нему не применяется пункт "
+"(1) или (2)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:126
+msgid ""
+"Any encumbered file requires specific approval from the link:https://"
+"www.FreeBSD.org/administration/#t-core[Core Team] before it is added to the "
+"repository."
+msgstr ""
+"Любой файл с правовыми ограничениями требует специального одобрения от "
+"link:https://www.FreeBSD.org/administration/#t-core[Core Team] перед "
+"добавлением в репозиторий."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:127
+msgid ""
+"Encumbered files go in [.filename]#src/contrib# or [.filename]#src/sys/"
+"contrib#."
+msgstr ""
+"Обремененные файлы помещаются в [.filename]#src/contrib# или [.filename]#src/"
+"sys/contrib#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:128
+msgid ""
+"The entire module should be kept together. There is no point in splitting "
+"it, unless there is code-sharing with non-encumbered code."
+msgstr ""
+"Весь модуль должен храниться вместе. Нет смысла разделять его, если только "
+"нет совместного использования кода с необременённой частью кода."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:130
+msgid ""
+"In the past binary files were typically uuencoded, and named "
+"[.filename]#arch/filename.o.uu#. This is no longer necessary, and binary "
+"files may be added to the repository unchanged."
+msgstr ""
+"В прошлом бинарные файлы обычно кодировались с помощью uuencode и назывались "
+"[.filename]#arch/filename.o.uu#. Теперь в этом нет необходимости, и бинарные "
+"файлы могут добавляться в репозиторий без изменений."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:131
+msgid "Kernel files:"
+msgstr "Файлы ядра системы:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:132
+msgid ""
+"Should always be referenced in [.filename]#conf/files.*# (for build "
+"simplicity)."
+msgstr ""
+"Всегда должны быть указана в [.filename]#conf/files.*# (для упрощения "
+"сборки)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:133
+msgid ""
+"Should always be in [.filename]#LINT#, but the link:https://www.FreeBSD.org/"
+"administration/#t-core[Core Team] decides per case if it should be commented "
+"out or not. The link:https://www.FreeBSD.org/administration/#t-core[Core "
+"Team] can, of course, change their minds later on."
+msgstr ""
+"Всегда должны быть в [.filename]#LINT#, но link:https://www.FreeBSD.org/"
+"administration/#t-core[Core Team] решает в каждом конкретном случае, следует "
+"ли их закомментировать или нет. link:https://www.FreeBSD.org/administration/"
+"#t-core[Core Team] может, конечно, позже изменить свое решение."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:134
+msgid "The _Release Engineer_ decides whether or not it goes into the release."
+msgstr "_Инженер по выпуску_ решает, будет ли это включено в выпуск."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:136
+msgid "User-land files:"
+msgstr "Пользовательские файлы:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:137
+msgid ""
+"The link:https://www.FreeBSD.org/administration/#t-core[Core team] decides "
+"if the code should be part of the installed base system."
+msgstr ""
+"Команда link:https://www.FreeBSD.org/administration/#t-core[Core team] "
+"принимает решение о включении кода в базовую устанавливаемую систему."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:138
+msgid ""
+"The link:https://www.FreeBSD.org/administration/#t-re[Release Engineering] "
+"decides if it goes into the release."
+msgstr ""
+"link:https://www.FreeBSD.org/administration/#t-re[Отдел разработки релизов] "
+"решает, войдет ли это в релиз."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:140
+#, no-wrap
+msgid "Shared Libraries"
+msgstr "Динамические библиотеки"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:144
+msgid ""
+"If you are adding shared library support to a port or other piece of "
+"software that does not have one, the version numbers should follow these "
+"rules. Generally, the resulting numbers will have nothing to do with the "
+"release version of the software."
+msgstr ""
+"Если вы добавляете поддержку динамических библиотек в порт или другое "
+"программное обеспечение, у которого её нет, номера версий библиотек должны "
+"следовать этим правилам. Обычно итоговые номера не будут иметь ничего общего "
+"с версией выпуска программного обеспечения."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:146
+msgid "For ports:"
+msgstr "Для портов:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:148
+msgid "Prefer using the number already selected by upstream"
+msgstr "Предпочитайте использовать номер, уже выбранный вышестоящим проектом"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:149
+msgid "If upstream provides symbol versioning, ensure that we use their script"
+msgstr ""
+"Если вышестоящий источник предоставляет управление версиями символов, "
+"убедитесь, что мы используем их скрипт"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:151
+msgid "For the base system:"
+msgstr "Для базовой системы:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:153
+msgid "Start library version from 1"
+msgstr "Начните версии библиотеки с 1"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:154
+msgid "It is strongly recommended to add symbol versioning to the new library"
+msgstr ""
+"Настоятельно рекомендуется добавить контроль версий символов в новую "
+"библиотеку"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:155
+msgid ""
+"If there is an incompatible change, handle it with symbol versioning, "
+"maintaining backward ABI compatibility"
+msgstr ""
+"Если есть несовместимое изменение, обработайте его с помощью версионирования "
+"символов, сохраняя обратную совместимость ABI"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:156
+msgid ""
+"If this is impossible, or the library does not use symbol versioning, bump "
+"the library version"
+msgstr ""
+"Если это невозможно или библиотека не использует версионирование символов, "
+"увеличьте версию библиотеки"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:157
+msgid ""
+"Before even considering bumping library version for symbol-versioned "
+"library, consult with Release Engineering team, providing reasons why the "
+"change is so important that it should be allowed despite breaking the ABI"
+msgstr ""
+"Прежде чем даже рассматривать увеличение версии библиотеки для библиотеки с "
+"версионированием символов, проконсультируйтесь с командой Release "
+"Engineering, предоставив причины, почему изменение настолько важно, что его "
+"следует разрешить, несмотря на нарушение ABI"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:159
+msgid ""
+"For instance, added functions and bugfixes not changing the interfaces are "
+"fine, while deleted functions, changed function call syntax, etc. should "
+"either provide backward-compat symbols, or will force the major version "
+"number to change."
+msgstr ""
+"Например, добавленные функции и исправления ошибок, не изменяющие "
+"интерфейсы, допустимы, тогда как удалённые функции, изменённый синтаксис "
+"вызовов и т.д. должны либо предоставлять обратно-совместимые символы, либо "
+"приведут к изменению старшего номера версии."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:161
+msgid ""
+"It is the duty of the committer making the change to handle library "
+"versioning."
+msgstr ""
+"Обязанность коммиттера, вносящего изменения, — управлять версионированием "
+"библиотек."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/policies/_index.adoc:166
+msgid ""
+"The ELF dynamic linker matches library names literally. There is a popular "
+"convention where library version is written in the form `libexample.so.x.y`, "
+"where x is the major version, and y is minor. Common practice is to set the "
+"library' soname (`DT_SONAME` ELF tag) to `libexample.so.x`, and set up "
+"symlinks `libexample.so.x->libexample.so.x.y`, `libexample.so-"
+">libexample.so.x` on library installation for the latest minor version y. "
+"Then, since the static linker searches for `libexample.so` when the `-"
+"lexample` command line option is specified, objects linked with libexample "
+"get a dependency on the right library. Almost all popular build systems use "
+"this scheme automatically."
+msgstr ""
+"Динамический загрузчик ELF сопоставляет имена библиотек буквально. "
+"Существует популярное соглашение, согласно которому версия библиотеки "
+"записывается в виде `libexample.so.x.y`, где x — это мажорная версия, а y — "
+"минорная. Общепринятой практикой является установка поля soname у библиотеки "
+"(тег ELF `DT_SONAME`) в `libexample.so.x`, а также создание символических "
+"ссылок `libexample.so.x->libexample.so.x.y`, `libexample.so-"
+">libexample.so.x` при установке библиотеки для последней минорной версии y. "
+"Таким образом, поскольку статический компоновщик ищет `libexample.so`, когда "
+"указана опция командной строки `-lexample`, объекты, скомпонованные с "
+"libexample, получают информацию о зависимости от правильной библиотеки. "
+"Почти все популярные системы сборки автоматически используют эту схему."
diff --git a/documentation/content/ru/books/developers-handbook/secure/_index.adoc b/documentation/content/ru/books/developers-handbook/secure/_index.adoc
new file mode 100644
index 0000000000..1631ee018b
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/secure/_index.adoc
@@ -0,0 +1,230 @@
+---
+authors:
+ -
+ author: 'Murray Stokely'
+description: 'Безопасное программирование в FreeBSD'
+next: books/developers-handbook/l10n
+params:
+ path: /books/developers-handbook/secure/
+prev: books/developers-handbook/tools
+showBookMenu: true
+tags: ["secure programming", "Buffer Overflows", "SetUID issues"]
+title: 'Глава 3. Безопасное программирование'
+weight: 4
+---
+
+[[secure]]
+= Безопасное программирование
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 3
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/developers-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[secure-synopsis]]
+== Обзор
+
+В этой главе описываются некоторые проблемы безопасности, которые преследуют программистов UNIX(R) на протяжении десятилетий, а также новые инструменты, помогающие избежать написания уязвимого кода.
+
+[[secure-philosophy]]
+== Методология безопасного проектирования
+
+Написание безопасных приложений требует очень внимательного и пессимистичного взгляда на жизнь. Приложения должны работать по принципу "наименьших привилегий", чтобы ни один процесс не выполнялся с доступом, превышающим необходимый минимум для выполнения его функций. По возможности следует повторно использовать уже проверенный код, чтобы избежать распространённых ошибок, которые, возможно, уже исправили другие.
+
+Одной из ловушек среды UNIX(R) является то, насколько легко делать предположения о разумности окружения. Приложения никогда не должны доверять пользовательскому вводу (во всех его формах), системным ресурсам, межпроцессному взаимодействию или времени событий. Процессы UNIX(R) выполняются не синхронно, поэтому логические операции редко бывают атомарными.
+
+[[secure-bufferov]]
+== Переполнение буфера
+
+Переполнение буфера существовало с самых истоков архитектуры фон Неймана crossref:bibliography[cod,1]. Впервые оно получило широкую известность в 1988 году благодаря червю Морриса. К сожалению, эта базовая атака остаётся эффективной и по сей день. Наиболее распространённый тип атаки с переполнением буфера основан на повреждении стека.
+
+Большинство современных компьютерных систем используют стек для передачи аргументов процедурам и хранения локальных переменных. Стек — это буфер типа "последним пришёл — первым ушёл" (LIFO) в верхней области памяти процесса. Когда программа вызывает функцию, создаётся новый "стековый кадр". Этот стековый кадр состоит из аргументов, переданных функции, а также динамического количества места для локальных переменных. "Указатель стека" — это регистр, который содержит текущее местоположение вершины стека. Поскольку это значение постоянно меняется по мере добавления новых значений на вершину стека, многие реализации также предоставляют "указатель кадра", который располагается вблизи начала стекового кадра, чтобы локальные переменные могли легче адресоваться относительно этого значения. crossref:bibliography[cod,1] Адрес возврата для вызовов функций также хранится в стеке, и это является причиной эксплойтов переполнения стека, поскольку переполнение локальной переменной в функции может перезаписать адрес возврата этой функции, потенциально позволяя злоумышленнику выполнить любой код по своему усмотрению.
+
+Хотя атаки на стек являются наиболее распространенными, также возможно переполнение стека с помощью атаки на кучу (malloc/free).
+
+Язык программирования C не выполняет автоматическую проверку границ массивов или указателей, как это делают многие другие языки. Кроме того, стандартная библиотека C содержит множество очень опасных функций.
+
+[.informaltable]
+[cols="1,1", frame="none"]
+|===
+
+|`strcpy`(char *dest, const char *src)
+|
+
+Может переполнить буфер назначения
+
+|`strcat`(char *dest, const char *src)
+|
+
+Может переполнить буфер назначения
+
+|`getwd`(char *buf)
+|
+
+Может переполнить буфер buf
+
+|`gets`(char *s)
+|
+
+Может переполнить буфер s
+
+|`[vf]scanf`(const char *format, ...)
+|
+
+Может переполнить свои аргументы.
+
+|`realpath`(char *path, char resolved_path[])
+|
+
+Может переполнить буфер пути
+
+|`[v]sprintf`(char *str, const char *format, ...)
+|
+
+Может переполнить буфер str.
+|===
+
+=== Пример переполнения буфера
+
+Следующий пример кода содержит переполнение буфера, предназначенное для перезаписи адреса возврата и пропуска инструкции, следующей сразу после вызова функции. (Вдохновлено crossref:bibliography[Phrack,4])
+
+[.programlisting]
+....
+#include <stdio.h>
+
+void manipulate(char *buffer) {
+ char newbuffer[80];
+ strcpy(newbuffer,buffer);
+}
+
+int main() {
+ char ch,buffer[4096];
+ int i=0;
+
+ while ((buffer[i++] = getchar()) != '\n') {};
+
+ i=1;
+ manipulate(buffer);
+ i=2;
+ printf("The value of i is : %d\n",i);
+ return 0;
+}
+....
+
+Давайте рассмотрим, как будет выглядеть образ памяти этого процесса, если мы введем 160 пробелов в нашу небольшую программу перед нажатием Enter.
+
+[XXX figure here!]
+
+Очевидно, что можно разработать более вредоносные входные данные для выполнения реальных скомпилированных инструкций (например, exec(/bin/sh)).
+
+=== Избегание переполнения буфера
+
+Наиболее простое решение проблемы переполнения стека — всегда использовать функции копирования памяти и строк с ограничением длины. `strncpy` и `strncat` являются частью стандартной библиотеки C. Эти функции принимают параметр длины, который не должен превышать размер целевого буфера. Затем эти функции копируют до 'length' байтов из источника в назначение. Однако у этих функций есть ряд проблем. Ни одна из них не гарантирует завершающий NUL, если размер входного буфера равен размеру целевого. Параметр длины также используется неодинаково между `strncpy` и `strncat`, что может сбивать программистов с толку относительно их правильного использования. Также наблюдается значительное снижение производительности по сравнению с `strcpy` при копировании короткой строки в большой буфер, поскольку `strncpy` заполняет оставшееся пространство до указанного размера символами NUL.
+
+Существует другая реализация копирования памяти для решения этих проблем. Функции `strlcpy` и `strlcat` гарантируют, что они всегда завершат строку назначения нулевым символом при передаче аргумента ненулевой длины.
+
+==== Скомпилированная проверка границ во время выполнения
+
+К сожалению, до сих пор существует очень большое количество кода в открытом доступе, который бездумно копирует память, не используя ни одну из ограниченных функций копирования, которые мы только что обсудили. К счастью, есть способ помочь предотвратить такие атаки — проверка границ во время выполнения, которая реализована в нескольких компиляторах C/C++.
+
+ProPolice — это одна из таких функций компилятора, интегрированная в man:gcc[1] версий 4.1 и выше. Она заменяет и расширяет более раннее расширение StackGuard для man:gcc[1].
+
+ProPolice помогает защититься от переполнений буфера на стеке и других атак, размещая псевдослучайные числа в ключевых областях стека перед вызовом любой функции. Когда функция завершается, эти "канарейки" проверяются, и если обнаруживается, что они были изменены, выполнение программы немедленно прекращается. Таким образом, любая попытка изменить адрес возврата или другие переменные, хранящиеся на стеке, с целью запуска вредоносного кода, вряд ли увенчается успехом, так как злоумышленнику также необходимо оставить псевдослучайные канарейки нетронутыми.
+
+Перекомпиляция вашего приложения с использованием ProPolice является эффективным способом предотвращения большинства атак, связанных с переполнением буфера, но оно всё ещё может быть скомпрометировано.
+
+==== Библиотечная проверка границ во время выполнения
+
+Механизмы на основе компилятора совершенно бесполезны для проприетарного программного обеспечения, которое невозможно перекомпилировать. Для таких ситуаций существует ряд библиотек, которые переопределяют небезопасные функции стандартной библиотеки C (`strcpy`, `fscanf`, `getwd` и т.д.) и гарантируют, что эти функции никогда не смогут записать данные за указатель стека.
+
+* libsafe
+* libverify
+* libparanoia
+
+К сожалению, эти защиты на основе библиотек имеют ряд недостатков. Они защищают лишь от очень небольшого набора проблем, связанных с безопасностью, и не устраняют основную причину. Эти защиты могут не сработать, если приложение было скомпилировано с флагом -fomit-frame-pointer. Кроме того, переменные окружения LD_PRELOAD и LD_LIBRARY_PATH могут быть перезаписаны или сброшены пользователем.
+
+[[secure-setuid]]
+== Проблемы с SetUID
+
+Существует как минимум 6 различных идентификаторов, связанных с каждым процессом, поэтому необходимо очень внимательно следить за уровнем доступа вашего процесса в любой момент времени. В частности, все приложения с seteuid должны отказываться от своих привилегий, как только в них больше нет необходимости.
+
+Действительный идентификатор пользователя может быть изменён только процессом с правами суперпользователя. Программа login устанавливает его при первоначальном входе пользователя в систему, и он редко изменяется.
+
+Эффективный идентификатор пользователя устанавливается функциями `exec()`, если у программы установлен бит seteuid. Приложение может вызывать `seteuid()` в любое время, чтобы установить эффективный идентификатор пользователя либо в реальный идентификатор пользователя, либо в сохранённый set-user-ID. Когда эффективный идентификатор пользователя устанавливается функциями `exec()`, предыдущее значение сохраняется в сохранённом set-user-ID.
+
+[[secure-chroot]]
+== Ограничение окружения вашей программы
+
+Традиционный метод ограничения процесса — это системный вызов `chroot()`. Этот системный вызов изменяет корневой каталог, от которого ссылаются все остальные пути для процесса и любых дочерних процессов. Для успешного выполнения этого вызова процесс должен иметь право на выполнение (поиск) в указанном каталоге. Новая среда фактически не вступает в силу, пока вы не выполните `chdir()` в новой среде. Также следует отметить, что процесс может легко выйти из окружения chroot, если он имеет привилегии root. Это может быть достигнуто путем создания узлов устройств для чтения памяти ядра, подключения отладчика к процессу вне окружения man:chroot[8] или многими другими творческими способами.
+
+Поведение системного вызова `chroot()` можно частично контролировать с помощью переменной `sysctl` kern.chroot_allow_open_directories. Если этому параметру присвоено значение 0, `chroot()` завершится с ошибкой EPERM, если есть какие-либо открытые каталоги. Если установлено значение по умолчанию 1, то `chroot()` завершится с ошибкой EPERM, если есть открытые каталоги и процесс уже находится внутри вызова `chroot()`. Для любого другого значения проверка на открытые каталоги будет полностью пропущена.
+
+=== Функциональность клеток FreeBSD
+
+Концепция `клетки` расширяет возможности `chroot()`, ограничивая права суперпользователя для создания настоящего `виртуального сервера`. После настройки клетки все сетевые взаимодействия должны осуществляться через указанный IP-адрес, а привилегии `root` внутри этой клетки сильно ограничены.
+
+Находясь в клетке, любые проверки прав суперпользователя в ядре с использованием вызова `suser()` завершатся неудачей. Однако некоторые вызовы `suser()` были заменены на новый интерфейс `suser_xxx()`. Эта функция отвечает за распознавание или запрет доступа к правам суперпользователя для процессов в клетке.
+
+Суперпользователь в среде клетки имеет возможность:
+
+* Управлять учетными данными с помощью `setuid`, `seteuid`, `setgid`, `setegid`, `setgroups`, `setreuid`, `setregid`, `setlogin`
+* Установливать ограничений ресурсов с помощью `setrlimit`
+* Изменять некоторые узлы sysctl (kern.hostname)
+* `chroot()`
+* Устанавливать флаги на vnode: `chflags`, `fchflags`
+* Устанавливать атрибуты vnode, такие как права доступа к файлу, владелец, группа, размер, время доступа и время изменения.
+* Привязываться к привилегированным портам в домене Интернета (порты < 1024)
+
+`Клетка` — это очень полезный инструмент для запуска приложений в безопасной среде, но у него есть некоторые недостатки. В настоящее время механизмы IPC не были преобразованы в `suser_xxx`, поэтому такие приложения, как MySQL, не могут быть запущены внутри клетки. Доступ суперпользователя может иметь очень ограниченное значение внутри клетки, но нет возможности точно указать, что означает «очень ограниченный».
+
+=== Возможности процесса в POSIX(R).1e
+
+POSIX(R) выпустил рабочий проект, который добавляет аудит событий, списки контроля доступа, детализированные привилегии, маркировку информации и обязательный контроль доступа.
+
+Это работа в процессе, и она является основным направлением проекта http://www.trustedbsd.org/[TrustedBSD]. Некоторые первоначальные наработки были добавлены в FreeBSD-CURRENT (cap_set_proc(3)).
+
+[[secure-trust]]
+== Доверие
+
+Приложение никогда не должно предполагать, что окружение пользователя является предсказуемым. Это включает (но не ограничивается): пользовательский ввод, сигналы, переменные окружения, ресурсы, IPC, mmaps, текущую рабочую директорию файловой системы, файловые дескрипторы, количество открытых файлов и т.д.
+
+Никогда не следует предполагать, что можно отловить все виды некорректных входных данных, которые может предоставить пользователь. Вместо этого ваше приложение должно использовать позитивную фильтрацию, разрешая только определённое подмножество входных данных, которые вы считаете безопасными. Некорректная проверка данных стала причиной многих уязвимостей, особенно в CGI-скриптах во всемирной паутине. Для имён файлов необходимо быть особенно осторожными с путями ("../", "/"), символическими ссылками и escape-символами оболочки.
+
+В Perl есть замечательная функция под названием "Режим Taint", которая может использоваться для предотвращения небезопасного использования данных, полученных извне программы. Этот режим проверяет аргументы командной строки, переменные окружения, информацию о локали, результаты определённых системных вызовов (`readdir()`, `readlink()`, `getpwxxx()`) и все вводимые данные из файлов.
+
+[[secure-race-conditions]]
+== Состояние гонки
+
+Состояние гонки — это аномальное поведение, вызванное непредвиденной зависимостью от относительного времени событий. Другими словами, программист ошибочно предположил, что определенное событие всегда произойдет раньше другого.
+
+Некоторые из распространённых причин состояний гонки — это сигналы, проверки доступа и открытие файлов. Сигналы по своей природе являются асинхронными событиями, поэтому при работе с ними необходимо проявлять особую осторожность. Проверка доступа с помощью `access(2)`, а затем `open(2)` явно неатомарна. Пользователи могут перемещать файлы между этими двумя вызовами. Вместо этого привилегированные приложения должны использовать `seteuid()`, а затем вызывать `open()` напрямую. По аналогии, приложение всегда должно устанавливать правильную маску (`umask`) перед вызовом `open()`, чтобы избежать необходимости в лишних вызовах `chmod()`.
diff --git a/documentation/content/ru/books/developers-handbook/secure/_index.po b/documentation/content/ru/books/developers-handbook/secure/_index.po
new file mode 100644
index 0000000000..c2900d4b7a
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/secure/_index.po
@@ -0,0 +1,824 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-09-05 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbooksecure_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:1
+#, no-wrap
+msgid "Secure Programming in FreeBSD"
+msgstr "Безопасное программирование в FreeBSD"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:1
+#, no-wrap
+msgid "Chapter 3. Secure Programming"
+msgstr "Глава 3. Безопасное программирование"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:16
+#, no-wrap
+msgid "Secure Programming"
+msgstr "Безопасное программирование"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:54
+#, no-wrap
+msgid "Synopsis"
+msgstr "Обзор"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:57
+msgid ""
+"This chapter describes some of the security issues that have plagued UNIX(R) "
+"programmers for decades and some of the new tools available to help "
+"programmers avoid writing exploitable code."
+msgstr ""
+"В этой главе описываются некоторые проблемы безопасности, которые преследуют "
+"программистов UNIX(R) на протяжении десятилетий, а также новые инструменты, "
+"помогающие избежать написания уязвимого кода."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:59
+#, no-wrap
+msgid "Secure Design Methodology"
+msgstr "Методология безопасного проектирования"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:64
+msgid ""
+"Writing secure applications takes a very scrutinous and pessimistic outlook "
+"on life. Applications should be run with the principle of \"least "
+"privilege\" so that no process is ever running with more than the bare "
+"minimum access that it needs to accomplish its function. Previously tested "
+"code should be reused whenever possible to avoid common mistakes that others "
+"may have already fixed."
+msgstr ""
+"Написание безопасных приложений требует очень внимательного и "
+"пессимистичного взгляда на жизнь. Приложения должны работать по принципу "
+"\"наименьших привилегий\", чтобы ни один процесс не выполнялся с доступом, "
+"превышающим необходимый минимум для выполнения его функций. По возможности "
+"следует повторно использовать уже проверенный код, чтобы избежать "
+"распространённых ошибок, которые, возможно, уже исправили другие."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:68
+msgid ""
+"One of the pitfalls of the UNIX(R) environment is how easy it is to make "
+"assumptions about the sanity of the environment. Applications should never "
+"trust user input (in all its forms), system resources, inter-process "
+"communication, or the timing of events. UNIX(R) processes do not execute "
+"synchronously so logical operations are rarely atomic."
+msgstr ""
+"Одной из ловушек среды UNIX(R) является то, насколько легко делать "
+"предположения о разумности окружения. Приложения никогда не должны доверять "
+"пользовательскому вводу (во всех его формах), системным ресурсам, "
+"межпроцессному взаимодействию или времени событий. Процессы UNIX(R) "
+"выполняются не синхронно, поэтому логические операции редко бывают "
+"атомарными."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:70
+#, no-wrap
+msgid "Buffer Overflows"
+msgstr "Переполнение буфера"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:76
+msgid ""
+"Buffer Overflows have been around since the very beginnings of the von "
+"Neumann crossref:bibliography[cod,1] architecture. They first gained "
+"widespread notoriety in 1988 with the Morris Internet worm. Unfortunately, "
+"the same basic attack remains effective today. By far the most common type "
+"of buffer overflow attack is based on corrupting the stack."
+msgstr ""
+"Переполнение буфера существовало с самых истоков архитектуры фон Неймана "
+"crossref:bibliography[cod,1]. Впервые оно получило широкую известность в "
+"1988 году благодаря червю Морриса. К сожалению, эта базовая атака остаётся "
+"эффективной и по сей день. Наиболее распространённый тип атаки с "
+"переполнением буфера основан на повреждении стека."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:85
+msgid ""
+"Most modern computer systems use a stack to pass arguments to procedures and "
+"to store local variables. A stack is a last in first out (LIFO) buffer in "
+"the high memory area of a process image. When a program invokes a function "
+"a new \"stack frame\" is created. This stack frame consists of the "
+"arguments passed to the function as well as a dynamic amount of local "
+"variable space. The \"stack pointer\" is a register that holds the current "
+"location of the top of the stack. Since this value is constantly changing "
+"as new values are pushed onto the top of the stack, many implementations "
+"also provide a \"frame pointer\" that is located near the beginning of a "
+"stack frame so that local variables can more easily be addressed relative to "
+"this value. crossref:bibliography[cod,1] The return address for function "
+"calls is also stored on the stack, and this is the cause of stack-overflow "
+"exploits since overflowing a local variable in a function can overwrite the "
+"return address of that function, potentially allowing a malicious user to "
+"execute any code he or she wants."
+msgstr ""
+"Большинство современных компьютерных систем используют стек для передачи "
+"аргументов процедурам и хранения локальных переменных. Стек — это буфер типа "
+"\"последним пришёл — первым ушёл\" (LIFO) в верхней области памяти процесса. "
+"Когда программа вызывает функцию, создаётся новый \"стековый кадр\". Этот "
+"стековый кадр состоит из аргументов, переданных функции, а также "
+"динамического количества места для локальных переменных. \"Указатель стека\" "
+"— это регистр, который содержит текущее местоположение вершины стека. "
+"Поскольку это значение постоянно меняется по мере добавления новых значений "
+"на вершину стека, многие реализации также предоставляют \"указатель кадра\", "
+"который располагается вблизи начала стекового кадра, чтобы локальные "
+"переменные могли легче адресоваться относительно этого значения. "
+"crossref:bibliography[cod,1] Адрес возврата для вызовов функций также "
+"хранится в стеке, и это является причиной эксплойтов переполнения стека, "
+"поскольку переполнение локальной переменной в функции может перезаписать "
+"адрес возврата этой функции, потенциально позволяя злоумышленнику выполнить "
+"любой код по своему усмотрению."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:88
+msgid ""
+"Although stack-based attacks are by far the most common, it would also be "
+"possible to overrun the stack with a heap-based (malloc/free) attack."
+msgstr ""
+"Хотя атаки на стек являются наиболее распространенными, также возможно "
+"переполнение стека с помощью атаки на кучу (malloc/free)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:91
+msgid ""
+"The C programming language does not perform automatic bounds checking on "
+"arrays or pointers as many other languages do. In addition, the standard C "
+"library is filled with a handful of very dangerous functions."
+msgstr ""
+"Язык программирования C не выполняет автоматическую проверку границ массивов "
+"или указателей, как это делают многие другие языки. Кроме того, стандартная "
+"библиотека C содержит множество очень опасных функций."
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:97
+#, no-wrap
+msgid "`strcpy`(char *dest, const char *src)"
+msgstr "`strcpy`(char *dest, const char *src)"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:101
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:106
+#, no-wrap
+msgid "May overflow the dest buffer"
+msgstr "Может переполнить буфер назначения"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:102
+#, no-wrap
+msgid "`strcat`(char *dest, const char *src)"
+msgstr "`strcat`(char *dest, const char *src)"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:107
+#, no-wrap
+msgid "`getwd`(char *buf)"
+msgstr "`getwd`(char *buf)"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:111
+#, no-wrap
+msgid "May overflow the buf buffer"
+msgstr "Может переполнить буфер buf"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:112
+#, no-wrap
+msgid "`gets`(char *s)"
+msgstr "`gets`(char *s)"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:116
+#, no-wrap
+msgid "May overflow the s buffer"
+msgstr "Может переполнить буфер s"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:117
+#, no-wrap
+msgid "`[vf]scanf`(const char *format, ...)"
+msgstr "`[vf]scanf`(const char *format, ...)"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:121
+#, no-wrap
+msgid "May overflow its arguments."
+msgstr "Может переполнить свои аргументы."
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:122
+#, no-wrap
+msgid "`realpath`(char *path, char resolved_path[])"
+msgstr "`realpath`(char *path, char resolved_path[])"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:126
+#, no-wrap
+msgid "May overflow the path buffer"
+msgstr "Может переполнить буфер пути"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:127
+#, no-wrap
+msgid "`[v]sprintf`(char *str, const char *format, ...)"
+msgstr "`[v]sprintf`(char *str, const char *format, ...)"
+
+#. type: Table
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:130
+#, no-wrap
+msgid "May overflow the str buffer."
+msgstr "Может переполнить буфер str."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:132
+#, no-wrap
+msgid "Example Buffer Overflow"
+msgstr "Пример переполнения буфера"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:135
+msgid ""
+"The following example code contains a buffer overflow designed to overwrite "
+"the return address and skip the instruction immediately following the "
+"function call. (Inspired by crossref:bibliography[Phrack,4])"
+msgstr ""
+"Следующий пример кода содержит переполнение буфера, предназначенное для "
+"перезаписи адреса возврата и пропуска инструкции, следующей сразу после "
+"вызова функции. (Вдохновлено crossref:bibliography[Phrack,4])"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:139
+#, no-wrap
+msgid "#include <stdio.h>\n"
+msgstr "#include <stdio.h>\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:144
+#, no-wrap
+msgid ""
+"void manipulate(char *buffer) {\n"
+" char newbuffer[80];\n"
+" strcpy(newbuffer,buffer);\n"
+"}\n"
+msgstr ""
+"void manipulate(char *buffer) {\n"
+" char newbuffer[80];\n"
+" strcpy(newbuffer,buffer);\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:148
+#, no-wrap
+msgid ""
+"int main() {\n"
+" char ch,buffer[4096];\n"
+" int i=0;\n"
+msgstr ""
+"int main() {\n"
+" char ch,buffer[4096];\n"
+" int i=0;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:150
+#, no-wrap
+msgid " while ((buffer[i++] = getchar()) != '\\n') {};\n"
+msgstr " while ((buffer[i++] = getchar()) != '\\n') {};\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:157
+#, no-wrap
+msgid ""
+" i=1;\n"
+" manipulate(buffer);\n"
+" i=2;\n"
+" printf(\"The value of i is : %d\\n\",i);\n"
+" return 0;\n"
+"}\n"
+msgstr ""
+" i=1;\n"
+" manipulate(buffer);\n"
+" i=2;\n"
+" printf(\"The value of i is : %d\\n\",i);\n"
+" return 0;\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:160
+msgid ""
+"Let us examine what the memory image of this process would look like if we "
+"were to input 160 spaces into our little program before hitting return."
+msgstr ""
+"Давайте рассмотрим, как будет выглядеть образ памяти этого процесса, если мы "
+"введем 160 пробелов в нашу небольшую программу перед нажатием Enter."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:164
+msgid ""
+"Obviously more malicious input can be devised to execute actual compiled "
+"instructions (such as exec(/bin/sh))."
+msgstr ""
+"Очевидно, что можно разработать более вредоносные входные данные для "
+"выполнения реальных скомпилированных инструкций (например, exec(/bin/sh))."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:165
+#, no-wrap
+msgid "Avoiding Buffer Overflows"
+msgstr "Избегание переполнения буфера"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:175
+msgid ""
+"The most straightforward solution to the problem of stack-overflows is to "
+"always use length restricted memory and string copy functions. `strncpy` "
+"and `strncat` are part of the standard C library. These functions accept a "
+"length value as a parameter which should be no larger than the size of the "
+"destination buffer. These functions will then copy up to 'length' bytes "
+"from the source to the destination. However there are a number of problems "
+"with these functions. Neither function guarantees NUL termination if the "
+"size of the input buffer is as large as the destination. The length "
+"parameter is also used inconsistently between strncpy and strncat so it is "
+"easy for programmers to get confused as to their proper usage. There is "
+"also a significant performance loss compared to `strcpy` when copying a "
+"short string into a large buffer since `strncpy` NUL fills up the size "
+"specified."
+msgstr ""
+"Наиболее простое решение проблемы переполнения стека — всегда использовать "
+"функции копирования памяти и строк с ограничением длины. `strncpy` и "
+"`strncat` являются частью стандартной библиотеки C. Эти функции принимают "
+"параметр длины, который не должен превышать размер целевого буфера. Затем "
+"эти функции копируют до 'length' байтов из источника в назначение. Однако у "
+"этих функций есть ряд проблем. Ни одна из них не гарантирует завершающий "
+"NUL, если размер входного буфера равен размеру целевого. Параметр длины "
+"также используется неодинаково между `strncpy` и `strncat`, что может "
+"сбивать программистов с толку относительно их правильного использования. "
+"Также наблюдается значительное снижение производительности по сравнению с "
+"`strcpy` при копировании короткой строки в большой буфер, поскольку "
+"`strncpy` заполняет оставшееся пространство до указанного размера символами "
+"NUL."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:178
+msgid ""
+"Another memory copy implementation exists to get around these problems. The "
+"`strlcpy` and `strlcat` functions guarantee that they will always null "
+"terminate the destination string when given a non-zero length argument."
+msgstr ""
+"Существует другая реализация копирования памяти для решения этих проблем. "
+"Функции `strlcpy` и `strlcat` гарантируют, что они всегда завершат строку "
+"назначения нулевым символом при передаче аргумента ненулевой длины."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:179
+#, no-wrap
+msgid "Compiler based run-time bounds checking"
+msgstr "Скомпилированная проверка границ во время выполнения"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:183
+msgid ""
+"Unfortunately there is still a very large assortment of code in public use "
+"which blindly copies memory around without using any of the bounded copy "
+"routines we just discussed. Fortunately, there is a way to help prevent "
+"such attacks - run-time bounds checking, which is implemented by several C/C+"
+"+ compilers."
+msgstr ""
+"К сожалению, до сих пор существует очень большое количество кода в открытом "
+"доступе, который бездумно копирует память, не используя ни одну из "
+"ограниченных функций копирования, которые мы только что обсудили. К счастью, "
+"есть способ помочь предотвратить такие атаки — проверка границ во время "
+"выполнения, которая реализована в нескольких компиляторах C/C++."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:186
+msgid ""
+"ProPolice is one such compiler feature, and is integrated into man:gcc[1] "
+"versions 4.1 and later. It replaces and extends the earlier StackGuard "
+"man:gcc[1] extension."
+msgstr ""
+"ProPolice — это одна из таких функций компилятора, интегрированная в "
+"man:gcc[1] версий 4.1 и выше. Она заменяет и расширяет более раннее "
+"расширение StackGuard для man:gcc[1]."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:190
+msgid ""
+"ProPolice helps to protect against stack-based buffer overflows and other "
+"attacks by laying pseudo-random numbers in key areas of the stack before "
+"calling any function. When a function returns, these \"canaries\" are "
+"checked and if they are found to have been changed the executable is "
+"immediately aborted. Thus any attempt to modify the return address or other "
+"variable stored on the stack in an attempt to get malicious code to run is "
+"unlikely to succeed, as the attacker would have to also manage to leave the "
+"pseudo-random canaries untouched."
+msgstr ""
+"ProPolice помогает защититься от переполнений буфера на стеке и других атак, "
+"размещая псевдослучайные числа в ключевых областях стека перед вызовом любой "
+"функции. Когда функция завершается, эти \"канарейки\" проверяются, и если "
+"обнаруживается, что они были изменены, выполнение программы немедленно "
+"прекращается. Таким образом, любая попытка изменить адрес возврата или "
+"другие переменные, хранящиеся на стеке, с целью запуска вредоносного кода, "
+"вряд ли увенчается успехом, так как злоумышленнику также необходимо оставить "
+"псевдослучайные канарейки нетронутыми."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:192
+msgid ""
+"Recompiling your application with ProPolice is an effective means of "
+"stopping most buffer-overflow attacks, but it can still be compromised."
+msgstr ""
+"Перекомпиляция вашего приложения с использованием ProPolice является "
+"эффективным способом предотвращения большинства атак, связанных с "
+"переполнением буфера, но оно всё ещё может быть скомпрометировано."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:193
+#, no-wrap
+msgid "Library based run-time bounds checking"
+msgstr "Библиотечная проверка границ во время выполнения"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:197
+msgid ""
+"Compiler-based mechanisms are completely useless for binary-only software "
+"for which you cannot recompile. For these situations there are a number of "
+"libraries which re-implement the unsafe functions of the C-library "
+"(`strcpy`, `fscanf`, `getwd`, etc..) and ensure that these functions can "
+"never write past the stack pointer."
+msgstr ""
+"Механизмы на основе компилятора совершенно бесполезны для проприетарного "
+"программного обеспечения, которое невозможно перекомпилировать. Для таких "
+"ситуаций существует ряд библиотек, которые переопределяют небезопасные "
+"функции стандартной библиотеки C (`strcpy`, `fscanf`, `getwd` и т.д.) и "
+"гарантируют, что эти функции никогда не смогут записать данные за указатель "
+"стека."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:199
+msgid "libsafe"
+msgstr "libsafe"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:200
+msgid "libverify"
+msgstr "libverify"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:201
+msgid "libparanoia"
+msgstr "libparanoia"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:206
+msgid ""
+"Unfortunately these library-based defenses have a number of shortcomings. "
+"These libraries only protect against a very small set of security related "
+"issues and they neglect to fix the actual problem. These defenses may fail "
+"if the application was compiled with -fomit-frame-pointer. Also, the "
+"LD_PRELOAD and LD_LIBRARY_PATH environment variables can be overwritten/"
+"unset by the user."
+msgstr ""
+"К сожалению, эти защиты на основе библиотек имеют ряд недостатков. Они "
+"защищают лишь от очень небольшого набора проблем, связанных с безопасностью, "
+"и не устраняют основную причину. Эти защиты могут не сработать, если "
+"приложение было скомпилировано с флагом -fomit-frame-pointer. Кроме того, "
+"переменные окружения LD_PRELOAD и LD_LIBRARY_PATH могут быть перезаписаны "
+"или сброшены пользователем."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:208
+#, no-wrap
+msgid "SetUID issues"
+msgstr "Проблемы с SetUID"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:212
+msgid ""
+"There are at least 6 different IDs associated with any given process, and "
+"you must therefore be very careful with the access that your process has at "
+"any given time. In particular, all seteuid applications should give up "
+"their privileges as soon as it is no longer required."
+msgstr ""
+"Существует как минимум 6 различных идентификаторов, связанных с каждым "
+"процессом, поэтому необходимо очень внимательно следить за уровнем доступа "
+"вашего процесса в любой момент времени. В частности, все приложения с "
+"seteuid должны отказываться от своих привилегий, как только в них больше нет "
+"необходимости."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:215
+msgid ""
+"The real user ID can only be changed by a superuser process. The login "
+"program sets this when a user initially logs in and it is seldom changed."
+msgstr ""
+"Действительный идентификатор пользователя может быть изменён только "
+"процессом с правами суперпользователя. Программа login устанавливает его при "
+"первоначальном входе пользователя в систему, и он редко изменяется."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:219
+msgid ""
+"The effective user ID is set by the `exec()` functions if a program has its "
+"seteuid bit set. An application can call `seteuid()` at any time to set the "
+"effective user ID to either the real user ID or the saved set-user-ID. When "
+"the effective user ID is set by `exec()` functions, the previous value is "
+"saved in the saved set-user-ID."
+msgstr ""
+"Эффективный идентификатор пользователя устанавливается функциями `exec()`, "
+"если у программы установлен бит seteuid. Приложение может вызывать "
+"`seteuid()` в любое время, чтобы установить эффективный идентификатор "
+"пользователя либо в реальный идентификатор пользователя, либо в сохранённый "
+"set-user-ID. Когда эффективный идентификатор пользователя устанавливается "
+"функциями `exec()`, предыдущее значение сохраняется в сохранённом set-user-"
+"ID."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:221
+#, no-wrap
+msgid "Limiting your program's environment"
+msgstr "Ограничение окружения вашей программы"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:229
+msgid ""
+"The traditional method of restricting a process is with the `chroot()` "
+"system call. This system call changes the root directory from which all "
+"other paths are referenced for a process and any child processes. For this "
+"call to succeed the process must have execute (search) permission on the "
+"directory being referenced. The new environment does not actually take "
+"effect until you `chdir()` into your new environment. It should also be "
+"noted that a process can easily break out of a chroot environment if it has "
+"root privilege. This could be accomplished by creating device nodes to read "
+"kernel memory, attaching a debugger to a process outside of the "
+"man:chroot[8] environment, or in many other creative ways."
+msgstr ""
+"Традиционный метод ограничения процесса — это системный вызов `chroot()`. "
+"Этот системный вызов изменяет корневой каталог, от которого ссылаются все "
+"остальные пути для процесса и любых дочерних процессов. Для успешного "
+"выполнения этого вызова процесс должен иметь право на выполнение (поиск) в "
+"указанном каталоге. Новая среда фактически не вступает в силу, пока вы не "
+"выполните `chdir()` в новой среде. Также следует отметить, что процесс может "
+"легко выйти из окружения chroot, если он имеет привилегии root. Это может "
+"быть достигнуто путем создания узлов устройств для чтения памяти ядра, "
+"подключения отладчика к процессу вне окружения man:chroot[8] или многими "
+"другими творческими способами."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:234
+msgid ""
+"The behavior of the `chroot()` system call can be controlled somewhat with "
+"the kern.chroot_allow_open_directories `sysctl` variable. When this value "
+"is set to 0, `chroot()` will fail with EPERM if there are any directories "
+"open. If set to the default value of 1, then `chroot()` will fail with "
+"EPERM if there are any directories open and the process is already subject "
+"to a `chroot()` call. For any other value, the check for open directories "
+"will be bypassed completely."
+msgstr ""
+"Поведение системного вызова `chroot()` можно частично контролировать с "
+"помощью переменной `sysctl` kern.chroot_allow_open_directories. Если этому "
+"параметру присвоено значение 0, `chroot()` завершится с ошибкой EPERM, если "
+"есть какие-либо открытые каталоги. Если установлено значение по умолчанию 1, "
+"то `chroot()` завершится с ошибкой EPERM, если есть открытые каталоги и "
+"процесс уже находится внутри вызова `chroot()`. Для любого другого значения "
+"проверка на открытые каталоги будет полностью пропущена."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:235
+#, no-wrap
+msgid "FreeBSD's jail functionality"
+msgstr "Функциональность клеток FreeBSD"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:239
+msgid ""
+"The concept of a Jail extends upon the `chroot()` by limiting the powers of "
+"the superuser to create a true `virtual server'. Once a prison is set up "
+"all network communication must take place through the specified IP address, "
+"and the power of \"root privilege\" in this jail is severely constrained."
+msgstr ""
+"Концепция `клетки` расширяет возможности `chroot()`, ограничивая права "
+"суперпользователя для создания настоящего `виртуального сервера`. После "
+"настройки клетки все сетевые взаимодействия должны осуществляться через "
+"указанный IP-адрес, а привилегии `root` внутри этой клетки сильно ограничены."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:243
+msgid ""
+"While in a prison, any tests of superuser power within the kernel using the "
+"`suser()` call will fail. However, some calls to `suser()` have been "
+"changed to a new interface `suser_xxx()`. This function is responsible for "
+"recognizing or denying access to superuser power for imprisoned processes."
+msgstr ""
+"Находясь в клетке, любые проверки прав суперпользователя в ядре с "
+"использованием вызова `suser()` завершатся неудачей. Однако некоторые вызовы "
+"`suser()` были заменены на новый интерфейс `suser_xxx()`. Эта функция "
+"отвечает за распознавание или запрет доступа к правам суперпользователя для "
+"процессов в клетке."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:245
+msgid "A superuser process within a jailed environment has the power to:"
+msgstr "Суперпользователь в среде клетки имеет возможность:"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:247
+msgid ""
+"Manipulate credential with `setuid`, `seteuid`, `setgid`, `setegid`, "
+"`setgroups`, `setreuid`, `setregid`, `setlogin`"
+msgstr ""
+"Управлять учетными данными с помощью `setuid`, `seteuid`, `setgid`, "
+"`setegid`, `setgroups`, `setreuid`, `setregid`, `setlogin`"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:248
+msgid "Set resource limits with `setrlimit`"
+msgstr "Установливать ограничений ресурсов с помощью `setrlimit`"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:249
+msgid "Modify some sysctl nodes (kern.hostname)"
+msgstr "Изменять некоторые узлы sysctl (kern.hostname)"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:250
+msgid "`chroot()`"
+msgstr "`chroot()`"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:251
+msgid "Set flags on a vnode: `chflags`, `fchflags`"
+msgstr "Устанавливать флаги на vnode: `chflags`, `fchflags`"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:252
+msgid ""
+"Set attributes of a vnode such as file permission, owner, group, size, "
+"access time, and modification time."
+msgstr ""
+"Устанавливать атрибуты vnode, такие как права доступа к файлу, владелец, "
+"группа, размер, время доступа и время изменения."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:253
+msgid "Bind to privileged ports in the Internet domain (ports < 1024)"
+msgstr ""
+"Привязываться к привилегированным портам в домене Интернета (порты < 1024)"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:257
+msgid ""
+"`Jail` is a very useful tool for running applications in a secure "
+"environment but it does have some shortcomings. Currently, the IPC "
+"mechanisms have not been converted to the `suser_xxx` so applications such "
+"as MySQL cannot be run within a jail. Superuser access may have a very "
+"limited meaning within a jail, but there is no way to specify exactly what "
+"\"very limited\" means."
+msgstr ""
+"`Клетка` — это очень полезный инструмент для запуска приложений в безопасной "
+"среде, но у него есть некоторые недостатки. В настоящее время механизмы IPC "
+"не были преобразованы в `suser_xxx`, поэтому такие приложения, как MySQL, не "
+"могут быть запущены внутри клетки. Доступ суперпользователя может иметь "
+"очень ограниченное значение внутри клетки, но нет возможности точно указать, "
+"что означает «очень ограниченный»."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:258
+#, no-wrap
+msgid "POSIX(R).1e Process Capabilities"
+msgstr "Возможности процесса в POSIX(R).1e"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:261
+msgid ""
+"POSIX(R) has released a working draft that adds event auditing, access "
+"control lists, fine grained privileges, information labeling, and mandatory "
+"access control."
+msgstr ""
+"POSIX(R) выпустил рабочий проект, который добавляет аудит событий, списки "
+"контроля доступа, детализированные привилегии, маркировку информации и "
+"обязательный контроль доступа."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:264
+msgid ""
+"This is a work in progress and is the focus of the http://www.trustedbsd.org/"
+"[TrustedBSD] project. Some of the initial work has been committed to "
+"FreeBSD-CURRENT (cap_set_proc(3))."
+msgstr ""
+"Это работа в процессе, и она является основным направлением проекта http://"
+"www.trustedbsd.org/[TrustedBSD]. Некоторые первоначальные наработки были "
+"добавлены в FreeBSD-CURRENT (cap_set_proc(3))."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:266
+#, no-wrap
+msgid "Trust"
+msgstr "Доверие"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:270
+msgid ""
+"An application should never assume that anything about the users environment "
+"is sane. This includes (but is certainly not limited to): user input, "
+"signals, environment variables, resources, IPC, mmaps, the filesystem "
+"working directory, file descriptors, the # of open files, etc."
+msgstr ""
+"Приложение никогда не должно предполагать, что окружение пользователя "
+"является предсказуемым. Это включает (но не ограничивается): "
+"пользовательский ввод, сигналы, переменные окружения, ресурсы, IPC, mmaps, "
+"текущую рабочую директорию файловой системы, файловые дескрипторы, "
+"количество открытых файлов и т.д."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:275
+msgid ""
+"You should never assume that you can catch all forms of invalid input that a "
+"user might supply. Instead, your application should use positive filtering "
+"to only allow a specific subset of inputs that you deem safe. Improper data "
+"validation has been the cause of many exploits, especially with CGI scripts "
+"on the world wide web. For filenames you need to be extra careful about "
+"paths (\"../\", \"/\"), symbolic links, and shell escape characters."
+msgstr ""
+"Никогда не следует предполагать, что можно отловить все виды некорректных "
+"входных данных, которые может предоставить пользователь. Вместо этого ваше "
+"приложение должно использовать позитивную фильтрацию, разрешая только "
+"определённое подмножество входных данных, которые вы считаете безопасными. "
+"Некорректная проверка данных стала причиной многих уязвимостей, особенно в "
+"CGI-скриптах во всемирной паутине. Для имён файлов необходимо быть особенно "
+"осторожными с путями (\"../\", \"/\"), символическими ссылками и escape-"
+"символами оболочки."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:278
+msgid ""
+"Perl has a really cool feature called \"Taint\" mode which can be used to "
+"prevent scripts from using data derived outside the program in an unsafe "
+"way. This mode will check command line arguments, environment variables, "
+"locale information, the results of certain syscalls (`readdir()`, "
+"`readlink()`, `getpwxxx()`), and all file input."
+msgstr ""
+"В Perl есть замечательная функция под названием \"Режим Taint\", которая "
+"может использоваться для предотвращения небезопасного использования данных, "
+"полученных извне программы. Этот режим проверяет аргументы командной строки, "
+"переменные окружения, информацию о локали, результаты определённых системных "
+"вызовов (`readdir()`, `readlink()`, `getpwxxx()`) и все вводимые данные из "
+"файлов."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:280
+#, no-wrap
+msgid "Race Conditions"
+msgstr "Состояние гонки"
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:284
+msgid ""
+"A race condition is anomalous behavior caused by the unexpected dependence "
+"on the relative timing of events. In other words, a programmer incorrectly "
+"assumed that a particular event would always happen before another."
+msgstr ""
+"Состояние гонки — это аномальное поведение, вызванное непредвиденной "
+"зависимостью от относительного времени событий. Другими словами, программист "
+"ошибочно предположил, что определенное событие всегда произойдет раньше "
+"другого."
+
+#. type: XXX figure here!
+#: documentation/content/en/books/developers-handbook/secure/_index.adoc:290
+msgid ""
+"Some of the common causes of race conditions are signals, access checks, and "
+"file opens. Signals are asynchronous events by nature so special care must "
+"be taken in dealing with them. Checking access with `access(2)` then "
+"`open(2)` is clearly non-atomic. Users can move files in between the two "
+"calls. Instead, privileged applications should `seteuid()` and then call "
+"`open()` directly. Along the same lines, an application should always set a "
+"proper umask before `open()` to obviate the need for spurious `chmod()` "
+"calls."
+msgstr ""
+"Некоторые из распространённых причин состояний гонки — это сигналы, проверки "
+"доступа и открытие файлов. Сигналы по своей природе являются асинхронными "
+"событиями, поэтому при работе с ними необходимо проявлять особую "
+"осторожность. Проверка доступа с помощью `access(2)`, а затем `open(2)` явно "
+"неатомарна. Пользователи могут перемещать файлы между этими двумя вызовами. "
+"Вместо этого привилегированные приложения должны использовать `seteuid()`, а "
+"затем вызывать `open()` напрямую. По аналогии, приложение всегда должно "
+"устанавливать правильную маску (`umask`) перед вызовом `open()`, чтобы "
+"избежать необходимости в лишних вызовах `chmod()`."
diff --git a/documentation/content/ru/books/developers-handbook/sockets/_index.adoc b/documentation/content/ru/books/developers-handbook/sockets/_index.adoc
new file mode 100644
index 0000000000..669a53fb60
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/sockets/_index.adoc
@@ -0,0 +1,909 @@
+---
+authors:
+ -
+ author: 'G. Adam Stanislav'
+description: 'Сокеты FreeBSD'
+next: books/developers-handbook/ipv6
+params:
+ path: /books/developers-handbook/sockets/
+prev: books/developers-handbook/partii
+showBookMenu: true
+tags: ["Sockets", "Protocols"]
+title: 'Глава 7. Сокеты'
+weight: 9
+---
+
+[[sockets]]
+= Сокеты
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 7
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/developers-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[sockets-synopsis]]
+== Обзор
+
+Сокеты BSD выводят межпроцессное взаимодействие на новый уровень. Теперь взаимодействующие процессы не обязательно должны выполняться на одной машине. Они всё ещё _могут_, но не обязаны.
+
+Не только эти процессы не обязаны выполняться на одной машине, они также могут работать под разными операционными системами. Благодаря BSD-сокетам, ваше ПО на FreeBSD может легко взаимодействовать с программой, работающей на Macintosh(R), другой — на рабочей станции Sun(TM), и ещё одной — под Windows(R) 2000, при этом все они подключены к локальной сети на основе Ethernet.
+
+Но ваше программное обеспечение может так же эффективно взаимодействовать с процессами, работающими в другом здании, на другом континенте, внутри подводной лодки или космического челнока.
+
+Он также может взаимодействовать с процессами, которые не являются частью компьютера (по крайней мере, не в строгом смысле этого слова), а таких устройств, как принтеры, цифровые камеры, медицинское оборудование. Практически со всем, что способно к цифровой коммуникации.
+
+[[sockets-diversity]]
+== Сетевое взаимодействие и разнообразие
+
+Мы уже упоминали о _разнообразии_ сетевых технологий. Множество различных систем должны взаимодействовать друг с другом. И они должны говорить на одном языке. Также они должны _понимать_ этот язык одинаковым образом.
+
+Часто думают, что _язык тела_ универсален. Но это не так. В ранней юности отец взял меня с собой в Болгарию. Мы сидели за столиком в парке Софии, когда к нам подошел продавец, предлагая купить жареный миндаль.
+
+Я тогда еще не знал болгарского, поэтому вместо словесного отказа я покачал головой из стороны в сторону — это «универсальный» язык тела для обозначения _нет_. Продавец тут же начал угощать нас миндалем.
+
+Затем я вспомнил, что мне говорили, будто в Болгарии покачивание головой из стороны в сторону означает _да_. Быстро я начал кивать головой вверх-вниз. Продавец заметил, взял свои миндалины и ушёл. Для непосвящённого наблюдателя я не изменил язык тела: я продолжал использовать движения головой — покачивание и кивание. Изменился _смысл_ языка тела. Сначала продавец и я интерпретировали одни и те же жесты как имеющие совершенно разный смысл. Мне пришлось скорректировать свою собственную интерпретацию этих жестов, чтобы продавец меня понял.
+
+То же самое и с компьютерами: одни и те же символы могут иметь разное, даже полностью противоположное значение. Поэтому, чтобы два компьютера понимали друг друга, они должны договориться не только об одном _языке_, но и об одном _толковании_ языка.
+
+[[sockets-protocols]]
+== Протоколы
+
+В то время как различные языки программирования обычно имеют сложный синтаксис и используют множество многосимвольных зарезервированных слов (что облегчает их понимание для человека-программиста), языки передачи данных, как правило, очень лаконичны. Вместо многосимвольных слов они часто используют отдельные _биты_. Для этого есть очень убедительная причина: хотя данные внутри вашего компьютера передаются со скоростью, близкой к скорости света, между двумя компьютерами они часто передаются значительно медленнее.
+
+Поскольку языки, используемые в передаче данных, настолько лаконичны, мы обычно называем их _протоколами_, а не языками.
+
+При передаче данных от одного компьютера к другому всегда используется более одного протокола. Эти протоколы _располагаются слоями_. Данные можно сравнить с внутренней частью лука: необходимо снять несколько слоёв «кожи», чтобы добраться до данных. Это лучше всего проиллюстрировано на рисунке:
+
+.Уровни протоколов
+image::layers.png[]
+
+В этом примере мы пытаемся получить изображение с веб-страницы, к которой подключены через Ethernet.
+
+Изображение состоит из необработанных данных, которые представляют собой просто последовательность значений RGB, которые наше программное обеспечение может обработать, т.е. преобразовать в изображение и отобразить на нашем мониторе.
+
+Увы, наше программное обеспечение не может определить, как организованы сырые данные: это последовательность значений RGB, последовательность интенсивностей в градациях серого или, возможно, цвета в кодировке CMYK? Представлены ли данные 8-битными квантами, или они имеют размер 16 бит, а может быть, 4 бита? Из скольких строк и столбцов состоит изображение? Должны ли определённые пиксели быть прозрачными?
+
+Я думаю, вы поняли...
+
+Чтобы наше программное обеспечение понимало, как обрабатывать сырые данные, они кодируются в формате PNG. Это мог бы быть GIF или JPEG, но выбран PNG.
+
+И PNG — это протокол.
+
+В этот момент я слышу, как некоторые из вас кричат: _"Нет, это не так! Это формат файла!"_
+
+Ну что ж, конечно, это формат файла. Но с точки зрения передачи данных, формат файла — это протокол: структура файла — это _язык_, причем лаконичный, сообщающий нашему _процессу_, как организованы данные. Следовательно, это _протокол_.
+
+Увы, если бы мы получили только PNG-файл, наше программное обеспечение столкнулось бы с серьёзной проблемой: как ему узнать, что данные представляют изображение, а не текст, звук или что-то ещё? Во-вторых, как ему определить, что изображение сохранено в формате PNG, а не GIF, JPEG или каком-либо другом формате изображений?
+
+Для получения этой информации мы используем другой протокол: HTTP. Этот протокол может точно сообщить нам, что данные представляют изображение и используют протокол PNG. Он также может сообщить некоторые другие сведения, но давайте сосредоточимся на уровнях протоколов здесь.
+
+Итак, теперь у нас есть некоторые данные, упакованные в протокол PNG, который в свою очередь упакован в протокол HTTP. Как мы получили их с сервера?
+
+Используя TCP/IP поверх Ethernet, вот как. Действительно, это ещё три протокола. Вместо того чтобы продолжать изнутри наружу, я теперь расскажу про Ethernet, просто потому что так проще объяснить остальное.
+
+Ethernet — это интересная система соединения компьютеров в _локальной сети_ (LAN). У каждого компьютера есть _карта сетевого интерфейса_ (NIC — Network Interface Card) с уникальным 48-битным идентификатором, называемым _адресом_. Не существует двух сетевых интерфейсов Ethernet в мире с одинаковым адресом.
+
+Эти сетевые карты соединены между собой. Когда один компьютер хочет связаться с другим в той же локальной сети Ethernet, он отправляет сообщение по сети. Каждая сетевая карта видит это сообщение. Однако, согласно _протоколу_ Ethernet, данные содержат адрес сетевой карты назначения (среди прочего). Таким образом, только одна из всех сетевых карт обратит на него внимание, остальные проигнорируют его.
+
+Но не все компьютеры подключены к одной сети. Тот факт, что мы получили данные через наш Ethernet, не означает, что они возникли в нашей локальной сети. Они могли попасть к нам из другой сети (которая может быть даже не на основе Ethernet), соединённой с нашей сетью через Интернет.
+
+Все данные передаются через Интернет с использованием IP, что означает _Internet Protocol_. Его основная роль — сообщать нам, откуда в мире пришли данные и куда они должны быть направлены. Он не _гарантирует_, что мы получим данные, только что мы узнаем, откуда они пришли, _если_ мы их получим.
+
+Даже если мы получим данные, IP не гарантирует, что различные фрагменты данных придут в том же порядке, в котором их отправил другой компьютер. Например, мы можем получить центр нашего изображения до того, как получим его верхний левый угол, а после — нижний правый.
+
+Это TCP (_Transmission Control Protocol_), который запрашивает у отправителя повторную отправку потерянных данных и располагает их в правильном порядке.
+
+В итоге потребовалось _пять_ различных протоколов, чтобы один компьютер мог сообщить другому, как выглядит изображение. Мы получили данные, упакованные в протокол PNG, который был упакован в протокол HTTP, который был упакован в протокол TCP, который был упакован в протокол IP, который был упакован в протокол Ethernet.
+
+О, и кстати, вероятно, на пути были задействованы и несколько других протоколов. Например, если наша локальная сеть была подключена к Интернету через дозвон, то использовался протокол PPP над модемом, который, в свою очередь, использовал один (или несколько) из различных модемных протоколов, и так далее, и так далее, и так далее...
+
+Как разработчик, вы уже должны задаваться вопросом: _"Как я должен со всем этим справляться?"_
+
+К счастью для вас, вам _не_ нужно разбираться во всём этом. Вам _придётся_ разобраться в некоторой части, но не во всей. В частности, вам не нужно беспокоиться о физическом подключении (в нашем случае Ethernet и, возможно, PPP и т.д.). Также вам не нужно разбираться с протоколом IP или протоколом TCP.
+
+Другими словами, вам не нужно ничего делать, чтобы получить данные с другого компьютера. Ну, разве что _попросить_ их, но это почти так же просто, как открыть файл.
+
+Получив данные, вам предстоит решить, что с ними делать. В нашем случае потребуется понимание протокола HTTP и структуры файла PNG.
+
+Используя аналогию, все межсетевые протоколы становятся серой зоной: не столько потому, что мы не понимаем, как они работают, а потому, что нас это больше не беспокоит. Интерфейс сокетов берёт на себя заботу об этой серой зоне:
+
+.Уровни протоколов, покрываемые сокетами
+image::slayers.png[]
+
+Нам нужно понимать только те протоколы, которые говорят нам, как _интерпретировать данные_, а не как _получать_ их от другого процесса или как _передавать_ их другому процессу.
+
+[[sockets-model]]
+== Модель сокетов
+
+Сокеты BSD построены по базовой модели UNIX(R): _Все является файлом._ Таким образом, в нашем примере сокеты позволят нам получить, образно говоря, _HTTP-файл_. Затем нам предстоит извлечь из него _PNG-файл_.
+
+Из-за сложности межсетевого взаимодействия мы не можем просто использовать системный вызов `open` или функцию `open()` в языке C. Вместо этого необходимо выполнить несколько шагов для "открытия" сокета.
+
+Однако, как только мы это сделаем, мы можем начать обращаться с _сокетом_ так же, как и с любым _файловым дескриптором_: мы можем `читать` из него, `писать` в него, передавать его через `канал` и, в конечном итоге, `закрывать` его.
+
+[[sockets-essential-functions]]
+== Основные функции сокетов
+
+В то время как FreeBSD предлагает различные функции для работы с сокетами, нам _требуется_ только четыре, чтобы "открыть" сокет. А в некоторых случаях достаточно двух.
+
+[[sockets-client-server]]
+=== Разница между клиентом и сервером
+
+Обычно одним из концов связи на основе сокетов является _сервер_, а другой — _клиент_.
+
+[[sockets-common-elements]]
+==== Общие элементы
+
+[[sockets-socket]]
+===== `socket`
+
+Функция, используемая как клиентами, так и серверами, это man:socket[2]. Она объявляется следующим образом:
+
+[.programlisting]
+....
+int socket(int domain, int type, int protocol);
+....
+
+Возвращаемое значение имеет тот же тип, что и у `open`, целое число. FreeBSD выделяет его значение из того же пула, что и дескрипторы файлов. Это позволяет обрабатывать сокеты так же, как файлы.
+
+Аргумент `domain` указывает системе, какое _семейство протоколов_ следует использовать. Существует множество семейств, некоторые из них специфичны для определённых поставщиков, другие широко распространены. Они объявлены в [.filename]#sys/socket.h#.
+
+Используйте `PF_INET` для UDP, TCP и других интернет-протоколов (IPv4).
+
+Для аргумента `type` определено пять значений, также указанных в [.filename]#sys/socket.h#. Все они начинаются с "`SOCK_`". Наиболее распространённое — `SOCK_STREAM`, которое указывает системе, что запрашивается _надёжный сервис потоковой доставки_ (это TCP при использовании с `PF_INET`).
+
+Если бы вы запросили `SOCK_DGRAM`, вы бы запросили _сервис доставки датаграмм без установления соединения_ (в нашем случае, UDP).
+
+Если вы хотите управлять низкоуровневыми протоколами (такими как IP) или даже сетевыми интерфейсами (например, Ethernet), вам потребуется указать `SOCK_RAW`.
+
+Наконец, аргумент `protocol` зависит от двух предыдущих аргументов и не всегда имеет смысл. В таком случае используйте значение `0`.
+
+[NOTE]
+.Неподключенный сокет
+====
+Нигде в функции `socket` мы не указали, к какой другой системе должны быть подключены. Наш только что созданный сокет остаётся _неподключённым_.
+
+Это сделано намеренно: если проводить аналогию с телефоном, мы только что подключили модем к телефонной линии. Мы не сказали модему совершить звонок или ответить, если телефон зазвонит.
+====
+
+[[sockets-sockaddr]]
+===== `sockaddr`
+
+Различные функции семейства сокетов ожидают адрес (или указатель, если использовать терминологию языка C) небольшой области памяти. Различные объявления на языке C в файле [.filename]#sys/socket.h# ссылаются на неё как на `struct sockaddr`. Эта структура объявлена в том же файле:
+
+[.programlisting]
+....
+/*
+ * Structure used by kernel to store most
+ * addresses.
+ */
+struct sockaddr {
+ unsigned char sa_len; /* total length */
+ sa_family_t sa_family; /* address family */
+ char sa_data[14]; /* actually longer; address value */
+};
+#define SOCK_MAXADDRLEN 255 /* longest possible addresses */
+....
+
+Обратите внимание на _неопределённость_, с которой объявлено поле `sa_data` — просто как массив из `14` байт, с комментарием, намекающим, что их может быть больше `14`.
+
+Эта неопределенность вполне преднамеренна. Сокеты — это очень мощный интерфейс. Хотя большинство людей, возможно, считают их не более чем интерфейсом для Интернета — и большинство приложений, вероятно, используют их именно для этого в наши дни — сокеты могут быть использованы практически для _любого_ вида межпроцессного взаимодействия, из которых Интернет (или, точнее, IP) — лишь один из них.
+
+[.filename]#sys/socket.h# ссылается на различные типы протоколов, с которыми работают сокеты, как на _семейства адресов_, и перечисляет их непосредственно перед определением `sockaddr`:
+
+[.programlisting]
+....
+/*
+ * Address families.
+ */
+#define AF_UNSPEC 0 /* unspecified */
+#define AF_LOCAL 1 /* local to host (pipes, portals) */
+#define AF_UNIX AF_LOCAL /* backward compatibility */
+#define AF_INET 2 /* internetwork: UDP, TCP, etc. */
+#define AF_IMPLINK 3 /* arpanet imp addresses */
+#define AF_PUP 4 /* pup protocols: e.g. BSP */
+#define AF_CHAOS 5 /* mit CHAOS protocols */
+#define AF_NS 6 /* XEROX NS protocols */
+#define AF_ISO 7 /* ISO protocols */
+#define AF_OSI AF_ISO
+#define AF_ECMA 8 /* European computer manufacturers */
+#define AF_DATAKIT 9 /* datakit protocols */
+#define AF_CCITT 10 /* CCITT protocols, X.25 etc */
+#define AF_SNA 11 /* IBM SNA */
+#define AF_DECnet 12 /* DECnet */
+#define AF_DLI 13 /* DEC Direct data link interface */
+#define AF_LAT 14 /* LAT */
+#define AF_HYLINK 15 /* NSC Hyperchannel */
+#define AF_APPLETALK 16 /* Apple Talk */
+#define AF_ROUTE 17 /* Internal Routing Protocol */
+#define AF_LINK 18 /* Link layer interface */
+#define pseudo_AF_XTP 19 /* eXpress Transfer Protocol (no AF) */
+#define AF_COIP 20 /* connection-oriented IP, aka ST II */
+#define AF_CNT 21 /* Computer Network Technology */
+#define pseudo_AF_RTIP 22 /* Help Identify RTIP packets */
+#define AF_IPX 23 /* Novell Internet Protocol */
+#define AF_SIP 24 /* Simple Internet Protocol */
+#define pseudo_AF_PIP 25 /* Help Identify PIP packets */
+#define AF_ISDN 26 /* Integrated Services Digital Network*/
+#define AF_E164 AF_ISDN /* CCITT E.164 recommendation */
+#define pseudo_AF_KEY 27 /* Internal key-management function */
+#define AF_INET6 28 /* IPv6 */
+#define AF_NATM 29 /* native ATM access */
+#define AF_ATM 30 /* ATM */
+#define pseudo_AF_HDRCMPLT 31 /* Used by BPF to not rewrite headers
+ * in interface output routine
+ */
+#define AF_NETGRAPH 32 /* Netgraph sockets */
+#define AF_SLOW 33 /* 802.3ad slow protocol */
+#define AF_SCLUSTER 34 /* Sitara cluster protocol */
+#define AF_ARP 35
+#define AF_BLUETOOTH 36 /* Bluetooth sockets */
+#define AF_MAX 37
+....
+
+Используемый для IP — это AF_INET. Это символ для константы `2`.
+
+Это _семейство адресов_, указанное в поле `sa_family` структуры `sockaddr`, определяет, как именно будут использоваться нечетко названные байты `sa_data`.
+
+В частности, когда _семейство адресов_ — AF_INET, можно использовать `struct sockaddr_in` из [.filename]#netinet/in.h# везде, где ожидается `sockaddr`:
+
+[.programlisting]
+....
+/*
+ * Socket address, internet style.
+ */
+struct sockaddr_in {
+ uint8_t sin_len;
+ sa_family_t sin_family;
+ in_port_t sin_port;
+ struct in_addr sin_addr;
+ char sin_zero[8];
+};
+....
+
+Мы можем визуализировать его организацию следующим образом:
+
+.Структура `sockaddr_in`
+image::sain.png[]
+
+Три важных поля — это `sin_family`, которое находится в байте 1 структуры, `sin_port`, 16-битное значение, расположенное в байтах 2 и 3, и `sin_addr`, 32-битное целочисленное представление IP-адреса, хранящееся в байтах 4–7.
+
+Теперь попробуем заполнить его. Предположим, мы пытаемся написать клиент для протокола _daytime_, который просто указывает, что его сервер записывает текстовую строку с текущей датой и временем в порт 13. Мы хотим использовать TCP/IP, поэтому нам нужно указать `AF_INET` в поле семейства адресов. `AF_INET` определен как `2`. Давайте используем IP-адрес `192.43.244.18`, который является сервером времени федерального правительства США (`time.nist.gov`).
+
+.Конкретный пример sockaddr_in
+image::sainfill.png[]
+
+Кстати, поле `sin_addr` объявлено как имеющее тип `struct in_addr`, который определён в [.filename]#netinet/in.h#:
+
+[.programlisting]
+....
+/*
+ * Internet address (a structure for historical reasons)
+ */
+struct in_addr {
+ in_addr_t s_addr;
+};
+....
+
+В дополнение, `in_addr_t` является 32-битным целым числом.
+
+`192.43.244.18` — это просто удобная форма записи 32-битного целого числа, в которой перечисляются все его 8-битные байты, начиная с _старшего_.
+
+До сих пор мы рассматривали `sockaddr` как абстракцию. Наш компьютер не хранит `short` целые числа как единую 16-битную сущность, а как последовательность 2 байт. Аналогично, он хранит 32-битные целые числа как последовательность 4 байт.
+
+Предположим, мы написали что-то вроде этого:
+
+[.programlisting]
+....
+sa.sin_family = AF_INET;
+sa.sin_port = 13;
+sa.sin_addr.s_addr = (((((192 << 8) | 43) << 8) | 244) << 8) | 18;
+....
+
+Как будет выглядеть результат?
+
+Ну, это, конечно, зависит от многого. На компьютере с процессором Pentium(R) или другим на базе x86 это будет выглядеть так:
+
+.`sockaddr_in` в системе с архитектурой Intel
+image::sainlsb.png[]
+
+На другой системе это может выглядеть так:
+
+.`sockaddr_in` в системе с порядком байтов от старшего к младшему
+image::sainmsb.png[]
+
+И на PDP это может выглядеть иначе. Однако два приведённых выше варианта являются наиболее распространёнными на сегодняшний день.
+
+Обычно, стремясь писать переносимый код, программисты делают вид, что этих различий не существует. И им это сходит с рук (за исключением случаев, когда они пишут на ассемблере). Увы, при программировании сокетов так легко отделаться не получится.
+
+Почему?
+
+Потому что при обмене данными с другим компьютером вы обычно не знаете, хранит ли он данные, начиная со _старшего байта_ (MSB) или с _младшего байта_ (LSB).
+
+Вы можете задаться вопросом: _"Значит, сокеты не будут это делать за меня?"_
+
+Не будут.
+
+Хотя этот ответ может сначала вас удивить, помните, что общий интерфейс сокетов понимает только поля `sa_len` и `sa_family` структуры `sockaddr`. Вам не нужно беспокоиться о порядке байтов (конечно, в FreeBSD `sa_family` занимает всего 1 байт, но многие другие UNIX(R)-системы не имеют `sa_len` и используют 2 байта для `sa_family`, ожидая данные в том порядке, который является родным для компьютера).
+
+Но остальные данные — это просто `sa_data[14]` с точки зрения сокетов. В зависимости от _семейства адресов_ сокеты просто передают эти данные по назначению.
+
+Действительно, когда мы указываем номер порта, это делается для того, чтобы другая компьютерная система знала, какую службу мы запрашиваем. И, когда мы выступаем в роли сервера, мы считываем номер порта, чтобы понять, какую службу ожидает от нас другая система. В любом случае, сокетам нужно лишь передать номер порта в качестве данных. Они никак его не интерпретируют.
+
+Аналогично, мы указываем IP-адрес, чтобы сообщить всем на пути, куда отправлять наши данные. Сокеты, опять же, просто пересылают их как данные.
+
+Вот почему мы (программисты, а не сокеты) должны различать порядок байтов, используемый нашим компьютером, и условный порядок байтов для отправки данных на другой компьютер.
+
+Мы будем называть порядок байтов, который использует наш компьютер, _порядком байтов хоста_ или просто _хост-порядком_.
+
+Существует соглашение о передаче многобайтовых данных по IP _старшим байтом вперёд_. Это мы будем называть _порядком байтов сети_ или просто _сетевым порядком_.
+
+Вот, если бы мы скомпилировали приведённый выше код для компьютера на базе Intel, наш _порядок байтов хоста_ выдал бы:
+
+.Порядок байтов на хосте в системе Intel
+image::sainlsb.png[]
+
+Но порядок байтов в _сетевом формате_ требует, чтобы данные хранились начиная со старшего байта (MSB):
+
+.Порядок байтов в сети
+image::sainmsb.png[]
+
+К сожалению, наш _порядок хоста_ полностью противоположен _порядку сети_.
+
+У нас есть несколько способов решения этой проблемы. Один из них — _инвертировать_ значения в нашем коде:
+
+[.programlisting]
+....
+sa.sin_family = AF_INET;
+sa.sin_port = 13 << 8;
+sa.sin_addr.s_addr = (((((18 << 8) | 244) << 8) | 43) << 8) | 192;
+....
+
+Это _обманет_ наш компилятор, заставив его сохранить данные в _порядке байтов сети_. В некоторых случаях это именно тот способ, который нужен (например, при программировании на ассемблере). Однако в большинстве случаев это может вызвать проблему.
+
+Предположим, вы написали программу на C, использующую сокеты. Вы знаете, что она будет работать на Pentium(R), поэтому вводите все константы в обратном порядке и приводите их к _порядку байтов сети_. Она работает хорошо.
+
+Затем, однажды, ваш надежный старый Pentium(R) превращается в ржавый старый Pentium(R). Вы заменяете его системой, у которой _порядок байтов хоста_ совпадает с _сетевым порядком байтов_. Вам нужно перекомпилировать все ваше программное обеспечение. Все ваши программы продолжают работать хорошо, кроме той одной программы, которую вы написали.
+
+Вы уже забыли, что принудительно задали все свои константы противоположными _порядку хоста_. Вы проводите некоторое время, яростно рвя на себе волосы, взывая ко всем известным вам богам (и к некоторым, которых вы придумали), стуча нерф-битой по монитору и выполняя прочие традиционные ритуалы в попытке понять, почему то, что работало так хорошо, внезапно перестало работать вообще.
+
+В конце концов, вы разбираетесь в проблеме, произносите пару крепких словечек и начинаете переписывать свой код.
+
+К счастью, вы не первый, кто столкнулся с этой проблемой. Кто-то уже создал функции man:htons[3] и man:htonl[3] на языке C для преобразования `short` и `long` соответственно из _порядка байтов хоста_ в _порядок байтов сети_, а также функции man:ntohs[3] и man:ntohl[3] на языке C для обратного преобразования.
+
+На системах с порядком _старший байт первый_ эти функции не выполняют никаких действий. На системах с порядком _младший байт первый_ они преобразуют значения в правильный порядок.
+
+Итак, независимо от того, на какой системе компилируется ваше программное обеспечение, ваши данные будут в правильном порядке, если вы используете эти функции.
+
+[[sockets-client-functions]]
+==== Функции клиента
+
+Обычно клиент инициирует подключение к серверу. Клиент знает, к какому серверу он собирается обратиться: он знает его IP-адрес и _порт_, на котором работает сервер. Это похоже на то, как вы поднимаете трубку и набираете номер (_адрес_), а затем, когда кто-то отвечает, просите соединить со специалистом по непонятным символам (_порт_).
+
+[[sockets-connect]]
+===== `connect`
+
+Как только клиент создал сокет, ему нужно подключить его к определённому порту на удалённой системе. Для этого используется man:connect[2]:
+
+[.programlisting]
+....
+int connect(int s, const struct sockaddr *name, socklen_t namelen);
+....
+
+Аргумент `s` — это сокет, то есть значение, возвращаемое функцией `socket`. Аргумент `name` — это указатель на структуру `sockaddr`, которую мы подробно обсуждали. Наконец, `namelen` сообщает системе, сколько байт находится в нашей структуре `sockaddr`.
+
+Если `connect` завершается успешно, он возвращает `0`. В противном случае возвращается `-1`, а код ошибки сохраняется в `errno`.
+
+Существует множество причин, по которым `connect` может завершиться неудачей. Например, при попытке подключения к интернету, IP-адрес может не существовать, быть недоступен, перегружен или на указанном порту может не быть сервера. Или же подключение может быть явно _отклонено_ по определённым причинам.
+
+[[sockets-first-client]]
+===== Наш первый клиент
+
+Теперь мы знаем достаточно, чтобы написать очень простого клиента, который получит текущее время от `192.43.244.18` и выведет его в [.filename]#stdout#.
+
+[.programlisting]
+....
+/*
+ * daytime.c
+ *
+ * Programmed by G. Adam Stanislav
+ */
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+
+int main() {
+ int s, bytes;
+ struct sockaddr_in sa;
+ char buffer[BUFSIZ+1];
+
+ if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ memset(&sa, '\0', sizeof(sa));
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(13);
+ sa.sin_addr.s_addr = htonl((((((192 << 8) | 43) << 8) | 244) << 8) | 18);
+ if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {
+ perror("connect");
+ close(s);
+ return 2;
+ }
+
+ while ((bytes = read(s, buffer, BUFSIZ)) > 0)
+ write(1, buffer, bytes);
+
+ close(s);
+ return 0;
+}
+....
+
+Вперед! Введите это в вашем редакторе, сохраните как [.filename]#daytime.c#, затем скомпилируйте и запустите:
+
+[source, bash]
+....
+% cc -O3 -o daytime daytime.c
+% ./daytime
+
+52079 01-06-19 02:29:25 50 0 1 543.9 UTC(NIST) *
+%
+....
+
+В данном случае дата была 19 июня 2001 года, время — 02:29:25 UTC. Естественно, ваши результаты могут отличаться.
+
+[[sockets-server-functions]]
+==== Функции сервера
+
+Типичный сервер не инициирует соединение. Вместо этого он ожидает, когда клиент обратится к нему и запросит услуги. Он не знает, когда клиент обратится, ни сколько клиентов обратится. В один момент он может просто спокойно ожидать, а в следующий момент он может оказаться перегруженным запросами от множества клиентов, обращающихся одновременно.
+
+Интерфейс сокетов предоставляет три основные функции для обработки этого.
+
+[[sockets-bind]]
+===== `bind`
+
+Порты подобны внутренним номерам телефонной линии: после набора основного номера вы набираете внутренний номер, чтобы связаться с конкретным человеком или отделом.
+
+Существует 65535 IP-портов, но сервер обычно обрабатывает запросы, поступающие только на один из них. Это как сказать оператору телефонной комнаты, что мы сейчас на месте и готовы отвечать на звонки по определённому внутреннему номеру. Мы используем man:bind[2], чтобы указать сокетам, на каком порту мы хотим обслуживать запросы.
+
+[.programlisting]
+....
+int bind(int s, const struct sockaddr *addr, socklen_t addrlen);
+....
+
+Помимо указания порта в `addr`, сервер может включать свой IP-адрес. Однако он может просто использовать символическую константу INADDR_ANY, чтобы указать, что будет обслуживать все запросы на указанный порт, независимо от его IP-адреса. Этот символ, наряду с несколькими аналогичными, объявлен в [.filename]#netinet/in.h#
+
+[.programlisting]
+....
+#define INADDR_ANY (u_int32_t)0x00000000
+....
+
+Предположим, мы пишем сервер для протокола _daytime_ поверх TCP/IP. Напомним, что он использует порт 13. Наша структура `sockaddr_in` будет выглядеть так:
+
+.Пример sockaddr_in сервера
+image::sainserv.png[]
+
+[[sockets-listen]]
+===== `listen`
+
+Продолжая аналогию с офисным телефоном, после того как вы сообщили оператору АТС, на каком внутреннем номере вы будете находиться, вы заходите в свой офис и убеждаетесь, что ваш телефон подключен и звонок включен. Кроме того, вы активируете функцию ожидания вызова, чтобы слышать звонок даже во время разговора с кем-то.
+
+Сервер обеспечивает все это с помощью функции man:listen[2].
+
+[.programlisting]
+....
+int listen(int s, int backlog);
+....
+
+Здесь переменная `backlog` указывает сокетам, сколько входящих запросов принимать, пока вы заняты обработкой последнего запроса. Другими словами, она определяет максимальный размер очереди ожидающих соединений.
+
+[[sockets-accept]]
+===== `accept`
+
+После того как вы услышите телефонный звонок, вы принимаете вызов, отвечая на звонок. Теперь вы установили соединение с вашим клиентом. Это соединение остается активным, пока вы или ваш клиент не повесите трубку.
+
+Сервер принимает соединение, используя функцию man:accept[2].
+
+[.programlisting]
+....
+int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
+....
+
+Обратите внимание, что в этот раз `addrlen` является указателем. Это необходимо, потому что в данном случае именно сокет заполняет структуру `addr` — `sockaddr_in`.
+
+Возвращаемое значение является целым числом. Действительно, `accept` возвращает _новый сокет_. Этот новый сокет будет использоваться для обмена данными с клиентом.
+
+Что происходит со старым сокетом? Он продолжает ожидать новые запросы (помните переменную `backlog`, которую мы передали в `listen`?), пока мы не закроем его (`close`).
+
+Теперь новый сокет предназначен только для обмена данными. Он полностью подключен. Мы не можем снова передать его в `listen`, чтобы принимать дополнительные соединения.
+
+[[sockets-first-server]]
+===== Наш первый сервер
+
+Наш первый сервер будет несколько сложнее, чем первый клиент: нам нужно не только использовать больше функций сокетов, но и написать его как демон.
+
+Это лучше всего достигается созданием _дочернего процесса_ после привязки порта. Затем основной процесс завершается и возвращает управление оболочке (или любой другой программе, которая его вызвала).
+
+Дочерний процесс вызывает `listen`, затем запускает бесконечный цикл, который принимает соединение, обслуживает его и в конечном итоге закрывает свой сокет.
+
+[.programlisting]
+....
+/*
+ * daytimed - a port 13 server
+ *
+ * Programmed by G. Adam Stanislav
+ * June 19, 2001
+ */
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#define BACKLOG 4
+
+int main() {
+ int s, c;
+ socklen_t b;
+ struct sockaddr_in sa;
+ time_t t;
+ struct tm *tm;
+ FILE *client;
+
+ if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ memset(&sa, '\0', sizeof(sa));
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(13);
+
+ if (INADDR_ANY)
+ sa.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if (bind(s, (struct sockaddr *)&sa, sizeof sa) < 0) {
+ perror("bind");
+ return 2;
+ }
+
+ switch (fork()) {
+ case -1:
+ perror("fork");
+ return 3;
+ default:
+ close(s);
+ return 0;
+ case 0:
+ break;
+ }
+
+ listen(s, BACKLOG);
+
+ for (;;) {
+ b = sizeof sa;
+
+ if ((c = accept(s, (struct sockaddr *)&sa, &b)) < 0) {
+ perror("daytimed accept");
+ return 4;
+ }
+
+ if ((client = fdopen(c, "w")) == NULL) {
+ perror("daytimed fdopen");
+ return 5;
+ }
+
+ if ((t = time(NULL)) < 0) {
+ perror("daytimed time");
+ return 6;
+ }
+
+ tm = gmtime(&t);
+ fprintf(client, "%.4i-%.2i-%.2iT%.2i:%.2i:%.2iZ\n",
+ tm->tm_year + 1900,
+ tm->tm_mon + 1,
+ tm->tm_mday,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
+
+ fclose(client);
+ }
+}
+....
+
+Начинаем с создания сокета. Затем заполняем структуру `sockaddr_in` в `sa`. Обратите внимание на условное использование INADDR_ANY:
+
+[.programlisting]
+....
+if (INADDR_ANY)
+ sa.sin_addr.s_addr = htonl(INADDR_ANY);
+....
+
+Его значение равно `0`. Поскольку мы только что использовали `bzero` для всей структуры, будет избыточным снова устанавливать его в `0`. Но если мы перенесем наш код на другую систему, где INADDR_ANY, возможно, не равен нулю, нам нужно будет присвоить его `sa.sin_addr.s_addr`. Большинство современных компиляторов C достаточно умны, чтобы заметить, что INADDR_ANY — это константа. Пока она равна нулю, они оптимизируют все условное выражение из кода.
+
+После успешного вызова `bind` мы готовы стать _демоном_: используем `fork` для создания дочернего процесса. В обоих процессах, родительском и дочернем, переменная `s` является нашим сокетом. Родительскому процессу он больше не нужен, поэтому он вызывает `close`, затем возвращает `0`, чтобы сообщить своему родителю об успешном завершении.
+
+Между тем, дочерний процесс продолжает работать в фоновом режиме. Он вызывает `listen` и устанавливает размер очереди ожидания (`backlog`) равным `4`. Здесь не требуется большое значение, так как _daytime_ — это не протокол, который часто запрашивают клиенты, и, кроме того, он может мгновенно обрабатывать каждый запрос.
+
+Наконец, демон запускает бесконечный цикл, который выполняет следующие шаги:
+
+[.procedure]
+. Вызовите `accept`. Он ожидает здесь, пока клиент не свяжется с ним. В этот момент он получает новый сокет, `c`, который можно использовать для обмена данными с этим конкретным клиентом.
+. Он использует функцию C `fdopen` для преобразования сокета из низкоуровневого _дескриптора файла_ в указатель типа `FILE` в стиле C. Это позволит в дальнейшем использовать `fprintf`.
+. Он проверяет время и выводит его в формате _ISO 8601_ в «файл» `client`. Затем он использует `fclose` для закрытия файла. Это также автоматически закроет сокет.
+
+Мы можем _обобщить_ это и использовать в качестве модели для многих других серверов:
+
+.Последовательный Сервер
+image::serv.png[]
+
+Эта блок-схема подходит для _последовательных серверов_, то есть серверов, которые могут обслуживать одного клиента за раз, как это было возможно с нашим _daytime_ сервером. Это возможно только в тех случаях, когда между клиентом и сервером не происходит реального "диалога": как только сервер обнаруживает подключение клиента, он отправляет некоторые данные и закрывает соединение. Вся операция может занять наносекунды, и она завершена.
+
+Преимущество этой блок-схемы в том, что, за исключением короткого момента после того, как родительский процесс выполняет ``fork`` и до его завершения, всегда активен только один _процесс_: Наш сервер не занимает много памяти и других системных ресурсов.
+
+Обратите внимание, что мы добавили _инициализацию демона_ в нашу блок-схему. Нам не нужно было инициализировать собственный демон, но это подходящее место в потоке выполнения программы для настройки обработчиков `signal`, открытия необходимых файлов и т. д.
+
+Почти все элементы блок-схемы могут быть использованы буквально на множестве различных серверов. Элемент _serve_ является исключением. Мы рассматриваем его как _"чёрный ящик"_, то есть нечто, что вы проектируете специально для своего сервера и просто "подключаете к остальной системе."
+
+Не все протоколы настолько просты. Многие получают запрос от клиента, отвечают на него, а затем получают ещё один запрос от того же клиента. В результате, они не знают заранее, как долго будут обслуживать клиента. Такие серверы обычно запускают новый процесс для каждого клиента. Пока новый процесс обслуживает своего клиента, демон может продолжать прослушивать новые подключения.
+
+Теперь сохраните приведённый исходный код в файл [.filename]#daytimed.c# (обычно имена демонов оканчиваются буквой `d`). После компиляции попробуйте запустить его:
+
+[source, bash]
+....
+% ./daytimed
+bind: Permission denied
+%
+....
+
+Что произошло? Как вы помните, протокол _daytime_ использует порт 13. Однако все порты ниже 1024 зарезервированы для суперпользователя (в противном случае любой мог бы запустить демон, притворяясь, что обслуживает часто используемый порт, создавая угрозу безопасности).
+
+Попробуйте снова, на этот раз как суперпользователь:
+
+[source, bash]
+....
+# ./daytimed
+#
+....
+
+Что... Ничего? Давайте попробуем еще раз:
+
+[source, bash]
+....
+# ./daytimed
+
+bind: Address already in use
+#
+....
+
+Каждый порт может быть связан только одной программой одновременно. Наша первая попытка действительно была успешной: она запустила дочерний демон и завершилась без ошибок. Он продолжает работать и будет работать до тех пор, пока вы его не завершите командой kill, пока какой-либо из его системных вызовов не завершится с ошибкой или пока вы не перезагрузите систему.
+
+Хорошо, мы знаем, что он работает в фоновом режиме. Но работает ли он? Как мы можем убедиться, что это настоящий сервер _daytime_? Просто:
+
+[source, bash]
+....
+% telnet localhost 13
+
+Trying ::1...
+telnet: connect to address ::1: Connection refused
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+2001-06-19T21:04:42Z
+Connection closed by foreign host.
+%
+....
+
+telnet попробовал использовать новый IPv6, но не смог. Затем он повторил попытку с IPv4, и это удалось. Демон работает.
+
+Если у вас есть доступ к другой UNIX(R)-системе через telnet, вы можете использовать её для проверки удалённого доступа к серверу. Мой компьютер не имеет статического IP-адреса, поэтому я сделал следующее:
+
+[source, bash]
+....
+% who
+
+whizkid ttyp0 Jun 19 16:59 (216.127.220.143)
+xxx ttyp1 Jun 19 16:06 (xx.xx.xx.xx)
+% telnet 216.127.220.143 13
+
+Trying 216.127.220.143...
+Connected to r47.bfm.org.
+Escape character is '^]'.
+2001-06-19T21:31:11Z
+Connection closed by foreign host.
+%
+....
+
+Снова, это сработало. Сработает ли это с использованием доменного имени?
+
+[source, bash]
+....
+% telnet r47.bfm.org 13
+
+Trying 216.127.220.143...
+Connected to r47.bfm.org.
+Escape character is '^]'.
+2001-06-19T21:31:40Z
+Connection closed by foreign host.
+%
+....
+
+Кстати, telnet выводит сообщение _Connection closed by foreign host_ после того, как наш демон закрыл сокет. Это показывает, что использование `fclose(client);` в нашем коде действительно работает, как заявлено.
+
+[[sockets-helper-functions]]
+== Вспомогательные функции
+
+Библиотека C в FreeBSD содержит множество вспомогательных функций для программирования сокетов. Например, в нашем примере клиента мы жестко прописали IP-адрес `time.nist.gov`. Но мы не всегда знаем IP-адрес. Даже если знаем, наше программное обеспечение будет более гибким, если позволит пользователю ввести IP-адрес или даже доменное имя.
+
+[[sockets-gethostbyname]]
+=== `gethostbyname`
+
+Хотя нет возможности передать имя домена напрямую в какие-либо функции сокетов, стандартная библиотека C в FreeBSD предоставляет функции man:gethostbyname[3] и man:gethostbyname2[3], объявленные в [.filename]#netdb.h#.
+
+[.programlisting]
+....
+struct hostent * gethostbyname(const char *name);
+struct hostent * gethostbyname2(const char *name, int af);
+....
+
+Оба возвращают указатель на структуру `hostent`, содержащую много информации о домене. Для наших целей поле `h_addr_list[0]` структуры указывает на `h_length` байтов правильного адреса, уже сохранённого в _порядке байтов сети_.
+
+Это позволяет нам создать гораздо более гибкую — и гораздо более полезную — версию нашей программы daytime:
+
+[.programlisting]
+....
+/*
+ * daytime.c
+ *
+ * Programmed by G. Adam Stanislav
+ * 19 June 2001
+ */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+int main(int argc, char *argv[]) {
+ int s, bytes;
+ struct sockaddr_in sa;
+ struct hostent *he;
+ char buf[BUFSIZ+1];
+ char *host;
+
+ if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ memset(&sa, '\0', sizeof(sa));
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(13);
+
+ host = (argc > 1) ? argv[1] : "time.nist.gov";
+
+ if ((he = gethostbyname(host)) == NULL) {
+ herror(host);
+ return 2;
+ }
+
+ memcpy(&sa.sin_addr, he->h_addr_list[0], he->h_length);
+
+ if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {
+ perror("connect");
+ return 3;
+ }
+
+ while ((bytes = read(s, buf, BUFSIZ)) > 0)
+ write(1, buf, bytes);
+
+ close(s);
+ return 0;
+}
+....
+
+Теперь мы можем ввести доменное имя (или IP-адрес, это работает в обоих направлениях) в командной строке, и программа попытается подключиться к его серверу _daytime_. В противном случае, по умолчанию будет использоваться `time.nist.gov`. Однако даже в этом случае мы будем использовать `gethostbyname` вместо жесткого задания `192.43.244.18`. Таким образом, даже если его IP-адрес изменится в будущем, мы всё равно сможем его найти.
+
+Поскольку получение времени от локального сервера занимает практически нулевое время, вы можете запустить daytime дважды подряд: сначала для получения времени от `time.nist.gov`, а затем от вашей собственной системы. После этого вы можете сравнить результаты и увидеть, насколько точны часы вашей системы:
+
+[source, bash]
+....
+% daytime ; daytime localhost
+
+52080 01-06-20 04:02:33 50 0 0 390.2 UTC(NIST) *
+2001-06-20T04:02:35Z
+%
+....
+
+Как видно, моя система опережала время NIST на две секунды.
+
+[[sockets-getservbyname]]
+=== `getservbyname`
+
+Иногда вы можете быть не уверены, какой порт использует определённая служба. В таких случаях очень полезна функция man:getservbyname[3], также объявленная в [.filename]#netdb.h#:
+
+[.programlisting]
+....
+struct servent * getservbyname(const char *name, const char *proto);
+....
+
+Структура `servent` содержит `s_port`, в котором находится соответствующий порт, уже в _порядке байтов сети_.
+
+Если бы мы не знали правильный порт для службы _daytime_, мы могли бы найти его следующим образом:
+
+[.programlisting]
+....
+struct servent *se;
+ ...
+ if ((se = getservbyname("daytime", "tcp")) == NULL {
+ fprintf(stderr, "Cannot determine which port to use.\n");
+ return 7;
+ }
+ sa.sin_port = se->s_port;
+....
+
+Обычно порт известен. Но если вы разрабатываете новый протокол, вы можете тестировать его на неофициальном порту. Когда-нибудь вы зарегистрируете протокол и его порт (если не где-то ещё, то хотя бы в вашем [.filename]#/etc/services#, где `getservbyname` ищет). Вместо возврата ошибки в приведённом выше коде вы просто используете временный номер порта. Как только вы добавите протокол в [.filename]#/etc/services#, ваше программное обеспечение найдёт его порт без необходимости переписывать код.
+
+[[sockets-concurrent-servers]]
+== Многозадачные серверы
+
+В отличие от последовательного сервера, _многозадачный сервер_ должен иметь возможность обслуживать более одного клиента одновременно. Например, _сервер чата_ может обслуживать конкретного клиента часами — он не может ждать, пока закончит обслуживать текущего клиента, прежде чем перейти к следующему.
+
+Это требует значительных изменений в нашей блок-схеме:
+
+.Многозадачный сервер
+image::serv2.png[]
+
+Мы переместили _службу_ из _демона_ в её собственный _серверный процесс_. Однако, поскольку каждый дочерний процесс наследует все открытые файлы (а сокет обрабатывается так же, как файл), новый процесс наследует не только _"принятый дескриптор"_, т.е. сокет, возвращённый вызовом `accept`, но и _главный сокет_, т.е. тот, который был открыт главным процессом в самом начале.
+
+Однако _серверному процессу_ этот сокет не нужен, и он должен немедленно вызвать ему `close`. Аналогично, _демону_ больше не нужен _сокет, принятый вызовом accept_, и он не только должен, но и _обязан_ вызвать ему `close` — в противном случае рано или поздно закончатся доступные _файловые дескрипторы_.
+
+После завершения обслуживания _серверного процесса_ он должен закрыть _принятый сокет_. Вместо возврата к `accept`, процесс теперь завершается.
+
+В UNIX(R) процесс на самом деле не _завершается_. Вместо этого он _возвращается_ к своему родителю. Обычно родительский процесс ``ждёт`` (wait) завершения своего дочернего процесса и получает возвращаемое значение. Однако наш _демон-процесс_ не может просто остановиться и ждать. Это бы свело на нет всю цель создания дополнительных процессов. Но если он никогда не выполняет `wait`, его дочерние процессы станут _зомби_ — более не функционирующими, но всё ещё бродящими вокруг.
+
+По этой причине _демону_ необходимо установить _обработчики сигналов_ на этапе _инициализации демона_. Как минимум, должен обрабатываться сигнал SIGCHLD, чтобы демон мог удалять зомби-процессы из системы и освобождать занимаемые ими системные ресурсы.
+
+Вот почему наша блок-схема теперь содержит блок _обработки сигналов_, который не соединен с другими блоками. Кстати, многие серверы также обрабатывают SIGHUP и обычно интерпретируют его как сигнал от суперпользователя, указывающий на необходимость перечитать конфигурационные файлы. Это позволяет нам изменять настройки без необходимости завершать и перезапускать эти серверы.
diff --git a/documentation/content/ru/books/developers-handbook/sockets/_index.po b/documentation/content/ru/books/developers-handbook/sockets/_index.po
new file mode 100644
index 0000000000..7136478a70
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/sockets/_index.po
@@ -0,0 +1,3021 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-09-05 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbooksockets_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Yaml Front Matter Hash Value: description
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1
+#, no-wrap
+msgid "FreeBSD Sockets"
+msgstr "Сокеты FreeBSD"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1
+#, no-wrap
+msgid "Chapter 7. Sockets"
+msgstr "Глава 7. Сокеты"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:16
+#, no-wrap
+msgid "Sockets"
+msgstr "Сокеты"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:54
+#, no-wrap
+msgid "Synopsis"
+msgstr "Обзор"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:59
+msgid ""
+"BSD sockets take interprocess communications to a new level. It is no "
+"longer necessary for the communicating processes to run on the same "
+"machine. They still _can_, but they do not have to."
+msgstr ""
+"Сокеты BSD выводят межпроцессное взаимодействие на новый уровень. Теперь "
+"взаимодействующие процессы не обязательно должны выполняться на одной "
+"машине. Они всё ещё _могут_, но не обязаны."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:62
+msgid ""
+"Not only do these processes not have to run on the same machine, they do not "
+"have to run under the same operating system. Thanks to BSD sockets, your "
+"FreeBSD software can smoothly cooperate with a program running on a "
+"Macintosh(R), another one running on a Sun(TM) workstation, yet another one "
+"running under Windows(R) 2000, all connected with an Ethernet-based local "
+"area network."
+msgstr ""
+"Не только эти процессы не обязаны выполняться на одной машине, они также "
+"могут работать под разными операционными системами. Благодаря BSD-сокетам, "
+"ваше ПО на FreeBSD может легко взаимодействовать с программой, работающей на "
+"Macintosh(R), другой — на рабочей станции Sun(TM), и ещё одной — под "
+"Windows(R) 2000, при этом все они подключены к локальной сети на основе "
+"Ethernet."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:64
+msgid ""
+"But your software can equally well cooperate with processes running in "
+"another building, or on another continent, inside a submarine, or a space "
+"shuttle."
+msgstr ""
+"Но ваше программное обеспечение может так же эффективно взаимодействовать с "
+"процессами, работающими в другом здании, на другом континенте, внутри "
+"подводной лодки или космического челнока."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:67
+msgid ""
+"It can also cooperate with processes that are not part of a computer (at "
+"least not in the strict sense of the word), but of such devices as printers, "
+"digital cameras, medical equipment. Just about anything capable of digital "
+"communications."
+msgstr ""
+"Он также может взаимодействовать с процессами, которые не являются частью "
+"компьютера (по крайней мере, не в строгом смысле этого слова), а таких "
+"устройств, как принтеры, цифровые камеры, медицинское оборудование. "
+"Практически со всем, что способно к цифровой коммуникации."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:69
+#, no-wrap
+msgid "Networking and Diversity"
+msgstr "Сетевое взаимодействие и разнообразие"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:75
+msgid ""
+"We have already hinted on the _diversity_ of networking. Many different "
+"systems have to talk to each other. And they have to speak the same "
+"language. They also have to _understand_ the same language the same way."
+msgstr ""
+"Мы уже упоминали о _разнообразии_ сетевых технологий. Множество различных "
+"систем должны взаимодействовать друг с другом. И они должны говорить на "
+"одном языке. Также они должны _понимать_ этот язык одинаковым образом."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:80
+msgid ""
+"People often think that _body language_ is universal. But it is not. Back "
+"in my early teens, my father took me to Bulgaria. We were sitting at a "
+"table in a park in Sofia, when a vendor approached us trying to sell us some "
+"roasted almonds."
+msgstr ""
+"Часто думают, что _язык тела_ универсален. Но это не так. В ранней юности "
+"отец взял меня с собой в Болгарию. Мы сидели за столиком в парке Софии, "
+"когда к нам подошел продавец, предлагая купить жареный миндаль."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:83
+msgid ""
+"I had not learned much Bulgarian by then, so, instead of saying no, I shook "
+"my head from side to side, the \"universal\" body language for _no_. The "
+"vendor quickly started serving us some almonds."
+msgstr ""
+"Я тогда еще не знал болгарского, поэтому вместо словесного отказа я покачал "
+"головой из стороны в сторону — это «универсальный» язык тела для обозначения "
+"_нет_. Продавец тут же начал угощать нас миндалем."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:91
+msgid ""
+"I then remembered I had been told that in Bulgaria shaking your head "
+"sideways meant _yes_. Quickly, I started nodding my head up and down. The "
+"vendor noticed, took his almonds, and walked away. To an uninformed "
+"observer, I did not change the body language: I continued using the language "
+"of shaking and nodding my head. What changed was the _meaning_ of the body "
+"language. At first, the vendor and I interpreted the same language as "
+"having completely different meaning. I had to adjust my own interpretation "
+"of that language so the vendor would understand."
+msgstr ""
+"Затем я вспомнил, что мне говорили, будто в Болгарии покачивание головой из "
+"стороны в сторону означает _да_. Быстро я начал кивать головой вверх-вниз. "
+"Продавец заметил, взял свои миндалины и ушёл. Для непосвящённого наблюдателя "
+"я не изменил язык тела: я продолжал использовать движения головой — "
+"покачивание и кивание. Изменился _смысл_ языка тела. Сначала продавец и я "
+"интерпретировали одни и те же жесты как имеющие совершенно разный смысл. Мне "
+"пришлось скорректировать свою собственную интерпретацию этих жестов, чтобы "
+"продавец меня понял."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:95
+msgid ""
+"It is the same with computers: The same symbols may have different, even "
+"outright opposite meaning. Therefore, for two computers to understand each "
+"other, they must not only agree on the same _language_, but on the same "
+"_interpretation_ of the language."
+msgstr ""
+"То же самое и с компьютерами: одни и те же символы могут иметь разное, даже "
+"полностью противоположное значение. Поэтому, чтобы два компьютера понимали "
+"друг друга, они должны договориться не только об одном _языке_, но и об "
+"одном _толковании_ языка."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:97
+#, no-wrap
+msgid "Protocols"
+msgstr "Протоколы"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:104
+msgid ""
+"While various programming languages tend to have complex syntax and use a "
+"number of multi-letter reserved words (which makes them easy for the human "
+"programmer to understand), the languages of data communications tend to be "
+"very terse. Instead of multi-byte words, they often use individual _bits_. "
+"There is a very convincing reason for it: While data travels _inside_ your "
+"computer at speeds approaching the speed of light, it often travels "
+"considerably slower between two computers."
+msgstr ""
+"В то время как различные языки программирования обычно имеют сложный "
+"синтаксис и используют множество многосимвольных зарезервированных слов (что "
+"облегчает их понимание для человека-программиста), языки передачи данных, "
+"как правило, очень лаконичны. Вместо многосимвольных слов они часто "
+"используют отдельные _биты_. Для этого есть очень убедительная причина: хотя "
+"данные внутри вашего компьютера передаются со скоростью, близкой к скорости "
+"света, между двумя компьютерами они часто передаются значительно медленнее."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:106
+msgid ""
+"As the languages used in data communications are so terse, we usually refer "
+"to them as _protocols_ rather than languages."
+msgstr ""
+"Поскольку языки, используемые в передаче данных, настолько лаконичны, мы "
+"обычно называем их _протоколами_, а не языками."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:111
+msgid ""
+"As data travels from one computer to another, it always uses more than one "
+"protocol. These protocols are _layered_. The data can be compared to the "
+"inside of an onion: You have to peel off several layers of \"skin\" to get "
+"to the data. This is best illustrated with a picture:"
+msgstr ""
+"При передаче данных от одного компьютера к другому всегда используется более "
+"одного протокола. Эти протоколы _располагаются слоями_. Данные можно "
+"сравнить с внутренней частью лука: необходимо снять несколько слоёв «кожи», "
+"чтобы добраться до данных. Это лучше всего проиллюстрировано на рисунке:"
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:112
+#, no-wrap
+msgid "Protocol Layers"
+msgstr "Уровни протоколов"
+
+#. type: Target for macro image
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:113
+#, no-wrap
+msgid "layers.png"
+msgstr "layers.png"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:116
+msgid ""
+"In this example, we are trying to get an image from a web page we are "
+"connected to via an Ethernet."
+msgstr ""
+"В этом примере мы пытаемся получить изображение с веб-страницы, к которой "
+"подключены через Ethernet."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:119
+msgid ""
+"The image consists of raw data, which is simply a sequence of RGB values "
+"that our software can process, i.e., convert into an image and display on "
+"our monitor."
+msgstr ""
+"Изображение состоит из необработанных данных, которые представляют собой "
+"просто последовательность значений RGB, которые наше программное обеспечение "
+"может обработать, т.е. преобразовать в изображение и отобразить на нашем "
+"мониторе."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:124
+msgid ""
+"Alas, our software has no way of knowing how the raw data is organized: Is "
+"it a sequence of RGB values, or a sequence of grayscale intensities, or "
+"perhaps of CMYK encoded colors? Is the data represented by 8-bit quanta, or "
+"are they 16 bits in size, or perhaps 4 bits? How many rows and columns does "
+"the image consist of? Should certain pixels be transparent?"
+msgstr ""
+"Увы, наше программное обеспечение не может определить, как организованы "
+"сырые данные: это последовательность значений RGB, последовательность "
+"интенсивностей в градациях серого или, возможно, цвета в кодировке CMYK? "
+"Представлены ли данные 8-битными квантами, или они имеют размер 16 бит, а "
+"может быть, 4 бита? Из скольких строк и столбцов состоит изображение? Должны "
+"ли определённые пиксели быть прозрачными?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:126
+msgid "I think you get the picture..."
+msgstr "Я думаю, вы поняли..."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:129
+msgid ""
+"To inform our software how to handle the raw data, it is encoded as a PNG "
+"file. It could be a GIF, or a JPEG, but it is a PNG."
+msgstr ""
+"Чтобы наше программное обеспечение понимало, как обрабатывать сырые данные, "
+"они кодируются в формате PNG. Это мог бы быть GIF или JPEG, но выбран PNG."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:131
+msgid "And PNG is a protocol."
+msgstr "И PNG — это протокол."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:133
+msgid ""
+"At this point, I can hear some of you yelling, _\"No, it is not! It is a "
+"file format!\"_"
+msgstr ""
+"В этот момент я слышу, как некоторые из вас кричат: _\"Нет, это не так! Это "
+"формат файла!\"_"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:138
+msgid ""
+"Well, of course it is a file format. But from the perspective of data "
+"communications, a file format is a protocol: The file structure is a "
+"_language_, a terse one at that, communicating to our _process_ how the data "
+"is organized. Ergo, it is a _protocol_."
+msgstr ""
+"Ну что ж, конечно, это формат файла. Но с точки зрения передачи данных, "
+"формат файла — это протокол: структура файла — это _язык_, причем "
+"лаконичный, сообщающий нашему _процессу_, как организованы данные. "
+"Следовательно, это _протокол_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:141
+msgid ""
+"Alas, if all we received was the PNG file, our software would be facing a "
+"serious problem: How is it supposed to know the data is representing an "
+"image, as opposed to some text, or perhaps a sound, or what not? Secondly, "
+"how is it supposed to know the image is in the PNG format as opposed to GIF, "
+"or JPEG, or some other image format?"
+msgstr ""
+"Увы, если бы мы получили только PNG-файл, наше программное обеспечение "
+"столкнулось бы с серьёзной проблемой: как ему узнать, что данные "
+"представляют изображение, а не текст, звук или что-то ещё? Во-вторых, как "
+"ему определить, что изображение сохранено в формате PNG, а не GIF, JPEG или "
+"каком-либо другом формате изображений?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:145
+msgid ""
+"To obtain that information, we are using another protocol: HTTP. This "
+"protocol can tell us exactly that the data represents an image, and that it "
+"uses the PNG protocol. It can also tell us some other things, but let us "
+"stay focused on protocol layers here."
+msgstr ""
+"Для получения этой информации мы используем другой протокол: HTTP. Этот "
+"протокол может точно сообщить нам, что данные представляют изображение и "
+"используют протокол PNG. Он также может сообщить некоторые другие сведения, "
+"но давайте сосредоточимся на уровнях протоколов здесь."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:148
+msgid ""
+"So, now we have some data wrapped in the PNG protocol, wrapped in the HTTP "
+"protocol. How did we get it from the server?"
+msgstr ""
+"Итак, теперь у нас есть некоторые данные, упакованные в протокол PNG, "
+"который в свою очередь упакован в протокол HTTP. Как мы получили их с "
+"сервера?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:152
+msgid ""
+"By using TCP/IP over Ethernet, that is how. Indeed, that is three more "
+"protocols. Instead of continuing inside out, I am now going to talk about "
+"Ethernet, simply because it is easier to explain the rest that way."
+msgstr ""
+"Используя TCP/IP поверх Ethernet, вот как. Действительно, это ещё три "
+"протокола. Вместо того чтобы продолжать изнутри наружу, я теперь расскажу "
+"про Ethernet, просто потому что так проще объяснить остальное."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:156
+msgid ""
+"Ethernet is an interesting system of connecting computers in a _local area "
+"network_ (LAN). Each computer has a _network interface card_ (NIC), which "
+"has a unique 48-bit ID called its _address_. No two Ethernet NICs in the "
+"world have the same address."
+msgstr ""
+"Ethernet — это интересная система соединения компьютеров в _локальной сети_ "
+"(LAN). У каждого компьютера есть _карта сетевого интерфейса_ (NIC — Network "
+"Interface Card) с уникальным 48-битным идентификатором, называемым "
+"_адресом_. Не существует двух сетевых интерфейсов Ethernet в мире с "
+"одинаковым адресом."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:162
+msgid ""
+"These NICs are all connected with each other. Whenever one computer wants "
+"to communicate with another in the same Ethernet LAN, it sends a message "
+"over the network. Every NIC sees the message. But as part of the Ethernet "
+"_protocol_, the data contains the address of the destination NIC (among "
+"other things). So, only one of all the network interface cards will pay "
+"attention to it, the rest will ignore it."
+msgstr ""
+"Эти сетевые карты соединены между собой. Когда один компьютер хочет "
+"связаться с другим в той же локальной сети Ethernet, он отправляет сообщение "
+"по сети. Каждая сетевая карта видит это сообщение. Однако, согласно "
+"_протоколу_ Ethernet, данные содержат адрес сетевой карты назначения (среди "
+"прочего). Таким образом, только одна из всех сетевых карт обратит на него "
+"внимание, остальные проигнорируют его."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:166
+msgid ""
+"But not all computers are connected to the same network. Just because we "
+"have received the data over our Ethernet does not mean it originated in our "
+"own local area network. It could have come to us from some other network "
+"(which may not even be Ethernet based) connected with our own network via "
+"the Internet."
+msgstr ""
+"Но не все компьютеры подключены к одной сети. Тот факт, что мы получили "
+"данные через наш Ethernet, не означает, что они возникли в нашей локальной "
+"сети. Они могли попасть к нам из другой сети (которая может быть даже не на "
+"основе Ethernet), соединённой с нашей сетью через Интернет."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:170
+msgid ""
+"All data is transferred over the Internet using IP, which stands for "
+"_Internet Protocol_. Its basic role is to let us know where in the world "
+"the data has arrived from, and where it is supposed to go to. It does not "
+"_guarantee_ we will receive the data, only that we will know where it came "
+"from _if_ we do receive it."
+msgstr ""
+"Все данные передаются через Интернет с использованием IP, что означает "
+"_Internet Protocol_. Его основная роль — сообщать нам, откуда в мире пришли "
+"данные и куда они должны быть направлены. Он не _гарантирует_, что мы "
+"получим данные, только что мы узнаем, откуда они пришли, _если_ мы их "
+"получим."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:173
+msgid ""
+"Even if we do receive the data, IP does not guarantee we will receive "
+"various chunks of data in the same order the other computer has sent it to "
+"us. So, we can receive the center of our image before we receive the upper "
+"left corner and after the lower right, for example."
+msgstr ""
+"Даже если мы получим данные, IP не гарантирует, что различные фрагменты "
+"данных придут в том же порядке, в котором их отправил другой компьютер. "
+"Например, мы можем получить центр нашего изображения до того, как получим "
+"его верхний левый угол, а после — нижний правый."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:175
+msgid ""
+"It is TCP (_Transmission Control Protocol_) that asks the sender to resend "
+"any lost data and that places it all into the proper order."
+msgstr ""
+"Это TCP (_Transmission Control Protocol_), который запрашивает у отправителя "
+"повторную отправку потерянных данных и располагает их в правильном порядке."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:178
+msgid ""
+"All in all, it took _five_ different protocols for one computer to "
+"communicate to another what an image looks like. We received the data "
+"wrapped into the PNG protocol, which was wrapped into the HTTP protocol, "
+"which was wrapped into the TCP protocol, which was wrapped into the IP "
+"protocol, which was wrapped into the Ethernet protocol."
+msgstr ""
+"В итоге потребовалось _пять_ различных протоколов, чтобы один компьютер мог "
+"сообщить другому, как выглядит изображение. Мы получили данные, упакованные "
+"в протокол PNG, который был упакован в протокол HTTP, который был упакован в "
+"протокол TCP, который был упакован в протокол IP, который был упакован в "
+"протокол Ethernet."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:182
+msgid ""
+"Oh, and by the way, there probably were several other protocols involved "
+"somewhere on the way. For example, if our LAN was connected to the Internet "
+"through a dial-up call, it used the PPP protocol over the modem which used "
+"one (or several) of the various modem protocols, et cetera, et cetera, et "
+"cetera..."
+msgstr ""
+"О, и кстати, вероятно, на пути были задействованы и несколько других "
+"протоколов. Например, если наша локальная сеть была подключена к Интернету "
+"через дозвон, то использовался протокол PPP над модемом, который, в свою "
+"очередь, использовал один (или несколько) из различных модемных протоколов, "
+"и так далее, и так далее, и так далее..."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:184
+msgid ""
+"As a developer you should be asking by now, _\"How am I supposed to handle "
+"it all?\"_"
+msgstr ""
+"Как разработчик, вы уже должны задаваться вопросом: _\"Как я должен со всем "
+"этим справляться?\"_"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:189
+msgid ""
+"Luckily for you, you are _not_ supposed to handle it all. You _are_ "
+"supposed to handle some of it, but not all of it. Specifically, you need "
+"not worry about the physical connection (in our case Ethernet and possibly "
+"PPP, etc). Nor do you need to handle the Internet Protocol, or the "
+"Transmission Control Protocol."
+msgstr ""
+"К счастью для вас, вам _не_ нужно разбираться во всём этом. Вам _придётся_ "
+"разобраться в некоторой части, но не во всей. В частности, вам не нужно "
+"беспокоиться о физическом подключении (в нашем случае Ethernet и, возможно, "
+"PPP и т.д.). Также вам не нужно разбираться с протоколом IP или протоколом "
+"TCP."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:192
+msgid ""
+"In other words, you do not have to do anything to receive the data from the "
+"other computer. Well, you do have to _ask_ for it, but that is almost as "
+"simple as opening a file."
+msgstr ""
+"Другими словами, вам не нужно ничего делать, чтобы получить данные с другого "
+"компьютера. Ну, разве что _попросить_ их, но это почти так же просто, как "
+"открыть файл."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:195
+msgid ""
+"Once you have received the data, it is up to you to figure out what to do "
+"with it. In our case, you would need to understand the HTTP protocol and "
+"the PNG file structure."
+msgstr ""
+"Получив данные, вам предстоит решить, что с ними делать. В нашем случае "
+"потребуется понимание протокола HTTP и структуры файла PNG."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:199
+msgid ""
+"To use an analogy, all the internetworking protocols become a gray area: Not "
+"so much because we do not understand how it works, but because we are no "
+"longer concerned about it. The sockets interface takes care of this gray "
+"area for us:"
+msgstr ""
+"Используя аналогию, все межсетевые протоколы становятся серой зоной: не "
+"столько потому, что мы не понимаем, как они работают, а потому, что нас это "
+"больше не беспокоит. Интерфейс сокетов берёт на себя заботу об этой серой "
+"зоне:"
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:200
+#, no-wrap
+msgid "Sockets Covered Protocol Layers"
+msgstr "Уровни протоколов, покрываемые сокетами"
+
+#. type: Target for macro image
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:201
+#, no-wrap
+msgid "slayers.png"
+msgstr "slayers.png"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:204
+msgid ""
+"We only need to understand any protocols that tell us how to _interpret the "
+"data_, not how to _receive_ it from another process, nor how to _send_ it to "
+"another process."
+msgstr ""
+"Нам нужно понимать только те протоколы, которые говорят нам, как "
+"_интерпретировать данные_, а не как _получать_ их от другого процесса или "
+"как _передавать_ их другому процессу."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:206
+#, no-wrap
+msgid "The Sockets Model"
+msgstr "Модель сокетов"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:211
+msgid ""
+"BSD sockets are built on the basic UNIX(R) model: _Everything is a file._ In "
+"our example, then, sockets would let us receive an _HTTP file_, so to "
+"speak. It would then be up to us to extract the _PNG file_ from it."
+msgstr ""
+"Сокеты BSD построены по базовой модели UNIX(R): _Все является файлом._ Таким "
+"образом, в нашем примере сокеты позволят нам получить, образно говоря, _HTTP-"
+"файл_. Затем нам предстоит извлечь из него _PNG-файл_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:214
+msgid ""
+"Due to the complexity of internetworking, we cannot just use the `open` "
+"system call, or the `open()` C function. Instead, we need to take several "
+"steps to \"opening\" a socket."
+msgstr ""
+"Из-за сложности межсетевого взаимодействия мы не можем просто использовать "
+"системный вызов `open` или функцию `open()` в языке C. Вместо этого "
+"необходимо выполнить несколько шагов для \"открытия\" сокета."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:217
+msgid ""
+"Once we do, however, we can start treating the _socket_ the same way we "
+"treat any _file descriptor_: We can `read` from it, `write` to it, `pipe` "
+"it, and, eventually, `close` it."
+msgstr ""
+"Однако, как только мы это сделаем, мы можем начать обращаться с _сокетом_ "
+"так же, как и с любым _файловым дескриптором_: мы можем `читать` из него, "
+"`писать` в него, передавать его через `канал` и, в конечном итоге, "
+"`закрывать` его."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:219
+#, no-wrap
+msgid "Essential Socket Functions"
+msgstr "Основные функции сокетов"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:223
+msgid ""
+"While FreeBSD offers different functions to work with sockets, we only "
+"_need_ four to \"open\" a socket. And in some cases we only need two."
+msgstr ""
+"В то время как FreeBSD предлагает различные функции для работы с сокетами, "
+"нам _требуется_ только четыре, чтобы \"открыть\" сокет. А в некоторых "
+"случаях достаточно двух."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:225
+#, no-wrap
+msgid "The Client-Server Difference"
+msgstr "Разница между клиентом и сервером"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:228
+msgid ""
+"Typically, one of the ends of a socket-based data communication is a "
+"_server_, the other is a _client_."
+msgstr ""
+"Обычно одним из концов связи на основе сокетов является _сервер_, а другой — "
+"_клиент_."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:230
+#, no-wrap
+msgid "The Common Elements"
+msgstr "Общие элементы"
+
+#. type: Title =====
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:233
+#, no-wrap
+msgid "`socket`"
+msgstr "`socket`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:237
+msgid ""
+"The one function used by both, clients and servers, is man:socket[2]. It is "
+"declared this way:"
+msgstr ""
+"Функция, используемая как клиентами, так и серверами, это man:socket[2]. Она "
+"объявляется следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:241
+#, no-wrap
+msgid "int socket(int domain, int type, int protocol);\n"
+msgstr "int socket(int domain, int type, int protocol);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:246
+msgid ""
+"The return value is of the same type as that of `open`, an integer. FreeBSD "
+"allocates its value from the same pool as that of file handles. That is "
+"what allows sockets to be treated the same way as files."
+msgstr ""
+"Возвращаемое значение имеет тот же тип, что и у `open`, целое число. FreeBSD "
+"выделяет его значение из того же пула, что и дескрипторы файлов. Это "
+"позволяет обрабатывать сокеты так же, как файлы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:250
+msgid ""
+"The `domain` argument tells the system what _protocol family_ you want it to "
+"use. Many of them exist, some are vendor specific, others are very common. "
+"They are declared in [.filename]#sys/socket.h#."
+msgstr ""
+"Аргумент `domain` указывает системе, какое _семейство протоколов_ следует "
+"использовать. Существует множество семейств, некоторые из них специфичны для "
+"определённых поставщиков, другие широко распространены. Они объявлены в "
+"[.filename]#sys/socket.h#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:252
+msgid "Use `PF_INET` for UDP, TCP and other Internet protocols (IPv4)."
+msgstr ""
+"Используйте `PF_INET` для UDP, TCP и других интернет-протоколов (IPv4)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:256
+msgid ""
+"Five values are defined for the `type` argument, again, in [.filename]#sys/"
+"socket.h#. All of them start with \"`SOCK_`\". The most common one is "
+"`SOCK_STREAM`, which tells the system you are asking for a _reliable stream "
+"delivery service_ (which is TCP when used with `PF_INET`)."
+msgstr ""
+"Для аргумента `type` определено пять значений, также указанных в "
+"[.filename]#sys/socket.h#. Все они начинаются с \"`SOCK_`\". Наиболее "
+"распространённое — `SOCK_STREAM`, которое указывает системе, что "
+"запрашивается _надёжный сервис потоковой доставки_ (это TCP при "
+"использовании с `PF_INET`)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:258
+msgid ""
+"If you asked for `SOCK_DGRAM`, you would be requesting a _connectionless "
+"datagram delivery service_ (in our case, UDP)."
+msgstr ""
+"Если бы вы запросили `SOCK_DGRAM`, вы бы запросили _сервис доставки "
+"датаграмм без установления соединения_ (в нашем случае, UDP)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:260
+msgid ""
+"If you wanted to be in charge of the low-level protocols (such as IP), or "
+"even network interfaces (e.g., the Ethernet), you would need to specify "
+"`SOCK_RAW`."
+msgstr ""
+"Если вы хотите управлять низкоуровневыми протоколами (такими как IP) или "
+"даже сетевыми интерфейсами (например, Ethernet), вам потребуется указать "
+"`SOCK_RAW`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:263
+msgid ""
+"Finally, the `protocol` argument depends on the previous two arguments, and "
+"is not always meaningful. In that case, use `0` for its value."
+msgstr ""
+"Наконец, аргумент `protocol` зависит от двух предыдущих аргументов и не "
+"всегда имеет смысл. В таком случае используйте значение `0`."
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:265
+#, no-wrap
+msgid "The Unconnected Socket"
+msgstr "Неподключенный сокет"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:269
+msgid ""
+"Nowhere, in the `socket` function have we specified to what other system we "
+"should be connected. Our newly created socket remains _unconnected_."
+msgstr ""
+"Нигде в функции `socket` мы не указали, к какой другой системе должны быть "
+"подключены. Наш только что созданный сокет остаётся _неподключённым_."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:272
+msgid ""
+"This is on purpose: To use a telephone analogy, we have just attached a "
+"modem to the phone line. We have neither told the modem to make a call, nor "
+"to answer if the phone rings."
+msgstr ""
+"Это сделано намеренно: если проводить аналогию с телефоном, мы только что "
+"подключили модем к телефонной линии. Мы не сказали модему совершить звонок "
+"или ответить, если телефон зазвонит."
+
+#. type: Title =====
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:275
+#, no-wrap
+msgid "`sockaddr`"
+msgstr "`sockaddr`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:280
+msgid ""
+"Various functions of the sockets family expect the address of (or pointer "
+"to, to use C terminology) a small area of the memory. The various C "
+"declarations in the [.filename]#sys/socket.h# refer to it as `struct "
+"sockaddr`. This structure is declared in the same file:"
+msgstr ""
+"Различные функции семейства сокетов ожидают адрес (или указатель, если "
+"использовать терминологию языка C) небольшой области памяти. Различные "
+"объявления на языке C в файле [.filename]#sys/socket.h# ссылаются на неё как "
+"на `struct sockaddr`. Эта структура объявлена в том же файле:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:293
+#, no-wrap
+msgid ""
+"/*\n"
+" * Structure used by kernel to store most\n"
+" * addresses.\n"
+" */\n"
+"struct sockaddr {\n"
+"\tunsigned char\tsa_len;\t\t/* total length */\n"
+"\tsa_family_t\tsa_family;\t/* address family */\n"
+"\tchar\t\tsa_data[14];\t/* actually longer; address value */\n"
+"};\n"
+"#define\tSOCK_MAXADDRLEN\t255\t\t/* longest possible addresses */\n"
+msgstr ""
+"/*\n"
+" * Structure used by kernel to store most\n"
+" * addresses.\n"
+" */\n"
+"struct sockaddr {\n"
+"\tunsigned char\tsa_len;\t\t/* total length */\n"
+"\tsa_family_t\tsa_family;\t/* address family */\n"
+"\tchar\t\tsa_data[14];\t/* actually longer; address value */\n"
+"};\n"
+"#define\tSOCK_MAXADDRLEN\t255\t\t/* longest possible addresses */\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:297
+msgid ""
+"Please note the _vagueness_ with which the `sa_data` field is declared, just "
+"as an array of `14` bytes, with the comment hinting there can be more than "
+"`14` of them."
+msgstr ""
+"Обратите внимание на _неопределённость_, с которой объявлено поле `sa_data` "
+"— просто как массив из `14` байт, с комментарием, намекающим, что их может "
+"быть больше `14`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:300
+msgid ""
+"This vagueness is quite deliberate. Sockets is a very powerful interface. "
+"While most people perhaps think of it as nothing more than the Internet "
+"interface-and most applications probably use it for that nowadays-sockets "
+"can be used for just about _any_ kind of interprocess communications, of "
+"which the Internet (or, more precisely, IP) is only one."
+msgstr ""
+"Эта неопределенность вполне преднамеренна. Сокеты — это очень мощный "
+"интерфейс. Хотя большинство людей, возможно, считают их не более чем "
+"интерфейсом для Интернета — и большинство приложений, вероятно, используют "
+"их именно для этого в наши дни — сокеты могут быть использованы практически "
+"для _любого_ вида межпроцессного взаимодействия, из которых Интернет (или, "
+"точнее, IP) — лишь один из них."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:303
+msgid ""
+"The [.filename]#sys/socket.h# refers to the various types of protocols "
+"sockets will handle as _address families_, and lists them right before the "
+"definition of `sockaddr`:"
+msgstr ""
+"[.filename]#sys/socket.h# ссылается на различные типы протоколов, с которыми "
+"работают сокеты, как на _семейства адресов_, и перечисляет их "
+"непосредственно перед определением `sockaddr`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:352
+#, no-wrap
+msgid ""
+"/*\n"
+" * Address families.\n"
+" */\n"
+"#define\tAF_UNSPEC\t0\t\t/* unspecified */\n"
+"#define\tAF_LOCAL\t1\t\t/* local to host (pipes, portals) */\n"
+"#define\tAF_UNIX\t\tAF_LOCAL\t/* backward compatibility */\n"
+"#define\tAF_INET\t\t2\t\t/* internetwork: UDP, TCP, etc. */\n"
+"#define\tAF_IMPLINK\t3\t\t/* arpanet imp addresses */\n"
+"#define\tAF_PUP\t\t4\t\t/* pup protocols: e.g. BSP */\n"
+"#define\tAF_CHAOS\t5\t\t/* mit CHAOS protocols */\n"
+"#define\tAF_NS\t\t6\t\t/* XEROX NS protocols */\n"
+"#define\tAF_ISO\t\t7\t\t/* ISO protocols */\n"
+"#define\tAF_OSI\t\tAF_ISO\n"
+"#define\tAF_ECMA\t\t8\t\t/* European computer manufacturers */\n"
+"#define\tAF_DATAKIT\t9\t\t/* datakit protocols */\n"
+"#define\tAF_CCITT\t10\t\t/* CCITT protocols, X.25 etc */\n"
+"#define\tAF_SNA\t\t11\t\t/* IBM SNA */\n"
+"#define AF_DECnet\t12\t\t/* DECnet */\n"
+"#define AF_DLI\t\t13\t\t/* DEC Direct data link interface */\n"
+"#define AF_LAT\t\t14\t\t/* LAT */\n"
+"#define\tAF_HYLINK\t15\t\t/* NSC Hyperchannel */\n"
+"#define\tAF_APPLETALK\t16\t\t/* Apple Talk */\n"
+"#define\tAF_ROUTE\t17\t\t/* Internal Routing Protocol */\n"
+"#define\tAF_LINK\t\t18\t\t/* Link layer interface */\n"
+"#define\tpseudo_AF_XTP\t19\t\t/* eXpress Transfer Protocol (no AF) */\n"
+"#define\tAF_COIP\t\t20\t\t/* connection-oriented IP, aka ST II */\n"
+"#define\tAF_CNT\t\t21\t\t/* Computer Network Technology */\n"
+"#define pseudo_AF_RTIP\t22\t\t/* Help Identify RTIP packets */\n"
+"#define\tAF_IPX\t\t23\t\t/* Novell Internet Protocol */\n"
+"#define\tAF_SIP\t\t24\t\t/* Simple Internet Protocol */\n"
+"#define\tpseudo_AF_PIP\t25\t\t/* Help Identify PIP packets */\n"
+"#define\tAF_ISDN\t\t26\t\t/* Integrated Services Digital Network*/\n"
+"#define\tAF_E164\t\tAF_ISDN\t\t/* CCITT E.164 recommendation */\n"
+"#define\tpseudo_AF_KEY\t27\t\t/* Internal key-management function */\n"
+"#define\tAF_INET6\t28\t\t/* IPv6 */\n"
+"#define\tAF_NATM\t\t29\t\t/* native ATM access */\n"
+"#define\tAF_ATM\t\t30\t\t/* ATM */\n"
+"#define pseudo_AF_HDRCMPLT 31\t\t/* Used by BPF to not rewrite headers\n"
+"\t\t\t\t\t * in interface output routine\n"
+"\t\t\t\t\t */\n"
+"#define\tAF_NETGRAPH\t32\t\t/* Netgraph sockets */\n"
+"#define\tAF_SLOW\t\t33\t\t/* 802.3ad slow protocol */\n"
+"#define\tAF_SCLUSTER\t34\t\t/* Sitara cluster protocol */\n"
+"#define\tAF_ARP\t\t35\n"
+"#define\tAF_BLUETOOTH\t36\t\t/* Bluetooth sockets */\n"
+"#define\tAF_MAX\t\t37\n"
+msgstr ""
+"/*\n"
+" * Address families.\n"
+" */\n"
+"#define\tAF_UNSPEC\t0\t\t/* unspecified */\n"
+"#define\tAF_LOCAL\t1\t\t/* local to host (pipes, portals) */\n"
+"#define\tAF_UNIX\t\tAF_LOCAL\t/* backward compatibility */\n"
+"#define\tAF_INET\t\t2\t\t/* internetwork: UDP, TCP, etc. */\n"
+"#define\tAF_IMPLINK\t3\t\t/* arpanet imp addresses */\n"
+"#define\tAF_PUP\t\t4\t\t/* pup protocols: e.g. BSP */\n"
+"#define\tAF_CHAOS\t5\t\t/* mit CHAOS protocols */\n"
+"#define\tAF_NS\t\t6\t\t/* XEROX NS protocols */\n"
+"#define\tAF_ISO\t\t7\t\t/* ISO protocols */\n"
+"#define\tAF_OSI\t\tAF_ISO\n"
+"#define\tAF_ECMA\t\t8\t\t/* European computer manufacturers */\n"
+"#define\tAF_DATAKIT\t9\t\t/* datakit protocols */\n"
+"#define\tAF_CCITT\t10\t\t/* CCITT protocols, X.25 etc */\n"
+"#define\tAF_SNA\t\t11\t\t/* IBM SNA */\n"
+"#define AF_DECnet\t12\t\t/* DECnet */\n"
+"#define AF_DLI\t\t13\t\t/* DEC Direct data link interface */\n"
+"#define AF_LAT\t\t14\t\t/* LAT */\n"
+"#define\tAF_HYLINK\t15\t\t/* NSC Hyperchannel */\n"
+"#define\tAF_APPLETALK\t16\t\t/* Apple Talk */\n"
+"#define\tAF_ROUTE\t17\t\t/* Internal Routing Protocol */\n"
+"#define\tAF_LINK\t\t18\t\t/* Link layer interface */\n"
+"#define\tpseudo_AF_XTP\t19\t\t/* eXpress Transfer Protocol (no AF) */\n"
+"#define\tAF_COIP\t\t20\t\t/* connection-oriented IP, aka ST II */\n"
+"#define\tAF_CNT\t\t21\t\t/* Computer Network Technology */\n"
+"#define pseudo_AF_RTIP\t22\t\t/* Help Identify RTIP packets */\n"
+"#define\tAF_IPX\t\t23\t\t/* Novell Internet Protocol */\n"
+"#define\tAF_SIP\t\t24\t\t/* Simple Internet Protocol */\n"
+"#define\tpseudo_AF_PIP\t25\t\t/* Help Identify PIP packets */\n"
+"#define\tAF_ISDN\t\t26\t\t/* Integrated Services Digital Network*/\n"
+"#define\tAF_E164\t\tAF_ISDN\t\t/* CCITT E.164 recommendation */\n"
+"#define\tpseudo_AF_KEY\t27\t\t/* Internal key-management function */\n"
+"#define\tAF_INET6\t28\t\t/* IPv6 */\n"
+"#define\tAF_NATM\t\t29\t\t/* native ATM access */\n"
+"#define\tAF_ATM\t\t30\t\t/* ATM */\n"
+"#define pseudo_AF_HDRCMPLT 31\t\t/* Used by BPF to not rewrite headers\n"
+"\t\t\t\t\t * in interface output routine\n"
+"\t\t\t\t\t */\n"
+"#define\tAF_NETGRAPH\t32\t\t/* Netgraph sockets */\n"
+"#define\tAF_SLOW\t\t33\t\t/* 802.3ad slow protocol */\n"
+"#define\tAF_SCLUSTER\t34\t\t/* Sitara cluster protocol */\n"
+"#define\tAF_ARP\t\t35\n"
+"#define\tAF_BLUETOOTH\t36\t\t/* Bluetooth sockets */\n"
+"#define\tAF_MAX\t\t37\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:356
+msgid "The one used for IP is AF_INET. It is a symbol for the constant `2`."
+msgstr "Используемый для IP — это AF_INET. Это символ для константы `2`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:358
+msgid ""
+"It is the _address family_ listed in the `sa_family` field of `sockaddr` "
+"that decides how exactly the vaguely named bytes of `sa_data` will be used."
+msgstr ""
+"Это _семейство адресов_, указанное в поле `sa_family` структуры `sockaddr`, "
+"определяет, как именно будут использоваться нечетко названные байты "
+"`sa_data`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:360
+msgid ""
+"Specifically, whenever the _address family_ is AF_INET, we can use `struct "
+"sockaddr_in` found in [.filename]#netinet/in.h#, wherever `sockaddr` is "
+"expected:"
+msgstr ""
+"В частности, когда _семейство адресов_ — AF_INET, можно использовать `struct "
+"sockaddr_in` из [.filename]#netinet/in.h# везде, где ожидается `sockaddr`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:373
+#, no-wrap
+msgid ""
+"/*\n"
+" * Socket address, internet style.\n"
+" */\n"
+"struct sockaddr_in {\n"
+"\tuint8_t\t\tsin_len;\n"
+"\tsa_family_t\tsin_family;\n"
+"\tin_port_t\tsin_port;\n"
+"\tstruct\tin_addr sin_addr;\n"
+"\tchar\tsin_zero[8];\n"
+"};\n"
+msgstr ""
+"/*\n"
+" * Socket address, internet style.\n"
+" */\n"
+"struct sockaddr_in {\n"
+"\tuint8_t\t\tsin_len;\n"
+"\tsa_family_t\tsin_family;\n"
+"\tin_port_t\tsin_port;\n"
+"\tstruct\tin_addr sin_addr;\n"
+"\tchar\tsin_zero[8];\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:376
+msgid "We can visualize its organization this way:"
+msgstr "Мы можем визуализировать его организацию следующим образом:"
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:377
+#, no-wrap
+msgid "sockaddr_in structure"
+msgstr "Структура `sockaddr_in`"
+
+#. type: Target for macro image
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:378
+#, no-wrap
+msgid "sain.png"
+msgstr "sain.png"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:381
+msgid ""
+"The three important fields are `sin_family`, which is byte 1 of the "
+"structure, `sin_port`, a 16-bit value found in bytes 2 and 3, and "
+"`sin_addr`, a 32-bit integer representation of the IP address, stored in "
+"bytes 4-7."
+msgstr ""
+"Три важных поля — это `sin_family`, которое находится в байте 1 структуры, "
+"`sin_port`, 16-битное значение, расположенное в байтах 2 и 3, и `sin_addr`, "
+"32-битное целочисленное представление IP-адреса, хранящееся в байтах 4–7."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:387
+msgid ""
+"Now, let us try to fill it out. Let us assume we are trying to write a "
+"client for the _daytime_ protocol, which simply states that its server will "
+"write a text string representing the current date and time to port 13. We "
+"want to use TCP/IP, so we need to specify `AF_INET` in the address family "
+"field. `AF_INET` is defined as `2`. Let us use the IP address of "
+"`192.43.244.18`, which is the time server of US federal government "
+"(`time.nist.gov`)."
+msgstr ""
+"Теперь попробуем заполнить его. Предположим, мы пытаемся написать клиент для "
+"протокола _daytime_, который просто указывает, что его сервер записывает "
+"текстовую строку с текущей датой и временем в порт 13. Мы хотим использовать "
+"TCP/IP, поэтому нам нужно указать `AF_INET` в поле семейства адресов. "
+"`AF_INET` определен как `2`. Давайте используем IP-адрес `192.43.244.18`, "
+"который является сервером времени федерального правительства США "
+"(`time.nist.gov`)."
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:388
+#, no-wrap
+msgid "Specific example of sockaddr_in"
+msgstr "Конкретный пример sockaddr_in"
+
+#. type: Target for macro image
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:389
+#, no-wrap
+msgid "sainfill.png"
+msgstr "sainfill.png"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:392
+msgid ""
+"By the way the `sin_addr` field is declared as being of the `struct in_addr` "
+"type, which is defined in [.filename]#netinet/in.h#:"
+msgstr ""
+"Кстати, поле `sin_addr` объявлено как имеющее тип `struct in_addr`, который "
+"определён в [.filename]#netinet/in.h#:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:401
+#, no-wrap
+msgid ""
+"/*\n"
+" * Internet address (a structure for historical reasons)\n"
+" */\n"
+"struct in_addr {\n"
+"\tin_addr_t s_addr;\n"
+"};\n"
+msgstr ""
+"/*\n"
+" * Internet address (a structure for historical reasons)\n"
+" */\n"
+"struct in_addr {\n"
+"\tin_addr_t s_addr;\n"
+"};\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:404
+msgid "In addition, `in_addr_t` is a 32-bit integer."
+msgstr "В дополнение, `in_addr_t` является 32-битным целым числом."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:406
+msgid ""
+"The `192.43.244.18` is just a convenient notation of expressing a 32-bit "
+"integer by listing all of its 8-bit bytes, starting with the _most "
+"significant_ one."
+msgstr ""
+"`192.43.244.18` — это просто удобная форма записи 32-битного целого числа, в "
+"которой перечисляются все его 8-битные байты, начиная с _старшего_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:410
+msgid ""
+"So far, we have viewed `sockaddr` as an abstraction. Our computer does not "
+"store `short` integers as a single 16-bit entity, but as a sequence of 2 "
+"bytes. Similarly, it stores 32-bit integers as a sequence of 4 bytes."
+msgstr ""
+"До сих пор мы рассматривали `sockaddr` как абстракцию. Наш компьютер не "
+"хранит `short` целые числа как единую 16-битную сущность, а как "
+"последовательность 2 байт. Аналогично, он хранит 32-битные целые числа как "
+"последовательность 4 байт."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:412
+msgid "Suppose we coded something like this:"
+msgstr "Предположим, мы написали что-то вроде этого:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:418
+#, no-wrap
+msgid ""
+"sa.sin_family = AF_INET;\n"
+"sa.sin_port = 13;\n"
+"sa.sin_addr.s_addr = (((((192 << 8) | 43) << 8) | 244) << 8) | 18;\n"
+msgstr ""
+"sa.sin_family = AF_INET;\n"
+"sa.sin_port = 13;\n"
+"sa.sin_addr.s_addr = (((((192 << 8) | 43) << 8) | 244) << 8) | 18;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:421
+msgid "What would the result look like?"
+msgstr "Как будет выглядеть результат?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:424
+msgid ""
+"Well, that depends, of course. On a Pentium(R), or other x86, based "
+"computer, it would look like this:"
+msgstr ""
+"Ну, это, конечно, зависит от многого. На компьютере с процессором Pentium(R) "
+"или другим на базе x86 это будет выглядеть так:"
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:425
+#, no-wrap
+msgid "sockaddr_in on an Intel system"
+msgstr "`sockaddr_in` в системе с архитектурой Intel"
+
+#. type: Target for macro image
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:426
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:472
+#, no-wrap
+msgid "sainlsb.png"
+msgstr "sainlsb.png"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:429
+msgid "On a different system, it might look like this:"
+msgstr "На другой системе это может выглядеть так:"
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:430
+#, no-wrap
+msgid "sockaddr_in on an MSB system"
+msgstr "`sockaddr_in` в системе с порядком байтов от старшего к младшему"
+
+#. type: Target for macro image
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:431
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:477
+#, no-wrap
+msgid "sainmsb.png"
+msgstr "sainmsb.png"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:435
+msgid ""
+"And on a PDP it might look different yet. But the above two are the most "
+"common ways in use today."
+msgstr ""
+"И на PDP это может выглядеть иначе. Однако два приведённых выше варианта "
+"являются наиболее распространёнными на сегодняшний день."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:439
+msgid ""
+"Ordinarily, wanting to write portable code, programmers pretend that these "
+"differences do not exist. And they get away with it (except when they code "
+"in assembly language). Alas, you cannot get away with it that easily when "
+"coding for sockets."
+msgstr ""
+"Обычно, стремясь писать переносимый код, программисты делают вид, что этих "
+"различий не существует. И им это сходит с рук (за исключением случаев, когда "
+"они пишут на ассемблере). Увы, при программировании сокетов так легко "
+"отделаться не получится."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:441
+msgid "Why?"
+msgstr "Почему?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:443
+msgid ""
+"Because when communicating with another computer, you usually do not know "
+"whether it stores data _most significant byte_ (MSB) or _least significant "
+"byte_ (LSB) first."
+msgstr ""
+"Потому что при обмене данными с другим компьютером вы обычно не знаете, "
+"хранит ли он данные, начиная со _старшего байта_ (MSB) или с _младшего "
+"байта_ (LSB)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:445
+msgid "You might be wondering, _\"So, will sockets not handle it for me?\"_"
+msgstr ""
+"Вы можете задаться вопросом: _\"Значит, сокеты не будут это делать за меня?"
+"\"_"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:447
+msgid "It will not."
+msgstr "Не будут."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:450
+msgid ""
+"While that answer may surprise you at first, remember that the general "
+"sockets interface only understands the `sa_len` and `sa_family` fields of "
+"the `sockaddr` structure. You do not have to worry about the byte order "
+"there (of course, on FreeBSD `sa_family` is only 1 byte anyway, but many "
+"other UNIX(R) systems do not have `sa_len` and use 2 bytes for `sa_family`, "
+"and expect the data in whatever order is native to the computer)."
+msgstr ""
+"Хотя этот ответ может сначала вас удивить, помните, что общий интерфейс "
+"сокетов понимает только поля `sa_len` и `sa_family` структуры `sockaddr`. "
+"Вам не нужно беспокоиться о порядке байтов (конечно, в FreeBSD `sa_family` "
+"занимает всего 1 байт, но многие другие UNIX(R)-системы не имеют `sa_len` и "
+"используют 2 байта для `sa_family`, ожидая данные в том порядке, который "
+"является родным для компьютера)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:453
+msgid ""
+"But the rest of the data is just `sa_data[14]` as far as sockets goes. "
+"Depending on the _address family_, sockets just forwards that data to its "
+"destination."
+msgstr ""
+"Но остальные данные — это просто `sa_data[14]` с точки зрения сокетов. В "
+"зависимости от _семейства адресов_ сокеты просто передают эти данные по "
+"назначению."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:458
+msgid ""
+"Indeed, when we enter a port number, it is because we want the other "
+"computer to know what service we are asking for. And, when we are the "
+"server, we read the port number so we know what service the other computer "
+"is expecting from us. Either way, sockets only has to forward the port "
+"number as data. It does not interpret it in any way."
+msgstr ""
+"Действительно, когда мы указываем номер порта, это делается для того, чтобы "
+"другая компьютерная система знала, какую службу мы запрашиваем. И, когда мы "
+"выступаем в роли сервера, мы считываем номер порта, чтобы понять, какую "
+"службу ожидает от нас другая система. В любом случае, сокетам нужно лишь "
+"передать номер порта в качестве данных. Они никак его не интерпретируют."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:461
+msgid ""
+"Similarly, we enter the IP address to tell everyone on the way where to send "
+"our data to. Sockets, again, only forwards it as data."
+msgstr ""
+"Аналогично, мы указываем IP-адрес, чтобы сообщить всем на пути, куда "
+"отправлять наши данные. Сокеты, опять же, просто пересылают их как данные."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:463
+msgid ""
+"That is why, we (the _programmers_, not the _sockets_) have to distinguish "
+"between the byte order used by our computer and a conventional byte order to "
+"send the data in to the other computer."
+msgstr ""
+"Вот почему мы (программисты, а не сокеты) должны различать порядок байтов, "
+"используемый нашим компьютером, и условный порядок байтов для отправки "
+"данных на другой компьютер."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:465
+msgid ""
+"We will call the byte order our computer uses the _host byte order_, or just "
+"the _host order_."
+msgstr ""
+"Мы будем называть порядок байтов, который использует наш компьютер, "
+"_порядком байтов хоста_ или просто _хост-порядком_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:468
+msgid ""
+"There is a convention of sending the multi-byte data over IP _MSB first_. "
+"This, we will refer to as the _network byte order_, or simply the _network "
+"order_."
+msgstr ""
+"Существует соглашение о передаче многобайтовых данных по IP _старшим байтом "
+"вперёд_. Это мы будем называть _порядком байтов сети_ или просто _сетевым "
+"порядком_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:470
+msgid ""
+"Now, if we compiled the above code for an Intel based computer, our _host "
+"byte order_ would produce:"
+msgstr ""
+"Вот, если бы мы скомпилировали приведённый выше код для компьютера на базе "
+"Intel, наш _порядок байтов хоста_ выдал бы:"
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:471
+#, no-wrap
+msgid "Host byte order on an Intel system"
+msgstr "Порядок байтов на хосте в системе Intel"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:475
+msgid "But the _network byte order_ requires that we store the data MSB first:"
+msgstr ""
+"Но порядок байтов в _сетевом формате_ требует, чтобы данные хранились "
+"начиная со старшего байта (MSB):"
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:476
+#, no-wrap
+msgid "Network byte order"
+msgstr "Порядок байтов в сети"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:480
+msgid ""
+"Unfortunately, our _host order_ is the exact opposite of the _network order_."
+msgstr ""
+"К сожалению, наш _порядок хоста_ полностью противоположен _порядку сети_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:483
+msgid ""
+"We have several ways of dealing with it. One would be to _reverse_ the "
+"values in our code:"
+msgstr ""
+"У нас есть несколько способов решения этой проблемы. Один из них — "
+"_инвертировать_ значения в нашем коде:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:489
+#, no-wrap
+msgid ""
+"sa.sin_family = AF_INET;\n"
+"sa.sin_port = 13 << 8;\n"
+"sa.sin_addr.s_addr = (((((18 << 8) | 244) << 8) | 43) << 8) | 192;\n"
+msgstr ""
+"sa.sin_family = AF_INET;\n"
+"sa.sin_port = 13 << 8;\n"
+"sa.sin_addr.s_addr = (((((18 << 8) | 244) << 8) | 43) << 8) | 192;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:494
+msgid ""
+"This will _trick_ our compiler into storing the data in the _network byte "
+"order_. In some cases, this is exactly the way to do it (e.g., when "
+"programming in assembly language). In most cases, however, it can cause a "
+"problem."
+msgstr ""
+"Это _обманет_ наш компилятор, заставив его сохранить данные в _порядке "
+"байтов сети_. В некоторых случаях это именно тот способ, который нужен "
+"(например, при программировании на ассемблере). Однако в большинстве случаев "
+"это может вызвать проблему."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:498
+msgid ""
+"Suppose, you wrote a sockets-based program in C. You know it is going to "
+"run on a Pentium(R), so you enter all your constants in reverse and force "
+"them to the _network byte order_. It works well."
+msgstr ""
+"Предположим, вы написали программу на C, использующую сокеты. Вы знаете, что "
+"она будет работать на Pentium(R), поэтому вводите все константы в обратном "
+"порядке и приводите их к _порядку байтов сети_. Она работает хорошо."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:503
+msgid ""
+"Then, some day, your trusted old Pentium(R) becomes a rusty old Pentium(R). "
+"You replace it with a system whose _host order_ is the same as the _network "
+"order_. You need to recompile all your software. All of your software "
+"continues to perform well, except the one program you wrote."
+msgstr ""
+"Затем, однажды, ваш надежный старый Pentium(R) превращается в ржавый старый "
+"Pentium(R). Вы заменяете его системой, у которой _порядок байтов хоста_ "
+"совпадает с _сетевым порядком байтов_. Вам нужно перекомпилировать все ваше "
+"программное обеспечение. Все ваши программы продолжают работать хорошо, "
+"кроме той одной программы, которую вы написали."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:507
+msgid ""
+"You have since forgotten that you had forced all of your constants to the "
+"opposite of the _host order_. You spend some quality time tearing out your "
+"hair, calling the names of all gods you ever heard of (and some you made "
+"up), hitting your monitor with a nerf bat, and performing all the other "
+"traditional ceremonies of trying to figure out why something that has worked "
+"so well is suddenly not working at all."
+msgstr ""
+"Вы уже забыли, что принудительно задали все свои константы противоположными "
+"_порядку хоста_. Вы проводите некоторое время, яростно рвя на себе волосы, "
+"взывая ко всем известным вам богам (и к некоторым, которых вы придумали), "
+"стуча нерф-битой по монитору и выполняя прочие традиционные ритуалы в "
+"попытке понять, почему то, что работало так хорошо, внезапно перестало "
+"работать вообще."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:509
+msgid ""
+"Eventually, you figure it out, say a couple of swear words, and start "
+"rewriting your code."
+msgstr ""
+"В конце концов, вы разбираетесь в проблеме, произносите пару крепких "
+"словечек и начинаете переписывать свой код."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:512
+msgid ""
+"Luckily, you are not the first one to face the problem. Someone else has "
+"created the man:htons[3] and man:htonl[3] C functions to convert a `short` "
+"and `long` respectively from the _host byte order_ to the _network byte "
+"order_, and the man:ntohs[3] and man:ntohl[3] C functions to go the other "
+"way."
+msgstr ""
+"К счастью, вы не первый, кто столкнулся с этой проблемой. Кто-то уже создал "
+"функции man:htons[3] и man:htonl[3] на языке C для преобразования `short` и "
+"`long` соответственно из _порядка байтов хоста_ в _порядок байтов сети_, а "
+"также функции man:ntohs[3] и man:ntohl[3] на языке C для обратного "
+"преобразования."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:515
+msgid ""
+"On _MSB-first_ systems these functions do nothing. On _LSB-first_ systems "
+"they convert values to the proper order."
+msgstr ""
+"На системах с порядком _старший байт первый_ эти функции не выполняют "
+"никаких действий. На системах с порядком _младший байт первый_ они "
+"преобразуют значения в правильный порядок."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:517
+msgid ""
+"So, regardless of what system your software is compiled on, your data will "
+"end up in the correct order if you use these functions."
+msgstr ""
+"Итак, независимо от того, на какой системе компилируется ваше программное "
+"обеспечение, ваши данные будут в правильном порядке, если вы используете эти "
+"функции."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:519
+#, no-wrap
+msgid "Client Functions"
+msgstr "Функции клиента"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:524
+msgid ""
+"Typically, the client initiates the connection to the server. The client "
+"knows which server it is about to call: It knows its IP address, and it "
+"knows the _port_ the server resides at. It is akin to you picking up the "
+"phone and dialing the number (the _address_), then, after someone answers, "
+"asking for the person in charge of wingdings (the _port_)."
+msgstr ""
+"Обычно клиент инициирует подключение к серверу. Клиент знает, к какому "
+"серверу он собирается обратиться: он знает его IP-адрес и _порт_, на котором "
+"работает сервер. Это похоже на то, как вы поднимаете трубку и набираете "
+"номер (_адрес_), а затем, когда кто-то отвечает, просите соединить со "
+"специалистом по непонятным символам (_порт_)."
+
+#. type: Title =====
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:526
+#, no-wrap
+msgid "`connect`"
+msgstr "`connect`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:529
+msgid ""
+"Once a client has created a socket, it needs to connect it to a specific "
+"port on a remote system. It uses man:connect[2]:"
+msgstr ""
+"Как только клиент создал сокет, ему нужно подключить его к определённому "
+"порту на удалённой системе. Для этого используется man:connect[2]:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:533
+#, no-wrap
+msgid "int connect(int s, const struct sockaddr *name, socklen_t namelen);\n"
+msgstr "int connect(int s, const struct sockaddr *name, socklen_t namelen);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:538
+msgid ""
+"The `s` argument is the socket, i.e., the value returned by the `socket` "
+"function. The `name` is a pointer to `sockaddr`, the structure we have "
+"talked about extensively. Finally, `namelen` informs the system how many "
+"bytes are in our `sockaddr` structure."
+msgstr ""
+"Аргумент `s` — это сокет, то есть значение, возвращаемое функцией `socket`. "
+"Аргумент `name` — это указатель на структуру `sockaddr`, которую мы подробно "
+"обсуждали. Наконец, `namelen` сообщает системе, сколько байт находится в "
+"нашей структуре `sockaddr`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:541
+msgid ""
+"If `connect` is successful, it returns `0`. Otherwise it returns `-1` and "
+"stores the error code in `errno`."
+msgstr ""
+"Если `connect` завершается успешно, он возвращает `0`. В противном случае "
+"возвращается `-1`, а код ошибки сохраняется в `errno`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:545
+msgid ""
+"There are many reasons why `connect` may fail. For example, with an attempt "
+"to an Internet connection, the IP address may not exist, or it may be down, "
+"or just too busy, or it may not have a server listening at the specified "
+"port. Or it may outright _refuse_ any request for specific code."
+msgstr ""
+"Существует множество причин, по которым `connect` может завершиться "
+"неудачей. Например, при попытке подключения к интернету, IP-адрес может не "
+"существовать, быть недоступен, перегружен или на указанном порту может не "
+"быть сервера. Или же подключение может быть явно _отклонено_ по определённым "
+"причинам."
+
+#. type: Title =====
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:547
+#, no-wrap
+msgid "Our First Client"
+msgstr "Наш первый клиент"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:550
+msgid ""
+"We now know enough to write a very simple client, one that will get current "
+"time from `192.43.244.18` and print it to [.filename]#stdout#."
+msgstr ""
+"Теперь мы знаем достаточно, чтобы написать очень простого клиента, который "
+"получит текущее время от `192.43.244.18` и выведет его в [.filename]#stdout#."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:564
+#, no-wrap
+msgid ""
+"/*\n"
+" * daytime.c\n"
+" *\n"
+" * Programmed by G. Adam Stanislav\n"
+" */\n"
+"#include <stdio.h>\n"
+"#include <string.h>\n"
+"#include <sys/types.h>\n"
+"#include <sys/socket.h>\n"
+"#include <netinet/in.h>\n"
+"#include <unistd.h>\n"
+msgstr ""
+"/*\n"
+" * daytime.c\n"
+" *\n"
+" * Programmed by G. Adam Stanislav\n"
+" */\n"
+"#include <stdio.h>\n"
+"#include <string.h>\n"
+"#include <sys/types.h>\n"
+"#include <sys/socket.h>\n"
+"#include <netinet/in.h>\n"
+"#include <unistd.h>\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:569
+#, no-wrap
+msgid ""
+"int main() {\n"
+" int s, bytes;\n"
+" struct sockaddr_in sa;\n"
+" char buffer[BUFSIZ+1];\n"
+msgstr ""
+"int main() {\n"
+" int s, bytes;\n"
+" struct sockaddr_in sa;\n"
+" char buffer[BUFSIZ+1];\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:574
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:993
+#, no-wrap
+msgid ""
+" if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {\n"
+" perror(\"socket\");\n"
+" return 1;\n"
+" }\n"
+msgstr ""
+" if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {\n"
+" perror(\"socket\");\n"
+" return 1;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:576
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:995
+#, no-wrap
+msgid " memset(&sa, '\\0', sizeof(sa));\n"
+msgstr " memset(&sa, '\\0', sizeof(sa));\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:585
+#, no-wrap
+msgid ""
+" sa.sin_family = AF_INET;\n"
+" sa.sin_port = htons(13);\n"
+" sa.sin_addr.s_addr = htonl((((((192 << 8) | 43) << 8) | 244) << 8) | 18);\n"
+" if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {\n"
+" perror(\"connect\");\n"
+" close(s);\n"
+" return 2;\n"
+" }\n"
+msgstr ""
+" sa.sin_family = AF_INET;\n"
+" sa.sin_port = htons(13);\n"
+" sa.sin_addr.s_addr = htonl((((((192 << 8) | 43) << 8) | 244) << 8) | 18);\n"
+" if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {\n"
+" perror(\"connect\");\n"
+" close(s);\n"
+" return 2;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:588
+#, no-wrap
+msgid ""
+" while ((bytes = read(s, buffer, BUFSIZ)) > 0)\n"
+" write(1, buffer, bytes);\n"
+msgstr ""
+" while ((bytes = read(s, buffer, BUFSIZ)) > 0)\n"
+" write(1, buffer, bytes);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:592
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1019
+#, no-wrap
+msgid ""
+" close(s);\n"
+" return 0;\n"
+"}\n"
+msgstr ""
+" close(s);\n"
+" return 0;\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:595
+msgid ""
+"Go ahead, enter it in your editor, save it as [.filename]#daytime.c#, then "
+"compile and run it:"
+msgstr ""
+"Вперед! Введите это в вашем редакторе, сохраните как [.filename]#daytime.c#, "
+"затем скомпилируйте и запустите:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:600
+#, no-wrap
+msgid ""
+"% cc -O3 -o daytime daytime.c\n"
+"% ./daytime\n"
+msgstr ""
+"% cc -O3 -o daytime daytime.c\n"
+"% ./daytime\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:603
+#, no-wrap
+msgid ""
+"52079 01-06-19 02:29:25 50 0 1 543.9 UTC(NIST) *\n"
+"%\n"
+msgstr ""
+"52079 01-06-19 02:29:25 50 0 1 543.9 UTC(NIST) *\n"
+"%\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:607
+msgid ""
+"In this case, the date was June 19, 2001, the time was 02:29:25 UTC. "
+"Naturally, your results will vary."
+msgstr ""
+"В данном случае дата была 19 июня 2001 года, время — 02:29:25 UTC. "
+"Естественно, ваши результаты могут отличаться."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:609
+#, no-wrap
+msgid "Server Functions"
+msgstr "Функции сервера"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:615
+msgid ""
+"The typical server does not initiate the connection. Instead, it waits for "
+"a client to call it and request services. It does not know when the client "
+"will call, nor how many clients will call. It may be just sitting there, "
+"waiting patiently, one moment, The next moment, it can find itself swamped "
+"with requests from a number of clients, all calling in at the same time."
+msgstr ""
+"Типичный сервер не инициирует соединение. Вместо этого он ожидает, когда "
+"клиент обратится к нему и запросит услуги. Он не знает, когда клиент "
+"обратится, ни сколько клиентов обратится. В один момент он может просто "
+"спокойно ожидать, а в следующий момент он может оказаться перегруженным "
+"запросами от множества клиентов, обращающихся одновременно."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:617
+msgid "The sockets interface offers three basic functions to handle this."
+msgstr ""
+"Интерфейс сокетов предоставляет три основные функции для обработки этого."
+
+#. type: Title =====
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:619
+#, no-wrap
+msgid "`bind`"
+msgstr "`bind`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:622
+msgid ""
+"Ports are like extensions to a phone line: After you dial a number, you dial "
+"the extension to get to a specific person or department."
+msgstr ""
+"Порты подобны внутренним номерам телефонной линии: после набора основного "
+"номера вы набираете внутренний номер, чтобы связаться с конкретным человеком "
+"или отделом."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:626
+msgid ""
+"There are 65535 IP ports, but a server usually processes requests that come "
+"in on only one of them. It is like telling the phone room operator that we "
+"are now at work and available to answer the phone at a specific extension. "
+"We use man:bind[2] to tell sockets which port we want to serve."
+msgstr ""
+"Существует 65535 IP-портов, но сервер обычно обрабатывает запросы, "
+"поступающие только на один из них. Это как сказать оператору телефонной "
+"комнаты, что мы сейчас на месте и готовы отвечать на звонки по определённому "
+"внутреннему номеру. Мы используем man:bind[2], чтобы указать сокетам, на "
+"каком порту мы хотим обслуживать запросы."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:630
+#, no-wrap
+msgid "int bind(int s, const struct sockaddr *addr, socklen_t addrlen);\n"
+msgstr "int bind(int s, const struct sockaddr *addr, socklen_t addrlen);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:635
+msgid ""
+"Beside specifying the port in `addr`, the server may include its IP "
+"address. However, it can just use the symbolic constant INADDR_ANY to "
+"indicate it will serve all requests to the specified port regardless of what "
+"its IP address is. This symbol, along with several similar ones, is "
+"declared in [.filename]#netinet/in.h#"
+msgstr ""
+"Помимо указания порта в `addr`, сервер может включать свой IP-адрес. Однако "
+"он может просто использовать символическую константу INADDR_ANY, чтобы "
+"указать, что будет обслуживать все запросы на указанный порт, независимо от "
+"его IP-адреса. Этот символ, наряду с несколькими аналогичными, объявлен в "
+"[.filename]#netinet/in.h#"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:639
+#, no-wrap
+msgid "#define\tINADDR_ANY\t\t(u_int32_t)0x00000000\n"
+msgstr "#define\tINADDR_ANY\t\t(u_int32_t)0x00000000\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:644
+msgid ""
+"Suppose we were writing a server for the _daytime_ protocol over TCP/IP. "
+"Recall that it uses port 13. Our `sockaddr_in` structure would look like "
+"this:"
+msgstr ""
+"Предположим, мы пишем сервер для протокола _daytime_ поверх TCP/IP. "
+"Напомним, что он использует порт 13. Наша структура `sockaddr_in` будет "
+"выглядеть так:"
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:645
+#, no-wrap
+msgid "Example Server sockaddr_in"
+msgstr "Пример sockaddr_in сервера"
+
+#. type: Target for macro image
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:646
+#, no-wrap
+msgid "sainserv.png"
+msgstr "sainserv.png"
+
+#. type: Title =====
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:649
+#, no-wrap
+msgid "`listen`"
+msgstr "`listen`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:653
+msgid ""
+"To continue our office phone analogy, after you have told the phone central "
+"operator what extension you will be at, you now walk into your office, and "
+"make sure your own phone is plugged in and the ringer is turned on. Plus, "
+"you make sure your call waiting is activated, so you can hear the phone ring "
+"even while you are talking to someone."
+msgstr ""
+"Продолжая аналогию с офисным телефоном, после того как вы сообщили оператору "
+"АТС, на каком внутреннем номере вы будете находиться, вы заходите в свой "
+"офис и убеждаетесь, что ваш телефон подключен и звонок включен. Кроме того, "
+"вы активируете функцию ожидания вызова, чтобы слышать звонок даже во время "
+"разговора с кем-то."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:655
+msgid "The server ensures all of that with the man:listen[2] function."
+msgstr "Сервер обеспечивает все это с помощью функции man:listen[2]."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:659
+#, no-wrap
+msgid "int listen(int s, int backlog);\n"
+msgstr "int listen(int s, int backlog);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:663
+msgid ""
+"In here, the `backlog` variable tells sockets how many incoming requests to "
+"accept while you are busy processing the last request. In other words, it "
+"determines the maximum size of the queue of pending connections."
+msgstr ""
+"Здесь переменная `backlog` указывает сокетам, сколько входящих запросов "
+"принимать, пока вы заняты обработкой последнего запроса. Другими словами, "
+"она определяет максимальный размер очереди ожидающих соединений."
+
+#. type: Title =====
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:665
+#, no-wrap
+msgid "`accept`"
+msgstr "`accept`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:670
+msgid ""
+"After you hear the phone ringing, you accept the call by answering the "
+"call. You have now established a connection with your client. This "
+"connection remains active until either you or your client hang up."
+msgstr ""
+"После того как вы услышите телефонный звонок, вы принимаете вызов, отвечая "
+"на звонок. Теперь вы установили соединение с вашим клиентом. Это соединение "
+"остается активным, пока вы или ваш клиент не повесите трубку."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:672
+msgid "The server accepts the connection by using the man:accept[2] function."
+msgstr "Сервер принимает соединение, используя функцию man:accept[2]."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:676
+#, no-wrap
+msgid "int accept(int s, struct sockaddr *addr, socklen_t *addrlen);\n"
+msgstr "int accept(int s, struct sockaddr *addr, socklen_t *addrlen);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:680
+msgid ""
+"Note that this time `addrlen` is a pointer. This is necessary because in "
+"this case it is the socket that fills out `addr`, the `sockaddr_in` "
+"structure."
+msgstr ""
+"Обратите внимание, что в этот раз `addrlen` является указателем. Это "
+"необходимо, потому что в данном случае именно сокет заполняет структуру "
+"`addr` — `sockaddr_in`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:684
+msgid ""
+"The return value is an integer. Indeed, the `accept` returns a _new "
+"socket_. You will use this new socket to communicate with the client."
+msgstr ""
+"Возвращаемое значение является целым числом. Действительно, `accept` "
+"возвращает _новый сокет_. Этот новый сокет будет использоваться для обмена "
+"данными с клиентом."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:686
+msgid ""
+"What happens to the old socket? It continues to listen for more requests "
+"(remember the `backlog` variable we passed to `listen`?) until we `close` it."
+msgstr ""
+"Что происходит со старым сокетом? Он продолжает ожидать новые запросы "
+"(помните переменную `backlog`, которую мы передали в `listen`?), пока мы не "
+"закроем его (`close`)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:690
+msgid ""
+"Now, the new socket is meant only for communications. It is fully "
+"connected. We cannot pass it to `listen` again, trying to accept additional "
+"connections."
+msgstr ""
+"Теперь новый сокет предназначен только для обмена данными. Он полностью "
+"подключен. Мы не можем снова передать его в `listen`, чтобы принимать "
+"дополнительные соединения."
+
+#. type: Title =====
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:692
+#, no-wrap
+msgid "Our First Server"
+msgstr "Наш первый сервер"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:696
+msgid ""
+"Our first server will be somewhat more complex than our first client was: "
+"Not only do we have more sockets functions to use, but we need to write it "
+"as a daemon."
+msgstr ""
+"Наш первый сервер будет несколько сложнее, чем первый клиент: нам нужно не "
+"только использовать больше функций сокетов, но и написать его как демон."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:699
+msgid ""
+"This is best achieved by creating a _child process_ after binding the port. "
+"The main process then exits and returns control to the shell (or whatever "
+"program invoked it)."
+msgstr ""
+"Это лучше всего достигается созданием _дочернего процесса_ после привязки "
+"порта. Затем основной процесс завершается и возвращает управление оболочке "
+"(или любой другой программе, которая его вызвала)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:701
+msgid ""
+"The child calls `listen`, then starts an endless loop, which accepts a "
+"connection, serves it, and eventually closes its socket."
+msgstr ""
+"Дочерний процесс вызывает `listen`, затем запускает бесконечный цикл, "
+"который принимает соединение, обслуживает его и в конечном итоге закрывает "
+"свой сокет."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:717
+#, no-wrap
+msgid ""
+"/*\n"
+" * daytimed - a port 13 server\n"
+" *\n"
+" * Programmed by G. Adam Stanislav\n"
+" * June 19, 2001\n"
+" */\n"
+"#include <stdio.h>\n"
+"#include <string.h>\n"
+"#include <time.h>\n"
+"#include <unistd.h>\n"
+"#include <sys/types.h>\n"
+"#include <sys/socket.h>\n"
+"#include <netinet/in.h>\n"
+msgstr ""
+"/*\n"
+" * daytimed - a port 13 server\n"
+" *\n"
+" * Programmed by G. Adam Stanislav\n"
+" * June 19, 2001\n"
+" */\n"
+"#include <stdio.h>\n"
+"#include <string.h>\n"
+"#include <time.h>\n"
+"#include <unistd.h>\n"
+"#include <sys/types.h>\n"
+"#include <sys/socket.h>\n"
+"#include <netinet/in.h>\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:719
+#, no-wrap
+msgid "#define BACKLOG 4\n"
+msgstr "#define BACKLOG 4\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:727
+#, no-wrap
+msgid ""
+"int main() {\n"
+" int s, c;\n"
+" socklen_t b;\n"
+" struct sockaddr_in sa;\n"
+" time_t t;\n"
+" struct tm *tm;\n"
+" FILE *client;\n"
+msgstr ""
+"int main() {\n"
+" int s, c;\n"
+" socklen_t b;\n"
+" struct sockaddr_in sa;\n"
+" time_t t;\n"
+" struct tm *tm;\n"
+" FILE *client;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:732
+#, no-wrap
+msgid ""
+" if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {\n"
+" perror(\"socket\");\n"
+" return 1;\n"
+" }\n"
+msgstr ""
+" if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {\n"
+" perror(\"socket\");\n"
+" return 1;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:734
+#, no-wrap
+msgid " memset(&sa, '\\0', sizeof(sa));\n"
+msgstr " memset(&sa, '\\0', sizeof(sa));\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:737
+#, no-wrap
+msgid ""
+" sa.sin_family = AF_INET;\n"
+" sa.sin_port = htons(13);\n"
+msgstr ""
+" sa.sin_family = AF_INET;\n"
+" sa.sin_port = htons(13);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:740
+#, no-wrap
+msgid ""
+" if (INADDR_ANY)\n"
+" sa.sin_addr.s_addr = htonl(INADDR_ANY);\n"
+msgstr ""
+" if (INADDR_ANY)\n"
+" sa.sin_addr.s_addr = htonl(INADDR_ANY);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:745
+#, no-wrap
+msgid ""
+" if (bind(s, (struct sockaddr *)&sa, sizeof sa) < 0) {\n"
+" perror(\"bind\");\n"
+" return 2;\n"
+" }\n"
+msgstr ""
+" if (bind(s, (struct sockaddr *)&sa, sizeof sa) < 0) {\n"
+" perror(\"bind\");\n"
+" return 2;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:756
+#, no-wrap
+msgid ""
+" switch (fork()) {\n"
+" case -1:\n"
+" perror(\"fork\");\n"
+" return 3;\n"
+" default:\n"
+" close(s);\n"
+" return 0;\n"
+" case 0:\n"
+" break;\n"
+" }\n"
+msgstr ""
+" switch (fork()) {\n"
+" case -1:\n"
+" perror(\"fork\");\n"
+" return 3;\n"
+" default:\n"
+" close(s);\n"
+" return 0;\n"
+" case 0:\n"
+" break;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:758
+#, no-wrap
+msgid " listen(s, BACKLOG);\n"
+msgstr " listen(s, BACKLOG);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:761
+#, no-wrap
+msgid ""
+" for (;;) {\n"
+" b = sizeof sa;\n"
+msgstr ""
+" for (;;) {\n"
+" b = sizeof sa;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:766
+#, no-wrap
+msgid ""
+" if ((c = accept(s, (struct sockaddr *)&sa, &b)) < 0) {\n"
+" perror(\"daytimed accept\");\n"
+" return 4;\n"
+" }\n"
+msgstr ""
+" if ((c = accept(s, (struct sockaddr *)&sa, &b)) < 0) {\n"
+" perror(\"daytimed accept\");\n"
+" return 4;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:771
+#, no-wrap
+msgid ""
+" if ((client = fdopen(c, \"w\")) == NULL) {\n"
+" perror(\"daytimed fdopen\");\n"
+" return 5;\n"
+" }\n"
+msgstr ""
+" if ((client = fdopen(c, \"w\")) == NULL) {\n"
+" perror(\"daytimed fdopen\");\n"
+" return 5;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:776
+#, no-wrap
+msgid ""
+" if ((t = time(NULL)) < 0) {\n"
+" perror(\"daytimed time\");\n"
+" return 6;\n"
+" }\n"
+msgstr ""
+" if ((t = time(NULL)) < 0) {\n"
+" perror(\"daytimed time\");\n"
+" return 6;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:785
+#, no-wrap
+msgid ""
+" tm = gmtime(&t);\n"
+" fprintf(client, \"%.4i-%.2i-%.2iT%.2i:%.2i:%.2iZ\\n\",\n"
+" tm->tm_year + 1900,\n"
+" tm->tm_mon + 1,\n"
+" tm->tm_mday,\n"
+" tm->tm_hour,\n"
+" tm->tm_min,\n"
+" tm->tm_sec);\n"
+msgstr ""
+" tm = gmtime(&t);\n"
+" fprintf(client, \"%.4i-%.2i-%.2iT%.2i:%.2i:%.2iZ\\n\",\n"
+" tm->tm_year + 1900,\n"
+" tm->tm_mon + 1,\n"
+" tm->tm_mday,\n"
+" tm->tm_hour,\n"
+" tm->tm_min,\n"
+" tm->tm_sec);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:789
+#, no-wrap
+msgid ""
+" fclose(client);\n"
+" }\n"
+"}\n"
+msgstr ""
+" fclose(client);\n"
+" }\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:794
+msgid ""
+"We start by creating a socket. Then we fill out the `sockaddr_in` structure "
+"in `sa`. Note the conditional use of INADDR_ANY:"
+msgstr ""
+"Начинаем с создания сокета. Затем заполняем структуру `sockaddr_in` в `sa`. "
+"Обратите внимание на условное использование INADDR_ANY:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:799
+#, no-wrap
+msgid ""
+"if (INADDR_ANY)\n"
+" sa.sin_addr.s_addr = htonl(INADDR_ANY);\n"
+msgstr ""
+"if (INADDR_ANY)\n"
+" sa.sin_addr.s_addr = htonl(INADDR_ANY);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:806
+msgid ""
+"Its value is `0`. Since we have just used `bzero` on the entire structure, "
+"it would be redundant to set it to `0` again. But if we port our code to "
+"some other system where INADDR_ANY is perhaps not a zero, we need to assign "
+"it to `sa.sin_addr.s_addr`. Most modern C compilers are clever enough to "
+"notice that INADDR_ANY is a constant. As long as it is a zero, they will "
+"optimize the entire conditional statement out of the code."
+msgstr ""
+"Его значение равно `0`. Поскольку мы только что использовали `bzero` для "
+"всей структуры, будет избыточным снова устанавливать его в `0`. Но если мы "
+"перенесем наш код на другую систему, где INADDR_ANY, возможно, не равен "
+"нулю, нам нужно будет присвоить его `sa.sin_addr.s_addr`. Большинство "
+"современных компиляторов C достаточно умны, чтобы заметить, что INADDR_ANY — "
+"это константа. Пока она равна нулю, они оптимизируют все условное выражение "
+"из кода."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:810
+msgid ""
+"After we have called `bind` successfully, we are ready to become a _daemon_: "
+"We use `fork` to create a child process. In both, the parent and the child, "
+"the `s` variable is our socket. The parent process will not need it, so it "
+"calls `close`, then it returns `0` to inform its own parent it had "
+"terminated successfully."
+msgstr ""
+"После успешного вызова `bind` мы готовы стать _демоном_: используем `fork` "
+"для создания дочернего процесса. В обоих процессах, родительском и дочернем, "
+"переменная `s` является нашим сокетом. Родительскому процессу он больше не "
+"нужен, поэтому он вызывает `close`, затем возвращает `0`, чтобы сообщить "
+"своему родителю об успешном завершении."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:815
+msgid ""
+"Meanwhile, the child process continues working in the background. It calls "
+"`listen` and sets its backlog to `4`. It does not need a large value here "
+"because _daytime_ is not a protocol many clients request all the time, and "
+"because it can process each request instantly anyway."
+msgstr ""
+"Между тем, дочерний процесс продолжает работать в фоновом режиме. Он "
+"вызывает `listen` и устанавливает размер очереди ожидания (`backlog`) равным "
+"`4`. Здесь не требуется большое значение, так как _daytime_ — это не "
+"протокол, который часто запрашивают клиенты, и, кроме того, он может "
+"мгновенно обрабатывать каждый запрос."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:817
+msgid ""
+"Finally, the daemon starts an endless loop, which performs the following "
+"steps:"
+msgstr ""
+"Наконец, демон запускает бесконечный цикл, который выполняет следующие шаги:"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:820
+msgid ""
+"Call `accept`. It waits here until a client contacts it. At that point, it "
+"receives a new socket, `c`, which it can use to communicate with this "
+"particular client."
+msgstr ""
+"Вызовите `accept`. Он ожидает здесь, пока клиент не свяжется с ним. В этот "
+"момент он получает новый сокет, `c`, который можно использовать для обмена "
+"данными с этим конкретным клиентом."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:821
+msgid ""
+"It uses the C function `fdopen` to turn the socket from a low-level _file "
+"descriptor_ to a C-style `FILE` pointer. This will allow the use of "
+"`fprintf` later on."
+msgstr ""
+"Он использует функцию C `fdopen` для преобразования сокета из "
+"низкоуровневого _дескриптора файла_ в указатель типа `FILE` в стиле C. Это "
+"позволит в дальнейшем использовать `fprintf`."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:822
+msgid ""
+"It checks the time, and prints it in the _ISO 8601_ format to the `client` "
+"\"file\". It then uses `fclose` to close the file. That will automatically "
+"close the socket as well."
+msgstr ""
+"Он проверяет время и выводит его в формате _ISO 8601_ в «файл» `client`. "
+"Затем он использует `fclose` для закрытия файла. Это также автоматически "
+"закроет сокет."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:824
+msgid "We can _generalize_ this, and use it as a model for many other servers:"
+msgstr ""
+"Мы можем _обобщить_ это и использовать в качестве модели для многих других "
+"серверов:"
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:825
+#, no-wrap
+msgid "Sequential Server"
+msgstr "Последовательный Сервер"
+
+#. type: Target for macro image
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:826
+#, no-wrap
+msgid "serv.png"
+msgstr "serv.png"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:832
+msgid ""
+"This flowchart is good for _sequential servers_, i.e., servers that can "
+"serve one client at a time, just as we were able to with our _daytime_ "
+"server. This is only possible whenever there is no real \"conversation\" "
+"going on between the client and the server: As soon as the server detects a "
+"connection to the client, it sends out some data and closes the connection. "
+"The entire operation may take nanoseconds, and it is finished."
+msgstr ""
+"Эта блок-схема подходит для _последовательных серверов_, то есть серверов, "
+"которые могут обслуживать одного клиента за раз, как это было возможно с "
+"нашим _daytime_ сервером. Это возможно только в тех случаях, когда между "
+"клиентом и сервером не происходит реального \"диалога\": как только сервер "
+"обнаруживает подключение клиента, он отправляет некоторые данные и закрывает "
+"соединение. Вся операция может занять наносекунды, и она завершена."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:835
+msgid ""
+"The advantage of this flowchart is that, except for the brief moment after "
+"the parent ``fork``s and before it exits, there is always only one _process_ "
+"active: Our server does not take up much memory and other system resources."
+msgstr ""
+"Преимущество этой блок-схемы в том, что, за исключением короткого момента "
+"после того, как родительский процесс выполняет ``fork`` и до его завершения, "
+"всегда активен только один _процесс_: Наш сервер не занимает много памяти и "
+"других системных ресурсов."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:839
+msgid ""
+"Note that we have added _initialize daemon_ in our flowchart. We did not "
+"need to initialize our own daemon, but this is a good place in the flow of "
+"the program to set up any `signal` handlers, open any files we may need, etc."
+msgstr ""
+"Обратите внимание, что мы добавили _инициализацию демона_ в нашу блок-схему. "
+"Нам не нужно было инициализировать собственный демон, но это подходящее "
+"место в потоке выполнения программы для настройки обработчиков `signal`, "
+"открытия необходимых файлов и т. д."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:843
+msgid ""
+"Just about everything in the flow chart can be used literally on many "
+"different servers. The _serve_ entry is the exception. We think of it as a "
+"_\"black box\"_, i.e., something you design specifically for your own "
+"server, and just \"plug it into the rest.\""
+msgstr ""
+"Почти все элементы блок-схемы могут быть использованы буквально на множестве "
+"различных серверов. Элемент _serve_ является исключением. Мы рассматриваем "
+"его как _\"чёрный ящик\"_, то есть нечто, что вы проектируете специально для "
+"своего сервера и просто \"подключаете к остальной системе.\""
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:849
+msgid ""
+"Not all protocols are that simple. Many receive a request from the client, "
+"reply to it, then receive another request from the same client. As a "
+"result, they do not know in advance how long they will be serving the "
+"client. Such servers usually start a new process for each client. While "
+"the new process is serving its client, the daemon can continue listening for "
+"more connections."
+msgstr ""
+"Не все протоколы настолько просты. Многие получают запрос от клиента, "
+"отвечают на него, а затем получают ещё один запрос от того же клиента. В "
+"результате, они не знают заранее, как долго будут обслуживать клиента. Такие "
+"серверы обычно запускают новый процесс для каждого клиента. Пока новый "
+"процесс обслуживает своего клиента, демон может продолжать прослушивать "
+"новые подключения."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:852
+msgid ""
+"Now, go ahead, save the above source code as [.filename]#daytimed.c# (it is "
+"customary to end the names of daemons with the letter `d`). After you have "
+"compiled it, try running it:"
+msgstr ""
+"Теперь сохраните приведённый исходный код в файл [.filename]#daytimed.c# "
+"(обычно имена демонов оканчиваются буквой `d`). После компиляции попробуйте "
+"запустить его:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:858
+#, no-wrap
+msgid ""
+"% ./daytimed\n"
+"bind: Permission denied\n"
+"%\n"
+msgstr ""
+"% ./daytimed\n"
+"bind: Permission denied\n"
+"%\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:862
+msgid ""
+"What happened here? As you will recall, the _daytime_ protocol uses port "
+"13. But all ports below 1024 are reserved to the superuser (otherwise, "
+"anyone could start a daemon pretending to serve a commonly used port, while "
+"causing a security breach)."
+msgstr ""
+"Что произошло? Как вы помните, протокол _daytime_ использует порт 13. Однако "
+"все порты ниже 1024 зарезервированы для суперпользователя (в противном "
+"случае любой мог бы запустить демон, притворяясь, что обслуживает часто "
+"используемый порт, создавая угрозу безопасности)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:864
+msgid "Try again, this time as the superuser:"
+msgstr "Попробуйте снова, на этот раз как суперпользователь:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:869
+#, no-wrap
+msgid ""
+"# ./daytimed\n"
+"#\n"
+msgstr ""
+"# ./daytimed\n"
+"#\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:872
+msgid "What... Nothing? Let us try again:"
+msgstr "Что... Ничего? Давайте попробуем еще раз:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:876
+#, no-wrap
+msgid "# ./daytimed\n"
+msgstr "# ./daytimed\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:879
+#, no-wrap
+msgid ""
+"bind: Address already in use\n"
+"#\n"
+msgstr ""
+"bind: Address already in use\n"
+"#\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:884
+msgid ""
+"Every port can only be bound by one program at a time. Our first attempt "
+"was indeed successful: It started the child daemon and returned quietly. It "
+"is still running and will continue to run until you either kill it, or any "
+"of its system calls fail, or you reboot the system."
+msgstr ""
+"Каждый порт может быть связан только одной программой одновременно. Наша "
+"первая попытка действительно была успешной: она запустила дочерний демон и "
+"завершилась без ошибок. Он продолжает работать и будет работать до тех пор, "
+"пока вы его не завершите командой kill, пока какой-либо из его системных "
+"вызовов не завершится с ошибкой или пока вы не перезагрузите систему."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:887
+msgid ""
+"Fine, we know it is running in the background. But is it working? How do we "
+"know it is a proper _daytime_ server? Simple:"
+msgstr ""
+"Хорошо, мы знаем, что он работает в фоновом режиме. Но работает ли он? Как "
+"мы можем убедиться, что это настоящий сервер _daytime_? Просто:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:891
+#, no-wrap
+msgid "% telnet localhost 13\n"
+msgstr "% telnet localhost 13\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:900
+#, no-wrap
+msgid ""
+"Trying ::1...\n"
+"telnet: connect to address ::1: Connection refused\n"
+"Trying 127.0.0.1...\n"
+"Connected to localhost.\n"
+"Escape character is '^]'.\n"
+"2001-06-19T21:04:42Z\n"
+"Connection closed by foreign host.\n"
+"%\n"
+msgstr ""
+"Trying ::1...\n"
+"telnet: connect to address ::1: Connection refused\n"
+"Trying 127.0.0.1...\n"
+"Connected to localhost.\n"
+"Escape character is '^]'.\n"
+"2001-06-19T21:04:42Z\n"
+"Connection closed by foreign host.\n"
+"%\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:905
+msgid ""
+"telnet tried the new IPv6, and failed. It retried with IPv4 and succeeded. "
+"The daemon works."
+msgstr ""
+"telnet попробовал использовать новый IPv6, но не смог. Затем он повторил "
+"попытку с IPv4, и это удалось. Демон работает."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:908
+msgid ""
+"If you have access to another UNIX(R) system via telnet, you can use it to "
+"test accessing the server remotely. My computer does not have a static IP "
+"address, so this is what I did:"
+msgstr ""
+"Если у вас есть доступ к другой UNIX(R)-системе через telnet, вы можете "
+"использовать её для проверки удалённого доступа к серверу. Мой компьютер не "
+"имеет статического IP-адреса, поэтому я сделал следующее:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:912
+#, no-wrap
+msgid "% who\n"
+msgstr "% who\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:916
+#, no-wrap
+msgid ""
+"whizkid ttyp0 Jun 19 16:59 (216.127.220.143)\n"
+"xxx ttyp1 Jun 19 16:06 (xx.xx.xx.xx)\n"
+"% telnet 216.127.220.143 13\n"
+msgstr ""
+"whizkid ttyp0 Jun 19 16:59 (216.127.220.143)\n"
+"xxx ttyp1 Jun 19 16:06 (xx.xx.xx.xx)\n"
+"% telnet 216.127.220.143 13\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:923
+#, no-wrap
+msgid ""
+"Trying 216.127.220.143...\n"
+"Connected to r47.bfm.org.\n"
+"Escape character is '^]'.\n"
+"2001-06-19T21:31:11Z\n"
+"Connection closed by foreign host.\n"
+"%\n"
+msgstr ""
+"Trying 216.127.220.143...\n"
+"Connected to r47.bfm.org.\n"
+"Escape character is '^]'.\n"
+"2001-06-19T21:31:11Z\n"
+"Connection closed by foreign host.\n"
+"%\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:926
+msgid "Again, it worked. Will it work using the domain name?"
+msgstr ""
+"Снова, это сработало. Сработает ли это с использованием доменного имени?"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:930
+#, no-wrap
+msgid "% telnet r47.bfm.org 13\n"
+msgstr "% telnet r47.bfm.org 13\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:937
+#, no-wrap
+msgid ""
+"Trying 216.127.220.143...\n"
+"Connected to r47.bfm.org.\n"
+"Escape character is '^]'.\n"
+"2001-06-19T21:31:40Z\n"
+"Connection closed by foreign host.\n"
+"%\n"
+msgstr ""
+"Trying 216.127.220.143...\n"
+"Connected to r47.bfm.org.\n"
+"Escape character is '^]'.\n"
+"2001-06-19T21:31:40Z\n"
+"Connection closed by foreign host.\n"
+"%\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:941
+msgid ""
+"By the way, telnet prints the _Connection closed by foreign host_ message "
+"after our daemon has closed the socket. This shows us that, indeed, using "
+"`fclose(client);` in our code works as advertised."
+msgstr ""
+"Кстати, telnet выводит сообщение _Connection closed by foreign host_ после "
+"того, как наш демон закрыл сокет. Это показывает, что использование "
+"`fclose(client);` в нашем коде действительно работает, как заявлено."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:943
+#, no-wrap
+msgid "Helper Functions"
+msgstr "Вспомогательные функции"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:949
+msgid ""
+"FreeBSD C library contains many helper functions for sockets programming. "
+"For example, in our sample client we hard coded the `time.nist.gov` IP "
+"address. But we do not always know the IP address. Even if we do, our "
+"software is more flexible if it allows the user to enter the IP address, or "
+"even the domain name."
+msgstr ""
+"Библиотека C в FreeBSD содержит множество вспомогательных функций для "
+"программирования сокетов. Например, в нашем примере клиента мы жестко "
+"прописали IP-адрес `time.nist.gov`. Но мы не всегда знаем IP-адрес. Даже "
+"если знаем, наше программное обеспечение будет более гибким, если позволит "
+"пользователю ввести IP-адрес или даже доменное имя."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:951
+#, no-wrap
+msgid "`gethostbyname`"
+msgstr "`gethostbyname`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:954
+msgid ""
+"While there is no way to pass the domain name directly to any of the sockets "
+"functions, the FreeBSD C library comes with the man:gethostbyname[3] and "
+"man:gethostbyname2[3] functions, declared in [.filename]#netdb.h#."
+msgstr ""
+"Хотя нет возможности передать имя домена напрямую в какие-либо функции "
+"сокетов, стандартная библиотека C в FreeBSD предоставляет функции "
+"man:gethostbyname[3] и man:gethostbyname2[3], объявленные в "
+"[.filename]#netdb.h#."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:959
+#, no-wrap
+msgid ""
+"struct hostent * gethostbyname(const char *name);\n"
+"struct hostent * gethostbyname2(const char *name, int af);\n"
+msgstr ""
+"struct hostent * gethostbyname(const char *name);\n"
+"struct hostent * gethostbyname2(const char *name, int af);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:963
+msgid ""
+"Both return a pointer to the `hostent` structure, with much information "
+"about the domain. For our purposes, the `h_addr_list[0]` field of the "
+"structure points at `h_length` bytes of the correct address, already stored "
+"in the _network byte order_."
+msgstr ""
+"Оба возвращают указатель на структуру `hostent`, содержащую много информации "
+"о домене. Для наших целей поле `h_addr_list[0]` структуры указывает на "
+"`h_length` байтов правильного адреса, уже сохранённого в _порядке байтов "
+"сети_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:965
+msgid ""
+"This allows us to create a much more flexible-and much more useful-version "
+"of our daytime program:"
+msgstr ""
+"Это позволяет нам создать гораздо более гибкую — и гораздо более полезную — "
+"версию нашей программы daytime:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:981
+#, no-wrap
+msgid ""
+"/*\n"
+" * daytime.c\n"
+" *\n"
+" * Programmed by G. Adam Stanislav\n"
+" * 19 June 2001\n"
+" */\n"
+"#include <stdio.h>\n"
+"#include <string.h>\n"
+"#include <unistd.h>\n"
+"#include <sys/types.h>\n"
+"#include <sys/socket.h>\n"
+"#include <netinet/in.h>\n"
+"#include <netdb.h>\n"
+msgstr ""
+"/*\n"
+" * daytime.c\n"
+" *\n"
+" * Programmed by G. Adam Stanislav\n"
+" * 19 June 2001\n"
+" */\n"
+"#include <stdio.h>\n"
+"#include <string.h>\n"
+"#include <unistd.h>\n"
+"#include <sys/types.h>\n"
+"#include <sys/socket.h>\n"
+"#include <netinet/in.h>\n"
+"#include <netdb.h>\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:988
+#, no-wrap
+msgid ""
+"int main(int argc, char *argv[]) {\n"
+" int s, bytes;\n"
+" struct sockaddr_in sa;\n"
+" struct hostent *he;\n"
+" char buf[BUFSIZ+1];\n"
+" char *host;\n"
+msgstr ""
+"int main(int argc, char *argv[]) {\n"
+" int s, bytes;\n"
+" struct sockaddr_in sa;\n"
+" struct hostent *he;\n"
+" char buf[BUFSIZ+1];\n"
+" char *host;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:998
+#, no-wrap
+msgid ""
+" sa.sin_family = AF_INET;\n"
+" sa.sin_port = htons(13);\n"
+msgstr ""
+" sa.sin_family = AF_INET;\n"
+" sa.sin_port = htons(13);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1000
+#, no-wrap
+msgid " host = (argc > 1) ? argv[1] : \"time.nist.gov\";\n"
+msgstr " host = (argc > 1) ? argv[1] : \"time.nist.gov\";\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1005
+#, no-wrap
+msgid ""
+" if ((he = gethostbyname(host)) == NULL) {\n"
+" herror(host);\n"
+" return 2;\n"
+" }\n"
+msgstr ""
+" if ((he = gethostbyname(host)) == NULL) {\n"
+" herror(host);\n"
+" return 2;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1007
+#, no-wrap
+msgid " memcpy(&sa.sin_addr, he->h_addr_list[0], he->h_length);\n"
+msgstr " memcpy(&sa.sin_addr, he->h_addr_list[0], he->h_length);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1012
+#, no-wrap
+msgid ""
+" if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {\n"
+" perror(\"connect\");\n"
+" return 3;\n"
+" }\n"
+msgstr ""
+" if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {\n"
+" perror(\"connect\");\n"
+" return 3;\n"
+" }\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1015
+#, no-wrap
+msgid ""
+" while ((bytes = read(s, buf, BUFSIZ)) > 0)\n"
+" write(1, buf, bytes);\n"
+msgstr ""
+" while ((bytes = read(s, buf, BUFSIZ)) > 0)\n"
+" write(1, buf, bytes);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1025
+msgid ""
+"We now can type a domain name (or an IP address, it works both ways) on the "
+"command line, and the program will try to connect to its _daytime_ server. "
+"Otherwise, it will still default to `time.nist.gov`. However, even in this "
+"case we will use `gethostbyname` rather than hard coding `192.43.244.18`. "
+"That way, even if its IP address changes in the future, we will still find "
+"it."
+msgstr ""
+"Теперь мы можем ввести доменное имя (или IP-адрес, это работает в обоих "
+"направлениях) в командной строке, и программа попытается подключиться к его "
+"серверу _daytime_. В противном случае, по умолчанию будет использоваться "
+"`time.nist.gov`. Однако даже в этом случае мы будем использовать "
+"`gethostbyname` вместо жесткого задания `192.43.244.18`. Таким образом, даже "
+"если его IP-адрес изменится в будущем, мы всё равно сможем его найти."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1029
+msgid ""
+"Since it takes virtually no time to get the time from your local server, you "
+"could run daytime twice in a row: First to get the time from "
+"`time.nist.gov`, the second time from your own system. You can then compare "
+"the results and see how exact your system clock is:"
+msgstr ""
+"Поскольку получение времени от локального сервера занимает практически "
+"нулевое время, вы можете запустить daytime дважды подряд: сначала для "
+"получения времени от `time.nist.gov`, а затем от вашей собственной системы. "
+"После этого вы можете сравнить результаты и увидеть, насколько точны часы "
+"вашей системы:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1033
+#, no-wrap
+msgid "% daytime ; daytime localhost\n"
+msgstr "% daytime ; daytime localhost\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1037
+#, no-wrap
+msgid ""
+"52080 01-06-20 04:02:33 50 0 0 390.2 UTC(NIST) *\n"
+"2001-06-20T04:02:35Z\n"
+"%\n"
+msgstr ""
+"52080 01-06-20 04:02:33 50 0 0 390.2 UTC(NIST) *\n"
+"2001-06-20T04:02:35Z\n"
+"%\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1040
+msgid "As you can see, my system was two seconds ahead of the NIST time."
+msgstr "Как видно, моя система опережала время NIST на две секунды."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1042
+#, no-wrap
+msgid "`getservbyname`"
+msgstr "`getservbyname`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1046
+msgid ""
+"Sometimes you may not be sure what port a certain service uses. The "
+"man:getservbyname[3] function, also declared in [.filename]#netdb.h# comes "
+"in very handy in those cases:"
+msgstr ""
+"Иногда вы можете быть не уверены, какой порт использует определённая служба. "
+"В таких случаях очень полезна функция man:getservbyname[3], также "
+"объявленная в [.filename]#netdb.h#:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1050
+#, no-wrap
+msgid "struct servent * getservbyname(const char *name, const char *proto);\n"
+msgstr "struct servent * getservbyname(const char *name, const char *proto);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1053
+msgid ""
+"The `servent` structure contains the `s_port`, which contains the proper "
+"port, already in _network byte order_."
+msgstr ""
+"Структура `servent` содержит `s_port`, в котором находится соответствующий "
+"порт, уже в _порядке байтов сети_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1055
+msgid ""
+"Had we not known the correct port for the _daytime_ service, we could have "
+"found it this way:"
+msgstr ""
+"Если бы мы не знали правильный порт для службы _daytime_, мы могли бы найти "
+"его следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1065
+#, no-wrap
+msgid ""
+"struct servent *se;\n"
+" ...\n"
+" if ((se = getservbyname(\"daytime\", \"tcp\")) == NULL {\n"
+" fprintf(stderr, \"Cannot determine which port to use.\\n\");\n"
+" return 7;\n"
+" }\n"
+" sa.sin_port = se->s_port;\n"
+msgstr ""
+"struct servent *se;\n"
+" ...\n"
+" if ((se = getservbyname(\"daytime\", \"tcp\")) == NULL {\n"
+" fprintf(stderr, \"Cannot determine which port to use.\\n\");\n"
+" return 7;\n"
+" }\n"
+" sa.sin_port = se->s_port;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1072
+msgid ""
+"You usually do know the port. But if you are developing a new protocol, you "
+"may be testing it on an unofficial port. Some day, you will register the "
+"protocol and its port (if nowhere else, at least in your [.filename]#/etc/"
+"services#, which is where `getservbyname` looks). Instead of returning an "
+"error in the above code, you just use the temporary port number. Once you "
+"have listed the protocol in [.filename]#/etc/services#, your software will "
+"find its port without you having to rewrite the code."
+msgstr ""
+"Обычно порт известен. Но если вы разрабатываете новый протокол, вы можете "
+"тестировать его на неофициальном порту. Когда-нибудь вы зарегистрируете "
+"протокол и его порт (если не где-то ещё, то хотя бы в вашем [.filename]#/etc/"
+"services#, где `getservbyname` ищет). Вместо возврата ошибки в приведённом "
+"выше коде вы просто используете временный номер порта. Как только вы "
+"добавите протокол в [.filename]#/etc/services#, ваше программное обеспечение "
+"найдёт его порт без необходимости переписывать код."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1074
+#, no-wrap
+msgid "Concurrent Servers"
+msgstr "Многозадачные серверы"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1078
+msgid ""
+"Unlike a sequential server, a _concurrent server_ has to be able to serve "
+"more than one client at a time. For example, a _chat server_ may be serving "
+"a specific client for hours-it cannot wait till it stops serving a client "
+"before it serves the next one."
+msgstr ""
+"В отличие от последовательного сервера, _многозадачный сервер_ должен иметь "
+"возможность обслуживать более одного клиента одновременно. Например, _сервер "
+"чата_ может обслуживать конкретного клиента часами — он не может ждать, пока "
+"закончит обслуживать текущего клиента, прежде чем перейти к следующему."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1080
+msgid "This requires a significant change in our flowchart:"
+msgstr "Это требует значительных изменений в нашей блок-схеме:"
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1081
+#, no-wrap
+msgid "Concurrent Server"
+msgstr "Многозадачный сервер"
+
+#. type: Target for macro image
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1082
+#, no-wrap
+msgid "serv2.png"
+msgstr "serv2.png"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1086
+msgid ""
+"We moved the _serve_ from the _daemon process_ to its own _server process_. "
+"However, because each child process inherits all open files (and a socket is "
+"treated just like a file), the new process inherits not only the _\"accepted "
+"handle,\"_ i.e., the socket returned by the `accept` call, but also the _top "
+"socket_, i.e., the one opened by the top process right at the beginning."
+msgstr ""
+"Мы переместили _службу_ из _демона_ в её собственный _серверный процесс_. "
+"Однако, поскольку каждый дочерний процесс наследует все открытые файлы (а "
+"сокет обрабатывается так же, как файл), новый процесс наследует не только "
+"_\"принятый дескриптор\"_, т.е. сокет, возвращённый вызовом `accept`, но и "
+"_главный сокет_, т.е. тот, который был открыт главным процессом в самом "
+"начале."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1089
+msgid ""
+"However, the _server process_ does not need this socket and should `close` "
+"it immediately. Similarly, the _daemon process_ no longer needs the "
+"_accepted socket_, and not only should, but _must_ `close` it-otherwise, it "
+"will run out of available _file descriptors_ sooner or later."
+msgstr ""
+"Однако _серверному процессу_ этот сокет не нужен, и он должен немедленно "
+"вызвать ему `close`. Аналогично, _демону_ больше не нужен _сокет, принятый "
+"вызовом accept_, и он не только должен, но и _обязан_ вызвать ему `close` — "
+"в противном случае рано или поздно закончатся доступные _файловые "
+"дескрипторы_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1092
+msgid ""
+"After the _server process_ is done serving, it should close the _accepted "
+"socket_. Instead of returning to `accept`, it now exits."
+msgstr ""
+"После завершения обслуживания _серверного процесса_ он должен закрыть "
+"_принятый сокет_. Вместо возврата к `accept`, процесс теперь завершается."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1099
+msgid ""
+"Under UNIX(R), a process does not really _exit_. Instead, it _returns_ to "
+"its parent. Typically, a parent process ``wait``s for its child process, "
+"and obtains a return value. However, our _daemon process_ cannot simply "
+"stop and wait. That would defeat the whole purpose of creating additional "
+"processes. But if it never does `wait`, its children will become _zombies_-"
+"no longer functional but still roaming around."
+msgstr ""
+"В UNIX(R) процесс на самом деле не _завершается_. Вместо этого он "
+"_возвращается_ к своему родителю. Обычно родительский процесс ``ждёт`` "
+"(wait) завершения своего дочернего процесса и получает возвращаемое "
+"значение. Однако наш _демон-процесс_ не может просто остановиться и ждать. "
+"Это бы свело на нет всю цель создания дополнительных процессов. Но если он "
+"никогда не выполняет `wait`, его дочерние процессы станут _зомби_ — более не "
+"функционирующими, но всё ещё бродящими вокруг."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1102
+msgid ""
+"For that reason, the _daemon process_ needs to set _signal handlers_ in its "
+"_initialize daemon_ phase. At least a SIGCHLD signal has to be processed, "
+"so the daemon can remove the zombie return values from the system and "
+"release the system resources they are taking up."
+msgstr ""
+"По этой причине _демону_ необходимо установить _обработчики сигналов_ на "
+"этапе _инициализации демона_. Как минимум, должен обрабатываться сигнал "
+"SIGCHLD, чтобы демон мог удалять зомби-процессы из системы и освобождать "
+"занимаемые ими системные ресурсы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/sockets/_index.adoc:1105
+msgid ""
+"That is why our flowchart now contains a _process signals_ box, which is not "
+"connected to any other box. By the way, many servers also process SIGHUP, "
+"and typically interpret as the signal from the superuser that they should "
+"reread their configuration files. This allows us to change settings without "
+"having to kill and restart these servers."
+msgstr ""
+"Вот почему наша блок-схема теперь содержит блок _обработки сигналов_, "
+"который не соединен с другими блоками. Кстати, многие серверы также "
+"обрабатывают SIGHUP и обычно интерпретируют его как сигнал от "
+"суперпользователя, указывающий на необходимость перечитать конфигурационные "
+"файлы. Это позволяет нам изменять настройки без необходимости завершать и "
+"перезапускать эти серверы."
diff --git a/documentation/content/ru/books/developers-handbook/testing/_index.adoc b/documentation/content/ru/books/developers-handbook/testing/_index.adoc
new file mode 100644
index 0000000000..a801c9644f
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/testing/_index.adoc
@@ -0,0 +1,187 @@
+---
+authors:
+description: 'Регрессионное и нагрузочное тестирование'
+next: books/developers-handbook/partii
+params:
+ path: /books/developers-handbook/testing/
+prev: books/developers-handbook/policies
+showBookMenu: true
+tags: ["Regression", "Performance Testing", "Testing", "Tinderbox"]
+title: 'Глава 6. Регрессионное и нагрузочное тестирование'
+weight: 7
+---
+
+[[testing]]
+= Регрессионное и нагрузочное тестирование
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 6
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/developers-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+Регрессионные тесты используются для проверки определенной части системы, чтобы убедиться, что она работает как ожидается, и для предотвращения повторного появления старых ошибок.
+
+Инструменты для регрессионного тестирования FreeBSD можно найти в дереве исходных кодов FreeBSD в каталоге [.filename]#src/tools/regression#.
+
+[[testing-micro-benchmark]]
+== Контрольный список для бенчмарка низкоуровневых операций
+
+Этот раздел содержит рекомендации по проведению корректного бенчмарка низкоуровненых операций на FreeBSD или самой FreeBSD.
+
+Невозможно использовать все приведенные ниже рекомендации каждый раз, но чем больше их применяется, тем лучше способность теста выявлять небольшие различия.
+
+* Отключить APM и любые другие манипуляции с часами (ACPI ?).
+* Запускайте тесты в однопользовательском режиме. Например, man:cron[8] и другие демоны только добавляют шум. Демон man:sshd[8] также может вызвать проблемы. Если требуется доступ по SSH во время тестирования, либо отключите перегенерацию ключа SSHv1, либо завершите родительский демон `sshd` во время тестов.
+* Не запускайте man:ntpd[8].
+* Если события man:syslog[3] генерируются, запустите man:syslogd[8] с пустым [.filename]#/etc/syslogd.conf#, в противном случае не запускайте его.
+* Минимизируйте дисковые операции ввода-вывода, по возможности избегайте их полностью.
+* Не монтируйте файловые системы, которые не требуются.
+* Смонтируйте [.filename]#/#, [.filename]#/usr# и любые другие файловые системы в режиме только для чтения, если это возможно. Это исключает обновления atime на диске (и т.д.) из общей картины ввода-вывода.
+* Переинициализируйте тестовую файловую систему с возможностью чтения/записи с помощью man:newfs[8] и заполните её из файла man:tar[1] или man:dump[8] перед каждым запуском. Размонтируйте и смонтируйте её перед началом теста. Это обеспечит согласованную структуру файловой системы. Для теста worldstone это применимо к [.filename]#/usr/obj# (просто переинициализируйте с помощью `newfs` и смонтируйте). Для достижения 100% воспроизводимости заполните файловую систему из файла man:dd[1] (например: `dd if=myimage of=/dev/ad0s1h bs=1m`)
+* Используйте разделы man:md[4] с поддержкой malloc или предзагруженные.
+* Перезагружайтесь между отдельными итерациями теста, это обеспечивает более согласованное состояние.
+* Удалите все необязательные драйверы устройств из ядра. Например, если USB не нужен для теста, не включайте поддержку USB в ядре. Драйверы, которые подключаются, часто имеют работающие таймауты.
+* Отключите неиспользуемое оборудование. Отсоедините диски с помощью man:atacontrol[8] и man:camcontrol[8], если диски не используются для тестирования.
+* Не настраивайте сеть, если она не тестируется, или дождитесь завершения тестирования, чтобы отправить результаты на другой компьютер.
+* Отключите "турбо-режимы", так как они делают тактовую частоту явно зависимой от окружающей среды. Это означает, что результаты тестирования на 100% идентичном коде могут зависеть от времени суток, употребления кофе или газировки или даже от количества людей в офисе.
+
+Если система должна быть подключена к общедоступной сети, следите за всплесками широковещательного трафика. Даже если они почти незаметны, они будут занимать циклы процессора. Многоадресная рассылка имеет аналогичные предостережения.
+* Размещайте каждую файловую систему на отдельном диске. Это минимизирует задержки, вызванные оптимизацией перемещения головок диска.
+* Минимизируйте вывод на последовательные или VGA-консоли. Запись вывода в файлы снижает дрожание. (Консоли на последовательном порту легко становятся узким местом.) Не касайтесь клавиатуры во время выполнения теста, даже нажатия kbd:[пробел] или kbd:[back-space] отражаются в числах.
+* Убедитесь, что тест достаточно длинный, но не слишком. Если тест слишком короткий, возникают проблемы с временными метками. Если он слишком длинный, изменения температуры и дрейф повлияют на частоту кварцевых кристаллов в компьютере. Эмпирическое правило: больше минуты, меньше часа.
+* Попытайтесь поддерживать температуру вокруг машины как можно более стабильной. Это влияет как на кварцевые резонаторы, так и на алгоритмы работы дисковых накопителей. Для получения действительно стабильных часов рассмотрите возможность использования стабилизированного тактового сигнала. Например, используйте OCXO + PLL и подавайте выходной сигнал в тактовые схемы вместо кварцевого резонатора на материнской плате. Для получения дополнительной информации по этому вопросу свяжитесь с {phk}.
+* Выполните тест как минимум 3 раза, но лучше запустить более 20 раз как для кода "до", так и для кода "после". По возможности чередуйте запуски (т.е. не следует запускать 20 раз "до", а затем 20 раз "после"), это поможет выявить влияние окружения. Не чередуйте строго 1:1, а лучше 3:3, чтобы можно было обнаружить эффекты взаимодействия.
++
+Хороший шаблон: `bababa{bbbaaa}*`. Это дает подсказку после первых 1+1 прогонов (так что можно остановить тест, если всё идет совсем не так), стандартное отклонение после первых 3+3 (дает хорошее представление, стоит ли проводить длительный прогон), а также тренды и показатели взаимодействия позже.
+* Используйте man:ministat[1], чтобы определить, являются ли числа значимыми. Рекомендуется приобрести книгу "Cartoon guide to statistics" ISBN: 0062731025, особенно если вы забыли или никогда не изучали стандартное отклонение и t-критерий Стьюдента.
+* Не используйте фоновый man:fsck[8], если тест не является бенчмарком фонового `fsck`. Также отключите `background_fsck` в [.filename]#/etc/rc.conf#, если бенчмарк не запускается как минимум через 60+«время работы ``fsck``» секунд после загрузки, так как man:rc[8] пробуждается и проверяет, нужно ли запускать `fsck` для каких-либо файловых систем, когда включен фоновый `fsck`. Аналогично, убедитесь, что нет оставшихся снимков, если только бенчмарк не является тестом со снимками.
+* Если тесты производительности показывают неожиданно низкие результаты, проверьте такие факторы, как высокий объем прерываний из неожиданного источника. Сообщалось, что некоторые версии ACPI могут "вести себя неправильно" и генерировать избыточные прерывания. Для диагностики необычных результатов тестов сделайте несколько снимков `vmstat -i` и поищите что-то необычное.
+* Будьте внимательны к параметрам оптимизации для ядра и пользовательского пространства, а также отладки. Легко упустить что-то и позже понять, что тест сравнивал не одно и то же.
+* Никогда не проводите тестирование производительности с включёнными параметрами ядра `WITNESS` и `INVARIANTS`, если тест не направлен на оценку производительности именно этих функций. `WITNESS` может привести к снижению производительности на 400% и более. Аналогично, параметры userspace man:malloc[3] по умолчанию отличаются в -CURRENT от тех, что поставляются в релизах.
+
+[[testing-tinderbox]]
+== Tinderbox для исходного текста FreeBSD
+
+Исходный Tinderbox состоит из:
+
+* Скрипта сборки [.filename]#tinderbox#, который автоматизирует выгрузку определённой версии исходного кода FreeBSD и её сборку.
+* Скрипта-супервизора [.filename]#tbmaster#, который отслеживает отдельные экземпляры Tinderbox, записывает их вывод и отправляет уведомления о сбоях по электронной почте.
+* Скрипта CGI с именем [.filename]#index.cgi#, который читает набор журналов tbmaster и представляет их в виде удобочитаемой HTML-сводки.
+* Набора серверов сборки, которые постоянно тестируют последние изменения наиболее важных веток кода FreeBSD.
+* Веб-сервера, хранящего полный набор журналов Tinderbox и отображающий актуальную сводку.
+
+Скрипты поддерживаются и были разработаны {des}, и сейчас написаны на Perl, что стало шагом вперед по сравнению с их первоначальной версией в виде shell-скриптов. Все скрипты и конфигурационные файлы хранятся в https://www.freebsd.org/cgi/cvsweb.cgi/projects/tinderbox/[/projects/tinderbox/].
+
+Для получения дополнительной информации о скриптах tinderbox и tbmaster на данном этапе обратитесь к соответствующим руководствам: tinderbox(1) и tbmaster(1).
+
+== Скрипт index.cgi
+
+Скрипт [.filename]#index.cgi# генерирует HTML-сводку журналов tinderbox и tbmaster. Хотя изначально он предназначался для использования в качестве CGI-скрипта, как следует из его названия, этот скрипт также может быть запущен из командной строки или из задачи man:cron[8], в таком случае он будет искать логи в директории, где расположен сам скрипт. Он автоматически определяет контекст, генерируя HTTP-заголовки при запуске в качестве CGI-скрипта. Он соответствует стандартам XHTML и использует CSS для стилизации.
+
+Скрипт начинает работу в блоке `main()`, пытаясь проверить, что он выполняется на официальном сайте Tinderbox. Если это не так, создается страница с указанием, что это не официальный сайт, и предоставляется URL официального сайта.
+
+Далее выполняется сканирование каталога журналов для получения перечня конфигураций, веток и архитектур, для которых существуют файлы журналов, чтобы избежать жесткого задания списка в скрипте и потенциального появления пустых строк или столбцов. Эта информация извлекается из имен файлов журналов, соответствующих следующему шаблону:
+
+[.programlisting]
+....
+tinderbox-$config-$branch-$arch-$machine.{brief,full}
+....
+
+Конфигурации, используемые на официальных серверах сборки Tinderbox, названы в соответствии с ветками, которые они собирают. Например, конфигурация `releng_8` используется для сборки `RELENG_8`, а также всех поддерживаемых веток выпусков.
+
+После успешного завершения всей процедуры запуска для каждой конфигурации вызывается `do_config()`.
+
+Функция `do_config()` генерирует HTML для отдельной конфигурации Tinderbox.
+
+Он работает, сначала создавая строку заголовка, затем перебирая каждую сборку ветки с указанной конфигурацией, формируя одну строку результатов для каждой следующим образом:
+
+* Для каждого элемента:
+
+** Для каждой машины в рамках этой архитектуры:
+
+*** Если существует краткий файл журнала, то:
+
+**** Вызвать `success()`, чтобы определить результат сборки.
+**** Вывести размер изменения.
+**** Вывести размер краткого файла журнала со ссылкой на сам файл журнала.
+**** Если также существует полный файл журнала, то:
+
+***** Вывести размер полного файла журнала со ссылкой на сам файл журнала.
+
+*** В противном случае:
+
+**** Нечего не выводить.
+
+Упомянутая выше функция `success()` проверяет краткий лог-файл на наличие строки "tinderbox run completed", чтобы определить, был ли сборка успешной.
+
+Конфигурации и ветви сортируются в соответствии с их рангом. Это вычисляется следующим образом:
+
+* `HEAD` и `CURRENT` имеют ранг 9999.
+* `RELENG_x` имеет ранг __``xx``__99.
+* `RELENG_x_y` имеет ранг _xxyy_.
+
+Это означает, что `HEAD` всегда имеет наивысший приоритет, а ветви `RELENG` ранжируются в числовом порядке, причём каждая ветвь `STABLE` имеет более высокий приоритет, чем ветви выпусков, ответвлённые от неё. Например, для FreeBSD 8 порядок от наивысшего к низшему будет следующим:
+
+* `RELENG_8` (ранг ветки 899).
+* `RELENG_8_3` (ранг ветки 803).
+* `RELENG_8_2` (ранг ветки 802).
+* `RELENG_8_1` (ранг ветки 801).
+* `RELENG_8_0` (ранг ветки 800).
+
+Цвета, которые Tinderbox использует для каждой ячейки в таблице, определяются CSS. Успешные сборки отображаются зелёным текстом; неудачные сборки отображаются красным текстом. Цвет блекнет со временем с момента соответствующей сборки, приближаясь к серому каждые полчаса.
+
+== Официальные серверы сборки
+
+Официальные серверы сборки Tinderbox размещены на площадке http://www.sentex.ca[Sentex Data Communications], которая также предоставляет хостинг для кластера Netperf FreeBSD.
+
+В настоящее время работают три сервера сборки:
+
+_freebsd-current.sentex.ca_ собирает:
+
+* `HEAD` для amd64, arm, i386, i386/pc98, ia64, mips, powerpc, powerpc64 и sparc64.
+* `RELENG_9` и поддерживаемые ветки 9._X_ для amd64, arm, i386, i386/pc98, ia64, mips, powerpc, powerpc64 и sparc64.
+
+_freebsd-stable.sentex.ca_ собирает:
+
+* `RELENG_8` и поддерживаемые ветки 8._X_ для amd64, i386, i386/pc98, ia64, mips, powerpc и sparc64.
+
+_freebsd-legacy.sentex.ca_ собирает:
+
+* `RELENG_7` и поддерживаемые ветки 7._X_ для amd64, i386, i386/pc98, ia64, powerpc и sparc64.
+
+== Официальный сайт со сводками
+
+Сводки и журналы с официальных серверов сборки доступны в сети по адресу http://tinderbox.FreeBSD.org[http://tinderbox.FreeBSD.org], размещены на {des} и настроены следующим образом:
+
+* Задание man:cron[8] проверяет серверы сборки через регулярные интервалы и загружает все новые файлы журналов с помощью man:rsync[1].
+* Apache настроен на использование [.filename]#index.cgi# в качестве `DirectoryIndex`.
diff --git a/documentation/content/ru/books/developers-handbook/testing/_index.po b/documentation/content/ru/books/developers-handbook/testing/_index.po
new file mode 100644
index 0000000000..220968249a
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/testing/_index.po
@@ -0,0 +1,802 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-06-27 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbooktesting_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:1
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:15
+#, no-wrap
+msgid "Regression and Performance Testing"
+msgstr "Регрессионное и нагрузочное тестирование"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:1
+#, no-wrap
+msgid "Chapter 6. Regression and Performance Testing"
+msgstr "Глава 6. Регрессионное и нагрузочное тестирование"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:53
+msgid ""
+"Regression tests are used to exercise a particular bit of the system to "
+"check that it works as expected, and to make sure that old bugs are not "
+"reintroduced."
+msgstr ""
+"Регрессионные тесты используются для проверки определенной части системы, "
+"чтобы убедиться, что она работает как ожидается, и для предотвращения "
+"повторного появления старых ошибок."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:55
+msgid ""
+"The FreeBSD regression testing tools can be found in the FreeBSD source tree "
+"in the directory [.filename]#src/tools/regression#."
+msgstr ""
+"Инструменты для регрессионного тестирования FreeBSD можно найти в дереве "
+"исходных кодов FreeBSD в каталоге [.filename]#src/tools/regression#."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:57
+#, no-wrap
+msgid "Micro Benchmark Checklist"
+msgstr "Контрольный список для бенчмарка низкоуровневых операций"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:60
+msgid ""
+"This section contains hints for doing proper micro-benchmarking on FreeBSD "
+"or of FreeBSD itself."
+msgstr ""
+"Этот раздел содержит рекомендации по проведению корректного бенчмарка "
+"низкоуровненых операций на FreeBSD или самой FreeBSD."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:62
+msgid ""
+"It is not possible to use all of the suggestions below every single time, "
+"but the more used, the better the benchmark's ability to test small "
+"differences will be."
+msgstr ""
+"Невозможно использовать все приведенные ниже рекомендации каждый раз, но чем "
+"больше их применяется, тем лучше способность теста выявлять небольшие "
+"различия."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:64
+msgid "Disable APM and any other kind of clock fiddling (ACPI ?)."
+msgstr "Отключить APM и любые другие манипуляции с часами (ACPI ?)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:65
+msgid ""
+"Run in single user mode. E.g., man:cron[8], and other daemons only add "
+"noise. The man:sshd[8] daemon can also cause problems. If ssh access is "
+"required during testing either disable the SSHv1 key regeneration, or kill "
+"the parent `sshd` daemon during the tests."
+msgstr ""
+"Запускайте тесты в однопользовательском режиме. Например, man:cron[8] и "
+"другие демоны только добавляют шум. Демон man:sshd[8] также может вызвать "
+"проблемы. Если требуется доступ по SSH во время тестирования, либо отключите "
+"перегенерацию ключа SSHv1, либо завершите родительский демон `sshd` во время "
+"тестов."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:66
+msgid "Do not run man:ntpd[8]."
+msgstr "Не запускайте man:ntpd[8]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:67
+msgid ""
+"If man:syslog[3] events are generated, run man:syslogd[8] with an empty "
+"[.filename]#/etc/syslogd.conf#, otherwise, do not run it."
+msgstr ""
+"Если события man:syslog[3] генерируются, запустите man:syslogd[8] с пустым "
+"[.filename]#/etc/syslogd.conf#, в противном случае не запускайте его."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:68
+msgid "Minimize disk-I/O, avoid it entirely if possible."
+msgstr ""
+"Минимизируйте дисковые операции ввода-вывода, по возможности избегайте их "
+"полностью."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:69
+msgid "Do not mount file systems that are not needed."
+msgstr "Не монтируйте файловые системы, которые не требуются."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:70
+msgid ""
+"Mount [.filename]#/#, [.filename]#/usr#, and any other file system as read-"
+"only if possible. This removes atime updates to disk (etc.) from the I/O "
+"picture."
+msgstr ""
+"Смонтируйте [.filename]#/#, [.filename]#/usr# и любые другие файловые "
+"системы в режиме только для чтения, если это возможно. Это исключает "
+"обновления atime на диске (и т.д.) из общей картины ввода-вывода."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:71
+msgid ""
+"Reinitialize the read/write test file system with man:newfs[8] and populate "
+"it from a man:tar[1] or man:dump[8] file before every run. Unmount and mount "
+"it before starting the test. This results in a consistent file system "
+"layout. For a worldstone test this would apply to [.filename]#/usr/obj# "
+"(just reinitialize with `newfs` and mount). To get 100% reproducibility, "
+"populate the file system from a man:dd[1] file (i.e.: `dd if=myimage of=/dev/"
+"ad0s1h bs=1m`)"
+msgstr ""
+"Переинициализируйте тестовую файловую систему с возможностью чтения/записи с "
+"помощью man:newfs[8] и заполните её из файла man:tar[1] или man:dump[8] "
+"перед каждым запуском. Размонтируйте и смонтируйте её перед началом теста. "
+"Это обеспечит согласованную структуру файловой системы. Для теста worldstone "
+"это применимо к [.filename]#/usr/obj# (просто переинициализируйте с помощью "
+"`newfs` и смонтируйте). Для достижения 100% воспроизводимости заполните "
+"файловую систему из файла man:dd[1] (например: `dd if=myimage of=/dev/ad0s1h "
+"bs=1m`)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:72
+msgid "Use malloc backed or preloaded man:md[4] partitions."
+msgstr "Используйте разделы man:md[4] с поддержкой malloc или предзагруженные."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:73
+msgid ""
+"Reboot between individual iterations of the test, this gives a more "
+"consistent state."
+msgstr ""
+"Перезагружайтесь между отдельными итерациями теста, это обеспечивает более "
+"согласованное состояние."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:74
+msgid ""
+"Remove all non-essential device drivers from the kernel. For instance if USB "
+"is not needed for the test, do not put USB in the kernel. Drivers which "
+"attach often have timeouts ticking away."
+msgstr ""
+"Удалите все необязательные драйверы устройств из ядра. Например, если USB не "
+"нужен для теста, не включайте поддержку USB в ядре. Драйверы, которые "
+"подключаются, часто имеют работающие таймауты."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:75
+msgid ""
+"Unconfigure hardware that are not in use. Detach disks with "
+"man:atacontrol[8] and man:camcontrol[8] if the disks are not used for the "
+"test."
+msgstr ""
+"Отключите неиспользуемое оборудование. Отсоедините диски с помощью "
+"man:atacontrol[8] и man:camcontrol[8], если диски не используются для "
+"тестирования."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:76
+msgid ""
+"Do not configure the network unless it is being tested, or wait until after "
+"the test has been performed to ship the results off to another computer."
+msgstr ""
+"Не настраивайте сеть, если она не тестируется, или дождитесь завершения "
+"тестирования, чтобы отправить результаты на другой компьютер."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:78
+msgid ""
+"Disable \"Turbo-modes\" because they make the clock frequency explicitly "
+"depend on the environment. This means that benchmark runs on 100% identical "
+"code, may depend on time of day, coffee vs. soda or even how many other "
+"people are in the office."
+msgstr ""
+"Отключите \"турбо-режимы\", так как они делают тактовую частоту явно "
+"зависимой от окружающей среды. Это означает, что результаты тестирования на "
+"100% идентичном коде могут зависеть от времени суток, употребления кофе или "
+"газировки или даже от количества людей в офисе."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:80
+msgid ""
+"If the system must be connected to a public network, watch out for spikes of "
+"broadcast traffic. Even though it is hardly noticeable, it will take up CPU "
+"cycles. Multicast has similar caveats."
+msgstr ""
+"Если система должна быть подключена к общедоступной сети, следите за "
+"всплесками широковещательного трафика. Даже если они почти незаметны, они "
+"будут занимать циклы процессора. Многоадресная рассылка имеет аналогичные "
+"предостережения."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:81
+msgid ""
+"Put each file system on its own disk. This minimizes jitter from head-seek "
+"optimizations."
+msgstr ""
+"Размещайте каждую файловую систему на отдельном диске. Это минимизирует "
+"задержки, вызванные оптимизацией перемещения головок диска."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:82
+msgid ""
+"Minimize output to serial or VGA consoles. Running output into files gives "
+"less jitter. (Serial consoles easily become a bottleneck.) Do not touch "
+"keyboard while the test is running, even kbd:[space] or kbd:[back-space] "
+"shows up in the numbers."
+msgstr ""
+"Минимизируйте вывод на последовательные или VGA-консоли. Запись вывода в "
+"файлы снижает дрожание. (Консоли на последовательном порту легко становятся "
+"узким местом.) Не касайтесь клавиатуры во время выполнения теста, даже "
+"нажатия kbd:[пробел] или kbd:[back-space] отражаются в числах."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:83
+msgid ""
+"Make sure the test is long enough, but not too long. If the test is too "
+"short, timestamping is a problem. If it is too long temperature changes and "
+"drift will affect the frequency of the quartz crystals in the computer. Rule "
+"of thumb: more than a minute, less than an hour."
+msgstr ""
+"Убедитесь, что тест достаточно длинный, но не слишком. Если тест слишком "
+"короткий, возникают проблемы с временными метками. Если он слишком длинный, "
+"изменения температуры и дрейф повлияют на частоту кварцевых кристаллов в "
+"компьютере. Эмпирическое правило: больше минуты, меньше часа."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:84
+msgid ""
+"Try to keep the temperature as stable as possible around the machine. This "
+"affects both quartz crystals and disk drive algorithms. To get real stable "
+"clock, consider stabilized clock injection. E.g., get a OCXO + PLL, inject "
+"output into clock circuits instead of motherboard xtal. Contact {phk} for "
+"more information about this."
+msgstr ""
+"Попытайтесь поддерживать температуру вокруг машины как можно более "
+"стабильной. Это влияет как на кварцевые резонаторы, так и на алгоритмы "
+"работы дисковых накопителей. Для получения действительно стабильных часов "
+"рассмотрите возможность использования стабилизированного тактового сигнала. "
+"Например, используйте OCXO + PLL и подавайте выходной сигнал в тактовые "
+"схемы вместо кварцевого резонатора на материнской плате. Для получения "
+"дополнительной информации по этому вопросу свяжитесь с {phk}."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:85
+msgid ""
+"Run the test at least 3 times but it is better to run more than 20 times "
+"both for \"before\" and \"after\" code. Try to interleave if possible (i.e.: "
+"do not run 20 times before then 20 times after), this makes it possible to "
+"spot environmental effects. Do not interleave 1:1, but 3:3, this makes it "
+"possible to spot interaction effects."
+msgstr ""
+"Выполните тест как минимум 3 раза, но лучше запустить более 20 раз как для "
+"кода \"до\", так и для кода \"после\". По возможности чередуйте запуски "
+"(т.е. не следует запускать 20 раз \"до\", а затем 20 раз \"после\"), это "
+"поможет выявить влияние окружения. Не чередуйте строго 1:1, а лучше 3:3, "
+"чтобы можно было обнаружить эффекты взаимодействия."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:88
+msgid ""
+"A good pattern is: `bababa{bbbaaa}*`. This gives hint after the first 1+1 "
+"runs (so it is possible to stop the test if it goes entirely the wrong way), "
+"a standard deviation after the first 3+3 (gives a good indication if it is "
+"going to be worth a long run) and trending and interaction numbers later on."
+msgstr ""
+"Хороший шаблон: `bababa{bbbaaa}*`. Это дает подсказку после первых 1+1 "
+"прогонов (так что можно остановить тест, если всё идет совсем не так), "
+"стандартное отклонение после первых 3+3 (дает хорошее представление, стоит "
+"ли проводить длительный прогон), а также тренды и показатели взаимодействия "
+"позже."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:89
+msgid ""
+"Use man:ministat[1] to see if the numbers are significant. Consider buying "
+"\"Cartoon guide to statistics\" ISBN: 0062731025, highly recommended, if you "
+"have forgotten or never learned about standard deviation and Student's T."
+msgstr ""
+"Используйте man:ministat[1], чтобы определить, являются ли числа значимыми. "
+"Рекомендуется приобрести книгу \"Cartoon guide to statistics\" ISBN: "
+"0062731025, особенно если вы забыли или никогда не изучали стандартное "
+"отклонение и t-критерий Стьюдента."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:90
+msgid ""
+"Do not use background man:fsck[8] unless the test is a benchmark of "
+"background `fsck`. Also, disable `background_fsck` in [.filename]#/etc/"
+"rc.conf# unless the benchmark is not started at least 60+\"``fsck`` "
+"runtime\" seconds after the boot, as man:rc[8] wakes up and checks if `fsck` "
+"needs to run on any file systems when background `fsck` is enabled. "
+"Likewise, make sure there are no snapshots lying around unless the benchmark "
+"is a test with snapshots."
+msgstr ""
+"Не используйте фоновый man:fsck[8], если тест не является бенчмарком "
+"фонового `fsck`. Также отключите `background_fsck` в [.filename]#/etc/"
+"rc.conf#, если бенчмарк не запускается как минимум через 60+«время работы "
+"``fsck``» секунд после загрузки, так как man:rc[8] пробуждается и проверяет, "
+"нужно ли запускать `fsck` для каких-либо файловых систем, когда включен "
+"фоновый `fsck`. Аналогично, убедитесь, что нет оставшихся снимков, если "
+"только бенчмарк не является тестом со снимками."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:91
+msgid ""
+"If the benchmark show unexpected bad performance, check for things like high "
+"interrupt volume from an unexpected source. Some versions of ACPI have been "
+"reported to \"misbehave\" and generate excess interrupts. To help diagnose "
+"odd test results, take a few snapshots of `vmstat -i` and look for anything "
+"unusual."
+msgstr ""
+"Если тесты производительности показывают неожиданно низкие результаты, "
+"проверьте такие факторы, как высокий объем прерываний из неожиданного "
+"источника. Сообщалось, что некоторые версии ACPI могут \"вести себя "
+"неправильно\" и генерировать избыточные прерывания. Для диагностики "
+"необычных результатов тестов сделайте несколько снимков `vmstat -i` и "
+"поищите что-то необычное."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:92
+msgid ""
+"Make sure to be careful about optimization parameters for kernel and "
+"userspace, likewise debugging. It is easy to let something slip through and "
+"realize later the test was not comparing the same thing."
+msgstr ""
+"Будьте внимательны к параметрам оптимизации для ядра и пользовательского "
+"пространства, а также отладки. Легко упустить что-то и позже понять, что "
+"тест сравнивал не одно и то же."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:93
+msgid ""
+"Do not ever benchmark with the `WITNESS` and `INVARIANTS` kernel options "
+"enabled unless the test is interested to benchmarking those features. "
+"`WITNESS` can cause 400%+ drops in performance. Likewise, userspace "
+"man:malloc[3] parameters default differently in -CURRENT from the way they "
+"ship in production releases."
+msgstr ""
+"Никогда не проводите тестирование производительности с включёнными "
+"параметрами ядра `WITNESS` и `INVARIANTS`, если тест не направлен на оценку "
+"производительности именно этих функций. `WITNESS` может привести к снижению "
+"производительности на 400% и более. Аналогично, параметры userspace "
+"man:malloc[3] по умолчанию отличаются в -CURRENT от тех, что поставляются в "
+"релизах."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:95
+#, no-wrap
+msgid "The FreeBSD Source Tinderbox"
+msgstr "Tinderbox для исходного текста FreeBSD"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:98
+msgid "The source Tinderbox consists of:"
+msgstr "Исходный Tinderbox состоит из:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:100
+msgid ""
+"A build script, [.filename]#tinderbox#, that automates checking out a "
+"specific version of the FreeBSD source tree and building it."
+msgstr ""
+"Скрипта сборки [.filename]#tinderbox#, который автоматизирует выгрузку "
+"определённой версии исходного кода FreeBSD и её сборку."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:101
+msgid ""
+"A supervisor script, [.filename]#tbmaster#, that monitors individual "
+"Tinderbox instances, logs their output, and emails failure notices."
+msgstr ""
+"Скрипта-супервизора [.filename]#tbmaster#, который отслеживает отдельные "
+"экземпляры Tinderbox, записывает их вывод и отправляет уведомления о сбоях "
+"по электронной почте."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:102
+msgid ""
+"A CGI script named [.filename]#index.cgi# that reads a set of tbmaster logs "
+"and presents an easy-to-read HTML summary of them."
+msgstr ""
+"Скрипта CGI с именем [.filename]#index.cgi#, который читает набор журналов "
+"tbmaster и представляет их в виде удобочитаемой HTML-сводки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:103
+msgid ""
+"A set of build servers that continually test the tip of the most important "
+"FreeBSD code branches."
+msgstr ""
+"Набора серверов сборки, которые постоянно тестируют последние изменения "
+"наиболее важных веток кода FreeBSD."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:104
+msgid ""
+"A webserver that keeps a complete set of Tinderbox logs and displays an up-"
+"to-date summary."
+msgstr ""
+"Веб-сервера, хранящего полный набор журналов Tinderbox и отображающий "
+"актуальную сводку."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:107
+msgid ""
+"The scripts are maintained and were developed by {des}, and are now written "
+"in Perl, a move on from their original incarnation as shell scripts. All "
+"scripts and configuration files are kept in https://www.freebsd.org/cgi/"
+"cvsweb.cgi/projects/tinderbox/[/projects/tinderbox/]."
+msgstr ""
+"Скрипты поддерживаются и были разработаны {des}, и сейчас написаны на Perl, "
+"что стало шагом вперед по сравнению с их первоначальной версией в виде shell-"
+"скриптов. Все скрипты и конфигурационные файлы хранятся в https://"
+"www.freebsd.org/cgi/cvsweb.cgi/projects/tinderbox/[/projects/tinderbox/]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:109
+msgid ""
+"For more information about the tinderbox and tbmaster scripts at this stage, "
+"see their respective man pages: tinderbox(1) and tbmaster(1)."
+msgstr ""
+"Для получения дополнительной информации о скриптах tinderbox и tbmaster на "
+"данном этапе обратитесь к соответствующим руководствам: tinderbox(1) и "
+"tbmaster(1)."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:110
+#, no-wrap
+msgid "The index.cgi Script"
+msgstr "Скрипт index.cgi"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:117
+msgid ""
+"The [.filename]#index.cgi# script generates the HTML summary of tinderbox "
+"and tbmaster logs. Although originally intended to be used as a CGI script, "
+"as indicated by its name, this script can also be run from the command line "
+"or from a man:cron[8] job, in which case it will look for logs in the "
+"directory where the script is located. It will automatically detect "
+"context, generating HTTP headers when it is run as a CGI script. It "
+"conforms to XHTML standards and is styled using CSS."
+msgstr ""
+"Скрипт [.filename]#index.cgi# генерирует HTML-сводку журналов tinderbox и "
+"tbmaster. Хотя изначально он предназначался для использования в качестве CGI-"
+"скрипта, как следует из его названия, этот скрипт также может быть запущен "
+"из командной строки или из задачи man:cron[8], в таком случае он будет "
+"искать логи в директории, где расположен сам скрипт. Он автоматически "
+"определяет контекст, генерируя HTTP-заголовки при запуске в качестве CGI-"
+"скрипта. Он соответствует стандартам XHTML и использует CSS для стилизации."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:120
+msgid ""
+"The script starts in the `main()` block by attempting to verify that it is "
+"running on the official Tinderbox website. If it is not, a page indicating "
+"it is not an official website is produced, and a URL to the official site is "
+"provided."
+msgstr ""
+"Скрипт начинает работу в блоке `main()`, пытаясь проверить, что он "
+"выполняется на официальном сайте Tinderbox. Если это не так, создается "
+"страница с указанием, что это не официальный сайт, и предоставляется URL "
+"официального сайта."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:123
+msgid ""
+"Next, it scans the log directory to get an inventory of configurations, "
+"branches and architectures for which log files exist, to avoid hard-coding a "
+"list into the script and potentially ending up with blank rows or columns. "
+"This information is derived from the names of the log files matching the "
+"following pattern:"
+msgstr ""
+"Далее выполняется сканирование каталога журналов для получения перечня "
+"конфигураций, веток и архитектур, для которых существуют файлы журналов, "
+"чтобы избежать жесткого задания списка в скрипте и потенциального появления "
+"пустых строк или столбцов. Эта информация извлекается из имен файлов "
+"журналов, соответствующих следующему шаблону:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:127
+#, no-wrap
+msgid "tinderbox-$config-$branch-$arch-$machine.{brief,full}\n"
+msgstr "tinderbox-$config-$branch-$arch-$machine.{brief,full}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:131
+msgid ""
+"The configurations used on the official Tinderbox build servers are named "
+"for the branches they build. For example, the `releng_8` configuration is "
+"used to build `RELENG_8` as well as all still-supported release branches."
+msgstr ""
+"Конфигурации, используемые на официальных серверах сборки Tinderbox, названы "
+"в соответствии с ветками, которые они собирают. Например, конфигурация "
+"`releng_8` используется для сборки `RELENG_8`, а также всех поддерживаемых "
+"веток выпусков."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:133
+msgid ""
+"Once all of this startup procedure has been successfully completed, "
+"`do_config()` is called for each configuration."
+msgstr ""
+"После успешного завершения всей процедуры запуска для каждой конфигурации "
+"вызывается `do_config()`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:135
+msgid ""
+"The `do_config()` function generates HTML for a single Tinderbox "
+"configuration."
+msgstr ""
+"Функция `do_config()` генерирует HTML для отдельной конфигурации Tinderbox."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:138
+msgid ""
+"It works by first generating a header row, then iterating over each branch "
+"build with the specified configuration, producing a single row of results "
+"for each in the following manner:"
+msgstr ""
+"Он работает, сначала создавая строку заголовка, затем перебирая каждую "
+"сборку ветки с указанной конфигурацией, формируя одну строку результатов для "
+"каждой следующим образом:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:140
+msgid "For each item:"
+msgstr "Для каждого элемента:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:142
+msgid "For each machine within that architecture:"
+msgstr "Для каждой машины в рамках этой архитектуры:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:144
+msgid "If a brief log file exists, then:"
+msgstr "Если существует краткий файл журнала, то:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:146
+msgid "Call `success()` to determine the outcome of the build."
+msgstr "Вызвать `success()`, чтобы определить результат сборки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:147
+msgid "Output the modification size."
+msgstr "Вывести размер изменения."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:148
+msgid ""
+"Output the size of the brief log file with a link to the log file itself."
+msgstr "Вывести размер краткого файла журнала со ссылкой на сам файл журнала."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:149
+msgid "If a full log file also exists, then:"
+msgstr "Если также существует полный файл журнала, то:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:151
+msgid ""
+"Output the size of the full log file with a link to the log file itself."
+msgstr "Вывести размер полного файла журнала со ссылкой на сам файл журнала."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:153
+msgid "Otherwise:"
+msgstr "В противном случае:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:155
+msgid "No output."
+msgstr "Нечего не выводить."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:157
+msgid ""
+"The `success()` function mentioned above scans a brief log file for the "
+"string \"tinderbox run completed\" in order to determine whether the build "
+"was successful."
+msgstr ""
+"Упомянутая выше функция `success()` проверяет краткий лог-файл на наличие "
+"строки \"tinderbox run completed\", чтобы определить, был ли сборка успешной."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:160
+msgid ""
+"Configurations and branches are sorted according to their branch rank. This "
+"is computed as follows:"
+msgstr ""
+"Конфигурации и ветви сортируются в соответствии с их рангом. Это вычисляется "
+"следующим образом:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:162
+msgid "`HEAD` and `CURRENT` have rank 9999."
+msgstr "`HEAD` и `CURRENT` имеют ранг 9999."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:163
+msgid "`RELENG_x` has rank __``xx``__99."
+msgstr "`RELENG_x` имеет ранг __``xx``__99."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:164
+msgid "`RELENG_x_y` has rank _xxyy_."
+msgstr "`RELENG_x_y` имеет ранг _xxyy_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:167
+msgid ""
+"This means that `HEAD` always ranks highest, and `RELENG` branches are "
+"ranked in numerical order, with each `STABLE` branch ranking higher than the "
+"release branches forked off of it. For instance, for FreeBSD 8, the order "
+"from highest to lowest would be:"
+msgstr ""
+"Это означает, что `HEAD` всегда имеет наивысший приоритет, а ветви `RELENG` "
+"ранжируются в числовом порядке, причём каждая ветвь `STABLE` имеет более "
+"высокий приоритет, чем ветви выпусков, ответвлённые от неё. Например, для "
+"FreeBSD 8 порядок от наивысшего к низшему будет следующим:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:169
+msgid "`RELENG_8` (branch rank 899)."
+msgstr "`RELENG_8` (ранг ветки 899)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:170
+msgid "`RELENG_8_3` (branch rank 803)."
+msgstr "`RELENG_8_3` (ранг ветки 803)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:171
+msgid "`RELENG_8_2` (branch rank 802)."
+msgstr "`RELENG_8_2` (ранг ветки 802)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:172
+msgid "`RELENG_8_1` (branch rank 801)."
+msgstr "`RELENG_8_1` (ранг ветки 801)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:173
+msgid "`RELENG_8_0` (branch rank 800)."
+msgstr "`RELENG_8_0` (ранг ветки 800)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:177
+msgid ""
+"The colors that Tinderbox uses for each cell in the table are defined by "
+"CSS. Successful builds are displayed with green text; unsuccessful builds "
+"are displayed with red text. The color fades as time passes since the "
+"corresponding build, with every half an hour bringing the color closer to "
+"grey."
+msgstr ""
+"Цвета, которые Tinderbox использует для каждой ячейки в таблице, "
+"определяются CSS. Успешные сборки отображаются зелёным текстом; неудачные "
+"сборки отображаются красным текстом. Цвет блекнет со временем с момента "
+"соответствующей сборки, приближаясь к серому каждые полчаса."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:178
+#, no-wrap
+msgid "Official Build Servers"
+msgstr "Официальные серверы сборки"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:181
+msgid ""
+"The official Tinderbox build servers are hosted by http://"
+"www.sentex.ca[Sentex Data Communications], who also host the FreeBSD Netperf "
+"Cluster."
+msgstr ""
+"Официальные серверы сборки Tinderbox размещены на площадке http://"
+"www.sentex.ca[Sentex Data Communications], которая также предоставляет "
+"хостинг для кластера Netperf FreeBSD."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:183
+msgid "Three build servers currently exist:"
+msgstr "В настоящее время работают три сервера сборки:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:185
+msgid "_freebsd-current.sentex.ca_ builds:"
+msgstr "_freebsd-current.sentex.ca_ собирает:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:187
+msgid ""
+"`HEAD` for amd64, arm, i386, i386/pc98, ia64, mips, powerpc, powerpc64, and "
+"sparc64."
+msgstr ""
+"`HEAD` для amd64, arm, i386, i386/pc98, ia64, mips, powerpc, powerpc64 и "
+"sparc64."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:188
+msgid ""
+"`RELENG_9` and supported 9._X_ branches for amd64, arm, i386, i386/pc98, "
+"ia64, mips, powerpc, powerpc64, and sparc64."
+msgstr ""
+"`RELENG_9` и поддерживаемые ветки 9._X_ для amd64, arm, i386, i386/pc98, "
+"ia64, mips, powerpc, powerpc64 и sparc64."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:190
+msgid "_freebsd-stable.sentex.ca_ builds:"
+msgstr "_freebsd-stable.sentex.ca_ собирает:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:192
+msgid ""
+"`RELENG_8` and supported 8._X_ branches for amd64, i386, i386/pc98, ia64, "
+"mips, powerpc and sparc64."
+msgstr ""
+"`RELENG_8` и поддерживаемые ветки 8._X_ для amd64, i386, i386/pc98, ia64, "
+"mips, powerpc и sparc64."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:194
+msgid "_freebsd-legacy.sentex.ca_ builds:"
+msgstr "_freebsd-legacy.sentex.ca_ собирает:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:196
+msgid ""
+"`RELENG_7` and supported 7._X_ branches for amd64, i386, i386/pc98, ia64, "
+"powerpc, and sparc64."
+msgstr ""
+"`RELENG_7` и поддерживаемые ветки 7._X_ для amd64, i386, i386/pc98, ia64, "
+"powerpc и sparc64."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:197
+#, no-wrap
+msgid "Official Summary Site"
+msgstr "Официальный сайт со сводками"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:200
+msgid ""
+"Summaries and logs from the official build servers are available online at "
+"http://tinderbox.FreeBSD.org[http://tinderbox.FreeBSD.org], hosted by {des} "
+"and set up as follows:"
+msgstr ""
+"Сводки и журналы с официальных серверов сборки доступны в сети по адресу "
+"http://tinderbox.FreeBSD.org[http://tinderbox.FreeBSD.org], размещены на "
+"{des} и настроены следующим образом:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:202
+msgid ""
+"A man:cron[8] job checks the build servers at regular intervals and "
+"downloads any new log files using man:rsync[1]."
+msgstr ""
+"Задание man:cron[8] проверяет серверы сборки через регулярные интервалы и "
+"загружает все новые файлы журналов с помощью man:rsync[1]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/testing/_index.adoc:202
+msgid "Apache is set up to use [.filename]#index.cgi# as `DirectoryIndex`."
+msgstr ""
+"Apache настроен на использование [.filename]#index.cgi# в качестве "
+"`DirectoryIndex`."
diff --git a/documentation/content/ru/books/developers-handbook/tools/_index.adoc b/documentation/content/ru/books/developers-handbook/tools/_index.adoc
new file mode 100644
index 0000000000..83dbc7784c
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/tools/_index.adoc
@@ -0,0 +1,1428 @@
+---
+authors:
+ -
+ author: 'James Raynard'
+ -
+ author: 'Murray Stokely'
+description: 'Инструменты разработки'
+next: books/developers-handbook/secure
+params:
+ path: /books/developers-handbook/tools/
+prev: books/developers-handbook/introduction
+showBookMenu: true
+tags: ["tools", "Interpreters", "Compilers", "cc", "make", "Debugging", "lldb", "gdb", "clang", "Emacs"]
+title: 'Глава 2. Инструменты разработки'
+weight: 3
+---
+
+[[tools]]
+= Инструменты разработки
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: 2
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:c-plus-plus-command: c++
+:clang-plus-plus-command: clang++
+:images-path: books/developers-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+[[tools-synopsis]]
+== Обзор
+
+В этой главе представлено введение в использование некоторых инструментов для программирования, поставляемых с FreeBSD, хотя многое из описанного применимо и к другим версиям UNIX(R). Она _не_ претендует на детальное описание процесса написания кода. Большая часть главы предполагает наличие минимальных или отсутствие знаний в программировании, хотя предполагается, что даже опытные программисты найдут в ней что-то полезное.
+
+[[tools-intro]]
+== Введение
+
+FreeBSD предоставляет отличную среду разработки. Компиляторы для C и C++, а также ассемблер входят в базовую систему, не говоря уже о классических инструментах UNIX(R), таких как `sed` и `awk`. Если этого недостаточно, в коллекции Ports доступно множество других компиляторов и интерпретаторов. В следующем разделе, crossref:tools[tools-programming,Введение в программирование], перечислены некоторые из доступных вариантов. FreeBSD обладает высокой совместимостью со стандартами, такими как POSIX(R) и ANSI C, а также с собственным наследием BSD, что позволяет создавать приложения, которые будут компилироваться и запускаться с минимальными изменениями или без них на широком спектре платформ.
+
+Однако вся эта мощь может поначалу ошеломить, если вы никогда раньше не писали программы на платформе UNIX(R). Этот документ призван помочь вам начать работу, не углубляясь слишком сильно в более сложные темы. Цель заключается в том, чтобы дать вам достаточно базовых знаний для понимания документации.
+
+Большая часть документа не требует или почти не требует знаний программирования, хотя предполагает базовые навыки работы с UNIX(R) и готовность учиться!
+
+[[tools-programming]]
+== Введение в программирование
+
+Программа — это набор инструкций, которые указывают компьютеру выполнять различные действия; иногда выполняемая инструкция зависит от результата предыдущей. В этом разделе представлен обзор двух основных способов передачи таких инструкций, или, как их обычно называют, «команд». Один способ использует _интерпретатор_, другой — _компилятор_. Поскольку человеческие языки слишком сложны для однозначного понимания компьютером, команды обычно записываются на одном из специально разработанных для этого языков.
+
+=== Интерпретаторы
+
+С интерпретатором язык поставляется как среда, в которой вы вводите команды в приглашении, и среда выполняет их для вас. Для более сложных программ вы можете ввести команды в файл и заставить интерпретатор загрузить файл и выполнить команды в нём. Если что-то пойдёт не так, многие интерпретаторы переведут вас в отладчик, чтобы помочь найти проблему.
+
+Преимущество этого подхода в том, что вы сразу видите результаты выполнения команд, а ошибки можно легко исправить. Самый большой недостаток проявляется, когда вы хотите поделиться своими программами с кем-то. У них должен быть такой же интерпретатор, или у вас должен быть способ предоставить его, и они должны понимать, как им пользоваться. Кроме того, пользователям может не понравиться, если они попадут в отладчик при нажатии не той клавиши! С точки зрения производительности интерпретаторы могут потреблять много памяти и обычно генерируют код менее эффективно, чем компиляторы.
+
+По моему мнению, интерпретируемые языки — это лучший способ начать, если вы раньше не занимались программированием. Такая среда обычно встречается в языках вроде Lisp, Smalltalk, Perl и Basic. Можно также утверждать, что UNIX(R) shell (`sh`, `csh`) сам по себе является интерпретатором, и многие часто пишут shell-«скрипты» для помощи в различных «хозяйственных» задачах на своих машинах. Действительно, часть оригинальной философии UNIX(R) заключалась в предоставлении множества небольших утилит, которые можно было связывать вместе в shell-скриптах для выполнения полезных задач.
+
+=== Доступные интерпретаторы в FreeBSD
+
+Вот список интерпретаторов, доступных в Коллекции портов FreeBSD, с кратким обзором некоторых наиболее популярных интерпретируемых языков.
+
+Инструкции по получению и установке приложений из Коллекции портов можно найти в extref:{handbook}[разделе Порты, ports-using] руководства.
+
+BASIC::
+Сокращение от Beginner's All-purpose Symbolic Instruction Code. Разработан в 1950-х годах для обучения студентов университетов программированию и поставлялся с каждым уважающим себя персональным компьютером в 1980-х. BASIC — первый язык программирования для многих программистов. Он также является основой для Visual Basic.
++
+Интерпретатор Bywater Basic можно найти в Коллекции портов как package:lang/bwbasic[], а интерпретатор Phil Cockroft's Basic (ранее известный как Rabbit Basic) доступен как package:lang/pbasic[].
+
+Lisp::
+Язык, разработанный в конце 1950-х годов как альтернатива популярным в то время языкам для «численных расчётов». В отличие от них, Lisp основан на списках; фактически, название является сокращением от «List Processing» (обработка списков). Он очень популярен в кругах, связанных с ИИ (искусственным интеллектом).
++
+Lisp — это чрезвычайно мощный и сложный язык, но он может показаться довольно большим и громоздким.
++
+В Коллекции портов FreeBSD доступны различные реализации Lisp, которые могут работать в системах UNIX(R). CLISP от Bruno Haible и Michael Stoll доступен как package:lang/clisp[]. Более простая реализация Lisp, SLisp, доступна как package:lang/slisp[].
+
+Perl::
+Очень популярен среди системных администраторов для написания скриптов; также часто используется на веб-серверах для написания CGI-скриптов.
++
+Perl доступен в Коллекции портов как package:lang/perl5.36[] для всех выпусков FreeBSD.
+
+Scheme::
+Диалект Lisp, который более компактен и чист по сравнению с Common Lisp. Популярен в университетах, так как достаточно прост для обучения студентов в качестве первого языка, и при этом обладает достаточным уровнем абстракции для использования в исследовательской работе.
++
+Схема доступна из Коллекции Портов как package:lang/elk[] для Интерпретатора Elk Scheme. Интерпретатор MIT Scheme можно найти в package:lang/mit-scheme[], а Интерпретатор SCM Scheme — в package:lang/scm[].
+
+Lua::
+Lua — это легковесный встраиваемый язык сценариев. Он обладает высокой переносимостью и относительно прост. Lua доступен в коллекции портов в пакете package:lang/lua54[]. Он также включен в базовую систему как [.filename]#/usr/libexec/flua# для использования компонентами базовой системы. Стороннее программное обеспечение не должно зависеть от [.filename]#flua#.
+
+Python::
+Python — это объектно-ориентированный интерпретируемый язык. Его сторонники утверждают, что это один из лучших языков для начала программирования, поскольку он относительно прост в освоении, но не уступает другим популярным интерпретируемым языкам, используемым для разработки крупных и сложных приложений (Perl и Tcl — два других языка, популярных для таких задач).
++
+Последняя версия Python доступна в Коллекции портов в пакете package:lang/python[].
+
+Ruby::
+Ruby — это интерпретируемый, чисто объектно-ориентированный язык программирования. Он получил широкую популярность благодаря простому для понимания синтаксису, гибкости при написании кода и возможности легко разрабатывать и поддерживать большие, сложные программы.
++
+Ruby доступен в Коллекции портов как package:lang/ruby32[].
+
+Tcl и Tk::
+Tcl — это встраиваемый интерпретируемый язык, который получил широкое распространение и популярность в основном благодаря своей переносимости на множество платформ. Он может использоваться как для быстрого написания небольших прототипов приложений, так и (в сочетании с Tk, набором инструментов для графического интерфейса) полноценных программ с богатым функционалом.
++
+Различные версии Tcl доступны в качестве портов для FreeBSD. Последняя версия, Tcl 8.7, находится в пакете package:lang/tcl87[].
+
+=== Компиляторы
+
+Компиляторы довольно сильно различаются. Прежде всего, вы пишете свой код в файле (или файлах) с помощью редактора. Затем вы запускаете компилятор и проверяете, принимает ли он вашу программу. Если программа не скомпилировалась, стисните зубы и вернитесь к редактору; если же компиляция прошла успешно и программа была создана, вы можете запустить её либо в командной строке оболочки, либо в отладчике, чтобы проверить её работу.footnote:[Если вы запустите её в оболочке, может произойти дамп памяти.]
+
+Очевидно, это требует больше усилий по сравнению с использованием интерпретатора. Однако это позволяет делать множество вещей, которые очень сложны или даже невозможны с интерпретатором, например, писать код, тесно взаимодействующий с операционной системой — или даже создавать собственную операционную систему! Это также полезно, если требуется написать очень эффективный код, так как компилятор может не спешить и оптимизировать код, что было бы неприемлемо в интерпретаторе. Более того, распространение программы, написанной для компилятора, обычно проще, чем для интерпретатора — можно просто предоставить копию исполняемого файла, предполагая, что у пользователя та же операционная система, что и у вас.
+
+Поскольку цикл редактирования-компиляции-запуска-отладки довольно утомителен при использовании отдельных программ, многие производители коммерческих компиляторов создали интегрированные среды разработки (сокращённо IDE). FreeBSD не включает IDE в базовую систему, но в Коллекции портов доступен package:devel/kdevelop[], и многие используют для этой цели Emacs. Использование Emacs в качестве IDE обсуждается в crossref:tools[emacs, Использование Emacs как среды разработки].
+
+[[tools-compiling]]
+== Компиляция с помощью `cc`
+
+Этот раздел посвящён компилятору clang для языков C и C++, так как он устанавливается вместе с базовой системой FreeBSD. Clang устанавливается как `cc`; пакет GNU-компилятора package:lang/gcc[gcc] доступен в Коллекции портов. Детали создания программы с интерпретатором значительно различаются в зависимости от интерпретатора и обычно хорошо описаны в документации и онлайн-справке интерпретатора.
+
+Как только вы напишете свой шедевр, следующий шаг — преобразовать его во что-то, что (надеюсь!) будет работать на FreeBSD. Обычно это включает несколько шагов, каждый из которых выполняется отдельной программой.
+
+[.procedure]
+. Обработь исходный код, чтобы удалить комментарии и выполнить другие действия, такие как раскрытие макросов в C.
+. Проверить синтаксис вашего кода, чтобы убедиться, что вы соблюдаете правила языка. Если нет, он пожалуется!
+. Преобразовать исходный код в ассемблерный язык — это очень близко к машинному коду, но всё ещё понятно человеку. Как утверждается.
+. Преобразовать язык ассемблера в машинный код — да, здесь речь идет о битах и байтах, единицах и нулях.
+. Проверить, что вы использовали такие элементы, как функции и глобальные переменные, правильно и последовательно. Например, если вы вызвали несуществующую функцию, это будет отмечено.
+. Если вы пытаетесь создать исполняемый файл из нескольких исходных файлов, определить, как объединить их все вместе.
+. Определить, как создать что-то, что загрузчик времени выполнения системы сможет загрузить в память и запустить.
+. Наконец, записать исполняемый файл в файловую систему.
+
+Слово _компиляция_ часто относится только к шагам с 1 по 4, а остальные шаги называются _линковкой_. Иногда шаг 1 называют _препроцессированием_, а шаги 3-4 — _ассемблированием_.
+
+К счастью, почти все эти детали скрыты от вас, так как `cc` — это интерфейс, который управляет вызовом всех этих программ с правильными аргументами за вас; достаточно просто набрать
+
+[source, bash]
+....
+% cc foobar.c
+....
+
+и это вызовет компиляцию файла [.filename]#foobar.c# всеми перечисленными выше шагами. Если у вас несколько файлов для компиляции, просто сделайте что-то вроде
+
+[source, bash]
+....
+% cc foo.c bar.c
+....
+
+Обратите внимание, что проверка синтаксиса — это всего лишь проверка синтаксиса. Она не выявит логических ошибок, которые вы могли допустить, например, создание бесконечного цикла или использование пузырьковой сортировки вместо бинарной.footnote:[На случай, если вы не знали: бинарная сортировка — это эффективный способ упорядочивания элементов, в отличие от пузырьковой.]
+
+Существует множество опций для `cc`, все они описаны в руководстве. Вот несколько наиболее важных из них с примерами использования.
+
+`-o _filename_`::
+Имя выходного файла. Если вы не используете эту опцию, `cc` создаст исполняемый файл с именем [.filename]#a.out#.footnote:[Причины этого кроются в глубинах истории.]
++
+[source, bash]
+....
+% cc foobar.c executable is a.out
+% cc -o foobar foobar.c executable is foobar
+....
+
+`-c`::
+Просто скомпилирует файл, не связывая его. Полезно для небольших программ, где нужно только проверить синтаксис, или если вы используете [.filename]#Makefile#.
++
+[source, bash]
+....
+% cc -c foobar.c
+....
++
+Это создаст _объектный файл_ (не исполняемый) с именем [.filename]#foobar.o#. Его можно скомпоновать с другими объектными файлами в исполняемый файл.
+
+`-g`::
+Создать отладочную версию исполняемого файла. Это заставляет компилятор записывать в исполняемый файл информацию о том, какая строка какого исходного файла соответствует какому вызову функции. Отладчик может использовать эту информацию для отображения исходного кода при пошаговом выполнении программы, что _очень_ полезно; недостатком является то, что вся эта дополнительная информация значительно увеличивает размер программы. Обычно вы компилируете с `-g` во время разработки программы, а затем компилируете "релизную версию" без `-g`, когда убедитесь, что она работает правильно.
++
+
+[source, bash]
+....
+% cc -g foobar.c
+....
++
+Это создаст отладочную версию программы. footnote:[Примечание: мы не использовали флаг -o для указания имени исполняемого файла, поэтому получим исполняемый файл с именем a.out. Создание отладочной версии с именем foobar остается упражнением для читателя!]
+
+`-O`::
+Создает оптимизированную версию исполняемого файла. Компилятор применяет различные хитрые приёмы, чтобы попытаться создать исполняемый файл, который работает быстрее обычного. Вы можете добавить число после `-O`, чтобы указать более высокий уровень оптимизации, но это часто выявляет ошибки в оптимизаторе компилятора.
++
+[source, bash]
+....
+% cc -O -o foobar foobar.c
+....
++
+Это создаст оптимизированную версию [.filename]#foobar#.
+
+Следующие три флага заставят `cc` проверять, что ваш код соответствует соответствующему международному стандарту, часто называемому стандартом ANSI, хотя строго говоря, это стандарт ISO.
+
+`-Wall`::
+Включить все предупреждения, которые разработчики `cc` считают полезными. Несмотря на название, это не включит все предупреждения, которые `cc` способен выдавать.
+
+`-ansi`::
+Отключит большинство, но не все, не-ANSI C функции, предоставляемые `cc`. Несмотря на название, это не гарантирует строгого соответствия вашего кода стандарту.
+
+`-pedantic`::
+Отключит _все_ не-ANSI C возможности ``cc``.
+
+Без этих флагов `cc` позволит вам использовать некоторые из своих нестандартных расширений стандарта. Некоторые из них очень полезны, но не будут работать с другими компиляторами — фактически, одна из основных целей стандарта заключается в том, чтобы позволить людям писать код, который будет работать с любым компилятором на любой системе. Это известно как _переносимый код_.
+
+Обычно следует стремиться к тому, чтобы ваш код был как можно более переносимым, иначе позже вам, возможно, придётся полностью переписать программу для её работы в другом месте — а кто знает, что вы будете использовать через несколько лет?
+
+[source, bash]
+....
+% cc -Wall -ansi -pedantic -o foobar foobar.c
+....
+
+В результате будет создан исполняемый файл [.filename]#foobar# после проверки [.filename]#foobar.c# на соответствие стандартам.
+
+`-l__library__`::
+Укажите библиотеку функций, которая будет использоваться во время компоновки.
++
+Наиболее распространённый пример этого — компиляция программы, использующей некоторые математические функции в C. В отличие от большинства других платформ, они находятся в отдельной библиотеке, отличной от стандартной библиотеки C, и необходимо указать компилятору добавить её.
++
+Правило заключается в том, что если библиотека называется [.filename]#libsomething.a#, то вы передаёте `cc` аргумент `-l__something__`. Например, математическая библиотека называется [.filename]#libm.a#, поэтому вы передаёте `cc` аргумент `-lm`. Типичный подводный камень с математической библиотекой заключается в том, что она должна быть последней библиотекой в командной строке.
++
+[source, bash]
+....
+% cc -o foobar foobar.c -lm
+....
++
+Это приведёт к подключению функций математической библиотеки в [.filename]#foobar#.
++
+Если вы компилируете код на C++, используйте {c-plus-plus-command}. {c-plus-plus-command} также может быть вызван как {clang-plus-plus-command} в FreeBSD.
++
+[source, bash]
+....
+% c++ -o foobar foobar.cc
+....
++
+Это создаст исполняемый файл [.filename]#foobar# из исходного файла на C++
+[.filename]#foobar.cc#.
+
+=== Распространённые вопросы и проблемы `cc`
+
+==== Я скомпилировал файл с именем foobar.c и не могу найти исполняемый файл с именем foobar. Куда он пропал?
+
+Помните, что `cc` вызовет исполняемый файл [.filename]#a.out#, если вы не укажете иное. Используйте опцию `-o _имя_файла_`:
+
+[source, bash]
+....
+% cc -o foobar foobar.c
+....
+
+==== Хорошо, у меня есть исполняемый файл с именем foobar, я вижу его при выполнении команды ls, но когда я ввожу foobar в командной строке, система сообщает, что такого файла нет. Почему он не может его найти?
+
+В отличие от MS-DOS(R), UNIX(R) не ищет в текущем каталоге, когда пытается определить, какую программу нужно запустить, если вы явно не укажете это. Введите `./foobar`, что означает "запустить файл с именем [.filename]#foobar# в текущем каталоге."
+
+=== Я назвал свой исполняемый файл test, но при запуске ничего не происходит. В чем дело?
+
+Большинство UNIX(R) систем имеют программу под названием `test` в [.filename]#/usr/bin#, и оболочка выбирает её, прежде чем проверить текущий каталог. Введите следующее:
+
+[source, bash]
+....
+% ./test
+....
+
+или выберите более подходящее название для вашей программы!
+
+==== Я скомпилировал свою программу, и сначала она работала нормально, но потом произошла ошибка, и было сообщение о core dumped. Что это значит?
+
+Название _core dump_ восходит к самым ранним дням UNIX(R), когда машины использовали ферритовую память для хранения данных. По сути, если программа завершалась сбоем при определённых условиях, система записывала содержимое ферритовой памяти на диск в файл с именем [.filename]#core#, который программист затем мог изучить, чтобы выяснить причину ошибки.
+
+==== Увлекательный материал, но что мне теперь делать?
+
+Используйте отладчик для анализа образа памяти (см. crossref:tools[debugging, Отладка]).
+
+==== Когда моя программа сбросила core, она сообщила что-то о segmentation fault. Что это?
+
+Это означает, что ваша программа попыталась выполнить какую-то недопустимую операцию с памятью; UNIX(R) разработана для защиты операционной системы и других программ от некорректно работающих программ.
+
+Распространенные причины этого:
+
+* Попытка записи в NULL-указатель, например:
++
+[.programlisting]
+....
+char *foo = NULL;
+strcpy(foo, "bang!");
+....
+
+* Использование неинициализированного указателя, например:
++
+[.programlisting]
+....
+char *foo;
+strcpy(foo, "bang!");
+....
++
+Указатель будет иметь случайное значение, которое, возможно, укажет на область памяти, недоступную вашей программе, и ядро завершит вашу программу до того, как она сможет нанести какой-либо ущерб. Если вам не повезет, он укажет внутрь вашей собственной программы и повредит одну из структур данных, что приведет к загадочному сбою программы.
+* Попытка доступа за пределы массива, например
++
+[.programlisting]
+....
+int bar[20];
+bar[27] = 6;
+....
+
+* Попытка сохранить что-то в память только для чтения, например
++
+[.programlisting]
+....
+char *foo = "My string";
+strcpy(foo, "bang!");
+....
++
+Версии UNIX(R) компиляторы часто помещают строковые литералы, такие как `"Моя строка"`, в области памяти только для чтения.
+* Выполнение нежелательных действий с `malloc()` и `free()`, например
++
+[.programlisting]
+....
+char bar[80];
+free(bar);
+....
++
+или
++
+[.programlisting]
+....
+char *foo = malloc(27);
+free(foo);
+free(foo);
+....
+
+Совершение одной из этих ошибок не всегда приведет к сбою, но это всегда плохая практика. Некоторые системы и компиляторы более терпимы, чем другие, поэтому программы, которые хорошо работают на одной системе, могут аварийно завершаться при попытке запустить их на другой.
+
+==== Иногда при получении дампа памяти я вижу сообщение ошибки шины (bus error). В моей книге по UNIX(R) сказано, что это означает аппаратную проблему, но компьютер продолжает работать. Это правда?
+
+Нет, к счастью, нет (если, конечно, у вас действительно нет аппаратной проблемы...). Обычно это означает, что вы обратились к памяти способом, который не следует использовать.
+
+==== Этот процесс создания дампа памяти звучит довольно полезно, если я могу запускать его по своему желанию. Могу ли я это сделать, или нужно ждать возникновения ошибки?
+
+Можете. Просто перейдите на другую консоль или xterm, выполните
+
+[source, bash]
+....
+% ps
+....
+
+чтобы узнать идентификатор процесса вашей программы и выполните
+
+[source, bash]
+....
+% kill -ABRT pid
+....
+
+где `_pid_` — идентификатор процесса, который вы нашли.
+
+Это полезно, если ваша программа зависла в бесконечном цикле, например. Если ваша программа перехватывает SIGABRT, есть несколько других сигналов, которые оказывают аналогичный эффект.
+
+В качестве альтернативы, вы можете создать дамп памяти изнутри вашей программы, вызвав функцию `abort()`. Дополнительную информацию можно найти на man:abort[3].
+
+Если вы хотите создать дамп памяти извне вашей программы, но не хотите завершать процесс, вы можете использовать программу `gcore`. Подробнее см. на странице руководства man:gcore[1].
+
+[[tools-make]]
+== Make
+
+=== Что такое `make`?
+
+Когда вы работаете над простой программой с одним или двумя исходными файлами, вводя
+
+[source, bash]
+....
+% cc file1.c file2.c
+....
+
+это не слишком плохо, но быстро становится очень утомительным, когда есть несколько файлов — и компиляция тоже может занять время.
+
+Один из способов обойти это — использовать объектные файлы и перекомпилировать исходный файл только в случае изменения исходного кода. Таким образом, у нас может получиться что-то вроде:
+
+[source, bash]
+....
+% cc file1.o file2.o … file37.c …
+....
+
+если бы мы изменили файл [.filename]#file37.c#, но не трогали остальные с момента последней компиляции. Это может значительно ускорить компиляцию, но не решает проблему с вводом.
+
+Или мы могли бы написать shell-скрипт для решения проблемы с вводом, но тогда пришлось бы перекомпилировать всё, что сделало бы его очень неэффективным для крупного проекта.
+
+Что произойдет, если у нас есть сотни исходных файлов? Что, если мы работаем в команде с другими людьми, которые забывают сообщить нам, когда они изменили один из своих исходных файлов, которые мы используем?
+
+Возможно, мы могли бы объединить два решения и написать что-то вроде shell-скрипта, который содержал бы какое-то волшебное правило, указывающее, когда исходный файл нужно компилировать. Теперь нам осталось только найти программу, которая сможет понимать эти правила, так как для shell это немного слишком сложно.
+
+Эта программа называется `make`. Она читает файл, называемый _makefile_, который указывает, как различные файлы зависят друг от друга, и определяет, какие файлы нужно перекомпилировать, а какие нет. Например, правило может звучать так: «если [.filename]#fromboz.o# старше, чем [.filename]#fromboz.c#, значит, кто-то изменил [.filename]#fromboz.c#, и его нужно перекомпилировать». В makefile также содержатся правила, указывающие make, _как_ именно перекомпилировать исходный файл, что делает эту программу гораздо более мощным инструментом.
+
+Файлы Makefile обычно хранятся в том же каталоге, что и исходный код, к которому они применяются, и могут называться [.filename]#makefile#, [.filename]#Makefile# или [.filename]#MAKEFILE#. Большинство программистов используют имя [.filename]#Makefile#, так как это помещает его в начало списка файлов в каталоге, где его легко заметить.footnote:[Они не используют форму MAKEFILE, так как заглавные буквы часто применяются для файлов документации, таких как README.]
+
+=== Пример использования `make`
+
+Вот очень простой файл для make:
+
+[.programlisting]
+....
+foo: foo.c
+ cc -o foo foo.c
+....
+
+Он состоит из двух строк: строки зависимости и строки создания.
+
+Строка зависимости здесь состоит из имени программы (известного как _цель_), за которым следует двоеточие, пробел и имя исходного файла. Когда `make` читает эту строку, он проверяет, существует ли файл [.filename]#foo#; если он существует, программа сравнивает время последнего изменения файла [.filename]#foo# с временем последнего изменения файла [.filename]#foo.c#. Если файл [.filename]#foo# не существует или старше файла [.filename]#foo.c#, программа смотрит на строку создания, чтобы выяснить, что делать. Другими словами, это правило для определения, когда файл [.filename]#foo.c# нужно перекомпилировать.
+
+Строка создания начинается с табуляции (нажмите kbd:[tab]), а затем следует команда, которую вы бы ввели для создания [.filename]#foo#, если бы делали это в командной строке. Если [.filename]#foo# устарел или не существует, `make` выполняет эту команду для его создания. Другими словами, это правило, которое сообщает make, как перекомпилировать [.filename]#foo.c#.
+
+Таким образом, при вводе команды `make` система обеспечит актуальность файла [.filename]#foo# относительно последних изменений в [.filename]#foo.c#. Этот принцип можно распространить на [.filename]#Makefile#, содержащие сотни целей — фактически, в FreeBSD можно собрать всю операционную систему, просто введя `make buildworld buildkernel` в корневом каталоге дерева исходных кодов (src).
+
+Еще одно полезное свойство makefile заключается в том, что цели не обязательно должны быть программами. Например, у нас может быть makefile, который выглядит так:
+
+[.programlisting]
+....
+foo: foo.c
+ cc -o foo foo.c
+
+install:
+ cp foo /home/me
+....
+
+Мы можем указать make, какую цель мы хотим собрать, набрав:
+
+[source, bash]
+....
+% make target
+....
+
+`make` будет рассматривать только указанную цель и игнорировать все остальные. Например, если мы введём `make foo` с указанным выше makefile, make проигнорирует цель `install`.
+
+Если мы просто введем `make` без параметров, make всегда будет обращаться к первой цели и затем остановится, не рассматривая остальные. Поэтому если мы введем `make` здесь, он просто перейдет к цели `foo`, перекомпилирует [.filename]#foo# при необходимости и затем остановится, не переходя к цели `install`.
+
+Обратите внимание, что цель `install` не зависит ни от чего! Это означает, что команда в следующей строке всегда выполняется при попытке создать эту цель с помощью команды `make install`. В данном случае она скопирует [.filename]#foo# в домашний каталог пользователя. Это часто используется в makefile приложений, чтобы приложение можно было установить в правильный каталог после успешной компиляции.
+
+Это немного запутанная тема для объяснения. Если вы не до конца понимаете, как работает `make`, лучше всего написать простую программу, например, "hello world", и make-файл, как указано выше, и поэкспериментировать. Затем можно перейти к использованию нескольких исходных файлов или добавлению заголовочного файла в исходный код. В этом случае очень полезен `touch` — он изменяет дату файла без необходимости его редактирования.
+
+=== make и include-файлы
+
+Код на C часто начинается со списка подключаемых файлов, например stdio.h. Некоторые из этих файлов являются системными, а некоторые принадлежат текущему проекту:
+
+[.programlisting]
+....
+#include <stdio.h>
+#include "foo.h"
+
+int main(....
+....
+
+Чтобы убедиться, что этот файл перекомпилируется при изменении [.filename]#foo.h#, необходимо добавить его в [.filename]#Makefile#:
+
+[.programlisting]
+....
+foo: foo.c foo.h
+....
+
+В момент, когда ваш проект становится больше и у вас появляется все больше собственных включаемых файлов для поддержки, отслеживание всех включаемых файлов и файлов, которые от них зависят, становится проблемой. Если вы измените включаемый файл, но забудете перекомпилировать все файлы, которые от него зависят, последствия будут катастрофическими. У `clang` есть опция для анализа ваших файлов и создания списка включаемых файлов и их зависимостей: `-MM`.
+
+Если вы добавите это в ваш Makefile:
+
+[.programlisting]
+....
+depend:
+ cc -E -MM *.c > .depend
+....
+
+и выполните `make depend`, появится файл [.filename]#.depend# со списком объектных файлов, C-файлов и включаемых файлов:
+
+[.programlisting]
+....
+foo.o: foo.c foo.h
+....
+
+Если вы измените файл [.filename]#foo.h#, при следующем запуске `make` все файлы, зависящие от [.filename]#foo.h#, будут перекомпилированы.
+
+Не забудьте выполнить `make depend` каждый раз, когда вы добавляете include-файл в один из своих файлов.
+
+=== Файлы Makefile системы FreeBSD
+
+Makefile-ы могут быть довольно сложными для написания. К счастью, в BSD-системах, таких как FreeBSD, есть очень мощные Makefile-ы, поставляемые в составе системы. Отличным примером этого является система портов FreeBSD. Вот основная часть типичного [.filename]#Makefile# для портов:
+
+[.programlisting]
+....
+MASTER_SITES= ftp://freefall.cdrom.com/pub/FreeBSD/LOCAL_PORTS/
+DISTFILES= scheme-microcode+dist-7.3-freebsd.tgz
+
+.include <bsd.port.mk>
+....
+
+Теперь, если мы перейдем в каталог этого порта и наберем `make`, произойдет следующее:
+
+[.procedure]
+. Проверяется, есть ли исходный код этого порта уже в системе.
+. Если это не так, устанавливается FTP-соединение с URL в MASTER_SITES для загрузки исходного кода.
+. Контрольная сумма исходного кода вычисляется и сравнивается с контрольной суммой известной и хорошей копии исходного кода. Это делается для того, чтобы убедиться, что исходный код не был поврежден во время передачи.
+. Все необходимые изменения для адаптации исходного кода к работе в FreeBSD применяются — это называется применением _патча_.
+. Любая необходимая специальная настройка для исходного кода выполнена. (Многие дистрибутивы программ UNIX(R) пытаются определить, на какой версии UNIX(R) они компилируются и какие дополнительные функции UNIX(R) доступны — именно здесь они получают эту информацию в сценарии портов FreeBSD).
+. Компилируется исходный код программы. По сути, мы переходим в каталог, куда были распакованы исходные файлы, и выполняем `make` — собственный make-файл программы содержит необходимую информацию для сборки программы.
+. Теперь у нас есть скомпилированная версия программы. При желании мы можем протестировать её сейчас; когда мы уверены в программе, можно ввести `make install`. Это приведёт к копированию программы и всех необходимых вспомогательных файлов в нужные места, а также к добавлению записи в `базу данных пакетов`, чтобы позже можно было легко удалить порт, если мы передумаем.
+
+Вот теперь, я думаю, вы согласитесь, что это довольно впечатляюще для скрипта из четырёх строк!
+
+Секрет кроется в последней строке, которая указывает `make` обратиться к системному makefile под названием [.filename]#bsd.port.mk#. Эту строку легко пропустить, но именно здесь начинается вся магия — кто-то написал makefile, который предписывает `make` выполнить все вышеперечисленные действия (плюс несколько других, которые я не упомянул, включая обработку возможных ошибок), и любой может получить доступ к этому функционалу, просто добавив одну строку в свой собственный makefile!
+
+Если вы хотите взглянуть на эти системные makefile-ы, они находятся в [.filename]#/usr/share/mk#, но, вероятно, лучше подождать, пока у вас не появится немного практики с makefile, так как они очень сложные (и если вы всё же решите их посмотреть, убедитесь, что у вас под рукой есть фляга крепкого кофе!)
+
+=== Более сложные способы использования `make`
+
+`Make` — это очень мощный инструмент, способный на гораздо большее, чем показано в простом примере выше. К сожалению, существует несколько различных версий `make`, и все они значительно отличаются друг от друга. Лучший способ узнать, на что они способны, — вероятно, прочитать документацию. Надеюсь, это введение дало вам основу, с которой вы сможете это сделать. В man:make[1] подробно обсуждаются переменные, аргументы и то, как использовать `make`.
+
+Многие приложения в портах используют GNU make, который имеет очень хороший набор страниц "info". Если вы установили любой из этих портов, GNU make будет автоматически установлен как `gmake`. Он также доступен как отдельный порт и пакет.
+
+Для просмотра справочных страниц (info) GNU make вам потребуется отредактировать файл [.filename]#dir# в каталоге [.filename]#/usr/local/info#, добавив соответствующую запись. Добавьте строку
+
+[.programlisting]
+....
+ * Make: (make). The GNU Make utility.
+....
+
+в файл. После этого вы можете ввести `info` и затем выбрать [.guimenuitem]#make# из меню (или в Emacs выполнить `C-h i`).
+
+[[debugging]]
+== Отладка
+
+=== Обзор отладчиков, поставляемых в системе
+
+Использование отладчика позволяет запускать программу в более контролируемых условиях. Обычно можно выполнять программу построчно, проверять значения переменных, изменять их, указывать отладчику выполнение до определённой точки и затем останавливаться и так далее. Также можно подключиться к уже работающей программе или загрузить core-файл, чтобы исследовать причину аварийного завершения программы.
+
+Этот раздел представляет собой краткое введение в использование отладчиков и не затрагивает специализированные темы, такие как отладка ядра. Для получения дополнительной информации по этой теме обратитесь к главе crossref:kerneldebug[kerneldebug,Отладка ядра].
+
+Стандартный отладчик, поставляемый с FreeBSD, называется `lldb` (LLVM debugger). Поскольку он является частью стандартной установки для данного выпуска, нет необходимости выполнять какие-либо дополнительные действия для его использования. Он обладает хорошей справкой по командам, доступной через команду `help`, а также https://lldb.llvm.org/[руководством и документацией в интернете].
+
+[NOTE]
+====
+Команда `lldb` также доступна extref:{handbook}ports/[из портов или пакетов, ports-using] как пакет package:devel/llvm[].
+====
+
+Другой отладчик, доступный в FreeBSD, называется `gdb` (GNU debugger). В отличие от lldb, он не устанавливается по умолчанию в FreeBSD; для его использования необходимо extref:{handbook}#ports-using/[установить] пакет package:devel/gdb[] из портов или пакетов. Он обладает отличной встроенной справкой, а также набором info-страниц.
+
+Два отладчика обладают схожим набором функций, поэтому выбор между ними в основном зависит от личных предпочтений. Если вы знакомы только с одним из них, используйте его. Тем, кто не знаком ни с одним или знаком с обоими, но хочет использовать отладчик внутри Emacs, придётся выбрать `gdb`, так как `lldb` не поддерживается Emacs. В остальных случаях попробуйте оба и решите, какой вам больше нравится.
+
+=== Использование lldb
+
+==== Запуск lldb
+
+Запустите lldb, набрав
+
+[source, bash]
+....
+% lldb -- progname
+....
+
+==== Запуск программы с lldb
+
+Скомпилируйте программу с `-g`, чтобы максимально использовать возможности `lldb`. Без этого флаг она будет работать, но отображать только имя текущей выполняемой функции вместо исходного кода. Если отображается строка вида:
+
+[source, bash]
+....
+Breakpoint 1: where = temp`main, address = …
+....
+
+(без указания имени файла исходного кода и номера строки) при установке точки останова это означает, что программа не была скомпилирована с параметром `-g`.
+
+[TIP]
+====
+Большинство команд `lldb` имеют более короткие формы, которые можно использовать вместо полных. Здесь используются полные формы для ясности.
+====
+
+На строке `lldb` введите `breakpoint set -n main`. Это укажет отладчику не показывать предварительный код настройки в запускаемой программе и остановить выполнение в начале кода программы. Теперь введите `process launch`, чтобы фактически запустить программу — она начнётся с кода настройки, а затем будет остановлена отладчиком при вызове `main()`.
+
+Для пошагового выполнения программы строка за строкой введите `thread step-over`. Когда программа дойдёт до вызова функции, войдите в неё, набрав `thread step-in`. Оказавшись внутри вызова функции, вернитесь из него с помощью команды `thread step-out` или используйте `up` и `down`, чтобы быстро посмотреть на вызывающий код.
+
+Вот простой пример того, как найти ошибку в программе с помощью `lldb`. Это наша программа (с умышленной ошибкой):
+
+[.programlisting]
+....
+#include <stdio.h>
+
+int bazz(int anint);
+
+main() {
+ int i;
+
+ printf("This is my program\n");
+ bazz(i);
+ return 0;
+}
+
+int bazz(int anint) {
+ printf("You gave me %d\n", anint);
+ return anint;
+}
+....
+
+Эта программа устанавливает значение `i` равным `5` и передает его в функцию `bazz()`, которая выводит переданное число.
+
+Компиляция и запуск программы отображают
+
+[source, bash]
+....
+% cc -g -o temp temp.c
+% ./temp
+This is my program
+anint = -5360
+....
+
+Это не то, что ожидалось! Пора разобраться, что происходит!
+
+[source, bash]
+....
+% lldb -- temp
+(lldb) target create "temp"
+Current executable set to 'temp' (x86_64).
+(lldb) breakpoint set -n main Skip the set-up code
+Breakpoint 1: where = temp`main + 15 at temp.c:8:2, address = 0x00000000002012ef lldb puts breakpoint at main()
+(lldb) process launch Run as far as main()
+Process 9992 launching
+Process 9992 launched: '/home/pauamma/tmp/temp' (x86_64) Program starts running
+
+Process 9992 stopped
+* thread #1, name = 'temp', stop reason = breakpoint 1.1 lldb stops at main()
+ frame #0: 0x00000000002012ef temp`main at temp.c:8:2
+ 5 main() {
+ 6 int i;
+ 7
+-> 8 printf("This is my program\n"); Indicates the line where it stopped
+ 9 bazz(i);
+ 10 return 0;
+ 11 }
+(lldb) thread step-over Go to next line
+This is my program Program prints out
+Process 9992 stopped
+* thread #1, name = 'temp', stop reason = step over
+ frame #0: 0x0000000000201300 temp`main at temp.c:9:7
+ 6 int i;
+ 7
+ 8 printf("This is my program\n");
+-> 9 bazz(i);
+ 10 return 0;
+ 11 }
+ 12
+(lldb) thread step-in step into bazz()
+Process 9992 stopped
+* thread #1, name = 'temp', stop reason = step in
+ frame #0: 0x000000000020132b temp`bazz(anint=-5360) at temp.c:14:29 lldb displays stack frame
+ 11 }
+ 12
+ 13 int bazz(int anint) {
+-> 14 printf("You gave me %d\n", anint);
+ 15 return anint;
+ 16 }
+(lldb)
+....
+
+Подождите минуту! Как переменная `int` стала равна `-5360`? Разве она не была установлена в `5` в `main()`? Давайте поднимемся к `main()` и посмотрим.
+
+[source, bash]
+....
+(lldb) up Move up call stack
+frame #1: 0x000000000020130b temp`main at temp.c:9:2 lldb displays stack frame
+ 6 int i;
+ 7
+ 8 printf("This is my program\n");
+-> 9 bazz(i);
+ 10 return 0;
+ 11 }
+ 12
+(lldb) frame variable i Show us the value of i
+(int) i = -5360 lldb displays -5360
+....
+
+О боже! Глядя на код, мы забыли инициализировать i. Мы хотели добавить
+
+[.programlisting]
+....
+...
+main() {
+ int i;
+
+ i = 5;
+ printf("This is my program\n");
+...
+....
+
+но мы пропустили строку `i=5;`. Поскольку мы не инициализировали `i`, она содержала любое число, которое оказалось в той области памяти при запуске программы, и в данном случае это оказалось `-5360`.
+
+[NOTE]
+====
+Команда `lldb` отображает стек вызовов каждый раз, когда мы входим в функцию или выходим из неё, даже если мы используем `up` и `down` для перемещения по стеку вызовов. Это показывает имя функции и значения её аргументов, что помогает отслеживать текущее положение и происходящее. (Стек — это область хранения, где программа сохраняет информацию об аргументах, переданных в функции, и о том, куда возвращаться после вызова функции.)
+====
+
+==== Изучение файла Core с помощью lldb
+
+Файл core — это, по сути, файл, содержащий полное состояние процесса на момент его аварийного завершения. В «старые добрые времена» программистам приходилось распечатывать шестнадцатеричные дампы файлов core и корпеть над руководствами по машинному коду, но сейчас жизнь стала немного проще. Кстати, в FreeBSD и других системах на базе 4.4BSD файл core называется [.filename]#progname.core#, а не просто [.filename]#core#, чтобы было понятнее, какой программе он принадлежит.
+
+Для анализа файла core укажите имя файла core в дополнение к самой программе. Вместо обычного запуска `lldb` введите `lldb -c _имя_программы_.core \-- _имя_программы_`.
+
+Отладчик отобразит что-то вроде этого:
+
+[source, bash, subs="verbatim,quotes"]
+....
+% lldb -c [.filename]#progname.core# -- [.filename]#progname#
+(lldb) target create "[.filename]#progname#" --core "[.filename]#progname#.core"
+Core file '/home/pauamma/tmp/[.filename]#progname.core#' (x86_64) was loaded.
+(lldb)
+....
+
+В этом случае программа называлась [.filename]#progname#, поэтому файл дампа имеет имя [.filename]#progname.core#. Отладчик не показывает, почему программа завершилась аварийно или где это произошло. Для этого используйте команду `thread backtrace all`. Она также покажет, как была вызвана функция, в которой программа завершилась дампом ядра.
+
+[source, bash, subs="verbatim,quotes"]
+....
+(lldb) thread backtrace all
+* thread #1, name = 'progname', stop reason = signal SIGSEGV
+ * frame #0: 0x0000000000201347 progname`bazz(anint=5) at temp2.c:17:10
+ frame #1: 0x0000000000201312 progname`main at temp2.c:10:2
+ frame #2: 0x000000000020110f progname`_start(ap=<unavailable>, cleanup=<unavailable>) at crt1.c:76:7
+(lldb)
+....
+
+`SIGSEGV` указывает, что программа пыталась получить доступ к памяти (обычно выполнить код или прочитать/записать данные) по адресу, который ей не принадлежит, но не предоставляет конкретных деталей. Для этого обратитесь к исходному коду на строке 10 файла temp2.c, в функции `bazz()`. Трассировка также показывает, что в данном случае `bazz()` была вызвана из `main()`.
+
+==== Подключение к работающей программе с помощью lldb
+
+Одной из самых замечательных особенностей `lldb` является возможность подключения к уже работающей программе. Конечно, для этого требуются соответствующие разрешения. Распространённая проблема — пошаговое выполнение программы, которая создаёт ответвления, с необходимостью отслеживать дочерний процесс, но отладчик отслеживает только родительский.
+
+Для этого запустите другой `lldb`, используйте `ps` для поиска идентификатора процесса дочернего процесса и выполните
+
+[source, bash]
+....
+(lldb) process attach -p pid
+....
+
+в `lldb`, а затем отлаживайте как обычно.
+
+Для того чтобы это работало правильно, код, который вызывает `fork` для создания дочернего процесса, должен делать что-то вроде следующего (предоставлено из документации `gdb`):
+
+[.programlisting]
+....
+...
+if ((pid = fork()) < 0) /* _Always_ check this */
+ error();
+else if (pid == 0) { /* child */
+ int PauseMode = 1;
+
+ while (PauseMode)
+ sleep(10); /* Wait until someone attaches to us */
+ ...
+} else { /* parent */
+ ...
+....
+
+Вот все, что нужно сделать: подключиться к дочернему процессу, установить `PauseMode` в `0` с помощью `expr PauseMode = 0` и дождаться возврата из вызова `sleep()`.
+
+=== Удаленная отладка с использованием LLDB
+
+[NOTE]
+====
+Описанная функциональность доступна начиная с версии LLDB 12.0.0. Пользователи релизов FreeBSD, содержащих более раннюю версию LLDB, могут воспользоваться снимком из extref:{handbook}[портов или пакетов, ports-using], как package:devel/llvm-devel[].
+====
+
+Начиная с LLDB 12.0.0, удалённая отладка поддерживается в FreeBSD. Это означает, что `lldb-server` может быть запущен для отладки программы на одном узле, в то время как интерактивный клиент `lldb` подключается к нему с другого.
+
+Чтобы запустить новый процесс для удалённой отладки, выполните `lldb-server` на удалённом сервере, набрав
+
+[source, bash]
+....
+% lldb-server g host:port -- progname
+....
+
+Процесс будет остановлен сразу после запуска, и `lldb-server` будет ожидать подключения клиента.
+
+Запустите `lldb` локально и введите следующую команду для подключения к удалённому серверу:
+
+[source, bash]
+....
+(lldb) gdb-remote host:port
+....
+
+`lldb-server` также может присоединиться к работающему процессу. Для этого введите следующее на удалённом сервере:
+
+[source, bash]
+....
+% lldb-server g host:port --attach pid-or-name
+....
+
+=== Использование gdb
+
+==== Запуск gdb
+
+Запустите gdb, набрав
+
+[source, bash]
+....
+% gdb progname
+....
+
+хотя многие предпочитают запускать его внутри Emacs. Для этого введите:
+
+[source, bash]
+....
+ M-x gdb RET progname RET
+....
+
+Наконец, для тех, кого отпугивает текстовый интерфейс командной строки, существует графический интерфейс (package:devel/xxgdb[]) в Коллекции портов.
+
+==== Запуск программы под отладчиком gdb
+
+Скомпилируйте программу с `-g`, чтобы максимально использовать возможности `gdb`. Она будет работать и без этого, но отобразит только имя текущей выполняемой функции вместо исходного кода. Строка вида:
+
+[source, bash]
+....
+... (no debugging symbols found) ...
+....
+
+когда `gdb` запускается, это означает, что программа не была скомпилирована с опцией `-g`.
+
+На приглашении `gdb` введите `break main`. Это укажет отладчику пропустить предварительный код настройки в выполняемой программе и остановить выполнение в начале кода программы. Теперь введите `run`, чтобы запустить программу — она начнётся с начала кода настройки, а затем будет остановлена отладчиком при вызове `main()`.
+
+Для пошагового выполнения программы нажимайте `n`. При вызове функции войдите в неё, нажав `s`. Оказавшись внутри функции, вернитесь из неё, нажав `f`, или используйте `up` и `down` для быстрого просмотра вызывающего кода.
+
+Вот простой пример того, как найти ошибку в программе с помощью `gdb`. Это наша программа (с умышленной ошибкой):
+
+[.programlisting]
+....
+#include <stdio.h>
+
+int bazz(int anint);
+
+main() {
+ int i;
+
+ printf("This is my program\n");
+ bazz(i);
+ return 0;
+}
+
+int bazz(int anint) {
+ printf("You gave me %d\n", anint);
+ return anint;
+}
+....
+
+Эта программа устанавливает значение `i` равным `5` и передает его в функцию `bazz()`, которая выводит переданное число.
+
+Компиляция и запуск программы отображают
+
+[source, bash]
+....
+% cc -g -o temp temp.c
+% ./temp
+This is my program
+anint = 4231
+....
+
+Это было не то, что мы ожидали! Пора разобраться, что происходит!
+
+[source, bash]
+....
+% gdb temp
+GDB is free software and you are welcome to distribute copies of it
+ under certain conditions; type "show copying" to see the conditions.
+There is absolutely no warranty for GDB; type "show warranty" for details.
+GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc.
+(gdb) break main Skip the set-up code
+Breakpoint 1 at 0x160f: file temp.c, line 9. gdb puts breakpoint at main()
+(gdb) run Run as far as main()
+Starting program: /home/james/tmp/temp Program starts running
+
+Breakpoint 1, main () at temp.c:9 gdb stops at main()
+(gdb) n Go to next line
+This is my program Program prints out
+(gdb) s step into bazz()
+bazz (anint=4231) at temp.c:17 gdb displays stack frame
+(gdb)
+....
+
+Подождите минуту! Как `int` стал равен `4231`? Разве он не был установлен в `5` в `main()`? Давайте поднимемся к `main()` и посмотрим.
+
+[source, bash]
+....
+(gdb) up Move up call stack
+#1 0x1625 in main () at temp.c:11 gdb displays stack frame
+(gdb) p i Show us the value of i
+$1 = 4231 gdb displays 4231
+....
+
+О боже! Глядя на код, мы забыли инициализировать i. Мы хотели добавить
+
+[.programlisting]
+....
+...
+main() {
+ int i;
+
+ i = 5;
+ printf("This is my program\n");
+...
+....
+
+но мы пропустили строку `i=5;`. Поскольку мы не инициализировали `i`, она содержала любое число, которое оказалось в той области памяти при запуске программы, и в данном случае это оказалось `4231`.
+
+[NOTE]
+====
+Команда `gdb` отображает стек вызовов каждый раз при входе в функцию или выходе из неё, даже при использовании `up` и `down` для перемещения по стеку вызовов. Это показывает имя функции и значения её аргументов, что помогает отслеживать текущее положение и происходящее. (Стек — это область хранения, где программа сохраняет информацию об аргументах, переданных в функции, и о месте, куда нужно вернуться после вызова функции.)
+====
+
+==== Изучение файла core с помощью gdb
+
+Файл core — это, по сути, файл, содержащий полное состояние процесса на момент его аварийного завершения. В «старые добрые времена» программистам приходилось распечатывать шестнадцатеричные дампы файлов core и корпеть над руководствами по машинному коду, но сейчас жизнь стала немного проще. Кстати, в FreeBSD и других системах на базе 4.4BSD файл core называется [.filename]#progname.core#, а не просто [.filename]#core#, чтобы было понятнее, какой программе он принадлежит.
+
+Для анализа файла core запустите `gdb` обычным способом. Вместо ввода команд `break` или `run` введите
+
+[source, bash]
+....
+(gdb) core progname.core
+....
+
+Если файл core отсутствует в текущем каталоге, сначала введите `dir /путь/к/core/файлу`.
+
+Отладчик должен отобразить что-то вроде этого:
+
+[source, bash, subs="verbatim,quotes"]
+....
+% gdb [.filename]#progname#
+GDB is free software and you are welcome to distribute copies of it
+ under certain conditions; type "show copying" to see the conditions.
+There is absolutely no warranty for GDB; type "show warranty" for details.
+GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc.
+(gdb) core [.filename]#progname.core#
+Core was generated by `[.filename]#progname#'.
+Program terminated with signal 11, Segmentation fault.
+Cannot access memory at address 0x7020796d.
+#0 0x164a in bazz (anint=0x5) at temp.c:17
+(gdb)
+....
+
+В этом случае программа называлась [.filename]#progname#, поэтому файл дампа памяти называется [.filename]#progname.core#. Мы видим, что программа завершилась аварийно из-за попытки доступа к области памяти, которая ей не доступна, в функции `bazz`.
+
+Иногда полезно увидеть, как была вызвана функция, поскольку проблема могла возникнуть гораздо выше по стеку вызовов в сложной программе. `bt` заставляет `gdb` вывести трассировку стека вызовов:
+
+[source, bash]
+....
+(gdb) bt
+#0 0x164a in bazz (anint=0x5) at temp.c:17
+#1 0xefbfd888 in end ()
+#2 0x162c in main () at temp.c:11
+(gdb)
+....
+
+Функция `end()` вызывается при аварийном завершении программы; в данном случае функция `bazz()` была вызвана из `main()`.
+
+==== Подключение к работающей программе с помощью gdb
+
+Одной из самых удобных функций `gdb` является возможность подключения к уже запущенной программе. Конечно, для этого требуются соответствующие разрешения. Частой проблемой является пошаговое выполнение программы, которая создает дочерний процесс, когда нужно отслеживать дочерний процесс, но отладчик продолжает отслеживать только родительский.
+
+Для этого запустите другой `gdb`, используйте `ps` для поиска идентификатора процесса дочернего элемента и выполните
+
+[source, bash]
+....
+(gdb) attach pid
+....
+
+в `gdb`, а затем отлаживайте как обычно.
+
+Для того чтобы это работало правильно, код, который вызывает `fork` для создания дочернего процесса, должен делать что-то вроде следующего (предоставлено из документации `gdb`):
+
+[.programlisting]
+....
+...
+if ((pid = fork()) < 0) /* _Always_ check this */
+ error();
+else if (pid == 0) { /* child */
+ int PauseMode = 1;
+
+ while (PauseMode)
+ sleep(10); /* Wait until someone attaches to us */
+ ...
+} else { /* parent */
+ ...
+....
+
+Теперь осталось только подключиться к дочернему процессу, установить PauseMode в `0` и дождаться возврата из вызова `sleep()`!
+
+[[emacs]]
+== Использование Emacs в качестве среды разработки
+
+=== Emacs
+
+Emacs — это высоконастраиваемый редактор — настолько, что его можно скорее назвать операционной системой, чем редактором! Многие разработчики и системные администраторы действительно проводят практически всё своё время, работая внутри Emacs, выходя из него только для завершения сеанса.
+
+Невозможно даже кратко описать все, что может делать Emacs, но вот некоторые особенности, которые могут быть интересны разработчикам:
+
+* Очень мощный редактор, позволяющий выполнять поиск и замену как строк, так и регулярных выражений (шаблонов), переход к началу/концу блока выражения и многое другое.
+* Выпадающие меню и встроенная справка.
+* Подсветка синтаксиса и форматирование отступов в зависимости от языка.
+* Полностью настраиваемый.
+* Вы можете компилировать и отлаживать программы из Emacs.
+* При ошибке компиляции можно перейти к проблемной строке исходного кода.
+* Дружелюбный интерфейс для программы `info`, используемой для чтения гипертекстовой документации GNU, включая документацию по самому Emacs.
+* Дружелюбный интерфейс для `gdb`, позволяющий просматривать исходный код во время пошагового выполнения программы.
+
+И, несомненно, множество других, которые были упущены из виду.
+
+Emacs можно установить на FreeBSD с помощью пакета package:editors/emacs[].
+
+После установки запустите его и выполните `C-h t`, чтобы прочитать руководство по Emacs — это означает, что нужно удерживать kbd:[control], нажать kbd:[h], отпустить kbd:[control], а затем нажать kbd:[t]. (Также можно использовать мышь для выбора [.guimenuitem]#Руководство по Emacs# в меню menu:Help[].)
+
+Хотя в Emacs и есть меню, стоит изучить сочетания клавиш, так как редактировать что-либо с их помощью гораздо быстрее, чем искать мышку и кликать в нужное место. Кроме того, общаясь с опытными пользователями Emacs, вы часто услышите выражения вроде «`M-x replace-s RET foo RET bar RET`» — полезно понимать, что они значат. Да и вообще, в Emacs столько полезных функций, что все они просто не поместятся на панелях меню.
+
+К счастью, освоить сочетания клавиш довольно легко, так как они отображаются рядом с пунктами меню. Мой совет — использовать пункты меню для, скажем, открытия файла, пока вы не разберётесь, как это работает, и не почувствуете себя уверенно, а затем попробуйте выполнить `C-x C-f`. Когда освоитесь с этим, переходите к следующей команде меню.
+
+Если вы не можете вспомнить, что делает определённая комбинация клавиш, выберите [.guimenuitem]#Описание Клавиши# в меню menu:Help[] и введите её — Emacs сообщит, что она делает. Вы также можете использовать пункт меню [.guimenuitem]#Command Apropos#, чтобы найти все команды, содержащие определённое слово, с указанием соответствующих клавишных сочетаний.
+
+Между прочим, выражение выше означает: удерживайте клавишу kbd:[Meta], нажмите kbd:[x], отпустите клавишу kbd:[Meta], введите `replace-s` (сокращение от `replace-string` — ещё одна особенность Emacs в том, что команды можно сокращать), нажмите клавишу kbd:[return], введите `foo` (строка, которую нужно заменить), нажмите клавишу kbd:[return], введите `bar` (строка, на которую нужно заменить `foo`) и снова нажмите kbd:[return]. Emacs выполнит операцию поиска и замены, которую вы только что запросили.
+
+Если вам интересно, что такое kbd:[Meta], то это специальная клавиша, которая есть на многих рабочих станциях UNIX(R). К сожалению, на PC её нет, поэтому обычно используется kbd:[alt] (или, если вам не повезло, kbd:[escape]).
+
+Ах да, чтобы выйти из Emacs, нажмите `C-x C-c` (это значит зажмите клавишу kbd:[control], нажмите kbd:[x], затем kbd:[c] и отпустите kbd:[control]). Если у вас есть несохранённые файлы, Emacs спросит, хотите ли вы их сохранить. (Игнорируйте часть документации, где говорится, что `C-z` — это обычный способ выхода из Emacs — это оставляет Emacs работающим в фоне и полезно только на системах без виртуальных терминалов).
+
+=== Настройка Emacs
+
+Emacs делает много замечательных вещей; некоторые из них встроены, некоторые требуют настройки.
+
+Вместо использования проприетарного языка макросов для конфигурации, Emacs применяет версию Lisp, специально адаптированную для редакторов, известную как Emacs Lisp. Работа с Emacs Lisp может быть весьма полезной, если вы хотите продолжить и изучить что-то вроде Common Lisp. Emacs Lisp обладает многими возможностями Common Lisp, хотя и значительно меньше (и, следовательно, проще для освоения).
+
+Лучший способ изучить Emacs Lisp — это прочитать онлайн-руководство link:https://www.gnu.org/software/emacs/manual/elisp.html[Emacs Reference].
+
+Однако для начала настройки Emacs не обязательно знать Lisp, так как я включил пример файла [.filename]#.emacs#, которого должно быть достаточно для старта. Просто скопируйте его в свой домашний каталог и перезапустите Emacs, если он уже запущен; он прочитает команды из файла и (надеюсь) предоставит вам полезную базовую конфигурацию.
+
+=== Пример файла [.filename]#.emacs#
+
+К сожалению, здесь слишком много информации, чтобы объяснять всё подробно; однако есть один или два момента, которые стоит упомянуть.
+
+* Всё, что начинается с `;`, является комментарием и игнорируется Emacs.
+* В первой строке `-*- Emacs-Lisp -*-` нужен для того, чтобы мы могли редактировать сам файл [.filename]#.emacs# в Emacs и использовать все удобные функции для редактирования Emacs Lisp. Обычно Emacs пытается угадать это по имени файла, но может не сделать это правильно для [.filename]#.emacs#.
+* Клавиша kbd:[tab] связана с функцией отступа в некоторых режимах, поэтому при нажатии клавиши tab текущая строка кода будет с отступом. Если вы хотите вставить символ табуляции в текст, удерживайте клавишу kbd:[control] во время нажатия kbd:[tab].
+* Этот файл поддерживает подсветку синтаксиса для C, C++, Perl, Lisp и Scheme, определяя язык по имени файла.
+* В Emacs уже есть предопределённая функция `next-error`. В окне вывода компиляции это позволяет переходить от одной ошибки компиляции к следующей с помощью `M-n`; мы определяем дополнительную функцию `previous-error`, которая позволяет вернуться к предыдущей ошибке с помощью `M-p`. Самое приятное — сочетание `C-c C-c` откроет исходный файл, в котором произошла ошибка, и перейдёт на соответствующую строку.
+* Включаем возможность Emacs работать как сервер, так что если вы заняты чем-то вне Emacs и хотите отредактировать файл, можно просто ввести
++
+[source, bash]
+....
+% emacsclient filename
+....
++
+и затем вы можете редактировать файл в вашем Emacs!footnote:[Многие пользователи Emacs устанавливают переменную окружения EDITOR в emacsclient, так что это происходит каждый раз, когда им нужно отредактировать файл.]
+
+.Пример файла [.filename]#.emacs#
+====
+[.programlisting]
+....
+;; -*-Emacs-Lisp-*-
+
+;; This file is designed to be re-evaled; use the variable first-time
+;; to avoid any problems with this.
+(defvar first-time t
+ "Flag signifying this is the first time that .emacs has been evaled")
+
+;; Meta
+(global-set-key "\M- " 'set-mark-command)
+(global-set-key "\M-\C-h" 'backward-kill-word)
+(global-set-key "\M-\C-r" 'query-replace)
+(global-set-key "\M-r" 'replace-string)
+(global-set-key "\M-g" 'goto-line)
+(global-set-key "\M-h" 'help-command)
+
+;; Function keys
+(global-set-key [f1] 'manual-entry)
+(global-set-key [f2] 'info)
+(global-set-key [f3] 'repeat-complex-command)
+(global-set-key [f4] 'advertised-undo)
+(global-set-key [f5] 'eval-current-buffer)
+(global-set-key [f6] 'buffer-menu)
+(global-set-key [f7] 'other-window)
+(global-set-key [f8] 'find-file)
+(global-set-key [f9] 'save-buffer)
+(global-set-key [f10] 'next-error)
+(global-set-key [f11] 'compile)
+(global-set-key [f12] 'grep)
+(global-set-key [C-f1] 'compile)
+(global-set-key [C-f2] 'grep)
+(global-set-key [C-f3] 'next-error)
+(global-set-key [C-f4] 'previous-error)
+(global-set-key [C-f5] 'display-faces)
+(global-set-key [C-f8] 'dired)
+(global-set-key [C-f10] 'kill-compilation)
+
+;; Keypad bindings
+(global-set-key [up] "\C-p")
+(global-set-key [down] "\C-n")
+(global-set-key [left] "\C-b")
+(global-set-key [right] "\C-f")
+(global-set-key [home] "\C-a")
+(global-set-key [end] "\C-e")
+(global-set-key [prior] "\M-v")
+(global-set-key [next] "\C-v")
+(global-set-key [C-up] "\M-\C-b")
+(global-set-key [C-down] "\M-\C-f")
+(global-set-key [C-left] "\M-b")
+(global-set-key [C-right] "\M-f")
+(global-set-key [C-home] "\M-<")
+(global-set-key [C-end] "\M->")
+(global-set-key [C-prior] "\M-<")
+(global-set-key [C-next] "\M->")
+
+;; Mouse
+(global-set-key [mouse-3] 'imenu)
+
+;; Misc
+(global-set-key [C-tab] "\C-q\t") ; Control tab quotes a tab.
+(setq backup-by-copying-when-mismatch t)
+
+;; Treat 'y' or <CR> as yes, 'n' as no.
+(fset 'yes-or-no-p 'y-or-n-p)
+(define-key query-replace-map [return] 'act)
+(define-key query-replace-map [?\C-m] 'act)
+
+;; Load packages
+(require 'desktop)
+(require 'tar-mode)
+
+;; Pretty diff mode
+(autoload 'ediff-buffers "ediff" "Intelligent Emacs interface to diff" t)
+(autoload 'ediff-files "ediff" "Intelligent Emacs interface to diff" t)
+(autoload 'ediff-files-remote "ediff"
+ "Intelligent Emacs interface to diff")
+
+(if first-time
+ (setq auto-mode-alist
+ (append '(("\\.cpp$" . c++-mode)
+ ("\\.hpp$" . c++-mode)
+ ("\\.lsp$" . lisp-mode)
+ ("\\.scm$" . scheme-mode)
+ ("\\.pl$" . perl-mode)
+ ) auto-mode-alist)))
+
+;; Auto font lock mode
+(defvar font-lock-auto-mode-list
+ (list 'c-mode 'c++-mode 'c++-c-mode 'emacs-lisp-mode 'lisp-mode 'perl-mode 'scheme-mode)
+ "List of modes to always start in font-lock-mode")
+
+(defvar font-lock-mode-keyword-alist
+ '((c++-c-mode . c-font-lock-keywords)
+ (perl-mode . perl-font-lock-keywords))
+ "Associations between modes and keywords")
+
+(defun font-lock-auto-mode-select ()
+ "Automatically select font-lock-mode if the current major mode is in font-lock-auto-mode-list"
+ (if (memq major-mode font-lock-auto-mode-list)
+ (progn
+ (font-lock-mode t))
+ )
+ )
+
+(global-set-key [M-f1] 'font-lock-fontify-buffer)
+
+;; New dabbrev stuff
+;(require 'new-dabbrev)
+(setq dabbrev-always-check-other-buffers t)
+(setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_")
+(add-hook 'emacs-lisp-mode-hook
+ '(lambda ()
+ (set (make-local-variable 'dabbrev-case-fold-search) nil)
+ (set (make-local-variable 'dabbrev-case-replace) nil)))
+(add-hook 'c-mode-hook
+ '(lambda ()
+ (set (make-local-variable 'dabbrev-case-fold-search) nil)
+ (set (make-local-variable 'dabbrev-case-replace) nil)))
+(add-hook 'text-mode-hook
+ '(lambda ()
+ (set (make-local-variable 'dabbrev-case-fold-search) t)
+ (set (make-local-variable 'dabbrev-case-replace) t)))
+
+;; C++ and C mode...
+(defun my-c++-mode-hook ()
+ (setq tab-width 4)
+ (define-key c++-mode-map "\C-m" 'reindent-then-newline-and-indent)
+ (define-key c++-mode-map "\C-ce" 'c-comment-edit)
+ (setq c++-auto-hungry-initial-state 'none)
+ (setq c++-delete-function 'backward-delete-char)
+ (setq c++-tab-always-indent t)
+ (setq c-indent-level 4)
+ (setq c-continued-statement-offset 4)
+ (setq c++-empty-arglist-indent 4))
+
+(defun my-c-mode-hook ()
+ (setq tab-width 4)
+ (define-key c-mode-map "\C-m" 'reindent-then-newline-and-indent)
+ (define-key c-mode-map "\C-ce" 'c-comment-edit)
+ (setq c-auto-hungry-initial-state 'none)
+ (setq c-delete-function 'backward-delete-char)
+ (setq c-tab-always-indent t)
+;; BSD-ish indentation style
+ (setq c-indent-level 4)
+ (setq c-continued-statement-offset 4)
+ (setq c-brace-offset -4)
+ (setq c-argdecl-indent 0)
+ (setq c-label-offset -4))
+
+;; Perl mode
+(defun my-perl-mode-hook ()
+ (setq tab-width 4)
+ (define-key c++-mode-map "\C-m" 'reindent-then-newline-and-indent)
+ (setq perl-indent-level 4)
+ (setq perl-continued-statement-offset 4))
+
+;; Scheme mode...
+(defun my-scheme-mode-hook ()
+ (define-key scheme-mode-map "\C-m" 'reindent-then-newline-and-indent))
+
+;; Emacs-Lisp mode...
+(defun my-lisp-mode-hook ()
+ (define-key lisp-mode-map "\C-m" 'reindent-then-newline-and-indent)
+ (define-key lisp-mode-map "\C-i" 'lisp-indent-line)
+ (define-key lisp-mode-map "\C-j" 'eval-print-last-sexp))
+
+;; Add all of the hooks...
+(add-hook 'c++-mode-hook 'my-c++-mode-hook)
+(add-hook 'c-mode-hook 'my-c-mode-hook)
+(add-hook 'scheme-mode-hook 'my-scheme-mode-hook)
+(add-hook 'emacs-lisp-mode-hook 'my-lisp-mode-hook)
+(add-hook 'lisp-mode-hook 'my-lisp-mode-hook)
+(add-hook 'perl-mode-hook 'my-perl-mode-hook)
+
+;; Complement to next-error
+(defun previous-error (n)
+ "Visit previous compilation error message and corresponding source code."
+ (interactive "p")
+ (next-error (- n)))
+
+;; Misc...
+(transient-mark-mode 1)
+(setq mark-even-if-inactive t)
+(setq visible-bell nil)
+(setq next-line-add-newlines nil)
+(setq compile-command "make")
+(setq suggest-key-bindings nil)
+(put 'eval-expression 'disabled nil)
+(put 'narrow-to-region 'disabled nil)
+(put 'set-goal-column 'disabled nil)
+(if (>= emacs-major-version 21)
+ (setq show-trailing-whitespace t))
+
+;; Elisp archive searching
+(autoload 'format-lisp-code-directory "lispdir" nil t)
+(autoload 'lisp-dir-apropos "lispdir" nil t)
+(autoload 'lisp-dir-retrieve "lispdir" nil t)
+(autoload 'lisp-dir-verify "lispdir" nil t)
+
+;; Font lock mode
+(defun my-make-face (face color &optional bold)
+ "Create a face from a color and optionally make it bold"
+ (make-face face)
+ (copy-face 'default face)
+ (set-face-foreground face color)
+ (if bold (make-face-bold face))
+ )
+
+(if (eq window-system 'x)
+ (progn
+ (my-make-face 'blue "blue")
+ (my-make-face 'red "red")
+ (my-make-face 'green "dark green")
+ (setq font-lock-comment-face 'blue)
+ (setq font-lock-string-face 'bold)
+ (setq font-lock-type-face 'bold)
+ (setq font-lock-keyword-face 'bold)
+ (setq font-lock-function-name-face 'red)
+ (setq font-lock-doc-string-face 'green)
+ (add-hook 'find-file-hooks 'font-lock-auto-mode-select)
+
+ (setq baud-rate 1000000)
+ (global-set-key "\C-cmm" 'menu-bar-mode)
+ (global-set-key "\C-cms" 'scroll-bar-mode)
+ (global-set-key [backspace] 'backward-delete-char)
+ ; (global-set-key [delete] 'delete-char)
+ (standard-display-european t)
+ (load-library "iso-transl")))
+
+;; X11 or PC using direct screen writes
+(if window-system
+ (progn
+ ;; (global-set-key [M-f1] 'hilit-repaint-command)
+ ;; (global-set-key [M-f2] [?\C-u M-f1])
+ (setq hilit-mode-enable-list
+ '(not text-mode c-mode c++-mode emacs-lisp-mode lisp-mode
+ scheme-mode)
+ hilit-auto-highlight nil
+ hilit-auto-rehighlight 'visible
+ hilit-inhibit-hooks nil
+ hilit-inhibit-rebinding t)
+ (require 'hilit19)
+ (require 'paren))
+ (setq baud-rate 2400) ; For slow serial connections
+ )
+
+;; TTY type terminal
+(if (and (not window-system)
+ (not (equal system-type 'ms-dos)))
+ (progn
+ (if first-time
+ (progn
+ (keyboard-translate ?\C-h ?\C-?)
+ (keyboard-translate ?\C-? ?\C-h)))))
+
+;; Under UNIX
+(if (not (equal system-type 'ms-dos))
+ (progn
+ (if first-time
+ (server-start))))
+
+;; Add any face changes here
+(add-hook 'term-setup-hook 'my-term-setup-hook)
+(defun my-term-setup-hook ()
+ (if (eq window-system 'pc)
+ (progn
+;; (set-face-background 'default "red")
+ )))
+
+;; Restore the "desktop" - do this as late as possible
+(if first-time
+ (progn
+ (desktop-load-default)
+ (desktop-read)))
+
+;; Indicate that this file has been read at least once
+(setq first-time nil)
+
+;; No need to debug anything now
+
+(setq debug-on-error nil)
+
+;; All done
+(message "All done, %s%s" (user-login-name) ".")
+....
+====
+
+=== Расширение списка языков, понимаемых Emacs
+
+Вот, это все хорошо, если вы хотите программировать только на языках, уже предусмотренных в [.filename]#.emacs# (C, C++, Perl, Lisp и Scheme), но что произойдет, если появится новый язык под названием "whizbang", полный захватывающих возможностей?
+
+Первое, что нужно сделать, — это выяснить, поставляются ли с whizbang какие-либо файлы, сообщающие Emacs о языке. Обычно они заканчиваются на [.filename]#.el#, что означает "Emacs Lisp". Например, если whizbang является портом FreeBSD, мы можем найти эти файлы, выполнив
+
+[source, bash]
+....
+% find /usr/ports/lang/whizbang -name "*.el" -print
+....
+
+и установите их, скопировав в каталог Emacs, где находятся файлы Lisp (site Lisp). В FreeBSD это [.filename]#/usr/local/share/emacs/site-lisp#.
+
+Вот пример, если вывод команды find был
+
+[source, bash]
+....
+/usr/ports/lang/whizbang/work/misc/whizbang.el
+....
+
+мы бы сделали
+
+[source, bash]
+....
+# cp /usr/ports/lang/whizbang/work/misc/whizbang.el /usr/local/share/emacs/site-lisp
+....
+
+Далее нам нужно решить, какое расширение имеют исходные файлы whizbang. Допустим, для примера, что все они заканчиваются на [.filename]#.wiz#. Нам необходимо добавить запись в наш [.filename]#.emacs#, чтобы убедиться, что Emacs сможет использовать информацию из [.filename]#whizbang.el#.
+
+Найдите запись auto-mode-alist в файле [.filename]#.emacs# и добавьте строку для whizbang, например:
+
+[.programlisting]
+....
+...
+("\\.lsp$" . lisp-mode)
+("\\.wiz$" . whizbang-mode)
+("\\.scm$" . scheme-mode)
+...
+....
+
+Это означает, что Emacs автоматически перейдёт в режим `whizbang-mode` при редактировании файла с расширением [.filename]#.wiz#.
+
+Непосредственно ниже вы найдете запись font-lock-auto-mode-list. Добавьте `whizbang-mode` в нее следующим образом:
+
+[.programlisting]
+....
+;; Auto font lock mode
+(defvar font-lock-auto-mode-list
+ (list 'c-mode 'c++-mode 'c++-c-mode 'emacs-lisp-mode 'whizbang-mode 'lisp-mode 'perl-mode 'scheme-mode)
+ "List of modes to always start in font-lock-mode")
+....
+
+Это означает, что Emacs всегда будет включать `font-lock-mode` (т.е. подсветку синтаксиса) при редактировании файла [.filename]#.wiz#.
+
+И это всё, что требуется. Если вам нужно, чтобы что-то ещё выполнялось автоматически при открытии [.filename]#.wiz#, вы можете добавить `whizbang-mode hook` (см. `my-scheme-mode-hook` для простого примера, который добавляет `auto-indent`).
+
+[[tools-reading]]
+== Для дальнейшего ознакомления
+
+Для получения информации о настройке среды разработки для внесения исправлений в саму FreeBSD см. man:development[7].
+
+* Brian Harvey and Matthew Wright _Simply Scheme_ MIT 1994. ISBN 0-262-08226-8
+* Randall Schwartz _Learning Perl_ O'Reilly 1993 ISBN 1-56592-042-2
+* Patrick Henry Winston and Berthold Klaus Paul Horn _Lisp (3rd Edition)_ Addison-Wesley 1989 ISBN 0-201-08319-1
+* Brian W. Kernighan and Rob Pike _The Unix Programming Environment_ Prentice-Hall 1984 ISBN 0-13-937681-X
+* Brian W. Kernighan and Dennis M. Ritchie _The C Programming Language (2nd Edition)_ Prentice-Hall 1988 ISBN 0-13-110362-8
+* Bjarne Stroustrup _The C++ Programming Language_ Addison-Wesley 1991 ISBN 0-201-53992-6
+* W. Richard Stevens _Advanced Programming in the Unix Environment_ Addison-Wesley 1992 ISBN 0-201-56317-7
+* W. Richard Stevens _Unix Network Programming_ Prentice-Hall 1990 ISBN 0-13-949876-1
diff --git a/documentation/content/ru/books/developers-handbook/tools/_index.po b/documentation/content/ru/books/developers-handbook/tools/_index.po
new file mode 100644
index 0000000000..47c68c25ff
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/tools/_index.po
@@ -0,0 +1,4483 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-07-05 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbooktools_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:17
+#, no-wrap
+msgid "Programming Tools"
+msgstr "Инструменты разработки"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1
+#, no-wrap
+msgid "Chapter 2. Programming Tools"
+msgstr "Глава 2. Инструменты разработки"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:57
+#, no-wrap
+msgid "Synopsis"
+msgstr "Обзор"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:64
+msgid ""
+"This chapter is an introduction to using some of the programming tools "
+"supplied with FreeBSD, although much of it will be applicable to many other "
+"versions of UNIX(R). It does _not_ attempt to describe coding in any "
+"detail. Most of the chapter assumes little or no previous programming "
+"knowledge, although it is hoped that most programmers will find something of "
+"value in it."
+msgstr ""
+"В этой главе представлено введение в использование некоторых инструментов "
+"для программирования, поставляемых с FreeBSD, хотя многое из описанного "
+"применимо и к другим версиям UNIX(R). Она _не_ претендует на детальное "
+"описание процесса написания кода. Большая часть главы предполагает наличие "
+"минимальных или отсутствие знаний в программировании, хотя предполагается, "
+"что даже опытные программисты найдут в ней что-то полезное."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:66
+#, no-wrap
+msgid "Introduction"
+msgstr "Введение"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:73
+msgid ""
+"FreeBSD offers an excellent development environment. Compilers for C and C+"
+"+ and an assembler come with the basic system, not to mention classic "
+"UNIX(R) tools such as `sed` and `awk`. If that is not enough, there are "
+"many more compilers and interpreters in the Ports collection. The following "
+"section, crossref:tools[tools-programming,Introduction to Programming], "
+"lists some of the available options. FreeBSD is very compatible with "
+"standards such as POSIX(R) and ANSI C, as well with its own BSD heritage, so "
+"it is possible to write applications that will compile and run with little "
+"or no modification on a wide range of platforms."
+msgstr ""
+"FreeBSD предоставляет отличную среду разработки. Компиляторы для C и C++, а "
+"также ассемблер входят в базовую систему, не говоря уже о классических "
+"инструментах UNIX(R), таких как `sed` и `awk`. Если этого недостаточно, в "
+"коллекции Ports доступно множество других компиляторов и интерпретаторов. В "
+"следующем разделе, crossref:tools[tools-programming,Введение в "
+"программирование], перечислены некоторые из доступных вариантов. FreeBSD "
+"обладает высокой совместимостью со стандартами, такими как POSIX(R) и ANSI "
+"C, а также с собственным наследием BSD, что позволяет создавать приложения, "
+"которые будут компилироваться и запускаться с минимальными изменениями или "
+"без них на широком спектре платформ."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:77
+msgid ""
+"However, all this power can be rather overwhelming at first if you have "
+"never written programs on a UNIX(R) platform before. This document aims to "
+"help you get up and running, without getting too deeply into more advanced "
+"topics. The intention is that this document should give you enough of the "
+"basics to be able to make some sense of the documentation."
+msgstr ""
+"Однако вся эта мощь может поначалу ошеломить, если вы никогда раньше не "
+"писали программы на платформе UNIX(R). Этот документ призван помочь вам "
+"начать работу, не углубляясь слишком сильно в более сложные темы. Цель "
+"заключается в том, чтобы дать вам достаточно базовых знаний для понимания "
+"документации."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:79
+msgid ""
+"Most of the document requires little or no knowledge of programming, "
+"although it does assume a basic competence with using UNIX(R) and a "
+"willingness to learn!"
+msgstr ""
+"Большая часть документа не требует или почти не требует знаний "
+"программирования, хотя предполагает базовые навыки работы с UNIX(R) и "
+"готовность учиться!"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:81
+#, no-wrap
+msgid "Introduction to Programming"
+msgstr "Введение в программирование"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:87
+msgid ""
+"A program is a set of instructions that tell the computer to do various "
+"things; sometimes the instruction it has to perform depends on what happened "
+"when it performed a previous instruction. This section gives an overview of "
+"the two main ways in which you can give these instructions, or \"commands\" "
+"as they are usually called. One way uses an _interpreter_, the other a "
+"_compiler_. As human languages are too difficult for a computer to "
+"understand in an unambiguous way, commands are usually written in one or "
+"other languages specially designed for the purpose."
+msgstr ""
+"Программа — это набор инструкций, которые указывают компьютеру выполнять "
+"различные действия; иногда выполняемая инструкция зависит от результата "
+"предыдущей. В этом разделе представлен обзор двух основных способов передачи "
+"таких инструкций, или, как их обычно называют, «команд». Один способ "
+"использует _интерпретатор_, другой — _компилятор_. Поскольку человеческие "
+"языки слишком сложны для однозначного понимания компьютером, команды обычно "
+"записываются на одном из специально разработанных для этого языков."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:88
+#, no-wrap
+msgid "Interpreters"
+msgstr "Интерпретаторы"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:93
+msgid ""
+"With an interpreter, the language comes as an environment, where you type in "
+"commands at a prompt and the environment executes them for you. For more "
+"complicated programs, you can type the commands into a file and get the "
+"interpreter to load the file and execute the commands in it. If anything "
+"goes wrong, many interpreters will drop you into a debugger to help you "
+"track down the problem."
+msgstr ""
+"С интерпретатором язык поставляется как среда, в которой вы вводите команды "
+"в приглашении, и среда выполняет их для вас. Для более сложных программ вы "
+"можете ввести команды в файл и заставить интерпретатор загрузить файл и "
+"выполнить команды в нём. Если что-то пойдёт не так, многие интерпретаторы "
+"переведут вас в отладчик, чтобы помочь найти проблему."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:98
+msgid ""
+"The advantage of this is that you can see the results of your commands "
+"immediately, and mistakes can be corrected readily. The biggest "
+"disadvantage comes when you want to share your programs with someone. They "
+"must have the same interpreter, or you must have some way of giving it to "
+"them, and they need to understand how to use it. Also users may not "
+"appreciate being thrown into a debugger if they press the wrong key! From a "
+"performance point of view, interpreters can use up a lot of memory, and "
+"generally do not generate code as efficiently as compilers."
+msgstr ""
+"Преимущество этого подхода в том, что вы сразу видите результаты выполнения "
+"команд, а ошибки можно легко исправить. Самый большой недостаток "
+"проявляется, когда вы хотите поделиться своими программами с кем-то. У них "
+"должен быть такой же интерпретатор, или у вас должен быть способ "
+"предоставить его, и они должны понимать, как им пользоваться. Кроме того, "
+"пользователям может не понравиться, если они попадут в отладчик при нажатии "
+"не той клавиши! С точки зрения производительности интерпретаторы могут "
+"потреблять много памяти и обычно генерируют код менее эффективно, чем "
+"компиляторы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:103
+msgid ""
+"In my opinion, interpreted languages are the best way to start if you have "
+"not done any programming before. This kind of environment is typically "
+"found with languages like Lisp, Smalltalk, Perl and Basic. It could also be "
+"argued that the UNIX(R) shell (`sh`, `csh`) is itself an interpreter, and "
+"many people do in fact write shell \"scripts\" to help with various "
+"\"housekeeping\" tasks on their machine. Indeed, part of the original "
+"UNIX(R) philosophy was to provide lots of small utility programs that could "
+"be linked together in shell scripts to perform useful tasks."
+msgstr ""
+"По моему мнению, интерпретируемые языки — это лучший способ начать, если вы "
+"раньше не занимались программированием. Такая среда обычно встречается в "
+"языках вроде Lisp, Smalltalk, Perl и Basic. Можно также утверждать, что "
+"UNIX(R) shell (`sh`, `csh`) сам по себе является интерпретатором, и многие "
+"часто пишут shell-«скрипты» для помощи в различных «хозяйственных» задачах "
+"на своих машинах. Действительно, часть оригинальной философии UNIX(R) "
+"заключалась в предоставлении множества небольших утилит, которые можно было "
+"связывать вместе в shell-скриптах для выполнения полезных задач."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:104
+#, no-wrap
+msgid "Interpreters Available with FreeBSD"
+msgstr "Доступные интерпретаторы в FreeBSD"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:107
+msgid ""
+"Here is a list of interpreters that are available from the FreeBSD Ports "
+"Collection, with a brief discussion of some of the more popular interpreted "
+"languages."
+msgstr ""
+"Вот список интерпретаторов, доступных в Коллекции портов FreeBSD, с кратким "
+"обзором некоторых наиболее популярных интерпретируемых языков."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:109
+msgid ""
+"Instructions on how to get and install applications from the Ports "
+"Collection can be found in the extref:{handbook}[Ports section, ports-using] "
+"of the handbook."
+msgstr ""
+"Инструкции по получению и установке приложений из Коллекции портов можно "
+"найти в extref:{handbook}[разделе Порты, ports-using] руководства."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:110
+#, no-wrap
+msgid "BASIC"
+msgstr "BASIC"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:114
+msgid ""
+"Short for Beginner's All-purpose Symbolic Instruction Code. Developed in "
+"the 1950s for teaching University students to program and provided with "
+"every self-respecting personal computer in the 1980s, BASIC has been the "
+"first programming language for many programmers. It is also the foundation "
+"for Visual Basic."
+msgstr ""
+"Сокращение от Beginner's All-purpose Symbolic Instruction Code. Разработан в "
+"1950-х годах для обучения студентов университетов программированию и "
+"поставлялся с каждым уважающим себя персональным компьютером в 1980-х. BASIC "
+"— первый язык программирования для многих программистов. Он также является "
+"основой для Visual Basic."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:116
+msgid ""
+"The Bywater Basic Interpreter can be found in the Ports Collection as "
+"package:lang/bwbasic[] and the Phil Cockroft's Basic Interpreter (formerly "
+"Rabbit Basic) is available as package:lang/pbasic[]."
+msgstr ""
+"Интерпретатор Bywater Basic можно найти в Коллекции портов как package:lang/"
+"bwbasic[], а интерпретатор Phil Cockroft's Basic (ранее известный как Rabbit "
+"Basic) доступен как package:lang/pbasic[]."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:117
+#, no-wrap
+msgid "Lisp"
+msgstr "Lisp"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:121
+msgid ""
+"A language that was developed in the late 1950s as an alternative to the "
+"\"number-crunching\" languages that were popular at the time. Instead of "
+"being based on numbers, Lisp is based on lists; in fact, the name is short "
+"for \"List Processing\". It is very popular in AI (Artificial Intelligence) "
+"circles."
+msgstr ""
+"Язык, разработанный в конце 1950-х годов как альтернатива популярным в то "
+"время языкам для «численных расчётов». В отличие от них, Lisp основан на "
+"списках; фактически, название является сокращением от «List Processing» "
+"(обработка списков). Он очень популярен в кругах, связанных с ИИ "
+"(искусственным интеллектом)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:123
+msgid ""
+"Lisp is an extremely powerful and sophisticated language, but can be rather "
+"large and unwieldy."
+msgstr ""
+"Lisp — это чрезвычайно мощный и сложный язык, но он может показаться "
+"довольно большим и громоздким."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:127
+msgid ""
+"Various implementations of Lisp that can run on UNIX(R) systems are "
+"available in the Ports Collection for FreeBSD. CLISP by Bruno Haible and "
+"Michael Stoll is available as package:lang/clisp[]. SLisp, a simpler Lisp "
+"implementations, is available as package:lang/slisp[]."
+msgstr ""
+"В Коллекции портов FreeBSD доступны различные реализации Lisp, которые могут "
+"работать в системах UNIX(R). CLISP от Bruno Haible и Michael Stoll доступен "
+"как package:lang/clisp[]. Более простая реализация Lisp, SLisp, доступна как "
+"package:lang/slisp[]."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:128
+#, no-wrap
+msgid "Perl"
+msgstr "Perl"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:130
+msgid ""
+"Very popular with system administrators for writing scripts; also often used "
+"on World Wide Web servers for writing CGI scripts."
+msgstr ""
+"Очень популярен среди системных администраторов для написания скриптов; "
+"также часто используется на веб-серверах для написания CGI-скриптов."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:132
+msgid ""
+"Perl is available in the Ports Collection as package:lang/perl5.36[] for all "
+"FreeBSD releases."
+msgstr ""
+"Perl доступен в Коллекции портов как package:lang/perl5.36[] для всех "
+"выпусков FreeBSD."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:133
+#, no-wrap
+msgid "Scheme"
+msgstr "Scheme"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:137
+msgid ""
+"A dialect of Lisp that is rather more compact and cleaner than Common Lisp. "
+"Popular in Universities as it is simple enough to teach to undergraduates as "
+"a first language, while it has a high enough level of abstraction to be used "
+"in research work."
+msgstr ""
+"Диалект Lisp, который более компактен и чист по сравнению с Common Lisp. "
+"Популярен в университетах, так как достаточно прост для обучения студентов в "
+"качестве первого языка, и при этом обладает достаточным уровнем абстракции "
+"для использования в исследовательской работе."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:140
+msgid ""
+"Scheme is available from the Ports Collection as package:lang/elk[] for the "
+"Elk Scheme Interpreter. The MIT Scheme Interpreter can be found in "
+"package:lang/mit-scheme[] and the SCM Scheme Interpreter in package:lang/"
+"scm[]."
+msgstr ""
+"Схема доступна из Коллекции Портов как package:lang/elk[] для Интерпретатора "
+"Elk Scheme. Интерпретатор MIT Scheme можно найти в package:lang/mit-"
+"scheme[], а Интерпретатор SCM Scheme — в package:lang/scm[]."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:141
+#, no-wrap
+msgid "Lua"
+msgstr "Lua"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:147
+msgid ""
+"Lua is a lightweight embeddable scripting language. It is widely portable "
+"and relatively simple. Lua is available in the Ports Collection in "
+"package:lang/lua54[]. It is also included in the base system as "
+"[.filename]#/usr/libexec/flua# for use by base system components. Third "
+"party software should not depend on [.filename]#flua#."
+msgstr ""
+"Lua — это легковесный встраиваемый язык сценариев. Он обладает высокой "
+"переносимостью и относительно прост. Lua доступен в коллекции портов в "
+"пакете package:lang/lua54[]. Он также включен в базовую систему как "
+"[.filename]#/usr/libexec/flua# для использования компонентами базовой "
+"системы. Стороннее программное обеспечение не должно зависеть от "
+"[.filename]#flua#."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:148
+#, no-wrap
+msgid "Python"
+msgstr "Python"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:151
+msgid ""
+"Python is an Object-Oriented, interpreted language. Its advocates argue "
+"that it is one of the best languages to start programming with, since it is "
+"relatively easy to start with, but is not limited in comparison to other "
+"popular interpreted languages that are used for the development of large, "
+"complex applications (Perl and Tcl are two other languages that are popular "
+"for such tasks)."
+msgstr ""
+"Python — это объектно-ориентированный интерпретируемый язык. Его сторонники "
+"утверждают, что это один из лучших языков для начала программирования, "
+"поскольку он относительно прост в освоении, но не уступает другим популярным "
+"интерпретируемым языкам, используемым для разработки крупных и сложных "
+"приложений (Perl и Tcl — два других языка, популярных для таких задач)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:153
+msgid ""
+"The latest version of Python is available from the Ports Collection in "
+"package:lang/python[]."
+msgstr ""
+"Последняя версия Python доступна в Коллекции портов в пакете package:lang/"
+"python[]."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:154
+#, no-wrap
+msgid "Ruby"
+msgstr "Ruby"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:157
+msgid ""
+"Ruby is an interpreter, pure object-oriented programming language. It has "
+"become widely popular because of its easy to understand syntax, flexibility "
+"when writing code, and the ability to easily develop and maintain large, "
+"complex programs."
+msgstr ""
+"Ruby — это интерпретируемый, чисто объектно-ориентированный язык "
+"программирования. Он получил широкую популярность благодаря простому для "
+"понимания синтаксису, гибкости при написании кода и возможности легко "
+"разрабатывать и поддерживать большие, сложные программы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:159
+msgid "Ruby is available from the Ports Collection as package:lang/ruby32[]."
+msgstr "Ruby доступен в Коллекции портов как package:lang/ruby32[]."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:160
+#, no-wrap
+msgid "Tcl and Tk"
+msgstr "Tcl и Tk"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:163
+msgid ""
+"Tcl is an embeddable, interpreted language, that has become widely used and "
+"became popular mostly because of its portability to many platforms. It can "
+"be used both for quickly writing small, prototype applications, or (when "
+"combined with Tk, a GUI toolkit) fully-fledged, featureful programs."
+msgstr ""
+"Tcl — это встраиваемый интерпретируемый язык, который получил широкое "
+"распространение и популярность в основном благодаря своей переносимости на "
+"множество платформ. Он может использоваться как для быстрого написания "
+"небольших прототипов приложений, так и (в сочетании с Tk, набором "
+"инструментов для графического интерфейса) полноценных программ с богатым "
+"функционалом."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:166
+msgid ""
+"Various versions of Tcl are available as ports for FreeBSD. The latest "
+"version, Tcl 8.7, can be found in package:lang/tcl87[]."
+msgstr ""
+"Различные версии Tcl доступны в качестве портов для FreeBSD. Последняя "
+"версия, Tcl 8.7, находится в пакете package:lang/tcl87[]."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:167
+#, no-wrap
+msgid "Compilers"
+msgstr "Компиляторы"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:174
+msgid ""
+"Compilers are rather different. First of all, you write your code in a file "
+"(or files) using an editor. You then run the compiler and see if it accepts "
+"your program. If it did not compile, grit your teeth and go back to the "
+"editor; if it did compile and gave you a program, you can run it either at a "
+"shell command prompt or in a debugger to see if it works properly.footnote:"
+"[If you run it in the shell, you may get a core dump.]"
+msgstr ""
+"Компиляторы довольно сильно различаются. Прежде всего, вы пишете свой код в "
+"файле (или файлах) с помощью редактора. Затем вы запускаете компилятор и "
+"проверяете, принимает ли он вашу программу. Если программа не "
+"скомпилировалась, стисните зубы и вернитесь к редактору; если же компиляция "
+"прошла успешно и программа была создана, вы можете запустить её либо в "
+"командной строке оболочки, либо в отладчике, чтобы проверить её "
+"работу.footnote:[Если вы запустите её в оболочке, может произойти дамп "
+"памяти.]"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:181
+msgid ""
+"Obviously, this is not quite as direct as using an interpreter. However it "
+"allows you to do a lot of things which are very difficult or even impossible "
+"with an interpreter, such as writing code which interacts closely with the "
+"operating system-or even writing your own operating system! It is also "
+"useful if you need to write very efficient code, as the compiler can take "
+"its time and optimize the code, which would not be acceptable in an "
+"interpreter. Moreover, distributing a program written for a compiler is "
+"usually more straightforward than one written for an interpreter-you can "
+"just give them a copy of the executable, assuming they have the same "
+"operating system as you."
+msgstr ""
+"Очевидно, это требует больше усилий по сравнению с использованием "
+"интерпретатора. Однако это позволяет делать множество вещей, которые очень "
+"сложны или даже невозможны с интерпретатором, например, писать код, тесно "
+"взаимодействующий с операционной системой — или даже создавать собственную "
+"операционную систему! Это также полезно, если требуется написать очень "
+"эффективный код, так как компилятор может не спешить и оптимизировать код, "
+"что было бы неприемлемо в интерпретаторе. Более того, распространение "
+"программы, написанной для компилятора, обычно проще, чем для интерпретатора "
+"— можно просто предоставить копию исполняемого файла, предполагая, что у "
+"пользователя та же операционная система, что и у вас."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:185
+msgid ""
+"As the edit-compile-run-debug cycle is rather tedious when using separate "
+"programs, many commercial compiler makers have produced Integrated "
+"Development Environments (IDEs for short). FreeBSD does not include an IDE "
+"in the base system, but package:devel/kdevelop[] is available in the Ports "
+"Collection and many use Emacs for this purpose. Using Emacs as an IDE is "
+"discussed in crossref:tools[emacs, Using Emacs as a Development Environment]."
+msgstr ""
+"Поскольку цикл редактирования-компиляции-запуска-отладки довольно утомителен "
+"при использовании отдельных программ, многие производители коммерческих "
+"компиляторов создали интегрированные среды разработки (сокращённо IDE). "
+"FreeBSD не включает IDE в базовую систему, но в Коллекции портов доступен "
+"package:devel/kdevelop[], и многие используют для этой цели Emacs. "
+"Использование Emacs в качестве IDE обсуждается в crossref:tools[emacs, "
+"Использование Emacs как среды разработки]."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:187
+#, no-wrap
+msgid "Compiling with `cc`"
+msgstr "Компиляция с помощью `cc`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:192
+msgid ""
+"This section deals with the clang compiler for C and C++, as it's installed "
+"with the FreeBSD base system. Clang is installed as `cc`; the GNU compiler "
+"package:lang/gcc[gcc] is available in the Ports Collection. The details of "
+"producing a program with an interpreter vary considerably between "
+"interpreters, and are usually well covered in the documentation and on-line "
+"help for the interpreter."
+msgstr ""
+"Этот раздел посвящён компилятору clang для языков C и C++, так как он "
+"устанавливается вместе с базовой системой FreeBSD. Clang устанавливается как "
+"`cc`; пакет GNU-компилятора package:lang/gcc[gcc] доступен в Коллекции "
+"портов. Детали создания программы с интерпретатором значительно различаются "
+"в зависимости от интерпретатора и обычно хорошо описаны в документации и "
+"онлайн-справке интерпретатора."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:195
+msgid ""
+"Once you have written your masterpiece, the next step is to convert it into "
+"something that will (hopefully!) run on FreeBSD. This usually involves "
+"several steps, each of which is done by a separate program."
+msgstr ""
+"Как только вы напишете свой шедевр, следующий шаг — преобразовать его во что-"
+"то, что (надеюсь!) будет работать на FreeBSD. Обычно это включает несколько "
+"шагов, каждый из которых выполняется отдельной программой."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:198
+msgid ""
+"Pre-process your source code to remove comments and do other tricks like "
+"expanding macros in C."
+msgstr ""
+"Обработь исходный код, чтобы удалить комментарии и выполнить другие "
+"действия, такие как раскрытие макросов в C."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:199
+msgid ""
+"Check the syntax of your code to see if you have obeyed the rules of the "
+"language. If you have not, it will complain!"
+msgstr ""
+"Проверить синтаксис вашего кода, чтобы убедиться, что вы соблюдаете правила "
+"языка. Если нет, он пожалуется!"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:200
+msgid ""
+"Convert the source code into assembly language-this is very close to machine "
+"code, but still understandable by humans. Allegedly."
+msgstr ""
+"Преобразовать исходный код в ассемблерный язык — это очень близко к "
+"машинному коду, но всё ещё понятно человеку. Как утверждается."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:201
+msgid ""
+"Convert the assembly language into machine code-yep, we are talking bits and "
+"bytes, ones and zeros here."
+msgstr ""
+"Преобразовать язык ассемблера в машинный код — да, здесь речь идет о битах и "
+"байтах, единицах и нулях."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:202
+msgid ""
+"Check that you have used things like functions and global variables in a "
+"consistent way. For example, if you have called a non-existent function, it "
+"will complain."
+msgstr ""
+"Проверить, что вы использовали такие элементы, как функции и глобальные "
+"переменные, правильно и последовательно. Например, если вы вызвали "
+"несуществующую функцию, это будет отмечено."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:203
+msgid ""
+"If you are trying to produce an executable from several source code files, "
+"work out how to fit them all together."
+msgstr ""
+"Если вы пытаетесь создать исполняемый файл из нескольких исходных файлов, "
+"определить, как объединить их все вместе."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:204
+msgid ""
+"Work out how to produce something that the system's run-time loader will be "
+"able to load into memory and run."
+msgstr ""
+"Определить, как создать что-то, что загрузчик времени выполнения системы "
+"сможет загрузить в память и запустить."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:205
+msgid "Finally, write the executable on the filesystem."
+msgstr "Наконец, записать исполняемый файл в файловую систему."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:208
+msgid ""
+"The word _compiling_ is often used to refer to just steps 1 to 4-the others "
+"are referred to as _linking_. Sometimes step 1 is referred to as _pre-"
+"processing_ and steps 3-4 as _assembling_."
+msgstr ""
+"Слово _компиляция_ часто относится только к шагам с 1 по 4, а остальные шаги "
+"называются _линковкой_. Иногда шаг 1 называют _препроцессированием_, а шаги "
+"3-4 — _ассемблированием_."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:210
+msgid ""
+"Fortunately, almost all this detail is hidden from you, as `cc` is a front "
+"end that manages calling all these programs with the right arguments for "
+"you; simply typing"
+msgstr ""
+"К счастью, почти все эти детали скрыты от вас, так как `cc` — это интерфейс, "
+"который управляет вызовом всех этих программ с правильными аргументами за "
+"вас; достаточно просто набрать"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:214
+#, no-wrap
+msgid "% cc foobar.c\n"
+msgstr "% cc foobar.c\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:218
+msgid ""
+"will cause [.filename]#foobar.c# to be compiled by all the steps above. If "
+"you have more than one file to compile, just do something like"
+msgstr ""
+"и это вызовет компиляцию файла [.filename]#foobar.c# всеми перечисленными "
+"выше шагами. Если у вас несколько файлов для компиляции, просто сделайте что-"
+"то вроде"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:222
+#, no-wrap
+msgid "% cc foo.c bar.c\n"
+msgstr "% cc foo.c bar.c\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:227
+msgid ""
+"Note that the syntax checking is just that - checking the syntax. It will "
+"not check for any logical mistakes you may have made, like putting the "
+"program into an infinite loop, or using a bubble sort when you meant to use "
+"a binary sort.footnote:[In case you did not know, a binary sort is an "
+"efficient way of sorting things into order and a bubble sort is not.]"
+msgstr ""
+"Обратите внимание, что проверка синтаксиса — это всего лишь проверка "
+"синтаксиса. Она не выявит логических ошибок, которые вы могли допустить, "
+"например, создание бесконечного цикла или использование пузырьковой "
+"сортировки вместо бинарной.footnote:[На случай, если вы не знали: бинарная "
+"сортировка — это эффективный способ упорядочивания элементов, в отличие от "
+"пузырьковой.]"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:230
+msgid ""
+"There are lots and lots of options for `cc`, which are all in the manual "
+"page. Here are a few of the most important ones, with examples of how to "
+"use them."
+msgstr ""
+"Существует множество опций для `cc`, все они описаны в руководстве. Вот "
+"несколько наиболее важных из них с примерами использования."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:231
+#, no-wrap
+msgid "`-o _filename_`"
+msgstr "`-o _filename_`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:233
+msgid ""
+"The output name of the file. If you do not use this option, `cc` will "
+"produce an executable called [.filename]#a.out#.footnote:[The reasons for "
+"this are buried in the mists of history.]"
+msgstr ""
+"Имя выходного файла. Если вы не используете эту опцию, `cc` создаст "
+"исполняемый файл с именем [.filename]#a.out#.footnote:[Причины этого кроются "
+"в глубинах истории.]"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:238
+#, no-wrap
+msgid ""
+"% cc foobar.c executable is a.out\n"
+"% cc -o foobar foobar.c executable is foobar\n"
+msgstr ""
+"% cc foobar.c executable is a.out\n"
+"% cc -o foobar foobar.c executable is foobar\n"
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:240
+#, no-wrap
+msgid "`-c`"
+msgstr "`-c`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:243
+msgid ""
+"Just compile the file, do not link it. Useful for toy programs where you "
+"just want to check the syntax, or if you are using a [.filename]#Makefile#."
+msgstr ""
+"Просто скомпилирует файл, не связывая его. Полезно для небольших программ, "
+"где нужно только проверить синтаксис, или если вы используете "
+"[.filename]#Makefile#."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:247
+#, no-wrap
+msgid "% cc -c foobar.c\n"
+msgstr "% cc -c foobar.c\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:251
+msgid ""
+"This will produce an _object file_ (not an executable) called "
+"[.filename]#foobar.o#. This can be linked together with other object files "
+"into an executable."
+msgstr ""
+"Это создаст _объектный файл_ (не исполняемый) с именем "
+"[.filename]#foobar.o#. Его можно скомпоновать с другими объектными файлами в "
+"исполняемый файл."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:252
+#, no-wrap
+msgid "`-g`"
+msgstr "`-g`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:258
+msgid ""
+"Create a debug version of the executable. This makes the compiler put "
+"information into the executable about which line of which source file "
+"corresponds to which function call. A debugger can use this information to "
+"show the source code as you step through the program, which is _very_ "
+"useful; the disadvantage is that all this extra information makes the "
+"program much bigger. Normally, you compile with `-g` while you are "
+"developing a program and then compile a \"release version\" without `-g` "
+"when you are satisfied it works properly."
+msgstr ""
+"Создать отладочную версию исполняемого файла. Это заставляет компилятор "
+"записывать в исполняемый файл информацию о том, какая строка какого "
+"исходного файла соответствует какому вызову функции. Отладчик может "
+"использовать эту информацию для отображения исходного кода при пошаговом "
+"выполнении программы, что _очень_ полезно; недостатком является то, что вся "
+"эта дополнительная информация значительно увеличивает размер программы. "
+"Обычно вы компилируете с `-g` во время разработки программы, а затем "
+"компилируете \"релизную версию\" без `-g`, когда убедитесь, что она работает "
+"правильно."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:263
+#, no-wrap
+msgid "% cc -g foobar.c\n"
+msgstr "% cc -g foobar.c\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:266
+msgid ""
+"This will produce a debug version of the program. footnote:[Note, we did not "
+"use the -o flag to specify the executable name, so we will get an executable "
+"called a.out. Producing a debug version called foobar is left as an exercise "
+"for the reader!]"
+msgstr ""
+"Это создаст отладочную версию программы. footnote:[Примечание: мы не "
+"использовали флаг -o для указания имени исполняемого файла, поэтому получим "
+"исполняемый файл с именем a.out. Создание отладочной версии с именем foobar "
+"остается упражнением для читателя!]"
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:267
+#, no-wrap
+msgid "`-O`"
+msgstr "`-O`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:271
+msgid ""
+"Create an optimized version of the executable. The compiler performs "
+"various clever tricks to try to produce an executable that runs faster than "
+"normal. You can add a number after the `-O` to specify a higher level of "
+"optimization, but this often exposes bugs in the compiler's optimizer."
+msgstr ""
+"Создает оптимизированную версию исполняемого файла. Компилятор применяет "
+"различные хитрые приёмы, чтобы попытаться создать исполняемый файл, который "
+"работает быстрее обычного. Вы можете добавить число после `-O`, чтобы "
+"указать более высокий уровень оптимизации, но это часто выявляет ошибки в "
+"оптимизаторе компилятора."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:275
+#, no-wrap
+msgid "% cc -O -o foobar foobar.c\n"
+msgstr "% cc -O -o foobar foobar.c\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:278
+msgid "This will produce an optimized version of [.filename]#foobar#."
+msgstr "Это создаст оптимизированную версию [.filename]#foobar#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:281
+msgid ""
+"The following three flags will force `cc` to check that your code complies "
+"to the relevant international standard, often referred to as the ANSI "
+"standard, though strictly speaking it is an ISO standard."
+msgstr ""
+"Следующие три флага заставят `cc` проверять, что ваш код соответствует "
+"соответствующему международному стандарту, часто называемому стандартом "
+"ANSI, хотя строго говоря, это стандарт ISO."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:282
+#, no-wrap
+msgid "`-Wall`"
+msgstr "`-Wall`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:285
+msgid ""
+"Enable all the warnings which the authors of `cc` believe are worthwhile. "
+"Despite the name, it will not enable all the warnings `cc` is capable of."
+msgstr ""
+"Включить все предупреждения, которые разработчики `cc` считают полезными. "
+"Несмотря на название, это не включит все предупреждения, которые `cc` "
+"способен выдавать."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:286
+#, no-wrap
+msgid "`-ansi`"
+msgstr "`-ansi`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:289
+msgid ""
+"Turn off most, but not all, of the non-ANSI C features provided by `cc`. "
+"Despite the name, it does not guarantee strictly that your code will comply "
+"to the standard."
+msgstr ""
+"Отключит большинство, но не все, не-ANSI C функции, предоставляемые `cc`. "
+"Несмотря на название, это не гарантирует строгого соответствия вашего кода "
+"стандарту."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:290
+#, no-wrap
+msgid "`-pedantic`"
+msgstr "`-pedantic`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:292
+msgid "Turn off _all_ ``cc``'s non-ANSI C features."
+msgstr "Отключит _все_ не-ANSI C возможности ``cc``."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:297
+msgid ""
+"Without these flags, `cc` will allow you to use some of its non-standard "
+"extensions to the standard. Some of these are very useful, but will not "
+"work with other compilers - in fact, one of the main aims of the standard is "
+"to allow people to write code that will work with any compiler on any "
+"system. This is known as _portable code_."
+msgstr ""
+"Без этих флагов `cc` позволит вам использовать некоторые из своих "
+"нестандартных расширений стандарта. Некоторые из них очень полезны, но не "
+"будут работать с другими компиляторами — фактически, одна из основных целей "
+"стандарта заключается в том, чтобы позволить людям писать код, который будет "
+"работать с любым компилятором на любой системе. Это известно как "
+"_переносимый код_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:300
+msgid ""
+"Generally, you should try to make your code as portable as possible, as "
+"otherwise you may have to completely rewrite the program later to get it to "
+"work somewhere else - and who knows what you may be using in a few years "
+"time?"
+msgstr ""
+"Обычно следует стремиться к тому, чтобы ваш код был как можно более "
+"переносимым, иначе позже вам, возможно, придётся полностью переписать "
+"программу для её работы в другом месте — а кто знает, что вы будете "
+"использовать через несколько лет?"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:304
+#, no-wrap
+msgid "% cc -Wall -ansi -pedantic -o foobar foobar.c\n"
+msgstr "% cc -Wall -ansi -pedantic -o foobar foobar.c\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:307
+msgid ""
+"This will produce an executable [.filename]#foobar# after checking "
+"[.filename]#foobar.c# for standard compliance."
+msgstr ""
+"В результате будет создан исполняемый файл [.filename]#foobar# после "
+"проверки [.filename]#foobar.c# на соответствие стандартам."
+
+#. type: Labeled list
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:308
+#, no-wrap
+msgid "`-l__library__`"
+msgstr "`-l__library__`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:310
+msgid "Specify a function library to be used at link time."
+msgstr ""
+"Укажите библиотеку функций, которая будет использоваться во время компоновки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:313
+msgid ""
+"The most common example of this is when compiling a program that uses some "
+"of the mathematical functions in C. Unlike most other platforms, these are "
+"in a separate library from the standard C one and you have to tell the "
+"compiler to add it."
+msgstr ""
+"Наиболее распространённый пример этого — компиляция программы, использующей "
+"некоторые математические функции в C. В отличие от большинства других "
+"платформ, они находятся в отдельной библиотеке, отличной от стандартной "
+"библиотеки C, и необходимо указать компилятору добавить её."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:317
+msgid ""
+"The rule is that if the library is called [.filename]#libsomething.a#, you "
+"give `cc` the argument `-l__something__`. For example, the math library is "
+"[.filename]#libm.a#, so you give `cc` the argument `-lm`. A common "
+"\"gotcha\" with the math library is that it has to be the last library on "
+"the command line."
+msgstr ""
+"Правило заключается в том, что если библиотека называется "
+"[.filename]#libsomething.a#, то вы передаёте `cc` аргумент `-"
+"l__something__`. Например, математическая библиотека называется "
+"[.filename]#libm.a#, поэтому вы передаёте `cc` аргумент `-lm`. Типичный "
+"подводный камень с математической библиотекой заключается в том, что она "
+"должна быть последней библиотекой в командной строке."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:321
+#, no-wrap
+msgid "% cc -o foobar foobar.c -lm\n"
+msgstr "% cc -o foobar foobar.c -lm\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:324
+msgid "This will link the math library functions into [.filename]#foobar#."
+msgstr ""
+"Это приведёт к подключению функций математической библиотеки в "
+"[.filename]#foobar#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:327
+msgid ""
+"If you are compiling C++ code, use {c-plus-plus-command}. {c-plus-plus-"
+"command} can also be invoked as {clang-plus-plus-command} on FreeBSD."
+msgstr ""
+"Если вы компилируете код на C++, используйте {c-plus-plus-command}. {c-plus-"
+"plus-command} также может быть вызван как {clang-plus-plus-command} в "
+"FreeBSD."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:331
+#, no-wrap
+msgid "% c++ -o foobar foobar.cc\n"
+msgstr "% c++ -o foobar foobar.cc\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:334
+msgid ""
+"This will both produce an executable [.filename]#foobar# from the C++ source "
+"file [.filename]#foobar.cc#."
+msgstr ""
+"Это создаст исполняемый файл [.filename]#foobar# из исходного файла на C++ "
+"[.filename]#foobar.cc#."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:335
+#, no-wrap
+msgid "Common `cc` Queries and Problems"
+msgstr "Распространённые вопросы и проблемы `cc`"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:337
+#, no-wrap
+msgid "I compiled a file called foobar.c and I cannot find an executable called foobar. Where has it gone?"
+msgstr "Я скомпилировал файл с именем foobar.c и не могу найти исполняемый файл с именем foobar. Куда он пропал?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:341
+msgid ""
+"Remember, `cc` will call the executable [.filename]#a.out# unless you tell "
+"it differently. Use the `-o _filename_` option:"
+msgstr ""
+"Помните, что `cc` вызовет исполняемый файл [.filename]#a.out#, если вы не "
+"укажете иное. Используйте опцию `-o _имя_файла_`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:345
+#, no-wrap
+msgid "% cc -o foobar foobar.c\n"
+msgstr "% cc -o foobar foobar.c\n"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:347
+#, no-wrap
+msgid "OK, I have an executable called foobar, I can see it when I run ls, but when I type in foobar at the command prompt it tells me there is no such file. Why can it not find it?"
+msgstr "Хорошо, у меня есть исполняемый файл с именем foobar, я вижу его при выполнении команды ls, но когда я ввожу foobar в командной строке, система сообщает, что такого файла нет. Почему он не может его найти?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:351
+msgid ""
+"Unlike MS-DOS(R), UNIX(R) does not look in the current directory when it is "
+"trying to find out which executable you want it to run, unless you tell it "
+"to. Type `./foobar`, which means \"run the file called [.filename]#foobar# "
+"in the current directory.\""
+msgstr ""
+"В отличие от MS-DOS(R), UNIX(R) не ищет в текущем каталоге, когда пытается "
+"определить, какую программу нужно запустить, если вы явно не укажете это. "
+"Введите `./foobar`, что означает \"запустить файл с именем "
+"[.filename]#foobar# в текущем каталоге.\""
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:352
+#, no-wrap
+msgid "I called my executable test, but nothing happens when I run it. What is going on?"
+msgstr "Я назвал свой исполняемый файл test, но при запуске ничего не происходит. В чем дело?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:356
+msgid ""
+"Most UNIX(R) systems have a program called `test` in [.filename]#/usr/bin# "
+"and the shell is picking that one up before it gets to checking the current "
+"directory. Either type:"
+msgstr ""
+"Большинство UNIX(R) систем имеют программу под названием `test` в "
+"[.filename]#/usr/bin#, и оболочка выбирает её, прежде чем проверить текущий "
+"каталог. Введите следующее:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:360
+#, no-wrap
+msgid "% ./test\n"
+msgstr "% ./test\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:363
+msgid "or choose a better name for your program!"
+msgstr "или выберите более подходящее название для вашей программы!"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:364
+#, no-wrap
+msgid "I compiled my program and it seemed to run all right at first, then there was an error and it said something about core dumped. What does that mean?"
+msgstr "Я скомпилировал свою программу, и сначала она работала нормально, но потом произошла ошибка, и было сообщение о core dumped. Что это значит?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:368
+msgid ""
+"The name _core dump_ dates back to the very early days of UNIX(R), when the "
+"machines used core memory for storing data. Basically, if the program "
+"failed under certain conditions, the system would write the contents of core "
+"memory to disk in a file called [.filename]#core#, which the programmer "
+"could then pore over to find out what went wrong."
+msgstr ""
+"Название _core dump_ восходит к самым ранним дням UNIX(R), когда машины "
+"использовали ферритовую память для хранения данных. По сути, если программа "
+"завершалась сбоем при определённых условиях, система записывала содержимое "
+"ферритовой памяти на диск в файл с именем [.filename]#core#, который "
+"программист затем мог изучить, чтобы выяснить причину ошибки."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:369
+#, no-wrap
+msgid "Fascinating stuff, but what I am supposed to do now?"
+msgstr "Увлекательный материал, но что мне теперь делать?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:372
+msgid ""
+"Use a debugger to analyze the core (see crossref:tools[debugging, "
+"Debugging])."
+msgstr ""
+"Используйте отладчик для анализа образа памяти (см. "
+"crossref:tools[debugging, Отладка])."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:373
+#, no-wrap
+msgid "When my program dumped core, it said something about a segmentation fault. What is that?"
+msgstr "Когда моя программа сбросила core, она сообщила что-то о segmentation fault. Что это?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:377
+msgid ""
+"This basically means that your program tried to perform some sort of illegal "
+"operation on memory; UNIX(R) is designed to protect the operating system and "
+"other programs from rogue programs."
+msgstr ""
+"Это означает, что ваша программа попыталась выполнить какую-то недопустимую "
+"операцию с памятью; UNIX(R) разработана для защиты операционной системы и "
+"других программ от некорректно работающих программ."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:379
+msgid "Common causes for this are:"
+msgstr "Распространенные причины этого:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:381
+msgid "Trying to write to a NULL pointer, eg"
+msgstr "Попытка записи в NULL-указатель, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:386
+#, no-wrap
+msgid ""
+"char *foo = NULL;\n"
+"strcpy(foo, \"bang!\");\n"
+msgstr ""
+"char *foo = NULL;\n"
+"strcpy(foo, \"bang!\");\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:389
+msgid "Using a pointer that has not been initialized, eg"
+msgstr "Использование неинициализированного указателя, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:394
+#, no-wrap
+msgid ""
+"char *foo;\n"
+"strcpy(foo, \"bang!\");\n"
+msgstr ""
+"char *foo;\n"
+"strcpy(foo, \"bang!\");\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:398
+msgid ""
+"The pointer will have some random value that, with luck, will point into an "
+"area of memory that is not available to your program and the kernel will "
+"kill your program before it can do any damage. If you are unlucky, it will "
+"point somewhere inside your own program and corrupt one of your data "
+"structures, causing the program to fail mysteriously."
+msgstr ""
+"Указатель будет иметь случайное значение, которое, возможно, укажет на "
+"область памяти, недоступную вашей программе, и ядро завершит вашу программу "
+"до того, как она сможет нанести какой-либо ущерб. Если вам не повезет, он "
+"укажет внутрь вашей собственной программы и повредит одну из структур "
+"данных, что приведет к загадочному сбою программы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:399
+msgid "Trying to access past the end of an array, eg"
+msgstr "Попытка доступа за пределы массива, например"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:404
+#, no-wrap
+msgid ""
+"int bar[20];\n"
+"bar[27] = 6;\n"
+msgstr ""
+"int bar[20];\n"
+"bar[27] = 6;\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:407
+msgid "Trying to store something in read-only memory, eg"
+msgstr "Попытка сохранить что-то в память только для чтения, например"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:412
+#, no-wrap
+msgid ""
+"char *foo = \"My string\";\n"
+"strcpy(foo, \"bang!\");\n"
+msgstr ""
+"char *foo = \"My string\";\n"
+"strcpy(foo, \"bang!\");\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:415
+msgid ""
+"UNIX(R) compilers often put string literals like `\"My string\"` into read-"
+"only areas of memory."
+msgstr ""
+"Версии UNIX(R) компиляторы часто помещают строковые литералы, такие как "
+"`\"Моя строка\"`, в области памяти только для чтения."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:416
+msgid "Doing naughty things with `malloc()` and `free()`, eg"
+msgstr "Выполнение нежелательных действий с `malloc()` и `free()`, например"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:421
+#, no-wrap
+msgid ""
+"char bar[80];\n"
+"free(bar);\n"
+msgstr ""
+"char bar[80];\n"
+"free(bar);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:424
+msgid "or"
+msgstr "или"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:430
+#, no-wrap
+msgid ""
+"char *foo = malloc(27);\n"
+"free(foo);\n"
+"free(foo);\n"
+msgstr ""
+"char *foo = malloc(27);\n"
+"free(foo);\n"
+"free(foo);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:435
+msgid ""
+"Making one of these mistakes will not always lead to an error, but they are "
+"always bad practice. Some systems and compilers are more tolerant than "
+"others, which is why programs that run well on one system can crash when you "
+"try them on another."
+msgstr ""
+"Совершение одной из этих ошибок не всегда приведет к сбою, но это всегда "
+"плохая практика. Некоторые системы и компиляторы более терпимы, чем другие, "
+"поэтому программы, которые хорошо работают на одной системе, могут аварийно "
+"завершаться при попытке запустить их на другой."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:436
+#, no-wrap
+msgid "Sometimes when I get a core dump it says bus error. It says in my UNIX(R) book that this means a hardware problem, but the computer still seems to be working. Is this true?"
+msgstr "Иногда при получении дампа памяти я вижу сообщение ошибки шины (bus error). В моей книге по UNIX(R) сказано, что это означает аппаратную проблему, но компьютер продолжает работать. Это правда?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:440
+msgid ""
+"No, fortunately not (unless of course you really do have a hardware "
+"problem...). This is usually another way of saying that you accessed memory "
+"in a way you should not have."
+msgstr ""
+"Нет, к счастью, нет (если, конечно, у вас действительно нет аппаратной "
+"проблемы...). Обычно это означает, что вы обратились к памяти способом, "
+"который не следует использовать."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:441
+#, no-wrap
+msgid "This dumping core business sounds as though it could be quite useful, if I can make it happen when I want to. Can I do this, or do I have to wait until there is an error?"
+msgstr "Этот процесс создания дампа памяти звучит довольно полезно, если я могу запускать его по своему желанию. Могу ли я это сделать, или нужно ждать возникновения ошибки?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:444
+msgid "Yes, just go to another console or xterm, do"
+msgstr "Можете. Просто перейдите на другую консоль или xterm, выполните"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:448
+#, no-wrap
+msgid "% ps\n"
+msgstr "% ps\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:451
+msgid "to find out the process ID of your program, and do"
+msgstr "чтобы узнать идентификатор процесса вашей программы и выполните"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:455
+#, no-wrap
+msgid "% kill -ABRT pid\n"
+msgstr "% kill -ABRT pid\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:458
+msgid "where `_pid_` is the process ID you looked up."
+msgstr "где `_pid_` — идентификатор процесса, который вы нашли."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:461
+msgid ""
+"This is useful if your program has got stuck in an infinite loop, for "
+"instance. If your program happens to trap SIGABRT, there are several other "
+"signals which have a similar effect."
+msgstr ""
+"Это полезно, если ваша программа зависла в бесконечном цикле, например. Если "
+"ваша программа перехватывает SIGABRT, есть несколько других сигналов, "
+"которые оказывают аналогичный эффект."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:464
+msgid ""
+"Alternatively, you can create a core dump from inside your program, by "
+"calling the `abort()` function. See the manual page of man:abort[3] to "
+"learn more."
+msgstr ""
+"В качестве альтернативы, вы можете создать дамп памяти изнутри вашей "
+"программы, вызвав функцию `abort()`. Дополнительную информацию можно найти "
+"на man:abort[3]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:467
+msgid ""
+"If you want to create a core dump from outside your program, but do not want "
+"the process to terminate, you can use the `gcore` program. See the manual "
+"page of man:gcore[1] for more information."
+msgstr ""
+"Если вы хотите создать дамп памяти извне вашей программы, но не хотите "
+"завершать процесс, вы можете использовать программу `gcore`. Подробнее см. "
+"на странице руководства man:gcore[1]."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:469
+#, no-wrap
+msgid "Make"
+msgstr "Make"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:471
+#, no-wrap
+msgid "What is `make`?"
+msgstr "Что такое `make`?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:474
+msgid ""
+"When you are working on a simple program with only one or two source files, "
+"typing in"
+msgstr ""
+"Когда вы работаете над простой программой с одним или двумя исходными "
+"файлами, вводя"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:478
+#, no-wrap
+msgid "% cc file1.c file2.c\n"
+msgstr "% cc file1.c file2.c\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:481
+msgid ""
+"is not too bad, but it quickly becomes very tedious when there are several "
+"files-and it can take a while to compile, too."
+msgstr ""
+"это не слишком плохо, но быстро становится очень утомительным, когда есть "
+"несколько файлов — и компиляция тоже может занять время."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:484
+msgid ""
+"One way to get around this is to use object files and only recompile the "
+"source file if the source code has changed. So we could have something like:"
+msgstr ""
+"Один из способов обойти это — использовать объектные файлы и "
+"перекомпилировать исходный файл только в случае изменения исходного кода. "
+"Таким образом, у нас может получиться что-то вроде:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:488
+#, no-wrap
+msgid "% cc file1.o file2.o … file37.c …\n"
+msgstr "% cc file1.o file2.o … file37.c …\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:492
+msgid ""
+"if we had changed [.filename]#file37.c#, but not any of the others, since "
+"the last time we compiled. This may speed up the compilation quite a bit, "
+"but does not solve the typing problem."
+msgstr ""
+"если бы мы изменили файл [.filename]#file37.c#, но не трогали остальные с "
+"момента последней компиляции. Это может значительно ускорить компиляцию, но "
+"не решает проблему с вводом."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:494
+msgid ""
+"Or we could write a shell script to solve the typing problem, but it would "
+"have to re-compile everything, making it very inefficient on a large project."
+msgstr ""
+"Или мы могли бы написать shell-скрипт для решения проблемы с вводом, но "
+"тогда пришлось бы перекомпилировать всё, что сделало бы его очень "
+"неэффективным для крупного проекта."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:496
+msgid ""
+"What happens if we have hundreds of source files lying about? What if we are "
+"working in a team with other people who forget to tell us when they have "
+"changed one of their source files that we use?"
+msgstr ""
+"Что произойдет, если у нас есть сотни исходных файлов? Что, если мы работаем "
+"в команде с другими людьми, которые забывают сообщить нам, когда они "
+"изменили один из своих исходных файлов, которые мы используем?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:499
+msgid ""
+"Perhaps we could put the two solutions together and write something like a "
+"shell script that would contain some kind of magic rule saying when a source "
+"file needs compiling. Now all we need now is a program that can understand "
+"these rules, as it is a bit too complicated for the shell."
+msgstr ""
+"Возможно, мы могли бы объединить два решения и написать что-то вроде shell-"
+"скрипта, который содержал бы какое-то волшебное правило, указывающее, когда "
+"исходный файл нужно компилировать. Теперь нам осталось только найти "
+"программу, которая сможет понимать эти правила, так как для shell это "
+"немного слишком сложно."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:504
+msgid ""
+"This program is called `make`. It reads in a file, called a _makefile_, "
+"that tells it how different files depend on each other, and works out which "
+"files need to be re-compiled and which ones do not. For example, a rule "
+"could say something like \"if [.filename]#fromboz.o# is older than "
+"[.filename]#fromboz.c#, that means someone must have changed "
+"[.filename]#fromboz.c#, so it needs to be re-compiled.\" The makefile also "
+"has rules telling make _how_ to re-compile the source file, making it a much "
+"more powerful tool."
+msgstr ""
+"Эта программа называется `make`. Она читает файл, называемый _makefile_, "
+"который указывает, как различные файлы зависят друг от друга, и определяет, "
+"какие файлы нужно перекомпилировать, а какие нет. Например, правило может "
+"звучать так: «если [.filename]#fromboz.o# старше, чем "
+"[.filename]#fromboz.c#, значит, кто-то изменил [.filename]#fromboz.c#, и его "
+"нужно перекомпилировать». В makefile также содержатся правила, указывающие "
+"make, _как_ именно перекомпилировать исходный файл, что делает эту программу "
+"гораздо более мощным инструментом."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:507
+msgid ""
+"Makefiles are typically kept in the same directory as the source they apply "
+"to, and can be called [.filename]#makefile#, [.filename]#Makefile# or "
+"[.filename]#MAKEFILE#. Most programmers use the name [.filename]#Makefile#, "
+"as this puts it near the top of a directory listing, where it can easily be "
+"seen.footnote:[They do not use the MAKEFILE form as block capitals are often "
+"used for documentation files like README.]"
+msgstr ""
+"Файлы Makefile обычно хранятся в том же каталоге, что и исходный код, к "
+"которому они применяются, и могут называться [.filename]#makefile#, "
+"[.filename]#Makefile# или [.filename]#MAKEFILE#. Большинство программистов "
+"используют имя [.filename]#Makefile#, так как это помещает его в начало "
+"списка файлов в каталоге, где его легко заметить.footnote:[Они не используют "
+"форму MAKEFILE, так как заглавные буквы часто применяются для файлов "
+"документации, таких как README.]"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:508
+#, no-wrap
+msgid "Example of Using `make`"
+msgstr "Пример использования `make`"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:511
+msgid "Here is a very simple make file:"
+msgstr "Вот очень простой файл для make:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:516
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:542
+#, no-wrap
+msgid ""
+"foo: foo.c\n"
+"\tcc -o foo foo.c\n"
+msgstr ""
+"foo: foo.c\n"
+"\tcc -o foo foo.c\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:519
+msgid "It consists of two lines, a dependency line and a creation line."
+msgstr "Он состоит из двух строк: строки зависимости и строки создания."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:526
+msgid ""
+"The dependency line here consists of the name of the program (known as the "
+"_target_), followed by a colon, then whitespace, then the name of the source "
+"file. When `make` reads this line, it looks to see if [.filename]#foo# "
+"exists; if it exists, it compares the time [.filename]#foo# was last "
+"modified to the time [.filename]#foo.c# was last modified. If "
+"[.filename]#foo# does not exist, or is older than [.filename]#foo.c#, it "
+"then looks at the creation line to find out what to do. In other words, "
+"this is the rule for working out when [.filename]#foo.c# needs to be re-"
+"compiled."
+msgstr ""
+"Строка зависимости здесь состоит из имени программы (известного как _цель_), "
+"за которым следует двоеточие, пробел и имя исходного файла. Когда `make` "
+"читает эту строку, он проверяет, существует ли файл [.filename]#foo#; если "
+"он существует, программа сравнивает время последнего изменения файла "
+"[.filename]#foo# с временем последнего изменения файла [.filename]#foo.c#. "
+"Если файл [.filename]#foo# не существует или старше файла "
+"[.filename]#foo.c#, программа смотрит на строку создания, чтобы выяснить, "
+"что делать. Другими словами, это правило для определения, когда файл "
+"[.filename]#foo.c# нужно перекомпилировать."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:530
+msgid ""
+"The creation line starts with a tab (press kbd:[tab]) and then the command "
+"you would type to create [.filename]#foo# if you were doing it at a command "
+"prompt. If [.filename]#foo# is out of date, or does not exist, `make` then "
+"executes this command to create it. In other words, this is the rule which "
+"tells make how to re-compile [.filename]#foo.c#."
+msgstr ""
+"Строка создания начинается с табуляции (нажмите kbd:[tab]), а затем следует "
+"команда, которую вы бы ввели для создания [.filename]#foo#, если бы делали "
+"это в командной строке. Если [.filename]#foo# устарел или не существует, "
+"`make` выполняет эту команду для его создания. Другими словами, это правило, "
+"которое сообщает make, как перекомпилировать [.filename]#foo.c#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:534
+msgid ""
+"So, when you type `make`, it will make sure that [.filename]#foo# is up to "
+"date with respect to your latest changes to [.filename]#foo.c#. This "
+"principle can be extended to [.filename]#Makefile#'s with hundreds of "
+"targets-in fact, on FreeBSD, it is possible to compile the entire operating "
+"system just by typing `make buildworld buildkernel` at the top level "
+"directory in the src tree."
+msgstr ""
+"Таким образом, при вводе команды `make` система обеспечит актуальность файла "
+"[.filename]#foo# относительно последних изменений в [.filename]#foo.c#. Этот "
+"принцип можно распространить на [.filename]#Makefile#, содержащие сотни "
+"целей — фактически, в FreeBSD можно собрать всю операционную систему, просто "
+"введя `make buildworld buildkernel` в корневом каталоге дерева исходных "
+"кодов (src)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:537
+msgid ""
+"Another useful property of makefiles is that the targets do not have to be "
+"programs. For instance, we could have a make file that looks like this:"
+msgstr ""
+"Еще одно полезное свойство makefile заключается в том, что цели не "
+"обязательно должны быть программами. Например, у нас может быть makefile, "
+"который выглядит так:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:545
+#, no-wrap
+msgid ""
+"install:\n"
+"\tcp foo /home/me\n"
+msgstr ""
+"install:\n"
+"\tcp foo /home/me\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:548
+msgid "We can tell make which target we want to make by typing:"
+msgstr "Мы можем указать make, какую цель мы хотим собрать, набрав:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:552
+#, no-wrap
+msgid "% make target\n"
+msgstr "% make target\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:556
+msgid ""
+"`make` will then only look at that target and ignore any others. For "
+"example, if we type `make foo` with the makefile above, make will ignore the "
+"`install` target."
+msgstr ""
+"`make` будет рассматривать только указанную цель и игнорировать все "
+"остальные. Например, если мы введём `make foo` с указанным выше makefile, "
+"make проигнорирует цель `install`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:559
+msgid ""
+"If we just type `make` on its own, make will always look at the first target "
+"and then stop without looking at any others. So if we typed `make` here, it "
+"will just go to the `foo` target, re-compile [.filename]#foo# if necessary, "
+"and then stop without going on to the `install` target."
+msgstr ""
+"Если мы просто введем `make` без параметров, make всегда будет обращаться к "
+"первой цели и затем остановится, не рассматривая остальные. Поэтому если мы "
+"введем `make` здесь, он просто перейдет к цели `foo`, перекомпилирует "
+"[.filename]#foo# при необходимости и затем остановится, не переходя к цели "
+"`install`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:563
+msgid ""
+"Notice that the `install` target does not actually depend on anything! This "
+"means that the command on the following line is always executed when we try "
+"to make that target by typing `make install`. In this case, it will copy "
+"[.filename]#foo# into the user's home directory. This is often used by "
+"application makefiles, so that the application can be installed in the "
+"correct directory when it has been correctly compiled."
+msgstr ""
+"Обратите внимание, что цель `install` не зависит ни от чего! Это означает, "
+"что команда в следующей строке всегда выполняется при попытке создать эту "
+"цель с помощью команды `make install`. В данном случае она скопирует "
+"[.filename]#foo# в домашний каталог пользователя. Это часто используется в "
+"makefile приложений, чтобы приложение можно было установить в правильный "
+"каталог после успешной компиляции."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:568
+msgid ""
+"This is a slightly confusing subject to try to explain. If you do not quite "
+"understand how `make` works, the best thing to do is to write a simple "
+"program like \"hello world\" and a make file like the one above and "
+"experiment. Then progress to using more than one source file, or having the "
+"source file include a header file. `touch` is very useful here-it changes "
+"the date on a file without you having to edit it."
+msgstr ""
+"Это немного запутанная тема для объяснения. Если вы не до конца понимаете, "
+"как работает `make`, лучше всего написать простую программу, например, "
+"\"hello world\", и make-файл, как указано выше, и поэкспериментировать. "
+"Затем можно перейти к использованию нескольких исходных файлов или "
+"добавлению заголовочного файла в исходный код. В этом случае очень полезен "
+"`touch` — он изменяет дату файла без необходимости его редактирования."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:569
+#, no-wrap
+msgid "Make and include-files"
+msgstr "make и include-файлы"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:573
+msgid ""
+"C code often starts with a list of files to include, for example stdio.h. "
+"Some of these files are system-include files, some of them are from the "
+"project you are now working on:"
+msgstr ""
+"Код на C часто начинается со списка подключаемых файлов, например stdio.h. "
+"Некоторые из этих файлов являются системными, а некоторые принадлежат "
+"текущему проекту:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:578
+#, no-wrap
+msgid ""
+"#include <stdio.h>\n"
+"#include \"foo.h\"\n"
+msgstr ""
+"#include <stdio.h>\n"
+"#include \"foo.h\"\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:580
+#, no-wrap
+msgid "int main(....\n"
+msgstr "int main(....\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:583
+msgid ""
+"To make sure that this file is recompiled the moment [.filename]#foo.h# is "
+"changed, you have to add it in your [.filename]#Makefile#:"
+msgstr ""
+"Чтобы убедиться, что этот файл перекомпилируется при изменении "
+"[.filename]#foo.h#, необходимо добавить его в [.filename]#Makefile#:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:587
+#, no-wrap
+msgid "foo: foo.c foo.h\n"
+msgstr "foo: foo.c foo.h\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:594
+msgid ""
+"The moment your project is getting bigger and you have more and more own "
+"include-files to maintain, it will be a pain to keep track of all include "
+"files and the files which are depending on it. If you change an include-"
+"file but forget to recompile all the files which are depending on it, the "
+"results will be devastating. `clang` has an option to analyze your files "
+"and to produce a list of include-files and their dependencies: `-MM`."
+msgstr ""
+"В момент, когда ваш проект становится больше и у вас появляется все больше "
+"собственных включаемых файлов для поддержки, отслеживание всех включаемых "
+"файлов и файлов, которые от них зависят, становится проблемой. Если вы "
+"измените включаемый файл, но забудете перекомпилировать все файлы, которые "
+"от него зависят, последствия будут катастрофическими. У `clang` есть опция "
+"для анализа ваших файлов и создания списка включаемых файлов и их "
+"зависимостей: `-MM`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:596
+msgid "If you add this to your Makefile:"
+msgstr "Если вы добавите это в ваш Makefile:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:601
+#, no-wrap
+msgid ""
+"depend:\n"
+"\tcc -E -MM *.c > .depend\n"
+msgstr ""
+"depend:\n"
+"\tcc -E -MM *.c > .depend\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:604
+msgid ""
+"and run `make depend`, the file [.filename]#.depend# will appear with a list "
+"of object-files, C-files and the include-files:"
+msgstr ""
+"и выполните `make depend`, появится файл [.filename]#.depend# со списком "
+"объектных файлов, C-файлов и включаемых файлов:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:608
+#, no-wrap
+msgid "foo.o: foo.c foo.h\n"
+msgstr "foo.o: foo.c foo.h\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:611
+msgid ""
+"If you change [.filename]#foo.h#, next time you run `make` all files "
+"depending on [.filename]#foo.h# will be recompiled."
+msgstr ""
+"Если вы измените файл [.filename]#foo.h#, при следующем запуске `make` все "
+"файлы, зависящие от [.filename]#foo.h#, будут перекомпилированы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:613
+msgid ""
+"Do not forget to run `make depend` each time you add an include-file to one "
+"of your files."
+msgstr ""
+"Не забудьте выполнить `make depend` каждый раз, когда вы добавляете include-"
+"файл в один из своих файлов."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:614
+#, no-wrap
+msgid "FreeBSD Makefiles"
+msgstr "Файлы Makefile системы FreeBSD"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:620
+msgid ""
+"Makefiles can be rather complicated to write. Fortunately, BSD-based "
+"systems like FreeBSD come with some very powerful ones as part of the "
+"system. One very good example of this is the FreeBSD ports system. Here is "
+"the essential part of a typical ports [.filename]#Makefile#:"
+msgstr ""
+"Makefile-ы могут быть довольно сложными для написания. К счастью, в BSD-"
+"системах, таких как FreeBSD, есть очень мощные Makefile-ы, поставляемые в "
+"составе системы. Отличным примером этого является система портов FreeBSD. "
+"Вот основная часть типичного [.filename]#Makefile# для портов:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:625
+#, no-wrap
+msgid ""
+"MASTER_SITES= ftp://freefall.cdrom.com/pub/FreeBSD/LOCAL_PORTS/\n"
+"DISTFILES= scheme-microcode+dist-7.3-freebsd.tgz\n"
+msgstr ""
+"MASTER_SITES= ftp://freefall.cdrom.com/pub/FreeBSD/LOCAL_PORTS/\n"
+"DISTFILES= scheme-microcode+dist-7.3-freebsd.tgz\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:627
+#, no-wrap
+msgid ".include <bsd.port.mk>\n"
+msgstr ".include <bsd.port.mk>\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:630
+msgid ""
+"Now, if we go to the directory for this port and type `make`, the following "
+"happens:"
+msgstr ""
+"Теперь, если мы перейдем в каталог этого порта и наберем `make`, произойдет "
+"следующее:"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:633
+msgid ""
+"A check is made to see if the source code for this port is already on the "
+"system."
+msgstr "Проверяется, есть ли исходный код этого порта уже в системе."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:634
+msgid ""
+"If it is not, an FTP connection to the URL in MASTER_SITES is set up to "
+"download the source."
+msgstr ""
+"Если это не так, устанавливается FTP-соединение с URL в MASTER_SITES для "
+"загрузки исходного кода."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:635
+msgid ""
+"The checksum for the source is calculated and compared it with one for a "
+"known, good, copy of the source. This is to make sure that the source was "
+"not corrupted while in transit."
+msgstr ""
+"Контрольная сумма исходного кода вычисляется и сравнивается с контрольной "
+"суммой известной и хорошей копии исходного кода. Это делается для того, "
+"чтобы убедиться, что исходный код не был поврежден во время передачи."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:636
+msgid ""
+"Any changes required to make the source work on FreeBSD are applied-this is "
+"known as _patching_."
+msgstr ""
+"Все необходимые изменения для адаптации исходного кода к работе в FreeBSD "
+"применяются — это называется применением _патча_."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:637
+msgid ""
+"Any special configuration needed for the source is done. (Many UNIX(R) "
+"program distributions try to work out which version of UNIX(R) they are "
+"being compiled on and which optional UNIX(R) features are present-this is "
+"where they are given the information in the FreeBSD ports scenario)."
+msgstr ""
+"Любая необходимая специальная настройка для исходного кода выполнена. "
+"(Многие дистрибутивы программ UNIX(R) пытаются определить, на какой версии "
+"UNIX(R) они компилируются и какие дополнительные функции UNIX(R) доступны — "
+"именно здесь они получают эту информацию в сценарии портов FreeBSD)."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:638
+msgid ""
+"The source code for the program is compiled. In effect, we change to the "
+"directory where the source was unpacked and do `make`-the program's own make "
+"file has the necessary information to build the program."
+msgstr ""
+"Компилируется исходный код программы. По сути, мы переходим в каталог, куда "
+"были распакованы исходные файлы, и выполняем `make` — собственный make-файл "
+"программы содержит необходимую информацию для сборки программы."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:639
+msgid ""
+"We now have a compiled version of the program. If we wish, we can test it "
+"now; when we feel confident about the program, we can type `make install`. "
+"This will cause the program and any supporting files it needs to be copied "
+"into the correct location; an entry is also made into a `package database`, "
+"so that the port can easily be uninstalled later if we change our mind about "
+"it."
+msgstr ""
+"Теперь у нас есть скомпилированная версия программы. При желании мы можем "
+"протестировать её сейчас; когда мы уверены в программе, можно ввести `make "
+"install`. Это приведёт к копированию программы и всех необходимых "
+"вспомогательных файлов в нужные места, а также к добавлению записи в `базу "
+"данных пакетов`, чтобы позже можно было легко удалить порт, если мы "
+"передумаем."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:641
+msgid ""
+"Now I think you will agree that is rather impressive for a four line script!"
+msgstr ""
+"Вот теперь, я думаю, вы согласитесь, что это довольно впечатляюще для "
+"скрипта из четырёх строк!"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:645
+msgid ""
+"The secret lies in the last line, which tells `make` to look in the system "
+"makefile called [.filename]#bsd.port.mk#. It is easy to overlook this line, "
+"but this is where all the clever stuff comes from-someone has written a "
+"makefile that tells `make` to do all the things above (plus a couple of "
+"other things I did not mention, including handling any errors that may "
+"occur) and anyone can get access to that just by putting a single line in "
+"their own make file!"
+msgstr ""
+"Секрет кроется в последней строке, которая указывает `make` обратиться к "
+"системному makefile под названием [.filename]#bsd.port.mk#. Эту строку легко "
+"пропустить, но именно здесь начинается вся магия — кто-то написал makefile, "
+"который предписывает `make` выполнить все вышеперечисленные действия (плюс "
+"несколько других, которые я не упомянул, включая обработку возможных "
+"ошибок), и любой может получить доступ к этому функционалу, просто добавив "
+"одну строку в свой собственный makefile!"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:649
+msgid ""
+"If you want to have a look at these system makefiles, they are in "
+"[.filename]#/usr/share/mk#, but it is probably best to wait until you have "
+"had a bit of practice with makefiles, as they are very complicated (and if "
+"you do look at them, make sure you have a flask of strong coffee handy!)"
+msgstr ""
+"Если вы хотите взглянуть на эти системные makefile-ы, они находятся в "
+"[.filename]#/usr/share/mk#, но, вероятно, лучше подождать, пока у вас не "
+"появится немного практики с makefile, так как они очень сложные (и если вы "
+"всё же решите их посмотреть, убедитесь, что у вас под рукой есть фляга "
+"крепкого кофе!)"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:650
+#, no-wrap
+msgid "More Advanced Uses of `make`"
+msgstr "Более сложные способы использования `make`"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:656
+msgid ""
+"`Make` is a very powerful tool, and can do much more than the simple example "
+"above shows. Unfortunately, there are several different versions of `make`, "
+"and they all differ considerably. The best way to learn what they can do is "
+"probably to read the documentation-hopefully this introduction will have "
+"given you a base from which you can do this. The man:make[1] manual page "
+"offers a comprehensive discussion of variables, arguments, and how to use "
+"make."
+msgstr ""
+"`Make` — это очень мощный инструмент, способный на гораздо большее, чем "
+"показано в простом примере выше. К сожалению, существует несколько различных "
+"версий `make`, и все они значительно отличаются друг от друга. Лучший способ "
+"узнать, на что они способны, — вероятно, прочитать документацию. Надеюсь, "
+"это введение дало вам основу, с которой вы сможете это сделать. В "
+"man:make[1] подробно обсуждаются переменные, аргументы и то, как "
+"использовать `make`."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:660
+msgid ""
+"Many applications in the ports use GNU make, which has a very good set of "
+"\"info\" pages. If you have installed any of these ports, GNU make will "
+"automatically have been installed as `gmake`. It is also available as a "
+"port and package in its own right."
+msgstr ""
+"Многие приложения в портах используют GNU make, который имеет очень хороший "
+"набор страниц \"info\". Если вы установили любой из этих портов, GNU make "
+"будет автоматически установлен как `gmake`. Он также доступен как отдельный "
+"порт и пакет."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:663
+msgid ""
+"To view the info pages for GNU make, you will have to edit [.filename]#dir# "
+"in the [.filename]#/usr/local/info# directory to add an entry for it. This "
+"involves adding a line like"
+msgstr ""
+"Для просмотра справочных страниц (info) GNU make вам потребуется "
+"отредактировать файл [.filename]#dir# в каталоге [.filename]#/usr/local/"
+"info#, добавив соответствующую запись. Добавьте строку"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:667
+#, no-wrap
+msgid " * Make: (make). The GNU Make utility.\n"
+msgstr " * Make: (make). The GNU Make utility.\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:670
+msgid ""
+"to the file. Once you have done this, you can type `info` and then select "
+"[.guimenuitem]#make# from the menu (or in Emacs, do `C-h i`)."
+msgstr ""
+"в файл. После этого вы можете ввести `info` и затем выбрать "
+"[.guimenuitem]#make# из меню (или в Emacs выполнить `C-h i`)."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:672
+#, no-wrap
+msgid "Debugging"
+msgstr "Отладка"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:674
+#, no-wrap
+msgid "Introduction to Available Debuggers"
+msgstr "Обзор отладчиков, поставляемых в системе"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:679
+msgid ""
+"Using a debugger allows running the program under more controlled "
+"circumstances. Typically, it is possible to step through the program a line "
+"at a time, inspect the value of variables, change them, tell the debugger to "
+"run up to a certain point and then stop, and so on. It is also possible to "
+"attach to a program that is already running, or load a core file to "
+"investigate why the program crashed."
+msgstr ""
+"Использование отладчика позволяет запускать программу в более контролируемых "
+"условиях. Обычно можно выполнять программу построчно, проверять значения "
+"переменных, изменять их, указывать отладчику выполнение до определённой "
+"точки и затем останавливаться и так далее. Также можно подключиться к уже "
+"работающей программе или загрузить core-файл, чтобы исследовать причину "
+"аварийного завершения программы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:682
+msgid ""
+"This section is intended to be a quick introduction to using debuggers and "
+"does not cover specialized topics such as debugging the kernel. For more "
+"information about that, refer to crossref:kerneldebug[kerneldebug,Kernel "
+"Debugging]."
+msgstr ""
+"Этот раздел представляет собой краткое введение в использование отладчиков и "
+"не затрагивает специализированные темы, такие как отладка ядра. Для "
+"получения дополнительной информации по этой теме обратитесь к главе "
+"crossref:kerneldebug[kerneldebug,Отладка ядра]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:686
+msgid ""
+"The standard debugger supplied with FreeBSD is called `lldb` (LLVM "
+"debugger). As it is part of the standard installation for that release, "
+"there is no need to do anything special to use it. It has good command "
+"help, accessible via the `help` command, as well as https://lldb.llvm.org/[a "
+"web tutorial and documentation]."
+msgstr ""
+"Стандартный отладчик, поставляемый с FreeBSD, называется `lldb` (LLVM "
+"debugger). Поскольку он является частью стандартной установки для данного "
+"выпуска, нет необходимости выполнять какие-либо дополнительные действия для "
+"его использования. Он обладает хорошей справкой по командам, доступной через "
+"команду `help`, а также https://lldb.llvm.org/[руководством и документацией "
+"в интернете]."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:690
+msgid ""
+"The `lldb` command is also available extref:{handbook}ports/[from ports or "
+"packages, ports-using] as package:devel/llvm[]."
+msgstr ""
+"Команда `lldb` также доступна extref:{handbook}ports/[из портов или пакетов, "
+"ports-using] как пакет package:devel/llvm[]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:696
+msgid ""
+"The other debugger available with FreeBSD is called `gdb` (GNU debugger). "
+"Unlike lldb, it is not installed by default on FreeBSD; to use it, extref:"
+"{handbook}#ports-using/[install] package:devel/gdb[] from ports or "
+"packages. It has excellent on-line help, as well as a set of info pages."
+msgstr ""
+"Другой отладчик, доступный в FreeBSD, называется `gdb` (GNU debugger). В "
+"отличие от lldb, он не устанавливается по умолчанию в FreeBSD; для его "
+"использования необходимо extref:{handbook}#ports-using/[установить] пакет "
+"package:devel/gdb[] из портов или пакетов. Он обладает отличной встроенной "
+"справкой, а также набором info-страниц."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:701
+msgid ""
+"The two debuggers have a similar feature set, so which one to use is largely "
+"a matter of taste. If familiar with one only, use that one. People "
+"familiar with neither or both but wanting to use one from inside Emacs will "
+"need to use `gdb` as `lldb` is unsupported by Emacs. Otherwise, try both "
+"and see which one you prefer."
+msgstr ""
+"Два отладчика обладают схожим набором функций, поэтому выбор между ними в "
+"основном зависит от личных предпочтений. Если вы знакомы только с одним из "
+"них, используйте его. Тем, кто не знаком ни с одним или знаком с обоими, но "
+"хочет использовать отладчик внутри Emacs, придётся выбрать `gdb`, так как "
+"`lldb` не поддерживается Emacs. В остальных случаях попробуйте оба и решите, "
+"какой вам больше нравится."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:702
+#, no-wrap
+msgid "Using lldb"
+msgstr "Использование lldb"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:704
+#, no-wrap
+msgid "Starting lldb"
+msgstr "Запуск lldb"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:707
+msgid "Start up lldb by typing"
+msgstr "Запустите lldb, набрав"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:711
+#, no-wrap
+msgid "% lldb -- progname\n"
+msgstr "% lldb -- progname\n"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:713
+#, no-wrap
+msgid "Running a Program with lldb"
+msgstr "Запуск программы с lldb"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:718
+msgid ""
+"Compile the program with `-g` to get the most out of using `lldb`. It will "
+"work without, but will only display the name of the function currently "
+"running, instead of the source code. If it displays a line like:"
+msgstr ""
+"Скомпилируйте программу с `-g`, чтобы максимально использовать возможности "
+"`lldb`. Без этого флаг она будет работать, но отображать только имя текущей "
+"выполняемой функции вместо исходного кода. Если отображается строка вида:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:722
+#, no-wrap
+msgid "Breakpoint 1: where = temp`main, address = …\n"
+msgstr "Breakpoint 1: where = temp`main, address = …\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:725
+msgid ""
+"(without an indication of source code filename and line number) when setting "
+"a breakpoint, this means that the program was not compiled with `-g`."
+msgstr ""
+"(без указания имени файла исходного кода и номера строки) при установке "
+"точки останова это означает, что программа не была скомпилирована с "
+"параметром `-g`."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:730
+msgid ""
+"Most `lldb` commands have shorter forms that can be used instead. The "
+"longer forms are used here for clarity."
+msgstr ""
+"Большинство команд `lldb` имеют более короткие формы, которые можно "
+"использовать вместо полных. Здесь используются полные формы для ясности."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:735
+msgid ""
+"At the `lldb` prompt, type `breakpoint set -n main`. This will tell the "
+"debugger not to display the preliminary set-up code in the program being run "
+"and to stop execution at the beginning of the program's code. Now type "
+"`process launch` to actually start the program- it will start at the "
+"beginning of the set-up code and then get stopped by the debugger when it "
+"calls `main()`."
+msgstr ""
+"На строке `lldb` введите `breakpoint set -n main`. Это укажет отладчику не "
+"показывать предварительный код настройки в запускаемой программе и "
+"остановить выполнение в начале кода программы. Теперь введите `process "
+"launch`, чтобы фактически запустить программу — она начнётся с кода "
+"настройки, а затем будет остановлена отладчиком при вызове `main()`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:739
+msgid ""
+"To step through the program a line at a time, type `thread step-over`. When "
+"the program gets to a function call, step into it by typing `thread step-"
+"in`. Once in a function call, return from it by typing `thread step-out` or "
+"use `up` and `down` to take a quick look at the caller."
+msgstr ""
+"Для пошагового выполнения программы строка за строкой введите `thread step-"
+"over`. Когда программа дойдёт до вызова функции, войдите в неё, набрав "
+"`thread step-in`. Оказавшись внутри вызова функции, вернитесь из него с "
+"помощью команды `thread step-out` или используйте `up` и `down`, чтобы "
+"быстро посмотреть на вызывающий код."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:742
+msgid ""
+"Here is a simple example of how to spot a mistake in a program with `lldb`. "
+"This is our program (with a deliberate mistake):"
+msgstr ""
+"Вот простой пример того, как найти ошибку в программе с помощью `lldb`. Это "
+"наша программа (с умышленной ошибкой):"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:746
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1020
+#, no-wrap
+msgid "#include <stdio.h>\n"
+msgstr "#include <stdio.h>\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:748
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1022
+#, no-wrap
+msgid "int bazz(int anint);\n"
+msgstr "int bazz(int anint);\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:751
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1025
+#, no-wrap
+msgid ""
+"main() {\n"
+"\tint i;\n"
+msgstr ""
+"main() {\n"
+"\tint i;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:756
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1030
+#, no-wrap
+msgid ""
+"\tprintf(\"This is my program\\n\");\n"
+"\tbazz(i);\n"
+"\treturn 0;\n"
+"}\n"
+msgstr ""
+"\tprintf(\"This is my program\\n\");\n"
+"\tbazz(i);\n"
+"\treturn 0;\n"
+"}\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:761
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1035
+#, no-wrap
+msgid ""
+"int bazz(int anint) {\n"
+"\tprintf(\"You gave me %d\\n\", anint);\n"
+"\treturn anint;\n"
+"}\n"
+msgstr ""
+"int bazz(int anint) {\n"
+"\tprintf(\"You gave me %d\\n\", anint);\n"
+"\treturn anint;\n"
+"}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:764
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1038
+msgid ""
+"This program sets i to be `5` and passes it to a function `bazz()` which "
+"prints out the number we gave it."
+msgstr ""
+"Эта программа устанавливает значение `i` равным `5` и передает его в функцию "
+"`bazz()`, которая выводит переданное число."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:766
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1040
+msgid "Compiling and running the program displays"
+msgstr "Компиляция и запуск программы отображают"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:773
+#, no-wrap
+msgid ""
+"% cc -g -o temp temp.c\n"
+"% ./temp\n"
+"This is my program\n"
+"anint = -5360\n"
+msgstr ""
+"% cc -g -o temp temp.c\n"
+"% ./temp\n"
+"This is my program\n"
+"anint = -5360\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:776
+msgid "That is not what was expected! Time to see what is going on!"
+msgstr "Это не то, что ожидалось! Пора разобраться, что происходит!"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:787
+#, no-wrap
+msgid ""
+"% lldb -- temp\n"
+"(lldb) target create \"temp\"\n"
+"Current executable set to 'temp' (x86_64).\n"
+"(lldb) breakpoint set -n main\t\t\t\tSkip the set-up code\n"
+"Breakpoint 1: where = temp`main + 15 at temp.c:8:2, address = 0x00000000002012ef\tlldb puts breakpoint at main()\n"
+"(lldb) process launch\t\t\t\t\tRun as far as main()\n"
+"Process 9992 launching\n"
+"Process 9992 launched: '/home/pauamma/tmp/temp' (x86_64)\tProgram starts running\n"
+msgstr ""
+"% lldb -- temp\n"
+"(lldb) target create \"temp\"\n"
+"Current executable set to 'temp' (x86_64).\n"
+"(lldb) breakpoint set -n main\t\t\t\tSkip the set-up code\n"
+"Breakpoint 1: where = temp`main + 15 at temp.c:8:2, address = 0x00000000002012ef\tlldb puts breakpoint at main()\n"
+"(lldb) process launch\t\t\t\t\tRun as far as main()\n"
+"Process 9992 launching\n"
+"Process 9992 launched: '/home/pauamma/tmp/temp' (x86_64)\tProgram starts running\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:821
+#, no-wrap
+msgid ""
+"Process 9992 stopped\n"
+"* thread #1, name = 'temp', stop reason = breakpoint 1.1\tlldb stops at main()\n"
+" frame #0: 0x00000000002012ef temp`main at temp.c:8:2\n"
+" 5\tmain() {\n"
+" 6\t\tint i;\n"
+" 7\n"
+"-> 8\t\tprintf(\"This is my program\\n\");\t\t\tIndicates the line where it stopped\n"
+" 9\t\tbazz(i);\n"
+" 10\t\treturn 0;\n"
+" 11\t}\n"
+"(lldb) thread step-over\t\t\tGo to next line\n"
+"This is my program\t\t\t\t\t\tProgram prints out\n"
+"Process 9992 stopped\n"
+"* thread #1, name = 'temp', stop reason = step over\n"
+" frame #0: 0x0000000000201300 temp`main at temp.c:9:7\n"
+" 6\t\tint i;\n"
+" 7\n"
+" 8\t\tprintf(\"This is my program\\n\");\n"
+"-> 9\t\tbazz(i);\n"
+" 10\t\treturn 0;\n"
+" 11\t}\n"
+" 12\n"
+"(lldb) thread step-in\t\t\tstep into bazz()\n"
+"Process 9992 stopped\n"
+"* thread #1, name = 'temp', stop reason = step in\n"
+" frame #0: 0x000000000020132b temp`bazz(anint=-5360) at temp.c:14:29\tlldb displays stack frame\n"
+" 11\t}\n"
+" 12\n"
+" 13\tint bazz(int anint) {\n"
+"-> 14\t\tprintf(\"You gave me %d\\n\", anint);\n"
+" 15\t\treturn anint;\n"
+" 16\t}\n"
+"(lldb)\n"
+msgstr ""
+"Process 9992 stopped\n"
+"* thread #1, name = 'temp', stop reason = breakpoint 1.1\tlldb stops at main()\n"
+" frame #0: 0x00000000002012ef temp`main at temp.c:8:2\n"
+" 5\tmain() {\n"
+" 6\t\tint i;\n"
+" 7\n"
+"-> 8\t\tprintf(\"This is my program\\n\");\t\t\tIndicates the line where it stopped\n"
+" 9\t\tbazz(i);\n"
+" 10\t\treturn 0;\n"
+" 11\t}\n"
+"(lldb) thread step-over\t\t\tGo to next line\n"
+"This is my program\t\t\t\t\t\tProgram prints out\n"
+"Process 9992 stopped\n"
+"* thread #1, name = 'temp', stop reason = step over\n"
+" frame #0: 0x0000000000201300 temp`main at temp.c:9:7\n"
+" 6\t\tint i;\n"
+" 7\n"
+" 8\t\tprintf(\"This is my program\\n\");\n"
+"-> 9\t\tbazz(i);\n"
+" 10\t\treturn 0;\n"
+" 11\t}\n"
+" 12\n"
+"(lldb) thread step-in\t\t\tstep into bazz()\n"
+"Process 9992 stopped\n"
+"* thread #1, name = 'temp', stop reason = step in\n"
+" frame #0: 0x000000000020132b temp`bazz(anint=-5360) at temp.c:14:29\tlldb displays stack frame\n"
+" 11\t}\n"
+" 12\n"
+" 13\tint bazz(int anint) {\n"
+"-> 14\t\tprintf(\"You gave me %d\\n\", anint);\n"
+" 15\t\treturn anint;\n"
+" 16\t}\n"
+"(lldb)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:824
+msgid ""
+"Hang on a minute! How did anint get to be `-5360`? Was it not set to `5` in "
+"`main()`? Let us move up to `main()` and have a look."
+msgstr ""
+"Подождите минуту! Как переменная `int` стала равна `-5360`? Разве она не "
+"была установлена в `5` в `main()`? Давайте поднимемся к `main()` и посмотрим."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:838
+#, no-wrap
+msgid ""
+"(lldb) up\t\tMove up call stack\n"
+"frame #1: 0x000000000020130b temp`main at temp.c:9:2\t\tlldb displays stack frame\n"
+" 6\t\tint i;\n"
+" 7\n"
+" 8\t\tprintf(\"This is my program\\n\");\n"
+"-> 9\t\tbazz(i);\n"
+" 10\t\treturn 0;\n"
+" 11\t}\n"
+" 12\n"
+"(lldb) frame variable i\t\t\tShow us the value of i\n"
+"(int) i = -5360\t\t\t\t\t\t\tlldb displays -5360\n"
+msgstr ""
+"(lldb) up\t\tMove up call stack\n"
+"frame #1: 0x000000000020130b temp`main at temp.c:9:2\t\tlldb displays stack frame\n"
+" 6\t\tint i;\n"
+" 7\n"
+" 8\t\tprintf(\"This is my program\\n\");\n"
+"-> 9\t\tbazz(i);\n"
+" 10\t\treturn 0;\n"
+" 11\t}\n"
+" 12\n"
+"(lldb) frame variable i\t\t\tShow us the value of i\n"
+"(int) i = -5360\t\t\t\t\t\t\tlldb displays -5360\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:842
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1083
+msgid ""
+"Oh dear! Looking at the code, we forgot to initialize i. We meant to put"
+msgstr "О боже! Глядя на код, мы забыли инициализировать i. Мы хотели добавить"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:848
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1089
+#, no-wrap
+msgid ""
+"...\n"
+"main() {\n"
+"\tint i;\n"
+msgstr ""
+"...\n"
+"main() {\n"
+"\tint i;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:852
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1093
+#, no-wrap
+msgid ""
+"\ti = 5;\n"
+"\tprintf(\"This is my program\\n\");\n"
+"...\n"
+msgstr ""
+"\ti = 5;\n"
+"\tprintf(\"This is my program\\n\");\n"
+"...\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:857
+msgid ""
+"but we left the `i=5;` line out. As we did not initialize i, it had "
+"whatever number happened to be in that area of memory when the program ran, "
+"which in this case happened to be `-5360`."
+msgstr ""
+"но мы пропустили строку `i=5;`. Поскольку мы не инициализировали `i`, она "
+"содержала любое число, которое оказалось в той области памяти при запуске "
+"программы, и в данном случае это оказалось `-5360`."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:863
+msgid ""
+"The `lldb` command displays the stack frame every time we go into or out of "
+"a function, even if we are using `up` and `down` to move around the call "
+"stack. This shows the name of the function and the values of its arguments, "
+"which helps us keep track of where we are and what is going on. (The stack "
+"is a storage area where the program stores information about the arguments "
+"passed to functions and where to go when it returns from a function call.)"
+msgstr ""
+"Команда `lldb` отображает стек вызовов каждый раз, когда мы входим в функцию "
+"или выходим из неё, даже если мы используем `up` и `down` для перемещения по "
+"стеку вызовов. Это показывает имя функции и значения её аргументов, что "
+"помогает отслеживать текущее положение и происходящее. (Стек — это область "
+"хранения, где программа сохраняет информацию об аргументах, переданных в "
+"функции, и о том, куда возвращаться после вызова функции.)"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:865
+#, no-wrap
+msgid "Examining a Core File with lldb"
+msgstr "Изучение файла Core с помощью lldb"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:870
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1111
+msgid ""
+"A core file is basically a file which contains the complete state of the "
+"process when it crashed. In \"the good old days\", programmers had to print "
+"out hex listings of core files and sweat over machine code manuals, but now "
+"life is a bit easier. Incidentally, under FreeBSD and other 4.4BSD systems, "
+"a core file is called [.filename]#progname.core# instead of just "
+"[.filename]#core#, to make it clearer which program a core file belongs to."
+msgstr ""
+"Файл core — это, по сути, файл, содержащий полное состояние процесса на "
+"момент его аварийного завершения. В «старые добрые времена» программистам "
+"приходилось распечатывать шестнадцатеричные дампы файлов core и корпеть над "
+"руководствами по машинному коду, но сейчас жизнь стала немного проще. "
+"Кстати, в FreeBSD и других системах на базе 4.4BSD файл core называется "
+"[.filename]#progname.core#, а не просто [.filename]#core#, чтобы было "
+"понятнее, какой программе он принадлежит."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:873
+msgid ""
+"To examine a core file, specify the name of the core file in addition to the "
+"program itself. Instead of starting up `lldb` in the usual way, type `lldb "
+"-c _progname_.core \\-- _progname_`."
+msgstr ""
+"Для анализа файла core укажите имя файла core в дополнение к самой "
+"программе. Вместо обычного запуска `lldb` введите `lldb -c "
+"_имя_программы_.core \\-- _имя_программы_`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:875
+msgid "The debugger will display something like this:"
+msgstr "Отладчик отобразит что-то вроде этого:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:882
+#, no-wrap
+msgid ""
+"% lldb -c [.filename]#progname.core# -- [.filename]#progname#\n"
+"(lldb) target create \"[.filename]#progname#\" --core \"[.filename]#progname#.core\"\n"
+"Core file '/home/pauamma/tmp/[.filename]#progname.core#' (x86_64) was loaded.\n"
+"(lldb)\n"
+msgstr ""
+"% lldb -c [.filename]#progname.core# -- [.filename]#progname#\n"
+"(lldb) target create \"[.filename]#progname#\" --core \"[.filename]#progname#.core\"\n"
+"Core file '/home/pauamma/tmp/[.filename]#progname.core#' (x86_64) was loaded.\n"
+"(lldb)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:888
+msgid ""
+"In this case, the program was called [.filename]#progname#, so the core file "
+"is called [.filename]#progname.core#. The debugger does not display why the "
+"program crashed or where. For this, use `thread backtrace all`. This will "
+"also show how the function where the program dumped core was called."
+msgstr ""
+"В этом случае программа называлась [.filename]#progname#, поэтому файл дампа "
+"имеет имя [.filename]#progname.core#. Отладчик не показывает, почему "
+"программа завершилась аварийно или где это произошло. Для этого используйте "
+"команду `thread backtrace all`. Она также покажет, как была вызвана функция, "
+"в которой программа завершилась дампом ядра."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:897
+#, no-wrap
+msgid ""
+"(lldb) thread backtrace all\n"
+"* thread #1, name = 'progname', stop reason = signal SIGSEGV\n"
+" * frame #0: 0x0000000000201347 progname`bazz(anint=5) at temp2.c:17:10\n"
+" frame #1: 0x0000000000201312 progname`main at temp2.c:10:2\n"
+" frame #2: 0x000000000020110f progname`_start(ap=<unavailable>, cleanup=<unavailable>) at crt1.c:76:7\n"
+"(lldb)\n"
+msgstr ""
+"(lldb) thread backtrace all\n"
+"* thread #1, name = 'progname', stop reason = signal SIGSEGV\n"
+" * frame #0: 0x0000000000201347 progname`bazz(anint=5) at temp2.c:17:10\n"
+" frame #1: 0x0000000000201312 progname`main at temp2.c:10:2\n"
+" frame #2: 0x000000000020110f progname`_start(ap=<unavailable>, cleanup=<unavailable>) at crt1.c:76:7\n"
+"(lldb)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:902
+msgid ""
+"`SIGSEGV` indicates that the program tried to access memory (run code or "
+"read/write data usually) at a location that does not belong to it, but does "
+"not give any specifics. For that, look at the source code at line 10 of "
+"file temp2.c, in `bazz()`. The backtrace also says that in this case, "
+"`bazz()` was called from `main()`."
+msgstr ""
+"`SIGSEGV` указывает, что программа пыталась получить доступ к памяти (обычно "
+"выполнить код или прочитать/записать данные) по адресу, который ей не "
+"принадлежит, но не предоставляет конкретных деталей. Для этого обратитесь к "
+"исходному коду на строке 10 файла temp2.c, в функции `bazz()`. Трассировка "
+"также показывает, что в данном случае `bazz()` была вызвана из `main()`."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:903
+#, no-wrap
+msgid "Attaching to a Running Program with lldb"
+msgstr "Подключение к работающей программе с помощью lldb"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:908
+msgid ""
+"One of the neatest features about `lldb` is that it can attach to a program "
+"that is already running. Of course, that requires sufficient permissions to "
+"do so. A common problem is stepping through a program that forks and "
+"wanting to trace the child, but the debugger will only trace the parent."
+msgstr ""
+"Одной из самых замечательных особенностей `lldb` является возможность "
+"подключения к уже работающей программе. Конечно, для этого требуются "
+"соответствующие разрешения. Распространённая проблема — пошаговое выполнение "
+"программы, которая создаёт ответвления, с необходимостью отслеживать "
+"дочерний процесс, но отладчик отслеживает только родительский."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:910
+msgid ""
+"To do that, start up another `lldb`, use `ps` to find the process ID for the "
+"child, and do"
+msgstr ""
+"Для этого запустите другой `lldb`, используйте `ps` для поиска "
+"идентификатора процесса дочернего процесса и выполните"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:914
+#, no-wrap
+msgid "(lldb) process attach -p pid\n"
+msgstr "(lldb) process attach -p pid\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:917
+msgid "in `lldb`, and then debug as usual."
+msgstr "в `lldb`, а затем отлаживайте как обычно."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:919
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1174
+msgid ""
+"For that to work well, the code that calls `fork` to create the child needs "
+"to do something like the following (courtesy of the `gdb` info pages):"
+msgstr ""
+"Для того чтобы это работало правильно, код, который вызывает `fork` для "
+"создания дочернего процесса, должен делать что-то вроде следующего "
+"(предоставлено из документации `gdb`):"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:927
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1182
+#, no-wrap
+msgid ""
+"...\n"
+"if ((pid = fork()) < 0)\t\t/* _Always_ check this */\n"
+"\terror();\n"
+"else if (pid == 0) {\t\t/* child */\n"
+"\tint PauseMode = 1;\n"
+msgstr ""
+"...\n"
+"if ((pid = fork()) < 0)\t\t/* _Always_ check this */\n"
+"\terror();\n"
+"else if (pid == 0) {\t\t/* child */\n"
+"\tint PauseMode = 1;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:933
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1188
+#, no-wrap
+msgid ""
+"\twhile (PauseMode)\n"
+"\t\tsleep(10);\t/* Wait until someone attaches to us */\n"
+"\t...\n"
+"} else {\t\t\t/* parent */\n"
+"\t...\n"
+msgstr ""
+"\twhile (PauseMode)\n"
+"\t\tsleep(10);\t/* Wait until someone attaches to us */\n"
+"\t...\n"
+"} else {\t\t\t/* parent */\n"
+"\t...\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:936
+msgid ""
+"Now all that is needed is to attach to the child, set PauseMode to `0` with "
+"`expr PauseMode = 0` and wait for the `sleep()` call to return."
+msgstr ""
+"Вот все, что нужно сделать: подключиться к дочернему процессу, установить "
+"`PauseMode` в `0` с помощью `expr PauseMode = 0` и дождаться возврата из "
+"вызова `sleep()`."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:937
+#, no-wrap
+msgid "Remote Debugging Using LLDB"
+msgstr "Удаленная отладка с использованием LLDB"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:943
+msgid ""
+"The described functionality is available starting with LLDB version 12.0.0. "
+"Users of FreeBSD releases containing an earlier LLDB version may wish to use "
+"the snapshot available in extref:{handbook}[ports or packages, ports-using], "
+"as package:devel/llvm-devel[]."
+msgstr ""
+"Описанная функциональность доступна начиная с версии LLDB 12.0.0. "
+"Пользователи релизов FreeBSD, содержащих более раннюю версию LLDB, могут "
+"воспользоваться снимком из extref:{handbook}[портов или пакетов, ports-"
+"using], как package:devel/llvm-devel[]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:947
+msgid ""
+"Starting with LLDB 12.0.0, remote debugging is supported on FreeBSD. This "
+"means that `lldb-server` can be started to debug a program on one host, "
+"while the interactive `lldb` client connects to it from another one."
+msgstr ""
+"Начиная с LLDB 12.0.0, удалённая отладка поддерживается в FreeBSD. Это "
+"означает, что `lldb-server` может быть запущен для отладки программы на "
+"одном узле, в то время как интерактивный клиент `lldb` подключается к нему с "
+"другого."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:949
+msgid ""
+"To launch a new process to be debugged remotely, run `lldb-server` on the "
+"remote server by typing"
+msgstr ""
+"Чтобы запустить новый процесс для удалённой отладки, выполните `lldb-server` "
+"на удалённом сервере, набрав"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:953
+#, no-wrap
+msgid "% lldb-server g host:port -- progname\n"
+msgstr "% lldb-server g host:port -- progname\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:956
+msgid ""
+"The process will be stopped immediately after launching, and `lldb-server` "
+"will wait for the client to connect."
+msgstr ""
+"Процесс будет остановлен сразу после запуска, и `lldb-server` будет ожидать "
+"подключения клиента."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:958
+msgid ""
+"Start `lldb` locally and type the following command to connect to the remote "
+"server:"
+msgstr ""
+"Запустите `lldb` локально и введите следующую команду для подключения к "
+"удалённому серверу:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:962
+#, no-wrap
+msgid "(lldb) gdb-remote host:port\n"
+msgstr "(lldb) gdb-remote host:port\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:966
+msgid ""
+"`lldb-server` can also attach to a running process. To do that, type the "
+"following on the remote server:"
+msgstr ""
+"`lldb-server` также может присоединиться к работающему процессу. Для этого "
+"введите следующее на удалённом сервере:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:970
+#, no-wrap
+msgid "% lldb-server g host:port --attach pid-or-name\n"
+msgstr "% lldb-server g host:port --attach pid-or-name\n"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:972
+#, no-wrap
+msgid "Using gdb"
+msgstr "Использование gdb"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:974
+#, no-wrap
+msgid "Starting gdb"
+msgstr "Запуск gdb"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:977
+msgid "Start up gdb by typing"
+msgstr "Запустите gdb, набрав"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:981
+#, no-wrap
+msgid "% gdb progname\n"
+msgstr "% gdb progname\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:985
+msgid "although many people prefer to run it inside Emacs. To do this, type:"
+msgstr ""
+"хотя многие предпочитают запускать его внутри Emacs. Для этого введите:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:989
+#, no-wrap
+msgid " M-x gdb RET progname RET\n"
+msgstr " M-x gdb RET progname RET\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:992
+msgid ""
+"Finally, for those finding its text-based command-prompt style off-putting, "
+"there is a graphical front-end for it (package:devel/xxgdb[]) in the Ports "
+"Collection."
+msgstr ""
+"Наконец, для тех, кого отпугивает текстовый интерфейс командной строки, "
+"существует графический интерфейс (package:devel/xxgdb[]) в Коллекции портов."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:993
+#, no-wrap
+msgid "Running a Program with gdb"
+msgstr "Запуск программы под отладчиком gdb"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:998
+msgid ""
+"Compile the program with `-g` to get the most out of using `gdb`. It will "
+"work without, but will only display the name of the function currently "
+"running, instead of the source code. A line like:"
+msgstr ""
+"Скомпилируйте программу с `-g`, чтобы максимально использовать возможности "
+"`gdb`. Она будет работать и без этого, но отобразит только имя текущей "
+"выполняемой функции вместо исходного кода. Строка вида:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1002
+#, no-wrap
+msgid "... (no debugging symbols found) ...\n"
+msgstr "... (no debugging symbols found) ...\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1005
+msgid "when `gdb` starts up means that the program was not compiled with `-g`."
+msgstr ""
+"когда `gdb` запускается, это означает, что программа не была скомпилирована "
+"с опцией `-g`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1009
+msgid ""
+"At the `gdb` prompt, type `break main`. This will tell the debugger to skip "
+"the preliminary set-up code in the program being run and to stop execution "
+"at the beginning of the program's code. Now type `run` to start the "
+"program- it will start at the beginning of the set-up code and then get "
+"stopped by the debugger when it calls `main()`."
+msgstr ""
+"На приглашении `gdb` введите `break main`. Это укажет отладчику пропустить "
+"предварительный код настройки в выполняемой программе и остановить "
+"выполнение в начале кода программы. Теперь введите `run`, чтобы запустить "
+"программу — она начнётся с начала кода настройки, а затем будет остановлена "
+"отладчиком при вызове `main()`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1013
+msgid ""
+"To step through the program a line at a time, press `n`. When at a function "
+"call, step into it by pressing `s`. Once in a function call, return from it "
+"by pressing `f`, or use `up` and `down` to take a quick look at the caller."
+msgstr ""
+"Для пошагового выполнения программы нажимайте `n`. При вызове функции "
+"войдите в неё, нажав `s`. Оказавшись внутри функции, вернитесь из неё, нажав "
+"`f`, или используйте `up` и `down` для быстрого просмотра вызывающего кода."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1016
+msgid ""
+"Here is a simple example of how to spot a mistake in a program with `gdb`. "
+"This is our program (with a deliberate mistake):"
+msgstr ""
+"Вот простой пример того, как найти ошибку в программе с помощью `gdb`. Это "
+"наша программа (с умышленной ошибкой):"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1047
+#, no-wrap
+msgid ""
+"% cc -g -o temp temp.c\n"
+"% ./temp\n"
+"This is my program\n"
+"anint = 4231\n"
+msgstr ""
+"% cc -g -o temp temp.c\n"
+"% ./temp\n"
+"This is my program\n"
+"anint = 4231\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1050
+msgid "That was not what we expected! Time to see what is going on!"
+msgstr "Это было не то, что мы ожидали! Пора разобраться, что происходит!"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1062
+#, no-wrap
+msgid ""
+"% gdb temp\n"
+"GDB is free software and you are welcome to distribute copies of it\n"
+" under certain conditions; type \"show copying\" to see the conditions.\n"
+"There is absolutely no warranty for GDB; type \"show warranty\" for details.\n"
+"GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc.\n"
+"(gdb) break main\t\t\t\tSkip the set-up code\n"
+"Breakpoint 1 at 0x160f: file temp.c, line 9.\tgdb puts breakpoint at main()\n"
+"(gdb) run\t\t\t\t\tRun as far as main()\n"
+"Starting program: /home/james/tmp/temp\t\tProgram starts running\n"
+msgstr ""
+"% gdb temp\n"
+"GDB is free software and you are welcome to distribute copies of it\n"
+" under certain conditions; type \"show copying\" to see the conditions.\n"
+"There is absolutely no warranty for GDB; type \"show warranty\" for details.\n"
+"GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc.\n"
+"(gdb) break main\t\t\t\tSkip the set-up code\n"
+"Breakpoint 1 at 0x160f: file temp.c, line 9.\tgdb puts breakpoint at main()\n"
+"(gdb) run\t\t\t\t\tRun as far as main()\n"
+"Starting program: /home/james/tmp/temp\t\tProgram starts running\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1069
+#, no-wrap
+msgid ""
+"Breakpoint 1, main () at temp.c:9\t\tgdb stops at main()\n"
+"(gdb) n\t\t\t\t\t\tGo to next line\n"
+"This is my program\t\t\t\tProgram prints out\n"
+"(gdb) s\t\t\t\t\t\tstep into bazz()\n"
+"bazz (anint=4231) at temp.c:17\t\t\tgdb displays stack frame\n"
+"(gdb)\n"
+msgstr ""
+"Breakpoint 1, main () at temp.c:9\t\tgdb stops at main()\n"
+"(gdb) n\t\t\t\t\t\tGo to next line\n"
+"This is my program\t\t\t\tProgram prints out\n"
+"(gdb) s\t\t\t\t\t\tstep into bazz()\n"
+"bazz (anint=4231) at temp.c:17\t\t\tgdb displays stack frame\n"
+"(gdb)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1072
+msgid ""
+"Hang on a minute! How did anint get to be `4231`? Was it not set to `5` in "
+"`main()`? Let us move up to `main()` and have a look."
+msgstr ""
+"Подождите минуту! Как `int` стал равен `4231`? Разве он не был установлен в "
+"`5` в `main()`? Давайте поднимемся к `main()` и посмотрим."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1079
+#, no-wrap
+msgid ""
+"(gdb) up\t\t\t\t\tMove up call stack\n"
+"#1 0x1625 in main () at temp.c:11\t\tgdb displays stack frame\n"
+"(gdb) p i\t\t\t\t\tShow us the value of i\n"
+"$1 = 4231\t\t\t\t\tgdb displays 4231\n"
+msgstr ""
+"(gdb) up\t\t\t\t\tMove up call stack\n"
+"#1 0x1625 in main () at temp.c:11\t\tgdb displays stack frame\n"
+"(gdb) p i\t\t\t\t\tShow us the value of i\n"
+"$1 = 4231\t\t\t\t\tgdb displays 4231\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1098
+msgid ""
+"but we left the `i=5;` line out. As we did not initialize i, it had "
+"whatever number happened to be in that area of memory when the program ran, "
+"which in this case happened to be `4231`."
+msgstr ""
+"но мы пропустили строку `i=5;`. Поскольку мы не инициализировали `i`, она "
+"содержала любое число, которое оказалось в той области памяти при запуске "
+"программы, и в данном случае это оказалось `4231`."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1104
+msgid ""
+"The `gdb` command displays the stack frame every time we go into or out of a "
+"function, even if we are using `up` and `down` to move around the call "
+"stack. This shows the name of the function and the values of its arguments, "
+"which helps us keep track of where we are and what is going on. (The stack "
+"is a storage area where the program stores information about the arguments "
+"passed to functions and where to go when it returns from a function call.)"
+msgstr ""
+"Команда `gdb` отображает стек вызовов каждый раз при входе в функцию или "
+"выходе из неё, даже при использовании `up` и `down` для перемещения по стеку "
+"вызовов. Это показывает имя функции и значения её аргументов, что помогает "
+"отслеживать текущее положение и происходящее. (Стек — это область хранения, "
+"где программа сохраняет информацию об аргументах, переданных в функции, и о "
+"месте, куда нужно вернуться после вызова функции.)"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1106
+#, no-wrap
+msgid "Examining a Core File with gdb"
+msgstr "Изучение файла core с помощью gdb"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1114
+msgid ""
+"To examine a core file, start up `gdb` in the usual way. Instead of typing "
+"`break` or `run`, type"
+msgstr ""
+"Для анализа файла core запустите `gdb` обычным способом. Вместо ввода команд "
+"`break` или `run` введите"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1118
+#, no-wrap
+msgid "(gdb) core progname.core\n"
+msgstr "(gdb) core progname.core\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1121
+msgid ""
+"If the core file is not in the current directory, type `dir /path/to/core/"
+"file` first."
+msgstr ""
+"Если файл core отсутствует в текущем каталоге, сначала введите `dir /путь/к/"
+"core/файлу`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1123
+msgid "The debugger should display something like this:"
+msgstr "Отладчик должен отобразить что-то вроде этого:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1137
+#, no-wrap
+msgid ""
+"% gdb [.filename]#progname#\n"
+"GDB is free software and you are welcome to distribute copies of it\n"
+" under certain conditions; type \"show copying\" to see the conditions.\n"
+"There is absolutely no warranty for GDB; type \"show warranty\" for details.\n"
+"GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc.\n"
+"(gdb) core [.filename]#progname.core#\n"
+"Core was generated by `[.filename]#progname#'.\n"
+"Program terminated with signal 11, Segmentation fault.\n"
+"Cannot access memory at address 0x7020796d.\n"
+"#0 0x164a in bazz (anint=0x5) at temp.c:17\n"
+"(gdb)\n"
+msgstr ""
+"% gdb [.filename]#progname#\n"
+"GDB is free software and you are welcome to distribute copies of it\n"
+" under certain conditions; type \"show copying\" to see the conditions.\n"
+"There is absolutely no warranty for GDB; type \"show warranty\" for details.\n"
+"GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc.\n"
+"(gdb) core [.filename]#progname.core#\n"
+"Core was generated by `[.filename]#progname#'.\n"
+"Program terminated with signal 11, Segmentation fault.\n"
+"Cannot access memory at address 0x7020796d.\n"
+"#0 0x164a in bazz (anint=0x5) at temp.c:17\n"
+"(gdb)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1141
+msgid ""
+"In this case, the program was called [.filename]#progname#, so the core file "
+"is called [.filename]#progname.core#. We can see that the program crashed "
+"due to trying to access an area in memory that was not available to it in a "
+"function called `bazz`."
+msgstr ""
+"В этом случае программа называлась [.filename]#progname#, поэтому файл дампа "
+"памяти называется [.filename]#progname.core#. Мы видим, что программа "
+"завершилась аварийно из-за попытки доступа к области памяти, которая ей не "
+"доступна, в функции `bazz`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1145
+msgid ""
+"Sometimes it is useful to be able to see how a function was called, as the "
+"problem could have occurred a long way up the call stack in a complex "
+"program. `bt` causes `gdb` to print out a back-trace of the call stack:"
+msgstr ""
+"Иногда полезно увидеть, как была вызвана функция, поскольку проблема могла "
+"возникнуть гораздо выше по стеку вызовов в сложной программе. `bt` "
+"заставляет `gdb` вывести трассировку стека вызовов:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1153
+#, no-wrap
+msgid ""
+"(gdb) bt\n"
+"#0 0x164a in bazz (anint=0x5) at temp.c:17\n"
+"#1 0xefbfd888 in end ()\n"
+"#2 0x162c in main () at temp.c:11\n"
+"(gdb)\n"
+msgstr ""
+"(gdb) bt\n"
+"#0 0x164a in bazz (anint=0x5) at temp.c:17\n"
+"#1 0xefbfd888 in end ()\n"
+"#2 0x162c in main () at temp.c:11\n"
+"(gdb)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1157
+msgid ""
+"The `end()` function is called when a program crashes; in this case, the "
+"`bazz()` function was called from `main()`."
+msgstr ""
+"Функция `end()` вызывается при аварийном завершении программы; в данном "
+"случае функция `bazz()` была вызвана из `main()`."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1158
+#, no-wrap
+msgid "Attaching to a Running Program with gdb"
+msgstr "Подключение к работающей программе с помощью gdb"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1163
+msgid ""
+"One of the neatest features about `gdb` is that it can attach to a program "
+"that is already running. Of course, that requires sufficient permissions to "
+"do so. A common problem is stepping through a program that forks and "
+"wanting to trace the child, but the debugger will only trace the parent."
+msgstr ""
+"Одной из самых удобных функций `gdb` является возможность подключения к уже "
+"запущенной программе. Конечно, для этого требуются соответствующие "
+"разрешения. Частой проблемой является пошаговое выполнение программы, "
+"которая создает дочерний процесс, когда нужно отслеживать дочерний процесс, "
+"но отладчик продолжает отслеживать только родительский."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1165
+msgid ""
+"To do that, start up another `gdb`, use `ps` to find the process ID for the "
+"child, and do"
+msgstr ""
+"Для этого запустите другой `gdb`, используйте `ps` для поиска идентификатора "
+"процесса дочернего элемента и выполните"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1169
+#, no-wrap
+msgid "(gdb) attach pid\n"
+msgstr "(gdb) attach pid\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1172
+msgid "in `gdb`, and then debug as usual."
+msgstr "в `gdb`, а затем отлаживайте как обычно."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1191
+msgid ""
+"Now all that is needed is to attach to the child, set PauseMode to `0`, and "
+"wait for the `sleep()` call to return!"
+msgstr ""
+"Теперь осталось только подключиться к дочернему процессу, установить "
+"PauseMode в `0` и дождаться возврата из вызова `sleep()`!"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1193
+#, no-wrap
+msgid "Using Emacs as a Development Environment"
+msgstr "Использование Emacs в качестве среды разработки"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1195
+#, no-wrap
+msgid "Emacs"
+msgstr "Emacs"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1199
+msgid ""
+"Emacs is a highly customizable editor-indeed, it has been customized to the "
+"point where it is more like an operating system than an editor! Many "
+"developers and sysadmins do in fact spend practically all their time working "
+"inside Emacs, leaving it only to log out."
+msgstr ""
+"Emacs — это высоконастраиваемый редактор — настолько, что его можно скорее "
+"назвать операционной системой, чем редактором! Многие разработчики и "
+"системные администраторы действительно проводят практически всё своё время, "
+"работая внутри Emacs, выходя из него только для завершения сеанса."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1201
+msgid ""
+"It is impossible even to summarize everything Emacs can do here, but here "
+"are some of the features of interest to developers:"
+msgstr ""
+"Невозможно даже кратко описать все, что может делать Emacs, но вот некоторые "
+"особенности, которые могут быть интересны разработчикам:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1203
+msgid ""
+"Very powerful editor, allowing search-and-replace on both strings and "
+"regular expressions (patterns), jumping to start/end of block expression, "
+"etc, etc."
+msgstr ""
+"Очень мощный редактор, позволяющий выполнять поиск и замену как строк, так и "
+"регулярных выражений (шаблонов), переход к началу/концу блока выражения и "
+"многое другое."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1204
+msgid "Pull-down menus and online help."
+msgstr "Выпадающие меню и встроенная справка."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1205
+msgid "Language-dependent syntax highlighting and indentation."
+msgstr "Подсветка синтаксиса и форматирование отступов в зависимости от языка."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1206
+msgid "Completely customizable."
+msgstr "Полностью настраиваемый."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1207
+msgid "You can compile and debug programs within Emacs."
+msgstr "Вы можете компилировать и отлаживать программы из Emacs."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1208
+msgid ""
+"On a compilation error, you can jump to the offending line of source code."
+msgstr ""
+"При ошибке компиляции можно перейти к проблемной строке исходного кода."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1209
+msgid ""
+"Friendly-ish front-end to the `info` program used for reading GNU hypertext "
+"documentation, including the documentation on Emacs itself."
+msgstr ""
+"Дружелюбный интерфейс для программы `info`, используемой для чтения "
+"гипертекстовой документации GNU, включая документацию по самому Emacs."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1210
+msgid ""
+"Friendly front-end to `gdb`, allowing you to look at the source code as you "
+"step through your program."
+msgstr ""
+"Дружелюбный интерфейс для `gdb`, позволяющий просматривать исходный код во "
+"время пошагового выполнения программы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1212
+msgid "And doubtless many more that have been overlooked."
+msgstr "И, несомненно, множество других, которые были упущены из виду."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1214
+msgid ""
+"Emacs can be installed on FreeBSD using the package:editors/emacs[] port."
+msgstr ""
+"Emacs можно установить на FreeBSD с помощью пакета package:editors/emacs[]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1217
+msgid ""
+"Once it is installed, start it up and do `C-h t` to read an Emacs tutorial-"
+"that means hold down kbd:[control], press kbd:[h], let go of kbd:[control], "
+"and then press kbd:[t]. (Alternatively, you can use the mouse to select "
+"[.guimenuitem]#Emacs Tutorial# from the menu:Help[] menu.)"
+msgstr ""
+"После установки запустите его и выполните `C-h t`, чтобы прочитать "
+"руководство по Emacs — это означает, что нужно удерживать kbd:[control], "
+"нажать kbd:[h], отпустить kbd:[control], а затем нажать kbd:[t]. (Также "
+"можно использовать мышь для выбора [.guimenuitem]#Руководство по Emacs# в "
+"меню menu:Help[].)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1222
+msgid ""
+"Although Emacs does have menus, it is well worth learning the key bindings, "
+"as it is much quicker when you are editing something to press a couple of "
+"keys than to try to find the mouse and then click on the right place. And, "
+"when you are talking to seasoned Emacs users, you will find they often "
+"casually throw around expressions like \"`M-x replace-s RET foo RET bar "
+"RET`\" so it is useful to know what they mean. And in any case, Emacs has "
+"far too many useful functions for them to all fit on the menu bars."
+msgstr ""
+"Хотя в Emacs и есть меню, стоит изучить сочетания клавиш, так как "
+"редактировать что-либо с их помощью гораздо быстрее, чем искать мышку и "
+"кликать в нужное место. Кроме того, общаясь с опытными пользователями Emacs, "
+"вы часто услышите выражения вроде «`M-x replace-s RET foo RET bar RET`» — "
+"полезно понимать, что они значат. Да и вообще, в Emacs столько полезных "
+"функций, что все они просто не поместятся на панелях меню."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1226
+msgid ""
+"Fortunately, it is quite easy to pick up the key-bindings, as they are "
+"displayed next to the menu item. My advice is to use the menu item for, "
+"say, opening a file until you understand how it works and feel confident "
+"with it, then try doing C-x C-f. When you are happy with that, move on to "
+"another menu command."
+msgstr ""
+"К счастью, освоить сочетания клавиш довольно легко, так как они отображаются "
+"рядом с пунктами меню. Мой совет — использовать пункты меню для, скажем, "
+"открытия файла, пока вы не разберётесь, как это работает, и не почувствуете "
+"себя уверенно, а затем попробуйте выполнить `C-x C-f`. Когда освоитесь с "
+"этим, переходите к следующей команде меню."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1229
+msgid ""
+"If you cannot remember what a particular combination of keys does, select "
+"[.guimenuitem]#Describe Key# from the menu:Help[] menu and type it in-Emacs "
+"will tell you what it does. You can also use the [.guimenuitem]#Command "
+"Apropos# menu item to find out all the commands which contain a particular "
+"word in them, with the key binding next to it."
+msgstr ""
+"Если вы не можете вспомнить, что делает определённая комбинация клавиш, "
+"выберите [.guimenuitem]#Описание Клавиши# в меню menu:Help[] и введите её — "
+"Emacs сообщит, что она делает. Вы также можете использовать пункт меню "
+"[.guimenuitem]#Command Apropos#, чтобы найти все команды, содержащие "
+"определённое слово, с указанием соответствующих клавишных сочетаний."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1232
+msgid ""
+"By the way, the expression above means hold down the kbd:[Meta] key, press "
+"kbd:[x], release the kbd:[Meta] key, type `replace-s` (short for `replace-"
+"string`-another feature of Emacs is that you can abbreviate commands), press "
+"the kbd:[return] key, type `foo` (the string you want replaced), press the "
+"kbd:[return] key, type bar (the string you want to replace `foo` with) and "
+"press kbd:[return] again. Emacs will then do the search-and-replace "
+"operation you have just requested."
+msgstr ""
+"Между прочим, выражение выше означает: удерживайте клавишу kbd:[Meta], "
+"нажмите kbd:[x], отпустите клавишу kbd:[Meta], введите `replace-s` "
+"(сокращение от `replace-string` — ещё одна особенность Emacs в том, что "
+"команды можно сокращать), нажмите клавишу kbd:[return], введите `foo` "
+"(строка, которую нужно заменить), нажмите клавишу kbd:[return], введите "
+"`bar` (строка, на которую нужно заменить `foo`) и снова нажмите kbd:"
+"[return]. Emacs выполнит операцию поиска и замены, которую вы только что "
+"запросили."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1235
+msgid ""
+"If you are wondering what on earth kbd:[Meta] is, it is a special key that "
+"many UNIX(R) workstations have. Unfortunately, PC's do not have one, so it "
+"is usually kbd:[alt] (or if you are unlucky, the kbd:[escape] key)."
+msgstr ""
+"Если вам интересно, что такое kbd:[Meta], то это специальная клавиша, "
+"которая есть на многих рабочих станциях UNIX(R). К сожалению, на PC её нет, "
+"поэтому обычно используется kbd:[alt] (или, если вам не повезло, kbd:"
+"[escape])."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1239
+msgid ""
+"Oh, and to get out of Emacs, do `C-x C-c` (that means hold down the kbd:"
+"[control] key, press kbd:[x], press kbd:[c] and release the kbd:[control] "
+"key). If you have any unsaved files open, Emacs will ask you if you want to "
+"save them. (Ignore the bit in the documentation where it says `C-z` is the "
+"usual way to leave Emacs-that leaves Emacs hanging around in the background, "
+"and is only really useful if you are on a system which does not have virtual "
+"terminals)."
+msgstr ""
+"Ах да, чтобы выйти из Emacs, нажмите `C-x C-c` (это значит зажмите клавишу "
+"kbd:[control], нажмите kbd:[x], затем kbd:[c] и отпустите kbd:[control]). "
+"Если у вас есть несохранённые файлы, Emacs спросит, хотите ли вы их "
+"сохранить. (Игнорируйте часть документации, где говорится, что `C-z` — это "
+"обычный способ выхода из Emacs — это оставляет Emacs работающим в фоне и "
+"полезно только на системах без виртуальных терминалов)."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1240
+#, no-wrap
+msgid "Configuring Emacs"
+msgstr "Настройка Emacs"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1243
+msgid ""
+"Emacs does many wonderful things; some of them are built in, some of them "
+"need to be configured."
+msgstr ""
+"Emacs делает много замечательных вещей; некоторые из них встроены, некоторые "
+"требуют настройки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1247
+msgid ""
+"Instead of using a proprietary macro language for configuration, Emacs uses "
+"a version of Lisp specially adapted for editors, known as Emacs Lisp. "
+"Working with Emacs Lisp can be quite helpful if you want to go on and learn "
+"something like Common Lisp. Emacs Lisp has many features of Common Lisp, "
+"although it is considerably smaller (and thus easier to master)."
+msgstr ""
+"Вместо использования проприетарного языка макросов для конфигурации, Emacs "
+"применяет версию Lisp, специально адаптированную для редакторов, известную "
+"как Emacs Lisp. Работа с Emacs Lisp может быть весьма полезной, если вы "
+"хотите продолжить и изучить что-то вроде Common Lisp. Emacs Lisp обладает "
+"многими возможностями Common Lisp, хотя и значительно меньше (и, "
+"следовательно, проще для освоения)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1249
+msgid ""
+"The best way to learn Emacs Lisp is to read the online link:https://"
+"www.gnu.org/software/emacs/manual/elisp.html[Emacs Reference] manual."
+msgstr ""
+"Лучший способ изучить Emacs Lisp — это прочитать онлайн-руководство "
+"link:https://www.gnu.org/software/emacs/manual/elisp.html[Emacs Reference]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1254
+msgid ""
+"However, there is no need to actually know any Lisp to get started with "
+"configuring Emacs, as I have included a sample [.filename]#.emacs#, which "
+"should be enough to get you started. Just copy it into your home directory "
+"and restart Emacs if it is already running; it will read the commands from "
+"the file and (hopefully) give you a useful basic setup."
+msgstr ""
+"Однако для начала настройки Emacs не обязательно знать Lisp, так как я "
+"включил пример файла [.filename]#.emacs#, которого должно быть достаточно "
+"для старта. Просто скопируйте его в свой домашний каталог и перезапустите "
+"Emacs, если он уже запущен; он прочитает команды из файла и (надеюсь) "
+"предоставит вам полезную базовую конфигурацию."
+
+#. type: Block title
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1255
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1273
+#, no-wrap
+msgid "A Sample [.filename]#.emacs#"
+msgstr "Пример файла [.filename]#.emacs#"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1258
+msgid ""
+"Unfortunately, there is far too much here to explain it in detail; however "
+"there are one or two points worth mentioning."
+msgstr ""
+"К сожалению, здесь слишком много информации, чтобы объяснять всё подробно; "
+"однако есть один или два момента, которые стоит упомянуть."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1260
+msgid "Everything beginning with a `;` is a comment and is ignored by Emacs."
+msgstr "Всё, что начинается с `;`, является комментарием и игнорируется Emacs."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1261
+msgid ""
+"In the first line, the `-*- Emacs-Lisp -*-` is so that we can edit "
+"[.filename]#.emacs# itself within Emacs and get all the fancy features for "
+"editing Emacs Lisp. Emacs usually tries to guess this based on the filename, "
+"and may not get it right for [.filename]#.emacs#."
+msgstr ""
+"В первой строке `-*- Emacs-Lisp -*-` нужен для того, чтобы мы могли "
+"редактировать сам файл [.filename]#.emacs# в Emacs и использовать все "
+"удобные функции для редактирования Emacs Lisp. Обычно Emacs пытается угадать "
+"это по имени файла, но может не сделать это правильно для "
+"[.filename]#.emacs#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1262
+msgid ""
+"The kbd:[tab] key is bound to an indentation function in some modes, so when "
+"you press the tab key, it will indent the current line of code. If you want "
+"to put a tab character in whatever you are writing, hold the kbd:[control] "
+"key down while you are pressing the kbd:[tab] key."
+msgstr ""
+"Клавиша kbd:[tab] связана с функцией отступа в некоторых режимах, поэтому "
+"при нажатии клавиши tab текущая строка кода будет с отступом. Если вы хотите "
+"вставить символ табуляции в текст, удерживайте клавишу kbd:[control] во "
+"время нажатия kbd:[tab]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1263
+msgid ""
+"This file supports syntax highlighting for C, C++, Perl, Lisp and Scheme, by "
+"guessing the language from the filename."
+msgstr ""
+"Этот файл поддерживает подсветку синтаксиса для C, C++, Perl, Lisp и Scheme, "
+"определяя язык по имени файла."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1264
+msgid ""
+"Emacs already has a pre-defined function called `next-error`. In a "
+"compilation output window, this allows you to move from one compilation "
+"error to the next by doing `M-n`; we define a complementary function, "
+"`previous-error`, that allows you to go to a previous error by doing `M-p`. "
+"The nicest feature of all is that `C-c C-c` will open up the source file in "
+"which the error occurred and jump to the appropriate line."
+msgstr ""
+"В Emacs уже есть предопределённая функция `next-error`. В окне вывода "
+"компиляции это позволяет переходить от одной ошибки компиляции к следующей с "
+"помощью `M-n`; мы определяем дополнительную функцию `previous-error`, "
+"которая позволяет вернуться к предыдущей ошибке с помощью `M-p`. Самое "
+"приятное — сочетание `C-c C-c` откроет исходный файл, в котором произошла "
+"ошибка, и перейдёт на соответствующую строку."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1265
+msgid ""
+"We enable Emacs's ability to act as a server, so that if you are doing "
+"something outside Emacs and you want to edit a file, you can just type in"
+msgstr ""
+"Включаем возможность Emacs работать как сервер, так что если вы заняты чем-"
+"то вне Emacs и хотите отредактировать файл, можно просто ввести"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1269
+#, no-wrap
+msgid "% emacsclient filename\n"
+msgstr "% emacsclient filename\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1272
+msgid ""
+"and then you can edit the file in your Emacs!footnote:[Many Emacs users set "
+"their EDITOR environment to emacsclient so this happens every time they need "
+"to edit a file.]"
+msgstr ""
+"и затем вы можете редактировать файл в вашем Emacs!footnote:[Многие "
+"пользователи Emacs устанавливают переменную окружения EDITOR в emacsclient, "
+"так что это происходит каждый раз, когда им нужно отредактировать файл.]"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1278
+#, no-wrap
+msgid ";; -*-Emacs-Lisp-*-\n"
+msgstr ";; -*-Emacs-Lisp-*-\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1283
+#, no-wrap
+msgid ""
+";; This file is designed to be re-evaled; use the variable first-time\n"
+";; to avoid any problems with this.\n"
+"(defvar first-time t\n"
+" \"Flag signifying this is the first time that .emacs has been evaled\")\n"
+msgstr ""
+";; This file is designed to be re-evaled; use the variable first-time\n"
+";; to avoid any problems with this.\n"
+"(defvar first-time t\n"
+" \"Flag signifying this is the first time that .emacs has been evaled\")\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1291
+#, no-wrap
+msgid ""
+";; Meta\n"
+"(global-set-key \"\\M- \" 'set-mark-command)\n"
+"(global-set-key \"\\M-\\C-h\" 'backward-kill-word)\n"
+"(global-set-key \"\\M-\\C-r\" 'query-replace)\n"
+"(global-set-key \"\\M-r\" 'replace-string)\n"
+"(global-set-key \"\\M-g\" 'goto-line)\n"
+"(global-set-key \"\\M-h\" 'help-command)\n"
+msgstr ""
+";; Meta\n"
+"(global-set-key \"\\M- \" 'set-mark-command)\n"
+"(global-set-key \"\\M-\\C-h\" 'backward-kill-word)\n"
+"(global-set-key \"\\M-\\C-r\" 'query-replace)\n"
+"(global-set-key \"\\M-r\" 'replace-string)\n"
+"(global-set-key \"\\M-g\" 'goto-line)\n"
+"(global-set-key \"\\M-h\" 'help-command)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1312
+#, no-wrap
+msgid ""
+";; Function keys\n"
+"(global-set-key [f1] 'manual-entry)\n"
+"(global-set-key [f2] 'info)\n"
+"(global-set-key [f3] 'repeat-complex-command)\n"
+"(global-set-key [f4] 'advertised-undo)\n"
+"(global-set-key [f5] 'eval-current-buffer)\n"
+"(global-set-key [f6] 'buffer-menu)\n"
+"(global-set-key [f7] 'other-window)\n"
+"(global-set-key [f8] 'find-file)\n"
+"(global-set-key [f9] 'save-buffer)\n"
+"(global-set-key [f10] 'next-error)\n"
+"(global-set-key [f11] 'compile)\n"
+"(global-set-key [f12] 'grep)\n"
+"(global-set-key [C-f1] 'compile)\n"
+"(global-set-key [C-f2] 'grep)\n"
+"(global-set-key [C-f3] 'next-error)\n"
+"(global-set-key [C-f4] 'previous-error)\n"
+"(global-set-key [C-f5] 'display-faces)\n"
+"(global-set-key [C-f8] 'dired)\n"
+"(global-set-key [C-f10] 'kill-compilation)\n"
+msgstr ""
+";; Function keys\n"
+"(global-set-key [f1] 'manual-entry)\n"
+"(global-set-key [f2] 'info)\n"
+"(global-set-key [f3] 'repeat-complex-command)\n"
+"(global-set-key [f4] 'advertised-undo)\n"
+"(global-set-key [f5] 'eval-current-buffer)\n"
+"(global-set-key [f6] 'buffer-menu)\n"
+"(global-set-key [f7] 'other-window)\n"
+"(global-set-key [f8] 'find-file)\n"
+"(global-set-key [f9] 'save-buffer)\n"
+"(global-set-key [f10] 'next-error)\n"
+"(global-set-key [f11] 'compile)\n"
+"(global-set-key [f12] 'grep)\n"
+"(global-set-key [C-f1] 'compile)\n"
+"(global-set-key [C-f2] 'grep)\n"
+"(global-set-key [C-f3] 'next-error)\n"
+"(global-set-key [C-f4] 'previous-error)\n"
+"(global-set-key [C-f5] 'display-faces)\n"
+"(global-set-key [C-f8] 'dired)\n"
+"(global-set-key [C-f10] 'kill-compilation)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1330
+#, no-wrap
+msgid ""
+";; Keypad bindings\n"
+"(global-set-key [up] \"\\C-p\")\n"
+"(global-set-key [down] \"\\C-n\")\n"
+"(global-set-key [left] \"\\C-b\")\n"
+"(global-set-key [right] \"\\C-f\")\n"
+"(global-set-key [home] \"\\C-a\")\n"
+"(global-set-key [end] \"\\C-e\")\n"
+"(global-set-key [prior] \"\\M-v\")\n"
+"(global-set-key [next] \"\\C-v\")\n"
+"(global-set-key [C-up] \"\\M-\\C-b\")\n"
+"(global-set-key [C-down] \"\\M-\\C-f\")\n"
+"(global-set-key [C-left] \"\\M-b\")\n"
+"(global-set-key [C-right] \"\\M-f\")\n"
+"(global-set-key [C-home] \"\\M-<\")\n"
+"(global-set-key [C-end] \"\\M->\")\n"
+"(global-set-key [C-prior] \"\\M-<\")\n"
+"(global-set-key [C-next] \"\\M->\")\n"
+msgstr ""
+";; Keypad bindings\n"
+"(global-set-key [up] \"\\C-p\")\n"
+"(global-set-key [down] \"\\C-n\")\n"
+"(global-set-key [left] \"\\C-b\")\n"
+"(global-set-key [right] \"\\C-f\")\n"
+"(global-set-key [home] \"\\C-a\")\n"
+"(global-set-key [end] \"\\C-e\")\n"
+"(global-set-key [prior] \"\\M-v\")\n"
+"(global-set-key [next] \"\\C-v\")\n"
+"(global-set-key [C-up] \"\\M-\\C-b\")\n"
+"(global-set-key [C-down] \"\\M-\\C-f\")\n"
+"(global-set-key [C-left] \"\\M-b\")\n"
+"(global-set-key [C-right] \"\\M-f\")\n"
+"(global-set-key [C-home] \"\\M-<\")\n"
+"(global-set-key [C-end] \"\\M->\")\n"
+"(global-set-key [C-prior] \"\\M-<\")\n"
+"(global-set-key [C-next] \"\\M->\")\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1333
+#, no-wrap
+msgid ""
+";; Mouse\n"
+"(global-set-key [mouse-3] 'imenu)\n"
+msgstr ""
+";; Mouse\n"
+"(global-set-key [mouse-3] 'imenu)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1337
+#, no-wrap
+msgid ""
+";; Misc\n"
+"(global-set-key [C-tab] \"\\C-q\\t\")\t; Control tab quotes a tab.\n"
+"(setq backup-by-copying-when-mismatch t)\n"
+msgstr ""
+";; Misc\n"
+"(global-set-key [C-tab] \"\\C-q\\t\")\t; Control tab quotes a tab.\n"
+"(setq backup-by-copying-when-mismatch t)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1342
+#, no-wrap
+msgid ""
+";; Treat 'y' or <CR> as yes, 'n' as no.\n"
+"(fset 'yes-or-no-p 'y-or-n-p)\n"
+"(define-key query-replace-map [return] 'act)\n"
+"(define-key query-replace-map [?\\C-m] 'act)\n"
+msgstr ""
+";; Treat 'y' or <CR> as yes, 'n' as no.\n"
+"(fset 'yes-or-no-p 'y-or-n-p)\n"
+"(define-key query-replace-map [return] 'act)\n"
+"(define-key query-replace-map [?\\C-m] 'act)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1346
+#, no-wrap
+msgid ""
+";; Load packages\n"
+"(require 'desktop)\n"
+"(require 'tar-mode)\n"
+msgstr ""
+";; Load packages\n"
+"(require 'desktop)\n"
+"(require 'tar-mode)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1352
+#, no-wrap
+msgid ""
+";; Pretty diff mode\n"
+"(autoload 'ediff-buffers \"ediff\" \"Intelligent Emacs interface to diff\" t)\n"
+"(autoload 'ediff-files \"ediff\" \"Intelligent Emacs interface to diff\" t)\n"
+"(autoload 'ediff-files-remote \"ediff\"\n"
+" \"Intelligent Emacs interface to diff\")\n"
+msgstr ""
+";; Pretty diff mode\n"
+"(autoload 'ediff-buffers \"ediff\" \"Intelligent Emacs interface to diff\" t)\n"
+"(autoload 'ediff-files \"ediff\" \"Intelligent Emacs interface to diff\" t)\n"
+"(autoload 'ediff-files-remote \"ediff\"\n"
+" \"Intelligent Emacs interface to diff\")\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1361
+#, no-wrap
+msgid ""
+"(if first-time\n"
+" (setq auto-mode-alist\n"
+"\t (append '((\"\\\\.cpp$\" . c++-mode)\n"
+"\t\t (\"\\\\.hpp$\" . c++-mode)\n"
+"\t\t (\"\\\\.lsp$\" . lisp-mode)\n"
+"\t\t (\"\\\\.scm$\" . scheme-mode)\n"
+"\t\t (\"\\\\.pl$\" . perl-mode)\n"
+"\t\t ) auto-mode-alist)))\n"
+msgstr ""
+"(if first-time\n"
+" (setq auto-mode-alist\n"
+"\t (append '((\"\\\\.cpp$\" . c++-mode)\n"
+"\t\t (\"\\\\.hpp$\" . c++-mode)\n"
+"\t\t (\"\\\\.lsp$\" . lisp-mode)\n"
+"\t\t (\"\\\\.scm$\" . scheme-mode)\n"
+"\t\t (\"\\\\.pl$\" . perl-mode)\n"
+"\t\t ) auto-mode-alist)))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1366
+#, no-wrap
+msgid ""
+";; Auto font lock mode\n"
+"(defvar font-lock-auto-mode-list\n"
+" (list 'c-mode 'c++-mode 'c++-c-mode 'emacs-lisp-mode 'lisp-mode 'perl-mode 'scheme-mode)\n"
+" \"List of modes to always start in font-lock-mode\")\n"
+msgstr ""
+";; Auto font lock mode\n"
+"(defvar font-lock-auto-mode-list\n"
+" (list 'c-mode 'c++-mode 'c++-c-mode 'emacs-lisp-mode 'lisp-mode 'perl-mode 'scheme-mode)\n"
+" \"List of modes to always start in font-lock-mode\")\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1371
+#, no-wrap
+msgid ""
+"(defvar font-lock-mode-keyword-alist\n"
+" '((c++-c-mode . c-font-lock-keywords)\n"
+" (perl-mode . perl-font-lock-keywords))\n"
+" \"Associations between modes and keywords\")\n"
+msgstr ""
+"(defvar font-lock-mode-keyword-alist\n"
+" '((c++-c-mode . c-font-lock-keywords)\n"
+" (perl-mode . perl-font-lock-keywords))\n"
+" \"Associations between modes and keywords\")\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1379
+#, no-wrap
+msgid ""
+"(defun font-lock-auto-mode-select ()\n"
+" \"Automatically select font-lock-mode if the current major mode is in font-lock-auto-mode-list\"\n"
+" (if (memq major-mode font-lock-auto-mode-list)\n"
+" (progn\n"
+"\t(font-lock-mode t))\n"
+" )\n"
+" )\n"
+msgstr ""
+"(defun font-lock-auto-mode-select ()\n"
+" \"Automatically select font-lock-mode if the current major mode is in font-lock-auto-mode-list\"\n"
+" (if (memq major-mode font-lock-auto-mode-list)\n"
+" (progn\n"
+"\t(font-lock-mode t))\n"
+" )\n"
+" )\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1381
+#, no-wrap
+msgid "(global-set-key [M-f1] 'font-lock-fontify-buffer)\n"
+msgstr "(global-set-key [M-f1] 'font-lock-fontify-buffer)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1398
+#, no-wrap
+msgid ""
+";; New dabbrev stuff\n"
+";(require 'new-dabbrev)\n"
+"(setq dabbrev-always-check-other-buffers t)\n"
+"(setq dabbrev-abbrev-char-regexp \"\\\\sw\\\\|\\\\s_\")\n"
+"(add-hook 'emacs-lisp-mode-hook\n"
+"\t '(lambda ()\n"
+"\t (set (make-local-variable 'dabbrev-case-fold-search) nil)\n"
+"\t (set (make-local-variable 'dabbrev-case-replace) nil)))\n"
+"(add-hook 'c-mode-hook\n"
+"\t '(lambda ()\n"
+"\t (set (make-local-variable 'dabbrev-case-fold-search) nil)\n"
+"\t (set (make-local-variable 'dabbrev-case-replace) nil)))\n"
+"(add-hook 'text-mode-hook\n"
+"\t '(lambda ()\n"
+"\t (set (make-local-variable 'dabbrev-case-fold-search) t)\n"
+"\t (set (make-local-variable 'dabbrev-case-replace) t)))\n"
+msgstr ""
+";; New dabbrev stuff\n"
+";(require 'new-dabbrev)\n"
+"(setq dabbrev-always-check-other-buffers t)\n"
+"(setq dabbrev-abbrev-char-regexp \"\\\\sw\\\\|\\\\s_\")\n"
+"(add-hook 'emacs-lisp-mode-hook\n"
+"\t '(lambda ()\n"
+"\t (set (make-local-variable 'dabbrev-case-fold-search) nil)\n"
+"\t (set (make-local-variable 'dabbrev-case-replace) nil)))\n"
+"(add-hook 'c-mode-hook\n"
+"\t '(lambda ()\n"
+"\t (set (make-local-variable 'dabbrev-case-fold-search) nil)\n"
+"\t (set (make-local-variable 'dabbrev-case-replace) nil)))\n"
+"(add-hook 'text-mode-hook\n"
+"\t '(lambda ()\n"
+"\t (set (make-local-variable 'dabbrev-case-fold-search) t)\n"
+"\t (set (make-local-variable 'dabbrev-case-replace) t)))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1410
+#, no-wrap
+msgid ""
+";; C++ and C mode...\n"
+"(defun my-c++-mode-hook ()\n"
+" (setq tab-width 4)\n"
+" (define-key c++-mode-map \"\\C-m\" 'reindent-then-newline-and-indent)\n"
+" (define-key c++-mode-map \"\\C-ce\" 'c-comment-edit)\n"
+" (setq c++-auto-hungry-initial-state 'none)\n"
+" (setq c++-delete-function 'backward-delete-char)\n"
+" (setq c++-tab-always-indent t)\n"
+" (setq c-indent-level 4)\n"
+" (setq c-continued-statement-offset 4)\n"
+" (setq c++-empty-arglist-indent 4))\n"
+msgstr ""
+";; C++ and C mode...\n"
+"(defun my-c++-mode-hook ()\n"
+" (setq tab-width 4)\n"
+" (define-key c++-mode-map \"\\C-m\" 'reindent-then-newline-and-indent)\n"
+" (define-key c++-mode-map \"\\C-ce\" 'c-comment-edit)\n"
+" (setq c++-auto-hungry-initial-state 'none)\n"
+" (setq c++-delete-function 'backward-delete-char)\n"
+" (setq c++-tab-always-indent t)\n"
+" (setq c-indent-level 4)\n"
+" (setq c-continued-statement-offset 4)\n"
+" (setq c++-empty-arglist-indent 4))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1424
+#, no-wrap
+msgid ""
+"(defun my-c-mode-hook ()\n"
+" (setq tab-width 4)\n"
+" (define-key c-mode-map \"\\C-m\" 'reindent-then-newline-and-indent)\n"
+" (define-key c-mode-map \"\\C-ce\" 'c-comment-edit)\n"
+" (setq c-auto-hungry-initial-state 'none)\n"
+" (setq c-delete-function 'backward-delete-char)\n"
+" (setq c-tab-always-indent t)\n"
+";; BSD-ish indentation style\n"
+" (setq c-indent-level 4)\n"
+" (setq c-continued-statement-offset 4)\n"
+" (setq c-brace-offset -4)\n"
+" (setq c-argdecl-indent 0)\n"
+" (setq c-label-offset -4))\n"
+msgstr ""
+"(defun my-c-mode-hook ()\n"
+" (setq tab-width 4)\n"
+" (define-key c-mode-map \"\\C-m\" 'reindent-then-newline-and-indent)\n"
+" (define-key c-mode-map \"\\C-ce\" 'c-comment-edit)\n"
+" (setq c-auto-hungry-initial-state 'none)\n"
+" (setq c-delete-function 'backward-delete-char)\n"
+" (setq c-tab-always-indent t)\n"
+";; BSD-ish indentation style\n"
+" (setq c-indent-level 4)\n"
+" (setq c-continued-statement-offset 4)\n"
+" (setq c-brace-offset -4)\n"
+" (setq c-argdecl-indent 0)\n"
+" (setq c-label-offset -4))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1431
+#, no-wrap
+msgid ""
+";; Perl mode\n"
+"(defun my-perl-mode-hook ()\n"
+" (setq tab-width 4)\n"
+" (define-key c++-mode-map \"\\C-m\" 'reindent-then-newline-and-indent)\n"
+" (setq perl-indent-level 4)\n"
+" (setq perl-continued-statement-offset 4))\n"
+msgstr ""
+";; Perl mode\n"
+"(defun my-perl-mode-hook ()\n"
+" (setq tab-width 4)\n"
+" (define-key c++-mode-map \"\\C-m\" 'reindent-then-newline-and-indent)\n"
+" (setq perl-indent-level 4)\n"
+" (setq perl-continued-statement-offset 4))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1435
+#, no-wrap
+msgid ""
+";; Scheme mode...\n"
+"(defun my-scheme-mode-hook ()\n"
+" (define-key scheme-mode-map \"\\C-m\" 'reindent-then-newline-and-indent))\n"
+msgstr ""
+";; Scheme mode...\n"
+"(defun my-scheme-mode-hook ()\n"
+" (define-key scheme-mode-map \"\\C-m\" 'reindent-then-newline-and-indent))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1441
+#, no-wrap
+msgid ""
+";; Emacs-Lisp mode...\n"
+"(defun my-lisp-mode-hook ()\n"
+" (define-key lisp-mode-map \"\\C-m\" 'reindent-then-newline-and-indent)\n"
+" (define-key lisp-mode-map \"\\C-i\" 'lisp-indent-line)\n"
+" (define-key lisp-mode-map \"\\C-j\" 'eval-print-last-sexp))\n"
+msgstr ""
+";; Emacs-Lisp mode...\n"
+"(defun my-lisp-mode-hook ()\n"
+" (define-key lisp-mode-map \"\\C-m\" 'reindent-then-newline-and-indent)\n"
+" (define-key lisp-mode-map \"\\C-i\" 'lisp-indent-line)\n"
+" (define-key lisp-mode-map \"\\C-j\" 'eval-print-last-sexp))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1449
+#, no-wrap
+msgid ""
+";; Add all of the hooks...\n"
+"(add-hook 'c++-mode-hook 'my-c++-mode-hook)\n"
+"(add-hook 'c-mode-hook 'my-c-mode-hook)\n"
+"(add-hook 'scheme-mode-hook 'my-scheme-mode-hook)\n"
+"(add-hook 'emacs-lisp-mode-hook 'my-lisp-mode-hook)\n"
+"(add-hook 'lisp-mode-hook 'my-lisp-mode-hook)\n"
+"(add-hook 'perl-mode-hook 'my-perl-mode-hook)\n"
+msgstr ""
+";; Add all of the hooks...\n"
+"(add-hook 'c++-mode-hook 'my-c++-mode-hook)\n"
+"(add-hook 'c-mode-hook 'my-c-mode-hook)\n"
+"(add-hook 'scheme-mode-hook 'my-scheme-mode-hook)\n"
+"(add-hook 'emacs-lisp-mode-hook 'my-lisp-mode-hook)\n"
+"(add-hook 'lisp-mode-hook 'my-lisp-mode-hook)\n"
+"(add-hook 'perl-mode-hook 'my-perl-mode-hook)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1455
+#, no-wrap
+msgid ""
+";; Complement to next-error\n"
+"(defun previous-error (n)\n"
+" \"Visit previous compilation error message and corresponding source code.\"\n"
+" (interactive \"p\")\n"
+" (next-error (- n)))\n"
+msgstr ""
+";; Complement to next-error\n"
+"(defun previous-error (n)\n"
+" \"Visit previous compilation error message and corresponding source code.\"\n"
+" (interactive \"p\")\n"
+" (next-error (- n)))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1468
+#, no-wrap
+msgid ""
+";; Misc...\n"
+"(transient-mark-mode 1)\n"
+"(setq mark-even-if-inactive t)\n"
+"(setq visible-bell nil)\n"
+"(setq next-line-add-newlines nil)\n"
+"(setq compile-command \"make\")\n"
+"(setq suggest-key-bindings nil)\n"
+"(put 'eval-expression 'disabled nil)\n"
+"(put 'narrow-to-region 'disabled nil)\n"
+"(put 'set-goal-column 'disabled nil)\n"
+"(if (>= emacs-major-version 21)\n"
+"\t(setq show-trailing-whitespace t))\n"
+msgstr ""
+";; Misc...\n"
+"(transient-mark-mode 1)\n"
+"(setq mark-even-if-inactive t)\n"
+"(setq visible-bell nil)\n"
+"(setq next-line-add-newlines nil)\n"
+"(setq compile-command \"make\")\n"
+"(setq suggest-key-bindings nil)\n"
+"(put 'eval-expression 'disabled nil)\n"
+"(put 'narrow-to-region 'disabled nil)\n"
+"(put 'set-goal-column 'disabled nil)\n"
+"(if (>= emacs-major-version 21)\n"
+"\t(setq show-trailing-whitespace t))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1474
+#, no-wrap
+msgid ""
+";; Elisp archive searching\n"
+"(autoload 'format-lisp-code-directory \"lispdir\" nil t)\n"
+"(autoload 'lisp-dir-apropos \"lispdir\" nil t)\n"
+"(autoload 'lisp-dir-retrieve \"lispdir\" nil t)\n"
+"(autoload 'lisp-dir-verify \"lispdir\" nil t)\n"
+msgstr ""
+";; Elisp archive searching\n"
+"(autoload 'format-lisp-code-directory \"lispdir\" nil t)\n"
+"(autoload 'lisp-dir-apropos \"lispdir\" nil t)\n"
+"(autoload 'lisp-dir-retrieve \"lispdir\" nil t)\n"
+"(autoload 'lisp-dir-verify \"lispdir\" nil t)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1483
+#, no-wrap
+msgid ""
+";; Font lock mode\n"
+"(defun my-make-face (face color &optional bold)\n"
+" \"Create a face from a color and optionally make it bold\"\n"
+" (make-face face)\n"
+" (copy-face 'default face)\n"
+" (set-face-foreground face color)\n"
+" (if bold (make-face-bold face))\n"
+" )\n"
+msgstr ""
+";; Font lock mode\n"
+"(defun my-make-face (face color &optional bold)\n"
+" \"Create a face from a color and optionally make it bold\"\n"
+" (make-face face)\n"
+" (copy-face 'default face)\n"
+" (set-face-foreground face color)\n"
+" (if bold (make-face-bold face))\n"
+" )\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1496
+#, no-wrap
+msgid ""
+"(if (eq window-system 'x)\n"
+" (progn\n"
+" (my-make-face 'blue \"blue\")\n"
+" (my-make-face 'red \"red\")\n"
+" (my-make-face 'green \"dark green\")\n"
+" (setq font-lock-comment-face 'blue)\n"
+" (setq font-lock-string-face 'bold)\n"
+" (setq font-lock-type-face 'bold)\n"
+" (setq font-lock-keyword-face 'bold)\n"
+" (setq font-lock-function-name-face 'red)\n"
+" (setq font-lock-doc-string-face 'green)\n"
+" (add-hook 'find-file-hooks 'font-lock-auto-mode-select)\n"
+msgstr ""
+"(if (eq window-system 'x)\n"
+" (progn\n"
+" (my-make-face 'blue \"blue\")\n"
+" (my-make-face 'red \"red\")\n"
+" (my-make-face 'green \"dark green\")\n"
+" (setq font-lock-comment-face 'blue)\n"
+" (setq font-lock-string-face 'bold)\n"
+" (setq font-lock-type-face 'bold)\n"
+" (setq font-lock-keyword-face 'bold)\n"
+" (setq font-lock-function-name-face 'red)\n"
+" (setq font-lock-doc-string-face 'green)\n"
+" (add-hook 'find-file-hooks 'font-lock-auto-mode-select)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1504
+#, no-wrap
+msgid ""
+" (setq baud-rate 1000000)\n"
+" (global-set-key \"\\C-cmm\" 'menu-bar-mode)\n"
+" (global-set-key \"\\C-cms\" 'scroll-bar-mode)\n"
+" (global-set-key [backspace] 'backward-delete-char)\n"
+"\t\t\t\t\t; (global-set-key [delete] 'delete-char)\n"
+" (standard-display-european t)\n"
+" (load-library \"iso-transl\")))\n"
+msgstr ""
+" (setq baud-rate 1000000)\n"
+" (global-set-key \"\\C-cmm\" 'menu-bar-mode)\n"
+" (global-set-key \"\\C-cms\" 'scroll-bar-mode)\n"
+" (global-set-key [backspace] 'backward-delete-char)\n"
+"\t\t\t\t\t; (global-set-key [delete] 'delete-char)\n"
+" (standard-display-european t)\n"
+" (load-library \"iso-transl\")))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1521
+#, no-wrap
+msgid ""
+";; X11 or PC using direct screen writes\n"
+"(if window-system\n"
+" (progn\n"
+" ;; (global-set-key [M-f1] 'hilit-repaint-command)\n"
+" ;; (global-set-key [M-f2] [?\\C-u M-f1])\n"
+" (setq hilit-mode-enable-list\n"
+"\t '(not text-mode c-mode c++-mode emacs-lisp-mode lisp-mode\n"
+"\t\t scheme-mode)\n"
+"\t hilit-auto-highlight nil\n"
+"\t hilit-auto-rehighlight 'visible\n"
+"\t hilit-inhibit-hooks nil\n"
+"\t hilit-inhibit-rebinding t)\n"
+" (require 'hilit19)\n"
+" (require 'paren))\n"
+" (setq baud-rate 2400)\t\t\t; For slow serial connections\n"
+" )\n"
+msgstr ""
+";; X11 or PC using direct screen writes\n"
+"(if window-system\n"
+" (progn\n"
+" ;; (global-set-key [M-f1] 'hilit-repaint-command)\n"
+" ;; (global-set-key [M-f2] [?\\C-u M-f1])\n"
+" (setq hilit-mode-enable-list\n"
+"\t '(not text-mode c-mode c++-mode emacs-lisp-mode lisp-mode\n"
+"\t\t scheme-mode)\n"
+"\t hilit-auto-highlight nil\n"
+"\t hilit-auto-rehighlight 'visible\n"
+"\t hilit-inhibit-hooks nil\n"
+"\t hilit-inhibit-rebinding t)\n"
+" (require 'hilit19)\n"
+" (require 'paren))\n"
+" (setq baud-rate 2400)\t\t\t; For slow serial connections\n"
+" )\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1530
+#, no-wrap
+msgid ""
+";; TTY type terminal\n"
+"(if (and (not window-system)\n"
+"\t (not (equal system-type 'ms-dos)))\n"
+" (progn\n"
+" (if first-time\n"
+"\t (progn\n"
+"\t (keyboard-translate ?\\C-h ?\\C-?)\n"
+"\t (keyboard-translate ?\\C-? ?\\C-h)))))\n"
+msgstr ""
+";; TTY type terminal\n"
+"(if (and (not window-system)\n"
+"\t (not (equal system-type 'ms-dos)))\n"
+" (progn\n"
+" (if first-time\n"
+"\t (progn\n"
+"\t (keyboard-translate ?\\C-h ?\\C-?)\n"
+"\t (keyboard-translate ?\\C-? ?\\C-h)))))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1536
+#, no-wrap
+msgid ""
+";; Under UNIX\n"
+"(if (not (equal system-type 'ms-dos))\n"
+" (progn\n"
+" (if first-time\n"
+"\t (server-start))))\n"
+msgstr ""
+";; Under UNIX\n"
+"(if (not (equal system-type 'ms-dos))\n"
+" (progn\n"
+" (if first-time\n"
+"\t (server-start))))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1544
+#, no-wrap
+msgid ""
+";; Add any face changes here\n"
+"(add-hook 'term-setup-hook 'my-term-setup-hook)\n"
+"(defun my-term-setup-hook ()\n"
+" (if (eq window-system 'pc)\n"
+" (progn\n"
+";;\t(set-face-background 'default \"red\")\n"
+"\t)))\n"
+msgstr ""
+";; Add any face changes here\n"
+"(add-hook 'term-setup-hook 'my-term-setup-hook)\n"
+"(defun my-term-setup-hook ()\n"
+" (if (eq window-system 'pc)\n"
+" (progn\n"
+";;\t(set-face-background 'default \"red\")\n"
+"\t)))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1550
+#, no-wrap
+msgid ""
+";; Restore the \"desktop\" - do this as late as possible\n"
+"(if first-time\n"
+" (progn\n"
+" (desktop-load-default)\n"
+" (desktop-read)))\n"
+msgstr ""
+";; Restore the \"desktop\" - do this as late as possible\n"
+"(if first-time\n"
+" (progn\n"
+" (desktop-load-default)\n"
+" (desktop-read)))\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1553
+#, no-wrap
+msgid ""
+";; Indicate that this file has been read at least once\n"
+"(setq first-time nil)\n"
+msgstr ""
+";; Indicate that this file has been read at least once\n"
+"(setq first-time nil)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1555
+#, no-wrap
+msgid ";; No need to debug anything now\n"
+msgstr ";; No need to debug anything now\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1557
+#, no-wrap
+msgid "(setq debug-on-error nil)\n"
+msgstr "(setq debug-on-error nil)\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1560
+#, no-wrap
+msgid ""
+";; All done\n"
+"(message \"All done, %s%s\" (user-login-name) \".\")\n"
+msgstr ""
+";; All done\n"
+"(message \"All done, %s%s\" (user-login-name) \".\")\n"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1563
+#, no-wrap
+msgid "Extending the Range of Languages Emacs Understands"
+msgstr "Расширение списка языков, понимаемых Emacs"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1566
+msgid ""
+"Now, this is all very well if you only want to program in the languages "
+"already catered for in [.filename]#.emacs# (C, C++, Perl, Lisp and Scheme), "
+"but what happens if a new language called \"whizbang\" comes out, full of "
+"exciting features?"
+msgstr ""
+"Вот, это все хорошо, если вы хотите программировать только на языках, уже "
+"предусмотренных в [.filename]#.emacs# (C, C++, Perl, Lisp и Scheme), но что "
+"произойдет, если появится новый язык под названием \"whizbang\", полный "
+"захватывающих возможностей?"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1570
+msgid ""
+"The first thing to do is find out if whizbang comes with any files that tell "
+"Emacs about the language. These usually end in [.filename]#.el#, short for "
+"\"Emacs Lisp\". For example, if whizbang is a FreeBSD port, we can locate "
+"these files by doing"
+msgstr ""
+"Первое, что нужно сделать, — это выяснить, поставляются ли с whizbang какие-"
+"либо файлы, сообщающие Emacs о языке. Обычно они заканчиваются на "
+"[.filename]#.el#, что означает \"Emacs Lisp\". Например, если whizbang "
+"является портом FreeBSD, мы можем найти эти файлы, выполнив"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1574
+#, no-wrap
+msgid "% find /usr/ports/lang/whizbang -name \"*.el\" -print\n"
+msgstr "% find /usr/ports/lang/whizbang -name \"*.el\" -print\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1578
+msgid ""
+"and install them by copying them into the Emacs site Lisp directory. On "
+"FreeBSD, this is [.filename]#/usr/local/share/emacs/site-lisp#."
+msgstr ""
+"и установите их, скопировав в каталог Emacs, где находятся файлы Lisp (site "
+"Lisp). В FreeBSD это [.filename]#/usr/local/share/emacs/site-lisp#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1580
+msgid "So for example, if the output from the find command was"
+msgstr "Вот пример, если вывод команды find был"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1584
+#, no-wrap
+msgid "/usr/ports/lang/whizbang/work/misc/whizbang.el\n"
+msgstr "/usr/ports/lang/whizbang/work/misc/whizbang.el\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1587
+msgid "we would do"
+msgstr "мы бы сделали"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1591
+#, no-wrap
+msgid "# cp /usr/ports/lang/whizbang/work/misc/whizbang.el /usr/local/share/emacs/site-lisp\n"
+msgstr "# cp /usr/ports/lang/whizbang/work/misc/whizbang.el /usr/local/share/emacs/site-lisp\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1596
+msgid ""
+"Next, we need to decide what extension whizbang source files have. Let us "
+"say for the sake of argument that they all end in [.filename]#.wiz#. We "
+"need to add an entry to our [.filename]#.emacs# to make sure Emacs will be "
+"able to use the information in [.filename]#whizbang.el#."
+msgstr ""
+"Далее нам нужно решить, какое расширение имеют исходные файлы whizbang. "
+"Допустим, для примера, что все они заканчиваются на [.filename]#.wiz#. Нам "
+"необходимо добавить запись в наш [.filename]#.emacs#, чтобы убедиться, что "
+"Emacs сможет использовать информацию из [.filename]#whizbang.el#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1598
+msgid ""
+"Find the auto-mode-alist entry in [.filename]#.emacs# and add a line for "
+"whizbang, such as:"
+msgstr ""
+"Найдите запись auto-mode-alist в файле [.filename]#.emacs# и добавьте строку "
+"для whizbang, например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1606
+#, no-wrap
+msgid ""
+"...\n"
+"(\"\\\\.lsp$\" . lisp-mode)\n"
+"(\"\\\\.wiz$\" . whizbang-mode)\n"
+"(\"\\\\.scm$\" . scheme-mode)\n"
+"...\n"
+msgstr ""
+"...\n"
+"(\"\\\\.lsp$\" . lisp-mode)\n"
+"(\"\\\\.wiz$\" . whizbang-mode)\n"
+"(\"\\\\.scm$\" . scheme-mode)\n"
+"...\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1609
+msgid ""
+"This means that Emacs will automatically go into `whizbang-mode` when you "
+"edit a file ending in [.filename]#.wiz#."
+msgstr ""
+"Это означает, что Emacs автоматически перейдёт в режим `whizbang-mode` при "
+"редактировании файла с расширением [.filename]#.wiz#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1612
+msgid ""
+"Just below this, you will find the font-lock-auto-mode-list entry. Add "
+"`whizbang-mode` to it like so:"
+msgstr ""
+"Непосредственно ниже вы найдете запись font-lock-auto-mode-list. Добавьте "
+"`whizbang-mode` в нее следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1619
+#, no-wrap
+msgid ""
+";; Auto font lock mode\n"
+"(defvar font-lock-auto-mode-list\n"
+" (list 'c-mode 'c++-mode 'c++-c-mode 'emacs-lisp-mode 'whizbang-mode 'lisp-mode 'perl-mode 'scheme-mode)\n"
+" \"List of modes to always start in font-lock-mode\")\n"
+msgstr ""
+";; Auto font lock mode\n"
+"(defvar font-lock-auto-mode-list\n"
+" (list 'c-mode 'c++-mode 'c++-c-mode 'emacs-lisp-mode 'whizbang-mode 'lisp-mode 'perl-mode 'scheme-mode)\n"
+" \"List of modes to always start in font-lock-mode\")\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1622
+msgid ""
+"This means that Emacs will always enable `font-lock-mode` (ie syntax "
+"highlighting) when editing a [.filename]#.wiz# file."
+msgstr ""
+"Это означает, что Emacs всегда будет включать `font-lock-mode` (т.е. "
+"подсветку синтаксиса) при редактировании файла [.filename]#.wiz#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1625
+msgid ""
+"And that is all that is needed. If there is anything else you want done "
+"automatically when you open up [.filename]#.wiz#, you can add a `whizbang-"
+"mode hook` (see `my-scheme-mode-hook` for a simple example that adds `auto-"
+"indent`)."
+msgstr ""
+"И это всё, что требуется. Если вам нужно, чтобы что-то ещё выполнялось "
+"автоматически при открытии [.filename]#.wiz#, вы можете добавить `whizbang-"
+"mode hook` (см. `my-scheme-mode-hook` для простого примера, который "
+"добавляет `auto-indent`)."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1627
+#, no-wrap
+msgid "Further Reading"
+msgstr "Для дальнейшего ознакомления"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1630
+msgid ""
+"For information about setting up a development environment for contributing "
+"fixes to FreeBSD itself, please see man:development[7]."
+msgstr ""
+"Для получения информации о настройке среды разработки для внесения "
+"исправлений в саму FreeBSD см. man:development[7]."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1632
+msgid ""
+"Brian Harvey and Matthew Wright _Simply Scheme_ MIT 1994. ISBN 0-262-08226-8"
+msgstr ""
+"Brian Harvey and Matthew Wright _Simply Scheme_ MIT 1994. ISBN 0-262-08226-8"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1633
+msgid "Randall Schwartz _Learning Perl_ O'Reilly 1993 ISBN 1-56592-042-2"
+msgstr "Randall Schwartz _Learning Perl_ O'Reilly 1993 ISBN 1-56592-042-2"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1634
+msgid ""
+"Patrick Henry Winston and Berthold Klaus Paul Horn _Lisp (3rd Edition)_ "
+"Addison-Wesley 1989 ISBN 0-201-08319-1"
+msgstr ""
+"Patrick Henry Winston and Berthold Klaus Paul Horn _Lisp (3rd Edition)_ "
+"Addison-Wesley 1989 ISBN 0-201-08319-1"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1635
+msgid ""
+"Brian W. Kernighan and Rob Pike _The Unix Programming Environment_ Prentice-"
+"Hall 1984 ISBN 0-13-937681-X"
+msgstr ""
+"Brian W. Kernighan and Rob Pike _The Unix Programming Environment_ Prentice-"
+"Hall 1984 ISBN 0-13-937681-X"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1636
+msgid ""
+"Brian W. Kernighan and Dennis M. Ritchie _The C Programming Language (2nd "
+"Edition)_ Prentice-Hall 1988 ISBN 0-13-110362-8"
+msgstr ""
+"Brian W. Kernighan and Dennis M. Ritchie _The C Programming Language (2nd "
+"Edition)_ Prentice-Hall 1988 ISBN 0-13-110362-8"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1637
+msgid ""
+"Bjarne Stroustrup _The C++ Programming Language_ Addison-Wesley 1991 ISBN "
+"0-201-53992-6"
+msgstr ""
+"Bjarne Stroustrup _The C++ Programming Language_ Addison-Wesley 1991 ISBN "
+"0-201-53992-6"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1638
+msgid ""
+"W. Richard Stevens _Advanced Programming in the Unix Environment_ Addison-"
+"Wesley 1992 ISBN 0-201-56317-7"
+msgstr ""
+"W. Richard Stevens _Advanced Programming in the Unix Environment_ Addison-"
+"Wesley 1992 ISBN 0-201-56317-7"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/tools/_index.adoc:1638
+msgid ""
+"W. Richard Stevens _Unix Network Programming_ Prentice-Hall 1990 ISBN "
+"0-13-949876-1"
+msgstr ""
+"W. Richard Stevens _Unix Network Programming_ Prentice-Hall 1990 ISBN "
+"0-13-949876-1"
diff --git a/documentation/content/ru/books/developers-handbook/x86/_index.adoc b/documentation/content/ru/books/developers-handbook/x86/_index.adoc
new file mode 100644
index 0000000000..c44f67bb5c
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/x86/_index.adoc
@@ -0,0 +1,3864 @@
+---
+authors:
+description: 'Программирование на ассемблере x86'
+next: books/developers-handbook/partv
+params:
+ path: /books/developers-handbook/x86/
+prev: books/developers-handbook/partiv
+showBookMenu: true
+tags: ["x86", "guide"]
+title: 'Глава 11. Программирование на языке ассемблера для x86'
+weight: 15
+---
+
+[[x86]]
+= Программирование на ассемблере x86
+:doctype: book
+:toc: macro
+:toclevels: 1
+:icons: font
+:sectnums:
+:sectnumlevels: 6
+:sectnumoffset: A
+:partnums:
+:source-highlighter: rouge
+:experimental:
+:images-path: books/developers-handbook/
+
+ifdef::env-beastie[]
+ifdef::backend-html5[]
+:imagesdir: ../../../../images/{images-path}
+endif::[]
+ifndef::book[]
+include::shared/authors.adoc[]
+include::shared/mirrors.adoc[]
+include::shared/releases.adoc[]
+include::shared/attributes/attributes-{{% lang %}}.adoc[]
+include::shared/{{% lang %}}/teams.adoc[]
+include::shared/{{% lang %}}/mailing-lists.adoc[]
+include::shared/{{% lang %}}/urls.adoc[]
+toc::[]
+endif::[]
+ifdef::backend-pdf,backend-epub3[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+endif::[]
+
+ifndef::env-beastie[]
+toc::[]
+include::../../../../../shared/asciidoctor.adoc[]
+endif::[]
+
+_Эта глава была написана {stanislav}._
+
+[[x86-intro]]
+== Обзор
+
+Программирование на ассемблере в UNIX(R) крайне плохо документировано. Обычно предполагается, что никто не захочет его использовать, поскольку различные системы UNIX(R) работают на разных микропроцессорах, и поэтому всё должно быть написано на C для обеспечения переносимости.
+
+В действительности переносимость программ на C — это скорее миф. Даже программы на C требуют изменений при переносе с одной UNIX(R)-системы на другую, независимо от процессора, на котором они работают. Обычно такая программа содержит множество условных операторов, зависящих от системы, для которой она компилируется.
+
+Даже если мы считаем, что всё программное обеспечение UNIX(R) должно быть написано на C или другом языке высокого уровня, нам всё равно нужны программисты на ассемблере: кто же ещё напишет часть библиотеки C, которая обращается к ядру?
+
+В этой главе я попытаюсь показать вам, как можно использовать язык ассемблера для написания программ под UNIX(R), в частности под FreeBSD.
+
+В этой главе не объясняются основы языка ассемблера. Существует достаточно ресурсов на эту тему (например, полный онлайн-курс по языку ассемблера можно найти в http://webster.cs.ucr.edu/[Искусстве языка ассемблера] Рэндалла Хайда; если вы предпочитаете печатные книги, обратите внимание на «Язык ассемблера шаг за шагом» Джеффа Дантемана (ISBN: 0471375233)). Однако после прочтения этой главы любой программист на языке ассемблера сможет писать программы для FreeBSD быстро и эффективно.
+
+Copyright (R) 2000-2001 G. Adam Stanislav. All rights reserved.
+
+[[x86-the-tools]]
+== Инструменты
+
+[[x86-the-assembler]]
+=== Ассемблер
+
+Важнейшим инструментом для программирования на языке ассемблера является ассемблер — программа, преобразующая код на языке ассемблера в машинный код.
+
+Три очень разных ассемблера доступны для FreeBSD. И man:llvm-as[1] (включён в package:devel/llvm[]), и man:as[1] (включён в package:devel/binutils[]) используют традиционный синтаксис ассемблера UNIX(R).
+
+С другой стороны, man:nasm[1] (устанавливаемый через package:devel/nasm[]) использует синтаксис Intel. Его основное преимущество в том, что он может ассемблировать код для многих операционных систем.
+
+В этой главе используется синтаксис nasm, потому что большинство программистов на ассемблере, приходящих в FreeBSD из других операционных систем, найдут его более понятным. Кроме того, если честно, это то, к чему я привык.
+
+[[x86-the-linker]]
+=== Компоновщик
+
+Результат работы ассемблера, как и любого компилятора, необходимо связать, чтобы получить исполняемый файл.
+
+Стандартный компоновщик man:ld[1] поставляется с FreeBSD. Он работает с кодом, собранным любым из ассемблеров.
+
+[[x86-system-calls]]
+== Системные вызовы
+
+[[x86-default-calling-convention]]
+=== Стандартное соглашение о вызовах
+
+По умолчанию ядро FreeBSD использует соглашение о вызовах C. Кроме того, хотя доступ к ядру осуществляется с помощью `int 80h`, предполагается, что программа вызовет функцию, которая выполняет `int 80h`, а не будет выполнять `int 80h` напрямую.
+
+Эта традиция очень удобна и значительно превосходит соглашение Microsoft(R), используемое в MS-DOS(R). Почему? Потому что соглашение UNIX(R) позволяет любой программе, написанной на любом языке, обращаться к ядру.
+
+Программа на ассемблере также может это сделать. Например, мы могли бы открыть файл:
+
+[.programlisting]
+....
+kernel:
+ int 80h ; Call kernel
+ ret
+
+open:
+ push dword mode
+ push dword flags
+ push dword path
+ mov eax, 5
+ call kernel
+ add esp, byte 12
+ ret
+....
+
+Это очень понятный и переносимый способ написания кода. Если вам нужно перенести код на UNIX(R)-систему, которая использует другое прерывание или другой способ передачи параметров, все, что вам нужно изменить, это процедуру kernel.
+
+Но программисты на ассемблере любят экономить такты. Приведённый выше пример требует комбинации `call/ret`. Мы можем исключить её, сделав ``push`` дополнительного двойного слова:
+
+[.programlisting]
+....
+open:
+ push dword mode
+ push dword flags
+ push dword path
+ mov eax, 5
+ push eax ; Or any other dword
+ int 80h
+ add esp, byte 16
+....
+
+Помещённое в `EAX` значение `5` идентифицирует функцию ядра, в данном случае `open`.
+
+[[x86-alternate-calling-convention]]
+=== Альтернативное соглашение о вызовах
+
+FreeBSD — это чрезвычайно гибкая система. Она предлагает другие способы вызова ядра. Однако для работы необходимо, чтобы в системе была установлена эмуляция Linux.
+
+Linux — это система, подобная UNIX(R). Однако ее ядро использует то же соглашение о системных вызовов для передачи параметров в регистрах, что и MS-DOS(R). Как и в соглашении UNIX(R), номер функции помещается в `EAX`. Однако параметры передаются не в стеке, а в регистрах `EBX, ECX, EDX, ESI, EDI, EBP`:
+
+[.programlisting]
+....
+open:
+ mov eax, 5
+ mov ebx, path
+ mov ecx, flags
+ mov edx, mode
+ int 80h
+....
+
+Этот подход имеет значительный недостаток по сравнению с UNIX(R), по крайней мере, в контексте программирования на ассемблере: каждый раз при вызове ядра необходимо сохранять регистры с помощью `push`, а затем восстанавливать их с помощью `pop`. Это делает ваш код более громоздким и медленным. Тем не менее, FreeBSD предоставляет вам выбор.
+
+Если вы решите использовать соглашение Linux, вы должны сообщить об этом системе. После того как ваша программа будет ассемблирована и слинкована, вам нужно пометить исполняемый файл:
+
+[source, shell]
+....
+% brandelf -t Linux filename
+....
+
+[[x86-use-geneva]]
+=== Какое соглашение следует использовать?
+
+Если вы разрабатываете код специально для FreeBSD, всегда следует использовать соглашение UNIX(R): это быстрее, вы можете хранить глобальные переменные в регистрах, вам не нужно маркировать исполняемый файл, и вы не требуете установки пакета эмуляции Linux на целевой системе.
+
+Хотя вы можете хотеть создать переносимый код, который также работает на Linux, вам, вероятно, по-прежнему будет нужен максимально эффективный код для пользователей FreeBSD. Я покажу вам, как этого добиться, после того как объясню основы.
+
+[[x86-call-numbers]]
+=== Номера вызовов
+
+Чтобы сообщить ядру, какую системную службу вы вызываете, поместите её номер в `EAX`. Разумеется, вам необходимо знать, что это за номер.
+
+[[x86-the-syscalls-file]]
+==== Файл [.filename]#syscalls#
+
+Номера перечислены в [.filename]#syscalls#. Команда `locate syscalls` находит этот файл в нескольких различных форматах, все они создаются автоматически из [.filename]#syscalls.master#.
+
+Основной файл для стандартного соглашения о вызовах UNIX(R) можно найти в [.filename]#/usr/src/sys/kern/syscalls.master#. Если вам необходимо использовать другое соглашение, реализованное в режиме эмуляции Linux, обратитесь к [.filename]#/usr/src/sys/i386/linux/syscalls.master#.
+
+[NOTE]
+====
+Не только FreeBSD и Linux используют разные соглашения о вызовах, но иногда они используют разные номера для одних и тех же функций.
+====
+
+[.filename]#syscalls.master# описывает, как должен быть выполнен вызов:
+
+[.programlisting]
+....
+0 STD NOHIDE { int nosys(void); } syscall nosys_args int
+1 STD NOHIDE { void exit(int rval); } exit rexit_args void
+2 STD POSIX { int fork(void); }
+3 STD POSIX { ssize_t read(int fd, void *buf, size_t nbyte); }
+4 STD POSIX { ssize_t write(int fd, const void *buf, size_t nbyte); }
+5 STD POSIX { int open(char *path, int flags, int mode); }
+6 STD POSIX { int close(int fd); }
+etc...
+....
+
+Это крайний левый столбец, который указывает число, которое нужно поместить в `EAX`.
+
+Самый правый столбец указывает, какие параметры нужно `втолкнуть` в стек командой push. Они `вталкиваются` _справа налево_.
+
+Например, чтобы `открыть` файл, нам сначала нужно сделать `push` для `mode`, затем `flags`, а затем адрес, по которому хранится `path`.
+
+[[x86-return-values]]
+== Возвращаемые значения
+
+От системных вызовов не было бы никакой пользы, если бы они не возвращали какое-либо значение: дескриптор открытого файла, количество байтов, прочитанных в буфер, системное время и т.д.
+
+Кроме того, система должна уведомлять нас, если возникает ошибка: файл не существует, системные ресурсы исчерпаны, передан недопустимый параметр и т. д.
+
+[[x86-man-pages]]
+=== Страницы справочника
+
+Традиционным источником информации о различных системных вызовах в UNIX(R)-системах являются страницы Справочника. В FreeBSD системные вызовы описаны в разделе 2, иногда в разделе 3.
+
+Например, man:open[2] говорит:
+
+[.blockquote]
+В случае успеха `open()` возвращает неотрицательное целое число, называемое файловым дескриптором. В случае ошибки возвращается `-1`, а переменной `errno` присваивается код ошибки.
+
+Программист на ассемблере, впервые столкнувшийся с UNIX(R) и FreeBSD, сразу же задастся вопросом: где находится `errno` и как к ней обратиться?
+
+[NOTE]
+====
+Информация, представленная в руководствах, применима к программам на языке C. Программистам на языке ассемблера требуется дополнительная информация.
+====
+
+[[x86-where-return-values]]
+=== Где возвращаемые значения?
+
+К сожалению, это зависит от ситуации... Для большинства системных вызовов возвращаемое значение находится в `EAX`, но не для всех. Хорошее правило при первой работе с системным вызовом — искать возвращаемое значение в `EAX`. Если его там нет, потребуется дополнительное исследование.
+
+[NOTE]
+====
+Я знаю о одном системном вызове, который возвращает значение в `EDX`: `SYS_fork`. Все остальные, с которыми я работал, используют `EAX`. Но я еще не работал со всеми из них.
+====
+
+[TIP]
+====
+Если вы не можете найти ответ здесь или где-либо ещё, изучите исходный код libc и посмотрите, как он взаимодействует с ядром.
+====
+
+[[x86-where-errno]]
+=== Где находится `errno`?
+
+Фактически, нигде...
+
+`errno` является частью языка C, а не ядра UNIX(R). При прямом доступе к сервисам ядра код ошибки возвращается в регистре `EAX` — том же регистре, в котором обычно оказывается корректное возвращаемое значение.
+
+Это совершенно логично. Если нет ошибки, то нет и кода ошибки. Если есть ошибка, то нет возвращаемого значения. Один регистр может содержать либо то, либо другое.
+
+[[x86-how-to-know-error]]
+=== Определение возникновения ошибки
+
+При использовании стандартного соглашения о вызовах FreeBSD флаг `carry flag` сбрасывается при успехе и устанавливается при неудаче.
+
+При использовании режима эмуляции Linux знаковое значение в `EAX` неотрицательно в случае успеха и содержит возвращаемое значение. В случае ошибки значение отрицательное, т.е. `-errno`.
+
+[[x86-portable-code]]
+== Создание переносимого кода
+
+Портативность обычно не является сильной стороной языка ассемблера. Тем не менее, написание программ на ассемблере для разных платформ возможно, особенно с использованием nasm. Я создавал библиотеки на ассемблере, которые можно было собрать для таких разных операционных систем, как Windows(R) и FreeBSD.
+
+Это становится еще более возможным, когда вы хотите, чтобы ваш код работал на двух платформах, которые, хотя и различны, основаны на схожих архитектурах.
+
+Например, FreeBSD — это UNIX(R), а Linux — UNIX(R)-подобная система. Я упомянул лишь три различия между ними (с точки зрения программиста на ассемблере): соглашение о вызовах, номера функций и способ возврата значений.
+
+[[x86-deal-with-function-numbers]]
+=== Работа с номерами функций
+
+Во многих случаях номера функций совпадают. Однако, даже если это не так, проблему легко решить: вместо использования чисел в коде применяйте константы, объявленные по-разному в зависимости от целевой архитектуры:
+
+[.programlisting]
+....
+%ifdef LINUX
+%define SYS_execve 11
+%else
+%define SYS_execve 59
+%endif
+....
+
+[[x86-deal-with-geneva]]
+=== Работа с соглашениями
+
+Оба, соглашение о вызовах и возвращаемое значение (проблема `errno`) могут быть решены с помощью макросов:
+
+[.programlisting]
+....
+%ifdef LINUX
+
+%macro system 0
+ call kernel
+%endmacro
+
+align 4
+kernel:
+ push ebx
+ push ecx
+ push edx
+ push esi
+ push edi
+ push ebp
+
+ mov ebx, [esp+32]
+ mov ecx, [esp+36]
+ mov edx, [esp+40]
+ mov esi, [esp+44]
+ mov ebp, [esp+48]
+ int 80h
+
+ pop ebp
+ pop edi
+ pop esi
+ pop edx
+ pop ecx
+ pop ebx
+
+ or eax, eax
+ js .errno
+ clc
+ ret
+
+.errno:
+ neg eax
+ stc
+ ret
+
+%else
+
+%macro system 0
+ int 80h
+%endmacro
+
+%endif
+....
+
+[[x86-deal-with-other-portability]]
+=== Устранение прочих проблем с переносимостью
+
+Приведённые выше решения могут помочь в большинстве случаев написания кода, переносимого между FreeBSD и Linux. Тем не менее, с некоторыми сервисами ядра различия более глубокие.
+
+В таком случае необходимо написать два разных обработчика для этих конкретных системных вызовов и использовать условную компиляцию. К счастью, большая часть вашего кода выполняет действия, отличные от вызовов ядра, поэтому обычно потребуется лишь несколько таких условных секций в коде.
+
+[[x86-portable-library]]
+=== Использование библиотеки
+
+Вы можете полностью избежать проблем с переносимостью в основном коде, написав библиотеку системных вызовов. Создайте отдельную библиотеку для FreeBSD, другую для Linux и ещё другие библиотеки для дополнительных операционных систем.
+
+В вашей библиотеке напишите отдельную функцию (или процедуру, если вы предпочитаете традиционную терминологию ассемблера) для каждого системного вызова. Используйте соглашение о вызовах C для передачи параметров. Однако по-прежнему передавайте номер вызова через `EAX`. В таком случае ваша библиотека FreeBSD может быть очень простой, так как множество внешне различных функций могут быть просто метками одного и того же кода:
+
+[.programlisting]
+....
+sys.open:
+sys.close:
+[etc...]
+ int 80h
+ ret
+....
+
+Ваша библиотека Linux потребует больше различных функций. Но даже здесь вы можете группировать системные вызовы, используя одинаковое количество параметров:
+
+[.programlisting]
+....
+sys.exit:
+sys.close:
+[etc... one-parameter functions]
+ push ebx
+ mov ebx, [esp+12]
+ int 80h
+ pop ebx
+ jmp sys.return
+
+...
+
+sys.return:
+ or eax, eax
+ js sys.err
+ clc
+ ret
+
+sys.err:
+ neg eax
+ stc
+ ret
+....
+
+Подход с использованием библиотек может показаться неудобным на первый взгляд, так как требует создания отдельного файла, от которого зависит ваш код. Однако у него есть множество преимуществ: во-первых, вам нужно написать его лишь один раз, и затем вы можете использовать его во всех своих программах. Вы даже можете позволить другим программистам на ассемблере использовать его или, возможно, воспользоваться библиотекой, написанной кем-то другим. Но, пожалуй, самое большое преимущество библиотеки заключается в том, что ваш код может быть перенесён на другие системы, даже другими программистами, просто путём написания новой библиотеки без каких-либо изменений в вашем коде.
+
+Если вам не нравится идея использования библиотеки, вы можете хотя бы разместить все системные вызовы в отдельном файле на ассемблере и скомпоновать его с основной программой. Здесь, опять же, все, что нужно сделать переносчикам, — это создать новый объектный файл для компоновки с основной программой.
+
+[[x86-portable-include]]
+=== Использование включаемого файла
+
+Если вы выпускаете своё программное обеспечение в виде исходного кода (или вместе с ним), вы можете использовать макросы и размещать их в отдельном файле, который включается в ваш код.
+
+Портеры вашего программного обеспечения просто напишут новый include-файл. Никакая библиотека или внешний объектный файл не требуются, и ваш код остается переносимым без необходимости редактирования.
+
+[NOTE]
+====
+Это подход, который мы будем использовать на протяжении всей главы. Мы назовем наш включаемый файл [.filename]#system.inc# и будем добавлять в него новые системные вызовы по мере их рассмотрения.
+====
+
+Мы можем начать наш [.filename]#system.inc# с объявления стандартных файловых дескрипторов:
+
+[.programlisting]
+....
+%define stdin 0
+%define stdout 1
+%define stderr 2
+....
+
+Далее мы создаем символическое имя для каждого системного вызова:
+
+[.programlisting]
+....
+%define SYS_nosys 0
+%define SYS_exit 1
+%define SYS_fork 2
+%define SYS_read 3
+%define SYS_write 4
+; [etc...]
+....
+
+Добавляем короткую, неглобальную процедуру с длинным именем, чтобы случайно не использовать это имя в нашем коде:
+
+[.programlisting]
+....
+section .text
+align 4
+access.the.bsd.kernel:
+ int 80h
+ ret
+....
+
+Мы создаем макрос, который принимает один аргумент — номер системного вызова:
+
+[.programlisting]
+....
+%macro system 1
+ mov eax, %1
+ call access.the.bsd.kernel
+%endmacro
+....
+
+Наконец, мы создаем макросы для каждого системного вызова. Эти макросы не принимают аргументов.
+
+[.programlisting]
+....
+%macro sys.exit 0
+ system SYS_exit
+%endmacro
+
+%macro sys.fork 0
+ system SYS_fork
+%endmacro
+
+%macro sys.read 0
+ system SYS_read
+%endmacro
+
+%macro sys.write 0
+ system SYS_write
+%endmacro
+
+; [etc...]
+....
+
+Продолжайте, введите это в ваш редактор и сохраните как [.filename]#system.inc#. Мы добавим больше по мере обсуждения дополнительных системных вызовов.
+
+[[x86-first-program]]
+== Наша первая программа
+
+Мы готовы к нашей первой обязательной программе — Hello, World!
+
+[.programlisting]
+....
+ %include 'system.inc'
+
+ section .data
+ hello db 'Hello, World!', 0Ah
+ hbytes equ $-hello
+
+ section .text
+ global _start
+_start:
+ push dword hbytes
+ push dword hello
+ push dword stdout
+ sys.write
+
+ push dword 0
+ sys.exit
+....
+
+Вот что он делает: Строка 1 включает определения, макросы и код из файла [.filename]#system.inc#.
+
+Строки 3-5 содержат данные: строка 3 начинает раздел/сегмент данных. Строка 4 содержит строку "Hello, World!", за которой следует новая строка (`0Ah`). Строка 5 создает константу, содержащую длину строки из строки 4 в байтах.
+
+Строки 7-16 содержат код. Обратите внимание, что FreeBSD использует формат файлов _elf_ для исполняемых файлов, который требует, чтобы каждая программа запускается с адреса, помеченного как `_start` (или, точнее, компоновщик ожидает этого). Эта метка должна быть глобальной.
+
+Строки 10-13 указывают системе записать `hbytes` байтов строки `hello` в `stdout`.
+
+Строки 15-16 указывают системе завершить программу с возвращаемым значением `0`. Системный вызов `SYS_exit` никогда не возвращает управление, поэтому код завершается в этой точке.
+
+[NOTE]
+====
+Если вы перешли на UNIX(R) с опытом программирования на ассемблере для MS-DOS(R), вы, возможно, привыкли писать напрямую в видеопамять. В FreeBSD или любой другой разновидности UNIX(R) вам не придётся об этом беспокоиться. С вашей точки зрения, вы записываете данные в файл под названием [.filename]#stdout#. Это может быть экран, терминал telnet, обычный файл или даже входные данные другой программы. Определять, что именно это будет, — задача системы.
+====
+
+[[x86-assemble-1]]
+=== Ассемблирование кода
+
+Наберите код в редакторе и сохраните его в файле с именем [.filename]#hello.asm#. Для сборки вам понадобится nasm.
+
+[[x86-get-nasm]]
+==== Установка nasm
+
+Если у вас нет nasm, введите:
+
+[source, shell]
+....
+% su
+Password:your root password
+# cd /usr/ports/devel/nasm
+# make install
+# exit
+%
+....
+
+Вы можете ввести `make install clean` вместо просто `make install`, если не хотите сохранять исходный код nasm.
+
+В любом случае FreeBSD автоматически загрузит nasm из интернета, скомпилирует его и установит в вашу систему.
+
+[NOTE]
+====
+Если ваша система не FreeBSD, вам нужно получить nasm с его https://sourceforge.net/projects/nasm[домашней страницы]. Вы по-прежнему можете использовать его для ассемблирования кода FreeBSD.
+====
+
+Теперь вы можете собрать, скомпоновать и запустить код:
+
+[source, shell]
+....
+% nasm -f elf hello.asm
+% ld -s -o hello hello.o
+% ./hello
+Hello, World!
+%
+....
+
+[[x86-unix-filters]]
+== Написание фильтров UNIX(R)
+
+Распространённым типом приложений в UNIX(R) являются фильтры — программы, которые читают данные из [.filename]#stdin#, обрабатывают их определённым образом, а затем записывают результат в [.filename]#stdout#.
+
+В этой главе мы разработаем простой фильтр и научимся читать из [.filename]#stdin# и писать в [.filename]#stdout#. Этот фильтр будет преобразовывать каждый байт входных данных в шестнадцатеричное число, за которым следует пробел.
+
+[.programlisting]
+....
+%include 'system.inc'
+
+section .data
+hex db '0123456789ABCDEF'
+buffer db 0, 0, ' '
+
+section .text
+global _start
+_start:
+ ; read a byte from stdin
+ push dword 1
+ push dword buffer
+ push dword stdin
+ sys.read
+ add esp, byte 12
+ or eax, eax
+ je .done
+
+ ; convert it to hex
+ movzx eax, byte [buffer]
+ mov edx, eax
+ shr dl, 4
+ mov dl, [hex+edx]
+ mov [buffer], dl
+ and al, 0Fh
+ mov al, [hex+eax]
+ mov [buffer+1], al
+
+ ; print it
+ push dword 3
+ push dword buffer
+ push dword stdout
+ sys.write
+ add esp, byte 12
+ jmp short _start
+
+.done:
+ push dword 0
+ sys.exit
+....
+
+В разделе данных мы создаем массив с именем `hex`. Он содержит 16 шестнадцатеричных цифр в порядке возрастания. За массивом следует буфер, который мы будем использовать как для ввода, так и для вывода. Первые два байта буфера изначально установлены в `0`. Именно сюда мы будем записывать две шестнадцатеричные цифры (первый байт также является местом, откуда мы будем считывать ввод). Третий байт — это пробел.
+
+Фрагмент кода состоит из четырех частей: чтение байта, преобразование его в шестнадцатеричное число, запись результата и завершение программы.
+
+Для чтения байта мы просим систему прочитать один байт из [.filename]#stdin# и сохранить его в первом байте `buffer`. Система возвращает количество прочитанных байтов в `EAX`. Это значение будет `1`, пока поступают данные, или `0`, если больше нет доступных входных данных. Поэтому мы проверяем значение `EAX`. Если оно равно `0`, мы переходим к метке `.done`, в противном случае продолжаем выполнение.
+
+[NOTE]
+====
+Для простоты мы пока игнорируем возможность возникновения ошибки.
+====
+
+Шестнадцатеричное преобразование считывает байт из `buffer` в `EAX`, а точнее только в `AL`, обнуляя остальные биты `EAX`. Мы также копируем байт в `EDX`, потому что нам нужно преобразовать верхние четыре бита (ниббл) отдельно от нижних четырех битов. Результат сохраняется в первых двух байтах буфера.
+
+Далее мы просим систему записать три байта буфера, то есть две шестнадцатеричные цифры и пробел, в [.filename]#stdout#. Затем мы возвращаемся к началу программы и обрабатываем следующий байт.
+
+Когда ввод больше не остаётся, мы просим систему завершить нашу программу, возвращая ноль, что традиционно означает успешное выполнение программы.
+
+Продолжайте и сохраните код в файле с именем [.filename]#hex.asm#, затем введите следующее (символ `^D` означает, что нужно нажать клавишу управления и, удерживая её, ввести `D`):
+
+[source, shell]
+....
+% nasm -f elf hex.asm
+% ld -s -o hex hex.o
+% ./hex
+Hello, World!
+48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A Here I come!
+48 65 72 65 20 49 20 63 6F 6D 65 21 0A ^D %
+....
+
+[NOTE]
+====
+Если вы переходите на UNIX(R) с MS-DOS(R), вам может быть интересно, почему каждая строка заканчивается на `0A` вместо `0D 0A`. Это связано с тем, что UNIX(R) не использует соглашение cr/lf, а использует соглашение "новая строка", которое в шестнадцатеричном виде представлено как `0A`.
+====
+
+Можем ли мы это улучшить? Что ж, во-первых, это немного запутанно, потому что после преобразования строки текста наш ввод больше не начинается с начала строки. Мы можем изменить это, чтобы после каждого `0A` выводилась новая строка вместо пробела:
+
+[.programlisting]
+....
+%include 'system.inc'
+
+section .data
+hex db '0123456789ABCDEF'
+buffer db 0, 0, ' '
+
+section .text
+global _start
+_start:
+ mov cl, ' '
+
+.loop:
+ ; read a byte from stdin
+ push dword 1
+ push dword buffer
+ push dword stdin
+ sys.read
+ add esp, byte 12
+ or eax, eax
+ je .done
+
+ ; convert it to hex
+ movzx eax, byte [buffer]
+ mov [buffer+2], cl
+ cmp al, 0Ah
+ jne .hex
+ mov [buffer+2], al
+
+.hex:
+ mov edx, eax
+ shr dl, 4
+ mov dl, [hex+edx]
+ mov [buffer], dl
+ and al, 0Fh
+ mov al, [hex+eax]
+ mov [buffer+1], al
+
+ ; print it
+ push dword 3
+ push dword buffer
+ push dword stdout
+ sys.write
+ add esp, byte 12
+ jmp short .loop
+
+.done:
+ push dword 0
+ sys.exit
+....
+
+Мы сохранили пробел в регистре `CL`. Это безопасно, потому что, в отличие от Microsoft(R) Windows(R), вызовы системы UNIX(R) не изменяют значение регистров, которые не используются для возврата значения.
+
+Это означает, что нам нужно установить `CL` только один раз. Поэтому мы добавили новую метку `.loop` и переходим к ней для следующего байта вместо перехода к `_start`. Мы также добавили метку `.hex`, чтобы третий байт `buffer` мог быть либо пробелом, либо новой строкой.
+
+После внесения изменений в файл [.filename]#hex.asm# введите:
+
+[source, shell]
+....
+% nasm -f elf hex.asm
+% ld -s -o hex hex.o
+% ./hex
+Hello, World!
+48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A
+Here I come!
+48 65 72 65 20 49 20 63 6F 6D 65 21 0A
+^D %
+....
+
+Выглядит лучше. Но этот код довольно неэффективен! Мы выполняем системный вызов для каждого отдельного байта дважды (один раз для чтения и ещё один для записи вывода).
+
+[[x86-buffered-io]]
+== Буферизованный ввод и вывод
+
+Мы можем повысить эффективность нашего кода, буферизуя ввод и вывод. Мы создаём входной буфер и читаем сразу целую последовательность байтов. Затем мы извлекаем их по одному из буфера.
+
+Мы также создаем выходной буфер. Мы сохраняем наш вывод в нем, пока он не заполнится. В этот момент мы просим ядро записать содержимое буфера в [.filename]#stdout#.
+
+Программа завершается, когда больше нет входных данных. Но нам всё ещё нужно попросить ядро записать содержимое нашего выходного буфера в [.filename]#stdout# в последний раз, иначе часть нашего вывода попадёт в буфер, но так и не будет отправлена. Не забудьте об этом, иначе будете недоумевать, куда пропала часть вывода.
+
+[.programlisting]
+....
+%include 'system.inc'
+
+%define BUFSIZE 2048
+
+section .data
+hex db '0123456789ABCDEF'
+
+section .bss
+ibuffer resb BUFSIZE
+obuffer resb BUFSIZE
+
+section .text
+global _start
+_start:
+ sub eax, eax
+ sub ebx, ebx
+ sub ecx, ecx
+ mov edi, obuffer
+
+.loop:
+ ; read a byte from stdin
+ call getchar
+
+ ; convert it to hex
+ mov dl, al
+ shr al, 4
+ mov al, [hex+eax]
+ call putchar
+
+ mov al, dl
+ and al, 0Fh
+ mov al, [hex+eax]
+ call putchar
+
+ mov al, ' '
+ cmp dl, 0Ah
+ jne .put
+ mov al, dl
+
+.put:
+ call putchar
+ jmp short .loop
+
+align 4
+getchar:
+ or ebx, ebx
+ jne .fetch
+
+ call read
+
+.fetch:
+ lodsb
+ dec ebx
+ ret
+
+read:
+ push dword BUFSIZE
+ mov esi, ibuffer
+ push esi
+ push dword stdin
+ sys.read
+ add esp, byte 12
+ mov ebx, eax
+ or eax, eax
+ je .done
+ sub eax, eax
+ ret
+
+align 4
+.done:
+ call write ; flush output buffer
+ push dword 0
+ sys.exit
+
+align 4
+putchar:
+ stosb
+ inc ecx
+ cmp ecx, BUFSIZE
+ je write
+ ret
+
+align 4
+write:
+ sub edi, ecx ; start of buffer
+ push ecx
+ push edi
+ push dword stdout
+ sys.write
+ add esp, byte 12
+ sub eax, eax
+ sub ecx, ecx ; buffer is empty now
+ ret
+....
+
+Теперь у нас есть третий раздел в исходном коде с именем `.bss`. Этот раздел не включается в исполняемый файл и, следовательно, не может быть инициализирован. Мы используем `resb` вместо `db`. Это просто резервирует запрошенный размер неинициализированной памяти для нашего использования.
+
+Мы используем тот факт, что система не изменяет регистры: мы используем регистры для того, что в противном случае пришлось бы хранить в глобальных переменных в секции `.data`. Именно поэтому соглашение UNIX(R) о передаче параметров системных вызовов через стек превосходит соглашение Microsoft о передаче их в регистрах: мы можем оставить регистры для собственного использования.
+
+Мы используем `EDI` и `ESI` как указатели на следующий байт для чтения или записи. Мы используем `EBX` и `ECX` для отслеживания количества байтов в двух буферах, чтобы знать, когда нужно вывести данные в систему или считать новые данные из системы.
+
+Давайте посмотрим, как это работает сейчас:
+
+[source, shell]
+....
+% nasm -f elf hex.asm
+% ld -s -o hex hex.o
+% ./hex
+Hello, World!
+Here I come!
+48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A
+48 65 72 65 20 49 20 63 6F 6D 65 21 0A
+^D %
+....
+
+Не то, что вы ожидали? Программа не выводила результат, пока мы не нажали `^D`. Это легко исправить, добавив три строки кода для вывода результата каждый раз, когда мы преобразуем новую строку в `0A`. Я пометил эти три строки символом > (не копируйте > в ваш [.filename]#hex.asm#).
+
+[.programlisting]
+....
+%include 'system.inc'
+
+%define BUFSIZE 2048
+
+section .data
+hex db '0123456789ABCDEF'
+
+section .bss
+ibuffer resb BUFSIZE
+obuffer resb BUFSIZE
+
+section .text
+global _start
+_start:
+ sub eax, eax
+ sub ebx, ebx
+ sub ecx, ecx
+ mov edi, obuffer
+
+.loop:
+ ; read a byte from stdin
+ call getchar
+
+ ; convert it to hex
+ mov dl, al
+ shr al, 4
+ mov al, [hex+eax]
+ call putchar
+
+ mov al, dl
+ and al, 0Fh
+ mov al, [hex+eax]
+ call putchar
+
+ mov al, ' '
+ cmp dl, 0Ah
+ jne .put
+ mov al, dl
+
+.put:
+ call putchar
+> cmp al, 0Ah
+> jne .loop
+> call write
+ jmp short .loop
+
+align 4
+getchar:
+ or ebx, ebx
+ jne .fetch
+
+ call read
+
+.fetch:
+ lodsb
+ dec ebx
+ ret
+
+read:
+ push dword BUFSIZE
+ mov esi, ibuffer
+ push esi
+ push dword stdin
+ sys.read
+ add esp, byte 12
+ mov ebx, eax
+ or eax, eax
+ je .done
+ sub eax, eax
+ ret
+
+align 4
+.done:
+ call write ; flush output buffer
+ push dword 0
+ sys.exit
+
+align 4
+putchar:
+ stosb
+ inc ecx
+ cmp ecx, BUFSIZE
+ je write
+ ret
+
+align 4
+write:
+ sub edi, ecx ; start of buffer
+ push ecx
+ push edi
+ push dword stdout
+ sys.write
+ add esp, byte 12
+ sub eax, eax
+ sub ecx, ecx ; buffer is empty now
+ ret
+....
+
+Теперь давайте посмотрим, как это работает:
+
+[source, shell]
+....
+% nasm -f elf hex.asm
+% ld -s -o hex hex.o
+% ./hex
+Hello, World!
+48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A
+Here I come!
+48 65 72 65 20 49 20 63 6F 6D 65 21 0A
+^D %
+....
+
+Неплохо для исполняемого файла размером 644 байта, не так ли!
+
+[NOTE]
+====
+Такой подход к буферизированному вводу/выводу всё ещё содержит скрытую опасность. Я расскажу об этом и исправлю её позже, когда речь пойдёт о crossref:x86[x86-buffered-dark-side,тёмной стороне буферизации].
+====
+
+[[x86-ungetc]]
+=== Как отменить чтение символа
+
+[WARNING]
+====
+Это может быть несколько сложной темой, в основном представляющей интерес для программистов, знакомых с теорией компиляторов. Если хотите, вы можете crossref:x86[x86-command-line, перейти к следующему разделу], и, возможно, прочитаете это позже.
+====
+
+Хотя наш пример программы не требует этого, более сложные фильтры часто нуждаются в предварительном просмотре. Другими словами, им может потребоваться узнать, какой следующий символ (или даже несколько символов). Если следующий символ имеет определённое значение, он является частью текущего обрабатываемого токена. В противном случае — нет.
+
+Например, вы можете анализировать входной поток на наличие текстовой строки (например, при реализации компилятора языка): если символ следует за другим символом или, возможно, цифрой, он является частью обрабатываемой лексемы. Если за ним следует пробел или другое значение, то он не является частью текущей лексемы.
+
+Это представляет интересную проблему: как вернуть следующий символ обратно во входной поток, чтобы его можно было прочитать позже?
+
+Одно из возможных решений — сохранить его в символьной переменной, а затем установить флаг. Мы можем изменить `getchar`, чтобы он проверял флаг, и если он установлен, извлекал байт из этой переменной вместо буфера ввода, а затем сбрасывал флаг. Но, конечно, это замедляет работу.
+
+В языке C есть функция `ungetc()`, как раз для этой цели. Есть ли быстрый способ реализовать её в нашем коде? Я хочу, чтобы вы пролистали назад и взглянули на процедуру `getchar`, и попробовали найти красивое и быстрое решение, прежде чем читать следующий абзац. Затем вернитесь сюда и посмотрите моё собственное решение.
+
+Ключом к возвращению символа обратно в поток является то, как мы получаем символы изначально:
+
+Сначала проверяем, пуст ли буфер, проверяя значение `EBX`. Если оно равно нулю, вызываем процедуру `read`.
+
+Если у нас есть доступный символ, мы используем `lodsb`, затем уменьшаем значение `EBX`. Инструкция `lodsb` фактически идентична:
+
+[.programlisting]
+....
+mov al, [esi]
+ inc esi
+....
+
+Байт, который мы извлекли, остается в буфере до следующего вызова `read`. Мы не знаем, когда это произойдет, но знаем, что этого не случится до следующего вызова `getchar`. Следовательно, чтобы "вернуть" последний прочитанный байт обратно в поток, нам достаточно уменьшить значение `ESI` и увеличить значение `EBX`:
+
+[.programlisting]
+....
+ungetc:
+ dec esi
+ inc ebx
+ ret
+....
+
+Но будьте осторожны! Мы в полной безопасности, если заглядываем вперед только на один символ за раз. Если же мы проверяем несколько следующих символов и вызываем `ungetc` несколько раз подряд, это будет работать в большинстве случаев, но не всегда (и ошибки будет сложно отладить). Почему?
+
+Потому что пока `getchar` не вызывает `read`, все предварительно прочитанные байты остаются в буфере, и наш `ungetc` работает без сбоев. Но как только `getchar` вызывает `read`, содержимое буфера изменяется.
+
+Мы всегда можем рассчитывать на корректную работу `ungetc` с последним символом, прочитанным через `getchar`, но не с любым символом, прочитанным до этого.
+
+Если ваша программа читает более одного байта вперед, у вас есть как минимум два варианта:
+
+Если возможно, измените программу так, чтобы она читала только один байт вперед. Это самое простое решение.
+
+Если эта опция недоступна, сначала определите максимальное количество символов, которое вашей программе может потребоваться вернуть во входной поток за один раз. Увеличьте это число немного, чтобы быть уверенным, предпочтительно до кратного 16 — так оно будет лучше выровнено. Затем измените секцию `.bss` в вашем коде и создайте небольшой "запасной" буфер прямо перед вашим входным буфером, примерно так:
+
+[.programlisting]
+....
+section .bss
+ resb 16 ; or whatever the value you came up with
+ibuffer resb BUFSIZE
+obuffer resb BUFSIZE
+....
+
+Вам также необходимо изменить ваш `ungetc`, чтобы передать значение байта для возврата в `AL`:
+
+[.programlisting]
+....
+ungetc:
+ dec esi
+ inc ebx
+ mov [esi], al
+ ret
+....
+
+С этим изменением вы можете безопасно вызывать `ungetc` до 17 раз подряд (первый вызов всё ещё будет в пределах буфера, остальные 16 могут быть либо в пределах буфера, либо в пределах "запасного" пространства).
+
+[[x86-command-line]]
+== Аргументы командной строки
+
+Наша программа hex будет полезнее, если она сможет читать имена входного и выходного файлов из командной строки, т.е. если она сможет обрабатывать аргументы командной строки. Но... Где они?
+
+Прежде чем UNIX(R) система запустит программу, она делает ``push`` для некоторых данных, помещая их в стек, затем переходит к метке `_start` программы. Да, я сказал "переходит", а не "вызывает". Это означает, что данные можно прочитать с помощью `[esp+offset]` или просто сделать ``pop`` для них.
+
+Значение на вершине стека содержит количество аргументов командной строки. Оно традиционно называется `argc`, что означает "argument count".
+
+Далее следуют аргументы командной строки, все `argc` штук. Обычно их называют `argv`, что означает "значение(я) аргумента". То есть мы получаем `argv[0]`, `argv[1]`, `...`, `argv[argc-1]`. Это не сами аргументы, а указатели на аргументы, то есть адреса памяти, где находятся реальные аргументы. Сами аргументы представляют собой строки символов, завершающиеся нулевым символом ('\0').
+
+Список `argv` завершается указателем NULL, который представляет собой просто `0`. Есть и другие детали, но пока этого достаточно для наших целей.
+
+[NOTE]
+====
+Если вы перешли из среды программирования MS-DOS(R), основное различие заключается в том, что каждый аргумент находится в отдельной строке. Второе различие состоит в том, что нет практического ограничения на количество аргументов.
+====
+
+Вооружившись этими знаниями, мы почти готовы к следующей версии [.filename]#hex.asm#. Однако сначала нам нужно добавить несколько строк в [.filename]#system.inc#:
+
+Сначала нам нужно добавить две новые записи в наш список номеров системных вызовов:
+
+[.programlisting]
+....
+%define SYS_open 5
+%define SYS_close 6
+....
+
+Затем мы добавляем два новых макроса в конце файла:
+
+[.programlisting]
+....
+%macro sys.open 0
+ system SYS_open
+%endmacro
+
+%macro sys.close 0
+ system SYS_close
+%endmacro
+....
+
+Вот наш измененный исходный код:
+
+[.programlisting]
+....
+%include 'system.inc'
+
+%define BUFSIZE 2048
+
+section .data
+fd.in dd stdin
+fd.out dd stdout
+hex db '0123456789ABCDEF'
+
+section .bss
+ibuffer resb BUFSIZE
+obuffer resb BUFSIZE
+
+section .text
+align 4
+err:
+ push dword 1 ; return failure
+ sys.exit
+
+align 4
+global _start
+_start:
+ add esp, byte 8 ; discard argc and argv[0]
+
+ pop ecx
+ jecxz .init ; no more arguments
+
+ ; ECX contains the path to input file
+ push dword 0 ; O_RDONLY
+ push ecx
+ sys.open
+ jc err ; open failed
+
+ add esp, byte 8
+ mov [fd.in], eax
+
+ pop ecx
+ jecxz .init ; no more arguments
+
+ ; ECX contains the path to output file
+ push dword 420 ; file mode (644 octal)
+ push dword 0200h | 0400h | 01h
+ ; O_CREAT | O_TRUNC | O_WRONLY
+ push ecx
+ sys.open
+ jc err
+
+ add esp, byte 12
+ mov [fd.out], eax
+
+.init:
+ sub eax, eax
+ sub ebx, ebx
+ sub ecx, ecx
+ mov edi, obuffer
+
+.loop:
+ ; read a byte from input file or stdin
+ call getchar
+
+ ; convert it to hex
+ mov dl, al
+ shr al, 4
+ mov al, [hex+eax]
+ call putchar
+
+ mov al, dl
+ and al, 0Fh
+ mov al, [hex+eax]
+ call putchar
+
+ mov al, ' '
+ cmp dl, 0Ah
+ jne .put
+ mov al, dl
+
+.put:
+ call putchar
+ cmp al, dl
+ jne .loop
+ call write
+ jmp short .loop
+
+align 4
+getchar:
+ or ebx, ebx
+ jne .fetch
+
+ call read
+
+.fetch:
+ lodsb
+ dec ebx
+ ret
+
+read:
+ push dword BUFSIZE
+ mov esi, ibuffer
+ push esi
+ push dword [fd.in]
+ sys.read
+ add esp, byte 12
+ mov ebx, eax
+ or eax, eax
+ je .done
+ sub eax, eax
+ ret
+
+align 4
+.done:
+ call write ; flush output buffer
+
+ ; close files
+ push dword [fd.in]
+ sys.close
+
+ push dword [fd.out]
+ sys.close
+
+ ; return success
+ push dword 0
+ sys.exit
+
+align 4
+putchar:
+ stosb
+ inc ecx
+ cmp ecx, BUFSIZE
+ je write
+ ret
+
+align 4
+write:
+ sub edi, ecx ; start of buffer
+ push ecx
+ push edi
+ push dword [fd.out]
+ sys.write
+ add esp, byte 12
+ sub eax, eax
+ sub ecx, ecx ; buffer is empty now
+ ret
+....
+
+В нашем разделе `.data` теперь есть две новые переменные, `fd.in` и `fd.out`. Здесь мы сохраняем дескрипторы файлов для ввода и вывода.
+
+В разделе `.text` мы заменили ссылки с `stdin` и `stdout` на `[fd.in]` и `[fd.out]`.
+
+Раздел `.text` теперь начинается с простого обработчика ошибок, который просто завершает программу с кодом возврата `1`. Обработчик ошибок расположен перед `_start`, чтобы находиться вблизи от места возникновения ошибок.
+
+Естественно, выполнение программы по-прежнему начинается с `_start`. Сначала мы удаляем `argc` и `argv[0]` из стека: они не представляют для нас интереса (по крайней мере, в этой программе).
+
+Мы помещаем `argv[1]` в `ECX`. Этот регистр особенно подходит для указателей, так как мы можем обрабатывать NULL-указатели с помощью `jecxz`. Если `argv[1]` не равен NULL, мы пытаемся открыть файл с именем, указанным в первом аргументе. В противном случае продолжаем программу как раньше: чтение из `stdin`, запись в `stdout`. Если нам не удаётся открыть входной файл (например, он не существует), мы переходим к обработчику ошибок и завершаем работу.
+
+Если всё прошло успешно, мы проверяем второй аргумент. Если он присутствует, мы открываем выходной файл. В противном случае, мы отправляем вывод в `stdout`. Если нам не удаётся открыть выходной файл (например, он существует и у нас нет прав на запись), мы снова переходим к обработчику ошибок.
+
+Остальная часть кода остается прежней, за исключением того, что мы закрываем входной и выходной файлы перед завершением, и, как упоминалось, используем `[fd.in]` и `[fd.out]`.
+
+Наш исполняемый файл теперь имеет внушительный размер в 768 байт.
+
+Можем ли мы улучшить его еще? Конечно! Каждую программу можно улучшить. Вот несколько идей, что мы могли бы сделать:
+
+* Сделать наш обработчик ошибок, выводящий сообщение в `stderr`.
+* Добавить обработчики ошибок в функции `read` и `write`.
+* Закрывать `stdin` при открытии входного файла, `stdout` при открытии выходного файла.
+* Добавить параметры командной строки, такие как `-i` и `-o`, чтобы можно было перечислять входные и выходные файлы в любом порядке или, возможно, читать из `stdin` и записывать в файл.
+* Выводить сообщение с подсказкой об использовании программы, если аргументы командной строки указаны неверно.
+
+Я оставлю эти улучшения в качестве упражнения для читателя: вы уже знаете всё необходимое для их реализации.
+
+[[x86-environment]]
+== Окружение UNIX(R)
+
+Важным концептом UNIX(R) является окружение, которое определяется _переменными окружения_. Некоторые из них устанавливаются системой, другие — пользователем, третьи — оболочкой или любой программой, которая загружает другую программу.
+
+[[x86-find-environment]]
+=== Как найти переменные окружения
+
+Я говорил ранее, что когда программа начинает выполняться, в стеке находятся `argc`, за которым следует массив `argv`, завершающийся NULL, а затем что-то ещё. Это "что-то ещё" — это _окружение_, или, если быть точнее, массив указателей на _переменные окружения_, завершающийся NULL. Это часто называют `env`.
+
+Структура `env` такая же, как у `argv` — список адресов памяти, заканчивающийся NULL (`0`). В данном случае нет `"envc"` — конец массива определяется поиском последнего NULL.
+
+Переменные обычно имеют формат `name=value`, но иногда часть `=value` может отсутствовать. Необходимо учитывать эту вероятность.
+
+[[x86-webvar]]
+=== webvars
+
+Я мог бы просто показать вам код, который выводит окружение так же, как команда UNIX(R) env. Но я подумал, что будет интереснее написать простую CGI-утилиту на ассемблере.
+
+[[x86-cgi]]
+==== CGI: краткий обзор
+
+У меня есть http://www.whizkidtech.redprince.net/cgi-bin/tutorial[подробное руководство по CGI] на моем веб-сайте, но вот очень краткий обзор CGI:
+
+* Веб-сервер взаимодействует с CGI-программой, устанавливая _переменные окружения_.
+* Программа CGI отправляет свой вывод в [.filename]#stdout#. Веб-сервер считывает его оттуда.
+* Он должен начинаться с HTTP-заголовка, за которым следуют две пустые строки.
+* Затем он выводит HTML-код или любые другие данные, которые он генерирует.
+
+[NOTE]
+====
+В то время как некоторые _переменные окружения_ используют стандартные имена, другие различаются в зависимости от веб-сервера. Это делает программу webvars весьма полезным инструментом для диагностики.
+====
+
+[[x86-webvars-the-code]]
+==== Код
+
+Наша программа webvars, таким образом, должна отправить HTTP-заголовок, за которым следует HTML-разметка. Затем она должна прочитать _переменные окружения_ одну за другой и отправить их как часть HTML-страницы.
+
+Код приведен ниже. Я разместил комментарии и пояснения прямо в коде:
+
+[.programlisting]
+....
+;;;;;;; webvars.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (c) 2000 G. Adam Stanislav
+; All rights reserved.
+;
+; Redistribution and use in source and binary forms, with or without
+; modification, are permitted provided that the following conditions
+; are met:
+; 1. Redistributions of source code must retain the above copyright
+; notice, this list of conditions and the following disclaimer.
+; 2. Redistributions in binary form must reproduce the above copyright
+; notice, this list of conditions and the following disclaimer in the
+; documentation and/or other materials provided with the distribution.
+;
+; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+; SUCH DAMAGE.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Version 1.0
+;
+; Started: 8-Dec-2000
+; Updated: 8-Dec-2000
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+%include 'system.inc'
+
+section .data
+http db 'Content-type: text/html', 0Ah, 0Ah
+ db '<?xml version="1.0" encoding="utf-8"?>', 0Ah
+ db '<!DOCTYPE html PUBLIC "-//W3C/DTD XHTML Strict//EN" '
+ db '"DTD/xhtml1-strict.dtd">', 0Ah
+ db '<html xmlns="http://www.w3.org/1999/xhtml" '
+ db 'xml.lang="en" lang="en">', 0Ah
+ db '<head>', 0Ah
+ db '<title>Web Environment</title>', 0Ah
+ db '<meta name="author" content="G. Adam Stanislav" />', 0Ah
+ db '</head>', 0Ah, 0Ah
+ db '<body bgcolor="#ffffff" text="#000000" link="#0000ff" '
+ db 'vlink="#840084" alink="#0000ff">', 0Ah
+ db '<div class="webvars">', 0Ah
+ db '<h1>Web Environment</h1>', 0Ah
+ db '<p>The following <b>environment variables</b> are defined '
+ db 'on this web server:</p>', 0Ah, 0Ah
+ db '<table align="center" width="80" border="0" cellpadding="10" '
+ db 'cellspacing="0" class="webvars">', 0Ah
+httplen equ $-http
+left db '<tr>', 0Ah
+ db '<td class="name"><tt>'
+leftlen equ $-left
+middle db '</tt></td>', 0Ah
+ db '<td class="value"><tt><b>'
+midlen equ $-middle
+undef db '<i>(undefined)</i>'
+undeflen equ $-undef
+right db '</b></tt></td>', 0Ah
+ db '</tr>', 0Ah
+rightlen equ $-right
+wrap db '</table>', 0Ah
+ db '</div>', 0Ah
+ db '</body>', 0Ah
+ db '</html>', 0Ah, 0Ah
+wraplen equ $-wrap
+
+section .text
+global _start
+_start:
+ ; First, send out all the http and xhtml stuff that is
+ ; needed before we start showing the environment
+ push dword httplen
+ push dword http
+ push dword stdout
+ sys.write
+
+ ; Now find how far on the stack the environment pointers
+ ; are. We have 12 bytes we have pushed before "argc"
+ mov eax, [esp+12]
+
+ ; We need to remove the following from the stack:
+ ;
+ ; The 12 bytes we pushed for sys.write
+ ; The 4 bytes of argc
+ ; The EAX*4 bytes of argv
+ ; The 4 bytes of the NULL after argv
+ ;
+ ; Total:
+ ; 20 + eax * 4
+ ;
+ ; Because stack grows down, we need to ADD that many bytes
+ ; to ESP.
+ lea esp, [esp+20+eax*4]
+ cld ; This should already be the case, but let's be sure.
+
+ ; Loop through the environment, printing it out
+.loop:
+ pop edi
+ or edi, edi ; Done yet?
+ je near .wrap
+
+ ; Print the left part of HTML
+ push dword leftlen
+ push dword left
+ push dword stdout
+ sys.write
+
+ ; It may be tempting to search for the '=' in the env string next.
+ ; But it is possible there is no '=', so we search for the
+ ; terminating NUL first.
+ mov esi, edi ; Save start of string
+ sub ecx, ecx
+ not ecx ; ECX = FFFFFFFF
+ sub eax, eax
+repne scasb
+ not ecx ; ECX = string length + 1
+ mov ebx, ecx ; Save it in EBX
+
+ ; Now is the time to find '='
+ mov edi, esi ; Start of string
+ mov al, '='
+repne scasb
+ not ecx
+ add ecx, ebx ; Length of name
+
+ push ecx
+ push esi
+ push dword stdout
+ sys.write
+
+ ; Print the middle part of HTML table code
+ push dword midlen
+ push dword middle
+ push dword stdout
+ sys.write
+
+ ; Find the length of the value
+ not ecx
+ lea ebx, [ebx+ecx-1]
+
+ ; Print "undefined" if 0
+ or ebx, ebx
+ jne .value
+
+ mov ebx, undeflen
+ mov edi, undef
+
+.value:
+ push ebx
+ push edi
+ push dword stdout
+ sys.write
+
+ ; Print the right part of the table row
+ push dword rightlen
+ push dword right
+ push dword stdout
+ sys.write
+
+ ; Get rid of the 60 bytes we have pushed
+ add esp, byte 60
+
+ ; Get the next variable
+ jmp .loop
+
+.wrap:
+ ; Print the rest of HTML
+ push dword wraplen
+ push dword wrap
+ push dword stdout
+ sys.write
+
+ ; Return success
+ push dword 0
+ sys.exit
+....
+
+Этот код создает исполняемый файл размером 1 396 байт. Большая его часть — это данные, а именно HTML-разметка, которую нам нужно отправить.
+
+Запустите ассемблер и слинкуйте как обычно:
+
+[source, shell]
+....
+% nasm -f elf webvars.asm
+% ld -s -o webvars webvars.o
+....
+
+Для использования необходимо загрузить [.filename]#webvars# на ваш веб-сервер. В зависимости от настроек веб-сервера, возможно, потребуется разместить его в специальном каталоге [.filename]#cgi-bin# или переименовать с расширением [.filename]#.cgi#.
+
+Затем вам нужно использовать браузер для просмотра вывода. Чтобы увидеть вывод на моем веб-сервере, перейдите по ссылке http://www.int80h.org/webvars/[http://www.int80h.org/webvars/]. Если вам интересно узнать о дополнительных переменных окружения в защищенном паролем веб-каталоге, перейдите по адресу http://www.int80h.org/private/[http://www.int80h.org/private/], используя имя `asm` и пароль `programmer`.
+
+[[x86-files]]
+== Работа с файлами
+
+Мы уже выполнили некоторые базовые операции с файлами: мы знаем, как их открывать и закрывать, как читать и записывать их с использованием буферов. Однако UNIX(R) предлагает гораздо больше возможностей при работе с файлами. В этом разделе мы рассмотрим некоторые из них и в итоге создадим удобную утилиту для преобразования файлов.
+
+В самом деле, начнем с конца, то есть с утилиты преобразования файлов. Всегда легче программировать, когда с самого начала известно, каким должен быть конечный продукт.
+
+Одной из первых программ, которые я написал для UNIX(R), была link:ftp://ftp.int80h.org/unix/tuc/[tuc] — конвертер текста в файл UNIX(R). Она преобразует текстовый файл из других операционных систем в текстовый файл UNIX(R). Другими словами, она изменяет различные виды окончаний строк на стандартные для UNIX(R). Результат сохраняется в другом файле. По желанию, она может преобразовать текстовый файл UNIX(R) в текстовый файл DOS.
+
+Я широко использовал `tuc`, но всегда только для преобразования из какой-либо другой ОС в UNIX(R), никогда наоборот. Мне всегда хотелось, чтобы он просто перезаписывал файл, вместо того чтобы мне приходилось отправлять вывод в другой файл. В большинстве случаев я в итоге использую его так:
+
+[source, shell]
+....
+% tuc myfile tempfile
+% mv tempfile myfile
+....
+
+Было бы здорово иметь ftuc, т.е., _быстрый tuc_, и использовать его вот так:
+
+[source, shell]
+....
+% ftuc myfile
+....
+
+В этой главе мы напишем ftuc на языке ассемблера (оригинальный tuc написан на C) и в процессе изучим различные файловые сервисы ядра.
+
+На первый взгляд, такое преобразование файла кажется очень простым: нужно всего лишь удалить символы возврата каретки, верно?
+
+Если вы ответили «да», подумайте ещё раз: такой подход будет работать в большинстве случаев (по крайней мере, с текстовыми файлами MS DOS), но иногда он будет давать сбой.
+
+Проблема в том, что не все текстовые файлы, не относящиеся к UNIX(R), завершают строки последовательностью возврата каретки / перевода строки. Некоторые используют возврат каретки без перевода строки. Другие объединяют несколько пустых строк в один возврат каретки, за которым следует несколько переводов строки. И так далее.
+
+Конвертер текстовых файлов, следовательно, должен уметь обрабатывать любые возможные окончания строк:
+
+* возврат каретки (carriage return) / перевод строки (line feed)
+* возврат каретки
+* перевод строки / возврат каретки
+* перевод строки
+
+Это также должно обрабатывать файлы, использующие комбинации вышеуказанного (например, возврат каретки с последующими несколькими переводами строки).
+
+[[x86-finite-state-machine]]
+=== Конечный автомат
+
+Проблема легко решается с использованием техники, называемой _конечный автомат_, изначально разработанной создателями цифровых электронных схем. _Конечный автомат_ — это цифровая схема, выход которой зависит не только от входа, но и от предыдущего входа, то есть от её состояния. Микропроцессор является примером _конечного автомата_: наш код на языке ассемблера транслируется в машинный язык, где одни инструкции ассемблера превращаются в один байт машинного кода, а другие — в несколько байтов. Когда микропроцессор извлекает байты из памяти один за другим, некоторые из них просто изменяют его состояние, а не производят какой-либо выходной сигнал. После извлечения всех байтов кода операции микропроцессор выдает выходной сигнал, изменяет значение регистра и т. д.
+
+Из-за этого всё программное обеспечение по сути представляет собой последовательность инструкций состояния для микропроцессора. Тем не менее, концепция _конечного автомата_ также полезна при проектировании программного обеспечения.
+
+Наш конвертер текстовых файлов можно представить в виде _конечного автомата_ с тремя возможными состояниями. Мы могли бы назвать их состояниями 0-2, но будет проще, если дадим им символические имена:
+
+* ordinary
+* cr
+* lf
+
+Наша программа начнёт работу в обычном состоянии. В этом состоянии действие программы зависит от её входных данных следующим образом:
+
+* Если ввод представляет собой что-либо, кроме возврата каретки или перевода строки, ввод просто передаётся на вывод. Состояние остаётся неизменным.
+* Если входной символ — возврат каретки, состояние изменяется на cr. Затем входной символ отбрасывается, т.е. вывод не производится.
+* Если входной символ является переводом строки, состояние изменяется на lf. Затем входной символ отбрасывается.
+
+Всякий раз, когда мы находимся в состоянии `cr`, это означает, что последним вводом был символ возврата каретки, который не был обработан. Действия нашего программного обеспечения в этом состоянии снова зависят от текущего ввода:
+
+* Если ввод отличается от возврата каретки или перевода строки, вывести перевод строки, затем вывести ввод, а затем изменить состояние на обычное.
+* Если входной символ — возврат каретки, значит, мы получили два (или более) возврата каретки подряд. Мы отбрасываем ввод, выводим перевод строки и оставляем состояние неизменным.
+* Если входной символ — это перевод строки, мы выводим перевод строки и меняем состояние на обычное. Обратите внимание, что это не то же самое, что в первом случае выше — если бы мы попытались объединить их, мы бы выводили два перевода строки вместо одного.
+
+Наконец, мы находимся в состоянии `lf` после получения перевода строки, которому не предшествовал возврат каретки. Это произойдет, если наш файл уже в формате UNIX(R), или когда несколько строк подряд выражены одним возвратом каретки, за которым следуют несколько переводов строк, или когда строка заканчивается последовательностью перевода строки / возврата каретки. Вот как нам нужно обрабатывать ввод в этом состоянии:
+
+* Если ввод отличается от возврата каретки или перевода строки, мы выводим перевод строки, затем выводим ввод и изменяем состояние на обычное. Это действие полностью совпадает с действием в состоянии `cr` при получении аналогичного ввода.
+* Если ввод представляет собой символ возврата каретки, мы отбрасываем ввод, выводим символ перевода строки, затем изменяем состояние на обычное.
+* Если входной символ — перевод строки, мы выводим перевод строки и оставляем состояние неизменным.
+
+[[x86-final-state]]
+==== Конечное состояние
+
+Приведённый выше _конечный автомат_ работает для всего файла, но оставляет возможность, что последний конец строки будет проигнорирован. Это произойдёт, если файл заканчивается одиночным возвратом каретки или одиночным переводом строки. Я не подумал об этом, когда писал tuc, и лишь позже обнаружил, что иногда он удаляет последний конец строки.
+
+Эта проблема легко решается проверкой состояния после обработки всего файла. Если состояние не является обычным, нам просто нужно вывести последний перевод строки.
+
+[NOTE]
+====
+Теперь, когда мы выразили наш алгоритм в виде _конечного автомата_, мы могли бы легко разработать специализированную цифровую электронную схему («чип») для выполнения преобразования. Конечно, это было бы значительно дороже, чем написание программы на языке ассемблера.
+====
+
+[[x86-tuc-counter]]
+==== Счетчик вывода
+
+Поскольку наша программа преобразования файлов может объединять два символа в один, нам необходимо использовать счётчик вывода. Мы инициализируем его значением `0` и увеличиваем каждый раз, когда отправляем символ на выход. В конце программы счётчик укажет, какой размер необходимо установить для файла.
+
+[[x86-software-fsm]]
+=== Реализация конечного автомата в программном обеспечении
+
+Самая сложная часть работы с _конечным автоматом_ — это анализ задачи и её представление в виде _конечного автомата_. После этого программное обеспечение практически пишется само.
+
+На языке высокого уровня, таком как C, существует несколько основных подходов. Один из них — использование оператора `switch`, который выбирает, какую функцию следует выполнить. Например,
+
+[.programlisting]
+....
+switch (state) {
+ default:
+ case REGULAR:
+ regular(inputchar);
+ break;
+ case CR:
+ cr(inputchar);
+ break;
+ case LF:
+ lf(inputchar);
+ break;
+ }
+....
+
+Еще один подход заключается в использовании массива указателей на функции, например:
+
+[.programlisting]
+....
+(output[state])(inputchar);
+....
+
+Еще один вариант — сделать `state` указателем на функцию, установив его на соответствующую функцию:
+
+[.programlisting]
+....
+(*state)(inputchar);
+....
+
+Это подход, который мы будем использовать в нашей программе, потому что его очень легко реализовать на языке ассемблера, и он также очень быстрый. Мы просто будем хранить адрес нужной процедуры в `EBX`, а затем выполним:
+
+[.programlisting]
+....
+call ebx
+....
+
+Это возможно быстрее, чем жестко задавать адрес в коде, потому что микропроцессору не нужно извлекать адрес из памяти — он уже хранится в одном из его регистров. Я сказал _возможно_, потому что с учетом кэширования, которое выполняют современные микропроцессоры, оба варианта могут быть одинаково быстрыми.
+
+[[memory-mapped-files]]
+=== Отображенные в память файлы
+
+Поскольку наша программа работает с одним файлом, мы не можем использовать подход, который работал ранее, то есть чтение из входного файла и запись в выходной файл.
+
+UNIX(R) позволяет нам отображать файл или его часть в память. Для этого сначала необходимо открыть файл с соответствующими флагами чтения/записи. Затем мы используем системный вызов `mmap`, чтобы отобразить его в память. Одно из преимуществ `mmap` заключается в том, что он автоматически работает с виртуальной памятью: мы можем отобразить в память больше файла, чем имеется физической памяти, и при этом обращаться к нему с помощью обычных команд работы с памятью, таких как `mov`, `lods` и `stos`. Все изменения, внесённые в память, отображённую из файла, будут записаны в файл системой. Нам даже не нужно держать файл открытым: пока он остаётся отображённым, мы можем читать из него и записывать в него.
+
+32-разрядные микропроцессоры Intel могут адресовать до четырёх гигабайт памяти — физической или виртуальной. Система FreeBSD позволяет использовать до половины этого объёма для отображения файлов.
+
+Для упрощения в этом руководстве мы будем преобразовывать только файлы, которые могут быть полностью отображены в памяти. Вероятно, не так много текстовых файлов превышают размер в два гигабайта. Если наша программа встретит такой файл, она просто выведет сообщение с предложением использовать оригинальный tuc.
+
+Если вы изучите свою копию файла [.filename]#syscalls.master#, вы найдёте два отдельных системных вызова с именем `mmap`. Это связано с эволюцией UNIX(R): существовал традиционный BSD `mmap`, системный вызов 71. Он был заменён на POSIX(R) `mmap`, системный вызов 197. Система FreeBSD поддерживает оба, поскольку старые программы были написаны с использованием оригинальной BSD-версии. Но новое программное обеспечение использует версию POSIX(R), которую мы и будем применять.
+
+В [.filename]#syscalls.master# POSIX(R) версия указана следующим образом:
+
+[.programlisting]
+....
+197 STD BSD { caddr_t mmap(caddr_t addr, size_t len, int prot, \
+ int flags, int fd, long pad, off_t pos); }
+....
+
+Это немного отличается от того, что указано в man:mmap[2]. Это связано с тем, что man:mmap[2] описывает версию на языке C.
+
+Разница заключается в аргументе `long pad`, который отсутствует в версии на C. Однако системные вызовы FreeBSD добавляют 32-битный заполнитель после ``push`` 64-битного аргумента. В данном случае `off_t` является 64-битным значением.
+
+Когда мы завершаем работу с файлом, отображённым в память, мы освобождаем его с помощью системного вызова `munmap`:
+
+[TIP]
+====
+Для подробного изучения `mmap` см. http://www.int80h.org/cgi-bin/isbn?isbn=0130810819[Unix Network Programming, Volume 2, Chapter 12] У. Ричарда Стивенса.
+====
+
+[[x86-file-size]]
+=== Определение размера файла
+
+Поскольку нам нужно указать `mmap`, сколько байт файла отобразить в памяти, и поскольку мы хотим отобразить весь файл, нам необходимо определить его размер.
+
+Мы можем использовать системный вызов `fstat` для получения всей информации об открытом файле, которую система может нам предоставить. Это включает в себя размер файла.
+
+Вновь, в [.filename]#syscalls.master# указаны две версии `fstat`: традиционная (системный вызов 62) и POSIX(R) (системный вызов 189). Естественно, мы будем использовать версию POSIX(R):
+
+[.programlisting]
+....
+189 STD POSIX { int fstat(int fd, struct stat *sb); }
+....
+
+Это очень простой вызов: мы передаем ему адрес структуры `stat` и дескриптор открытого файла. Он заполнит содержимое структуры `stat`.
+
+Однако должен сказать, что я пытался объявить структуру `stat` в секции `.bss`, и `fstat` это не понравилось: был установлен флаг переноса, указывающий на ошибку. После того как я изменил код, чтобы разместить структуру в стеке, всё заработало как надо.
+
+[[x86-ftruncate]]
+=== Изменение размера файла
+
+Поскольку наша программа может объединять последовательности возврата каретки / перевода строки в простые переводы строк, наш вывод может быть меньше, чем ввод. Однако, так как мы помещаем вывод в тот же файл, из которого читаем ввод, нам может потребоваться изменить размер файла.
+
+Системный вызов `ftruncate` позволяет нам сделать именно это. Несмотря на название , несколько вводящее в звблуждение, системный вызов `ftruncate` может использоваться как для усечения файла (уменьшения его размера), так и для его увеличения.
+
+И да, мы найдем две версии `ftruncate` в [.filename]#syscalls.master#, старую (130) и новую (201). Мы будем использовать новую:
+
+[.programlisting]
+....
+201 STD BSD { int ftruncate(int fd, int pad, off_t length); }
+....
+
+Обратите внимание, что здесь снова присутствует `int pad`.
+
+[[x86-ftuc]]
+=== ftuc
+
+Теперь мы знаем всё, что нужно для написания ftuc. Начнём с добавления нескольких новых строк в [.filename]#system.inc#. Сначала определим некоторые константы и структуры, где-нибудь в начале или около начала файла:
+
+[.programlisting]
+....
+;;;;;;; open flags
+%define O_RDONLY 0
+%define O_WRONLY 1
+%define O_RDWR 2
+
+;;;;;;; mmap flags
+%define PROT_NONE 0
+%define PROT_READ 1
+%define PROT_WRITE 2
+%define PROT_EXEC 4
+;;
+%define MAP_SHARED 0001h
+%define MAP_PRIVATE 0002h
+
+;;;;;;; stat structure
+struc stat
+st_dev resd 1 ; = 0
+st_ino resd 1 ; = 4
+st_mode resw 1 ; = 8, size is 16 bits
+st_nlink resw 1 ; = 10, ditto
+st_uid resd 1 ; = 12
+st_gid resd 1 ; = 16
+st_rdev resd 1 ; = 20
+st_atime resd 1 ; = 24
+st_atimensec resd 1 ; = 28
+st_mtime resd 1 ; = 32
+st_mtimensec resd 1 ; = 36
+st_ctime resd 1 ; = 40
+st_ctimensec resd 1 ; = 44
+st_size resd 2 ; = 48, size is 64 bits
+st_blocks resd 2 ; = 56, ditto
+st_blksize resd 1 ; = 64
+st_flags resd 1 ; = 68
+st_gen resd 1 ; = 72
+st_lspare resd 1 ; = 76
+st_qspare resd 4 ; = 80
+endstruc
+....
+
+Мы определяем новые системные вызовы:
+
+[.programlisting]
+....
+%define SYS_mmap 197
+%define SYS_munmap 73
+%define SYS_fstat 189
+%define SYS_ftruncate 201
+....
+
+Добавляем макросы для их использования:
+
+[.programlisting]
+....
+%macro sys.mmap 0
+ system SYS_mmap
+%endmacro
+
+%macro sys.munmap 0
+ system SYS_munmap
+%endmacro
+
+%macro sys.ftruncate 0
+ system SYS_ftruncate
+%endmacro
+
+%macro sys.fstat 0
+ system SYS_fstat
+%endmacro
+....
+
+И вот наш код:
+
+[.programlisting]
+....
+;;;;;;; Fast Text-to-Unix Conversion (ftuc.asm) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Started: 21-Dec-2000
+;; Updated: 22-Dec-2000
+;;
+;; Copyright 2000 G. Adam Stanislav.
+;; All rights reserved.
+;;
+;;;;;;; v.1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+%include 'system.inc'
+
+section .data
+ db 'Copyright 2000 G. Adam Stanislav.', 0Ah
+ db 'All rights reserved.', 0Ah
+usg db 'Usage: ftuc filename', 0Ah
+usglen equ $-usg
+co db "ftuc: Can't open file.", 0Ah
+colen equ $-co
+fae db 'ftuc: File access error.', 0Ah
+faelen equ $-fae
+ftl db 'ftuc: File too long, use regular tuc instead.', 0Ah
+ftllen equ $-ftl
+mae db 'ftuc: Memory allocation error.', 0Ah
+maelen equ $-mae
+
+section .text
+
+align 4
+memerr:
+ push dword maelen
+ push dword mae
+ jmp short error
+
+align 4
+toolong:
+ push dword ftllen
+ push dword ftl
+ jmp short error
+
+align 4
+facerr:
+ push dword faelen
+ push dword fae
+ jmp short error
+
+align 4
+cantopen:
+ push dword colen
+ push dword co
+ jmp short error
+
+align 4
+usage:
+ push dword usglen
+ push dword usg
+
+error:
+ push dword stderr
+ sys.write
+
+ push dword 1
+ sys.exit
+
+align 4
+global _start
+_start:
+ pop eax ; argc
+ pop eax ; program name
+ pop ecx ; file to convert
+ jecxz usage
+
+ pop eax
+ or eax, eax ; Too many arguments?
+ jne usage
+
+ ; Open the file
+ push dword O_RDWR
+ push ecx
+ sys.open
+ jc cantopen
+
+ mov ebp, eax ; Save fd
+
+ sub esp, byte stat_size
+ mov ebx, esp
+
+ ; Find file size
+ push ebx
+ push ebp ; fd
+ sys.fstat
+ jc facerr
+
+ mov edx, [ebx + st_size + 4]
+
+ ; File is too long if EDX != 0 ...
+ or edx, edx
+ jne near toolong
+ mov ecx, [ebx + st_size]
+ ; ... or if it is above 2 GB
+ or ecx, ecx
+ js near toolong
+
+ ; Do nothing if the file is 0 bytes in size
+ jecxz .quit
+
+ ; Map the entire file in memory
+ push edx
+ push edx ; starting at offset 0
+ push edx ; pad
+ push ebp ; fd
+ push dword MAP_SHARED
+ push dword PROT_READ | PROT_WRITE
+ push ecx ; entire file size
+ push edx ; let system decide on the address
+ sys.mmap
+ jc near memerr
+
+ mov edi, eax
+ mov esi, eax
+ push ecx ; for SYS_munmap
+ push edi
+
+ ; Use EBX for state machine
+ mov ebx, ordinary
+ mov ah, 0Ah
+ cld
+
+.loop:
+ lodsb
+ call ebx
+ loop .loop
+
+ cmp ebx, ordinary
+ je .filesize
+
+ ; Output final lf
+ mov al, ah
+ stosb
+ inc edx
+
+.filesize:
+ ; truncate file to new size
+ push dword 0 ; high dword
+ push edx ; low dword
+ push eax ; pad
+ push ebp
+ sys.ftruncate
+
+ ; close it (ebp still pushed)
+ sys.close
+
+ add esp, byte 16
+ sys.munmap
+
+.quit:
+ push dword 0
+ sys.exit
+
+align 4
+ordinary:
+ cmp al, 0Dh
+ je .cr
+
+ cmp al, ah
+ je .lf
+
+ stosb
+ inc edx
+ ret
+
+align 4
+.cr:
+ mov ebx, cr
+ ret
+
+align 4
+.lf:
+ mov ebx, lf
+ ret
+
+align 4
+cr:
+ cmp al, 0Dh
+ je .cr
+
+ cmp al, ah
+ je .lf
+
+ xchg al, ah
+ stosb
+ inc edx
+
+ xchg al, ah
+ ; fall through
+
+.lf:
+ stosb
+ inc edx
+ mov ebx, ordinary
+ ret
+
+align 4
+.cr:
+ mov al, ah
+ stosb
+ inc edx
+ ret
+
+align 4
+lf:
+ cmp al, ah
+ je .lf
+
+ cmp al, 0Dh
+ je .cr
+
+ xchg al, ah
+ stosb
+ inc edx
+
+ xchg al, ah
+ stosb
+ inc edx
+ mov ebx, ordinary
+ ret
+
+align 4
+.cr:
+ mov ebx, ordinary
+ mov al, ah
+ ; fall through
+
+.lf:
+ stosb
+ inc edx
+ ret
+....
+
+[WARNING]
+====
+Не используйте эту программу для файлов, хранящихся на диске, отформатированном в MS-DOS(R) или Windows(R). В коде FreeBSD присутствует неочевидная ошибка при использовании `mmap` на таких дисках, смонтированных в FreeBSD: если размер файла превышает определённое значение, `mmap` заполнит память нулями, а затем запишет их в файл, перезаписав его содержимое.
+====
+
+[[x86-one-pointed-mind]]
+== Спокойствие ума
+
+Как ученик дзэн, мне нравится идея спокойствия ума (экаггата): делай одно дело за раз и делай его хорошо.
+
+Вот именно так, в большинстве случаев, работает и UNIX(R). В то время как типичное приложение Windows(R) пытается сделать всё, что только можно (и поэтому кишит ошибками), типичная программа UNIX(R) делает только одну вещь, но делает её хорошо.
+
+Типичный пользователь UNIX(R) по сути собирает свои собственные приложения, написав shell-скрипт, который объединяет различные существующие программы, передавая вывод одной программы на вход другой.
+
+При написании собственного программного обеспечения для UNIX(R) обычно рекомендуется определить, какие части решаемой задачи могут быть обработаны существующими программами, и создавать собственные программы только для той части задачи, для которой нет готового решения.
+
+[[x86-csv]]
+=== CSV
+
+Я проиллюстрирую этот принцип конкретным примером из реальной жизни, с которым недавно столкнулся:
+
+Мне нужно было извлечь 11-е поле каждой записи из базы данных, которую я загрузил с веб-сайта. База данных представляла собой CSV-файл, то есть список _значений, разделённых запятыми_. Это довольно стандартный формат для обмена данными между людьми, которые могут использовать разное программное обеспечение для работы с базами данных.
+
+Первая строка файла содержит список различных полей, разделенных запятыми. Остальная часть файла содержит данные, перечисленные построчно, со значениями, разделенными запятыми.
+
+Я попробовал awk, используя запятую в качестве разделителя. Но поскольку несколько строк содержали запятую в кавычках, awk извлекал неправильное поле из этих строк.
+
+Следовательно, мне нужно было написать собственное программное обеспечение для извлечения 11-го поля из CSV-файла. Однако, следуя духу UNIX(R), мне нужно было лишь создать простой фильтр, выполняющий следующие действия:
+
+* Удалить первую строку из файла;
+* Заменить все не заключённые в кавычки запятые на другой символ;
+* Удалить все кавычки.
+
+Строго говоря, я мог бы использовать sed для удаления первой строки из файла, но сделать это в моей собственной программе было очень просто, поэтому я решил так поступить и уменьшить размер конвейера.
+
+В любом случае, написание подобной программы заняло у меня около 20 минут. Написание программы, которая извлекает 11-е поле из CSV-файла, заняло бы гораздо больше времени, и я не смог бы повторно использовать её для извлечения другого поля из другой базы данных.
+
+На этот раз я решил позволить ей выполнить немного больше работы, чем обычная учебная программа:
+
+* Она анализирует свою командную строку на наличие опций;
+* Она отображает подсказку, если обнаруживает неверные аргументы;
+* Она выдает понятные сообщения об ошибках.
+
+Вот какое сообщение она выводит о том, как ее использовать:
+
+[source, shell]
+....
+Usage: csv [-t<delim>] [-c<comma>] [-p] [-o <outfile>] [-i <infile>]
+....
+
+Все параметры необязательны и могут располагаться в любом порядке.
+
+Параметр `-t` указывает, на что заменить запятые. По умолчанию используется `tab`. Например, `-t;` заменит все незакавыченные запятые на точку с запятой.
+
+Мне не понадобилась опция `-c`, но в будущем она может пригодиться. Она позволяет указать, что я хочу заменить символ, отличный от запятой, на что-то другое. Например, `-c@` заменит все знаки @ (полезно, если нужно разделить список email-адресов на имена пользователей и домены).
+
+Опция `-p` сохраняет первую строку, т.е. не удаляет её. По умолчанию мы удаляем первую строку, потому что в CSV-файле она содержит названия полей, а не данные.
+
+Опции `-i` и `-o` позволяют указать входной и выходной файлы. По умолчанию используются [.filename]#stdin# и [.filename]#stdout#, как обычно работает стандартный фильтр UNIX(R).
+
+Я убедился, что принимаются как `-i filename`, так и `-ifilename`. Также я убедился, что может быть указан только один входной и один выходной файл.
+
+Чтобы получить 11-е поле каждой записи, теперь я могу сделать:
+
+[source, shell]
+....
+% csv '-t;' data.csv | awk '-F;' '{print $11}'
+....
+
+Код сохраняет параметры (за исключением файловых дескрипторов) в `EDX`: запятая в `DH`, новый разделитель в `DL`, а флаг параметра `-p` в старшем бите `EDX`, поэтому проверка его знака даст нам быстрое решение о дальнейших действиях.
+
+Вот код:
+
+[.programlisting]
+....
+;;;;;;; csv.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Convert a comma-separated file to a something-else separated file.
+;
+; Started: 31-May-2001
+; Updated: 1-Jun-2001
+;
+; Copyright (c) 2001 G. Adam Stanislav
+; All rights reserved.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+%include 'system.inc'
+
+%define BUFSIZE 2048
+
+section .data
+fd.in dd stdin
+fd.out dd stdout
+usg db 'Usage: csv [-t<delim>] [-c<comma>] [-p] [-o <outfile>] [-i <infile>]', 0Ah
+usglen equ $-usg
+iemsg db "csv: Can't open input file", 0Ah
+iemlen equ $-iemsg
+oemsg db "csv: Can't create output file", 0Ah
+oemlen equ $-oemsg
+
+section .bss
+ibuffer resb BUFSIZE
+obuffer resb BUFSIZE
+
+section .text
+align 4
+ierr:
+ push dword iemlen
+ push dword iemsg
+ push dword stderr
+ sys.write
+ push dword 1 ; return failure
+ sys.exit
+
+align 4
+oerr:
+ push dword oemlen
+ push dword oemsg
+ push dword stderr
+ sys.write
+ push dword 2
+ sys.exit
+
+align 4
+usage:
+ push dword usglen
+ push dword usg
+ push dword stderr
+ sys.write
+ push dword 3
+ sys.exit
+
+align 4
+global _start
+_start:
+ add esp, byte 8 ; discard argc and argv[0]
+ mov edx, (',' << 8) | 9
+
+.arg:
+ pop ecx
+ or ecx, ecx
+ je near .init ; no more arguments
+
+ ; ECX contains the pointer to an argument
+ cmp byte [ecx], '-'
+ jne usage
+
+ inc ecx
+ mov ax, [ecx]
+
+.o:
+ cmp al, 'o'
+ jne .i
+
+ ; Make sure we are not asked for the output file twice
+ cmp dword [fd.out], stdout
+ jne usage
+
+ ; Find the path to output file - it is either at [ECX+1],
+ ; i.e., -ofile --
+ ; or in the next argument,
+ ; i.e., -o file
+
+ inc ecx
+ or ah, ah
+ jne .openoutput
+ pop ecx
+ jecxz usage
+
+.openoutput:
+ push dword 420 ; file mode (644 octal)
+ push dword 0200h | 0400h | 01h
+ ; O_CREAT | O_TRUNC | O_WRONLY
+ push ecx
+ sys.open
+ jc near oerr
+
+ add esp, byte 12
+ mov [fd.out], eax
+ jmp short .arg
+
+.i:
+ cmp al, 'i'
+ jne .p
+
+ ; Make sure we are not asked twice
+ cmp dword [fd.in], stdin
+ jne near usage
+
+ ; Find the path to the input file
+ inc ecx
+ or ah, ah
+ jne .openinput
+ pop ecx
+ or ecx, ecx
+ je near usage
+
+.openinput:
+ push dword 0 ; O_RDONLY
+ push ecx
+ sys.open
+ jc near ierr ; open failed
+
+ add esp, byte 8
+ mov [fd.in], eax
+ jmp .arg
+
+.p:
+ cmp al, 'p'
+ jne .t
+ or ah, ah
+ jne near usage
+ or edx, 1 << 31
+ jmp .arg
+
+.t:
+ cmp al, 't' ; redefine output delimiter
+ jne .c
+ or ah, ah
+ je near usage
+ mov dl, ah
+ jmp .arg
+
+.c:
+ cmp al, 'c'
+ jne near usage
+ or ah, ah
+ je near usage
+ mov dh, ah
+ jmp .arg
+
+align 4
+.init:
+ sub eax, eax
+ sub ebx, ebx
+ sub ecx, ecx
+ mov edi, obuffer
+
+ ; See if we are to preserve the first line
+ or edx, edx
+ js .loop
+
+.firstline:
+ ; get rid of the first line
+ call getchar
+ cmp al, 0Ah
+ jne .firstline
+
+.loop:
+ ; read a byte from stdin
+ call getchar
+
+ ; is it a comma (or whatever the user asked for)?
+ cmp al, dh
+ jne .quote
+
+ ; Replace the comma with a tab (or whatever the user wants)
+ mov al, dl
+
+.put:
+ call putchar
+ jmp short .loop
+
+.quote:
+ cmp al, '"'
+ jne .put
+
+ ; Print everything until you get another quote or EOL. If it
+ ; is a quote, skip it. If it is EOL, print it.
+.qloop:
+ call getchar
+ cmp al, '"'
+ je .loop
+
+ cmp al, 0Ah
+ je .put
+
+ call putchar
+ jmp short .qloop
+
+align 4
+getchar:
+ or ebx, ebx
+ jne .fetch
+
+ call read
+
+.fetch:
+ lodsb
+ dec ebx
+ ret
+
+read:
+ jecxz .read
+ call write
+
+.read:
+ push dword BUFSIZE
+ mov esi, ibuffer
+ push esi
+ push dword [fd.in]
+ sys.read
+ add esp, byte 12
+ mov ebx, eax
+ or eax, eax
+ je .done
+ sub eax, eax
+ ret
+
+align 4
+.done:
+ call write ; flush output buffer
+
+ ; close files
+ push dword [fd.in]
+ sys.close
+
+ push dword [fd.out]
+ sys.close
+
+ ; return success
+ push dword 0
+ sys.exit
+
+align 4
+putchar:
+ stosb
+ inc ecx
+ cmp ecx, BUFSIZE
+ je write
+ ret
+
+align 4
+write:
+ jecxz .ret ; nothing to write
+ sub edi, ecx ; start of buffer
+ push ecx
+ push edi
+ push dword [fd.out]
+ sys.write
+ add esp, byte 12
+ sub eax, eax
+ sub ecx, ecx ; buffer is empty now
+.ret:
+ ret
+....
+
+Большая часть взята из [.filename]#hex.asm# выше. Однако есть одно важное отличие: я больше не вызываю `write` каждый раз при выводе перевода строки. Тем не менее, код можно использовать интерактивно.
+
+Я нашел лучшее решение для интерактивной проблемы с тех пор, как начал писать эту главу. Я хотел убедиться, что каждая строка выводится отдельно только при необходимости. В конце концов, нет необходимости выводить каждую строку при неинтерактивном использовании.
+
+Новое решение, которое я использую сейчас, заключается в вызове `write` каждый раз, когда обнаруживаю, что входной буфер пуст. Таким образом, при работе в интерактивном режиме программа считывает одну строку с клавиатуры пользователя, обрабатывает её и видит, что входной буфер пуст. Она сбрасывает свой вывод и читает следующую строку.
+
+[[x86-buffered-dark-side]]
+==== Темная сторона буферизации
+
+Это изменение предотвращает загадочную блокировку в очень специфическом случае. Я называю это _тёмной стороной буферизации_, в основном потому, что это представляет опасность, которая не совсем очевидна.
+
+Маловероятно, что это произойдет с такой программой, как csv выше, поэтому рассмотрим еще один фильтр: в этом случае мы ожидаем, что наши входные данные будут представлять собой необработанные данные, описывающие значения цветов, такие как интенсивности _красного_, _зеленого_ и _синего_ для пикселя. На выходе мы получим негатив входных данных.
+
+Такой фильтр было бы очень просто написать. Большая его часть выглядела бы так же, как и все другие фильтры, которые мы уже писали, поэтому я покажу только его внутренний цикл:
+
+[.programlisting]
+....
+.loop:
+ call getchar
+ not al ; Create a negative
+ call putchar
+ jmp short .loop
+....
+
+Поскольку этот фильтр работает с необработанными данными, он вряд ли будет использоваться интерактивно.
+
+Но он может вызываться программным обеспечением для обработки изображений. И, если он не вызывает `write` перед каждым вызовом `read`, высока вероятность, что он зависнет.
+
+Вот что может произойти:
+
+[.procedure]
+. Редактор изображений загрузит наш фильтр, используя функцию `popen()` на языке C.
+. Он прочитает первый ряд пикселей из битовой карты или пиксельной карты.
+. Он запишет первую строку пикселей в _канал_, ведущий к `fd.in` нашего фильтра.
+. Наш фильтр будет читать каждый пиксель из входных данных, преобразовывать его в негатив и записывать в выходной буфер.
+. Наш фильтр будет вызывать `getchar` для получения следующего пикселя.
+. `getchar` обнаружит пустой входной буфер, поэтому вызовет `read`.
+. `read` вызовет системный вызов `SYS_read`.
+. _Ядро_ приостановит работу нашего фильтра до тех пор, пока редактор изображений не отправит больше данных в канал.
+. Редактор изображений будет читать из другого канала, подключенного к `fd.out` нашего фильтра, чтобы он мог установить первую строку выходного изображения _до_ того, как отправит нам вторую строку входного.
+. _Ядро_ приостанавливает работу графического редактора до тех пор, пока не получит какие-либо данные от нашего фильтра, чтобы передать их редактору.
+
+На этом этапе наш фильтр ожидает, что редактор изображений отправит ему больше данных для обработки, в то время как редактор изображений ожидает, что наш фильтр отправит ему результат обработки первой строки. Однако результат находится в нашем выходном буфере.
+
+Фильтр и редактор изображений будут продолжать ждать друг друга вечно (или, по крайней мере, пока их не завершат командой kill). Наше программное обеспечение только что вошло в crossref:secure[secure-race-conditions,состояние гонки].
+
+Эта проблема не возникает, если наш фильтр очищает свой выходной буфер _перед_ запросом к _ядру_ для получения дополнительных входных данных.
+
+[[x86-fpu]]
+== Использование FPU
+
+Как ни странно, большая часть литературы по ассемблеру даже не упоминает о существовании FPU, или _блока обработки чисел с плавающей запятой_, не говоря уже о программировании для него.
+
+Тем не менее, язык ассемблера проявляет себя наилучшим образом, когда мы создаем высокооптимизированный код для FPU, выполняя вещи, которые можно сделать _только_ на языке ассемблера.
+
+[[x86-fpu-organization]]
+=== Организация FPU
+
+FPU состоит из 8 80-битных регистров с плавающей запятой. Они организованы в виде стека — вы можете `push` (поместить) значение на TOS (_вершина стека_) и `pop` (извлечь) его.
+
+Как бы то ни было, мнемоники ассемблера — не `push` и `pop`, потому что они уже заняты.
+
+Вы можете `push` (положить) значение на вершину стека (TOS), используя `fld`, `fild` и `fbld`. Несколько других кодов операций позволяют вам `push` (положить) многие распространённые _константы_ — например, _pi_ — на вершину стека (TOS).
+
+Аналогично, вы можете `извлечь` значение с помощью `fst`, `fstp`, `fist`, `fistp` и `fbstp`. На самом деле только коды операций, оканчивающиеся на _p_, буквально `извлекают` значение, остальные же `сохраняют` его в другом месте, не удаляя с вершины стека (TOS).
+
+Мы можем передавать данные между TOS и памятью компьютера либо как 32-битное, 64-битное или 80-битное _вещественное_ число, 16-битное, 32-битное или 64-битное _целое_ число, либо как 80-битное _упакованное десятичное_ число.
+
+80-битный _упакованный десятичный_ формат является особым случаем _двоично-десятичного кодирования_, который очень удобен при преобразовании между ASCII-представлением данных и внутренними данными FPU. Он позволяет использовать до 18 значащих цифр.
+
+Независимо от того, как мы представляем данные в памяти, FPU всегда хранит их в 80-битном формате _real_ в своих регистрах.
+
+Его внутренняя точность составляет не менее 19 десятичных цифр, поэтому даже если мы решим отображать результаты в формате ASCII с полной 18-значной точностью, мы всё равно будем показывать корректные результаты.
+
+Мы можем выполнять математические операции над TOS: вычислять его _синус_, _масштабировать_ (то есть умножать или делить на степень двойки), вычислять его _логарифм_ по основанию 2 и многое другое.
+
+Мы также можем _умножить_ или _разделить_ его, _прибавить_ к нему или _вычесть_ его из любого из регистров FPU (включая его самого).
+
+Официальный код операции Intel для TOS — `st`, а для _регистров_ — `st(0)`-`st(7)`. Таким образом, `st` и `st(0)` ссылаются на один и тот же регистр.
+
+По каким-то причинам оригинальный автор nasm решил использовать другие коды операций, а именно `st0`-`st7`. Другими словами, скобки отсутствуют, а вершина стека всегда `st0`, но никогда просто `st`.
+
+[[x86-fpu-packed-decimal]]
+==== Формат упакованного десятичного числа
+
+Формат _упакованного десятичного числа_ использует 10 байт (80 бит) памяти для представления 18 цифр. Представленное число всегда является _целым_.
+
+[TIP]
+====
+Вы можете использовать это для получения десятичных знаков, предварительно умножив TOS на степень 10.
+====
+
+Старший бит старшего байта (байт 9) является _знаковым битом_: если он установлен, число _отрицательное_, в противном случае — _положительное_. Остальные биты этого байта не используются/игнорируются.
+
+Оставшиеся 9 байт хранят 18 цифр числа: 2 цифры на байт.
+
+_Старший разряд_ хранится в старшем _полубайте_ (4 бита), _младший разряд_ — в младшем _полубайте_.
+
+Как бы то ни было, вы можете подумать, что `-1234567` будет храниться в памяти следующим образом (в шестнадцатеричной записи):
+
+[.programlisting]
+....
+80 00 00 00 00 00 01 23 45 67
+....
+
+Увы, это не так! Как и все остальное, созданное Intel, даже _упакованное десятичное число_ имеет порядок _от младшего к старшему_.
+
+Это означает, что наш `-1234567` хранится следующим образом:
+
+[.programlisting]
+....
+67 45 23 01 00 00 00 00 00 80
+....
+
+Помните об этом, иначе вы будете рвать на себе волосы в отчаянии!
+
+[NOTE]
+====
+Книга, которую стоит прочитать — если сможете её найти — это книга Ричарда Старца http://www.amazon.com/exec/obidos/ASIN/013246604X/whizkidtechnomag[8087/80287/80387 для IBM PC и совместимых]. Хотя в ней, кажется, факт о little-endian хранении _упакованного десятичного числа_ принимается как данность. Я не шучу насчёт отчаяния, которое испытывал, пытаясь понять, что не так с фильтром, который я привожу ниже, _прежде_ чем мне пришло в голову попробовать little-endian порядок даже для этого типа данных.
+====
+
+[[x86-pinhole-photography]]
+=== Экскурсия в фотографию с помощью камеры-обскуры
+
+Чтобы создавать полезное программное обеспечение, мы должны понимать не только наши инструменты программирования, но и область, для которой разрабатываем ПО.
+
+Наш следующий фильтр поможет нам, когда мы захотим создать _камеру-обскуру_, поэтому нам понадобятся некоторые знания о _фотографии с помощью обскуры_, прежде чем мы сможем продолжить.
+
+[[x86-camera]]
+==== Камера
+
+Самый простой способ описать любую когда-либо созданную камеру — это некоторое пустое пространство, заключённое в светонепроницаемый материал, с небольшим отверстием в корпусе.
+
+Корпус обычно прочный (например, коробка), хотя иногда он гибкий (гофрированная часть). Внутри камеры довольно темно. Однако отверстие пропускает световые лучи через одну точку (хотя в некоторых случаях их может быть несколько). Эти световые лучи формируют изображение — представление того, что находится снаружи камеры, перед отверстием.
+
+Если внутрь камеры поместить светочувствительный материал (например, плёнку), он может зафиксировать изображение.
+
+Отверстие часто содержит _линзу_ или сборку линз, которую часто называют _объективом_.
+
+[[x86-the-pinhole]]
+==== Игольное ушко
+
+Но, строго говоря, линза не обязательна: первые камеры использовали не линзу, а _маленькое отверстие_ размером с игольное ушко. Даже сегодня _маленькие отверстия_ применяются как инструмент для изучения принципов работы камер и для создания особого вида изображений.
+
+Изображение, создаваемое _маленьким отверстием_, одинаково резкое. Или _размытое_. Существует идеальный размер для маленького отверстия: если оно больше или меньше, изображение теряет резкость.
+
+[[x86-focal-length]]
+==== Фокусное расстояние
+
+Идеальный диаметр отверстия является функцией квадратного корня из _фокусного расстояния_, которое представляет собой расстояние от отверстия до плёнки.
+
+[.programlisting]
+....
+D = PC * sqrt(FL)
+....
+
+Здесь `D` — идеальный диаметр отверстия, `FL` — фокусное расстояние, а `PC` — константа отверстия. По данным Джейя Бендера, её значение равно `0,04`, тогда как Кеннет Коннорс определил его как `0,037`. Другие исследователи предложили иные значения. Кроме того, это значение справедливо только для дневного света: другие типы освещения потребуют иной константы, значение которой можно определить только экспериментальным путём.
+
+[[x86-f-number]]
+==== Число f (диафрагменное число)
+
+Число f — это очень полезный показатель того, сколько света попадает на плёнку. Экспонометр может определить, что, например, для экспонирования плёнки определённой чувствительности при f5.6 может потребоваться выдержка 1/1000 сек.
+
+Не имеет значения, 35-мм это камера или камера 6x9 см и т.д. Достаточно знать диафрагменное число, чтобы определить правильную экспозицию.
+
+Число f легко вычислить:
+
+[.programlisting]
+....
+F = FL / D
+....
+
+Другими словами, число f равно фокусному расстоянию, деленному на диаметр отверстия. Это также означает, что большее f-число подразумевает либо меньшее отверстие, либо большее фокусное расстояние, либо и то, и другое. В свою очередь, это означает, что чем больше число f, тем дольше должна быть выдержка.
+
+Кроме того, хотя диаметр отверстия и фокусное расстояние являются одномерными величинами, и плёнка, и отверстие — двумерны. Это означает, что если вы измерили экспозицию при диафрагменном числе `A` как `t`, то экспозиция при диафрагменном числе `B` будет:
+
+[.programlisting]
+....
+t * (B / A)²
+....
+
+[[x86-normalized-f-number]]
+==== Нормализованное число f
+
+Хотя многие современные камеры могут изменять диаметр своего отверстия, а следовательно и свое число f, довольно плавно и постепенно, так было не всегда.
+
+Для обеспечения различных значений диафрагмы в камерах обычно использовалась металлическая пластина с несколькими отверстиями разного размера.
+
+Их размеры были выбраны в соответствии с приведённой выше формулой таким образом, чтобы результирующее f-число было одним из стандартных f-чисел, используемых на всех фотоаппаратах. Например, у моего очень старого фотоаппарата Kodak Duaflex IV есть три таких отверстия для чисел f — 8, 11 и 16.
+
+Более современные камеры могут предлагать значения диафрагменного числа 2.8, 4, 5.6, 8, 11, 16, 22 и 32 (а также другие). Эти числа выбраны не произвольно: все они являются степенями квадратного корня из 2, хотя могут быть немного округлены.
+
+[[x86-f-stop]]
+==== Ступени числа f
+
+Типичная камера устроена так, что установка любого из нормализованных чисел f изменяет ощущение от регулятора. Он естественным образом _останавливается_ в этом положении. Из-за этого такие положения регулятора называются f-ступенями.
+
+Поскольку значения диафрагмы на каждой ступени являются степенями квадратного корня из 2, поворот диска на 1 ступень удваивает количество света, необходимое для правильной экспозиции. Поворот на 2 ступени увеличивает требуемую экспозицию вчетверо. Поворот диска на 3 ступени требует увеличения экспозиции в 8 раз и так далее.
+
+[[x86-pinhole-software]]
+=== Проектирование программного обеспечения камеры-обскуры
+
+Мы готовы решить, что именно должно делать наше программное обеспечение для камер-обскур.
+
+[[xpinhole-processing-input]]
+==== Обработка ввода программы
+
+Поскольку основная цель — помочь нам разработать работающую камеру-обскуру, мы будем использовать _фокусное расстояние_ в качестве входных данных для программы. Это можно определить без программного обеспечения: правильное фокусное расстояние зависит от размера плёнки и необходимости съёмки «обычных» изображений, широкоугольных или телефото.
+
+Большинство написанных нами до сих пор программ работали с отдельными символами или байтами в качестве входных данных: программа hex преобразовывала отдельные байты в шестнадцатеричное число, программа csv либо пропускала символ, либо удаляла его, либо заменяла на другой символ и т.д.
+
+Одна программа, `ftuc`, использовала конечный автомат для обработки не более двух входных байтов за раз.
+
+Но наша программа для камеры-обскуры не может работать только с отдельными символами, ей приходится иметь дело с более крупными синтаксическими единицами.
+
+Например, если мы хотим, чтобы программа рассчитала диаметр отверстия (и другие значения, которые мы обсудим позже) для фокусных расстояний `100 мм`, `150 мм` и `210 мм`, мы можем ввести что-то вроде этого:
+
+[source, shell]
+....
+ 100, 150, 210
+....
+
+Наша программа должна учитывать более одного байта входных данных за раз. Когда она видит первую `1`, она должна понимать, что это первая цифра десятичного числа. Когда она видит `0` и другой `0`, она должна знать, что это следующие цифры того же числа.
+
+Когда он встречает первую запятую, он должен понять, что больше не получает цифры первого числа. Он должен уметь преобразовать цифры первого числа в значение `100`. И цифры второго числа в значение `150`. И, конечно же, цифры третьего числа в числовое значение `210`.
+
+Нам нужно определиться с допустимыми разделителями: должны ли входные числа разделяться запятой? Если да, то как обрабатывать два числа, разделённые чем-то другим?
+
+Лично я предпочитаю простоту. Либо что-то является числом — и тогда я его обрабатываю. Либо не является числом — и тогда я это отбрасываю. Мне не нравится, когда компьютер жалуется на лишний символ, если _очевидно_, что он лишний. Да ладно!
+
+Плюс, это позволяет мне разбавить монотонность вычислений и ввести запрос вместо просто числа:
+
+[source, shell]
+....
+What is the best pinhole diameter for the
+ focal length of 150?
+....
+
+Нет причины, чтобы компьютер выводил множество жалоб:
+
+[source, shell]
+....
+Syntax error: What
+Syntax error: is
+Syntax error: the
+Syntax error: best
+....
+
+И так далее, и так далее, и так далее.
+
+Во-вторых, мне нравится символ `+#+` для обозначения начала комментария, который продолжается до конца строки. Это не требует больших усилий для реализации и позволяет мне рассматривать входные файлы для моего программного обеспечения как исполняемые скрипты.
+
+В нашем случае также необходимо определиться с единицами измерения входных данных: мы выбираем _миллиметры_, так как большинство фотографов измеряют фокусное расстояние именно в них.
+
+Наконец, нам нужно решить, разрешать ли использование десятичной точки (в этом случае мы также должны учитывать тот факт, что во многих странах используется десятичная _запятая_).
+
+В нашем случае разрешение десятичной точки/запятой создало бы ложное ощущение точности: разница между фокусными расстояниями `50` и `51` практически незаметна, поэтому разрешать пользователю вводить что-то вроде `50.5` — не лучшая идея. Это моё мнение, конечно, но программу пишу я. В своей программе вы можете сделать другие выбор, разумеется.
+
+[[x86-pinhole-options]]
+==== Передача параметров программе
+
+Самое важное, что нам нужно знать при создании камеры-обскуры — это диаметр отверстия. Поскольку мы хотим получать чёткие изображения, мы будем использовать приведённую выше формулу для расчёта диаметра отверстия от фокусного расстояния. Поскольку эксперты предлагают несколько различных значений для константы `PC`, нам нужно будет иметь выбор.
+
+В традициях программирования в UNIX(R) предусмотрены два основных способа выбора параметров программы, а также значение по умолчанию на случай, если пользователь не сделает выбор.
+
+Почему есть два способа выбора?
+
+Один из способов — это позволить (относительно) _постоянный_ выбор, который применяется автоматически каждый раз при запуске программы, без необходимости каждый раз указывать, что мы хотим, чтобы она сделала.
+
+Постоянные настройки могут быть сохранены в конфигурационном файле, обычно расположенном в домашнем каталоге пользователя. Файл обычно имеет то же имя, что и приложение, но начинается с точки. Часто к имени файла добавляется _"rc"_. Таким образом, наш файл может называться [.filename]#~/.pinhole# или [.filename]#~/.pinholerc#. (Обозначение [.filename]#~/# означает домашний каталог текущего пользователя.)
+
+Файл конфигурации в основном используется программами, у которых много настраиваемых параметров. Те, у которых он один (или несколько), часто используют другой метод: они ожидают найти параметр в _переменной окружения_. В нашем случае, мы можем посмотреть на переменную окружения с именем `PINHOLE`.
+
+Обычно программа использует один из вышеуказанных методов. В противном случае, если в конфигурационном файле указано одно, а в переменной окружения — другое, программа может запутаться (или стать слишком сложной).
+
+Поскольку нам нужно выбрать только _один_ такой параметр, мы воспользуемся вторым методом и поищем в окружении переменную с именем `PINHOLE`.
+
+Другой способ позволяет нам принимать _ad hoc_ решения: _"Хотя обычно я хочу, чтобы ты использовал 0.039, на этот раз мне нужно 0.03872."_ Другими словами, он позволяет нам _переопределить_ постоянный выбор.
+
+Такой выбор обычно осуществляется с помощью параметров командной строки.
+
+Наконец, программе _всегда_ необходим _значение по умолчанию_. Пользователь может не делать никакого выбора. Возможно, он не знает, что выбрать. Возможно, он «просто просматривает». Предпочтительно, чтобы значением по умолчанию было то, что выбрало бы большинство пользователей. Таким образом, им не нужно выбирать. Или, точнее, они могут выбрать значение по умолчанию без дополнительных усилий.
+
+Учитывая эту систему, программа может обнаружить конфликтующие параметры и обработать их следующим образом:
+
+[.procedure]
+. Если она находит _специальный_ выбор (например, параметр командной строки), она должна принять этот выбор. Она должна игнорировать любой постоянный выбор и значения по умолчанию.
+. _В противном случае_, если будет найден постоянный параметр (например, переменная окружения), он должен быть принят, а значение по умолчанию — проигнорировано.
+. _В противном случае_, следует использовать значение по умолчанию.
+
+Нам также необходимо решить, в каком _формате_ должна быть наша опция `PC`.
+
+На первый взгляд кажется очевидным использовать формат `PINHOLE=0.04` для переменной окружения и `-p0.04` для командной строки.
+
+Разрешение этого на самом деле представляет угрозу безопасности. Константа `PC` — это очень маленькое число. Естественно, мы протестируем наше программное обеспечение, используя различные небольшие значения `PC`. Но что произойдёт, если кто-то запустит программу, выбрав огромное значение?
+
+Это может привести к сбою программы, так как мы не разрабатывали её для обработки огромных чисел.
+
+Или мы можем потратить больше времени на программу, чтобы она могла обрабатывать огромные числа. Мы могли бы сделать это, если бы писали коммерческое программное обеспечение для аудитории, не знакомой с компьютерами.
+
+Или можно сказать: _"Пусть терпит! Пользователь сам должен был разобраться."_
+
+Или мы можем просто сделать невозможным ввод пользователем слишком большого числа. Это подход, который мы выберем: мы будем использовать _подразумеваемый префикс 0._.
+
+Другими словами, если пользователь хочет `0.04`, мы ожидаем, что он введёт `-p04` или установит `PINHOLE=04` в своём окружении. Таким образом, если он укажет `-p9999999`, мы интерпретируем это как ``0.9999999`` — всё ещё нелепо, но по крайней мере безопаснее.
+
+Во-вторых, многие пользователи просто захотят использовать либо константу Бендера, либо константу Коннорса. Чтобы облегчить им задачу, мы будем интерпретировать `-b` как идентичное `-p04`, а `-c` как идентичное `-p037`.
+
+[[x86-pinhole-output]]
+==== Вывод результата
+
+Нам нужно решить, что наше программное обеспечение должно отправлять на вывод и в каком формате.
+
+Поскольку наши входные данные допускают неограниченное количество значений фокусного расстояния, имеет смысл использовать традиционный вывод в стиле базы данных, показывая результат вычислений для каждого фокусного расстояния на отдельной строке, разделяя все значения в строке символом табуляции.
+
+Опционально, мы также должны разрешить пользователю указать использование формата CSV, который мы изучили ранее. В этом случае мы выведем строку с разделёнными запятыми названиями, описывающими каждое поле каждой строки, а затем отобразим результаты как прежде, но заменив `табуляцию` на `запятую`.
+
+Нам нужна опция командной строки для формата CSV. Мы не можем использовать `-c`, потому что это уже означает _использовать константу Коннорса_. По какой-то странной причине многие веб-сайты называют CSV-файлы _"электронными таблицами Excel"_ (хотя формат CSV появился раньше Excel). Поэтому мы будем использовать переключатель `-e`, чтобы указать нашему программному обеспечению, что мы хотим получить вывод в формате CSV.
+
+Мы начнем каждую строку вывода с фокусного расстояния. Это может показаться избыточным сначала, особенно в интерактивном режиме: пользователь вводит фокусное расстояние, а мы его повторяем.
+
+Но пользователь может ввести несколько фокусных расстояний в одной строке. Ввод также может поступать из файла или вывода другой программы. В этом случае пользователь вообще не видит вводимые данные.
+
+Таким же образом, вывод может быть направлен в файл, который мы захотим изучить позже, или на принтер, или стать входными данными для другой программы.
+
+Итак, имеет полный смысл начинать каждую строку с фокусного расстояния, введённого пользователем.
+
+Нет, подождите! Не так, как ввел пользователь. Что, если пользователь введет что-то вроде этого:
+
+[source, shell]
+....
+ 00000000150
+....
+
+Очевидно, нам нужно удалить ведущие нули.
+
+Итак, можно рассмотреть вариант чтения пользовательского ввода как есть, преобразования его в бинарный вид внутри FPU и последующего вывода оттуда.
+
+Но...
+
+Что делать, если пользователь введёт что-то вроде этого:
+
+[source, shell]
+....
+ 17459765723452353453534535353530530534563507309676764423
+....
+
+Ха! Упакованный десятичный формат FPU позволяет нам вводить 18-значные числа. Но пользователь ввёл больше 18 цифр. Как нам обработать это?
+
+Хорошо, мы _могли бы_ изменить наш код, чтобы он читал первые 18 цифр, передавал их в FPU, затем читал ещё, умножал уже имеющееся на вершине стека (TOS) на 10 в степени количества дополнительных цифр, а затем выполнял `сложение` с этим значением.
+
+Да, мы могли бы так поступить. Но в _этой_ программе это было бы нелепо (в другой это могло бы быть как раз тем, что нужно): даже длина окружности Земли, выраженная в миллиметрах, занимает всего 11 цифр. Очевидно, мы не можем построить камеру такого размера (по крайней мере, пока).
+
+Итак, если пользователь вводит такое огромное число, он либо скучает, либо проверяет нас, либо пытается взломать систему, либо играет — делает что угодно, кроме проектирования камеры-обскуры.
+
+Что мы будем делать?
+
+Мы ударим его по лицу, образно говоря:
+
+[source, shell]
+....
+17459765723452353453534535353530530534563507309676764423 ??? ??? ??? ??? ???
+....
+
+Для этого мы просто проигнорируем все ведущие нули. Как только мы найдем ненулевую цифру, мы инициализируем счетчик значением `0` и начнем выполнять три шага:
+
+[.procedure]
+. Отправить цифру на выход.
+. Добавить цифру в буфер, который мы позже используем для создания упакованного десятичного числа, которое можно отправить в FPU.
+. Увеличить счетчик.
+
+Теперь, пока мы выполняем эти три шага, нам также необходимо следить за одним из двух условий:
+
+* Если счётчик превышает 18, мы прекращаем добавление в буфер. Мы продолжаем читать цифры и отправлять их на вывод.
+* Если, или скорее _когда_, следующий вводимый символ не является цифрой, мы завершаем ввод на данный момент.
++
+Между прочим, мы можем просто отбросить нецифровой символ, если это не `+#+`, который необходимо вернуть во входной поток. Он начинает комментарий, поэтому мы должны увидеть его после завершения вывода и начала поиска следующего ввода.
+
+Остается одна непокрытая возможность: если пользователь вводит только ноль (или несколько нулей), мы никогда не найдем ненулевое значение для отображения.
+
+Мы можем определить, что это произошло, когда наш счетчик остается на `0`. В этом случае нам нужно отправить `0` на выход и выполнить еще один "удар по лицу":
+
+[source, shell]
+....
+0 ??? ??? ??? ??? ???
+....
+
+Как только мы определили фокусное расстояние и убедились, что оно корректно (больше `0`, но не превышает 18 цифр), можно рассчитать диаметр отверстия.
+
+Не случайно слово _булавочное ушко_ содержит слово _булавка_. Действительно, многие малые отверстия буквально являются _дырками от булавки_ — отверстиями, аккуратно проделанными остриём булавки.
+
+Вот потому что типичное отверстие очень маленькое. Наша формула дает результат в миллиметрах. Мы умножим его на `1000`, чтобы вывести результат в _микронах_.
+
+На этом этапе нас ожидает ещё одна ловушка: _Излишняя точность._
+
+Да, FPU был разработан для вычислений с высокой точностью. Но мы имеем дело не с вычислениями высокой точности. Мы имеем дело с физикой (конкретно, с оптикой).
+
+Предположим, мы хотим превратить грузовик в камеру-обскуру (мы будем не первыми, кто это сделал!). Допустим, его кузов имеет длину `12` метров, значит, фокусное расстояние равно `12000`. Используя константу Бендера, получаем квадратный корень из `12000`, умноженный на `0.04`, что составляет `4.381780460` миллиметра или `4381.780460` микрона.
+
+Как ни посмотри, результат абсурдно точен. Наш грузовик не имеет _точно_ `12000` миллиметров в длину. Мы не измеряли его длину с такой точностью, поэтому утверждение, что нам нужна отверстие диаметром `4,381780460` миллиметра, мягко говоря, вводит в заблуждение. `4,4` миллиметра будет вполне достаточно.
+
+[NOTE]
+====
+Я "всего лишь" использовал десять цифр в приведенном выше примере. Представьте абсурдность попытки использовать все 18!
+====
+
+Нам нужно ограничить количество значащих цифр в нашем результате. Один из способов сделать это — использовать целое число, представляющее микроны. Таким образом, нашему грузовику потребуется отверстие диаметром `4382` микрона. Глядя на это число, мы всё же решаем, что `4400` микрон, или `4.4` миллиметра, достаточно близко.
+
+Кроме того, мы можем решить, что независимо от размера результата, мы хотим отображать только четыре значащих цифры (или любое другое их количество, конечно). Увы, FPU не поддерживает округление до определенного количества цифр (в конце концов, он воспринимает числа не как десятичные, а как двоичные).
+
+Следовательно, мы должны разработать алгоритм для уменьшения количества значащих цифр.
+
+Вот мой (я думаю, он неуклюжий — если у вас есть вариант лучше, _пожалуйста_, дайте мне знать):
+
+[.procedure]
+. Инициализировать счетчик значением `0`.
+. Пока число больше или равно `10000`, делим его на `10` и увеличиваем счётчик.
+. Вывести результат.
+. Пока счетчик больше `0`, выводить `0` и уменьшать счетчик.
+
+[NOTE]
+====
+`10000` подходит только если вам нужно _четыре_ значащих цифры. Для любого другого количества значащих цифр замените `10000` на `10` в степени, равной количеству значащих цифр.
+====
+
+Мы затем выведем диаметр отверстия в микронах, округлённый до четырёх значащих цифр.
+
+На этом этапе нам известны _фокусное расстояние_ и _диаметр отверстия_. Это означает, что у нас достаточно информации для расчёта _диафрагменного числа_.
+
+Мы отобразим число f, округлённое до четырёх значащих цифр. Скорее всего, само число f мало что нам скажет. Чтобы придать ему больше смысла, мы можем найти ближайшее _нормализованное число f_, то есть ближайшую степень квадратного корня из 2.
+
+Мы делаем это, умножая фактическое значение диафрагмы на само себя, что, конечно же, даст нам его `квадрат`. Затем мы вычислим его логарифм по основанию 2, что намного проще, чем вычисление логарифма по основанию квадратного корня из 2! Мы округлим результат до ближайшего целого числа. Далее мы возведём 2 в полученную степень. На самом деле, FPU предоставляет нам удобный способ сделать это: мы можем использовать код операции `fscale` для "масштабирования" 1, что аналогично ``сдвигу`` целого числа влево. Наконец, мы вычисляем квадратный корень из всего этого и получаем ближайшее нормализованное значение диафрагмы.
+
+Если всё это звучит ошеломляюще — или, возможно, слишком сложно — всё может стать гораздо понятнее, если увидеть код. Вместе это занимает всего 9 инструкций процессора:
+
+[.programlisting]
+....
+fmul st0, st0
+ fld1
+ fld st1
+ fyl2x
+ frndint
+ fld1
+ fscale
+ fsqrt
+ fstp st1
+....
+
+Первая строка, `fmul st0, st0`, возводит в квадрат содержимое TOS (вершина стека, то же что `st`, называется `st0` в nasm). Команда `fld1` помещает `1` на вершину стека.
+
+Следующая строка, `fld st1`, помещает квадрат обратно в TOS. На этом этапе квадрат находится и в `st`, и в `st(2)` (скоро станет ясно, зачем мы оставляем вторую копию в стеке). В `st(1)` содержится `1`.
+
+Далее, `fyl2x` вычисляет логарифм по основанию 2 от `st`, умноженный на `st(1)`. Именно поэтому мы ранее поместили `1` в `st(1)`.
+
+На этом этапе `st` содержит логарифм, который мы только что вычислили, а `st(1)` содержит квадрат фактического значения диафрагменного числа, который мы сохранили для последующего использования.
+
+`frndint` округляет TOS до ближайшего целого числа. `fld1` помещает `1` в стек. `fscale` сдвигает `1`, находящееся на TOS, на значение в `st(1)`, фактически возводя 2 в степень `st(1)`.
+
+Наконец, `fsqrt` вычисляет квадратный корень из результата, т.е. ближайшее нормализованное число f.
+
+У нас теперь есть ближайшее нормализованное число f на вершине стека (TOS), округлённый до ближайшего целого двоичный логарифм в `st(1)` и квадрат фактического число f в `st(2)`. Мы сохраняем значение в `st(2)` для последующего использования.
+
+Но нам больше не нужно содержимое `st(1)`. Последняя строка, `fstp st1`, помещает содержимое `st` в `st(1)` и выполняет извлечение. В результате, то, что было `st(1)`, теперь становится `st`, то, что было `st(2)`, теперь становится `st(1)`, и так далее. Новый `st` содержит нормализованное число f. Новый `st(1)` содержит квадрат фактического число f, который мы сохранили для потомков.
+
+На этом этапе мы готовы вывести нормализованное число f. Поскольку оно нормализовано, мы не будем округлять его до четырёх значащих цифр, а отправим его с полной точностью.
+
+Нормализованное диафрагменное число полезно, пока оно достаточно мало и может быть найдено на нашем экспонометре. В противном случае нам нужен другой метод определения правильной экспозиции.
+
+Ранее мы вывели формулу для расчёта правильной экспозиции при произвольной диафрагме на основе измерений, сделанных при другой диафрагме.
+
+Каждый экспонометр, который я когда-либо видел, может определить правильную экспозицию при f5.6. Поэтому мы рассчитаем _"множитель f5.6"_, то есть насколько нужно умножить экспозицию, измеренную при f5.6, чтобы определить правильную экспозицию для нашей камеры-обскуры.
+
+Из приведённой формулы мы знаем, что этот коэффициент можно вычислить, разделив наше число f (фактическое, а не нормализованное) на `5.6` и возведя результат в квадрат.
+
+Математически, деление квадрата нашего числа f на квадрат `5.6` даст нам тот же результат.
+
+С вычислительной точки зрения, нам не нужно возводить в квадрат два числа, когда можно возвести только одно. Таким образом, первое решение на первый взгляд кажется лучше.
+
+Но...
+
+`5.6` — это _константа_. Нам не нужно заставлять наш FPU тратить драгоценные циклы. Мы можем просто указать ему разделить квадрат f-числа на то, чему равно `5.6²`. Или мы можем разделить f-число на `5.6`, а затем возвести результат в квадрат. Теперь оба способа кажутся равнозначными.
+
+Но они не такие!
+
+Изучив принципы фотографии выше, мы помним, что `5.6` — это квадратный корень из 2, возведённый в пятую степень. Это _иррациональное_ число. Квадрат этого числа _ровно_ `32`.
+
+`32` — это не просто целое число, это степень двойки. Нам не нужно делить квадрат числа f на `32`. Достаточно использовать `fscale` для сдвига вправо на пять позиций. В терминологии FPU это означает, что мы применим `fscale` со значением `st(1)` равным `-5`. Это _гораздо быстрее_, чем деление.
+
+Итак, теперь стало ясно, зачем мы сохранили квадрат числа f на вершине стека FPU. Расчёт множителя для f5.6 — это самое простое вычисление во всей программе! Мы выведем его, округлив до четырёх значащих цифр.
+
+Есть ещё одно полезное число, которое мы можем вычислить: количество ступеней, на которые наше значение диафрагмы отличается от f5.6. Это может помочь, если наше значение диафрагмы находится чуть за пределами диапазона нашего экспонометра, но у нас есть затвор, который позволяет устанавливать различные выдержки, и этот затвор использует ступени.
+
+Предположим, наше число диафрагмы на 5 ступеней отличается от f5.6, а экспонометр показывает, что нужно использовать выдержку 1/1000 сек. Тогда мы можем сначала установить выдержку на 1/1000, а затем повернуть диск на 5 ступеней.
+
+Этот расчет также довольно прост. Все, что нам нужно сделать, это вычислить логарифм по основанию 2 от множителя f5.6, который мы только что рассчитали (хотя нам нужно его значение до округления). Затем мы выводим результат, округленный до ближайшего целого числа. Нам не нужно беспокоиться о наличии более четырех значащих цифр в этом случае: скорее всего, результат будет содержать только одну или две цифры.
+
+[[x86-fpu-optimizations]]
+=== Оптимизации FPU
+
+В ассемблерном коде мы можем оптимизировать инструкции FPU способами, невозможными в языках высокого уровня, включая C.
+
+Всякий раз, когда функции на языке C требуется вычислить значение с плавающей запятой, она загружает все необходимые переменные и константы в регистры FPU. Затем выполняются все необходимые вычисления для получения правильного результата. Хорошие компиляторы C могут очень эффективно оптимизировать эту часть кода.
+
+Он "возвращает" значение, оставляя результат на вершине стека (TOS). Однако перед возвратом он выполняет очистку. Все переменные и константы, использованные в вычислениях, теперь удалены из FPU.
+
+Он не может сделать то, что мы только что сделали выше: мы вычислили квадрат числа f и оставили его в стеке для последующего использования другой функцией.
+
+Мы _знали_, что это значение понадобится позже. Мы также знали, что у нас достаточно места в стеке (в котором помещается только 8 чисел), чтобы сохранить его там.
+
+Компилятор C не может знать, что значение, находящееся в стеке, потребуется снова в ближайшем будущем.
+
+Конечно, программист на C может это знать. Но единственное средство, которое у него есть, — это сохранить значение в переменной памяти.
+
+Это означает, что значение будет изменено с 80-битной точности, используемой внутри FPU, на тип _double_ (64 бита) или даже _single_ (32 бита) в C.
+
+Это также означает, что значение должно быть перемещено из TOS в память, а затем обратно. Увы, среди всех операций с FPU, доступ к памяти компьютера является самым медленным.
+
+Итак, при программировании FPU на языке ассемблера ищите способы хранения промежуточных результатов в стеке FPU.
+
+Мы можем развить эту идею еще дальше! В нашей программе мы используем _константу_ (ту, которую назвали `PC`).
+
+Не имеет значения, сколько диаметров отверстий мы рассчитываем: 1, 10, 20, 1000, мы всегда используем одну и ту же константу. Следовательно, мы можем оптимизировать нашу программу, сохраняя константу в стеке всё время.
+
+В начале нашей программы мы вычисляем значение указанной константы. Нам нужно разделить наш вход на `10` для каждой цифры в константе.
+
+Гораздо быстрее умножать, чем делить. Поэтому в начале нашей программы мы делим `1` на `10`, чтобы получить `0.1`, который затем сохраняем в стеке: вместо того чтобы делить ввод на `10` для каждой цифры, мы умножаем его на `0.1`.
+
+Кстати, мы не вводим `0.1` напрямую, хотя могли бы. У нас есть причина для этого: хотя `0.1` можно выразить всего одним десятичным знаком, мы не знаем, сколько _двоичных_ разрядов для этого потребуется. Поэтому мы позволяем FPU вычислить его двоичное значение с собственной высокой точностью.
+
+Мы используем другие константы: умножаем диаметр отверстия на `1000`, чтобы перевести его из миллиметров в микроны. Мы сравниваем числа с `10000`, когда округляем их до четырёх значащих цифр. Таким образом, мы оставляем и `1000`, и `10000` в стеке. И, конечно же, мы повторно используем `0.1` при округлении чисел до четырёх цифр.
+
+И последнее, но не менее важное: мы оставляем `-5` в стеке. Он нам нужен для масштабирования квадрата числа f вместо деления его на `32`. Не случайно мы загружаем эту константу последней. Это делает её вершиной стека, когда в нём находятся только константы. Таким образом, при масштабировании квадрата число f `-5` находится в `st(1)`, именно там, где `fscale` ожидает его увидеть.
+
+Это обычная ситуация, когда некоторые константы создаются с нуля, вместо загрузки их из памяти. Именно это мы делаем с `-5`:
+
+[.programlisting]
+....
+ fld1 ; TOS = 1
+ fadd st0, st0 ; TOS = 2
+ fadd st0, st0 ; TOS = 4
+ fld1 ; TOS = 1
+ faddp st1, st0 ; TOS = 5
+ fchs ; TOS = -5
+....
+
+Мы можем обобщить все эти оптимизации в одном правиле: _Держите повторяющиеся значения в стеке!_
+
+[TIP]
+====
+_PostScript(R)_ — это стековая язык программирования. Существует гораздо больше книг о PostScript(R), чем о языке ассемблера FPU: освоение PostScript(R) поможет вам овладеть FPU.
+====
+
+[[x86-pinhole-the-code]]
+=== Код pinhole
+
+[.programlisting]
+....
+;;;;;;; pinhole.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Find various parameters of a pinhole camera construction and use
+;
+; Started: 9-Jun-2001
+; Updated: 10-Jun-2001
+;
+; Copyright (c) 2001 G. Adam Stanislav
+; All rights reserved.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+%include 'system.inc'
+
+%define BUFSIZE 2048
+
+section .data
+align 4
+ten dd 10
+thousand dd 1000
+tthou dd 10000
+fd.in dd stdin
+fd.out dd stdout
+envar db 'PINHOLE=' ; Exactly 8 bytes, or 2 dwords long
+pinhole db '04,', ; Bender's constant (0.04)
+connors db '037', 0Ah ; Connors' constant
+usg db 'Usage: pinhole [-b] [-c] [-e] [-p <value>] [-o <outfile>] [-i <infile>]', 0Ah
+usglen equ $-usg
+iemsg db "pinhole: Can't open input file", 0Ah
+iemlen equ $-iemsg
+oemsg db "pinhole: Can't create output file", 0Ah
+oemlen equ $-oemsg
+pinmsg db "pinhole: The PINHOLE constant must not be 0", 0Ah
+pinlen equ $-pinmsg
+toobig db "pinhole: The PINHOLE constant may not exceed 18 decimal places", 0Ah
+biglen equ $-toobig
+huhmsg db 9, '???'
+separ db 9, '???'
+sep2 db 9, '???'
+sep3 db 9, '???'
+sep4 db 9, '???', 0Ah
+huhlen equ $-huhmsg
+header db 'focal length in millimeters,pinhole diameter in microns,'
+ db 'F-number,normalized F-number,F-5.6 multiplier,stops '
+ db 'from F-5.6', 0Ah
+headlen equ $-header
+
+section .bss
+ibuffer resb BUFSIZE
+obuffer resb BUFSIZE
+dbuffer resb 20 ; decimal input buffer
+bbuffer resb 10 ; BCD buffer
+
+section .text
+align 4
+huh:
+ call write
+ push dword huhlen
+ push dword huhmsg
+ push dword [fd.out]
+ sys.write
+ add esp, byte 12
+ ret
+
+align 4
+perr:
+ push dword pinlen
+ push dword pinmsg
+ push dword stderr
+ sys.write
+ push dword 4 ; return failure
+ sys.exit
+
+align 4
+consttoobig:
+ push dword biglen
+ push dword toobig
+ push dword stderr
+ sys.write
+ push dword 5 ; return failure
+ sys.exit
+
+align 4
+ierr:
+ push dword iemlen
+ push dword iemsg
+ push dword stderr
+ sys.write
+ push dword 1 ; return failure
+ sys.exit
+
+align 4
+oerr:
+ push dword oemlen
+ push dword oemsg
+ push dword stderr
+ sys.write
+ push dword 2
+ sys.exit
+
+align 4
+usage:
+ push dword usglen
+ push dword usg
+ push dword stderr
+ sys.write
+ push dword 3
+ sys.exit
+
+align 4
+global _start
+_start:
+ add esp, byte 8 ; discard argc and argv[0]
+ sub esi, esi
+
+.arg:
+ pop ecx
+ or ecx, ecx
+ je near .getenv ; no more arguments
+
+ ; ECX contains the pointer to an argument
+ cmp byte [ecx], '-'
+ jne usage
+
+ inc ecx
+ mov ax, [ecx]
+ inc ecx
+
+.o:
+ cmp al, 'o'
+ jne .i
+
+ ; Make sure we are not asked for the output file twice
+ cmp dword [fd.out], stdout
+ jne usage
+
+ ; Find the path to output file - it is either at [ECX+1],
+ ; i.e., -ofile --
+ ; or in the next argument,
+ ; i.e., -o file
+
+ or ah, ah
+ jne .openoutput
+ pop ecx
+ jecxz usage
+
+.openoutput:
+ push dword 420 ; file mode (644 octal)
+ push dword 0200h | 0400h | 01h
+ ; O_CREAT | O_TRUNC | O_WRONLY
+ push ecx
+ sys.open
+ jc near oerr
+
+ add esp, byte 12
+ mov [fd.out], eax
+ jmp short .arg
+
+.i:
+ cmp al, 'i'
+ jne .p
+
+ ; Make sure we are not asked twice
+ cmp dword [fd.in], stdin
+ jne near usage
+
+ ; Find the path to the input file
+ or ah, ah
+ jne .openinput
+ pop ecx
+ or ecx, ecx
+ je near usage
+
+.openinput:
+ push dword 0 ; O_RDONLY
+ push ecx
+ sys.open
+ jc near ierr ; open failed
+
+ add esp, byte 8
+ mov [fd.in], eax
+ jmp .arg
+
+.p:
+ cmp al, 'p'
+ jne .c
+ or ah, ah
+ jne .pcheck
+
+ pop ecx
+ or ecx, ecx
+ je near usage
+
+ mov ah, [ecx]
+
+.pcheck:
+ cmp ah, '0'
+ jl near usage
+ cmp ah, '9'
+ ja near usage
+ mov esi, ecx
+ jmp .arg
+
+.c:
+ cmp al, 'c'
+ jne .b
+ or ah, ah
+ jne near usage
+ mov esi, connors
+ jmp .arg
+
+.b:
+ cmp al, 'b'
+ jne .e
+ or ah, ah
+ jne near usage
+ mov esi, pinhole
+ jmp .arg
+
+.e:
+ cmp al, 'e'
+ jne near usage
+ or ah, ah
+ jne near usage
+ mov al, ','
+ mov [huhmsg], al
+ mov [separ], al
+ mov [sep2], al
+ mov [sep3], al
+ mov [sep4], al
+ jmp .arg
+
+align 4
+.getenv:
+ ; If ESI = 0, we did not have a -p argument,
+ ; and need to check the environment for "PINHOLE="
+ or esi, esi
+ jne .init
+
+ sub ecx, ecx
+
+.nextenv:
+ pop esi
+ or esi, esi
+ je .default ; no PINHOLE envar found
+
+ ; check if this envar starts with 'PINHOLE='
+ mov edi, envar
+ mov cl, 2 ; 'PINHOLE=' is 2 dwords long
+rep cmpsd
+ jne .nextenv
+
+ ; Check if it is followed by a digit
+ mov al, [esi]
+ cmp al, '0'
+ jl .default
+ cmp al, '9'
+ jbe .init
+ ; fall through
+
+align 4
+.default:
+ ; We got here because we had no -p argument,
+ ; and did not find the PINHOLE envar.
+ mov esi, pinhole
+ ; fall through
+
+align 4
+.init:
+ sub eax, eax
+ sub ebx, ebx
+ sub ecx, ecx
+ sub edx, edx
+ mov edi, dbuffer+1
+ mov byte [dbuffer], '0'
+
+ ; Convert the pinhole constant to real
+.constloop:
+ lodsb
+ cmp al, '9'
+ ja .setconst
+ cmp al, '0'
+ je .processconst
+ jb .setconst
+
+ inc dl
+
+.processconst:
+ inc cl
+ cmp cl, 18
+ ja near consttoobig
+ stosb
+ jmp short .constloop
+
+align 4
+.setconst:
+ or dl, dl
+ je near perr
+
+ finit
+ fild dword [tthou]
+
+ fld1
+ fild dword [ten]
+ fdivp st1, st0
+
+ fild dword [thousand]
+ mov edi, obuffer
+
+ mov ebp, ecx
+ call bcdload
+
+.constdiv:
+ fmul st0, st2
+ loop .constdiv
+
+ fld1
+ fadd st0, st0
+ fadd st0, st0
+ fld1
+ faddp st1, st0
+ fchs
+
+ ; If we are creating a CSV file,
+ ; print header
+ cmp byte [separ], ','
+ jne .bigloop
+
+ push dword headlen
+ push dword header
+ push dword [fd.out]
+ sys.write
+
+.bigloop:
+ call getchar
+ jc near done
+
+ ; Skip to the end of the line if you got '#'
+ cmp al, '#'
+ jne .num
+ call skiptoeol
+ jmp short .bigloop
+
+.num:
+ ; See if you got a number
+ cmp al, '0'
+ jl .bigloop
+ cmp al, '9'
+ ja .bigloop
+
+ ; Yes, we have a number
+ sub ebp, ebp
+ sub edx, edx
+
+.number:
+ cmp al, '0'
+ je .number0
+ mov dl, 1
+
+.number0:
+ or dl, dl ; Skip leading 0's
+ je .nextnumber
+ push eax
+ call putchar
+ pop eax
+ inc ebp
+ cmp ebp, 19
+ jae .nextnumber
+ mov [dbuffer+ebp], al
+
+.nextnumber:
+ call getchar
+ jc .work
+ cmp al, '#'
+ je .ungetc
+ cmp al, '0'
+ jl .work
+ cmp al, '9'
+ ja .work
+ jmp short .number
+
+.ungetc:
+ dec esi
+ inc ebx
+
+.work:
+ ; Now, do all the work
+ or dl, dl
+ je near .work0
+
+ cmp ebp, 19
+ jae near .toobig
+
+ call bcdload
+
+ ; Calculate pinhole diameter
+
+ fld st0 ; save it
+ fsqrt
+ fmul st0, st3
+ fld st0
+ fmul st5
+ sub ebp, ebp
+
+ ; Round off to 4 significant digits
+.diameter:
+ fcom st0, st7
+ fstsw ax
+ sahf
+ jb .printdiameter
+ fmul st0, st6
+ inc ebp
+ jmp short .diameter
+
+.printdiameter:
+ call printnumber ; pinhole diameter
+
+ ; Calculate F-number
+
+ fdivp st1, st0
+ fld st0
+
+ sub ebp, ebp
+
+.fnumber:
+ fcom st0, st6
+ fstsw ax
+ sahf
+ jb .printfnumber
+ fmul st0, st5
+ inc ebp
+ jmp short .fnumber
+
+.printfnumber:
+ call printnumber ; F number
+
+ ; Calculate normalized F-number
+ fmul st0, st0
+ fld1
+ fld st1
+ fyl2x
+ frndint
+ fld1
+ fscale
+ fsqrt
+ fstp st1
+
+ sub ebp, ebp
+ call printnumber
+
+ ; Calculate time multiplier from F-5.6
+
+ fscale
+ fld st0
+
+ ; Round off to 4 significant digits
+.fmul:
+ fcom st0, st6
+ fstsw ax
+ sahf
+
+ jb .printfmul
+ inc ebp
+ fmul st0, st5
+ jmp short .fmul
+
+.printfmul:
+ call printnumber ; F multiplier
+
+ ; Calculate F-stops from 5.6
+
+ fld1
+ fxch st1
+ fyl2x
+
+ sub ebp, ebp
+ call printnumber
+
+ mov al, 0Ah
+ call putchar
+ jmp .bigloop
+
+.work0:
+ mov al, '0'
+ call putchar
+
+align 4
+.toobig:
+ call huh
+ jmp .bigloop
+
+align 4
+done:
+ call write ; flush output buffer
+
+ ; close files
+ push dword [fd.in]
+ sys.close
+
+ push dword [fd.out]
+ sys.close
+
+ finit
+
+ ; return success
+ push dword 0
+ sys.exit
+
+align 4
+skiptoeol:
+ ; Keep reading until you come to cr, lf, or eof
+ call getchar
+ jc done
+ cmp al, 0Ah
+ jne .cr
+ ret
+
+.cr:
+ cmp al, 0Dh
+ jne skiptoeol
+ ret
+
+align 4
+getchar:
+ or ebx, ebx
+ jne .fetch
+
+ call read
+
+.fetch:
+ lodsb
+ dec ebx
+ clc
+ ret
+
+read:
+ jecxz .read
+ call write
+
+.read:
+ push dword BUFSIZE
+ mov esi, ibuffer
+ push esi
+ push dword [fd.in]
+ sys.read
+ add esp, byte 12
+ mov ebx, eax
+ or eax, eax
+ je .empty
+ sub eax, eax
+ ret
+
+align 4
+.empty:
+ add esp, byte 4
+ stc
+ ret
+
+align 4
+putchar:
+ stosb
+ inc ecx
+ cmp ecx, BUFSIZE
+ je write
+ ret
+
+align 4
+write:
+ jecxz .ret ; nothing to write
+ sub edi, ecx ; start of buffer
+ push ecx
+ push edi
+ push dword [fd.out]
+ sys.write
+ add esp, byte 12
+ sub eax, eax
+ sub ecx, ecx ; buffer is empty now
+.ret:
+ ret
+
+align 4
+bcdload:
+ ; EBP contains the number of chars in dbuffer
+ push ecx
+ push esi
+ push edi
+
+ lea ecx, [ebp+1]
+ lea esi, [dbuffer+ebp-1]
+ shr ecx, 1
+
+ std
+
+ mov edi, bbuffer
+ sub eax, eax
+ mov [edi], eax
+ mov [edi+4], eax
+ mov [edi+2], ax
+
+.loop:
+ lodsw
+ sub ax, 3030h
+ shl al, 4
+ or al, ah
+ mov [edi], al
+ inc edi
+ loop .loop
+
+ fbld [bbuffer]
+
+ cld
+ pop edi
+ pop esi
+ pop ecx
+ sub eax, eax
+ ret
+
+align 4
+printnumber:
+ push ebp
+ mov al, [separ]
+ call putchar
+
+ ; Print the integer at the TOS
+ mov ebp, bbuffer+9
+ fbstp [bbuffer]
+
+ ; Check the sign
+ mov al, [ebp]
+ dec ebp
+ or al, al
+ jns .leading
+
+ ; We got a negative number (should never happen)
+ mov al, '-'
+ call putchar
+
+.leading:
+ ; Skip leading zeros
+ mov al, [ebp]
+ dec ebp
+ or al, al
+ jne .first
+ cmp ebp, bbuffer
+ jae .leading
+
+ ; We are here because the result was 0.
+ ; Print '0' and return
+ mov al, '0'
+ jmp putchar
+
+.first:
+ ; We have found the first non-zero.
+ ; But it is still packed
+ test al, 0F0h
+ jz .second
+ push eax
+ shr al, 4
+ add al, '0'
+ call putchar
+ pop eax
+ and al, 0Fh
+
+.second:
+ add al, '0'
+ call putchar
+
+.next:
+ cmp ebp, bbuffer
+ jb .done
+
+ mov al, [ebp]
+ push eax
+ shr al, 4
+ add al, '0'
+ call putchar
+ pop eax
+ and al, 0Fh
+ add al, '0'
+ call putchar
+
+ dec ebp
+ jmp short .next
+
+.done:
+ pop ebp
+ or ebp, ebp
+ je .ret
+
+.zeros:
+ mov al, '0'
+ call putchar
+ dec ebp
+ jne .zeros
+
+.ret:
+ ret
+....
+
+Код следует тому же формату, что и все остальные фильтры, которые мы видели ранее, с одним небольшим исключением:
+
+____
+Мы больше не предполагаем, что конец ввода означает конец задач, как мы привыкли в фильтрах, _ориентированных на символы_.
+
+Этот фильтр не обрабатывает символы. Он обрабатывает _язык_ (хотя и очень простой, состоящий только из чисел).
+
+Когда у нас больше нет входных данных, это может означать одно из двух:
+
+* Мы закончили и можем выйти. Это то же самое, что и раньше.
+* Последний прочитанный символ был цифрой. Мы сохранили его в конце буфера преобразования ASCII в число с плавающей точкой. Теперь нам нужно преобразовать содержимое этого буфера в число и записать последнюю строку нашего вывода.
+
+По этой причине мы изменили наши подпрограммы `getchar` и `read`, чтобы они возвращались с _сброшенным_ флагом `carry`, когда получают очередной символ из ввода, или с _установленным_ флагом `carry`, когда ввода больше нет.
+
+Конечно, мы по-прежнему используем магию ассемблера для этого! Внимательно посмотрите на `getchar`. Он _всегда_ возвращает _очищенный_ `флаг переноса`.
+
+Тем не менее, наш основной код использует `флаг переноса` для определения момента завершения — и это работает.
+
+Волшебство кроется в `read`. Каждый раз, когда он получает больше входных данных от системы, он просто возвращается к `getchar`, который извлекает символ из входного буфера, _сбрасывает_ флаг переноса (`carry flag`) и возвращает управление.
+
+Но когда `read` больше не получает входных данных от системы, он _не_ возвращается к `getchar` вообще. Вместо этого, инструкция `add esp, byte 4` добавляет `4` к `ESP`, _устанавливает_ флаг переноса (`carry flag`) и возвращает управление.
+
+Итак, куда же она возвращается? Каждый раз, когда программа использует операцию `call`, микропроцессор делает ``push`` для адрес возврата, то есть сохраняет его на вершине стека (не стека FPU, а системного стека, который находится в памяти). Когда программа использует операцию `ret`, микропроцессор делает ``pop`` для значения возврата из стека и переходит по адресу, который там был сохранён.
+
+Но поскольку мы добавили `4` к `ESP` (который является регистром указателя стека), мы фактически вызвали у микропроцессора лёгкий случай _амнезии_: он больше не помнит, что именно `getchar` ``вызвал`` `read`.
+
+И поскольку `getchar` не делал ``push`` ни для чего перед вызовом `read`, верхушка стека теперь содержит адрес возврата к тому, что или кто вызывал `getchar`. С точки зрения этого вызывающего, он вызывал `getchar`, который вызвал ``ret`` с установленным `флагом переноса`!
+____
+
+Помимо этого, процедура `bcdload` оказывается втянута в лилипутский конфликт между Биг-Эндианцами и Литл-Эндианцами.
+
+Он преобразует текстовое представление числа в само число: текст хранится в порядке big-endian, но _упакованный десятичный_ формат имеет порядок little-endian.
+
+Для разрешения конфликта мы используем инструкцию процессора `std` в самом начале. Позже мы отменяем его с помощью `cld`: очень важно не вызывать ничего, что может зависеть от стандартного значения _флага направления_, пока активен `std`.
+
+Всё остальное в этом коде должно быть достаточно понятным, при условии, что вы прочитали всю предшествующую главу.
+
+Это классический пример поговорки о том, что программирование требует много размышлений и лишь немного кодирования. Как только мы продумаем каждую мельчайшую деталь, код практически напишется сам.
+
+[[x86-pinhole-using]]
+=== Использование программы pinhole
+
+Поскольку мы решили сделать так, чтобы программа _игнорировала_ любой ввод, кроме чисел (и даже их внутри комментария), мы можем выполнять _текстовые запросы_. Мы не _обязаны_ этого делать, но _можем_.
+
+По моему скромному мнению, формирование текстового запроса вместо необходимости следовать очень строгому синтаксису делает программное обеспечение гораздо более дружелюбным к пользователю.
+
+Предположим, мы хотим построить камеру-обскуру для использования плёнки размером 4x5 дюймов. Стандартное фокусное расстояние для такой плёнки составляет около 150 мм. Мы хотим _точно настроить_ фокусное расстояние, чтобы диаметр отверстия был как можно более круглым числом. Допустим также, что мы хорошо разбираемся в фотоаппаратах, но немного боимся компьютеров. Вместо того чтобы просто вводить кучу цифр, мы хотим _задать_ пару вопросов.
+
+Наша сессия может выглядеть так:
+
+[source, shell]
+....
+% pinhole
+
+Computer,
+
+What size pinhole do I need for the focal length of 150?
+150 490 306 362 2930 12
+Hmmm... How about 160?
+160 506 316 362 3125 12
+Let's make it 155, please.
+155 498 311 362 3027 12
+Ah, let's try 157...
+157 501 313 362 3066 12
+156?
+156 500 312 362 3047 12
+That's it! Perfect! Thank you very much!
+^D
+....
+
+Мы выяснили, что при фокусном расстоянии 150 мм диаметр отверстия должен составлять 490 микрон, или 0,49 мм, но если взять почти идентичное фокусное расстояние 156 мм, можно использовать отверстие диаметром ровно половину миллиметра.
+
+[[x86-pinhole-scripting]]
+=== Скриптинг
+
+Поскольку мы выбрали символ `+#+` для обозначения начала комментария, мы можем рассматривать наше программное обеспечение pinhole как _скриптовый язык_.
+
+Вы, вероятно, видели _сценарии_ оболочки, которые начинаются с:
+
+[.programlisting]
+....
+#! /bin/sh
+....
+
+...или...
+
+[.programlisting]
+....
+#!/bin/sh
+....
+
+...потому что пробел после `#!` необязателен.
+
+Когда UNIX(R) получает запрос на выполнение исполняемого файла, который начинается с `#!`, он предполагает, что это скрипт. Он добавляет команду к остальной части первой строки скрипта и пытается выполнить её.
+
+Предположим, что мы установили pinhole в /usr/local/bin/, теперь мы можем написать скрипт для расчёта различных диаметров отверстий, подходящих для различных фокусных расстояний, обычно используемых с плёнкой 120.
+
+Скрипт может выглядеть примерно так:
+
+[.programlisting]
+....
+#! /usr/local/bin/pinhole -b -i
+# Find the best pinhole diameter
+# for the 120 film
+
+### Standard
+80
+
+### Wide angle
+30, 40, 50, 60, 70
+
+### Telephoto
+100, 120, 140
+....
+
+Поскольку 120 — это плёнка среднего размера, мы можем назвать этот файл `medium`.
+
+Мы можем установить права на выполнение и запустить его, как если бы это была программа:
+
+[source, shell]
+....
+% chmod 755 medium
+% ./medium
+....
+
+UNIX(R) интерпретирует последнюю команду следующим образом:
+
+[source, shell]
+....
+% /usr/local/bin/pinhole -b -i ./medium
+....
+
+Он выполнит эту команду и отобразит:
+
+[source, shell]
+....
+80 358 224 256 1562 11
+30 219 137 128 586 9
+40 253 158 181 781 10
+50 283 177 181 977 10
+60 310 194 181 1172 10
+70 335 209 181 1367 10
+100 400 250 256 1953 11
+120 438 274 256 2344 11
+140 473 296 256 2734 11
+....
+
+Теперь введем:
+
+[source, shell]
+....
+% ./medium -c
+....
+
+UNIX(R) интерпретирует это следующим образом:
+
+[source, shell]
+....
+% /usr/local/bin/pinhole -b -i ./medium -c
+....
+
+Это дает ему два конфликтующих параметра: `-b` и `-c` (Использовать константу Бендера и использовать константу Коннорса). Мы запрограммировали его так, что более поздние параметры переопределяют ранние — наша программа будет вычислять все, используя константу Коннорса:
+
+[source, shell]
+....
+80 331 242 256 1826 11
+30 203 148 128 685 9
+40 234 171 181 913 10
+50 262 191 181 1141 10
+60 287 209 181 1370 10
+70 310 226 256 1598 11
+100 370 270 256 2283 11
+120 405 296 256 2739 11
+140 438 320 362 3196 12
+....
+
+Мы решаем, что всё же выбираем константу Бендера. Мы хотим сохранить её значения в виде файла с разделителями-запятыми:
+
+[source, shell]
+....
+% ./medium -b -e > bender
+% cat bender
+focal length in millimeters,pinhole diameter in microns,F-number,normalized F-number,F-5.6 multiplier,stops from F-5.6
+80,358,224,256,1562,11
+30,219,137,128,586,9
+40,253,158,181,781,10
+50,283,177,181,977,10
+60,310,194,181,1172,10
+70,335,209,181,1367,10
+100,400,250,256,1953,11
+120,438,274,256,2344,11
+140,473,296,256,2734,11
+%
+....
+
+[[x86-caveats]]
+== Предостережения
+
+Программисты на ассемблере, которые "выросли" на MS-DOS(R) и Windows(R), часто склонны искать короткие пути. Чтение скан-кодов клавиатуры и запись напрямую в видеопамять — это два классических примера подходов, которые в MS-DOS(R) не только не порицаются, но и считаются правильными.
+
+Причина? И BIOS ПК, и MS-DOS(R) печально известны своей медленной работой при выполнении этих операций.
+
+Вас может возникнуть соблазн продолжить подобные практики в среде UNIX(R). Например, я видел веб-сайт, который объясняет, как получить доступ к скан-кодам клавиатуры на популярном клоне UNIX(R).
+
+Это, как правило, *очень плохая идея* в среде UNIX(R)! Позвольте объяснить почему.
+
+[[x86-protected]]
+=== UNIX(R) защищен
+
+Прежде всего, это может быть просто невозможно. UNIX(R) работает в защищённом режиме. Только ядро и драйверы устройств имеют прямой доступ к аппаратному обеспечению. Возможно, какой-то конкретный клон UNIX(R) позволит вам читать скан-коды клавиатуры, но скорее всего настоящая операционная система UNIX(R) этого не допустит. И даже если одна версия разрешает это, следующая может запретить, так что ваше тщательно разработанное программное обеспечение может в одночасье устареть.
+
+[[x86-abstraction]]
+=== UNIX(R) — это работа с абстракциями
+
+Но существует гораздо более важная причина не пытаться обращаться к оборудованию напрямую (если, конечно, вы не пишете драйвер устройства), даже в UNIX(R)-подобных системах, которые позволяют это делать:
+
+_UNIX(R) — это работа с абстракциями!_
+
+Существует фундаментальное различие в философии проектирования между MS-DOS(R) и UNIX(R). MS-DOS(R) разрабатывалась как однопользовательская система. Она работает на компьютере, к которому напрямую подключены клавиатура и монитор. Ввод пользователя практически гарантированно поступает с этой клавиатуры. Вывод вашей программы почти всегда отображается на этом экране.
+
+Это НИКОГДА не гарантируется в UNIX(R). Довольно часто пользователь UNIX(R) перенаправляет ввод и вывод программы с помощью конвейеров и перенаправлений:
+
+[source, shell]
+....
+% program1 | program2 | program3 > file1
+....
+
+Если вы написали program2, ваш ввод поступает не с клавиатуры, а из вывода program1. Аналогично, ваш вывод не выводится на экран, а становится вводом для program3, чей вывод, в свою очередь, отправляется в [.filename]#file1#.
+
+Но это еще не все! Даже если вы убедились, что ваш ввод поступает с терминала, а вывод отправляется на терминал, нет гарантии, что терминал является ПК: его видеопамять может находиться не там, где вы ожидаете, а клавиатура может генерировать не PC-совместимые скан-коды. Это может быть Macintosh(R) или любой другой компьютер.
+
+Вот вы, возможно, покачаете головой: мое программное обеспечение написано на языке ассемблера для ПК, как оно может работать на Macintosh(R)? Но я не говорил, что ваше программное обеспечение будет работать на Macintosh(R), а лишь что его терминалом может быть Macintosh(R).
+
+В UNIX(R) терминал не обязательно должен быть напрямую подключён к компьютеру, на котором работает ваше программное обеспечение — он может находиться даже на другом континенте или, например, на другой планете. Вполне возможно, что пользователь Macintosh(R) в Австралии подключается к системе UNIX(R) в Северной Америке (или где-либо ещё) через telnet. Программное обеспечение работает на одном компьютере, а терминал находится на другом: если попытаться считать скан-коды, будут получены неверные данные!
+
+То же самое относится и к любому другому оборудованию: файл, который вы читаете, может находиться на диске, к которому у вас нет прямого доступа. Камера, с которой вы считываете изображения, может находиться на космическом корабле, соединённом с вами через спутники.
+
+Вот почему в UNIX(R) никогда нельзя делать никаких предположений о том, откуда поступают ваши данные и куда они направляются. Всегда позволяйте системе управлять физическим доступом к оборудованию.
+
+[NOTE]
+====
+Это предостережения, а не абсолютные правила. Возможны исключения. Например, если текстовый редактор определил, что работает на локальной машине, он может захотеть читать скан-коды напрямую для улучшенного управления. Я упоминаю эти предостережения не для того, чтобы сказать вам, что делать или чего не делать, а просто чтобы вы осознавали определённые подводные камни, которые ждут вас, если вы только что перешли с MS-DOS(R) на UNIX(R). Конечно, творческие люди часто нарушают правила, и это нормально, пока они осознают, что нарушают их, и понимают почему.
+====
+
+[[x86-acknowledgements]]
+== Благодарности
+
+Это руководство никогда бы не было создано без помощи многих опытных программистов FreeBSD из {freebsd-hackers}, которые терпеливо отвечали на мои вопросы и направляли меня в моих попытках изучить внутренние механизмы программирования в системе UNIX(R) в целом и в FreeBSD в частности.
+
+Томас М. Соммерс открыл дверь для меня. Его https://web.archive.org/web/20090914064615/http://www.codebreakers-journal.com/content/view/262/27[Как написать "Hello, world" на ассемблере в FreeBSD?] веб-страница стала моей первой встречей с примером программирования на ассемблере под FreeBSD.
+
+Джейк Буркхолдер держал дверь открытой, охотно отвечая на все мои вопросы и предоставляя примеры исходного кода на языке ассемблера.
+
+Copyright (R) 2000-2001 G. Adam Stanislav. All rights reserved.
diff --git a/documentation/content/ru/books/developers-handbook/x86/_index.po b/documentation/content/ru/books/developers-handbook/x86/_index.po
new file mode 100644
index 0000000000..325770cdce
--- /dev/null
+++ b/documentation/content/ru/books/developers-handbook/x86/_index.po
@@ -0,0 +1,11160 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR The FreeBSD Project
+# This file is distributed under the same license as the FreeBSD Documentation package.
+# Vladlen Popolitov <vladlenpopolitov@list.ru>, 2025.
+msgid ""
+msgstr ""
+"Project-Id-Version: FreeBSD Documentation VERSION\n"
+"POT-Creation-Date: 2025-10-12 22:16+0300\n"
+"PO-Revision-Date: 2025-09-26 04:45+0000\n"
+"Last-Translator: Vladlen Popolitov <vladlenpopolitov@list.ru>\n"
+"Language-Team: Russian <https://translate-dev.freebsd.org/projects/"
+"documentation/booksdevelopers-handbookx86_index/ru/>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. type: Title =
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:15
+#, no-wrap
+msgid "x86 Assembly Language Programming"
+msgstr "Программирование на ассемблере x86"
+
+#. type: Yaml Front Matter Hash Value: title
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1
+#, no-wrap
+msgid "Chapter 11. x86 Assembly Language Programming"
+msgstr "Глава 11. Программирование на языке ассемблера для x86"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:53
+msgid "_This chapter was written by {stanislav}._"
+msgstr "_Эта глава была написана {stanislav}._"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:55
+#, no-wrap
+msgid "Synopsis"
+msgstr "Обзор"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:59
+msgid ""
+"Assembly language programming under UNIX(R) is highly undocumented. It is "
+"generally assumed that no one would ever want to use it because various "
+"UNIX(R) systems run on different microprocessors, so everything should be "
+"written in C for portability."
+msgstr ""
+"Программирование на ассемблере в UNIX(R) крайне плохо документировано. "
+"Обычно предполагается, что никто не захочет его использовать, поскольку "
+"различные системы UNIX(R) работают на разных микропроцессорах, и поэтому всё "
+"должно быть написано на C для обеспечения переносимости."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:63
+msgid ""
+"In reality, C portability is quite a myth. Even C programs need to be "
+"modified when ported from one UNIX(R) to another, regardless of what "
+"processor each runs on. Typically, such a program is full of conditional "
+"statements depending on the system it is compiled for."
+msgstr ""
+"В действительности переносимость программ на C — это скорее миф. Даже "
+"программы на C требуют изменений при переносе с одной UNIX(R)-системы на "
+"другую, независимо от процессора, на котором они работают. Обычно такая "
+"программа содержит множество условных операторов, зависящих от системы, для "
+"которой она компилируется."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:65
+msgid ""
+"Even if we believe that all of UNIX(R) software should be written in C, or "
+"some other high-level language, we still need assembly language programmers: "
+"Who else would write the section of C library that accesses the kernel?"
+msgstr ""
+"Даже если мы считаем, что всё программное обеспечение UNIX(R) должно быть "
+"написано на C или другом языке высокого уровня, нам всё равно нужны "
+"программисты на ассемблере: кто же ещё напишет часть библиотеки C, которая "
+"обращается к ядру?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:67
+msgid ""
+"In this chapter I will attempt to show you how you can use assembly language "
+"writing UNIX(R) programs, specifically under FreeBSD."
+msgstr ""
+"В этой главе я попытаюсь показать вам, как можно использовать язык "
+"ассемблера для написания программ под UNIX(R), в частности под FreeBSD."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:72
+msgid ""
+"This chapter does not explain the basics of assembly language. There are "
+"enough resources about that (for a complete online course in assembly "
+"language, see Randall Hyde's http://webster.cs.ucr.edu/[Art of Assembly "
+"Language]; or if you prefer a printed book, take a look at Jeff Duntemann's "
+"Assembly Language Step-by-Step (ISBN: 0471375233). However, once the "
+"chapter is finished, any assembly language programmer will be able to write "
+"programs for FreeBSD quickly and efficiently."
+msgstr ""
+"В этой главе не объясняются основы языка ассемблера. Существует достаточно "
+"ресурсов на эту тему (например, полный онлайн-курс по языку ассемблера можно "
+"найти в http://webster.cs.ucr.edu/[Искусстве языка ассемблера] Рэндалла "
+"Хайда; если вы предпочитаете печатные книги, обратите внимание на «Язык "
+"ассемблера шаг за шагом» Джеффа Дантемана (ISBN: 0471375233)). Однако после "
+"прочтения этой главы любой программист на языке ассемблера сможет писать "
+"программы для FreeBSD быстро и эффективно."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:74
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4311
+msgid "Copyright (R) 2000-2001 G. Adam Stanislav. All rights reserved."
+msgstr "Copyright (R) 2000-2001 G. Adam Stanislav. All rights reserved."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:76
+#, no-wrap
+msgid "The Tools"
+msgstr "Инструменты"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:79
+#, no-wrap
+msgid "The Assembler"
+msgstr "Ассемблер"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:82
+msgid ""
+"The most important tool for assembly language programming is the assembler, "
+"the software that converts assembly language code into machine language."
+msgstr ""
+"Важнейшим инструментом для программирования на языке ассемблера является "
+"ассемблер — программа, преобразующая код на языке ассемблера в машинный код."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:85
+msgid ""
+"Three very different assemblers are available for FreeBSD. Both man:llvm-"
+"as[1] (included in package:devel/llvm[]) and man:as[1] (included in "
+"package:devel/binutils[]) use the traditional UNIX(R) assembly language "
+"syntax."
+msgstr ""
+"Три очень разных ассемблера доступны для FreeBSD. И man:llvm-as[1] (включён "
+"в package:devel/llvm[]), и man:as[1] (включён в package:devel/binutils[]) "
+"используют традиционный синтаксис ассемблера UNIX(R)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:88
+msgid ""
+"On the other hand, man:nasm[1] (installed through package:devel/nasm[]) uses "
+"the Intel syntax. Its main advantage is that it can assemble code for many "
+"operating systems."
+msgstr ""
+"С другой стороны, man:nasm[1] (устанавливаемый через package:devel/nasm[]) "
+"использует синтаксис Intel. Его основное преимущество в том, что он может "
+"ассемблировать код для многих операционных систем."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:91
+msgid ""
+"This chapter uses nasm syntax because most assembly language programmers "
+"coming to FreeBSD from other operating systems will find it easier to "
+"understand. And, because, quite frankly, that is what I am used to."
+msgstr ""
+"В этой главе используется синтаксис nasm, потому что большинство "
+"программистов на ассемблере, приходящих в FreeBSD из других операционных "
+"систем, найдут его более понятным. Кроме того, если честно, это то, к чему я "
+"привык."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:93
+#, no-wrap
+msgid "The Linker"
+msgstr "Компоновщик"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:96
+msgid ""
+"The output of the assembler, like that of any compiler, needs to be linked "
+"to form an executable file."
+msgstr ""
+"Результат работы ассемблера, как и любого компилятора, необходимо связать, "
+"чтобы получить исполняемый файл."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:99
+msgid ""
+"The standard man:ld[1] linker comes with FreeBSD. It works with the code "
+"assembled with either assembler."
+msgstr ""
+"Стандартный компоновщик man:ld[1] поставляется с FreeBSD. Он работает с "
+"кодом, собранным любым из ассемблеров."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:101
+#, no-wrap
+msgid "System Calls"
+msgstr "Системные вызовы"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:104
+#, no-wrap
+msgid "Default Calling Convention"
+msgstr "Стандартное соглашение о вызовах"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:108
+msgid ""
+"By default, the FreeBSD kernel uses the C calling convention. Further, "
+"although the kernel is accessed using `int 80h`, it is assumed the program "
+"will call a function that issues `int 80h`, rather than issuing `int 80h` "
+"directly."
+msgstr ""
+"По умолчанию ядро FreeBSD использует соглашение о вызовах C. Кроме того, "
+"хотя доступ к ядру осуществляется с помощью `int 80h`, предполагается, что "
+"программа вызовет функцию, которая выполняет `int 80h`, а не будет выполнять "
+"`int 80h` напрямую."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:111
+msgid ""
+"This convention is very convenient, and quite superior to the Microsoft(R) "
+"convention used by MS-DOS(R). Why? Because the UNIX(R) convention allows "
+"any program written in any language to access the kernel."
+msgstr ""
+"Эта традиция очень удобна и значительно превосходит соглашение Microsoft(R), "
+"используемое в MS-DOS(R). Почему? Потому что соглашение UNIX(R) позволяет "
+"любой программе, написанной на любом языке, обращаться к ядру."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:114
+msgid ""
+"An assembly language program can do that as well. For example, we could "
+"open a file:"
+msgstr ""
+"Программа на ассемблере также может это сделать. Например, мы могли бы "
+"открыть файл:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:120
+#, no-wrap
+msgid ""
+"kernel:\n"
+"\tint\t80h\t; Call kernel\n"
+"\tret\n"
+msgstr ""
+"kernel:\n"
+"\tint\t80h\t; Call kernel\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:129
+#, no-wrap
+msgid ""
+"open:\n"
+"\tpush\tdword mode\n"
+"\tpush\tdword flags\n"
+"\tpush\tdword path\n"
+"\tmov\teax, 5\n"
+"\tcall\tkernel\n"
+"\tadd\tesp, byte 12\n"
+"\tret\n"
+msgstr ""
+"open:\n"
+"\tpush\tdword mode\n"
+"\tpush\tdword flags\n"
+"\tpush\tdword path\n"
+"\tmov\teax, 5\n"
+"\tcall\tkernel\n"
+"\tadd\tesp, byte 12\n"
+"\tret\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:133
+msgid ""
+"This is a very clean and portable way of coding. If you need to port the "
+"code to a UNIX(R) system which uses a different interrupt, or a different "
+"way of passing parameters, all you need to change is the kernel procedure."
+msgstr ""
+"Это очень понятный и переносимый способ написания кода. Если вам нужно "
+"перенести код на UNIX(R)-систему, которая использует другое прерывание или "
+"другой способ передачи параметров, все, что вам нужно изменить, это "
+"процедуру kernel."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:137
+msgid ""
+"But assembly language programmers like to shave off cycles. The above "
+"example requires a `call/ret` combination. We can eliminate it by "
+"``push``ing an extra dword:"
+msgstr ""
+"Но программисты на ассемблере любят экономить такты. Приведённый выше пример "
+"требует комбинации `call/ret`. Мы можем исключить её, сделав ``push`` "
+"дополнительного двойного слова:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:148
+#, no-wrap
+msgid ""
+"open:\n"
+"\tpush\tdword mode\n"
+"\tpush\tdword flags\n"
+"\tpush\tdword path\n"
+"\tmov\teax, 5\n"
+"\tpush\teax\t\t; Or any other dword\n"
+"\tint\t80h\n"
+"\tadd\tesp, byte 16\n"
+msgstr ""
+"open:\n"
+"\tpush\tdword mode\n"
+"\tpush\tdword flags\n"
+"\tpush\tdword path\n"
+"\tmov\teax, 5\n"
+"\tpush\teax\t\t; Or any other dword\n"
+"\tint\t80h\n"
+"\tadd\tesp, byte 16\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:151
+msgid ""
+"The `5` that we have placed in `EAX` identifies the kernel function, in this "
+"case `open`."
+msgstr ""
+"Помещённое в `EAX` значение `5` идентифицирует функцию ядра, в данном случае "
+"`open`."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:153
+#, no-wrap
+msgid "Alternate Calling Convention"
+msgstr "Альтернативное соглашение о вызовах"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:158
+msgid ""
+"FreeBSD is an extremely flexible system. It offers other ways of calling "
+"the kernel. For it to work, however, the system must have Linux emulation "
+"installed."
+msgstr ""
+"FreeBSD — это чрезвычайно гибкая система. Она предлагает другие способы "
+"вызова ядра. Однако для работы необходимо, чтобы в системе была установлена "
+"эмуляция Linux."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:163
+msgid ""
+"Linux is a UNIX(R) like system. However, its kernel uses the same system-"
+"call convention of passing parameters in registers MS-DOS(R) does. As with "
+"the UNIX(R) convention, the function number is placed in `EAX`. The "
+"parameters, however, are not passed on the stack but in `EBX, ECX, EDX, ESI, "
+"EDI, EBP`:"
+msgstr ""
+"Linux — это система, подобная UNIX(R). Однако ее ядро использует то же "
+"соглашение о системных вызовов для передачи параметров в регистрах, что и MS-"
+"DOS(R). Как и в соглашении UNIX(R), номер функции помещается в `EAX`. Однако "
+"параметры передаются не в стеке, а в регистрах `EBX, ECX, EDX, ESI, EDI, "
+"EBP`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:172
+#, no-wrap
+msgid ""
+"open:\n"
+"\tmov\teax, 5\n"
+"\tmov\tebx, path\n"
+"\tmov\tecx, flags\n"
+"\tmov\tedx, mode\n"
+"\tint\t80h\n"
+msgstr ""
+"open:\n"
+"\tmov\teax, 5\n"
+"\tmov\tebx, path\n"
+"\tmov\tecx, flags\n"
+"\tmov\tedx, mode\n"
+"\tint\t80h\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:178
+msgid ""
+"This convention has a great disadvantage over the UNIX(R) way, at least as "
+"far as assembly language programming is concerned: Every time you make a "
+"kernel call you must `push` the registers, then `pop` them later. This "
+"makes your code bulkier and slower. Nevertheless, FreeBSD gives you a "
+"choice."
+msgstr ""
+"Этот подход имеет значительный недостаток по сравнению с UNIX(R), по крайней "
+"мере, в контексте программирования на ассемблере: каждый раз при вызове ядра "
+"необходимо сохранять регистры с помощью `push`, а затем восстанавливать их с "
+"помощью `pop`. Это делает ваш код более громоздким и медленным. Тем не "
+"менее, FreeBSD предоставляет вам выбор."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:181
+msgid ""
+"If you do choose the Linux convention, you must let the system know about "
+"it. After your program is assembled and linked, you need to brand the "
+"executable:"
+msgstr ""
+"Если вы решите использовать соглашение Linux, вы должны сообщить об этом "
+"системе. После того как ваша программа будет ассемблирована и слинкована, "
+"вам нужно пометить исполняемый файл:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:185
+#, no-wrap
+msgid "% brandelf -t Linux filename\n"
+msgstr "% brandelf -t Linux filename\n"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:188
+#, no-wrap
+msgid "Which Convention Should You Use?"
+msgstr "Какое соглашение следует использовать?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:193
+msgid ""
+"If you are coding specifically for FreeBSD, you should always use the "
+"UNIX(R) convention: It is faster, you can store global variables in "
+"registers, you do not have to brand the executable, and you do not impose "
+"the installation of the Linux emulation package on the target system."
+msgstr ""
+"Если вы разрабатываете код специально для FreeBSD, всегда следует "
+"использовать соглашение UNIX(R): это быстрее, вы можете хранить глобальные "
+"переменные в регистрах, вам не нужно маркировать исполняемый файл, и вы не "
+"требуете установки пакета эмуляции Linux на целевой системе."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:196
+msgid ""
+"If you want to create portable code that can also run on Linux, you will "
+"probably still want to give the FreeBSD users as efficient a code as "
+"possible. I will show you how you can accomplish that after I have "
+"explained the basics."
+msgstr ""
+"Хотя вы можете хотеть создать переносимый код, который также работает на "
+"Linux, вам, вероятно, по-прежнему будет нужен максимально эффективный код "
+"для пользователей FreeBSD. Я покажу вам, как этого добиться, после того как "
+"объясню основы."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:198
+#, no-wrap
+msgid "Call Numbers"
+msgstr "Номера вызовов"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:202
+msgid ""
+"To tell the kernel which system service you are calling, place its number in "
+"`EAX`. Of course, you need to know what the number is."
+msgstr ""
+"Чтобы сообщить ядру, какую системную службу вы вызываете, поместите её номер "
+"в `EAX`. Разумеется, вам необходимо знать, что это за номер."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:204
+#, no-wrap
+msgid "The [.filename]#syscalls# File"
+msgstr "Файл [.filename]#syscalls#"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:208
+msgid ""
+"The numbers are listed in [.filename]#syscalls#. `locate syscalls` finds "
+"this file in several different formats, all produced automatically from "
+"[.filename]#syscalls.master#."
+msgstr ""
+"Номера перечислены в [.filename]#syscalls#. Команда `locate syscalls` "
+"находит этот файл в нескольких различных форматах, все они создаются "
+"автоматически из [.filename]#syscalls.master#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:211
+msgid ""
+"You can find the master file for the default UNIX(R) calling convention in "
+"[.filename]#/usr/src/sys/kern/syscalls.master#. If you need to use the "
+"other convention implemented in the Linux emulation mode, read [.filename]#/"
+"usr/src/sys/i386/linux/syscalls.master#."
+msgstr ""
+"Основной файл для стандартного соглашения о вызовах UNIX(R) можно найти в "
+"[.filename]#/usr/src/sys/kern/syscalls.master#. Если вам необходимо "
+"использовать другое соглашение, реализованное в режиме эмуляции Linux, "
+"обратитесь к [.filename]#/usr/src/sys/i386/linux/syscalls.master#."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:215
+msgid ""
+"Not only do FreeBSD and Linux use different calling conventions, they "
+"sometimes use different numbers for the same functions."
+msgstr ""
+"Не только FreeBSD и Linux используют разные соглашения о вызовах, но иногда "
+"они используют разные номера для одних и тех же функций."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:218
+msgid "[.filename]#syscalls.master# describes how the call is to be made:"
+msgstr ""
+"[.filename]#syscalls.master# описывает, как должен быть выполнен вызов:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:229
+#, no-wrap
+msgid ""
+"0\tSTD\tNOHIDE\t{ int nosys(void); } syscall nosys_args int\n"
+"1\tSTD\tNOHIDE\t{ void exit(int rval); } exit rexit_args void\n"
+"2\tSTD\tPOSIX\t{ int fork(void); }\n"
+"3\tSTD\tPOSIX\t{ ssize_t read(int fd, void *buf, size_t nbyte); }\n"
+"4\tSTD\tPOSIX\t{ ssize_t write(int fd, const void *buf, size_t nbyte); }\n"
+"5\tSTD\tPOSIX\t{ int open(char *path, int flags, int mode); }\n"
+"6\tSTD\tPOSIX\t{ int close(int fd); }\n"
+"etc...\n"
+msgstr ""
+"0\tSTD\tNOHIDE\t{ int nosys(void); } syscall nosys_args int\n"
+"1\tSTD\tNOHIDE\t{ void exit(int rval); } exit rexit_args void\n"
+"2\tSTD\tPOSIX\t{ int fork(void); }\n"
+"3\tSTD\tPOSIX\t{ ssize_t read(int fd, void *buf, size_t nbyte); }\n"
+"4\tSTD\tPOSIX\t{ ssize_t write(int fd, const void *buf, size_t nbyte); }\n"
+"5\tSTD\tPOSIX\t{ int open(char *path, int flags, int mode); }\n"
+"6\tSTD\tPOSIX\t{ int close(int fd); }\n"
+"etc...\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:232
+msgid "It is the leftmost column that tells us the number to place in `EAX`."
+msgstr ""
+"Это крайний левый столбец, который указывает число, которое нужно поместить "
+"в `EAX`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:235
+msgid ""
+"The rightmost column tells us what parameters to `push`. They are "
+"``push``ed _from right to left_."
+msgstr ""
+"Самый правый столбец указывает, какие параметры нужно `втолкнуть` в стек "
+"командой push. Они `вталкиваются` _справа налево_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:237
+msgid ""
+"For example, to `open` a file, we need to `push` the `mode` first, then "
+"`flags`, then the address at which the `path` is stored."
+msgstr ""
+"Например, чтобы `открыть` файл, нам сначала нужно сделать `push` для `mode`, "
+"затем `flags`, а затем адрес, по которому хранится `path`."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:239
+#, no-wrap
+msgid "Return Values"
+msgstr "Возвращаемые значения"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:243
+msgid ""
+"A system call would not be useful most of the time if it did not return some "
+"kind of a value: The file descriptor of an open file, the number of bytes "
+"read to a buffer, the system time, etc."
+msgstr ""
+"От системных вызовов не было бы никакой пользы, если бы они не возвращали "
+"какое-либо значение: дескриптор открытого файла, количество байтов, "
+"прочитанных в буфер, системное время и т.д."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:246
+msgid ""
+"Additionally, the system needs to inform us if an error occurs: A file does "
+"not exist, system resources are exhausted, we passed an invalid parameter, "
+"etc."
+msgstr ""
+"Кроме того, система должна уведомлять нас, если возникает ошибка: файл не "
+"существует, системные ресурсы исчерпаны, передан недопустимый параметр и т. "
+"д."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:248
+#, no-wrap
+msgid "Man Pages"
+msgstr "Страницы справочника"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:252
+msgid ""
+"The traditional place to look for information about various system calls "
+"under UNIX(R) systems are the manual pages. FreeBSD describes its system "
+"calls in section 2, sometimes in section 3."
+msgstr ""
+"Традиционным источником информации о различных системных вызовах в UNIX(R)-"
+"системах являются страницы Справочника. В FreeBSD системные вызовы описаны в "
+"разделе 2, иногда в разделе 3."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:254
+msgid "For example, man:open[2] says:"
+msgstr "Например, man:open[2] говорит:"
+
+#. type: .blockquote
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:258
+msgid ""
+"If successful, `open()` returns a non-negative integer, termed a file "
+"descriptor. It returns `-1` on failure, and sets `errno` to indicate the "
+"error."
+msgstr ""
+"В случае успеха `open()` возвращает неотрицательное целое число, называемое "
+"файловым дескриптором. В случае ошибки возвращается `-1`, а переменной "
+"`errno` присваивается код ошибки."
+
+#. type: .blockquote
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:260
+msgid ""
+"The assembly language programmer new to UNIX(R) and FreeBSD will immediately "
+"ask the puzzling question: Where is `errno` and how do I get to it?"
+msgstr ""
+"Программист на ассемблере, впервые столкнувшийся с UNIX(R) и FreeBSD, сразу "
+"же задастся вопросом: где находится `errno` и как к ней обратиться?"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:265
+msgid ""
+"The information presented in the manual pages applies to C programs. The "
+"assembly language programmer needs additional information."
+msgstr ""
+"Информация, представленная в руководствах, применима к программам на языке "
+"C. Программистам на языке ассемблера требуется дополнительная информация."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:268
+#, no-wrap
+msgid "Where Are the Return Values?"
+msgstr "Где возвращаемые значения?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:273
+msgid ""
+"Unfortunately, it depends... For most system calls it is in `EAX`, but not "
+"for all. A good rule of thumb, when working with a system call for the "
+"first time, is to look for the return value in `EAX`. If it is not there, "
+"you need further research."
+msgstr ""
+"К сожалению, это зависит от ситуации... Для большинства системных вызовов "
+"возвращаемое значение находится в `EAX`, но не для всех. Хорошее правило при "
+"первой работе с системным вызовом — искать возвращаемое значение в `EAX`. "
+"Если его там нет, потребуется дополнительное исследование."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:279
+msgid ""
+"I am aware of one system call that returns the value in `EDX`: `SYS_fork`. "
+"All others I have worked with use `EAX`. But I have not worked with them "
+"all yet."
+msgstr ""
+"Я знаю о одном системном вызове, который возвращает значение в `EDX`: "
+"`SYS_fork`. Все остальные, с которыми я работал, используют `EAX`. Но я еще "
+"не работал со всеми из них."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:284
+msgid ""
+"If you cannot find the answer here or anywhere else, study libc source code "
+"and see how it interfaces with the kernel."
+msgstr ""
+"Если вы не можете найти ответ здесь или где-либо ещё, изучите исходный код "
+"libc и посмотрите, как он взаимодействует с ядром."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:287
+#, no-wrap
+msgid "Where Is `errno`?"
+msgstr "Где находится `errno`?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:290
+msgid "Actually, nowhere..."
+msgstr "Фактически, нигде..."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:293
+msgid ""
+"`errno` is part of the C language, not the UNIX(R) kernel. When accessing "
+"kernel services directly, the error code is returned in `EAX`, the same "
+"register the proper return value generally ends up in."
+msgstr ""
+"`errno` является частью языка C, а не ядра UNIX(R). При прямом доступе к "
+"сервисам ядра код ошибки возвращается в регистре `EAX` — том же регистре, в "
+"котором обычно оказывается корректное возвращаемое значение."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:297
+msgid ""
+"This makes perfect sense. If there is no error, there is no error code. If "
+"there is an error, there is no return value. One register can contain "
+"either."
+msgstr ""
+"Это совершенно логично. Если нет ошибки, то нет и кода ошибки. Если есть "
+"ошибка, то нет возвращаемого значения. Один регистр может содержать либо то, "
+"либо другое."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:299
+#, no-wrap
+msgid "Determining an Error Occurred"
+msgstr "Определение возникновения ошибки"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:302
+msgid ""
+"When using the standard FreeBSD calling convention, the `carry flag` is "
+"cleared upon success, set upon failure."
+msgstr ""
+"При использовании стандартного соглашения о вызовах FreeBSD флаг `carry "
+"flag` сбрасывается при успехе и устанавливается при неудаче."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:305
+msgid ""
+"When using the Linux emulation mode, the signed value in `EAX` is non-"
+"negative upon success, and contains the return value. In case of an error, "
+"the value is negative, i.e., `-errno`."
+msgstr ""
+"При использовании режима эмуляции Linux знаковое значение в `EAX` "
+"неотрицательно в случае успеха и содержит возвращаемое значение. В случае "
+"ошибки значение отрицательное, т.е. `-errno`."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:307
+#, no-wrap
+msgid "Creating Portable Code"
+msgstr "Создание переносимого кода"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:312
+msgid ""
+"Portability is generally not one of the strengths of assembly language. "
+"Yet, writing assembly language programs for different platforms is possible, "
+"especially with nasm. I have written assembly language libraries that can "
+"be assembled for such different operating systems as Windows(R) and FreeBSD."
+msgstr ""
+"Портативность обычно не является сильной стороной языка ассемблера. Тем не "
+"менее, написание программ на ассемблере для разных платформ возможно, "
+"особенно с использованием nasm. Я создавал библиотеки на ассемблере, которые "
+"можно было собрать для таких разных операционных систем, как Windows(R) и "
+"FreeBSD."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:314
+msgid ""
+"It is all the more possible when you want your code to run on two platforms "
+"which, while different, are based on similar architectures."
+msgstr ""
+"Это становится еще более возможным, когда вы хотите, чтобы ваш код работал "
+"на двух платформах, которые, хотя и различны, основаны на схожих "
+"архитектурах."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:318
+msgid ""
+"For example, FreeBSD is UNIX(R), Linux is UNIX(R) like. I only mentioned "
+"three differences between them (from an assembly language programmer's "
+"perspective): The calling convention, the function numbers, and the way of "
+"returning values."
+msgstr ""
+"Например, FreeBSD — это UNIX(R), а Linux — UNIX(R)-подобная система. Я "
+"упомянул лишь три различия между ними (с точки зрения программиста на "
+"ассемблере): соглашение о вызовах, номера функций и способ возврата значений."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:320
+#, no-wrap
+msgid "Dealing with Function Numbers"
+msgstr "Работа с номерами функций"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:325
+msgid ""
+"In many cases the function numbers are the same. However, even when they "
+"are not, the problem is easy to deal with: Instead of using numbers in your "
+"code, use constants which you have declared differently depending on the "
+"target architecture:"
+msgstr ""
+"Во многих случаях номера функций совпадают. Однако, даже если это не так, "
+"проблему легко решить: вместо использования чисел в коде применяйте "
+"константы, объявленные по-разному в зависимости от целевой архитектуры:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:333
+#, no-wrap
+msgid ""
+"%ifdef\tLINUX\n"
+"%define\tSYS_execve\t11\n"
+"%else\n"
+"%define\tSYS_execve\t59\n"
+"%endif\n"
+msgstr ""
+"%ifdef\tLINUX\n"
+"%define\tSYS_execve\t11\n"
+"%else\n"
+"%define\tSYS_execve\t59\n"
+"%endif\n"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:336
+#, no-wrap
+msgid "Dealing with Conventions"
+msgstr "Работа с соглашениями"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:339
+msgid ""
+"Both, the calling convention, and the return value (the `errno` problem) can "
+"be resolved with macros:"
+msgstr ""
+"Оба, соглашение о вызовах и возвращаемое значение (проблема `errno`) могут "
+"быть решены с помощью макросов:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:343
+#, no-wrap
+msgid "%ifdef\tLINUX\n"
+msgstr "%ifdef\tLINUX\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:347
+#, no-wrap
+msgid ""
+"%macro\tsystem\t0\n"
+"\tcall\tkernel\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsystem\t0\n"
+"\tcall\tkernel\n"
+"%endmacro\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:356
+#, no-wrap
+msgid ""
+"align 4\n"
+"kernel:\n"
+"\tpush\tebx\n"
+"\tpush\tecx\n"
+"\tpush\tedx\n"
+"\tpush\tesi\n"
+"\tpush\tedi\n"
+"\tpush\tebp\n"
+msgstr ""
+"align 4\n"
+"kernel:\n"
+"\tpush\tebx\n"
+"\tpush\tecx\n"
+"\tpush\tedx\n"
+"\tpush\tesi\n"
+"\tpush\tedi\n"
+"\tpush\tebp\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:363
+#, no-wrap
+msgid ""
+"\tmov\tebx, [esp+32]\n"
+"\tmov\tecx, [esp+36]\n"
+"\tmov\tedx, [esp+40]\n"
+"\tmov\tesi, [esp+44]\n"
+"\tmov\tebp, [esp+48]\n"
+"\tint\t80h\n"
+msgstr ""
+"\tmov\tebx, [esp+32]\n"
+"\tmov\tecx, [esp+36]\n"
+"\tmov\tedx, [esp+40]\n"
+"\tmov\tesi, [esp+44]\n"
+"\tmov\tebp, [esp+48]\n"
+"\tint\t80h\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:370
+#, no-wrap
+msgid ""
+"\tpop\tebp\n"
+"\tpop\tedi\n"
+"\tpop\tesi\n"
+"\tpop\tedx\n"
+"\tpop\tecx\n"
+"\tpop\tebx\n"
+msgstr ""
+"\tpop\tebp\n"
+"\tpop\tedi\n"
+"\tpop\tesi\n"
+"\tpop\tedx\n"
+"\tpop\tecx\n"
+"\tpop\tebx\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:375
+#, no-wrap
+msgid ""
+"\tor\teax, eax\n"
+"\tjs\t.errno\n"
+"\tclc\n"
+"\tret\n"
+msgstr ""
+"\tor\teax, eax\n"
+"\tjs\t.errno\n"
+"\tclc\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:380
+#, no-wrap
+msgid ""
+".errno:\n"
+"\tneg\teax\n"
+"\tstc\n"
+"\tret\n"
+msgstr ""
+".errno:\n"
+"\tneg\teax\n"
+"\tstc\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:382
+#, no-wrap
+msgid "%else\n"
+msgstr "%else\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:386
+#, no-wrap
+msgid ""
+"%macro\tsystem\t0\n"
+"\tint\t80h\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsystem\t0\n"
+"\tint\t80h\n"
+"%endmacro\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:388
+#, no-wrap
+msgid "%endif\n"
+msgstr "%endif\n"
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:391
+#, no-wrap
+msgid "Dealing with Other Portability Issues"
+msgstr "Устранение прочих проблем с переносимостью"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:395
+msgid ""
+"The above solutions can handle most cases of writing code portable between "
+"FreeBSD and Linux. Nevertheless, with some kernel services the differences "
+"are deeper."
+msgstr ""
+"Приведённые выше решения могут помочь в большинстве случаев написания кода, "
+"переносимого между FreeBSD и Linux. Тем не менее, с некоторыми сервисами "
+"ядра различия более глубокие."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:398
+msgid ""
+"In that case, you need to write two different handlers for those particular "
+"system calls, and use conditional assembly. Luckily, most of your code does "
+"something other than calling the kernel, so usually you will only need a few "
+"such conditional sections in your code."
+msgstr ""
+"В таком случае необходимо написать два разных обработчика для этих "
+"конкретных системных вызовов и использовать условную компиляцию. К счастью, "
+"большая часть вашего кода выполняет действия, отличные от вызовов ядра, "
+"поэтому обычно потребуется лишь несколько таких условных секций в коде."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:400
+#, no-wrap
+msgid "Using a Library"
+msgstr "Использование библиотеки"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:404
+msgid ""
+"You can avoid portability issues in your main code altogether by writing a "
+"library of system calls. Create a separate library for FreeBSD, a different "
+"one for Linux, and yet other libraries for more operating systems."
+msgstr ""
+"Вы можете полностью избежать проблем с переносимостью в основном коде, "
+"написав библиотеку системных вызовов. Создайте отдельную библиотеку для "
+"FreeBSD, другую для Linux и ещё другие библиотеки для дополнительных "
+"операционных систем."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:409
+msgid ""
+"In your library, write a separate function (or procedure, if you prefer the "
+"traditional assembly language terminology) for each system call. Use the C "
+"calling convention of passing parameters. But still use `EAX` to pass the "
+"call number in. In that case, your FreeBSD library can be very simple, as "
+"many seemingly different functions can be just labels to the same code:"
+msgstr ""
+"В вашей библиотеке напишите отдельную функцию (или процедуру, если вы "
+"предпочитаете традиционную терминологию ассемблера) для каждого системного "
+"вызова. Используйте соглашение о вызовах C для передачи параметров. Однако "
+"по-прежнему передавайте номер вызова через `EAX`. В таком случае ваша "
+"библиотека FreeBSD может быть очень простой, так как множество внешне "
+"различных функций могут быть просто метками одного и того же кода:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:417
+#, no-wrap
+msgid ""
+"sys.open:\n"
+"sys.close:\n"
+"[etc...]\n"
+"\tint\t80h\n"
+"\tret\n"
+msgstr ""
+"sys.open:\n"
+"sys.close:\n"
+"[etc...]\n"
+"\tint\t80h\n"
+"\tret\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:421
+msgid ""
+"Your Linux library will require more different functions. But even here you "
+"can group system calls using the same number of parameters:"
+msgstr ""
+"Ваша библиотека Linux потребует больше различных функций. Но даже здесь вы "
+"можете группировать системные вызовы, используя одинаковое количество "
+"параметров:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:432
+#, no-wrap
+msgid ""
+"sys.exit:\n"
+"sys.close:\n"
+"[etc... one-parameter functions]\n"
+"\tpush\tebx\n"
+"\tmov\tebx, [esp+12]\n"
+"\tint\t80h\n"
+"\tpop\tebx\n"
+"\tjmp\tsys.return\n"
+msgstr ""
+"sys.exit:\n"
+"sys.close:\n"
+"[etc... one-parameter functions]\n"
+"\tpush\tebx\n"
+"\tmov\tebx, [esp+12]\n"
+"\tint\t80h\n"
+"\tpop\tebx\n"
+"\tjmp\tsys.return\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:434
+#, no-wrap
+msgid "...\n"
+msgstr "...\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:440
+#, no-wrap
+msgid ""
+"sys.return:\n"
+"\tor\teax, eax\n"
+"\tjs\tsys.err\n"
+"\tclc\n"
+"\tret\n"
+msgstr ""
+"sys.return:\n"
+"\tor\teax, eax\n"
+"\tjs\tsys.err\n"
+"\tclc\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:445
+#, no-wrap
+msgid ""
+"sys.err:\n"
+"\tneg\teax\n"
+"\tstc\n"
+"\tret\n"
+msgstr ""
+"sys.err:\n"
+"\tneg\teax\n"
+"\tstc\n"
+"\tret\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:452
+msgid ""
+"The library approach may seem inconvenient at first because it requires you "
+"to produce a separate file your code depends on. But it has many "
+"advantages: For one, you only need to write it once and can use it for all "
+"your programs. You can even let other assembly language programmers use it, "
+"or perhaps use one written by someone else. But perhaps the greatest "
+"advantage of the library is that your code can be ported to other systems, "
+"even by other programmers, by simply writing a new library without any "
+"changes to your code."
+msgstr ""
+"Подход с использованием библиотек может показаться неудобным на первый "
+"взгляд, так как требует создания отдельного файла, от которого зависит ваш "
+"код. Однако у него есть множество преимуществ: во-первых, вам нужно написать "
+"его лишь один раз, и затем вы можете использовать его во всех своих "
+"программах. Вы даже можете позволить другим программистам на ассемблере "
+"использовать его или, возможно, воспользоваться библиотекой, написанной кем-"
+"то другим. Но, пожалуй, самое большое преимущество библиотеки заключается в "
+"том, что ваш код может быть перенесён на другие системы, даже другими "
+"программистами, просто путём написания новой библиотеки без каких-либо "
+"изменений в вашем коде."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:454
+msgid ""
+"If you do not like the idea of having a library, you can at least place all "
+"your system calls in a separate assembly language file and link it with your "
+"main program. Here, again, all porters have to do is create a new object "
+"file to link with your main program."
+msgstr ""
+"Если вам не нравится идея использования библиотеки, вы можете хотя бы "
+"разместить все системные вызовы в отдельном файле на ассемблере и "
+"скомпоновать его с основной программой. Здесь, опять же, все, что нужно "
+"сделать переносчикам, — это создать новый объектный файл для компоновки с "
+"основной программой."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:456
+#, no-wrap
+msgid "Using an Include File"
+msgstr "Использование включаемого файла"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:459
+msgid ""
+"If you are releasing your software as (or with) source code, you can use "
+"macros and place them in a separate file, which you include in your code."
+msgstr ""
+"Если вы выпускаете своё программное обеспечение в виде исходного кода (или "
+"вместе с ним), вы можете использовать макросы и размещать их в отдельном "
+"файле, который включается в ваш код."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:462
+msgid ""
+"Porters of your software will simply write a new include file. No library "
+"or external object file is necessary, yet your code is portable without any "
+"need to edit the code."
+msgstr ""
+"Портеры вашего программного обеспечения просто напишут новый include-файл. "
+"Никакая библиотека или внешний объектный файл не требуются, и ваш код "
+"остается переносимым без необходимости редактирования."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:467
+msgid ""
+"This is the approach we will use throughout this chapter. We will name our "
+"include file [.filename]#system.inc#, and add to it whenever we deal with a "
+"new system call."
+msgstr ""
+"Это подход, который мы будем использовать на протяжении всей главы. Мы "
+"назовем наш включаемый файл [.filename]#system.inc# и будем добавлять в него "
+"новые системные вызовы по мере их рассмотрения."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:470
+msgid ""
+"We can start our [.filename]#system.inc# by declaring the standard file "
+"descriptors:"
+msgstr ""
+"Мы можем начать наш [.filename]#system.inc# с объявления стандартных "
+"файловых дескрипторов:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:476
+#, no-wrap
+msgid ""
+"%define\tstdin\t0\n"
+"%define\tstdout\t1\n"
+"%define\tstderr\t2\n"
+msgstr ""
+"%define\tstdin\t0\n"
+"%define\tstdout\t1\n"
+"%define\tstderr\t2\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:479
+msgid "Next, we create a symbolic name for each system call:"
+msgstr "Далее мы создаем символическое имя для каждого системного вызова:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:488
+#, no-wrap
+msgid ""
+"%define\tSYS_nosys\t0\n"
+"%define\tSYS_exit\t1\n"
+"%define\tSYS_fork\t2\n"
+"%define\tSYS_read\t3\n"
+"%define\tSYS_write\t4\n"
+"; [etc...]\n"
+msgstr ""
+"%define\tSYS_nosys\t0\n"
+"%define\tSYS_exit\t1\n"
+"%define\tSYS_fork\t2\n"
+"%define\tSYS_read\t3\n"
+"%define\tSYS_write\t4\n"
+"; [etc...]\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:491
+msgid ""
+"We add a short, non-global procedure with a long name, so we do not "
+"accidentally reuse the name in our code:"
+msgstr ""
+"Добавляем короткую, неглобальную процедуру с длинным именем, чтобы случайно "
+"не использовать это имя в нашем коде:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:499
+#, no-wrap
+msgid ""
+"section\t.text\n"
+"align 4\n"
+"access.the.bsd.kernel:\n"
+"\tint\t80h\n"
+"\tret\n"
+msgstr ""
+"section\t.text\n"
+"align 4\n"
+"access.the.bsd.kernel:\n"
+"\tint\t80h\n"
+"\tret\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:502
+msgid "We create a macro which takes one argument, the syscall number:"
+msgstr ""
+"Мы создаем макрос, который принимает один аргумент — номер системного вызова:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:509
+#, no-wrap
+msgid ""
+"%macro\tsystem\t1\n"
+"\tmov\teax, %1\n"
+"\tcall\taccess.the.bsd.kernel\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsystem\t1\n"
+"\tmov\teax, %1\n"
+"\tcall\taccess.the.bsd.kernel\n"
+"%endmacro\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:513
+msgid ""
+"Finally, we create macros for each syscall. These macros take no arguments."
+msgstr ""
+"Наконец, мы создаем макросы для каждого системного вызова. Эти макросы не "
+"принимают аргументов."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:519
+#, no-wrap
+msgid ""
+"%macro\tsys.exit\t0\n"
+"\tsystem\tSYS_exit\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsys.exit\t0\n"
+"\tsystem\tSYS_exit\n"
+"%endmacro\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:523
+#, no-wrap
+msgid ""
+"%macro\tsys.fork\t0\n"
+"\tsystem\tSYS_fork\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsys.fork\t0\n"
+"\tsystem\tSYS_fork\n"
+"%endmacro\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:527
+#, no-wrap
+msgid ""
+"%macro\tsys.read\t0\n"
+"\tsystem\tSYS_read\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsys.read\t0\n"
+"\tsystem\tSYS_read\n"
+"%endmacro\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:531
+#, no-wrap
+msgid ""
+"%macro\tsys.write\t0\n"
+"\tsystem\tSYS_write\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsys.write\t0\n"
+"\tsystem\tSYS_write\n"
+"%endmacro\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:533
+#, no-wrap
+msgid "; [etc...]\n"
+msgstr "; [etc...]\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:537
+msgid ""
+"Go ahead, enter it into your editor and save it as [.filename]#system.inc#. "
+"We will add more to it as we discuss more syscalls."
+msgstr ""
+"Продолжайте, введите это в ваш редактор и сохраните как "
+"[.filename]#system.inc#. Мы добавим больше по мере обсуждения дополнительных "
+"системных вызовов."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:539
+#, no-wrap
+msgid "Our First Program"
+msgstr "Наша первая программа"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:542
+msgid "We are now ready for our first program, the mandatory Hello, World!"
+msgstr "Мы готовы к нашей первой обязательной программе — Hello, World!"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:546
+#, no-wrap
+msgid "\t%include\t'system.inc'\n"
+msgstr "\t%include\t'system.inc'\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:550
+#, no-wrap
+msgid ""
+"\tsection\t.data\n"
+"\thello\tdb\t'Hello, World!', 0Ah\n"
+"\thbytes\tequ\t$-hello\n"
+msgstr ""
+"\tsection\t.data\n"
+"\thello\tdb\t'Hello, World!', 0Ah\n"
+"\thbytes\tequ\t$-hello\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:558
+#, no-wrap
+msgid ""
+"\tsection\t.text\n"
+"\tglobal\t_start\n"
+"_start:\n"
+"\tpush\tdword hbytes\n"
+"\tpush\tdword hello\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+msgstr ""
+"\tsection\t.text\n"
+"\tglobal\t_start\n"
+"_start:\n"
+"\tpush\tdword hbytes\n"
+"\tpush\tdword hello\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:561
+#, no-wrap
+msgid ""
+"\tpush\tdword 0\n"
+"\tsys.exit\n"
+msgstr ""
+"\tpush\tdword 0\n"
+"\tsys.exit\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:564
+msgid ""
+"Here is what it does: Line 1 includes the defines, the macros, and the code "
+"from [.filename]#system.inc#."
+msgstr ""
+"Вот что он делает: Строка 1 включает определения, макросы и код из файла "
+"[.filename]#system.inc#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:568
+msgid ""
+"Lines 3-5 are the data: Line 3 starts the data section/segment. Line 4 "
+"contains the string \"Hello, World!\" followed by a new line (`0Ah`). Line "
+"5 creates a constant that contains the length of the string from line 4 in "
+"bytes."
+msgstr ""
+"Строки 3-5 содержат данные: строка 3 начинает раздел/сегмент данных. Строка "
+"4 содержит строку \"Hello, World!\", за которой следует новая строка "
+"(`0Ah`). Строка 5 создает константу, содержащую длину строки из строки 4 в "
+"байтах."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:572
+msgid ""
+"Lines 7-16 contain the code. Note that FreeBSD uses the _elf_ file format "
+"for its executables, which requires every program to start at the point "
+"labeled `_start` (or, more precisely, the linker expects that). This label "
+"has to be global."
+msgstr ""
+"Строки 7-16 содержат код. Обратите внимание, что FreeBSD использует формат "
+"файлов _elf_ для исполняемых файлов, который требует, чтобы каждая программа "
+"запускается с адреса, помеченного как `_start` (или, точнее, компоновщик "
+"ожидает этого). Эта метка должна быть глобальной."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:574
+msgid ""
+"Lines 10-13 ask the system to write `hbytes` bytes of the `hello` string to "
+"`stdout`."
+msgstr ""
+"Строки 10-13 указывают системе записать `hbytes` байтов строки `hello` в "
+"`stdout`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:577
+msgid ""
+"Lines 15-16 ask the system to end the program with the return value of `0`. "
+"The `SYS_exit` syscall never returns, so the code ends there."
+msgstr ""
+"Строки 15-16 указывают системе завершить программу с возвращаемым значением "
+"`0`. Системный вызов `SYS_exit` никогда не возвращает управление, поэтому "
+"код завершается в этой точке."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:585
+msgid ""
+"If you have come to UNIX(R) from MS-DOS(R) assembly language background, you "
+"may be used to writing directly to the video hardware. You will never have "
+"to worry about this in FreeBSD, or any other flavor of UNIX(R). As far as "
+"you are concerned, you are writing to a file known as [.filename]#stdout#. "
+"This can be the video screen, or a telnet terminal, or an actual file, or "
+"even the input of another program. Which one it is, is for the system to "
+"figure out."
+msgstr ""
+"Если вы перешли на UNIX(R) с опытом программирования на ассемблере для MS-"
+"DOS(R), вы, возможно, привыкли писать напрямую в видеопамять. В FreeBSD или "
+"любой другой разновидности UNIX(R) вам не придётся об этом беспокоиться. С "
+"вашей точки зрения, вы записываете данные в файл под названием "
+"[.filename]#stdout#. Это может быть экран, терминал telnet, обычный файл или "
+"даже входные данные другой программы. Определять, что именно это будет, — "
+"задача системы."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:588
+#, no-wrap
+msgid "Assembling the Code"
+msgstr "Ассемблирование кода"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:592
+msgid ""
+"Type the code in an editor, and save it in a file named "
+"[.filename]#hello.asm#. You need nasm to assemble it."
+msgstr ""
+"Наберите код в редакторе и сохраните его в файле с именем "
+"[.filename]#hello.asm#. Для сборки вам понадобится nasm."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:594
+#, no-wrap
+msgid "Installing nasm"
+msgstr "Установка nasm"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:597
+msgid "If you do not have nasm, type:"
+msgstr "Если у вас нет nasm, введите:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:606
+#, no-wrap
+msgid ""
+"% su\n"
+"Password:your root password\n"
+"# cd /usr/ports/devel/nasm\n"
+"# make install\n"
+"# exit\n"
+"%\n"
+msgstr ""
+"% su\n"
+"Password:your root password\n"
+"# cd /usr/ports/devel/nasm\n"
+"# make install\n"
+"# exit\n"
+"%\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:609
+msgid ""
+"You may type `make install clean` instead of just `make install` if you do "
+"not want to keep nasm source code."
+msgstr ""
+"Вы можете ввести `make install clean` вместо просто `make install`, если не "
+"хотите сохранять исходный код nasm."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:611
+msgid ""
+"Either way, FreeBSD will automatically download nasm from the Internet, "
+"compile it, and install it on your system."
+msgstr ""
+"В любом случае FreeBSD автоматически загрузит nasm из интернета, "
+"скомпилирует его и установит в вашу систему."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:616
+msgid ""
+"If your system is not FreeBSD, you need to get nasm from its https://"
+"sourceforge.net/projects/nasm[home page]. You can still use it to assemble "
+"FreeBSD code."
+msgstr ""
+"Если ваша система не FreeBSD, вам нужно получить nasm с его https://"
+"sourceforge.net/projects/nasm[домашней страницы]. Вы по-прежнему можете "
+"использовать его для ассемблирования кода FreeBSD."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:619
+msgid "Now you can assemble, link, and run the code:"
+msgstr "Теперь вы можете собрать, скомпоновать и запустить код:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:627
+#, no-wrap
+msgid ""
+"% nasm -f elf hello.asm\n"
+"% ld -s -o hello hello.o\n"
+"% ./hello\n"
+"Hello, World!\n"
+"%\n"
+msgstr ""
+"% nasm -f elf hello.asm\n"
+"% ld -s -o hello hello.o\n"
+"% ./hello\n"
+"Hello, World!\n"
+"%\n"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:630
+#, no-wrap
+msgid "Writing UNIX(R) Filters"
+msgstr "Написание фильтров UNIX(R)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:633
+msgid ""
+"A common type of UNIX(R) application is a filter-a program that reads data "
+"from the [.filename]#stdin#, processes it somehow, then writes the result to "
+"[.filename]#stdout#."
+msgstr ""
+"Распространённым типом приложений в UNIX(R) являются фильтры — программы, "
+"которые читают данные из [.filename]#stdin#, обрабатывают их определённым "
+"образом, а затем записывают результат в [.filename]#stdout#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:636
+msgid ""
+"In this chapter, we shall develop a simple filter, and learn how to read "
+"from [.filename]#stdin# and write to [.filename]#stdout#. This filter will "
+"convert each byte of its input into a hexadecimal number followed by a blank "
+"space."
+msgstr ""
+"В этой главе мы разработаем простой фильтр и научимся читать из "
+"[.filename]#stdin# и писать в [.filename]#stdout#. Этот фильтр будет "
+"преобразовывать каждый байт входных данных в шестнадцатеричное число, за "
+"которым следует пробел."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:640
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:734
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:824
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:952
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1219
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2337
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3335
+#, no-wrap
+msgid "%include\t'system.inc'\n"
+msgstr "%include\t'system.inc'\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:644
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:738
+#, no-wrap
+msgid ""
+"section\t.data\n"
+"hex\tdb\t'0123456789ABCDEF'\n"
+"buffer\tdb\t0, 0, ' '\n"
+msgstr ""
+"section\t.data\n"
+"hex\tdb\t'0123456789ABCDEF'\n"
+"buffer\tdb\t0, 0, ' '\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:656
+#, no-wrap
+msgid ""
+"section\t.text\n"
+"global\t_start\n"
+"_start:\n"
+"\t; read a byte from stdin\n"
+"\tpush\tdword 1\n"
+"\tpush\tdword buffer\n"
+"\tpush\tdword stdin\n"
+"\tsys.read\n"
+"\tadd\tesp, byte 12\n"
+"\tor\teax, eax\n"
+"\tje\t.done\n"
+msgstr ""
+"section\t.text\n"
+"global\t_start\n"
+"_start:\n"
+"\t; read a byte from stdin\n"
+"\tpush\tdword 1\n"
+"\tpush\tdword buffer\n"
+"\tpush\tdword stdin\n"
+"\tsys.read\n"
+"\tadd\tesp, byte 12\n"
+"\tor\teax, eax\n"
+"\tje\t.done\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:666
+#, no-wrap
+msgid ""
+"\t; convert it to hex\n"
+"\tmovzx\teax, byte [buffer]\n"
+"\tmov\tedx, eax\n"
+"\tshr\tdl, 4\n"
+"\tmov\tdl, [hex+edx]\n"
+"\tmov\t[buffer], dl\n"
+"\tand\tal, 0Fh\n"
+"\tmov\tal, [hex+eax]\n"
+"\tmov\t[buffer+1], al\n"
+msgstr ""
+"\t; convert it to hex\n"
+"\tmovzx\teax, byte [buffer]\n"
+"\tmov\tedx, eax\n"
+"\tshr\tdl, 4\n"
+"\tmov\tdl, [hex+edx]\n"
+"\tmov\t[buffer], dl\n"
+"\tand\tal, 0Fh\n"
+"\tmov\tal, [hex+eax]\n"
+"\tmov\t[buffer+1], al\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:674
+#, no-wrap
+msgid ""
+"\t; print it\n"
+"\tpush\tdword 3\n"
+"\tpush\tdword buffer\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+"\tadd\tesp, byte 12\n"
+"\tjmp\tshort _start\n"
+msgstr ""
+"\t; print it\n"
+"\tpush\tdword 3\n"
+"\tpush\tdword buffer\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+"\tadd\tesp, byte 12\n"
+"\tjmp\tshort _start\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:678
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:781
+#, no-wrap
+msgid ""
+".done:\n"
+"\tpush\tdword 0\n"
+"\tsys.exit\n"
+msgstr ""
+".done:\n"
+"\tpush\tdword 0\n"
+"\tsys.exit\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:686
+msgid ""
+"In the data section we create an array called `hex`. It contains the 16 "
+"hexadecimal digits in ascending order. The array is followed by a buffer "
+"which we will use for both input and output. The first two bytes of the "
+"buffer are initially set to `0`. This is where we will write the two "
+"hexadecimal digits (the first byte also is where we will read the input). "
+"The third byte is a space."
+msgstr ""
+"В разделе данных мы создаем массив с именем `hex`. Он содержит 16 "
+"шестнадцатеричных цифр в порядке возрастания. За массивом следует буфер, "
+"который мы будем использовать как для ввода, так и для вывода. Первые два "
+"байта буфера изначально установлены в `0`. Именно сюда мы будем записывать "
+"две шестнадцатеричные цифры (первый байт также является местом, откуда мы "
+"будем считывать ввод). Третий байт — это пробел."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:688
+msgid ""
+"The code section consists of four parts: Reading the byte, converting it to "
+"a hexadecimal number, writing the result, and eventually exiting the program."
+msgstr ""
+"Фрагмент кода состоит из четырех частей: чтение байта, преобразование его в "
+"шестнадцатеричное число, запись результата и завершение программы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:694
+msgid ""
+"To read the byte, we ask the system to read one byte from "
+"[.filename]#stdin#, and store it in the first byte of the `buffer`. The "
+"system returns the number of bytes read in `EAX`. This will be `1` while "
+"data is coming, or `0`, when no more input data is available. Therefore, we "
+"check the value of `EAX`. If it is `0`, we jump to `.done`, otherwise we "
+"continue."
+msgstr ""
+"Для чтения байта мы просим систему прочитать один байт из [.filename]#stdin# "
+"и сохранить его в первом байте `buffer`. Система возвращает количество "
+"прочитанных байтов в `EAX`. Это значение будет `1`, пока поступают данные, "
+"или `0`, если больше нет доступных входных данных. Поэтому мы проверяем "
+"значение `EAX`. Если оно равно `0`, мы переходим к метке `.done`, в "
+"противном случае продолжаем выполнение."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:698
+msgid ""
+"For simplicity sake, we are ignoring the possibility of an error condition "
+"at this time."
+msgstr "Для простоты мы пока игнорируем возможность возникновения ошибки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:703
+msgid ""
+"The hexadecimal conversion reads the byte from the `buffer` into `EAX`, or "
+"actually just `AL`, while clearing the remaining bits of `EAX` to zeros. We "
+"also copy the byte to `EDX` because we need to convert the upper four bits "
+"(nibble) separately from the lower four bits. We store the result in the "
+"first two bytes of the buffer."
+msgstr ""
+"Шестнадцатеричное преобразование считывает байт из `buffer` в `EAX`, а "
+"точнее только в `AL`, обнуляя остальные биты `EAX`. Мы также копируем байт в "
+"`EDX`, потому что нам нужно преобразовать верхние четыре бита (ниббл) "
+"отдельно от нижних четырех битов. Результат сохраняется в первых двух байтах "
+"буфера."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:706
+msgid ""
+"Next, we ask the system to write the three bytes of the buffer, i.e., the "
+"two hexadecimal digits and the blank space, to [.filename]#stdout#. We then "
+"jump back to the beginning of the program and process the next byte."
+msgstr ""
+"Далее мы просим систему записать три байта буфера, то есть две "
+"шестнадцатеричные цифры и пробел, в [.filename]#stdout#. Затем мы "
+"возвращаемся к началу программы и обрабатываем следующий байт."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:708
+msgid ""
+"Once there is no more input left, we ask the system to exit our program, "
+"returning a zero, which is the traditional value meaning the program was "
+"successful."
+msgstr ""
+"Когда ввод больше не остаётся, мы просим систему завершить нашу программу, "
+"возвращая ноль, что традиционно означает успешное выполнение программы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:710
+msgid ""
+"Go ahead, and save the code in a file named [.filename]#hex.asm#, then type "
+"the following (the `^D` means press the control key and type `D` while "
+"holding the control key down):"
+msgstr ""
+"Продолжайте и сохраните код в файле с именем [.filename]#hex.asm#, затем "
+"введите следующее (символ `^D` означает, что нужно нажать клавишу управления "
+"и, удерживая её, ввести `D`):"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:719
+#, no-wrap
+msgid ""
+"% nasm -f elf hex.asm\n"
+"% ld -s -o hex hex.o\n"
+"% ./hex\n"
+"Hello, World!\n"
+"48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A Here I come!\n"
+"48 65 72 65 20 49 20 63 6F 6D 65 21 0A ^D %\n"
+msgstr ""
+"% nasm -f elf hex.asm\n"
+"% ld -s -o hex hex.o\n"
+"% ./hex\n"
+"Hello, World!\n"
+"48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A Here I come!\n"
+"48 65 72 65 20 49 20 63 6F 6D 65 21 0A ^D %\n"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:725
+msgid ""
+"If you are migrating to UNIX(R) from MS-DOS(R), you may be wondering why "
+"each line ends with `0A` instead of `0D 0A`. This is because UNIX(R) does "
+"not use the cr/lf convention, but a \"new line\" convention, which is `0A` "
+"in hexadecimal."
+msgstr ""
+"Если вы переходите на UNIX(R) с MS-DOS(R), вам может быть интересно, почему "
+"каждая строка заканчивается на `0A` вместо `0D 0A`. Это связано с тем, что "
+"UNIX(R) не использует соглашение cr/lf, а использует соглашение \"новая "
+"строка\", которое в шестнадцатеричном виде представлено как `0A`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:730
+msgid ""
+"Can we improve this? Well, for one, it is a bit confusing because once we "
+"have converted a line of text, our input no longer starts at the beginning "
+"of the line. We can modify it to print a new line instead of a space after "
+"each `0A`:"
+msgstr ""
+"Можем ли мы это улучшить? Что ж, во-первых, это немного запутанно, потому "
+"что после преобразования строки текста наш ввод больше не начинается с "
+"начала строки. Мы можем изменить это, чтобы после каждого `0A` выводилась "
+"новая строка вместо пробела:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:743
+#, no-wrap
+msgid ""
+"section\t.text\n"
+"global\t_start\n"
+"_start:\n"
+"\tmov\tcl, ' '\n"
+msgstr ""
+"section\t.text\n"
+"global\t_start\n"
+"_start:\n"
+"\tmov\tcl, ' '\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:753
+#, no-wrap
+msgid ""
+".loop:\n"
+"\t; read a byte from stdin\n"
+"\tpush\tdword 1\n"
+"\tpush\tdword buffer\n"
+"\tpush\tdword stdin\n"
+"\tsys.read\n"
+"\tadd\tesp, byte 12\n"
+"\tor\teax, eax\n"
+"\tje\t.done\n"
+msgstr ""
+".loop:\n"
+"\t; read a byte from stdin\n"
+"\tpush\tdword 1\n"
+"\tpush\tdword buffer\n"
+"\tpush\tdword stdin\n"
+"\tsys.read\n"
+"\tadd\tesp, byte 12\n"
+"\tor\teax, eax\n"
+"\tje\t.done\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:760
+#, no-wrap
+msgid ""
+"\t; convert it to hex\n"
+"\tmovzx\teax, byte [buffer]\n"
+"\tmov\t[buffer+2], cl\n"
+"\tcmp\tal, 0Ah\n"
+"\tjne\t.hex\n"
+"\tmov\t[buffer+2], al\n"
+msgstr ""
+"\t; convert it to hex\n"
+"\tmovzx\teax, byte [buffer]\n"
+"\tmov\t[buffer+2], cl\n"
+"\tcmp\tal, 0Ah\n"
+"\tjne\t.hex\n"
+"\tmov\t[buffer+2], al\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:769
+#, no-wrap
+msgid ""
+".hex:\n"
+"\tmov\tedx, eax\n"
+"\tshr\tdl, 4\n"
+"\tmov\tdl, [hex+edx]\n"
+"\tmov\t[buffer], dl\n"
+"\tand\tal, 0Fh\n"
+"\tmov\tal, [hex+eax]\n"
+"\tmov\t[buffer+1], al\n"
+msgstr ""
+".hex:\n"
+"\tmov\tedx, eax\n"
+"\tshr\tdl, 4\n"
+"\tmov\tdl, [hex+edx]\n"
+"\tmov\t[buffer], dl\n"
+"\tand\tal, 0Fh\n"
+"\tmov\tal, [hex+eax]\n"
+"\tmov\t[buffer+1], al\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:777
+#, no-wrap
+msgid ""
+"\t; print it\n"
+"\tpush\tdword 3\n"
+"\tpush\tdword buffer\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+"\tadd\tesp, byte 12\n"
+"\tjmp\tshort .loop\n"
+msgstr ""
+"\t; print it\n"
+"\tpush\tdword 3\n"
+"\tpush\tdword buffer\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+"\tadd\tesp, byte 12\n"
+"\tjmp\tshort .loop\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:785
+msgid ""
+"We have stored the space in the `CL` register. We can do this safely "
+"because, unlike Microsoft(R) Windows(R), UNIX(R) system calls do not modify "
+"the value of any register they do not use to return a value in."
+msgstr ""
+"Мы сохранили пробел в регистре `CL`. Это безопасно, потому что, в отличие от "
+"Microsoft(R) Windows(R), вызовы системы UNIX(R) не изменяют значение "
+"регистров, которые не используются для возврата значения."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:789
+msgid ""
+"That means we only need to set `CL` once. We have, therefore, added a new "
+"label `.loop` and jump to it for the next byte instead of jumping at "
+"`_start`. We have also added the `.hex` label so we can either have a blank "
+"space or a new line as the third byte of the `buffer`."
+msgstr ""
+"Это означает, что нам нужно установить `CL` только один раз. Поэтому мы "
+"добавили новую метку `.loop` и переходим к ней для следующего байта вместо "
+"перехода к `_start`. Мы также добавили метку `.hex`, чтобы третий байт "
+"`buffer` мог быть либо пробелом, либо новой строкой."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:791
+msgid ""
+"Once you have changed [.filename]#hex.asm# to reflect these changes, type:"
+msgstr "После внесения изменений в файл [.filename]#hex.asm# введите:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:802
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1061
+#, no-wrap
+msgid ""
+"% nasm -f elf hex.asm\n"
+"% ld -s -o hex hex.o\n"
+"% ./hex\n"
+"Hello, World!\n"
+"48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A\n"
+"Here I come!\n"
+"48 65 72 65 20 49 20 63 6F 6D 65 21 0A\n"
+"^D %\n"
+msgstr ""
+"% nasm -f elf hex.asm\n"
+"% ld -s -o hex hex.o\n"
+"% ./hex\n"
+"Hello, World!\n"
+"48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A\n"
+"Here I come!\n"
+"48 65 72 65 20 49 20 63 6F 6D 65 21 0A\n"
+"^D %\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:806
+msgid ""
+"That looks better. But this code is quite inefficient! We are making a "
+"system call for every single byte twice (once to read it, another time to "
+"write the output)."
+msgstr ""
+"Выглядит лучше. Но этот код довольно неэффективен! Мы выполняем системный "
+"вызов для каждого отдельного байта дважды (один раз для чтения и ещё один "
+"для записи вывода)."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:808
+#, no-wrap
+msgid "Buffered Input and Output"
+msgstr "Буферизованный ввод и вывод"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:813
+msgid ""
+"We can improve the efficiency of our code by buffering our input and "
+"output. We create an input buffer and read a whole sequence of bytes at one "
+"time. Then we fetch them one by one from the buffer."
+msgstr ""
+"Мы можем повысить эффективность нашего кода, буферизуя ввод и вывод. Мы "
+"создаём входной буфер и читаем сразу целую последовательность байтов. Затем "
+"мы извлекаем их по одному из буфера."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:816
+msgid ""
+"We also create an output buffer. We store our output in it until it is "
+"full. At that time we ask the kernel to write the contents of the buffer to "
+"[.filename]#stdout#."
+msgstr ""
+"Мы также создаем выходной буфер. Мы сохраняем наш вывод в нем, пока он не "
+"заполнится. В этот момент мы просим ядро записать содержимое буфера в "
+"[.filename]#stdout#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:820
+msgid ""
+"The program ends when there is no more input. But we still need to ask the "
+"kernel to write the contents of our output buffer to [.filename]#stdout# one "
+"last time, otherwise some of our output would make it to the output buffer, "
+"but never be sent out. Do not forget that, or you will be wondering why "
+"some of your output is missing."
+msgstr ""
+"Программа завершается, когда больше нет входных данных. Но нам всё ещё нужно "
+"попросить ядро записать содержимое нашего выходного буфера в "
+"[.filename]#stdout# в последний раз, иначе часть нашего вывода попадёт в "
+"буфер, но так и не будет отправлена. Не забудьте об этом, иначе будете "
+"недоумевать, куда пропала часть вывода."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:826
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:954
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1221
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2339
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3337
+#, no-wrap
+msgid "%define\tBUFSIZE\t2048\n"
+msgstr "%define\tBUFSIZE\t2048\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:829
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:957
+#, no-wrap
+msgid ""
+"section\t.data\n"
+"hex\tdb\t'0123456789ABCDEF'\n"
+msgstr ""
+"section\t.data\n"
+"hex\tdb\t'0123456789ABCDEF'\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:833
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:961
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1230
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2353
+#, no-wrap
+msgid ""
+"section .bss\n"
+"ibuffer\tresb\tBUFSIZE\n"
+"obuffer\tresb\tBUFSIZE\n"
+msgstr ""
+"section .bss\n"
+"ibuffer\tresb\tBUFSIZE\n"
+"obuffer\tresb\tBUFSIZE\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:841
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:969
+#, no-wrap
+msgid ""
+"section\t.text\n"
+"global\t_start\n"
+"_start:\n"
+"\tsub\teax, eax\n"
+"\tsub\tebx, ebx\n"
+"\tsub\tecx, ecx\n"
+"\tmov\tedi, obuffer\n"
+msgstr ""
+"section\t.text\n"
+"global\t_start\n"
+"_start:\n"
+"\tsub\teax, eax\n"
+"\tsub\tebx, ebx\n"
+"\tsub\tecx, ecx\n"
+"\tmov\tedi, obuffer\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:845
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:973
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2501
+#, no-wrap
+msgid ""
+".loop:\n"
+"\t; read a byte from stdin\n"
+"\tcall\tgetchar\n"
+msgstr ""
+".loop:\n"
+"\t; read a byte from stdin\n"
+"\tcall\tgetchar\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:851
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:979
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1283
+#, no-wrap
+msgid ""
+"\t; convert it to hex\n"
+"\tmov\tdl, al\n"
+"\tshr\tal, 4\n"
+"\tmov\tal, [hex+eax]\n"
+"\tcall\tputchar\n"
+msgstr ""
+"\t; convert it to hex\n"
+"\tmov\tdl, al\n"
+"\tshr\tal, 4\n"
+"\tmov\tal, [hex+eax]\n"
+"\tcall\tputchar\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:856
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:984
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1288
+#, no-wrap
+msgid ""
+"\tmov\tal, dl\n"
+"\tand\tal, 0Fh\n"
+"\tmov\tal, [hex+eax]\n"
+"\tcall\tputchar\n"
+msgstr ""
+"\tmov\tal, dl\n"
+"\tand\tal, 0Fh\n"
+"\tmov\tal, [hex+eax]\n"
+"\tcall\tputchar\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:861
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:989
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1293
+#, no-wrap
+msgid ""
+"\tmov\tal, ' '\n"
+"\tcmp\tdl, 0Ah\n"
+"\tjne\t.put\n"
+"\tmov\tal, dl\n"
+msgstr ""
+"\tmov\tal, ' '\n"
+"\tcmp\tdl, 0Ah\n"
+"\tjne\t.put\n"
+"\tmov\tal, dl\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:865
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2512
+#, no-wrap
+msgid ""
+".put:\n"
+"\tcall\tputchar\n"
+"\tjmp\tshort .loop\n"
+msgstr ""
+".put:\n"
+"\tcall\tputchar\n"
+"\tjmp\tshort .loop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:870
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1001
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1305
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2534
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3848
+#, no-wrap
+msgid ""
+"align 4\n"
+"getchar:\n"
+"\tor\tebx, ebx\n"
+"\tjne\t.fetch\n"
+msgstr ""
+"align 4\n"
+"getchar:\n"
+"\tor\tebx, ebx\n"
+"\tjne\t.fetch\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:872
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1003
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1307
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2536
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3850
+#, no-wrap
+msgid "\tcall\tread\n"
+msgstr "\tcall\tread\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:877
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1008
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1312
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2541
+#, no-wrap
+msgid ""
+".fetch:\n"
+"\tlodsb\n"
+"\tdec\tebx\n"
+"\tret\n"
+msgstr ""
+".fetch:\n"
+"\tlodsb\n"
+"\tdec\tebx\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:890
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1021
+#, no-wrap
+msgid ""
+"read:\n"
+"\tpush\tdword BUFSIZE\n"
+"\tmov\tesi, ibuffer\n"
+"\tpush\tesi\n"
+"\tpush\tdword stdin\n"
+"\tsys.read\n"
+"\tadd\tesp, byte 12\n"
+"\tmov\tebx, eax\n"
+"\tor\teax, eax\n"
+"\tje\t.done\n"
+"\tsub\teax, eax\n"
+"\tret\n"
+msgstr ""
+"read:\n"
+"\tpush\tdword BUFSIZE\n"
+"\tmov\tesi, ibuffer\n"
+"\tpush\tesi\n"
+"\tpush\tdword stdin\n"
+"\tsys.read\n"
+"\tadd\tesp, byte 12\n"
+"\tmov\tebx, eax\n"
+"\tor\teax, eax\n"
+"\tje\t.done\n"
+"\tsub\teax, eax\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:896
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1027
+#, no-wrap
+msgid ""
+"align 4\n"
+".done:\n"
+"\tcall\twrite\t\t; flush output buffer\n"
+"\tpush\tdword 0\n"
+"\tsys.exit\n"
+msgstr ""
+"align 4\n"
+".done:\n"
+"\tcall\twrite\t\t; flush output buffer\n"
+"\tpush\tdword 0\n"
+"\tsys.exit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:904
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1035
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1348
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2581
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3887
+#, no-wrap
+msgid ""
+"align 4\n"
+"putchar:\n"
+"\tstosb\n"
+"\tinc\tecx\n"
+"\tcmp\tecx, BUFSIZE\n"
+"\tje\twrite\n"
+"\tret\n"
+msgstr ""
+"align 4\n"
+"putchar:\n"
+"\tstosb\n"
+"\tinc\tecx\n"
+"\tcmp\tecx, BUFSIZE\n"
+"\tje\twrite\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:916
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1047
+#, no-wrap
+msgid ""
+"align 4\n"
+"write:\n"
+"\tsub\tedi, ecx\t; start of buffer\n"
+"\tpush\tecx\n"
+"\tpush\tedi\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+"\tadd\tesp, byte 12\n"
+"\tsub\teax, eax\n"
+"\tsub\tecx, ecx\t; buffer is empty now\n"
+"\tret\n"
+msgstr ""
+"align 4\n"
+"write:\n"
+"\tsub\tedi, ecx\t; start of buffer\n"
+"\tpush\tecx\n"
+"\tpush\tedi\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+"\tadd\tesp, byte 12\n"
+"\tsub\teax, eax\n"
+"\tsub\tecx, ecx\t; buffer is empty now\n"
+"\tret\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:922
+msgid ""
+"We now have a third section in the source code, named `.bss`. This section "
+"is not included in our executable file, and, therefore, cannot be "
+"initialized. We use `resb` instead of `db`. It simply reserves the "
+"requested size of uninitialized memory for our use."
+msgstr ""
+"Теперь у нас есть третий раздел в исходном коде с именем `.bss`. Этот раздел "
+"не включается в исполняемый файл и, следовательно, не может быть "
+"инициализирован. Мы используем `resb` вместо `db`. Это просто резервирует "
+"запрошенный размер неинициализированной памяти для нашего использования."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:926
+msgid ""
+"We take advantage of the fact that the system does not modify the registers: "
+"We use registers for what, otherwise, would have to be global variables "
+"stored in the `.data` section. This is also why the UNIX(R) convention of "
+"passing parameters to system calls on the stack is superior to the Microsoft "
+"convention of passing them in the registers: We can keep the registers for "
+"our own use."
+msgstr ""
+"Мы используем тот факт, что система не изменяет регистры: мы используем "
+"регистры для того, что в противном случае пришлось бы хранить в глобальных "
+"переменных в секции `.data`. Именно поэтому соглашение UNIX(R) о передаче "
+"параметров системных вызовов через стек превосходит соглашение Microsoft о "
+"передаче их в регистрах: мы можем оставить регистры для собственного "
+"использования."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:930
+msgid ""
+"We use `EDI` and `ESI` as pointers to the next byte to be read from or "
+"written to. We use `EBX` and `ECX` to keep count of the number of bytes in "
+"the two buffers, so we know when to dump the output to, or read more input "
+"from, the system."
+msgstr ""
+"Мы используем `EDI` и `ESI` как указатели на следующий байт для чтения или "
+"записи. Мы используем `EBX` и `ECX` для отслеживания количества байтов в "
+"двух буферах, чтобы знать, когда нужно вывести данные в систему или считать "
+"новые данные из системы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:932
+msgid "Let us see how it works now:"
+msgstr "Давайте посмотрим, как это работает сейчас:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:943
+#, no-wrap
+msgid ""
+"% nasm -f elf hex.asm\n"
+"% ld -s -o hex hex.o\n"
+"% ./hex\n"
+"Hello, World!\n"
+"Here I come!\n"
+"48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A\n"
+"48 65 72 65 20 49 20 63 6F 6D 65 21 0A\n"
+"^D %\n"
+msgstr ""
+"% nasm -f elf hex.asm\n"
+"% ld -s -o hex hex.o\n"
+"% ./hex\n"
+"Hello, World!\n"
+"Here I come!\n"
+"48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A\n"
+"48 65 72 65 20 49 20 63 6F 6D 65 21 0A\n"
+"^D %\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:948
+msgid ""
+"Not what you expected? The program did not print the output until we pressed "
+"`^D`. That is easy to fix by inserting three lines of code to write the "
+"output every time we have converted a new line to `0A`. I have marked the "
+"three lines with > (do not copy the > in your [.filename]#hex.asm#)."
+msgstr ""
+"Не то, что вы ожидали? Программа не выводила результат, пока мы не нажали "
+"`^D`. Это легко исправить, добавив три строки кода для вывода результата "
+"каждый раз, когда мы преобразуем новую строку в `0A`. Я пометил эти три "
+"строки символом > (не копируйте > в ваш [.filename]#hex.asm#)."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:996
+#, no-wrap
+msgid ""
+".put:\n"
+"\tcall\tputchar\n"
+">\tcmp\tal, 0Ah\n"
+">\tjne\t.loop\n"
+">\tcall\twrite\n"
+"\tjmp\tshort .loop\n"
+msgstr ""
+".put:\n"
+"\tcall\tputchar\n"
+">\tcmp\tal, 0Ah\n"
+">\tjne\t.loop\n"
+">\tcall\twrite\n"
+"\tjmp\tshort .loop\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1050
+msgid "Now, let us see how it works:"
+msgstr "Теперь давайте посмотрим, как это работает:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1064
+msgid "Not bad for a 644-byte executable, is it!"
+msgstr "Неплохо для исполняемого файла размером 644 байта, не так ли!"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1069
+msgid ""
+"This approach to buffered input/output still contains a hidden danger. I "
+"will discuss-and fix-it later, when I talk about the crossref:x86[x86-"
+"buffered-dark-side,dark side of buffering]."
+msgstr ""
+"Такой подход к буферизированному вводу/выводу всё ещё содержит скрытую "
+"опасность. Я расскажу об этом и исправлю её позже, когда речь пойдёт о "
+"crossref:x86[x86-buffered-dark-side,тёмной стороне буферизации]."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1072
+#, no-wrap
+msgid "How to Unread a Character"
+msgstr "Как отменить чтение символа"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1078
+msgid ""
+"This may be a somewhat advanced topic, mostly of interest to programmers "
+"familiar with the theory of compilers. If you wish, you may "
+"crossref:x86[x86-command-line,skip to the next section], and perhaps read "
+"this later."
+msgstr ""
+"Это может быть несколько сложной темой, в основном представляющей интерес "
+"для программистов, знакомых с теорией компиляторов. Если хотите, вы можете "
+"crossref:x86[x86-command-line, перейти к следующему разделу], и, возможно, "
+"прочитаете это позже."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1084
+msgid ""
+"While our sample program does not require it, more sophisticated filters "
+"often need to look ahead. In other words, they may need to see what the "
+"next character is (or even several characters). If the next character is of "
+"a certain value, it is part of the token currently being processed. "
+"Otherwise, it is not."
+msgstr ""
+"Хотя наш пример программы не требует этого, более сложные фильтры часто "
+"нуждаются в предварительном просмотре. Другими словами, им может "
+"потребоваться узнать, какой следующий символ (или даже несколько символов). "
+"Если следующий символ имеет определённое значение, он является частью "
+"текущего обрабатываемого токена. В противном случае — нет."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1088
+msgid ""
+"For example, you may be parsing the input stream for a textual string (e.g., "
+"when implementing a language compiler): If a character is followed by "
+"another character, or perhaps a digit, it is part of the token you are "
+"processing. If it is followed by white space, or some other value, then it "
+"is not part of the current token."
+msgstr ""
+"Например, вы можете анализировать входной поток на наличие текстовой строки "
+"(например, при реализации компилятора языка): если символ следует за другим "
+"символом или, возможно, цифрой, он является частью обрабатываемой лексемы. "
+"Если за ним следует пробел или другое значение, то он не является частью "
+"текущей лексемы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1090
+msgid ""
+"This presents an interesting problem: How to return the next character back "
+"to the input stream, so it can be read again later?"
+msgstr ""
+"Это представляет интересную проблему: как вернуть следующий символ обратно "
+"во входной поток, чтобы его можно было прочитать позже?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1094
+msgid ""
+"One possible solution is to store it in a character variable, then set a "
+"flag. We can modify `getchar` to check the flag, and if it is set, fetch "
+"the byte from that variable instead of the input buffer, and reset the "
+"flag. But, of course, that slows us down."
+msgstr ""
+"Одно из возможных решений — сохранить его в символьной переменной, а затем "
+"установить флаг. Мы можем изменить `getchar`, чтобы он проверял флаг, и если "
+"он установлен, извлекал байт из этой переменной вместо буфера ввода, а затем "
+"сбрасывал флаг. Но, конечно, это замедляет работу."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1099
+msgid ""
+"The C language has an `ungetc()` function, just for that purpose. Is there "
+"a quick way to implement it in our code? I would like you to scroll back up "
+"and take a look at the `getchar` procedure and see if you can find a nice "
+"and fast solution before reading the next paragraph. Then come back here "
+"and see my own solution."
+msgstr ""
+"В языке C есть функция `ungetc()`, как раз для этой цели. Есть ли быстрый "
+"способ реализовать её в нашем коде? Я хочу, чтобы вы пролистали назад и "
+"взглянули на процедуру `getchar`, и попробовали найти красивое и быстрое "
+"решение, прежде чем читать следующий абзац. Затем вернитесь сюда и "
+"посмотрите моё собственное решение."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1101
+msgid ""
+"The key to returning a character back to the stream is in how we are getting "
+"the characters to start with:"
+msgstr ""
+"Ключом к возвращению символа обратно в поток является то, как мы получаем "
+"символы изначально:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1104
+msgid ""
+"First we check if the buffer is empty by testing the value of `EBX`. If it "
+"is zero, we call the `read` procedure."
+msgstr ""
+"Сначала проверяем, пуст ли буфер, проверяя значение `EBX`. Если оно равно "
+"нулю, вызываем процедуру `read`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1107
+msgid ""
+"If we do have a character available, we use `lodsb`, then decrease the value "
+"of `EBX`. The `lodsb` instruction is effectively identical to:"
+msgstr ""
+"Если у нас есть доступный символ, мы используем `lodsb`, затем уменьшаем "
+"значение `EBX`. Инструкция `lodsb` фактически идентична:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1112
+#, no-wrap
+msgid ""
+"mov\tal, [esi]\n"
+"\tinc\tesi\n"
+msgstr ""
+"mov\tal, [esi]\n"
+"\tinc\tesi\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1117
+msgid ""
+"The byte we have fetched remains in the buffer until the next time `read` is "
+"called. We do not know when that happens, but we do know it will not happen "
+"until the next call to `getchar`. Hence, to \"return\" the last-read byte "
+"back to the stream, all we have to do is decrease the value of `ESI` and "
+"increase the value of `EBX`:"
+msgstr ""
+"Байт, который мы извлекли, остается в буфере до следующего вызова `read`. Мы "
+"не знаем, когда это произойдет, но знаем, что этого не случится до "
+"следующего вызова `getchar`. Следовательно, чтобы \"вернуть\" последний "
+"прочитанный байт обратно в поток, нам достаточно уменьшить значение `ESI` и "
+"увеличить значение `EBX`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1124
+#, no-wrap
+msgid ""
+"ungetc:\n"
+"\tdec\tesi\n"
+"\tinc\tebx\n"
+"\tret\n"
+msgstr ""
+"ungetc:\n"
+"\tdec\tesi\n"
+"\tinc\tebx\n"
+"\tret\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1128
+msgid ""
+"But, be careful! We are perfectly safe doing this if our look-ahead is at "
+"most one character at a time. If we are examining more than one upcoming "
+"character and call `ungetc` several times in a row, it will work most of the "
+"time, but not all the time (and will be tough to debug). Why?"
+msgstr ""
+"Но будьте осторожны! Мы в полной безопасности, если заглядываем вперед "
+"только на один символ за раз. Если же мы проверяем несколько следующих "
+"символов и вызываем `ungetc` несколько раз подряд, это будет работать в "
+"большинстве случаев, но не всегда (и ошибки будет сложно отладить). Почему?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1131
+msgid ""
+"Because as long as `getchar` does not have to call `read`, all of the pre-"
+"read bytes are still in the buffer, and our `ungetc` works without a "
+"glitch. But the moment `getchar` calls `read`, the contents of the buffer "
+"change."
+msgstr ""
+"Потому что пока `getchar` не вызывает `read`, все предварительно прочитанные "
+"байты остаются в буфере, и наш `ungetc` работает без сбоев. Но как только "
+"`getchar` вызывает `read`, содержимое буфера изменяется."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1133
+msgid ""
+"We can always rely on `ungetc` working properly on the last character we "
+"have read with `getchar`, but not on anything we have read before that."
+msgstr ""
+"Мы всегда можем рассчитывать на корректную работу `ungetc` с последним "
+"символом, прочитанным через `getchar`, но не с любым символом, прочитанным "
+"до этого."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1135
+msgid ""
+"If your program reads more than one byte ahead, you have at least two "
+"choices:"
+msgstr ""
+"Если ваша программа читает более одного байта вперед, у вас есть как минимум "
+"два варианта:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1138
+msgid ""
+"If possible, modify the program so it only reads one byte ahead. This is "
+"the simplest solution."
+msgstr ""
+"Если возможно, измените программу так, чтобы она читала только один байт "
+"вперед. Это самое простое решение."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1142
+msgid ""
+"If that option is not available, first of all determine the maximum number "
+"of characters your program needs to return to the input stream at one time. "
+"Increase that number slightly, just to be sure, preferably to a multiple of "
+"16-so it aligns nicely. Then modify the `.bss` section of your code, and "
+"create a small \"spare\" buffer right before your input buffer, something "
+"like this:"
+msgstr ""
+"Если эта опция недоступна, сначала определите максимальное количество "
+"символов, которое вашей программе может потребоваться вернуть во входной "
+"поток за один раз. Увеличьте это число немного, чтобы быть уверенным, "
+"предпочтительно до кратного 16 — так оно будет лучше выровнено. Затем "
+"измените секцию `.bss` в вашем коде и создайте небольшой \"запасной\" буфер "
+"прямо перед вашим входным буфером, примерно так:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1149
+#, no-wrap
+msgid ""
+"section\t.bss\n"
+"\tresb\t16\t; or whatever the value you came up with\n"
+"ibuffer\tresb\tBUFSIZE\n"
+"obuffer\tresb\tBUFSIZE\n"
+msgstr ""
+"section\t.bss\n"
+"\tresb\t16\t; or whatever the value you came up with\n"
+"ibuffer\tresb\tBUFSIZE\n"
+"obuffer\tresb\tBUFSIZE\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1152
+msgid ""
+"You also need to modify your `ungetc` to pass the value of the byte to unget "
+"in `AL`:"
+msgstr ""
+"Вам также необходимо изменить ваш `ungetc`, чтобы передать значение байта "
+"для возврата в `AL`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1160
+#, no-wrap
+msgid ""
+"ungetc:\n"
+"\tdec\tesi\n"
+"\tinc\tebx\n"
+"\tmov\t[esi], al\n"
+"\tret\n"
+msgstr ""
+"ungetc:\n"
+"\tdec\tesi\n"
+"\tinc\tebx\n"
+"\tmov\t[esi], al\n"
+"\tret\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1163
+msgid ""
+"With this modification, you can call `ungetc` up to 17 times in a row safely "
+"(the first call will still be within the buffer, the remaining 16 may be "
+"either within the buffer or within the \"spare\")."
+msgstr ""
+"С этим изменением вы можете безопасно вызывать `ungetc` до 17 раз подряд "
+"(первый вызов всё ещё будет в пределах буфера, остальные 16 могут быть либо "
+"в пределах буфера, либо в пределах \"запасного\" пространства)."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1165
+#, no-wrap
+msgid "Command Line Arguments"
+msgstr "Аргументы командной строки"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1169
+msgid ""
+"Our hex program will be more useful if it can read the names of an input and "
+"output file from its command line, i.e., if it can process the command line "
+"arguments. But... Where are they?"
+msgstr ""
+"Наша программа hex будет полезнее, если она сможет читать имена входного и "
+"выходного файлов из командной строки, т.е. если она сможет обрабатывать "
+"аргументы командной строки. Но... Где они?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1173
+msgid ""
+"Before a UNIX(R) system starts a program, it ``push``es some data on the "
+"stack, then jumps at the `_start` label of the program. Yes, I said jumps, "
+"not calls. That means the data can be accessed by reading `[esp+offset]`, "
+"or by simply ``pop``ping it."
+msgstr ""
+"Прежде чем UNIX(R) система запустит программу, она делает ``push`` для "
+"некоторых данных, помещая их в стек, затем переходит к метке `_start` "
+"программы. Да, я сказал \"переходит\", а не \"вызывает\". Это означает, что "
+"данные можно прочитать с помощью `[esp+offset]` или просто сделать ``pop`` "
+"для них."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1176
+msgid ""
+"The value at the top of the stack contains the number of command line "
+"arguments. It is traditionally called `argc`, for \"argument count.\""
+msgstr ""
+"Значение на вершине стека содержит количество аргументов командной строки. "
+"Оно традиционно называется `argc`, что означает \"argument count\"."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1181
+msgid ""
+"Command line arguments follow next, all `argc` of them. These are typically "
+"referred to as `argv`, for \"argument value(s).\" That is, we get `argv[0]`, "
+"`argv[1]`, `...`, `argv[argc-1]`. These are not the actual arguments, but "
+"pointers to arguments, i.e., memory addresses of the actual arguments. The "
+"arguments themselves are NUL-terminated character strings."
+msgstr ""
+"Далее следуют аргументы командной строки, все `argc` штук. Обычно их "
+"называют `argv`, что означает \"значение(я) аргумента\". То есть мы получаем "
+"`argv[0]`, `argv[1]`, `...`, `argv[argc-1]`. Это не сами аргументы, а "
+"указатели на аргументы, то есть адреса памяти, где находятся реальные "
+"аргументы. Сами аргументы представляют собой строки символов, завершающиеся "
+"нулевым символом ('\\0')."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1183
+msgid ""
+"The `argv` list is followed by a NULL pointer, which is simply a `0`. There "
+"is more, but this is enough for our purposes right now."
+msgstr ""
+"Список `argv` завершается указателем NULL, который представляет собой просто "
+"`0`. Есть и другие детали, но пока этого достаточно для наших целей."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1188
+msgid ""
+"If you have come from the MS-DOS(R) programming environment, the main "
+"difference is that each argument is in a separate string. The second "
+"difference is that there is no practical limit on how many arguments there "
+"can be."
+msgstr ""
+"Если вы перешли из среды программирования MS-DOS(R), основное различие "
+"заключается в том, что каждый аргумент находится в отдельной строке. Второе "
+"различие состоит в том, что нет практического ограничения на количество "
+"аргументов."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1192
+msgid ""
+"Armed with this knowledge, we are almost ready for the next version of "
+"[.filename]#hex.asm#. First, however, we need to add a few lines to "
+"[.filename]#system.inc#:"
+msgstr ""
+"Вооружившись этими знаниями, мы почти готовы к следующей версии "
+"[.filename]#hex.asm#. Однако сначала нам нужно добавить несколько строк в "
+"[.filename]#system.inc#:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1194
+msgid ""
+"First, we need to add two new entries to our list of system call numbers:"
+msgstr ""
+"Сначала нам нужно добавить две новые записи в наш список номеров системных "
+"вызовов:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1199
+#, no-wrap
+msgid ""
+"%define\tSYS_open\t5\n"
+"%define\tSYS_close\t6\n"
+msgstr ""
+"%define\tSYS_open\t5\n"
+"%define\tSYS_close\t6\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1202
+msgid "Then we add two new macros at the end of the file:"
+msgstr "Затем мы добавляем два новых макроса в конце файла:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1208
+#, no-wrap
+msgid ""
+"%macro\tsys.open\t0\n"
+"\tsystem\tSYS_open\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsys.open\t0\n"
+"\tsystem\tSYS_open\n"
+"%endmacro\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1212
+#, no-wrap
+msgid ""
+"%macro\tsys.close\t0\n"
+"\tsystem\tSYS_close\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsys.close\t0\n"
+"\tsystem\tSYS_close\n"
+"%endmacro\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1215
+msgid "Here, then, is our modified source code:"
+msgstr "Вот наш измененный исходный код:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1226
+#, no-wrap
+msgid ""
+"section\t.data\n"
+"fd.in\tdd\tstdin\n"
+"fd.out\tdd\tstdout\n"
+"hex\tdb\t'0123456789ABCDEF'\n"
+msgstr ""
+"section\t.data\n"
+"fd.in\tdd\tstdin\n"
+"fd.out\tdd\tstdout\n"
+"hex\tdb\t'0123456789ABCDEF'\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1236
+#, no-wrap
+msgid ""
+"section\t.text\n"
+"align 4\n"
+"err:\n"
+"\tpush\tdword 1\t\t; return failure\n"
+"\tsys.exit\n"
+msgstr ""
+"section\t.text\n"
+"align 4\n"
+"err:\n"
+"\tpush\tdword 1\t\t; return failure\n"
+"\tsys.exit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1241
+#, no-wrap
+msgid ""
+"align 4\n"
+"global\t_start\n"
+"_start:\n"
+"\tadd\tesp, byte 8\t; discard argc and argv[0]\n"
+msgstr ""
+"align 4\n"
+"global\t_start\n"
+"_start:\n"
+"\tadd\tesp, byte 8\t; discard argc and argv[0]\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1244
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1256
+#, no-wrap
+msgid ""
+"\tpop\tecx\n"
+"\tjecxz\t.init\t\t; no more arguments\n"
+msgstr ""
+"\tpop\tecx\n"
+"\tjecxz\t.init\t\t; no more arguments\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1250
+#, no-wrap
+msgid ""
+"\t; ECX contains the path to input file\n"
+"\tpush\tdword 0\t\t; O_RDONLY\n"
+"\tpush\tecx\n"
+"\tsys.open\n"
+"\tjc\terr\t\t; open failed\n"
+msgstr ""
+"\t; ECX contains the path to input file\n"
+"\tpush\tdword 0\t\t; O_RDONLY\n"
+"\tpush\tecx\n"
+"\tsys.open\n"
+"\tjc\terr\t\t; open failed\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1253
+#, no-wrap
+msgid ""
+"\tadd\tesp, byte 8\n"
+"\tmov\t[fd.in], eax\n"
+msgstr ""
+"\tadd\tesp, byte 8\n"
+"\tmov\t[fd.in], eax\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1264
+#, no-wrap
+msgid ""
+"\t; ECX contains the path to output file\n"
+"\tpush\tdword 420\t; file mode (644 octal)\n"
+"\tpush\tdword 0200h | 0400h | 01h\n"
+"\t; O_CREAT | O_TRUNC | O_WRONLY\n"
+"\tpush\tecx\n"
+"\tsys.open\n"
+"\tjc\terr\n"
+msgstr ""
+"\t; ECX contains the path to output file\n"
+"\tpush\tdword 420\t; file mode (644 octal)\n"
+"\tpush\tdword 0200h | 0400h | 01h\n"
+"\t; O_CREAT | O_TRUNC | O_WRONLY\n"
+"\tpush\tecx\n"
+"\tsys.open\n"
+"\tjc\terr\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1267
+#, no-wrap
+msgid ""
+"\tadd\tesp, byte 12\n"
+"\tmov\t[fd.out], eax\n"
+msgstr ""
+"\tadd\tesp, byte 12\n"
+"\tmov\t[fd.out], eax\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1273
+#, no-wrap
+msgid ""
+".init:\n"
+"\tsub\teax, eax\n"
+"\tsub\tebx, ebx\n"
+"\tsub\tecx, ecx\n"
+"\tmov\tedi, obuffer\n"
+msgstr ""
+".init:\n"
+"\tsub\teax, eax\n"
+"\tsub\tebx, ebx\n"
+"\tsub\tecx, ecx\n"
+"\tmov\tedi, obuffer\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1277
+#, no-wrap
+msgid ""
+".loop:\n"
+"\t; read a byte from input file or stdin\n"
+"\tcall\tgetchar\n"
+msgstr ""
+".loop:\n"
+"\t; read a byte from input file or stdin\n"
+"\tcall\tgetchar\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1300
+#, no-wrap
+msgid ""
+".put:\n"
+"\tcall\tputchar\n"
+"\tcmp\tal, dl\n"
+"\tjne\t.loop\n"
+"\tcall\twrite\n"
+"\tjmp\tshort .loop\n"
+msgstr ""
+".put:\n"
+"\tcall\tputchar\n"
+"\tcmp\tal, dl\n"
+"\tjne\t.loop\n"
+"\tcall\twrite\n"
+"\tjmp\tshort .loop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1325
+#, no-wrap
+msgid ""
+"read:\n"
+"\tpush\tdword BUFSIZE\n"
+"\tmov\tesi, ibuffer\n"
+"\tpush\tesi\n"
+"\tpush\tdword [fd.in]\n"
+"\tsys.read\n"
+"\tadd\tesp, byte 12\n"
+"\tmov\tebx, eax\n"
+"\tor\teax, eax\n"
+"\tje\t.done\n"
+"\tsub\teax, eax\n"
+"\tret\n"
+msgstr ""
+"read:\n"
+"\tpush\tdword BUFSIZE\n"
+"\tmov\tesi, ibuffer\n"
+"\tpush\tesi\n"
+"\tpush\tdword [fd.in]\n"
+"\tsys.read\n"
+"\tadd\tesp, byte 12\n"
+"\tmov\tebx, eax\n"
+"\tor\teax, eax\n"
+"\tje\t.done\n"
+"\tsub\teax, eax\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1329
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2562
+#, no-wrap
+msgid ""
+"align 4\n"
+".done:\n"
+"\tcall\twrite\t\t; flush output buffer\n"
+msgstr ""
+"align 4\n"
+".done:\n"
+"\tcall\twrite\t\t; flush output buffer\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1333
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2566
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3820
+#, no-wrap
+msgid ""
+"\t; close files\n"
+"\tpush\tdword [fd.in]\n"
+"\tsys.close\n"
+msgstr ""
+"\t; close files\n"
+"\tpush\tdword [fd.in]\n"
+"\tsys.close\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1336
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2569
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3823
+#, no-wrap
+msgid ""
+"\tpush\tdword [fd.out]\n"
+"\tsys.close\n"
+msgstr ""
+"\tpush\tdword [fd.out]\n"
+"\tsys.close\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1340
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2573
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3829
+#, no-wrap
+msgid ""
+"\t; return success\n"
+"\tpush\tdword 0\n"
+"\tsys.exit\n"
+msgstr ""
+"\t; return success\n"
+"\tpush\tdword 0\n"
+"\tsys.exit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1360
+#, no-wrap
+msgid ""
+"align 4\n"
+"write:\n"
+"\tsub\tedi, ecx\t; start of buffer\n"
+"\tpush\tecx\n"
+"\tpush\tedi\n"
+"\tpush\tdword [fd.out]\n"
+"\tsys.write\n"
+"\tadd\tesp, byte 12\n"
+"\tsub\teax, eax\n"
+"\tsub\tecx, ecx\t; buffer is empty now\n"
+"\tret\n"
+msgstr ""
+"align 4\n"
+"write:\n"
+"\tsub\tedi, ecx\t; start of buffer\n"
+"\tpush\tecx\n"
+"\tpush\tedi\n"
+"\tpush\tdword [fd.out]\n"
+"\tsys.write\n"
+"\tadd\tesp, byte 12\n"
+"\tsub\teax, eax\n"
+"\tsub\tecx, ecx\t; buffer is empty now\n"
+"\tret\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1364
+msgid ""
+"In our `.data` section we now have two new variables, `fd.in` and `fd.out`. "
+"We store the input and output file descriptors here."
+msgstr ""
+"В нашем разделе `.data` теперь есть две новые переменные, `fd.in` и "
+"`fd.out`. Здесь мы сохраняем дескрипторы файлов для ввода и вывода."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1366
+msgid ""
+"In the `.text` section we have replaced the references to `stdin` and "
+"`stdout` with `[fd.in]` and `[fd.out]`."
+msgstr ""
+"В разделе `.text` мы заменили ссылки с `stdin` и `stdout` на `[fd.in]` и "
+"`[fd.out]`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1369
+msgid ""
+"The `.text` section now starts with a simple error handler, which does "
+"nothing but exit the program with a return value of `1`. The error handler "
+"is before `_start` so we are within a short distance from where the errors "
+"occur."
+msgstr ""
+"Раздел `.text` теперь начинается с простого обработчика ошибок, который "
+"просто завершает программу с кодом возврата `1`. Обработчик ошибок "
+"расположен перед `_start`, чтобы находиться вблизи от места возникновения "
+"ошибок."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1372
+msgid ""
+"Naturally, the program execution still begins at `_start`. First, we remove "
+"`argc` and `argv[0]` from the stack: They are of no interest to us (in this "
+"program, that is)."
+msgstr ""
+"Естественно, выполнение программы по-прежнему начинается с `_start`. Сначала "
+"мы удаляем `argc` и `argv[0]` из стека: они не представляют для нас интереса "
+"(по крайней мере, в этой программе)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1378
+msgid ""
+"We pop `argv[1]` to `ECX`. This register is particularly suited for "
+"pointers, as we can handle NULL pointers with `jecxz`. If `argv[1]` is not "
+"NULL, we try to open the file named in the first argument. Otherwise, we "
+"continue the program as before: Reading from `stdin`, writing to `stdout`. "
+"If we fail to open the input file (e.g., it does not exist), we jump to the "
+"error handler and quit."
+msgstr ""
+"Мы помещаем `argv[1]` в `ECX`. Этот регистр особенно подходит для "
+"указателей, так как мы можем обрабатывать NULL-указатели с помощью `jecxz`. "
+"Если `argv[1]` не равен NULL, мы пытаемся открыть файл с именем, указанным в "
+"первом аргументе. В противном случае продолжаем программу как раньше: чтение "
+"из `stdin`, запись в `stdout`. Если нам не удаётся открыть входной файл "
+"(например, он не существует), мы переходим к обработчику ошибок и завершаем "
+"работу."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1383
+msgid ""
+"If all went well, we now check for the second argument. If it is there, we "
+"open the output file. Otherwise, we send the output to `stdout`. If we "
+"fail to open the output file (e.g., it exists and we do not have the write "
+"permission), we, again, jump to the error handler."
+msgstr ""
+"Если всё прошло успешно, мы проверяем второй аргумент. Если он присутствует, "
+"мы открываем выходной файл. В противном случае, мы отправляем вывод в "
+"`stdout`. Если нам не удаётся открыть выходной файл (например, он существует "
+"и у нас нет прав на запись), мы снова переходим к обработчику ошибок."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1385
+msgid ""
+"The rest of the code is the same as before, except we close the input and "
+"output files before exiting, and, as mentioned, we use `[fd.in]` and "
+"`[fd.out]`."
+msgstr ""
+"Остальная часть кода остается прежней, за исключением того, что мы закрываем "
+"входной и выходной файлы перед завершением, и, как упоминалось, используем "
+"`[fd.in]` и `[fd.out]`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1387
+msgid "Our executable is now a whopping 768 bytes long."
+msgstr "Наш исполняемый файл теперь имеет внушительный размер в 768 байт."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1390
+msgid ""
+"Can we still improve it? Of course! Every program can be improved. Here are "
+"a few ideas of what we could do:"
+msgstr ""
+"Можем ли мы улучшить его еще? Конечно! Каждую программу можно улучшить. Вот "
+"несколько идей, что мы могли бы сделать:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1392
+msgid "Have our error handler print a message to `stderr`."
+msgstr "Сделать наш обработчик ошибок, выводящий сообщение в `stderr`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1393
+msgid "Add error handlers to the `read` and `write` functions."
+msgstr "Добавить обработчики ошибок в функции `read` и `write`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1394
+msgid ""
+"Close `stdin` when we open an input file, `stdout` when we open an output "
+"file."
+msgstr ""
+"Закрывать `stdin` при открытии входного файла, `stdout` при открытии "
+"выходного файла."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1395
+msgid ""
+"Add command line switches, such as `-i` and `-o`, so we can list the input "
+"and output files in any order, or perhaps read from `stdin` and write to a "
+"file."
+msgstr ""
+"Добавить параметры командной строки, такие как `-i` и `-o`, чтобы можно было "
+"перечислять входные и выходные файлы в любом порядке или, возможно, читать "
+"из `stdin` и записывать в файл."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1396
+msgid "Print a usage message if command line arguments are incorrect."
+msgstr ""
+"Выводить сообщение с подсказкой об использовании программы, если аргументы "
+"командной строки указаны неверно."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1398
+msgid ""
+"I shall leave these enhancements as an exercise to the reader: You already "
+"know everything you need to know to implement them."
+msgstr ""
+"Я оставлю эти улучшения в качестве упражнения для читателя: вы уже знаете "
+"всё необходимое для их реализации."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1400
+#, no-wrap
+msgid "UNIX(R) Environment"
+msgstr "Окружение UNIX(R)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1404
+msgid ""
+"An important UNIX(R) concept is the environment, which is defined by "
+"_environment variables_. Some are set by the system, others by you, yet "
+"others by the shell, or any program that loads another program."
+msgstr ""
+"Важным концептом UNIX(R) является окружение, которое определяется "
+"_переменными окружения_. Некоторые из них устанавливаются системой, другие — "
+"пользователем, третьи — оболочкой или любой программой, которая загружает "
+"другую программу."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1406
+#, no-wrap
+msgid "How to Find Environment Variables"
+msgstr "Как найти переменные окружения"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1411
+msgid ""
+"I said earlier that when a program starts executing, the stack contains "
+"`argc` followed by the NULL-terminated `argv` array, followed by something "
+"else. The \"something else\" is the _environment_, or, to be more precise, "
+"a NULL-terminated array of pointers to _environment variables_. This is "
+"often referred to as `env`."
+msgstr ""
+"Я говорил ранее, что когда программа начинает выполняться, в стеке находятся "
+"`argc`, за которым следует массив `argv`, завершающийся NULL, а затем что-то "
+"ещё. Это \"что-то ещё\" — это _окружение_, или, если быть точнее, массив "
+"указателей на _переменные окружения_, завершающийся NULL. Это часто называют "
+"`env`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1414
+msgid ""
+"The structure of `env` is the same as that of `argv`, a list of memory "
+"addresses followed by a NULL (`0`). In this case, there is no `\"envc\"`-we "
+"figure out where the array ends by searching for the final NULL."
+msgstr ""
+"Структура `env` такая же, как у `argv` — список адресов памяти, "
+"заканчивающийся NULL (`0`). В данном случае нет `\"envc\"` — конец массива "
+"определяется поиском последнего NULL."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1417
+msgid ""
+"The variables usually come in the `name=value` format, but sometimes the "
+"`=value` part may be missing. We need to account for that possibility."
+msgstr ""
+"Переменные обычно имеют формат `name=value`, но иногда часть `=value` может "
+"отсутствовать. Необходимо учитывать эту вероятность."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1419
+#, no-wrap
+msgid "webvars"
+msgstr "webvars"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1423
+msgid ""
+"I could just show you some code that prints the environment the same way the "
+"UNIX(R) env command does. But I thought it would be more interesting to "
+"write a simple assembly language CGI utility."
+msgstr ""
+"Я мог бы просто показать вам код, который выводит окружение так же, как "
+"команда UNIX(R) env. Но я подумал, что будет интереснее написать простую CGI-"
+"утилиту на ассемблере."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1425
+#, no-wrap
+msgid "CGI: a Quick Overview"
+msgstr "CGI: краткий обзор"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1428
+msgid ""
+"I have a http://www.whizkidtech.redprince.net/cgi-bin/tutorial[detailed CGI "
+"tutorial] on my web site, but here is a very quick overview of CGI:"
+msgstr ""
+"У меня есть http://www.whizkidtech.redprince.net/cgi-bin/tutorial[подробное "
+"руководство по CGI] на моем веб-сайте, но вот очень краткий обзор CGI:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1430
+msgid ""
+"The web server communicates with the CGI program by setting _environment "
+"variables_."
+msgstr ""
+"Веб-сервер взаимодействует с CGI-программой, устанавливая _переменные "
+"окружения_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1431
+msgid ""
+"The CGI program sends its output to [.filename]#stdout#. The web server "
+"reads it from there."
+msgstr ""
+"Программа CGI отправляет свой вывод в [.filename]#stdout#. Веб-сервер "
+"считывает его оттуда."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1432
+msgid "It must start with an HTTP header followed by two blank lines."
+msgstr ""
+"Он должен начинаться с HTTP-заголовка, за которым следуют две пустые строки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1433
+msgid ""
+"It then prints the HTML code, or whatever other type of data it is producing."
+msgstr ""
+"Затем он выводит HTML-код или любые другие данные, которые он генерирует."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1438
+msgid ""
+"While certain _environment variables_ use standard names, others vary, "
+"depending on the web server. That makes webvars quite a useful diagnostic "
+"tool."
+msgstr ""
+"В то время как некоторые _переменные окружения_ используют стандартные "
+"имена, другие различаются в зависимости от веб-сервера. Это делает программу "
+"webvars весьма полезным инструментом для диагностики."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1441
+#, no-wrap
+msgid "The Code"
+msgstr "Код"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1445
+msgid ""
+"Our webvars program, then, must send out the HTTP header followed by some "
+"HTML mark-up. It then must read the _environment variables_ one by one and "
+"send them out as part of the HTML page."
+msgstr ""
+"Наша программа webvars, таким образом, должна отправить HTTP-заголовок, за "
+"которым следует HTML-разметка. Затем она должна прочитать _переменные "
+"окружения_ одну за другой и отправить их как часть HTML-страницы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1448
+msgid ""
+"The code follows. I placed comments and explanations right inside the code:"
+msgstr "Код приведен ниже. Я разместил комментарии и пояснения прямо в коде:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1485
+#, no-wrap
+msgid ""
+";;;;;;; webvars.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+";\n"
+"; Copyright (c) 2000 G. Adam Stanislav\n"
+"; All rights reserved.\n"
+";\n"
+"; Redistribution and use in source and binary forms, with or without\n"
+"; modification, are permitted provided that the following conditions\n"
+"; are met:\n"
+"; 1. Redistributions of source code must retain the above copyright\n"
+"; notice, this list of conditions and the following disclaimer.\n"
+"; 2. Redistributions in binary form must reproduce the above copyright\n"
+"; notice, this list of conditions and the following disclaimer in the\n"
+"; documentation and/or other materials provided with the distribution.\n"
+";\n"
+"; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n"
+"; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
+"; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
+"; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n"
+"; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
+"; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n"
+"; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
+"; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n"
+"; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
+"; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
+"; SUCH DAMAGE.\n"
+";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+";\n"
+"; Version 1.0\n"
+";\n"
+"; Started:\t 8-Dec-2000\n"
+"; Updated:\t 8-Dec-2000\n"
+";\n"
+";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+"%include\t'system.inc'\n"
+msgstr ""
+";;;;;;; webvars.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+";\n"
+"; Copyright (c) 2000 G. Adam Stanislav\n"
+"; All rights reserved.\n"
+";\n"
+"; Redistribution and use in source and binary forms, with or without\n"
+"; modification, are permitted provided that the following conditions\n"
+"; are met:\n"
+"; 1. Redistributions of source code must retain the above copyright\n"
+"; notice, this list of conditions and the following disclaimer.\n"
+"; 2. Redistributions in binary form must reproduce the above copyright\n"
+"; notice, this list of conditions and the following disclaimer in the\n"
+"; documentation and/or other materials provided with the distribution.\n"
+";\n"
+"; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n"
+"; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
+"; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
+"; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n"
+"; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
+"; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n"
+"; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
+"; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n"
+"; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
+"; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
+"; SUCH DAMAGE.\n"
+";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+";\n"
+"; Version 1.0\n"
+";\n"
+"; Started:\t 8-Dec-2000\n"
+"; Updated:\t 8-Dec-2000\n"
+";\n"
+";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+"%include\t'system.inc'\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1522
+#, no-wrap
+msgid ""
+"section\t.data\n"
+"http\tdb\t'Content-type: text/html', 0Ah, 0Ah\n"
+"\tdb\t'<?xml version=\"1.0\" encoding=\"utf-8\"?>', 0Ah\n"
+"\tdb\t'<!DOCTYPE html PUBLIC \"-//W3C/DTD XHTML Strict//EN\" '\n"
+"\tdb\t'\"DTD/xhtml1-strict.dtd\">', 0Ah\n"
+"\tdb\t'<html xmlns=\"http://www.w3.org/1999/xhtml\" '\n"
+"\tdb\t'xml.lang=\"en\" lang=\"en\">', 0Ah\n"
+"\tdb\t'<head>', 0Ah\n"
+"\tdb\t'<title>Web Environment</title>', 0Ah\n"
+"\tdb\t'<meta name=\"author\" content=\"G. Adam Stanislav\" />', 0Ah\n"
+"\tdb\t'</head>', 0Ah, 0Ah\n"
+"\tdb\t'<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#0000ff\" '\n"
+"\tdb\t'vlink=\"#840084\" alink=\"#0000ff\">', 0Ah\n"
+"\tdb\t'<div class=\"webvars\">', 0Ah\n"
+"\tdb\t'<h1>Web Environment</h1>', 0Ah\n"
+"\tdb\t'<p>The following <b>environment variables</b> are defined '\n"
+"\tdb\t'on this web server:</p>', 0Ah, 0Ah\n"
+"\tdb\t'<table align=\"center\" width=\"80\" border=\"0\" cellpadding=\"10\" '\n"
+"\tdb\t'cellspacing=\"0\" class=\"webvars\">', 0Ah\n"
+"httplen\tequ\t$-http\n"
+"left\tdb\t'<tr>', 0Ah\n"
+"\tdb\t'<td class=\"name\"><tt>'\n"
+"leftlen\tequ\t$-left\n"
+"middle\tdb\t'</tt></td>', 0Ah\n"
+"\tdb\t'<td class=\"value\"><tt><b>'\n"
+"midlen\tequ\t$-middle\n"
+"undef\tdb\t'<i>(undefined)</i>'\n"
+"undeflen\tequ\t$-undef\n"
+"right\tdb\t'</b></tt></td>', 0Ah\n"
+"\tdb\t'</tr>', 0Ah\n"
+"rightlen\tequ\t$-right\n"
+"wrap\tdb\t'</table>', 0Ah\n"
+"\tdb\t'</div>', 0Ah\n"
+"\tdb\t'</body>', 0Ah\n"
+"\tdb\t'</html>', 0Ah, 0Ah\n"
+"wraplen\tequ\t$-wrap\n"
+msgstr ""
+"section\t.data\n"
+"http\tdb\t'Content-type: text/html', 0Ah, 0Ah\n"
+"\tdb\t'<?xml version=\"1.0\" encoding=\"utf-8\"?>', 0Ah\n"
+"\tdb\t'<!DOCTYPE html PUBLIC \"-//W3C/DTD XHTML Strict//EN\" '\n"
+"\tdb\t'\"DTD/xhtml1-strict.dtd\">', 0Ah\n"
+"\tdb\t'<html xmlns=\"http://www.w3.org/1999/xhtml\" '\n"
+"\tdb\t'xml.lang=\"en\" lang=\"en\">', 0Ah\n"
+"\tdb\t'<head>', 0Ah\n"
+"\tdb\t'<title>Web Environment</title>', 0Ah\n"
+"\tdb\t'<meta name=\"author\" content=\"G. Adam Stanislav\" />', 0Ah\n"
+"\tdb\t'</head>', 0Ah, 0Ah\n"
+"\tdb\t'<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#0000ff\" '\n"
+"\tdb\t'vlink=\"#840084\" alink=\"#0000ff\">', 0Ah\n"
+"\tdb\t'<div class=\"webvars\">', 0Ah\n"
+"\tdb\t'<h1>Web Environment</h1>', 0Ah\n"
+"\tdb\t'<p>The following <b>environment variables</b> are defined '\n"
+"\tdb\t'on this web server:</p>', 0Ah, 0Ah\n"
+"\tdb\t'<table align=\"center\" width=\"80\" border=\"0\" cellpadding=\"10\" '\n"
+"\tdb\t'cellspacing=\"0\" class=\"webvars\">', 0Ah\n"
+"httplen\tequ\t$-http\n"
+"left\tdb\t'<tr>', 0Ah\n"
+"\tdb\t'<td class=\"name\"><tt>'\n"
+"leftlen\tequ\t$-left\n"
+"middle\tdb\t'</tt></td>', 0Ah\n"
+"\tdb\t'<td class=\"value\"><tt><b>'\n"
+"midlen\tequ\t$-middle\n"
+"undef\tdb\t'<i>(undefined)</i>'\n"
+"undeflen\tequ\t$-undef\n"
+"right\tdb\t'</b></tt></td>', 0Ah\n"
+"\tdb\t'</tr>', 0Ah\n"
+"rightlen\tequ\t$-right\n"
+"wrap\tdb\t'</table>', 0Ah\n"
+"\tdb\t'</div>', 0Ah\n"
+"\tdb\t'</body>', 0Ah\n"
+"\tdb\t'</html>', 0Ah, 0Ah\n"
+"wraplen\tequ\t$-wrap\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1532
+#, no-wrap
+msgid ""
+"section\t.text\n"
+"global\t_start\n"
+"_start:\n"
+"\t; First, send out all the http and xhtml stuff that is\n"
+"\t; needed before we start showing the environment\n"
+"\tpush\tdword httplen\n"
+"\tpush\tdword http\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+msgstr ""
+"section\t.text\n"
+"global\t_start\n"
+"_start:\n"
+"\t; First, send out all the http and xhtml stuff that is\n"
+"\t; needed before we start showing the environment\n"
+"\tpush\tdword httplen\n"
+"\tpush\tdword http\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1536
+#, no-wrap
+msgid ""
+"\t; Now find how far on the stack the environment pointers\n"
+"\t; are. We have 12 bytes we have pushed before \"argc\"\n"
+"\tmov\teax, [esp+12]\n"
+msgstr ""
+"\t; Now find how far on the stack the environment pointers\n"
+"\t; are. We have 12 bytes we have pushed before \"argc\"\n"
+"\tmov\teax, [esp+12]\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1551
+#, no-wrap
+msgid ""
+"\t; We need to remove the following from the stack:\n"
+"\t;\n"
+"\t;\tThe 12 bytes we pushed for sys.write\n"
+"\t;\tThe 4 bytes of argc\n"
+"\t;\tThe EAX*4 bytes of argv\n"
+"\t;\tThe 4 bytes of the NULL after argv\n"
+"\t;\n"
+"\t; Total:\n"
+"\t;\t20 + eax * 4\n"
+"\t;\n"
+"\t; Because stack grows down, we need to ADD that many bytes\n"
+"\t; to ESP.\n"
+"\tlea\tesp, [esp+20+eax*4]\n"
+"\tcld\t\t; This should already be the case, but let's be sure.\n"
+msgstr ""
+"\t; We need to remove the following from the stack:\n"
+"\t;\n"
+"\t;\tThe 12 bytes we pushed for sys.write\n"
+"\t;\tThe 4 bytes of argc\n"
+"\t;\tThe EAX*4 bytes of argv\n"
+"\t;\tThe 4 bytes of the NULL after argv\n"
+"\t;\n"
+"\t; Total:\n"
+"\t;\t20 + eax * 4\n"
+"\t;\n"
+"\t; Because stack grows down, we need to ADD that many bytes\n"
+"\t; to ESP.\n"
+"\tlea\tesp, [esp+20+eax*4]\n"
+"\tcld\t\t; This should already be the case, but let's be sure.\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1557
+#, no-wrap
+msgid ""
+"\t; Loop through the environment, printing it out\n"
+".loop:\n"
+"\tpop\tedi\n"
+"\tor\tedi, edi\t; Done yet?\n"
+"\tje\tnear .wrap\n"
+msgstr ""
+"\t; Loop through the environment, printing it out\n"
+".loop:\n"
+"\tpop\tedi\n"
+"\tor\tedi, edi\t; Done yet?\n"
+"\tje\tnear .wrap\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1563
+#, no-wrap
+msgid ""
+"\t; Print the left part of HTML\n"
+"\tpush\tdword leftlen\n"
+"\tpush\tdword left\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+msgstr ""
+"\t; Print the left part of HTML\n"
+"\tpush\tdword leftlen\n"
+"\tpush\tdword left\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1574
+#, no-wrap
+msgid ""
+"\t; It may be tempting to search for the '=' in the env string next.\n"
+"\t; But it is possible there is no '=', so we search for the\n"
+"\t; terminating NUL first.\n"
+"\tmov\tesi, edi\t; Save start of string\n"
+"\tsub\tecx, ecx\n"
+"\tnot\tecx\t\t; ECX = FFFFFFFF\n"
+"\tsub\teax, eax\n"
+"repne\tscasb\n"
+"\tnot\tecx\t\t; ECX = string length + 1\n"
+"\tmov\tebx, ecx\t; Save it in EBX\n"
+msgstr ""
+"\t; It may be tempting to search for the '=' in the env string next.\n"
+"\t; But it is possible there is no '=', so we search for the\n"
+"\t; terminating NUL first.\n"
+"\tmov\tesi, edi\t; Save start of string\n"
+"\tsub\tecx, ecx\n"
+"\tnot\tecx\t\t; ECX = FFFFFFFF\n"
+"\tsub\teax, eax\n"
+"repne\tscasb\n"
+"\tnot\tecx\t\t; ECX = string length + 1\n"
+"\tmov\tebx, ecx\t; Save it in EBX\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1581
+#, no-wrap
+msgid ""
+"\t; Now is the time to find '='\n"
+"\tmov\tedi, esi\t; Start of string\n"
+"\tmov\tal, '='\n"
+"repne\tscasb\n"
+"\tnot\tecx\n"
+"\tadd\tecx, ebx\t; Length of name\n"
+msgstr ""
+"\t; Now is the time to find '='\n"
+"\tmov\tedi, esi\t; Start of string\n"
+"\tmov\tal, '='\n"
+"repne\tscasb\n"
+"\tnot\tecx\n"
+"\tadd\tecx, ebx\t; Length of name\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1586
+#, no-wrap
+msgid ""
+"\tpush\tecx\n"
+"\tpush\tesi\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+msgstr ""
+"\tpush\tecx\n"
+"\tpush\tesi\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1592
+#, no-wrap
+msgid ""
+"\t; Print the middle part of HTML table code\n"
+"\tpush\tdword midlen\n"
+"\tpush\tdword middle\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+msgstr ""
+"\t; Print the middle part of HTML table code\n"
+"\tpush\tdword midlen\n"
+"\tpush\tdword middle\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1596
+#, no-wrap
+msgid ""
+"\t; Find the length of the value\n"
+"\tnot\tecx\n"
+"\tlea\tebx, [ebx+ecx-1]\n"
+msgstr ""
+"\t; Find the length of the value\n"
+"\tnot\tecx\n"
+"\tlea\tebx, [ebx+ecx-1]\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1600
+#, no-wrap
+msgid ""
+"\t; Print \"undefined\" if 0\n"
+"\tor\tebx, ebx\n"
+"\tjne\t.value\n"
+msgstr ""
+"\t; Print \"undefined\" if 0\n"
+"\tor\tebx, ebx\n"
+"\tjne\t.value\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1603
+#, no-wrap
+msgid ""
+"\tmov\tebx, undeflen\n"
+"\tmov\tedi, undef\n"
+msgstr ""
+"\tmov\tebx, undeflen\n"
+"\tmov\tedi, undef\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1609
+#, no-wrap
+msgid ""
+".value:\n"
+"\tpush\tebx\n"
+"\tpush\tedi\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+msgstr ""
+".value:\n"
+"\tpush\tebx\n"
+"\tpush\tedi\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1615
+#, no-wrap
+msgid ""
+"\t; Print the right part of the table row\n"
+"\tpush\tdword rightlen\n"
+"\tpush\tdword right\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+msgstr ""
+"\t; Print the right part of the table row\n"
+"\tpush\tdword rightlen\n"
+"\tpush\tdword right\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1618
+#, no-wrap
+msgid ""
+"\t; Get rid of the 60 bytes we have pushed\n"
+"\tadd\tesp, byte 60\n"
+msgstr ""
+"\t; Get rid of the 60 bytes we have pushed\n"
+"\tadd\tesp, byte 60\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1621
+#, no-wrap
+msgid ""
+"\t; Get the next variable\n"
+"\tjmp\t.loop\n"
+msgstr ""
+"\t; Get the next variable\n"
+"\tjmp\t.loop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1628
+#, no-wrap
+msgid ""
+".wrap:\n"
+"\t; Print the rest of HTML\n"
+"\tpush\tdword wraplen\n"
+"\tpush\tdword wrap\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+msgstr ""
+".wrap:\n"
+"\t; Print the rest of HTML\n"
+"\tpush\tdword wraplen\n"
+"\tpush\tdword wrap\n"
+"\tpush\tdword stdout\n"
+"\tsys.write\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1632
+#, no-wrap
+msgid ""
+"\t; Return success\n"
+"\tpush\tdword 0\n"
+"\tsys.exit\n"
+msgstr ""
+"\t; Return success\n"
+"\tpush\tdword 0\n"
+"\tsys.exit\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1636
+msgid ""
+"This code produces a 1,396-byte executable. Most of it is data, i.e., the "
+"HTML mark-up we need to send out."
+msgstr ""
+"Этот код создает исполняемый файл размером 1 396 байт. Большая его часть — "
+"это данные, а именно HTML-разметка, которую нам нужно отправить."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1638
+msgid "Assemble and link it as usual:"
+msgstr "Запустите ассемблер и слинкуйте как обычно:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1643
+#, no-wrap
+msgid ""
+"% nasm -f elf webvars.asm\n"
+"% ld -s -o webvars webvars.o\n"
+msgstr ""
+"% nasm -f elf webvars.asm\n"
+"% ld -s -o webvars webvars.o\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1647
+msgid ""
+"To use it, you need to upload [.filename]#webvars# to your web server. "
+"Depending on how your web server is set up, you may have to store it in a "
+"special [.filename]#cgi-bin# directory, or perhaps rename it with a "
+"[.filename]#.cgi# extension."
+msgstr ""
+"Для использования необходимо загрузить [.filename]#webvars# на ваш веб-"
+"сервер. В зависимости от настроек веб-сервера, возможно, потребуется "
+"разместить его в специальном каталоге [.filename]#cgi-bin# или переименовать "
+"с расширением [.filename]#.cgi#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1651
+msgid ""
+"Then you need to use your browser to view its output. To see its output on "
+"my web server, please go to http://www.int80h.org/webvars/[http://"
+"www.int80h.org/webvars/]. If curious about the additional environment "
+"variables present in a password protected web directory, go to http://"
+"www.int80h.org/private/[http://www.int80h.org/private/], using the name "
+"`asm` and password `programmer`."
+msgstr ""
+"Затем вам нужно использовать браузер для просмотра вывода. Чтобы увидеть "
+"вывод на моем веб-сервере, перейдите по ссылке http://www.int80h.org/webvars/"
+"[http://www.int80h.org/webvars/]. Если вам интересно узнать о дополнительных "
+"переменных окружения в защищенном паролем веб-каталоге, перейдите по адресу "
+"http://www.int80h.org/private/[http://www.int80h.org/private/], используя "
+"имя `asm` и пароль `programmer`."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1653
+#, no-wrap
+msgid "Working with Files"
+msgstr "Работа с файлами"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1658
+msgid ""
+"We have already done some basic file work: We know how to open and close "
+"them, how to read and write them using buffers. But UNIX(R) offers much "
+"more functionality when it comes to files. We will examine some of it in "
+"this section, and end up with a nice file conversion utility."
+msgstr ""
+"Мы уже выполнили некоторые базовые операции с файлами: мы знаем, как их "
+"открывать и закрывать, как читать и записывать их с использованием буферов. "
+"Однако UNIX(R) предлагает гораздо больше возможностей при работе с файлами. "
+"В этом разделе мы рассмотрим некоторые из них и в итоге создадим удобную "
+"утилиту для преобразования файлов."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1661
+msgid ""
+"Indeed, let us start at the end, that is, with the file conversion utility. "
+"It always makes programming easier when we know from the start what the end "
+"product is supposed to do."
+msgstr ""
+"В самом деле, начнем с конца, то есть с утилиты преобразования файлов. "
+"Всегда легче программировать, когда с самого начала известно, каким должен "
+"быть конечный продукт."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1667
+msgid ""
+"One of the first programs I wrote for UNIX(R) was link:ftp://ftp.int80h.org/"
+"unix/tuc/[tuc], a text-to-UNIX(R) file converter. It converts a text file "
+"from other operating systems to a UNIX(R) text file. In other words, it "
+"changes from different kind of line endings to the newline convention of "
+"UNIX(R). It saves the output in a different file. Optionally, it converts "
+"a UNIX(R) text file to a DOS text file."
+msgstr ""
+"Одной из первых программ, которые я написал для UNIX(R), была link:ftp://"
+"ftp.int80h.org/unix/tuc/[tuc] — конвертер текста в файл UNIX(R). Она "
+"преобразует текстовый файл из других операционных систем в текстовый файл "
+"UNIX(R). Другими словами, она изменяет различные виды окончаний строк на "
+"стандартные для UNIX(R). Результат сохраняется в другом файле. По желанию, "
+"она может преобразовать текстовый файл UNIX(R) в текстовый файл DOS."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1671
+msgid ""
+"I have used tuc extensively, but always only to convert from some other OS "
+"to UNIX(R), never the other way. I have always wished it would just "
+"overwrite the file instead of me having to send the output to a different "
+"file. Most of the time, I end up using it like this:"
+msgstr ""
+"Я широко использовал `tuc`, но всегда только для преобразования из какой-"
+"либо другой ОС в UNIX(R), никогда наоборот. Мне всегда хотелось, чтобы он "
+"просто перезаписывал файл, вместо того чтобы мне приходилось отправлять "
+"вывод в другой файл. В большинстве случаев я в итоге использую его так:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1676
+#, no-wrap
+msgid ""
+"% tuc myfile tempfile\n"
+"% mv tempfile myfile\n"
+msgstr ""
+"% tuc myfile tempfile\n"
+"% mv tempfile myfile\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1679
+msgid ""
+"It would be nice to have a ftuc, i.e., _fast tuc_, and use it like this:"
+msgstr ""
+"Было бы здорово иметь ftuc, т.е., _быстрый tuc_, и использовать его вот так:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1683
+#, no-wrap
+msgid "% ftuc myfile\n"
+msgstr "% ftuc myfile\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1686
+msgid ""
+"In this chapter, then, we will write ftuc in assembly language (the original "
+"tuc is in C), and study various file-oriented kernel services in the process."
+msgstr ""
+"В этой главе мы напишем ftuc на языке ассемблера (оригинальный tuc написан "
+"на C) и в процессе изучим различные файловые сервисы ядра."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1688
+msgid ""
+"At first sight, such a file conversion is very simple: All you have to do is "
+"strip the carriage returns, right?"
+msgstr ""
+"На первый взгляд, такое преобразование файла кажется очень простым: нужно "
+"всего лишь удалить символы возврата каретки, верно?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1690
+msgid ""
+"If you answered yes, think again: That approach will work most of the time "
+"(at least with MS DOS text files), but will fail occasionally."
+msgstr ""
+"Если вы ответили «да», подумайте ещё раз: такой подход будет работать в "
+"большинстве случаев (по крайней мере, с текстовыми файлами MS DOS), но "
+"иногда он будет давать сбой."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1695
+msgid ""
+"The problem is that not all non UNIX(R) text files end their line with the "
+"carriage return / line feed sequence. Some use carriage returns without "
+"line feeds. Others combine several blank lines into a single carriage "
+"return followed by several line feeds. And so on."
+msgstr ""
+"Проблема в том, что не все текстовые файлы, не относящиеся к UNIX(R), "
+"завершают строки последовательностью возврата каретки / перевода строки. "
+"Некоторые используют возврат каретки без перевода строки. Другие объединяют "
+"несколько пустых строк в один возврат каретки, за которым следует несколько "
+"переводов строки. И так далее."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1697
+msgid ""
+"A text file converter, then, must be able to handle any possible line "
+"endings:"
+msgstr ""
+"Конвертер текстовых файлов, следовательно, должен уметь обрабатывать любые "
+"возможные окончания строк:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1699
+msgid "carriage return / line feed"
+msgstr "возврат каретки (carriage return) / перевод строки (line feed)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1700
+msgid "carriage return"
+msgstr "возврат каретки"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1701
+msgid "line feed / carriage return"
+msgstr "перевод строки / возврат каретки"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1702
+msgid "line feed"
+msgstr "перевод строки"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1704
+msgid ""
+"It should also handle files that use some kind of a combination of the above "
+"(e.g., carriage return followed by several line feeds)."
+msgstr ""
+"Это также должно обрабатывать файлы, использующие комбинации вышеуказанного "
+"(например, возврат каретки с последующими несколькими переводами строки)."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1706
+#, no-wrap
+msgid "Finite State Machine"
+msgstr "Конечный автомат"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1713
+msgid ""
+"The problem is easily solved by the use of a technique called _finite state "
+"machine_, originally developed by the designers of digital electronic "
+"circuits. A _finite state machine_ is a digital circuit whose output is "
+"dependent not only on its input but on its previous input, i.e., on its "
+"state. The microprocessor is an example of a _finite state machine_: Our "
+"assembly language code is assembled to machine language in which some "
+"assembly language code produces a single byte of machine language, while "
+"others produce several bytes. As the microprocessor fetches the bytes from "
+"the memory one by one, some of them simply change its state rather than "
+"produce some output. When all the bytes of the op code are fetched, the "
+"microprocessor produces some output, or changes the value of a register, etc."
+msgstr ""
+"Проблема легко решается с использованием техники, называемой _конечный "
+"автомат_, изначально разработанной создателями цифровых электронных схем. "
+"_Конечный автомат_ — это цифровая схема, выход которой зависит не только от "
+"входа, но и от предыдущего входа, то есть от её состояния. Микропроцессор "
+"является примером _конечного автомата_: наш код на языке ассемблера "
+"транслируется в машинный язык, где одни инструкции ассемблера превращаются в "
+"один байт машинного кода, а другие — в несколько байтов. Когда "
+"микропроцессор извлекает байты из памяти один за другим, некоторые из них "
+"просто изменяют его состояние, а не производят какой-либо выходной сигнал. "
+"После извлечения всех байтов кода операции микропроцессор выдает выходной "
+"сигнал, изменяет значение регистра и т. д."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1716
+msgid ""
+"Because of that, all software is essentially a sequence of state "
+"instructions for the microprocessor. Nevertheless, the concept of _finite "
+"state machine_ is useful in software design as well."
+msgstr ""
+"Из-за этого всё программное обеспечение по сути представляет собой "
+"последовательность инструкций состояния для микропроцессора. Тем не менее, "
+"концепция _конечного автомата_ также полезна при проектировании программного "
+"обеспечения."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1719
+msgid ""
+"Our text file converter can be designer as a _finite state machine_ with "
+"three possible states. We could call them states 0-2, but it will make our "
+"life easier if we give them symbolic names:"
+msgstr ""
+"Наш конвертер текстовых файлов можно представить в виде _конечного автомата_ "
+"с тремя возможными состояниями. Мы могли бы назвать их состояниями 0-2, но "
+"будет проще, если дадим им символические имена:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1721
+msgid "ordinary"
+msgstr "ordinary"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1722
+msgid "cr"
+msgstr "cr"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1723
+msgid "lf"
+msgstr "lf"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1725
+msgid ""
+"Our program will start in the ordinary state. During this state, the program "
+"action depends on its input as follows:"
+msgstr ""
+"Наша программа начнёт работу в обычном состоянии. В этом состоянии действие "
+"программы зависит от её входных данных следующим образом:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1728
+msgid ""
+"If the input is anything other than a carriage return or line feed, the "
+"input is simply passed on to the output. The state remains unchanged."
+msgstr ""
+"Если ввод представляет собой что-либо, кроме возврата каретки или перевода "
+"строки, ввод просто передаётся на вывод. Состояние остаётся неизменным."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1730
+msgid ""
+"If the input is a carriage return, the state is changed to cr. The input is "
+"then discarded, i.e., no output is made."
+msgstr ""
+"Если входной символ — возврат каретки, состояние изменяется на cr. Затем "
+"входной символ отбрасывается, т.е. вывод не производится."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1732
+msgid ""
+"If the input is a line feed, the state is changed to lf. The input is then "
+"discarded."
+msgstr ""
+"Если входной символ является переводом строки, состояние изменяется на lf. "
+"Затем входной символ отбрасывается."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1735
+msgid ""
+"Whenever we are in the cr state, it is because the last input was a carriage "
+"return, which was unprocessed. What our software does in this state again "
+"depends on the current input:"
+msgstr ""
+"Всякий раз, когда мы находимся в состоянии `cr`, это означает, что последним "
+"вводом был символ возврата каретки, который не был обработан. Действия "
+"нашего программного обеспечения в этом состоянии снова зависят от текущего "
+"ввода:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1737
+msgid ""
+"If the input is anything other than a carriage return or line feed, output a "
+"line feed, then output the input, then change the state to ordinary."
+msgstr ""
+"Если ввод отличается от возврата каретки или перевода строки, вывести "
+"перевод строки, затем вывести ввод, а затем изменить состояние на обычное."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1738
+msgid ""
+"If the input is a carriage return, we have received two (or more) carriage "
+"returns in a row. We discard the input, we output a line feed, and leave the "
+"state unchanged."
+msgstr ""
+"Если входной символ — возврат каретки, значит, мы получили два (или более) "
+"возврата каретки подряд. Мы отбрасываем ввод, выводим перевод строки и "
+"оставляем состояние неизменным."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1739
+msgid ""
+"If the input is a line feed, we output the line feed and change the state to "
+"ordinary. Note that this is not the same as the first case above - if we "
+"tried to combine them, we would be outputting two line feeds instead of one."
+msgstr ""
+"Если входной символ — это перевод строки, мы выводим перевод строки и меняем "
+"состояние на обычное. Обратите внимание, что это не то же самое, что в "
+"первом случае выше — если бы мы попытались объединить их, мы бы выводили два "
+"перевода строки вместо одного."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1743
+msgid ""
+"Finally, we are in the lf state after we have received a line feed that was "
+"not preceded by a carriage return. This will happen when our file already "
+"is in UNIX(R) format, or whenever several lines in a row are expressed by a "
+"single carriage return followed by several line feeds, or when line ends "
+"with a line feed / carriage return sequence. Here is how we need to handle "
+"our input in this state:"
+msgstr ""
+"Наконец, мы находимся в состоянии `lf` после получения перевода строки, "
+"которому не предшествовал возврат каретки. Это произойдет, если наш файл уже "
+"в формате UNIX(R), или когда несколько строк подряд выражены одним возвратом "
+"каретки, за которым следуют несколько переводов строк, или когда строка "
+"заканчивается последовательностью перевода строки / возврата каретки. Вот "
+"как нам нужно обрабатывать ввод в этом состоянии:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1745
+msgid ""
+"If the input is anything other than a carriage return or line feed, we "
+"output a line feed, then output the input, then change the state to "
+"ordinary. This is exactly the same action as in the cr state upon receiving "
+"the same kind of input."
+msgstr ""
+"Если ввод отличается от возврата каретки или перевода строки, мы выводим "
+"перевод строки, затем выводим ввод и изменяем состояние на обычное. Это "
+"действие полностью совпадает с действием в состоянии `cr` при получении "
+"аналогичного ввода."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1746
+msgid ""
+"If the input is a carriage return, we discard the input, we output a line "
+"feed, then change the state to ordinary."
+msgstr ""
+"Если ввод представляет собой символ возврата каретки, мы отбрасываем ввод, "
+"выводим символ перевода строки, затем изменяем состояние на обычное."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1747
+msgid ""
+"If the input is a line feed, we output the line feed, and leave the state "
+"unchanged."
+msgstr ""
+"Если входной символ — перевод строки, мы выводим перевод строки и оставляем "
+"состояние неизменным."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1749
+#, no-wrap
+msgid "The Final State"
+msgstr "Конечное состояние"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1754
+msgid ""
+"The above _finite state machine_ works for the entire file, but leaves the "
+"possibility that the final line end will be ignored. That will happen "
+"whenever the file ends with a single carriage return or a single line feed. "
+"I did not think of it when I wrote tuc, just to discover that occasionally "
+"it strips the last line ending."
+msgstr ""
+"Приведённый выше _конечный автомат_ работает для всего файла, но оставляет "
+"возможность, что последний конец строки будет проигнорирован. Это "
+"произойдёт, если файл заканчивается одиночным возвратом каретки или "
+"одиночным переводом строки. Я не подумал об этом, когда писал tuc, и лишь "
+"позже обнаружил, что иногда он удаляет последний конец строки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1757
+msgid ""
+"This problem is easily fixed by checking the state after the entire file was "
+"processed. If the state is not ordinary, we simply need to output one last "
+"line feed."
+msgstr ""
+"Эта проблема легко решается проверкой состояния после обработки всего файла. "
+"Если состояние не является обычным, нам просто нужно вывести последний "
+"перевод строки."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1763
+msgid ""
+"Now that we have expressed our algorithm as a _finite state machine_, we "
+"could easily design a dedicated digital electronic circuit (a \"chip\") to "
+"do the conversion for us. Of course, doing so would be considerably more "
+"expensive than writing an assembly language program."
+msgstr ""
+"Теперь, когда мы выразили наш алгоритм в виде _конечного автомата_, мы могли "
+"бы легко разработать специализированную цифровую электронную схему («чип») "
+"для выполнения преобразования. Конечно, это было бы значительно дороже, чем "
+"написание программы на языке ассемблера."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1766
+#, no-wrap
+msgid "The Output Counter"
+msgstr "Счетчик вывода"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1771
+msgid ""
+"Because our file conversion program may be combining two characters into "
+"one, we need to use an output counter. We initialize it to `0`, and "
+"increase it every time we send a character to the output. At the end of the "
+"program, the counter will tell us what size we need to set the file to."
+msgstr ""
+"Поскольку наша программа преобразования файлов может объединять два символа "
+"в один, нам необходимо использовать счётчик вывода. Мы инициализируем его "
+"значением `0` и увеличиваем каждый раз, когда отправляем символ на выход. В "
+"конце программы счётчик укажет, какой размер необходимо установить для файла."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1773
+#, no-wrap
+msgid "Implementing FSM in Software"
+msgstr "Реализация конечного автомата в программном обеспечении"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1777
+msgid ""
+"The hardest part of working with a _finite state machine_ is analyzing the "
+"problem and expressing it as a _finite state machine_. That accomplished, "
+"the software almost writes itself."
+msgstr ""
+"Самая сложная часть работы с _конечным автоматом_ — это анализ задачи и её "
+"представление в виде _конечного автомата_. После этого программное "
+"обеспечение практически пишется само."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1780
+msgid ""
+"In a high-level language, such as C, there are several main approaches. One "
+"is to use a `switch` statement which chooses what function should be run. "
+"For example,"
+msgstr ""
+"На языке высокого уровня, таком как C, существует несколько основных "
+"подходов. Один из них — использование оператора `switch`, который выбирает, "
+"какую функцию следует выполнить. Например,"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1795
+#, no-wrap
+msgid ""
+"switch (state) {\n"
+"\tdefault:\n"
+"\tcase REGULAR:\n"
+"\t\tregular(inputchar);\n"
+"\t\tbreak;\n"
+"\tcase CR:\n"
+"\t\tcr(inputchar);\n"
+"\t\tbreak;\n"
+"\tcase LF:\n"
+"\t\tlf(inputchar);\n"
+"\t\tbreak;\n"
+"\t}\n"
+msgstr ""
+"switch (state) {\n"
+"\tdefault:\n"
+"\tcase REGULAR:\n"
+"\t\tregular(inputchar);\n"
+"\t\tbreak;\n"
+"\tcase CR:\n"
+"\t\tcr(inputchar);\n"
+"\t\tbreak;\n"
+"\tcase LF:\n"
+"\t\tlf(inputchar);\n"
+"\t\tbreak;\n"
+"\t}\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1798
+msgid ""
+"Another approach is by using an array of function pointers, something like "
+"this:"
+msgstr ""
+"Еще один подход заключается в использовании массива указателей на функции, "
+"например:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1802
+#, no-wrap
+msgid "(output[state])(inputchar);\n"
+msgstr "(output[state])(inputchar);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1805
+msgid ""
+"Yet another is to have `state` be a function pointer, set to point at the "
+"appropriate function:"
+msgstr ""
+"Еще один вариант — сделать `state` указателем на функцию, установив его на "
+"соответствующую функцию:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1809
+#, no-wrap
+msgid "(*state)(inputchar);\n"
+msgstr "(*state)(inputchar);\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1813
+msgid ""
+"This is the approach we will use in our program because it is very easy to "
+"do in assembly language, and very fast, too. We will simply keep the "
+"address of the right procedure in `EBX`, and then just issue:"
+msgstr ""
+"Это подход, который мы будем использовать в нашей программе, потому что его "
+"очень легко реализовать на языке ассемблера, и он также очень быстрый. Мы "
+"просто будем хранить адрес нужной процедуры в `EBX`, а затем выполним:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1817
+#, no-wrap
+msgid "call\tebx\n"
+msgstr "call\tebx\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1821
+msgid ""
+"This is possibly faster than hardcoding the address in the code because the "
+"microprocessor does not have to fetch the address from the memory-it is "
+"already stored in one of its registers. I said _possibly_ because with the "
+"caching modern microprocessors do, either way may be equally fast."
+msgstr ""
+"Это возможно быстрее, чем жестко задавать адрес в коде, потому что "
+"микропроцессору не нужно извлекать адрес из памяти — он уже хранится в одном "
+"из его регистров. Я сказал _возможно_, потому что с учетом кэширования, "
+"которое выполняют современные микропроцессоры, оба варианта могут быть "
+"одинаково быстрыми."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1823
+#, no-wrap
+msgid "Memory Mapped Files"
+msgstr "Отображенные в память файлы"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1827
+msgid ""
+"Because our program works on a single file, we cannot use the approach that "
+"worked for us before, i.e., to read from an input file and to write to an "
+"output file."
+msgstr ""
+"Поскольку наша программа работает с одним файлом, мы не можем использовать "
+"подход, который работал ранее, то есть чтение из входного файла и запись в "
+"выходной файл."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1835
+msgid ""
+"UNIX(R) allows us to map a file, or a section of a file, into memory. To do "
+"that, we first need to open the file with the appropriate read/write flags. "
+"Then we use the `mmap` system call to map it into the memory. One nice "
+"thing about `mmap` is that it automatically works with virtual memory: We "
+"can map more of the file into the memory than we have physical memory "
+"available, yet still access it through regular memory op codes, such as "
+"`mov`, `lods`, and `stos`. Whatever changes we make to the memory image of "
+"the file will be written to the file by the system. We do not even have to "
+"keep the file open: As long as it stays mapped, we can read from it and "
+"write to it."
+msgstr ""
+"UNIX(R) позволяет нам отображать файл или его часть в память. Для этого "
+"сначала необходимо открыть файл с соответствующими флагами чтения/записи. "
+"Затем мы используем системный вызов `mmap`, чтобы отобразить его в память. "
+"Одно из преимуществ `mmap` заключается в том, что он автоматически работает "
+"с виртуальной памятью: мы можем отобразить в память больше файла, чем "
+"имеется физической памяти, и при этом обращаться к нему с помощью обычных "
+"команд работы с памятью, таких как `mov`, `lods` и `stos`. Все изменения, "
+"внесённые в память, отображённую из файла, будут записаны в файл системой. "
+"Нам даже не нужно держать файл открытым: пока он остаётся отображённым, мы "
+"можем читать из него и записывать в него."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1838
+msgid ""
+"The 32-bit Intel microprocessors can access up to four gigabytes of memory - "
+"physical or virtual. The FreeBSD system allows us to use up to a half of it "
+"for file mapping."
+msgstr ""
+"32-разрядные микропроцессоры Intel могут адресовать до четырёх гигабайт "
+"памяти — физической или виртуальной. Система FreeBSD позволяет использовать "
+"до половины этого объёма для отображения файлов."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1842
+msgid ""
+"For simplicity sake, in this tutorial we will only convert files that can be "
+"mapped into the memory in their entirety. There are probably not too many "
+"text files that exceed two gigabytes in size. If our program encounters "
+"one, it will simply display a message suggesting we use the original tuc "
+"instead."
+msgstr ""
+"Для упрощения в этом руководстве мы будем преобразовывать только файлы, "
+"которые могут быть полностью отображены в памяти. Вероятно, не так много "
+"текстовых файлов превышают размер в два гигабайта. Если наша программа "
+"встретит такой файл, она просто выведет сообщение с предложением "
+"использовать оригинальный tuc."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1848
+msgid ""
+"If you examine your copy of [.filename]#syscalls.master#, you will find two "
+"separate syscalls named `mmap`. This is because of evolution of UNIX(R): "
+"There was the traditional BSD `mmap`, syscall 71. That one was superseded "
+"by the POSIX(R) `mmap`, syscall 197. The FreeBSD system supports both "
+"because older programs were written by using the original BSD version. But "
+"new software uses the POSIX(R) version, which is what we will use."
+msgstr ""
+"Если вы изучите свою копию файла [.filename]#syscalls.master#, вы найдёте "
+"два отдельных системных вызова с именем `mmap`. Это связано с эволюцией "
+"UNIX(R): существовал традиционный BSD `mmap`, системный вызов 71. Он был "
+"заменён на POSIX(R) `mmap`, системный вызов 197. Система FreeBSD "
+"поддерживает оба, поскольку старые программы были написаны с использованием "
+"оригинальной BSD-версии. Но новое программное обеспечение использует версию "
+"POSIX(R), которую мы и будем применять."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1850
+msgid "The [.filename]#syscalls.master# lists the POSIX(R) version like this:"
+msgstr ""
+"В [.filename]#syscalls.master# POSIX(R) версия указана следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1855
+#, no-wrap
+msgid ""
+"197\tSTD\tBSD\t{ caddr_t mmap(caddr_t addr, size_t len, int prot, \\\n"
+"\t\t\t int flags, int fd, long pad, off_t pos); }\n"
+msgstr ""
+"197\tSTD\tBSD\t{ caddr_t mmap(caddr_t addr, size_t len, int prot, \\\n"
+"\t\t\t int flags, int fd, long pad, off_t pos); }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1859
+msgid ""
+"This differs slightly from what man:mmap[2] says. That is because "
+"man:mmap[2] describes the C version."
+msgstr ""
+"Это немного отличается от того, что указано в man:mmap[2]. Это связано с "
+"тем, что man:mmap[2] описывает версию на языке C."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1863
+msgid ""
+"The difference is in the `long pad` argument, which is not present in the C "
+"version. However, the FreeBSD syscalls add a 32-bit pad after ``push``ing a "
+"64-bit argument. In this case, `off_t` is a 64-bit value."
+msgstr ""
+"Разница заключается в аргументе `long pad`, который отсутствует в версии на "
+"C. Однако системные вызовы FreeBSD добавляют 32-битный заполнитель после "
+"``push`` 64-битного аргумента. В данном случае `off_t` является 64-битным "
+"значением."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1865
+msgid ""
+"When we are finished working with a memory-mapped file, we unmap it with the "
+"`munmap` syscall:"
+msgstr ""
+"Когда мы завершаем работу с файлом, отображённым в память, мы освобождаем "
+"его с помощью системного вызова `munmap`:"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1869
+msgid ""
+"For an in-depth treatment of `mmap`, see W. Richard Stevens' http://"
+"www.int80h.org/cgi-bin/isbn?isbn=0130810819[Unix Network Programming, Volume "
+"2, Chapter 12]."
+msgstr ""
+"Для подробного изучения `mmap` см. http://www.int80h.org/cgi-bin/isbn?"
+"isbn=0130810819[Unix Network Programming, Volume 2, Chapter 12] У. Ричарда "
+"Стивенса."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1872
+#, no-wrap
+msgid "Determining File Size"
+msgstr "Определение размера файла"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1875
+msgid ""
+"Because we need to tell `mmap` how many bytes of the file to map into the "
+"memory, and because we want to map the entire file, we need to determine the "
+"size of the file."
+msgstr ""
+"Поскольку нам нужно указать `mmap`, сколько байт файла отобразить в памяти, "
+"и поскольку мы хотим отобразить весь файл, нам необходимо определить его "
+"размер."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1878
+msgid ""
+"We can use the `fstat` syscall to get all the information about an open file "
+"that the system can give us. That includes the file size."
+msgstr ""
+"Мы можем использовать системный вызов `fstat` для получения всей информации "
+"об открытом файле, которую система может нам предоставить. Это включает в "
+"себя размер файла."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1881
+msgid ""
+"Again, [.filename]#syscalls.master# lists two versions of `fstat`, a "
+"traditional one (syscall 62), and a POSIX(R) one (syscall 189). Naturally, "
+"we will use the POSIX(R) version:"
+msgstr ""
+"Вновь, в [.filename]#syscalls.master# указаны две версии `fstat`: "
+"традиционная (системный вызов 62) и POSIX(R) (системный вызов 189). "
+"Естественно, мы будем использовать версию POSIX(R):"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1885
+#, no-wrap
+msgid "189\tSTD\tPOSIX\t{ int fstat(int fd, struct stat *sb); }\n"
+msgstr "189\tSTD\tPOSIX\t{ int fstat(int fd, struct stat *sb); }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1889
+msgid ""
+"This is a very straightforward call: We pass to it the address of a `stat` "
+"structure and the descriptor of an open file. It will fill out the contents "
+"of the `stat` structure."
+msgstr ""
+"Это очень простой вызов: мы передаем ему адрес структуры `stat` и дескриптор "
+"открытого файла. Он заполнит содержимое структуры `stat`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1892
+msgid ""
+"I do, however, have to say that I tried to declare the `stat` structure in "
+"the `.bss` section, and `fstat` did not like it: It set the carry flag "
+"indicating an error. After I changed the code to allocate the structure on "
+"the stack, everything was working fine."
+msgstr ""
+"Однако должен сказать, что я пытался объявить структуру `stat` в секции "
+"`.bss`, и `fstat` это не понравилось: был установлен флаг переноса, "
+"указывающий на ошибку. После того как я изменил код, чтобы разместить "
+"структуру в стеке, всё заработало как надо."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1894
+#, no-wrap
+msgid "Changing the File Size"
+msgstr "Изменение размера файла"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1898
+msgid ""
+"Because our program may combine carriage return / line feed sequences into "
+"straight line feeds, our output may be smaller than our input. However, "
+"since we are placing our output into the same file we read the input from, "
+"we may have to change the size of the file."
+msgstr ""
+"Поскольку наша программа может объединять последовательности возврата "
+"каретки / перевода строки в простые переводы строк, наш вывод может быть "
+"меньше, чем ввод. Однако, так как мы помещаем вывод в тот же файл, из "
+"которого читаем ввод, нам может потребоваться изменить размер файла."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1901
+msgid ""
+"The `ftruncate` system call allows us to do just that. Despite its somewhat "
+"misleading name, the `ftruncate` system call can be used to both truncate "
+"the file (make it smaller) and to grow it."
+msgstr ""
+"Системный вызов `ftruncate` позволяет нам сделать именно это. Несмотря на "
+"название , несколько вводящее в звблуждение, системный вызов `ftruncate` "
+"может использоваться как для усечения файла (уменьшения его размера), так и "
+"для его увеличения."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1904
+msgid ""
+"And yes, we will find two versions of `ftruncate` in "
+"[.filename]#syscalls.master#, an older one (130), and a newer one (201). We "
+"will use the newer one:"
+msgstr ""
+"И да, мы найдем две версии `ftruncate` в [.filename]#syscalls.master#, "
+"старую (130) и новую (201). Мы будем использовать новую:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1908
+#, no-wrap
+msgid "201\tSTD\tBSD\t{ int ftruncate(int fd, int pad, off_t length); }\n"
+msgstr "201\tSTD\tBSD\t{ int ftruncate(int fd, int pad, off_t length); }\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1911
+msgid "Please note that this one contains a `int pad` again."
+msgstr "Обратите внимание, что здесь снова присутствует `int pad`."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1913
+#, no-wrap
+msgid "ftuc"
+msgstr "ftuc"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1918
+msgid ""
+"We now know everything we need to write ftuc. We start by adding some new "
+"lines in [.filename]#system.inc#. First, we define some constants and "
+"structures, somewhere at or near the beginning of the file:"
+msgstr ""
+"Теперь мы знаем всё, что нужно для написания ftuc. Начнём с добавления "
+"нескольких новых строк в [.filename]#system.inc#. Сначала определим "
+"некоторые константы и структуры, где-нибудь в начале или около начала файла:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1925
+#, no-wrap
+msgid ""
+";;;;;;; open flags\n"
+"%define\tO_RDONLY\t0\n"
+"%define\tO_WRONLY\t1\n"
+"%define\tO_RDWR\t2\n"
+msgstr ""
+";;;;;;; open flags\n"
+"%define\tO_RDONLY\t0\n"
+"%define\tO_WRONLY\t1\n"
+"%define\tO_RDWR\t2\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1934
+#, no-wrap
+msgid ""
+";;;;;;; mmap flags\n"
+"%define\tPROT_NONE\t0\n"
+"%define\tPROT_READ\t1\n"
+"%define\tPROT_WRITE\t2\n"
+"%define\tPROT_EXEC\t4\n"
+";;\n"
+"%define\tMAP_SHARED\t0001h\n"
+"%define\tMAP_PRIVATE\t0002h\n"
+msgstr ""
+";;;;;;; mmap flags\n"
+"%define\tPROT_NONE\t0\n"
+"%define\tPROT_READ\t1\n"
+"%define\tPROT_WRITE\t2\n"
+"%define\tPROT_EXEC\t4\n"
+";;\n"
+"%define\tMAP_SHARED\t0001h\n"
+"%define\tMAP_PRIVATE\t0002h\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1958
+#, no-wrap
+msgid ""
+";;;;;;; stat structure\n"
+"struc\tstat\n"
+"st_dev\t\tresd\t1\t; = 0\n"
+"st_ino\t\tresd\t1\t; = 4\n"
+"st_mode\t\tresw\t1\t; = 8, size is 16 bits\n"
+"st_nlink\tresw\t1\t; = 10, ditto\n"
+"st_uid\t\tresd\t1\t; = 12\n"
+"st_gid\t\tresd\t1\t; = 16\n"
+"st_rdev\t\tresd\t1\t; = 20\n"
+"st_atime\tresd\t1\t; = 24\n"
+"st_atimensec\tresd\t1\t; = 28\n"
+"st_mtime\tresd\t1\t; = 32\n"
+"st_mtimensec\tresd\t1\t; = 36\n"
+"st_ctime\tresd\t1\t; = 40\n"
+"st_ctimensec\tresd\t1\t; = 44\n"
+"st_size\t\tresd\t2\t; = 48, size is 64 bits\n"
+"st_blocks\tresd\t2\t; = 56, ditto\n"
+"st_blksize\tresd\t1\t; = 64\n"
+"st_flags\tresd\t1\t; = 68\n"
+"st_gen\t\tresd\t1\t; = 72\n"
+"st_lspare\tresd\t1\t; = 76\n"
+"st_qspare\tresd\t4\t; = 80\n"
+"endstruc\n"
+msgstr ""
+";;;;;;; stat structure\n"
+"struc\tstat\n"
+"st_dev\t\tresd\t1\t; = 0\n"
+"st_ino\t\tresd\t1\t; = 4\n"
+"st_mode\t\tresw\t1\t; = 8, size is 16 bits\n"
+"st_nlink\tresw\t1\t; = 10, ditto\n"
+"st_uid\t\tresd\t1\t; = 12\n"
+"st_gid\t\tresd\t1\t; = 16\n"
+"st_rdev\t\tresd\t1\t; = 20\n"
+"st_atime\tresd\t1\t; = 24\n"
+"st_atimensec\tresd\t1\t; = 28\n"
+"st_mtime\tresd\t1\t; = 32\n"
+"st_mtimensec\tresd\t1\t; = 36\n"
+"st_ctime\tresd\t1\t; = 40\n"
+"st_ctimensec\tresd\t1\t; = 44\n"
+"st_size\t\tresd\t2\t; = 48, size is 64 bits\n"
+"st_blocks\tresd\t2\t; = 56, ditto\n"
+"st_blksize\tresd\t1\t; = 64\n"
+"st_flags\tresd\t1\t; = 68\n"
+"st_gen\t\tresd\t1\t; = 72\n"
+"st_lspare\tresd\t1\t; = 76\n"
+"st_qspare\tresd\t4\t; = 80\n"
+"endstruc\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1961
+msgid "We define the new syscalls:"
+msgstr "Мы определяем новые системные вызовы:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1968
+#, no-wrap
+msgid ""
+"%define\tSYS_mmap\t197\n"
+"%define\tSYS_munmap\t73\n"
+"%define\tSYS_fstat\t189\n"
+"%define\tSYS_ftruncate\t201\n"
+msgstr ""
+"%define\tSYS_mmap\t197\n"
+"%define\tSYS_munmap\t73\n"
+"%define\tSYS_fstat\t189\n"
+"%define\tSYS_ftruncate\t201\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1971
+msgid "We add the macros for their use:"
+msgstr "Добавляем макросы для их использования:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1977
+#, no-wrap
+msgid ""
+"%macro\tsys.mmap\t0\n"
+"\tsystem\tSYS_mmap\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsys.mmap\t0\n"
+"\tsystem\tSYS_mmap\n"
+"%endmacro\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1981
+#, no-wrap
+msgid ""
+"%macro\tsys.munmap\t0\n"
+"\tsystem\tSYS_munmap\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsys.munmap\t0\n"
+"\tsystem\tSYS_munmap\n"
+"%endmacro\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1985
+#, no-wrap
+msgid ""
+"%macro\tsys.ftruncate\t0\n"
+"\tsystem\tSYS_ftruncate\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsys.ftruncate\t0\n"
+"\tsystem\tSYS_ftruncate\n"
+"%endmacro\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1989
+#, no-wrap
+msgid ""
+"%macro\tsys.fstat\t0\n"
+"\tsystem\tSYS_fstat\n"
+"%endmacro\n"
+msgstr ""
+"%macro\tsys.fstat\t0\n"
+"\tsystem\tSYS_fstat\n"
+"%endmacro\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:1992
+msgid "And here is our code:"
+msgstr "И вот наш код:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2005
+#, no-wrap
+msgid ""
+";;;;;;; Fast Text-to-Unix Conversion (ftuc.asm) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+";;\n"
+";; Started:\t21-Dec-2000\n"
+";; Updated:\t22-Dec-2000\n"
+";;\n"
+";; Copyright 2000 G. Adam Stanislav.\n"
+";; All rights reserved.\n"
+";;\n"
+";;;;;;; v.1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+"%include\t'system.inc'\n"
+msgstr ""
+";;;;;;; Fast Text-to-Unix Conversion (ftuc.asm) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+";;\n"
+";; Started:\t21-Dec-2000\n"
+";; Updated:\t22-Dec-2000\n"
+";;\n"
+";; Copyright 2000 G. Adam Stanislav.\n"
+";; All rights reserved.\n"
+";;\n"
+";;;;;;; v.1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+"%include\t'system.inc'\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2019
+#, no-wrap
+msgid ""
+"section\t.data\n"
+"\tdb\t'Copyright 2000 G. Adam Stanislav.', 0Ah\n"
+"\tdb\t'All rights reserved.', 0Ah\n"
+"usg\tdb\t'Usage: ftuc filename', 0Ah\n"
+"usglen\tequ\t$-usg\n"
+"co\tdb\t\"ftuc: Can't open file.\", 0Ah\n"
+"colen\tequ\t$-co\n"
+"fae\tdb\t'ftuc: File access error.', 0Ah\n"
+"faelen\tequ\t$-fae\n"
+"ftl\tdb\t'ftuc: File too long, use regular tuc instead.', 0Ah\n"
+"ftllen\tequ\t$-ftl\n"
+"mae\tdb\t'ftuc: Memory allocation error.', 0Ah\n"
+"maelen\tequ\t$-mae\n"
+msgstr ""
+"section\t.data\n"
+"\tdb\t'Copyright 2000 G. Adam Stanislav.', 0Ah\n"
+"\tdb\t'All rights reserved.', 0Ah\n"
+"usg\tdb\t'Usage: ftuc filename', 0Ah\n"
+"usglen\tequ\t$-usg\n"
+"co\tdb\t\"ftuc: Can't open file.\", 0Ah\n"
+"colen\tequ\t$-co\n"
+"fae\tdb\t'ftuc: File access error.', 0Ah\n"
+"faelen\tequ\t$-fae\n"
+"ftl\tdb\t'ftuc: File too long, use regular tuc instead.', 0Ah\n"
+"ftllen\tequ\t$-ftl\n"
+"mae\tdb\t'ftuc: Memory allocation error.', 0Ah\n"
+"maelen\tequ\t$-mae\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2021
+#, no-wrap
+msgid "section\t.text\n"
+msgstr "section\t.text\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2027
+#, no-wrap
+msgid ""
+"align 4\n"
+"memerr:\n"
+"\tpush\tdword maelen\n"
+"\tpush\tdword mae\n"
+"\tjmp\tshort error\n"
+msgstr ""
+"align 4\n"
+"memerr:\n"
+"\tpush\tdword maelen\n"
+"\tpush\tdword mae\n"
+"\tjmp\tshort error\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2033
+#, no-wrap
+msgid ""
+"align 4\n"
+"toolong:\n"
+"\tpush\tdword ftllen\n"
+"\tpush\tdword ftl\n"
+"\tjmp\tshort error\n"
+msgstr ""
+"align 4\n"
+"toolong:\n"
+"\tpush\tdword ftllen\n"
+"\tpush\tdword ftl\n"
+"\tjmp\tshort error\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2039
+#, no-wrap
+msgid ""
+"align 4\n"
+"facerr:\n"
+"\tpush\tdword faelen\n"
+"\tpush\tdword fae\n"
+"\tjmp\tshort error\n"
+msgstr ""
+"align 4\n"
+"facerr:\n"
+"\tpush\tdword faelen\n"
+"\tpush\tdword fae\n"
+"\tjmp\tshort error\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2045
+#, no-wrap
+msgid ""
+"align 4\n"
+"cantopen:\n"
+"\tpush\tdword colen\n"
+"\tpush\tdword co\n"
+"\tjmp\tshort error\n"
+msgstr ""
+"align 4\n"
+"cantopen:\n"
+"\tpush\tdword colen\n"
+"\tpush\tdword co\n"
+"\tjmp\tshort error\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2050
+#, no-wrap
+msgid ""
+"align 4\n"
+"usage:\n"
+"\tpush\tdword usglen\n"
+"\tpush\tdword usg\n"
+msgstr ""
+"align 4\n"
+"usage:\n"
+"\tpush\tdword usglen\n"
+"\tpush\tdword usg\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2054
+#, no-wrap
+msgid ""
+"error:\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+msgstr ""
+"error:\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2057
+#, no-wrap
+msgid ""
+"\tpush\tdword 1\n"
+"\tsys.exit\n"
+msgstr ""
+"\tpush\tdword 1\n"
+"\tsys.exit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2065
+#, no-wrap
+msgid ""
+"align 4\n"
+"global\t_start\n"
+"_start:\n"
+"\tpop\teax\t\t; argc\n"
+"\tpop\teax\t\t; program name\n"
+"\tpop\tecx\t\t; file to convert\n"
+"\tjecxz\tusage\n"
+msgstr ""
+"align 4\n"
+"global\t_start\n"
+"_start:\n"
+"\tpop\teax\t\t; argc\n"
+"\tpop\teax\t\t; program name\n"
+"\tpop\tecx\t\t; file to convert\n"
+"\tjecxz\tusage\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2069
+#, no-wrap
+msgid ""
+"\tpop\teax\n"
+"\tor\teax, eax\t; Too many arguments?\n"
+"\tjne\tusage\n"
+msgstr ""
+"\tpop\teax\n"
+"\tor\teax, eax\t; Too many arguments?\n"
+"\tjne\tusage\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2075
+#, no-wrap
+msgid ""
+"\t; Open the file\n"
+"\tpush\tdword O_RDWR\n"
+"\tpush\tecx\n"
+"\tsys.open\n"
+"\tjc\tcantopen\n"
+msgstr ""
+"\t; Open the file\n"
+"\tpush\tdword O_RDWR\n"
+"\tpush\tecx\n"
+"\tsys.open\n"
+"\tjc\tcantopen\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2077
+#, no-wrap
+msgid "\tmov\tebp, eax\t; Save fd\n"
+msgstr "\tmov\tebp, eax\t; Save fd\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2080
+#, no-wrap
+msgid ""
+"\tsub\tesp, byte stat_size\n"
+"\tmov\tebx, esp\n"
+msgstr ""
+"\tsub\tesp, byte stat_size\n"
+"\tmov\tebx, esp\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2086
+#, no-wrap
+msgid ""
+"\t; Find file size\n"
+"\tpush\tebx\n"
+"\tpush\tebp\t\t; fd\n"
+"\tsys.fstat\n"
+"\tjc\tfacerr\n"
+msgstr ""
+"\t; Find file size\n"
+"\tpush\tebx\n"
+"\tpush\tebp\t\t; fd\n"
+"\tsys.fstat\n"
+"\tjc\tfacerr\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2088
+#, no-wrap
+msgid "\tmov\tedx, [ebx + st_size + 4]\n"
+msgstr "\tmov\tedx, [ebx + st_size + 4]\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2096
+#, no-wrap
+msgid ""
+"\t; File is too long if EDX != 0 ...\n"
+"\tor\tedx, edx\n"
+"\tjne\tnear toolong\n"
+"\tmov\tecx, [ebx + st_size]\n"
+"\t; ... or if it is above 2 GB\n"
+"\tor\tecx, ecx\n"
+"\tjs\tnear toolong\n"
+msgstr ""
+"\t; File is too long if EDX != 0 ...\n"
+"\tor\tedx, edx\n"
+"\tjne\tnear toolong\n"
+"\tmov\tecx, [ebx + st_size]\n"
+"\t; ... or if it is above 2 GB\n"
+"\tor\tecx, ecx\n"
+"\tjs\tnear toolong\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2099
+#, no-wrap
+msgid ""
+"\t; Do nothing if the file is 0 bytes in size\n"
+"\tjecxz\t.quit\n"
+msgstr ""
+"\t; Do nothing if the file is 0 bytes in size\n"
+"\tjecxz\t.quit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2111
+#, no-wrap
+msgid ""
+"\t; Map the entire file in memory\n"
+"\tpush\tedx\n"
+"\tpush\tedx\t\t; starting at offset 0\n"
+"\tpush\tedx\t\t; pad\n"
+"\tpush\tebp\t\t; fd\n"
+"\tpush\tdword MAP_SHARED\n"
+"\tpush\tdword PROT_READ | PROT_WRITE\n"
+"\tpush\tecx\t\t; entire file size\n"
+"\tpush\tedx\t\t; let system decide on the address\n"
+"\tsys.mmap\n"
+"\tjc\tnear memerr\n"
+msgstr ""
+"\t; Map the entire file in memory\n"
+"\tpush\tedx\n"
+"\tpush\tedx\t\t; starting at offset 0\n"
+"\tpush\tedx\t\t; pad\n"
+"\tpush\tebp\t\t; fd\n"
+"\tpush\tdword MAP_SHARED\n"
+"\tpush\tdword PROT_READ | PROT_WRITE\n"
+"\tpush\tecx\t\t; entire file size\n"
+"\tpush\tedx\t\t; let system decide on the address\n"
+"\tsys.mmap\n"
+"\tjc\tnear memerr\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2116
+#, no-wrap
+msgid ""
+"\tmov\tedi, eax\n"
+"\tmov\tesi, eax\n"
+"\tpush\tecx\t\t; for SYS_munmap\n"
+"\tpush\tedi\n"
+msgstr ""
+"\tmov\tedi, eax\n"
+"\tmov\tesi, eax\n"
+"\tpush\tecx\t\t; for SYS_munmap\n"
+"\tpush\tedi\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2121
+#, no-wrap
+msgid ""
+"\t; Use EBX for state machine\n"
+"\tmov\tebx, ordinary\n"
+"\tmov\tah, 0Ah\n"
+"\tcld\n"
+msgstr ""
+"\t; Use EBX for state machine\n"
+"\tmov\tebx, ordinary\n"
+"\tmov\tah, 0Ah\n"
+"\tcld\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2126
+#, no-wrap
+msgid ""
+".loop:\n"
+"\tlodsb\n"
+"\tcall\tebx\n"
+"\tloop\t.loop\n"
+msgstr ""
+".loop:\n"
+"\tlodsb\n"
+"\tcall\tebx\n"
+"\tloop\t.loop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2129
+#, no-wrap
+msgid ""
+"\tcmp\tebx, ordinary\n"
+"\tje\t.filesize\n"
+msgstr ""
+"\tcmp\tebx, ordinary\n"
+"\tje\t.filesize\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2134
+#, no-wrap
+msgid ""
+"\t; Output final lf\n"
+"\tmov\tal, ah\n"
+"\tstosb\n"
+"\tinc\tedx\n"
+msgstr ""
+"\t; Output final lf\n"
+"\tmov\tal, ah\n"
+"\tstosb\n"
+"\tinc\tedx\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2142
+#, no-wrap
+msgid ""
+".filesize:\n"
+"\t; truncate file to new size\n"
+"\tpush\tdword 0\t\t; high dword\n"
+"\tpush\tedx\t\t; low dword\n"
+"\tpush\teax\t\t; pad\n"
+"\tpush\tebp\n"
+"\tsys.ftruncate\n"
+msgstr ""
+".filesize:\n"
+"\t; truncate file to new size\n"
+"\tpush\tdword 0\t\t; high dword\n"
+"\tpush\tedx\t\t; low dword\n"
+"\tpush\teax\t\t; pad\n"
+"\tpush\tebp\n"
+"\tsys.ftruncate\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2145
+#, no-wrap
+msgid ""
+"\t; close it (ebp still pushed)\n"
+"\tsys.close\n"
+msgstr ""
+"\t; close it (ebp still pushed)\n"
+"\tsys.close\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2148
+#, no-wrap
+msgid ""
+"\tadd\tesp, byte 16\n"
+"\tsys.munmap\n"
+msgstr ""
+"\tadd\tesp, byte 16\n"
+"\tsys.munmap\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2152
+#, no-wrap
+msgid ""
+".quit:\n"
+"\tpush\tdword 0\n"
+"\tsys.exit\n"
+msgstr ""
+".quit:\n"
+"\tpush\tdword 0\n"
+"\tsys.exit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2157
+#, no-wrap
+msgid ""
+"align 4\n"
+"ordinary:\n"
+"\tcmp\tal, 0Dh\n"
+"\tje\t.cr\n"
+msgstr ""
+"align 4\n"
+"ordinary:\n"
+"\tcmp\tal, 0Dh\n"
+"\tje\t.cr\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2160
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2182
+#, no-wrap
+msgid ""
+"\tcmp\tal, ah\n"
+"\tje\t.lf\n"
+msgstr ""
+"\tcmp\tal, ah\n"
+"\tje\t.lf\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2164
+#, no-wrap
+msgid ""
+"\tstosb\n"
+"\tinc\tedx\n"
+"\tret\n"
+msgstr ""
+"\tstosb\n"
+"\tinc\tedx\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2169
+#, no-wrap
+msgid ""
+"align 4\n"
+".cr:\n"
+"\tmov\tebx, cr\n"
+"\tret\n"
+msgstr ""
+"align 4\n"
+".cr:\n"
+"\tmov\tebx, cr\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2174
+#, no-wrap
+msgid ""
+"align 4\n"
+".lf:\n"
+"\tmov\tebx, lf\n"
+"\tret\n"
+msgstr ""
+"align 4\n"
+".lf:\n"
+"\tmov\tebx, lf\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2179
+#, no-wrap
+msgid ""
+"align 4\n"
+"cr:\n"
+"\tcmp\tal, 0Dh\n"
+"\tje\t.cr\n"
+msgstr ""
+"align 4\n"
+"cr:\n"
+"\tcmp\tal, 0Dh\n"
+"\tje\t.cr\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2186
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2214
+#, no-wrap
+msgid ""
+"\txchg\tal, ah\n"
+"\tstosb\n"
+"\tinc\tedx\n"
+msgstr ""
+"\txchg\tal, ah\n"
+"\tstosb\n"
+"\tinc\tedx\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2189
+#, no-wrap
+msgid ""
+"\txchg\tal, ah\n"
+"\t; fall through\n"
+msgstr ""
+"\txchg\tal, ah\n"
+"\t; fall through\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2195
+#, no-wrap
+msgid ""
+".lf:\n"
+"\tstosb\n"
+"\tinc\tedx\n"
+"\tmov\tebx, ordinary\n"
+"\tret\n"
+msgstr ""
+".lf:\n"
+"\tstosb\n"
+"\tinc\tedx\n"
+"\tmov\tebx, ordinary\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2202
+#, no-wrap
+msgid ""
+"align 4\n"
+".cr:\n"
+"\tmov\tal, ah\n"
+"\tstosb\n"
+"\tinc\tedx\n"
+"\tret\n"
+msgstr ""
+"align 4\n"
+".cr:\n"
+"\tmov\tal, ah\n"
+"\tstosb\n"
+"\tinc\tedx\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2207
+#, no-wrap
+msgid ""
+"align 4\n"
+"lf:\n"
+"\tcmp\tal, ah\n"
+"\tje\t.lf\n"
+msgstr ""
+"align 4\n"
+"lf:\n"
+"\tcmp\tal, ah\n"
+"\tje\t.lf\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2210
+#, no-wrap
+msgid ""
+"\tcmp\tal, 0Dh\n"
+"\tje\t.cr\n"
+msgstr ""
+"\tcmp\tal, 0Dh\n"
+"\tje\t.cr\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2220
+#, no-wrap
+msgid ""
+"\txchg\tal, ah\n"
+"\tstosb\n"
+"\tinc\tedx\n"
+"\tmov\tebx, ordinary\n"
+"\tret\n"
+msgstr ""
+"\txchg\tal, ah\n"
+"\tstosb\n"
+"\tinc\tedx\n"
+"\tmov\tebx, ordinary\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2226
+#, no-wrap
+msgid ""
+"align 4\n"
+".cr:\n"
+"\tmov\tebx, ordinary\n"
+"\tmov\tal, ah\n"
+"\t; fall through\n"
+msgstr ""
+"align 4\n"
+".cr:\n"
+"\tmov\tebx, ordinary\n"
+"\tmov\tal, ah\n"
+"\t; fall through\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2231
+#, no-wrap
+msgid ""
+".lf:\n"
+"\tstosb\n"
+"\tinc\tedx\n"
+"\tret\n"
+msgstr ""
+".lf:\n"
+"\tstosb\n"
+"\tinc\tedx\n"
+"\tret\n"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2238
+msgid ""
+"Do not use this program on files stored on a disk formatted by MS-DOS(R) or "
+"Windows(R). There seems to be a subtle bug in the FreeBSD code when using "
+"`mmap` on these drives mounted under FreeBSD: If the file is over a certain "
+"size, `mmap` will just fill the memory with zeros, and then copy them to the "
+"file overwriting its contents."
+msgstr ""
+"Не используйте эту программу для файлов, хранящихся на диске, "
+"отформатированном в MS-DOS(R) или Windows(R). В коде FreeBSD присутствует "
+"неочевидная ошибка при использовании `mmap` на таких дисках, смонтированных "
+"в FreeBSD: если размер файла превышает определённое значение, `mmap` "
+"заполнит память нулями, а затем запишет их в файл, перезаписав его "
+"содержимое."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2241
+#, no-wrap
+msgid "One-Pointed Mind"
+msgstr "Спокойствие ума"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2244
+msgid ""
+"As a student of Zen, I like the idea of a one-pointed mind: Do one thing at "
+"a time, and do it well."
+msgstr ""
+"Как ученик дзэн, мне нравится идея спокойствия ума (экаггата): делай одно "
+"дело за раз и делай его хорошо."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2247
+msgid ""
+"This, indeed, is very much how UNIX(R) works as well. While a typical "
+"Windows(R) application is attempting to do everything imaginable (and is, "
+"therefore, riddled with bugs), a typical UNIX(R) program does only one "
+"thing, and it does it well."
+msgstr ""
+"Вот именно так, в большинстве случаев, работает и UNIX(R). В то время как "
+"типичное приложение Windows(R) пытается сделать всё, что только можно (и "
+"поэтому кишит ошибками), типичная программа UNIX(R) делает только одну вещь, "
+"но делает её хорошо."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2249
+msgid ""
+"The typical UNIX(R) user then essentially assembles his own applications by "
+"writing a shell script which combines the various existing programs by "
+"piping the output of one program to the input of another."
+msgstr ""
+"Типичный пользователь UNIX(R) по сути собирает свои собственные приложения, "
+"написав shell-скрипт, который объединяет различные существующие программы, "
+"передавая вывод одной программы на вход другой."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2251
+msgid ""
+"When writing your own UNIX(R) software, it is generally a good idea to see "
+"what parts of the problem you need to solve can be handled by existing "
+"programs, and only write your own programs for that part of the problem that "
+"you do not have an existing solution for."
+msgstr ""
+"При написании собственного программного обеспечения для UNIX(R) обычно "
+"рекомендуется определить, какие части решаемой задачи могут быть обработаны "
+"существующими программами, и создавать собственные программы только для той "
+"части задачи, для которой нет готового решения."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2253
+#, no-wrap
+msgid "CSV"
+msgstr "CSV"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2256
+msgid ""
+"I will illustrate this principle with a specific real-life example I was "
+"faced with recently:"
+msgstr ""
+"Я проиллюстрирую этот принцип конкретным примером из реальной жизни, с "
+"которым недавно столкнулся:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2260
+msgid ""
+"I needed to extract the 11th field of each record from a database I "
+"downloaded from a web site. The database was a CSV file, i.e., a list of "
+"_comma-separated values_. That is quite a standard format for sharing data "
+"among people who may be using different database software."
+msgstr ""
+"Мне нужно было извлечь 11-е поле каждой записи из базы данных, которую я "
+"загрузил с веб-сайта. База данных представляла собой CSV-файл, то есть "
+"список _значений, разделённых запятыми_. Это довольно стандартный формат для "
+"обмена данными между людьми, которые могут использовать разное программное "
+"обеспечение для работы с базами данных."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2263
+msgid ""
+"The first line of the file contains the list of various fields separated by "
+"commas. The rest of the file contains the data listed line by line, with "
+"values separated by commas."
+msgstr ""
+"Первая строка файла содержит список различных полей, разделенных запятыми. "
+"Остальная часть файла содержит данные, перечисленные построчно, со "
+"значениями, разделенными запятыми."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2265
+msgid ""
+"I tried awk, using the comma as a separator. But because several lines "
+"contained a quoted comma, awk was extracting the wrong field from those "
+"lines."
+msgstr ""
+"Я попробовал awk, используя запятую в качестве разделителя. Но поскольку "
+"несколько строк содержали запятую в кавычках, awk извлекал неправильное поле "
+"из этих строк."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2268
+msgid ""
+"Therefore, I needed to write my own software to extract the 11th field from "
+"the CSV file. However, going with the UNIX(R) spirit, I only needed to "
+"write a simple filter that would do the following:"
+msgstr ""
+"Следовательно, мне нужно было написать собственное программное обеспечение "
+"для извлечения 11-го поля из CSV-файла. Однако, следуя духу UNIX(R), мне "
+"нужно было лишь создать простой фильтр, выполняющий следующие действия:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2270
+msgid "Remove the first line from the file;"
+msgstr "Удалить первую строку из файла;"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2271
+msgid "Change all unquoted commas to a different character;"
+msgstr "Заменить все не заключённые в кавычки запятые на другой символ;"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2272
+msgid "Remove all quotation marks."
+msgstr "Удалить все кавычки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2274
+msgid ""
+"Strictly speaking, I could use sed to remove the first line from the file, "
+"but doing so in my own program was very easy, so I decided to do it and "
+"reduce the size of the pipeline."
+msgstr ""
+"Строго говоря, я мог бы использовать sed для удаления первой строки из "
+"файла, но сделать это в моей собственной программе было очень просто, "
+"поэтому я решил так поступить и уменьшить размер конвейера."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2278
+msgid ""
+"At any rate, writing a program like this took me about 20 minutes. Writing "
+"a program that extracts the 11th field from the CSV file would take a lot "
+"longer, and I could not reuse it to extract some other field from some other "
+"database."
+msgstr ""
+"В любом случае, написание подобной программы заняло у меня около 20 минут. "
+"Написание программы, которая извлекает 11-е поле из CSV-файла, заняло бы "
+"гораздо больше времени, и я не смог бы повторно использовать её для "
+"извлечения другого поля из другой базы данных."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2280
+msgid ""
+"This time I decided to let it do a little more work than a typical tutorial "
+"program would:"
+msgstr ""
+"На этот раз я решил позволить ей выполнить немного больше работы, чем "
+"обычная учебная программа:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2282
+msgid "It parses its command line for options;"
+msgstr "Она анализирует свою командную строку на наличие опций;"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2283
+msgid "It displays proper usage if it finds wrong arguments;"
+msgstr "Она отображает подсказку, если обнаруживает неверные аргументы;"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2284
+msgid "It produces meaningful error messages."
+msgstr "Она выдает понятные сообщения об ошибках."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2286
+msgid "Here is its usage message:"
+msgstr "Вот какое сообщение она выводит о том, как ее использовать:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2290
+#, no-wrap
+msgid "Usage: csv [-t<delim>] [-c<comma>] [-p] [-o <outfile>] [-i <infile>]\n"
+msgstr "Usage: csv [-t<delim>] [-c<comma>] [-p] [-o <outfile>] [-i <infile>]\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2293
+msgid "All parameters are optional, and can appear in any order."
+msgstr "Все параметры необязательны и могут располагаться в любом порядке."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2297
+msgid ""
+"The `-t` parameter declares what to replace the commas with. The `tab` is "
+"the default here. For example, `-t;` will replace all unquoted commas with "
+"semicolons."
+msgstr ""
+"Параметр `-t` указывает, на что заменить запятые. По умолчанию используется "
+"`tab`. Например, `-t;` заменит все незакавыченные запятые на точку с запятой."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2301
+msgid ""
+"I did not need the `-c` option, but it may come in handy in the future. It "
+"lets me declare that I want a character other than a comma replaced with "
+"something else. For example, `-c@` will replace all at signs (useful if you "
+"want to split a list of email addresses to their user names and domains)."
+msgstr ""
+"Мне не понадобилась опция `-c`, но в будущем она может пригодиться. Она "
+"позволяет указать, что я хочу заменить символ, отличный от запятой, на что-"
+"то другое. Например, `-c@` заменит все знаки @ (полезно, если нужно "
+"разделить список email-адресов на имена пользователей и домены)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2304
+msgid ""
+"The `-p` option preserves the first line, i.e., it does not delete it. By "
+"default, we delete the first line because in a CSV file it contains the "
+"field names rather than data."
+msgstr ""
+"Опция `-p` сохраняет первую строку, т.е. не удаляет её. По умолчанию мы "
+"удаляем первую строку, потому что в CSV-файле она содержит названия полей, а "
+"не данные."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2307
+msgid ""
+"The `-i` and `-o` options let me specify the input and the output files. "
+"Defaults are [.filename]#stdin# and [.filename]#stdout#, so this is a "
+"regular UNIX(R) filter."
+msgstr ""
+"Опции `-i` и `-o` позволяют указать входной и выходной файлы. По умолчанию "
+"используются [.filename]#stdin# и [.filename]#stdout#, как обычно работает "
+"стандартный фильтр UNIX(R)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2310
+msgid ""
+"I made sure that both `-i filename` and `-ifilename` are accepted. I also "
+"made sure that only one input and one output files may be specified."
+msgstr ""
+"Я убедился, что принимаются как `-i filename`, так и `-ifilename`. Также я "
+"убедился, что может быть указан только один входной и один выходной файл."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2312
+msgid "To get the 11th field of each record, I can now do:"
+msgstr "Чтобы получить 11-е поле каждой записи, теперь я могу сделать:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2316
+#, no-wrap
+msgid "% csv '-t;' data.csv | awk '-F;' '{print $11}'\n"
+msgstr "% csv '-t;' data.csv | awk '-F;' '{print $11}'\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2319
+msgid ""
+"The code stores the options (except for the file descriptors) in `EDX`: The "
+"comma in `DH`, the new separator in `DL`, and the flag for the `-p` option "
+"in the highest bit of `EDX`, so a check for its sign will give us a quick "
+"decision what to do."
+msgstr ""
+"Код сохраняет параметры (за исключением файловых дескрипторов) в `EDX`: "
+"запятая в `DH`, новый разделитель в `DL`, а флаг параметра `-p` в старшем "
+"бите `EDX`, поэтому проверка его знака даст нам быстрое решение о дальнейших "
+"действиях."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2321
+msgid "Here is the code:"
+msgstr "Вот код:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2335
+#, no-wrap
+msgid ""
+";;;;;;; csv.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+";\n"
+"; Convert a comma-separated file to a something-else separated file.\n"
+";\n"
+"; Started:\t31-May-2001\n"
+"; Updated:\t 1-Jun-2001\n"
+";\n"
+"; Copyright (c) 2001 G. Adam Stanislav\n"
+"; All rights reserved.\n"
+";\n"
+";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+msgstr ""
+";;;;;;; csv.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+";\n"
+"; Convert a comma-separated file to a something-else separated file.\n"
+";\n"
+"; Started:\t31-May-2001\n"
+"; Updated:\t 1-Jun-2001\n"
+";\n"
+"; Copyright (c) 2001 G. Adam Stanislav\n"
+"; All rights reserved.\n"
+";\n"
+";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2349
+#, no-wrap
+msgid ""
+"section\t.data\n"
+"fd.in\tdd\tstdin\n"
+"fd.out\tdd\tstdout\n"
+"usg\tdb\t'Usage: csv [-t<delim>] [-c<comma>] [-p] [-o <outfile>] [-i <infile>]', 0Ah\n"
+"usglen\tequ\t$-usg\n"
+"iemsg\tdb\t\"csv: Can't open input file\", 0Ah\n"
+"iemlen\tequ\t$-iemsg\n"
+"oemsg\tdb\t\"csv: Can't create output file\", 0Ah\n"
+"oemlen\tequ\t$-oemsg\n"
+msgstr ""
+"section\t.data\n"
+"fd.in\tdd\tstdin\n"
+"fd.out\tdd\tstdout\n"
+"usg\tdb\t'Usage: csv [-t<delim>] [-c<comma>] [-p] [-o <outfile>] [-i <infile>]', 0Ah\n"
+"usglen\tequ\t$-usg\n"
+"iemsg\tdb\t\"csv: Can't open input file\", 0Ah\n"
+"iemlen\tequ\t$-iemsg\n"
+"oemsg\tdb\t\"csv: Can't create output file\", 0Ah\n"
+"oemlen\tequ\t$-oemsg\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2363
+#, no-wrap
+msgid ""
+"section\t.text\n"
+"align 4\n"
+"ierr:\n"
+"\tpush\tdword iemlen\n"
+"\tpush\tdword iemsg\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+"\tpush\tdword 1\t\t; return failure\n"
+"\tsys.exit\n"
+msgstr ""
+"section\t.text\n"
+"align 4\n"
+"ierr:\n"
+"\tpush\tdword iemlen\n"
+"\tpush\tdword iemsg\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+"\tpush\tdword 1\t\t; return failure\n"
+"\tsys.exit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2372
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3421
+#, no-wrap
+msgid ""
+"align 4\n"
+"oerr:\n"
+"\tpush\tdword oemlen\n"
+"\tpush\tdword oemsg\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+"\tpush\tdword 2\n"
+"\tsys.exit\n"
+msgstr ""
+"align 4\n"
+"oerr:\n"
+"\tpush\tdword oemlen\n"
+"\tpush\tdword oemsg\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+"\tpush\tdword 2\n"
+"\tsys.exit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2381
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3430
+#, no-wrap
+msgid ""
+"align 4\n"
+"usage:\n"
+"\tpush\tdword usglen\n"
+"\tpush\tdword usg\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+"\tpush\tdword 3\n"
+"\tsys.exit\n"
+msgstr ""
+"align 4\n"
+"usage:\n"
+"\tpush\tdword usglen\n"
+"\tpush\tdword usg\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+"\tpush\tdword 3\n"
+"\tsys.exit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2387
+#, no-wrap
+msgid ""
+"align 4\n"
+"global\t_start\n"
+"_start:\n"
+"\tadd\tesp, byte 8\t; discard argc and argv[0]\n"
+"\tmov\tedx, (',' << 8) | 9\n"
+msgstr ""
+"align 4\n"
+"global\t_start\n"
+"_start:\n"
+"\tadd\tesp, byte 8\t; discard argc and argv[0]\n"
+"\tmov\tedx, (',' << 8) | 9\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2392
+#, no-wrap
+msgid ""
+".arg:\n"
+"\tpop\tecx\n"
+"\tor\tecx, ecx\n"
+"\tje\tnear .init\t\t; no more arguments\n"
+msgstr ""
+".arg:\n"
+"\tpop\tecx\n"
+"\tor\tecx, ecx\n"
+"\tje\tnear .init\t\t; no more arguments\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2396
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3445
+#, no-wrap
+msgid ""
+"\t; ECX contains the pointer to an argument\n"
+"\tcmp\tbyte [ecx], '-'\n"
+"\tjne\tusage\n"
+msgstr ""
+"\t; ECX contains the pointer to an argument\n"
+"\tcmp\tbyte [ecx], '-'\n"
+"\tjne\tusage\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2399
+#, no-wrap
+msgid ""
+"\tinc\tecx\n"
+"\tmov\tax, [ecx]\n"
+msgstr ""
+"\tinc\tecx\n"
+"\tmov\tax, [ecx]\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2403
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3453
+#, no-wrap
+msgid ""
+".o:\n"
+"\tcmp\tal, 'o'\n"
+"\tjne\t.i\n"
+msgstr ""
+".o:\n"
+"\tcmp\tal, 'o'\n"
+"\tjne\t.i\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2407
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3457
+#, no-wrap
+msgid ""
+"\t; Make sure we are not asked for the output file twice\n"
+"\tcmp\tdword [fd.out], stdout\n"
+"\tjne\tusage\n"
+msgstr ""
+"\t; Make sure we are not asked for the output file twice\n"
+"\tcmp\tdword [fd.out], stdout\n"
+"\tjne\tusage\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2412
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3462
+#, no-wrap
+msgid ""
+"\t; Find the path to output file - it is either at [ECX+1],\n"
+"\t; i.e., -ofile --\n"
+"\t; or in the next argument,\n"
+"\t; i.e., -o file\n"
+msgstr ""
+"\t; Find the path to output file - it is either at [ECX+1],\n"
+"\t; i.e., -ofile --\n"
+"\t; or in the next argument,\n"
+"\t; i.e., -o file\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2418
+#, no-wrap
+msgid ""
+"\tinc\tecx\n"
+"\tor\tah, ah\n"
+"\tjne\t.openoutput\n"
+"\tpop\tecx\n"
+"\tjecxz\tusage\n"
+msgstr ""
+"\tinc\tecx\n"
+"\tor\tah, ah\n"
+"\tjne\t.openoutput\n"
+"\tpop\tecx\n"
+"\tjecxz\tusage\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2426
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3475
+#, no-wrap
+msgid ""
+".openoutput:\n"
+"\tpush\tdword 420\t; file mode (644 octal)\n"
+"\tpush\tdword 0200h | 0400h | 01h\n"
+"\t; O_CREAT | O_TRUNC | O_WRONLY\n"
+"\tpush\tecx\n"
+"\tsys.open\n"
+"\tjc\tnear oerr\n"
+msgstr ""
+".openoutput:\n"
+"\tpush\tdword 420\t; file mode (644 octal)\n"
+"\tpush\tdword 0200h | 0400h | 01h\n"
+"\t; O_CREAT | O_TRUNC | O_WRONLY\n"
+"\tpush\tecx\n"
+"\tsys.open\n"
+"\tjc\tnear oerr\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2430
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3479
+#, no-wrap
+msgid ""
+"\tadd\tesp, byte 12\n"
+"\tmov\t[fd.out], eax\n"
+"\tjmp\tshort .arg\n"
+msgstr ""
+"\tadd\tesp, byte 12\n"
+"\tmov\t[fd.out], eax\n"
+"\tjmp\tshort .arg\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2434
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3483
+#, no-wrap
+msgid ""
+".i:\n"
+"\tcmp\tal, 'i'\n"
+"\tjne\t.p\n"
+msgstr ""
+".i:\n"
+"\tcmp\tal, 'i'\n"
+"\tjne\t.p\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2438
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3487
+#, no-wrap
+msgid ""
+"\t; Make sure we are not asked twice\n"
+"\tcmp\tdword [fd.in], stdin\n"
+"\tjne\tnear usage\n"
+msgstr ""
+"\t; Make sure we are not asked twice\n"
+"\tcmp\tdword [fd.in], stdin\n"
+"\tjne\tnear usage\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2446
+#, no-wrap
+msgid ""
+"\t; Find the path to the input file\n"
+"\tinc\tecx\n"
+"\tor\tah, ah\n"
+"\tjne\t.openinput\n"
+"\tpop\tecx\n"
+"\tor\tecx, ecx\n"
+"\tje near usage\n"
+msgstr ""
+"\t; Find the path to the input file\n"
+"\tinc\tecx\n"
+"\tor\tah, ah\n"
+"\tjne\t.openinput\n"
+"\tpop\tecx\n"
+"\tor\tecx, ecx\n"
+"\tje near usage\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2452
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3500
+#, no-wrap
+msgid ""
+".openinput:\n"
+"\tpush\tdword 0\t\t; O_RDONLY\n"
+"\tpush\tecx\n"
+"\tsys.open\n"
+"\tjc\tnear ierr\t\t; open failed\n"
+msgstr ""
+".openinput:\n"
+"\tpush\tdword 0\t\t; O_RDONLY\n"
+"\tpush\tecx\n"
+"\tsys.open\n"
+"\tjc\tnear ierr\t\t; open failed\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2456
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3504
+#, no-wrap
+msgid ""
+"\tadd\tesp, byte 8\n"
+"\tmov\t[fd.in], eax\n"
+"\tjmp\t.arg\n"
+msgstr ""
+"\tadd\tesp, byte 8\n"
+"\tmov\t[fd.in], eax\n"
+"\tjmp\t.arg\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2464
+#, no-wrap
+msgid ""
+".p:\n"
+"\tcmp\tal, 'p'\n"
+"\tjne\t.t\n"
+"\tor\tah, ah\n"
+"\tjne\tnear usage\n"
+"\tor\tedx, 1 << 31\n"
+"\tjmp\t.arg\n"
+msgstr ""
+".p:\n"
+"\tcmp\tal, 'p'\n"
+"\tjne\t.t\n"
+"\tor\tah, ah\n"
+"\tjne\tnear usage\n"
+"\tor\tedx, 1 << 31\n"
+"\tjmp\t.arg\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2472
+#, no-wrap
+msgid ""
+".t:\n"
+"\tcmp\tal, 't'\t\t; redefine output delimiter\n"
+"\tjne\t.c\n"
+"\tor\tah, ah\n"
+"\tje\tnear usage\n"
+"\tmov\tdl, ah\n"
+"\tjmp\t.arg\n"
+msgstr ""
+".t:\n"
+"\tcmp\tal, 't'\t\t; redefine output delimiter\n"
+"\tjne\t.c\n"
+"\tor\tah, ah\n"
+"\tje\tnear usage\n"
+"\tmov\tdl, ah\n"
+"\tjmp\t.arg\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2480
+#, no-wrap
+msgid ""
+".c:\n"
+"\tcmp\tal, 'c'\n"
+"\tjne\tnear usage\n"
+"\tor\tah, ah\n"
+"\tje\tnear usage\n"
+"\tmov\tdh, ah\n"
+"\tjmp\t.arg\n"
+msgstr ""
+".c:\n"
+"\tcmp\tal, 'c'\n"
+"\tjne\tnear usage\n"
+"\tor\tah, ah\n"
+"\tje\tnear usage\n"
+"\tmov\tdh, ah\n"
+"\tjmp\t.arg\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2487
+#, no-wrap
+msgid ""
+"align 4\n"
+".init:\n"
+"\tsub\teax, eax\n"
+"\tsub\tebx, ebx\n"
+"\tsub\tecx, ecx\n"
+"\tmov\tedi, obuffer\n"
+msgstr ""
+"align 4\n"
+".init:\n"
+"\tsub\teax, eax\n"
+"\tsub\tebx, ebx\n"
+"\tsub\tecx, ecx\n"
+"\tmov\tedi, obuffer\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2491
+#, no-wrap
+msgid ""
+"\t; See if we are to preserve the first line\n"
+"\tor\tedx, edx\n"
+"\tjs\t.loop\n"
+msgstr ""
+"\t; See if we are to preserve the first line\n"
+"\tor\tedx, edx\n"
+"\tjs\t.loop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2497
+#, no-wrap
+msgid ""
+".firstline:\n"
+"\t; get rid of the first line\n"
+"\tcall\tgetchar\n"
+"\tcmp\tal, 0Ah\n"
+"\tjne\t.firstline\n"
+msgstr ""
+".firstline:\n"
+"\t; get rid of the first line\n"
+"\tcall\tgetchar\n"
+"\tcmp\tal, 0Ah\n"
+"\tjne\t.firstline\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2505
+#, no-wrap
+msgid ""
+"\t; is it a comma (or whatever the user asked for)?\n"
+"\tcmp\tal, dh\n"
+"\tjne\t.quote\n"
+msgstr ""
+"\t; is it a comma (or whatever the user asked for)?\n"
+"\tcmp\tal, dh\n"
+"\tjne\t.quote\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2508
+#, no-wrap
+msgid ""
+"\t; Replace the comma with a tab (or whatever the user wants)\n"
+"\tmov\tal, dl\n"
+msgstr ""
+"\t; Replace the comma with a tab (or whatever the user wants)\n"
+"\tmov\tal, dl\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2516
+#, no-wrap
+msgid ""
+".quote:\n"
+"\tcmp\tal, '\"'\n"
+"\tjne\t.put\n"
+msgstr ""
+".quote:\n"
+"\tcmp\tal, '\"'\n"
+"\tjne\t.put\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2523
+#, no-wrap
+msgid ""
+"\t; Print everything until you get another quote or EOL. If it\n"
+"\t; is a quote, skip it. If it is EOL, print it.\n"
+".qloop:\n"
+"\tcall\tgetchar\n"
+"\tcmp\tal, '\"'\n"
+"\tje\t.loop\n"
+msgstr ""
+"\t; Print everything until you get another quote or EOL. If it\n"
+"\t; is a quote, skip it. If it is EOL, print it.\n"
+".qloop:\n"
+"\tcall\tgetchar\n"
+"\tcmp\tal, '\"'\n"
+"\tje\t.loop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2526
+#, no-wrap
+msgid ""
+"\tcmp\tal, 0Ah\n"
+"\tje\t.put\n"
+msgstr ""
+"\tcmp\tal, 0Ah\n"
+"\tje\t.put\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2529
+#, no-wrap
+msgid ""
+"\tcall\tputchar\n"
+"\tjmp\tshort .qloop\n"
+msgstr ""
+"\tcall\tputchar\n"
+"\tjmp\tshort .qloop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2545
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3860
+#, no-wrap
+msgid ""
+"read:\n"
+"\tjecxz\t.read\n"
+"\tcall\twrite\n"
+msgstr ""
+"read:\n"
+"\tjecxz\t.read\n"
+"\tcall\twrite\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2558
+#, no-wrap
+msgid ""
+".read:\n"
+"\tpush\tdword BUFSIZE\n"
+"\tmov\tesi, ibuffer\n"
+"\tpush\tesi\n"
+"\tpush\tdword [fd.in]\n"
+"\tsys.read\n"
+"\tadd\tesp, byte 12\n"
+"\tmov\tebx, eax\n"
+"\tor\teax, eax\n"
+"\tje\t.done\n"
+"\tsub\teax, eax\n"
+"\tret\n"
+msgstr ""
+".read:\n"
+"\tpush\tdword BUFSIZE\n"
+"\tmov\tesi, ibuffer\n"
+"\tpush\tesi\n"
+"\tpush\tdword [fd.in]\n"
+"\tsys.read\n"
+"\tadd\tesp, byte 12\n"
+"\tmov\tebx, eax\n"
+"\tor\teax, eax\n"
+"\tje\t.done\n"
+"\tsub\teax, eax\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2595
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3901
+#, no-wrap
+msgid ""
+"align 4\n"
+"write:\n"
+"\tjecxz\t.ret\t; nothing to write\n"
+"\tsub\tedi, ecx\t; start of buffer\n"
+"\tpush\tecx\n"
+"\tpush\tedi\n"
+"\tpush\tdword [fd.out]\n"
+"\tsys.write\n"
+"\tadd\tesp, byte 12\n"
+"\tsub\teax, eax\n"
+"\tsub\tecx, ecx\t; buffer is empty now\n"
+".ret:\n"
+"\tret\n"
+msgstr ""
+"align 4\n"
+"write:\n"
+"\tjecxz\t.ret\t; nothing to write\n"
+"\tsub\tedi, ecx\t; start of buffer\n"
+"\tpush\tecx\n"
+"\tpush\tedi\n"
+"\tpush\tdword [fd.out]\n"
+"\tsys.write\n"
+"\tadd\tesp, byte 12\n"
+"\tsub\teax, eax\n"
+"\tsub\tecx, ecx\t; buffer is empty now\n"
+".ret:\n"
+"\tret\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2600
+msgid ""
+"Much of it is taken from [.filename]#hex.asm# above. But there is one "
+"important difference: I no longer call `write` whenever I am outputting a "
+"line feed. Yet, the code can be used interactively."
+msgstr ""
+"Большая часть взята из [.filename]#hex.asm# выше. Однако есть одно важное "
+"отличие: я больше не вызываю `write` каждый раз при выводе перевода строки. "
+"Тем не менее, код можно использовать интерактивно."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2604
+msgid ""
+"I have found a better solution for the interactive problem since I first "
+"started writing this chapter. I wanted to make sure each line is printed "
+"out separately only when needed. After all, there is no need to flush out "
+"every line when used non-interactively."
+msgstr ""
+"Я нашел лучшее решение для интерактивной проблемы с тех пор, как начал "
+"писать эту главу. Я хотел убедиться, что каждая строка выводится отдельно "
+"только при необходимости. В конце концов, нет необходимости выводить каждую "
+"строку при неинтерактивном использовании."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2608
+msgid ""
+"The new solution I use now is to call `write` every time I find the input "
+"buffer empty. That way, when running in the interactive mode, the program "
+"reads one line from the user's keyboard, processes it, and sees its input "
+"buffer is empty. It flushes its output and reads the next line."
+msgstr ""
+"Новое решение, которое я использую сейчас, заключается в вызове `write` "
+"каждый раз, когда обнаруживаю, что входной буфер пуст. Таким образом, при "
+"работе в интерактивном режиме программа считывает одну строку с клавиатуры "
+"пользователя, обрабатывает её и видит, что входной буфер пуст. Она "
+"сбрасывает свой вывод и читает следующую строку."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2610
+#, no-wrap
+msgid "The Dark Side of Buffering"
+msgstr "Темная сторона буферизации"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2614
+msgid ""
+"This change prevents a mysterious lockup in a very specific case. I refer "
+"to it as the _dark side of buffering_, mostly because it presents a danger "
+"that is not quite obvious."
+msgstr ""
+"Это изменение предотвращает загадочную блокировку в очень специфическом "
+"случае. Я называю это _тёмной стороной буферизации_, в основном потому, что "
+"это представляет опасность, которая не совсем очевидна."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2618
+msgid ""
+"It is unlikely to happen with a program like the csv above, so let us "
+"consider yet another filter: In this case we expect our input to be raw data "
+"representing color values, such as the _red_, _green_, and _blue_ "
+"intensities of a pixel. Our output will be the negative of our input."
+msgstr ""
+"Маловероятно, что это произойдет с такой программой, как csv выше, поэтому "
+"рассмотрим еще один фильтр: в этом случае мы ожидаем, что наши входные "
+"данные будут представлять собой необработанные данные, описывающие значения "
+"цветов, такие как интенсивности _красного_, _зеленого_ и _синего_ для "
+"пикселя. На выходе мы получим негатив входных данных."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2621
+msgid ""
+"Such a filter would be very simple to write. Most of it would look just "
+"like all the other filters we have written so far, so I am only going to "
+"show you its inner loop:"
+msgstr ""
+"Такой фильтр было бы очень просто написать. Большая его часть выглядела бы "
+"так же, как и все другие фильтры, которые мы уже писали, поэтому я покажу "
+"только его внутренний цикл:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2629
+#, no-wrap
+msgid ""
+".loop:\n"
+"\tcall\tgetchar\n"
+"\tnot\tal\t\t; Create a negative\n"
+"\tcall\tputchar\n"
+"\tjmp\tshort .loop\n"
+msgstr ""
+".loop:\n"
+"\tcall\tgetchar\n"
+"\tnot\tal\t\t; Create a negative\n"
+"\tcall\tputchar\n"
+"\tjmp\tshort .loop\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2632
+msgid ""
+"Because this filter works with raw data, it is unlikely to be used "
+"interactively."
+msgstr ""
+"Поскольку этот фильтр работает с необработанными данными, он вряд ли будет "
+"использоваться интерактивно."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2635
+msgid ""
+"But it could be called by image manipulation software. And, unless it calls "
+"`write` before each call to `read`, chances are it will lock up."
+msgstr ""
+"Но он может вызываться программным обеспечением для обработки изображений. "
+"И, если он не вызывает `write` перед каждым вызовом `read`, высока "
+"вероятность, что он зависнет."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2637
+msgid "Here is what might happen:"
+msgstr "Вот что может произойти:"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2640
+msgid "The image editor will load our filter using the C function `popen()`."
+msgstr ""
+"Редактор изображений загрузит наш фильтр, используя функцию `popen()` на "
+"языке C."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2641
+msgid "It will read the first row of pixels from a bitmap or pixmap."
+msgstr ""
+"Он прочитает первый ряд пикселей из битовой карты или пиксельной карты."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2642
+msgid ""
+"It will write the first row of pixels to the _pipe_ leading to the `fd.in` "
+"of our filter."
+msgstr ""
+"Он запишет первую строку пикселей в _канал_, ведущий к `fd.in` нашего "
+"фильтра."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2643
+msgid ""
+"Our filter will read each pixel from its input, turn it to a negative, and "
+"write it to its output buffer."
+msgstr ""
+"Наш фильтр будет читать каждый пиксель из входных данных, преобразовывать "
+"его в негатив и записывать в выходной буфер."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2644
+msgid "Our filter will call `getchar` to fetch the next pixel."
+msgstr "Наш фильтр будет вызывать `getchar` для получения следующего пикселя."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2645
+msgid "`getchar` will find an empty input buffer, so it will call `read`."
+msgstr "`getchar` обнаружит пустой входной буфер, поэтому вызовет `read`."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2646
+msgid "`read` will call the `SYS_read` system call."
+msgstr "`read` вызовет системный вызов `SYS_read`."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2647
+msgid ""
+"The _kernel_ will suspend our filter until the image editor sends more data "
+"to the pipe."
+msgstr ""
+"_Ядро_ приостановит работу нашего фильтра до тех пор, пока редактор "
+"изображений не отправит больше данных в канал."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2648
+msgid ""
+"The image editor will read from the other pipe, connected to the `fd.out` of "
+"our filter so it can set the first row of the output image _before_ it sends "
+"us the second row of the input."
+msgstr ""
+"Редактор изображений будет читать из другого канала, подключенного к "
+"`fd.out` нашего фильтра, чтобы он мог установить первую строку выходного "
+"изображения _до_ того, как отправит нам вторую строку входного."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2649
+msgid ""
+"The _kernel_ suspends the image editor until it receives some output from "
+"our filter, so it can pass it on to the image editor."
+msgstr ""
+"_Ядро_ приостанавливает работу графического редактора до тех пор, пока не "
+"получит какие-либо данные от нашего фильтра, чтобы передать их редактору."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2653
+msgid ""
+"At this point our filter waits for the image editor to send it more data to "
+"process, while the image editor is waiting for our filter to send it the "
+"result of the processing of the first row. But the result sits in our "
+"output buffer."
+msgstr ""
+"На этом этапе наш фильтр ожидает, что редактор изображений отправит ему "
+"больше данных для обработки, в то время как редактор изображений ожидает, "
+"что наш фильтр отправит ему результат обработки первой строки. Однако "
+"результат находится в нашем выходном буфере."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2656
+msgid ""
+"The filter and the image editor will continue waiting for each other forever "
+"(or, at least, until they are killed). Our software has just entered a "
+"crossref:secure[secure-race-conditions,race condition]."
+msgstr ""
+"Фильтр и редактор изображений будут продолжать ждать друг друга вечно (или, "
+"по крайней мере, пока их не завершат командой kill). Наше программное "
+"обеспечение только что вошло в crossref:secure[secure-race-"
+"conditions,состояние гонки]."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2658
+msgid ""
+"This problem does not exist if our filter flushes its output buffer _before_ "
+"asking the _kernel_ for more input data."
+msgstr ""
+"Эта проблема не возникает, если наш фильтр очищает свой выходной буфер "
+"_перед_ запросом к _ядру_ для получения дополнительных входных данных."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2660
+#, no-wrap
+msgid "Using the FPU"
+msgstr "Использование FPU"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2663
+msgid ""
+"Strangely enough, most of assembly language literature does not even mention "
+"the existence of the FPU, or _floating point unit_, let alone discuss "
+"programming it."
+msgstr ""
+"Как ни странно, большая часть литературы по ассемблеру даже не упоминает о "
+"существовании FPU, или _блока обработки чисел с плавающей запятой_, не "
+"говоря уже о программировании для него."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2665
+msgid ""
+"Yet, never does assembly language shine more than when we create highly "
+"optimized FPU code by doing things that can be done _only_ in assembly "
+"language."
+msgstr ""
+"Тем не менее, язык ассемблера проявляет себя наилучшим образом, когда мы "
+"создаем высокооптимизированный код для FPU, выполняя вещи, которые можно "
+"сделать _только_ на языке ассемблера."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2667
+#, no-wrap
+msgid "Organization of the FPU"
+msgstr "Организация FPU"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2671
+msgid ""
+"The FPU consists of 8 80-bit floating-point registers. These are organized "
+"in a stack fashion-you can `push` a value on TOS (_top of stack_) and you "
+"can `pop` it."
+msgstr ""
+"FPU состоит из 8 80-битных регистров с плавающей запятой. Они организованы в "
+"виде стека — вы можете `push` (поместить) значение на TOS (_вершина стека_) "
+"и `pop` (извлечь) его."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2673
+msgid ""
+"That said, the assembly language op codes are not `push` and `pop` because "
+"those are already taken."
+msgstr ""
+"Как бы то ни было, мнемоники ассемблера — не `push` и `pop`, потому что они "
+"уже заняты."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2676
+msgid ""
+"You can `push` a value on TOS by using `fld`, `fild`, and `fbld`. Several "
+"other op codes let you `push` many common _constants_-such as _pi_-on the "
+"TOS."
+msgstr ""
+"Вы можете `push` (положить) значение на вершину стека (TOS), используя "
+"`fld`, `fild` и `fbld`. Несколько других кодов операций позволяют вам `push` "
+"(положить) многие распространённые _константы_ — например, _pi_ — на вершину "
+"стека (TOS)."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2679
+msgid ""
+"Similarly, you can `pop` a value by using `fst`, `fstp`, `fist`, `fistp`, "
+"and `fbstp`. Actually, only the op codes that end with a _p_ will literally "
+"`pop` the value, the rest will `store` it somewhere else without removing it "
+"from the TOS."
+msgstr ""
+"Аналогично, вы можете `извлечь` значение с помощью `fst`, `fstp`, `fist`, "
+"`fistp` и `fbstp`. На самом деле только коды операций, оканчивающиеся на "
+"_p_, буквально `извлекают` значение, остальные же `сохраняют` его в другом "
+"месте, не удаляя с вершины стека (TOS)."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2681
+msgid ""
+"We can transfer the data between the TOS and the computer memory either as a "
+"32-bit, 64-bit, or 80-bit _real_, a 16-bit, 32-bit, or 64-bit _integer_, or "
+"an 80-bit _packed decimal_."
+msgstr ""
+"Мы можем передавать данные между TOS и памятью компьютера либо как 32-"
+"битное, 64-битное или 80-битное _вещественное_ число, 16-битное, 32-битное "
+"или 64-битное _целое_ число, либо как 80-битное _упакованное десятичное_ "
+"число."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2684
+msgid ""
+"The 80-bit _packed decimal_ is a special case of _binary coded decimal_ "
+"which is very convenient when converting between the ASCII representation of "
+"data and the internal data of the FPU. It allows us to use 18 significant "
+"digits."
+msgstr ""
+"80-битный _упакованный десятичный_ формат является особым случаем _двоично-"
+"десятичного кодирования_, который очень удобен при преобразовании между "
+"ASCII-представлением данных и внутренними данными FPU. Он позволяет "
+"использовать до 18 значащих цифр."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2686
+msgid ""
+"No matter how we represent data in the memory, the FPU always stores it in "
+"the 80-bit _real_ format in its registers."
+msgstr ""
+"Независимо от того, как мы представляем данные в памяти, FPU всегда хранит "
+"их в 80-битном формате _real_ в своих регистрах."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2688
+msgid ""
+"Its internal precision is at least 19 decimal digits, so even if we choose "
+"to display results as ASCII in the full 18-digit precision, we are still "
+"showing correct results."
+msgstr ""
+"Его внутренняя точность составляет не менее 19 десятичных цифр, поэтому даже "
+"если мы решим отображать результаты в формате ASCII с полной 18-значной "
+"точностью, мы всё равно будем показывать корректные результаты."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2690
+msgid ""
+"We can perform mathematical operations on the TOS: We can calculate its "
+"_sine_, we can _scale_ it (i.e., we can multiply or divide it by a power of "
+"2), we can calculate its base-2 _logarithm_, and many other things."
+msgstr ""
+"Мы можем выполнять математические операции над TOS: вычислять его _синус_, "
+"_масштабировать_ (то есть умножать или делить на степень двойки), вычислять "
+"его _логарифм_ по основанию 2 и многое другое."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2692
+msgid ""
+"We can also _multiply_ or _divide_ it by, _add_ it to, or _subtract_ it "
+"from, any of the FPU registers (including itself)."
+msgstr ""
+"Мы также можем _умножить_ или _разделить_ его, _прибавить_ к нему или "
+"_вычесть_ его из любого из регистров FPU (включая его самого)."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2695
+msgid ""
+"The official Intel op code for the TOS is `st`, and for the _registers_ "
+"`st(0)`-`st(7)`. `st` and `st(0)`, then, refer to the same register."
+msgstr ""
+"Официальный код операции Intel для TOS — `st`, а для _регистров_ — `st(0)`-"
+"`st(7)`. Таким образом, `st` и `st(0)` ссылаются на один и тот же регистр."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2698
+msgid ""
+"For whatever reasons, the original author of nasm has decided to use "
+"different op codes, namely `st0`-`st7`. In other words, there are no "
+"parentheses, and the TOS is always `st0`, never just `st`."
+msgstr ""
+"По каким-то причинам оригинальный автор nasm решил использовать другие коды "
+"операций, а именно `st0`-`st7`. Другими словами, скобки отсутствуют, а "
+"вершина стека всегда `st0`, но никогда просто `st`."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2700
+#, no-wrap
+msgid "The Packed Decimal Format"
+msgstr "Формат упакованного десятичного числа"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2704
+msgid ""
+"The _packed decimal_ format uses 10 bytes (80 bits) of memory to represent "
+"18 digits. The number represented there is always an _integer_."
+msgstr ""
+"Формат _упакованного десятичного числа_ использует 10 байт (80 бит) памяти "
+"для представления 18 цифр. Представленное число всегда является _целым_."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2708
+msgid ""
+"You can use it to get decimal places by multiplying the TOS by a power of 10 "
+"first."
+msgstr ""
+"Вы можете использовать это для получения десятичных знаков, предварительно "
+"умножив TOS на степень 10."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2712
+msgid ""
+"The highest bit of the highest byte (byte 9) is the _sign bit_: If it is "
+"set, the number is _negative_, otherwise, it is _positive_. The rest of the "
+"bits of this byte are unused/ignored."
+msgstr ""
+"Старший бит старшего байта (байт 9) является _знаковым битом_: если он "
+"установлен, число _отрицательное_, в противном случае — _положительное_. "
+"Остальные биты этого байта не используются/игнорируются."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2714
+msgid ""
+"The remaining 9 bytes store the 18 digits of the number: 2 digits per byte."
+msgstr "Оставшиеся 9 байт хранят 18 цифр числа: 2 цифры на байт."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2716
+msgid ""
+"The _more significant digit_ is stored in the high _nibble_ (4 bits), the "
+"_less significant digit_ in the low _nibble_."
+msgstr ""
+"_Старший разряд_ хранится в старшем _полубайте_ (4 бита), _младший разряд_ — "
+"в младшем _полубайте_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2718
+msgid ""
+"That said, you might think that `-1234567` would be stored in the memory "
+"like this (using hexadecimal notation):"
+msgstr ""
+"Как бы то ни было, вы можете подумать, что `-1234567` будет храниться в "
+"памяти следующим образом (в шестнадцатеричной записи):"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2722
+#, no-wrap
+msgid "80 00 00 00 00 00 01 23 45 67\n"
+msgstr "80 00 00 00 00 00 01 23 45 67\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2725
+msgid ""
+"Alas it is not! As with everything else of Intel make, even the _packed "
+"decimal_ is _little-endian_."
+msgstr ""
+"Увы, это не так! Как и все остальное, созданное Intel, даже _упакованное "
+"десятичное число_ имеет порядок _от младшего к старшему_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2727
+msgid "That means our `-1234567` is stored like this:"
+msgstr "Это означает, что наш `-1234567` хранится следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2731
+#, no-wrap
+msgid "67 45 23 01 00 00 00 00 00 80\n"
+msgstr "67 45 23 01 00 00 00 00 00 80\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2734
+msgid "Remember that, or you will be pulling your hair out in desperation!"
+msgstr "Помните об этом, иначе вы будете рвать на себе волосы в отчаянии!"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2740
+msgid ""
+"The book to read-if you can find it-is Richard Startz' http://www.amazon.com/"
+"exec/obidos/ASIN/013246604X/whizkidtechnomag[8087/80287/80387 for the IBM PC "
+"& Compatibles]. Though it does seem to take the fact about the little-"
+"endian storage of the _packed decimal_ for granted. I kid you not about the "
+"desperation of trying to figure out what was wrong with the filter I show "
+"below _before_ it occurred to me I should try the little-endian order even "
+"for this type of data."
+msgstr ""
+"Книга, которую стоит прочитать — если сможете её найти — это книга Ричарда "
+"Старца http://www.amazon.com/exec/obidos/ASIN/013246604X/"
+"whizkidtechnomag[8087/80287/80387 для IBM PC и совместимых]. Хотя в ней, "
+"кажется, факт о little-endian хранении _упакованного десятичного числа_ "
+"принимается как данность. Я не шучу насчёт отчаяния, которое испытывал, "
+"пытаясь понять, что не так с фильтром, который я привожу ниже, _прежде_ чем "
+"мне пришло в голову попробовать little-endian порядок даже для этого типа "
+"данных."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2743
+#, no-wrap
+msgid "Excursion to Pinhole Photography"
+msgstr "Экскурсия в фотографию с помощью камеры-обскуры"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2746
+msgid ""
+"To write meaningful software, we must not only understand our programming "
+"tools, but also the field we are creating software for."
+msgstr ""
+"Чтобы создавать полезное программное обеспечение, мы должны понимать не "
+"только наши инструменты программирования, но и область, для которой "
+"разрабатываем ПО."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2748
+msgid ""
+"Our next filter will help us whenever we want to build a _pinhole camera_, "
+"so, we need some background in _pinhole photography_ before we can continue."
+msgstr ""
+"Наш следующий фильтр поможет нам, когда мы захотим создать _камеру-обскуру_, "
+"поэтому нам понадобятся некоторые знания о _фотографии с помощью обскуры_, "
+"прежде чем мы сможем продолжить."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2750
+#, no-wrap
+msgid "The Camera"
+msgstr "Камера"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2753
+msgid ""
+"The easiest way to describe any camera ever built is as some empty space "
+"enclosed in some lightproof material, with a small hole in the enclosure."
+msgstr ""
+"Самый простой способ описать любую когда-либо созданную камеру — это "
+"некоторое пустое пространство, заключённое в светонепроницаемый материал, с "
+"небольшим отверстием в корпусе."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2758
+msgid ""
+"The enclosure is usually sturdy (e.g., a box), though sometimes it is "
+"flexible (the bellows). It is quite dark inside the camera. However, the "
+"hole lets light rays in through a single point (though in some cases there "
+"may be several). These light rays form an image, a representation of "
+"whatever is outside the camera, in front of the hole."
+msgstr ""
+"Корпус обычно прочный (например, коробка), хотя иногда он гибкий "
+"(гофрированная часть). Внутри камеры довольно темно. Однако отверстие "
+"пропускает световые лучи через одну точку (хотя в некоторых случаях их может "
+"быть несколько). Эти световые лучи формируют изображение — представление "
+"того, что находится снаружи камеры, перед отверстием."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2760
+msgid ""
+"If some light sensitive material (such as film) is placed inside the camera, "
+"it can capture the image."
+msgstr ""
+"Если внутрь камеры поместить светочувствительный материал (например, "
+"плёнку), он может зафиксировать изображение."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2762
+msgid ""
+"The hole often contains a _lens_, or a lens assembly, often called the "
+"_objective_."
+msgstr ""
+"Отверстие часто содержит _линзу_ или сборку линз, которую часто называют "
+"_объективом_."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2764
+#, no-wrap
+msgid "The Pinhole"
+msgstr "Игольное ушко"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2768
+msgid ""
+"But, strictly speaking, the lens is not necessary: The original cameras did "
+"not use a lens but a _pinhole_. Even today, _pinholes_ are used, both as a "
+"tool to study how cameras work, and to achieve a special kind of image."
+msgstr ""
+"Но, строго говоря, линза не обязательна: первые камеры использовали не "
+"линзу, а _маленькое отверстие_ размером с игольное ушко. Даже сегодня "
+"_маленькие отверстия_ применяются как инструмент для изучения принципов "
+"работы камер и для создания особого вида изображений."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2772
+msgid ""
+"The image produced by the _pinhole_ is all equally sharp. Or _blurred_. "
+"There is an ideal size for a pinhole: If it is either larger or smaller, the "
+"image loses its sharpness."
+msgstr ""
+"Изображение, создаваемое _маленьким отверстием_, одинаково резкое. Или "
+"_размытое_. Существует идеальный размер для маленького отверстия: если оно "
+"больше или меньше, изображение теряет резкость."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2774
+#, no-wrap
+msgid "Focal Length"
+msgstr "Фокусное расстояние"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2777
+msgid ""
+"This ideal pinhole diameter is a function of the square root of _focal "
+"length_, which is the distance of the pinhole from the film."
+msgstr ""
+"Идеальный диаметр отверстия является функцией квадратного корня из "
+"_фокусного расстояния_, которое представляет собой расстояние от отверстия "
+"до плёнки."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2781
+#, no-wrap
+msgid "D = PC * sqrt(FL)\n"
+msgstr "D = PC * sqrt(FL)\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2787
+msgid ""
+"In here, `D` is the ideal diameter of the pinhole, `FL` is the focal length, "
+"and `PC` is a pinhole constant. According to Jay Bender, its value is "
+"`0.04`, while Kenneth Connors has determined it to be `0.037`. Others have "
+"proposed other values. Plus, this value is for the daylight only: Other "
+"types of light will require a different constant, whose value can only be "
+"determined by experimentation."
+msgstr ""
+"Здесь `D` — идеальный диаметр отверстия, `FL` — фокусное расстояние, а `PC` "
+"— константа отверстия. По данным Джейя Бендера, её значение равно `0,04`, "
+"тогда как Кеннет Коннорс определил его как `0,037`. Другие исследователи "
+"предложили иные значения. Кроме того, это значение справедливо только для "
+"дневного света: другие типы освещения потребуют иной константы, значение "
+"которой можно определить только экспериментальным путём."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2789
+#, no-wrap
+msgid "The F-Number"
+msgstr "Число f (диафрагменное число)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2793
+msgid ""
+"The f-number is a very useful measure of how much light reaches the film. A "
+"light meter can determine that, for example, to expose a film of specific "
+"sensitivity with f5.6 mkay require the exposure to last 1/1000 sec."
+msgstr ""
+"Число f — это очень полезный показатель того, сколько света попадает на "
+"плёнку. Экспонометр может определить, что, например, для экспонирования "
+"плёнки определённой чувствительности при f5.6 может потребоваться выдержка "
+"1/1000 сек."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2796
+msgid ""
+"It does not matter whether it is a 35-mm camera, or a 6x9cm camera, etc. As "
+"long as we know the f-number, we can determine the proper exposure."
+msgstr ""
+"Не имеет значения, 35-мм это камера или камера 6x9 см и т.д. Достаточно "
+"знать диафрагменное число, чтобы определить правильную экспозицию."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2798
+msgid "The f-number is easy to calculate:"
+msgstr "Число f легко вычислить:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2802
+#, no-wrap
+msgid "F = FL / D\n"
+msgstr "F = FL / D\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2807
+msgid ""
+"In other words, the f-number equals the focal length divided by the diameter "
+"of the pinhole. It also means a higher f-number either implies a smaller "
+"pinhole or a larger focal distance, or both. That, in turn, implies, the "
+"higher the f-number, the longer the exposure has to be."
+msgstr ""
+"Другими словами, число f равно фокусному расстоянию, деленному на диаметр "
+"отверстия. Это также означает, что большее f-число подразумевает либо "
+"меньшее отверстие, либо большее фокусное расстояние, либо и то, и другое. В "
+"свою очередь, это означает, что чем больше число f, тем дольше должна быть "
+"выдержка."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2810
+msgid ""
+"Furthermore, while pinhole diameter and focal distance are one-dimensional "
+"measurements, both, the film and the pinhole, are two-dimensional. That "
+"means that if you have measured the exposure at f-number `A` as `t`, then "
+"the exposure at f-number `B` is:"
+msgstr ""
+"Кроме того, хотя диаметр отверстия и фокусное расстояние являются "
+"одномерными величинами, и плёнка, и отверстие — двумерны. Это означает, что "
+"если вы измерили экспозицию при диафрагменном числе `A` как `t`, то "
+"экспозиция при диафрагменном числе `B` будет:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2814
+#, no-wrap
+msgid "t * (B / A)²\n"
+msgstr "t * (B / A)²\n"
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2817
+#, no-wrap
+msgid "Normalized F-Number"
+msgstr "Нормализованное число f"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2820
+msgid ""
+"While many modern cameras can change the diameter of their pinhole, and thus "
+"their f-number, quite smoothly and gradually, such was not always the case."
+msgstr ""
+"Хотя многие современные камеры могут изменять диаметр своего отверстия, а "
+"следовательно и свое число f, довольно плавно и постепенно, так было не "
+"всегда."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2822
+msgid ""
+"To allow for different f-numbers, cameras typically contained a metal plate "
+"with several holes of different sizes drilled to them."
+msgstr ""
+"Для обеспечения различных значений диафрагмы в камерах обычно использовалась "
+"металлическая пластина с несколькими отверстиями разного размера."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2825
+msgid ""
+"Their sizes were chosen according to the above formula in such a way that "
+"the resultant f-number was one of standard f-numbers used on all cameras "
+"everywhere. For example, a very old Kodak Duaflex IV camera in my "
+"possession has three such holes for f-numbers 8, 11, and 16."
+msgstr ""
+"Их размеры были выбраны в соответствии с приведённой выше формулой таким "
+"образом, чтобы результирующее f-число было одним из стандартных f-чисел, "
+"используемых на всех фотоаппаратах. Например, у моего очень старого "
+"фотоаппарата Kodak Duaflex IV есть три таких отверстия для чисел f — 8, 11 и "
+"16."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2828
+msgid ""
+"A more recently made camera may offer f-numbers of 2.8, 4, 5.6, 8, 11, 16, "
+"22, and 32 (as well as others). These numbers were not chosen arbitrarily: "
+"They all are powers of the square root of 2, though they may be rounded "
+"somewha."
+msgstr ""
+"Более современные камеры могут предлагать значения диафрагменного числа 2.8, "
+"4, 5.6, 8, 11, 16, 22 и 32 (а также другие). Эти числа выбраны не "
+"произвольно: все они являются степенями квадратного корня из 2, хотя могут "
+"быть немного округлены."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2830
+#, no-wrap
+msgid "The F-Stop"
+msgstr "Ступени числа f"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2834
+msgid ""
+"A typical camera is designed in such a way that setting any of the "
+"normalized f-numbers changes the feel of the dial. It will naturally _stop_ "
+"in that position. Because of that, these positions of the dial are called f-"
+"stops."
+msgstr ""
+"Типичная камера устроена так, что установка любого из нормализованных чисел "
+"f изменяет ощущение от регулятора. Он естественным образом _останавливается_ "
+"в этом положении. Из-за этого такие положения регулятора называются f-"
+"ступенями."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2838
+msgid ""
+"Since the f-numbers at each stop are powers of the square root of 2, moving "
+"the dial by 1 stop will double the amount of light required for proper "
+"exposure. Moving it by 2 stops will quadruple the required exposure. "
+"Moving the dial by 3 stops will require the increase in exposure 8 times, "
+"etc."
+msgstr ""
+"Поскольку значения диафрагмы на каждой ступени являются степенями "
+"квадратного корня из 2, поворот диска на 1 ступень удваивает количество "
+"света, необходимое для правильной экспозиции. Поворот на 2 ступени "
+"увеличивает требуемую экспозицию вчетверо. Поворот диска на 3 ступени "
+"требует увеличения экспозиции в 8 раз и так далее."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2840
+#, no-wrap
+msgid "Designing the Pinhole Software"
+msgstr "Проектирование программного обеспечения камеры-обскуры"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2843
+msgid ""
+"We are now ready to decide what exactly we want our pinhole software to do."
+msgstr ""
+"Мы готовы решить, что именно должно делать наше программное обеспечение для "
+"камер-обскур."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2845
+#, no-wrap
+msgid "Processing Program Input"
+msgstr "Обработка ввода программы"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2849
+msgid ""
+"Since its main purpose is to help us design a working pinhole camera, we "
+"will use the _focal length_ as the input to the program. This is something "
+"we can determine without software: Proper focal length is determined by the "
+"size of the film and by the need to shoot \"regular\" pictures, wide angle "
+"pictures, or telephoto pictures."
+msgstr ""
+"Поскольку основная цель — помочь нам разработать работающую камеру-обскуру, "
+"мы будем использовать _фокусное расстояние_ в качестве входных данных для "
+"программы. Это можно определить без программного обеспечения: правильное "
+"фокусное расстояние зависит от размера плёнки и необходимости съёмки "
+"«обычных» изображений, широкоугольных или телефото."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2851
+msgid ""
+"Most of the programs we have written so far worked with individual "
+"characters, or bytes, as their input: The hex program converted individual "
+"bytes into a hexadecimal number, the csv program either let a character "
+"through, or deleted it, or changed it to a different character, etc."
+msgstr ""
+"Большинство написанных нами до сих пор программ работали с отдельными "
+"символами или байтами в качестве входных данных: программа hex "
+"преобразовывала отдельные байты в шестнадцатеричное число, программа csv "
+"либо пропускала символ, либо удаляла его, либо заменяла на другой символ и "
+"т.д."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2853
+msgid ""
+"One program, ftuc used the state machine to consider at most two input bytes "
+"at a time."
+msgstr ""
+"Одна программа, `ftuc`, использовала конечный автомат для обработки не более "
+"двух входных байтов за раз."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2855
+msgid ""
+"But our pinhole program cannot just work with individual characters, it has "
+"to deal with larger syntactic units."
+msgstr ""
+"Но наша программа для камеры-обскуры не может работать только с отдельными "
+"символами, ей приходится иметь дело с более крупными синтаксическими "
+"единицами."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2857
+msgid ""
+"For example, if we want the program to calculate the pinhole diameter (and "
+"other values we will discuss later) at the focal lengths of `100 mm`, `150 "
+"mm`, and `210 mm`, we may want to enter something like this:"
+msgstr ""
+"Например, если мы хотим, чтобы программа рассчитала диаметр отверстия (и "
+"другие значения, которые мы обсудим позже) для фокусных расстояний `100 мм`, "
+"`150 мм` и `210 мм`, мы можем ввести что-то вроде этого:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2861
+#, no-wrap
+msgid " 100, 150, 210\n"
+msgstr " 100, 150, 210\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2866
+msgid ""
+"Our program needs to consider more than a single byte of input at a time. "
+"When it sees the first `1`, it must understand it is seeing the first digit "
+"of a decimal number. When it sees the `0` and the other `0`, it must know "
+"it is seeing more digits of the same number."
+msgstr ""
+"Наша программа должна учитывать более одного байта входных данных за раз. "
+"Когда она видит первую `1`, она должна понимать, что это первая цифра "
+"десятичного числа. Когда она видит `0` и другой `0`, она должна знать, что "
+"это следующие цифры того же числа."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2871
+msgid ""
+"When it encounters the first comma, it must know it is no longer receiving "
+"the digits of the first number. It must be able to convert the digits of "
+"the first number into the value of `100`. And the digits of the second "
+"number into the value of `150`. And, of course, the digits of the third "
+"number into the numeric value of `210`."
+msgstr ""
+"Когда он встречает первую запятую, он должен понять, что больше не получает "
+"цифры первого числа. Он должен уметь преобразовать цифры первого числа в "
+"значение `100`. И цифры второго числа в значение `150`. И, конечно же, цифры "
+"третьего числа в числовое значение `210`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2873
+msgid ""
+"We need to decide what delimiters to accept: Do the input numbers have to be "
+"separated by a comma? If so, how do we treat two numbers separated by "
+"something else?"
+msgstr ""
+"Нам нужно определиться с допустимыми разделителями: должны ли входные числа "
+"разделяться запятой? Если да, то как обрабатывать два числа, разделённые чем-"
+"то другим?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2878
+msgid ""
+"Personally, I like to keep it simple. Something either is a number, so I "
+"process it. Or it is not a number, so I discard it. I do not like the "
+"computer complaining about me typing in an extra character when it is "
+"_obvious_ that it is an extra character. Duh!"
+msgstr ""
+"Лично я предпочитаю простоту. Либо что-то является числом — и тогда я его "
+"обрабатываю. Либо не является числом — и тогда я это отбрасываю. Мне не "
+"нравится, когда компьютер жалуется на лишний символ, если _очевидно_, что он "
+"лишний. Да ладно!"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2880
+msgid ""
+"Plus, it allows me to break up the monotony of computing and type in a query "
+"instead of just a number:"
+msgstr ""
+"Плюс, это позволяет мне разбавить монотонность вычислений и ввести запрос "
+"вместо просто числа:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2885
+#, no-wrap
+msgid ""
+"What is the best pinhole diameter for the\n"
+"\t focal length of 150?\n"
+msgstr ""
+"What is the best pinhole diameter for the\n"
+"\t focal length of 150?\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2888
+msgid "There is no reason for the computer to spit out a number of complaints:"
+msgstr "Нет причины, чтобы компьютер выводил множество жалоб:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2895
+#, no-wrap
+msgid ""
+"Syntax error: What\n"
+"Syntax error: is\n"
+"Syntax error: the\n"
+"Syntax error: best\n"
+msgstr ""
+"Syntax error: What\n"
+"Syntax error: is\n"
+"Syntax error: the\n"
+"Syntax error: best\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2898
+msgid "Et cetera, et cetera, et cetera."
+msgstr "И так далее, и так далее, и так далее."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2901
+msgid ""
+"Secondly, I like the `+#+` character to denote the start of a comment which "
+"extends to the end of the line. This does not take too much effort to code, "
+"and lets me treat input files for my software as executable scripts."
+msgstr ""
+"Во-вторых, мне нравится символ `+#+` для обозначения начала комментария, "
+"который продолжается до конца строки. Это не требует больших усилий для "
+"реализации и позволяет мне рассматривать входные файлы для моего "
+"программного обеспечения как исполняемые скрипты."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2903
+msgid ""
+"In our case, we also need to decide what units the input should come in: We "
+"choose _millimeters_ because that is how most photographers measure the "
+"focus length."
+msgstr ""
+"В нашем случае также необходимо определиться с единицами измерения входных "
+"данных: мы выбираем _миллиметры_, так как большинство фотографов измеряют "
+"фокусное расстояние именно в них."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2905
+msgid ""
+"Finally, we need to decide whether to allow the use of the decimal point (in "
+"which case we must also consider the fact that much of the world uses a "
+"decimal _comma_)."
+msgstr ""
+"Наконец, нам нужно решить, разрешать ли использование десятичной точки (в "
+"этом случае мы также должны учитывать тот факт, что во многих странах "
+"используется десятичная _запятая_)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2909
+msgid ""
+"In our case allowing for the decimal point/comma would offer a false sense "
+"of precision: There is little if any noticeable difference between the focus "
+"lengths of `50` and `51`, so allowing the user to input something like "
+"`50.5` is not a good idea. This is my opinion, mind you, but I am the one "
+"writing this program. You can make other choices in yours, of course."
+msgstr ""
+"В нашем случае разрешение десятичной точки/запятой создало бы ложное "
+"ощущение точности: разница между фокусными расстояниями `50` и `51` "
+"практически незаметна, поэтому разрешать пользователю вводить что-то вроде "
+"`50.5` — не лучшая идея. Это моё мнение, конечно, но программу пишу я. В "
+"своей программе вы можете сделать другие выбор, разумеется."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2911
+#, no-wrap
+msgid "Offering Options"
+msgstr "Передача параметров программе"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2916
+msgid ""
+"The most important thing we need to know when building a pinhole camera is "
+"the diameter of the pinhole. Since we want to shoot sharp images, we will "
+"use the above formula to calculate the pinhole diameter from focal length. "
+"As experts are offering several different values for the `PC` constant, we "
+"will need to have the choice."
+msgstr ""
+"Самое важное, что нам нужно знать при создании камеры-обскуры — это диаметр "
+"отверстия. Поскольку мы хотим получать чёткие изображения, мы будем "
+"использовать приведённую выше формулу для расчёта диаметра отверстия от "
+"фокусного расстояния. Поскольку эксперты предлагают несколько различных "
+"значений для константы `PC`, нам нужно будет иметь выбор."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2918
+msgid ""
+"It is traditional in UNIX(R) programming to have two main ways of choosing "
+"program parameters, plus to have a default for the time the user does not "
+"make a choice."
+msgstr ""
+"В традициях программирования в UNIX(R) предусмотрены два основных способа "
+"выбора параметров программы, а также значение по умолчанию на случай, если "
+"пользователь не сделает выбор."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2920
+msgid "Why have two ways of choosing?"
+msgstr "Почему есть два способа выбора?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2922
+msgid ""
+"One is to allow a (relatively) _permanent_ choice that applies automatically "
+"each time the software is run without us having to tell it over and over "
+"what we want it to do."
+msgstr ""
+"Один из способов — это позволить (относительно) _постоянный_ выбор, который "
+"применяется автоматически каждый раз при запуске программы, без "
+"необходимости каждый раз указывать, что мы хотим, чтобы она сделала."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2927
+msgid ""
+"The permanent choices may be stored in a configuration file, typically found "
+"in the user's home directory. The file usually has the same name as the "
+"application but is started with a dot. Often _\"rc\"_ is added to the file "
+"name. So, ours could be [.filename]#~/.pinhole# or "
+"[.filename]#~/.pinholerc#. (The [.filename]#~/# means current user's home "
+"directory.)"
+msgstr ""
+"Постоянные настройки могут быть сохранены в конфигурационном файле, обычно "
+"расположенном в домашнем каталоге пользователя. Файл обычно имеет то же имя, "
+"что и приложение, но начинается с точки. Часто к имени файла добавляется "
+"_\"rc\"_. Таким образом, наш файл может называться [.filename]#~/.pinhole# "
+"или [.filename]#~/.pinholerc#. (Обозначение [.filename]#~/# означает "
+"домашний каталог текущего пользователя.)"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2932
+msgid ""
+"The configuration file is used mostly by programs that have many "
+"configurable parameters. Those that have only one (or a few) often use a "
+"different method: They expect to find the parameter in an _environment "
+"variable_. In our case, we might look at an environment variable named "
+"`PINHOLE`."
+msgstr ""
+"Файл конфигурации в основном используется программами, у которых много "
+"настраиваемых параметров. Те, у которых он один (или несколько), часто "
+"используют другой метод: они ожидают найти параметр в _переменной "
+"окружения_. В нашем случае, мы можем посмотреть на переменную окружения с "
+"именем `PINHOLE`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2935
+msgid ""
+"Usually, a program uses one or the other of the above methods. Otherwise, "
+"if a configuration file said one thing, but an environment variable another, "
+"the program might get confused (or just too complicated)."
+msgstr ""
+"Обычно программа использует один из вышеуказанных методов. В противном "
+"случае, если в конфигурационном файле указано одно, а в переменной окружения "
+"— другое, программа может запутаться (или стать слишком сложной)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2937
+msgid ""
+"Because we only need to choose _one_ such parameter, we will go with the "
+"second method and search the environment for a variable named `PINHOLE`."
+msgstr ""
+"Поскольку нам нужно выбрать только _один_ такой параметр, мы воспользуемся "
+"вторым методом и поищем в окружении переменную с именем `PINHOLE`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2940
+msgid ""
+"The other way allows us to make _ad hoc_ decisions: _\"Though I usually want "
+"you to use 0.039, this time I want 0.03872.\"_ In other words, it allows us "
+"to _override_ the permanent choice."
+msgstr ""
+"Другой способ позволяет нам принимать _ad hoc_ решения: _\"Хотя обычно я "
+"хочу, чтобы ты использовал 0.039, на этот раз мне нужно 0.03872.\"_ Другими "
+"словами, он позволяет нам _переопределить_ постоянный выбор."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2942
+msgid "This type of choice is usually done with command line parameters."
+msgstr ""
+"Такой выбор обычно осуществляется с помощью параметров командной строки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2950
+msgid ""
+"Finally, a program _always_ needs a _default_. The user may not make any "
+"choices. Perhaps he does not know what to choose. Perhaps he is \"just "
+"browsing.\" Preferably, the default will be the value most users would "
+"choose anyway. That way they do not need to choose. Or, rather, they can "
+"choose the default without an additional effort."
+msgstr ""
+"Наконец, программе _всегда_ необходим _значение по умолчанию_. Пользователь "
+"может не делать никакого выбора. Возможно, он не знает, что выбрать. "
+"Возможно, он «просто просматривает». Предпочтительно, чтобы значением по "
+"умолчанию было то, что выбрало бы большинство пользователей. Таким образом, "
+"им не нужно выбирать. Или, точнее, они могут выбрать значение по умолчанию "
+"без дополнительных усилий."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2952
+msgid ""
+"Given this system, the program may find conflicting options, and handle them "
+"this way:"
+msgstr ""
+"Учитывая эту систему, программа может обнаружить конфликтующие параметры и "
+"обработать их следующим образом:"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2955
+msgid ""
+"If it finds an _ad hoc_ choice (e.g., command line parameter), it should "
+"accept that choice. It must ignore any permanent choice and any default."
+msgstr ""
+"Если она находит _специальный_ выбор (например, параметр командной строки), "
+"она должна принять этот выбор. Она должна игнорировать любой постоянный "
+"выбор и значения по умолчанию."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2956
+msgid ""
+"_Otherwise_, if it finds a permanent option (e.g., an environment variable), "
+"it should accept it, and ignore the default."
+msgstr ""
+"_В противном случае_, если будет найден постоянный параметр (например, "
+"переменная окружения), он должен быть принят, а значение по умолчанию — "
+"проигнорировано."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2957
+msgid "_Otherwise_, it should use the default."
+msgstr "_В противном случае_, следует использовать значение по умолчанию."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2959
+msgid "We also need to decide what _format_ our `PC` option should have."
+msgstr ""
+"Нам также необходимо решить, в каком _формате_ должна быть наша опция `PC`."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2961
+msgid ""
+"At first site, it seems obvious to use the `PINHOLE=0.04` format for the "
+"environment variable, and `-p0.04` for the command line."
+msgstr ""
+"На первый взгляд кажется очевидным использовать формат `PINHOLE=0.04` для "
+"переменной окружения и `-p0.04` для командной строки."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2965
+msgid ""
+"Allowing that is actually a security risk. The `PC` constant is a very small "
+"number. Naturally, we will test our software using various small values of "
+"`PC`. But what will happen if someone runs the program choosing a huge "
+"value?"
+msgstr ""
+"Разрешение этого на самом деле представляет угрозу безопасности. Константа "
+"`PC` — это очень маленькое число. Естественно, мы протестируем наше "
+"программное обеспечение, используя различные небольшие значения `PC`. Но что "
+"произойдёт, если кто-то запустит программу, выбрав огромное значение?"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2967
+msgid ""
+"It may crash the program because we have not designed it to handle huge "
+"numbers."
+msgstr ""
+"Это может привести к сбою программы, так как мы не разрабатывали её для "
+"обработки огромных чисел."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2970
+msgid ""
+"Or, we may spend more time on the program so it can handle huge numbers. We "
+"might do that if we were writing commercial software for computer illiterate "
+"audience."
+msgstr ""
+"Или мы можем потратить больше времени на программу, чтобы она могла "
+"обрабатывать огромные числа. Мы могли бы сделать это, если бы писали "
+"коммерческое программное обеспечение для аудитории, не знакомой с "
+"компьютерами."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2972
+msgid "Or, we might say, _\"Tough! The user should know better.\"\"_"
+msgstr ""
+"Или можно сказать: _\"Пусть терпит! Пользователь сам должен был разобраться."
+"\"_"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2975
+msgid ""
+"Or, we just may make it impossible for the user to enter a huge number. "
+"This is the approach we will take: We will use an _implied 0._ prefix."
+msgstr ""
+"Или мы можем просто сделать невозможным ввод пользователем слишком большого "
+"числа. Это подход, который мы выберем: мы будем использовать "
+"_подразумеваемый префикс 0._."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2978
+msgid ""
+"In other words, if the user wants `0.04`, we will expect him to type `-p04`, "
+"or set `PINHOLE=04` in his environment. So, if he says `-p9999999`, we will "
+"interpret it as ``0.9999999``-still ridiculous but at least safer."
+msgstr ""
+"Другими словами, если пользователь хочет `0.04`, мы ожидаем, что он введёт `-"
+"p04` или установит `PINHOLE=04` в своём окружении. Таким образом, если он "
+"укажет `-p9999999`, мы интерпретируем это как ``0.9999999`` — всё ещё "
+"нелепо, но по крайней мере безопаснее."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2981
+msgid ""
+"Secondly, many users will just want to go with either Bender's constant or "
+"Connors' constant. To make it easier on them, we will interpret `-b` as "
+"identical to `-p04`, and `-c` as identical to `-p037`."
+msgstr ""
+"Во-вторых, многие пользователи просто захотят использовать либо константу "
+"Бендера, либо константу Коннорса. Чтобы облегчить им задачу, мы будем "
+"интерпретировать `-b` как идентичное `-p04`, а `-c` как идентичное `-p037`."
+
+#. type: Title ====
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2983
+#, no-wrap
+msgid "The Output"
+msgstr "Вывод результата"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2986
+msgid ""
+"We need to decide what we want our software to send to the output, and in "
+"what format."
+msgstr ""
+"Нам нужно решить, что наше программное обеспечение должно отправлять на "
+"вывод и в каком формате."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2988
+msgid ""
+"Since our input allows for an unspecified number of focal length entries, it "
+"makes sense to use a traditional database-style output of showing the result "
+"of the calculation for each focal length on a separate line, while "
+"separating all values on one line by a `tab` character."
+msgstr ""
+"Поскольку наши входные данные допускают неограниченное количество значений "
+"фокусного расстояния, имеет смысл использовать традиционный вывод в стиле "
+"базы данных, показывая результат вычислений для каждого фокусного расстояния "
+"на отдельной строке, разделяя все значения в строке символом табуляции."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2991
+msgid ""
+"Optionally, we should also allow the user to specify the use of the CSV "
+"format we have studied earlier. In this case, we will print out a line of "
+"comma-separated names describing each field of every line, then show our "
+"results as before, but substituting a `comma` for the `tab`."
+msgstr ""
+"Опционально, мы также должны разрешить пользователю указать использование "
+"формата CSV, который мы изучили ранее. В этом случае мы выведем строку с "
+"разделёнными запятыми названиями, описывающими каждое поле каждой строки, а "
+"затем отобразим результаты как прежде, но заменив `табуляцию` на `запятую`."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:2996
+msgid ""
+"We need a command line option for the CSV format. We cannot use `-c` "
+"because that already means _use Connors' constant_. For some strange "
+"reason, many web sites refer to CSV files as _\"Excel spreadsheet\"_ (though "
+"the CSV format predates Excel). We will, therefore, use the `-e` switch to "
+"inform our software we want the output in the CSV format."
+msgstr ""
+"Нам нужна опция командной строки для формата CSV. Мы не можем использовать `-"
+"c`, потому что это уже означает _использовать константу Коннорса_. По какой-"
+"то странной причине многие веб-сайты называют CSV-файлы _\"электронными "
+"таблицами Excel\"_ (хотя формат CSV появился раньше Excel). Поэтому мы будем "
+"использовать переключатель `-e`, чтобы указать нашему программному "
+"обеспечению, что мы хотим получить вывод в формате CSV."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3000
+msgid ""
+"We will start each line of the output with the focal length. This may sound "
+"repetitious at first, especially in the interactive mode: The user types in "
+"the focal length, and we are repeating it."
+msgstr ""
+"Мы начнем каждую строку вывода с фокусного расстояния. Это может показаться "
+"избыточным сначала, особенно в интерактивном режиме: пользователь вводит "
+"фокусное расстояние, а мы его повторяем."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3004
+msgid ""
+"But the user can type several focal lengths on one line. The input can also "
+"come in from a file or from the output of another program. In that case the "
+"user does not see the input at all."
+msgstr ""
+"Но пользователь может ввести несколько фокусных расстояний в одной строке. "
+"Ввод также может поступать из файла или вывода другой программы. В этом "
+"случае пользователь вообще не видит вводимые данные."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3006
+msgid ""
+"By the same token, the output can go to a file which we will want to examine "
+"later, or it could go to the printer, or become the input of another program."
+msgstr ""
+"Таким же образом, вывод может быть направлен в файл, который мы захотим "
+"изучить позже, или на принтер, или стать входными данными для другой "
+"программы."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3008
+msgid ""
+"So, it makes perfect sense to start each line with the focal length as "
+"entered by the user."
+msgstr ""
+"Итак, имеет полный смысл начинать каждую строку с фокусного расстояния, "
+"введённого пользователем."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3011
+msgid ""
+"No, wait! Not as entered by the user. What if the user types in something "
+"like this:"
+msgstr ""
+"Нет, подождите! Не так, как ввел пользователь. Что, если пользователь "
+"введет что-то вроде этого:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3015
+#, no-wrap
+msgid " 00000000150\n"
+msgstr " 00000000150\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3018
+msgid "Clearly, we need to strip those leading zeros."
+msgstr "Очевидно, нам нужно удалить ведущие нули."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3020
+msgid ""
+"So, we might consider reading the user input as is, converting it to binary "
+"inside the FPU, and printing it out from there."
+msgstr ""
+"Итак, можно рассмотреть вариант чтения пользовательского ввода как есть, "
+"преобразования его в бинарный вид внутри FPU и последующего вывода оттуда."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3022
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3207
+msgid "But..."
+msgstr "Но..."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3024
+msgid "What if the user types something like this:"
+msgstr "Что делать, если пользователь введёт что-то вроде этого:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3028
+#, no-wrap
+msgid " 17459765723452353453534535353530530534563507309676764423\n"
+msgstr " 17459765723452353453534535353530530534563507309676764423\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3033
+msgid ""
+"Ha! The packed decimal FPU format lets us input 18-digit numbers. But the "
+"user has entered more than 18 digits. How do we handle that?"
+msgstr ""
+"Ха! Упакованный десятичный формат FPU позволяет нам вводить 18-значные "
+"числа. Но пользователь ввёл больше 18 цифр. Как нам обработать это?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3035
+msgid ""
+"Well, we _could_ modify our code to read the first 18 digits, enter it to "
+"the FPU, then read more, multiply what we already have on the TOS by 10 "
+"raised to the number of additional digits, then `add` to it."
+msgstr ""
+"Хорошо, мы _могли бы_ изменить наш код, чтобы он читал первые 18 цифр, "
+"передавал их в FPU, затем читал ещё, умножал уже имеющееся на вершине стека "
+"(TOS) на 10 в степени количества дополнительных цифр, а затем выполнял "
+"`сложение` с этим значением."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3039
+msgid ""
+"Yes, we could do that. But in _this_ program it would be ridiculous (in a "
+"different one it may be just the thing to do): Even the circumference of the "
+"Earth expressed in millimeters only takes 11 digits. Clearly, we cannot "
+"build a camera that large (not yet, anyway)."
+msgstr ""
+"Да, мы могли бы так поступить. Но в _этой_ программе это было бы нелепо (в "
+"другой это могло бы быть как раз тем, что нужно): даже длина окружности "
+"Земли, выраженная в миллиметрах, занимает всего 11 цифр. Очевидно, мы не "
+"можем построить камеру такого размера (по крайней мере, пока)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3041
+msgid ""
+"So, if the user enters such a huge number, he is either bored, or testing "
+"us, or trying to break into the system, or playing games-doing anything but "
+"designing a pinhole camera."
+msgstr ""
+"Итак, если пользователь вводит такое огромное число, он либо скучает, либо "
+"проверяет нас, либо пытается взломать систему, либо играет — делает что "
+"угодно, кроме проектирования камеры-обскуры."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3043
+msgid "What will we do?"
+msgstr "Что мы будем делать?"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3045
+msgid "We will slap him in the face, in a manner of speaking:"
+msgstr "Мы ударим его по лицу, образно говоря:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3049
+#, no-wrap
+msgid "17459765723452353453534535353530530534563507309676764423\t???\t???\t???\t???\t???\n"
+msgstr "17459765723452353453534535353530530534563507309676764423\t???\t???\t???\t???\t???\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3053
+msgid ""
+"To achieve that, we will simply ignore any leading zeros. Once we find a "
+"non-zero digit, we will initialize a counter to `0` and start taking three "
+"steps:"
+msgstr ""
+"Для этого мы просто проигнорируем все ведущие нули. Как только мы найдем "
+"ненулевую цифру, мы инициализируем счетчик значением `0` и начнем выполнять "
+"три шага:"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3056
+msgid "Send the digit to the output."
+msgstr "Отправить цифру на выход."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3057
+msgid ""
+"Append the digit to a buffer we will use later to produce the packed decimal "
+"we can send to the FPU."
+msgstr ""
+"Добавить цифру в буфер, который мы позже используем для создания "
+"упакованного десятичного числа, которое можно отправить в FPU."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3058
+msgid "Increase the counter."
+msgstr "Увеличить счетчик."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3060
+msgid ""
+"Now, while we are taking these three steps, we also need to watch out for "
+"one of two conditions:"
+msgstr ""
+"Теперь, пока мы выполняем эти три шага, нам также необходимо следить за "
+"одним из двух условий:"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3062
+msgid ""
+"If the counter grows above 18, we stop appending to the buffer. We continue "
+"reading the digits and sending them to the output."
+msgstr ""
+"Если счётчик превышает 18, мы прекращаем добавление в буфер. Мы продолжаем "
+"читать цифры и отправлять их на вывод."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3063
+msgid ""
+"If, or rather _when_, the next input character is not a digit, we are done "
+"inputting for now."
+msgstr ""
+"Если, или скорее _когда_, следующий вводимый символ не является цифрой, мы "
+"завершаем ввод на данный момент."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3066
+msgid ""
+"Incidentally, we can simply discard the non-digit, unless it is a `+#+`, "
+"which we must return to the input stream. It starts a comment, so we must "
+"see it after we are done producing output and start looking for more input."
+msgstr ""
+"Между прочим, мы можем просто отбросить нецифровой символ, если это не "
+"`+#+`, который необходимо вернуть во входной поток. Он начинает комментарий, "
+"поэтому мы должны увидеть его после завершения вывода и начала поиска "
+"следующего ввода."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3068
+msgid ""
+"That still leaves one possibility uncovered: If all the user enters is a "
+"zero (or several zeros), we will never find a non-zero to display."
+msgstr ""
+"Остается одна непокрытая возможность: если пользователь вводит только ноль "
+"(или несколько нулей), мы никогда не найдем ненулевое значение для "
+"отображения."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3071
+msgid ""
+"We can determine this has happened whenever our counter stays at `0`. In "
+"that case we need to send `0` to the output, and perform another \"slap in "
+"the face\":"
+msgstr ""
+"Мы можем определить, что это произошло, когда наш счетчик остается на `0`. В "
+"этом случае нам нужно отправить `0` на выход и выполнить еще один \"удар по "
+"лицу\":"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3075
+#, no-wrap
+msgid "0\t???\t???\t???\t???\t???\n"
+msgstr "0\t???\t???\t???\t???\t???\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3078
+msgid ""
+"Once we have displayed the focal length and determined it is valid (greater "
+"than `0` but not exceeding 18 digits), we can calculate the pinhole diameter."
+msgstr ""
+"Как только мы определили фокусное расстояние и убедились, что оно корректно "
+"(больше `0`, но не превышает 18 цифр), можно рассчитать диаметр отверстия."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3081
+msgid ""
+"It is not by coincidence that _pinhole_ contains the word _pin_. Indeed, "
+"many a pinhole literally is a _pin hole_, a hole carefully punched with the "
+"tip of a pin."
+msgstr ""
+"Не случайно слово _булавочное ушко_ содержит слово _булавка_. Действительно, "
+"многие малые отверстия буквально являются _дырками от булавки_ — "
+"отверстиями, аккуратно проделанными остриём булавки."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3084
+msgid ""
+"That is because a typical pinhole is very small. Our formula gets the result "
+"in millimeters. We will multiply it by `1000`, so we can output the result "
+"in _microns_."
+msgstr ""
+"Вот потому что типичное отверстие очень маленькое. Наша формула дает "
+"результат в миллиметрах. Мы умножим его на `1000`, чтобы вывести результат в "
+"_микронах_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3086
+msgid "At this point we have yet another trap to face: _Too much precision._"
+msgstr "На этом этапе нас ожидает ещё одна ловушка: _Излишняя точность._"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3090
+msgid ""
+"Yes, the FPU was designed for high precision mathematics. But we are not "
+"dealing with high precision mathematics. We are dealing with physics "
+"(optics, specifically)."
+msgstr ""
+"Да, FPU был разработан для вычислений с высокой точностью. Но мы имеем дело "
+"не с вычислениями высокой точности. Мы имеем дело с физикой (конкретно, с "
+"оптикой)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3094
+msgid ""
+"Suppose we want to convert a truck into a pinhole camera (we would not be "
+"the first ones to do that!). Suppose its box is `12` meters long, so we "
+"have the focal length of `12000`. Well, using Bender's constant, it gives "
+"us square root of `12000` multiplied by `0.04`, which is `4.381780460` "
+"millimeters, or `4381.780460` microns."
+msgstr ""
+"Предположим, мы хотим превратить грузовик в камеру-обскуру (мы будем не "
+"первыми, кто это сделал!). Допустим, его кузов имеет длину `12` метров, "
+"значит, фокусное расстояние равно `12000`. Используя константу Бендера, "
+"получаем квадратный корень из `12000`, умноженный на `0.04`, что составляет "
+"`4.381780460` миллиметра или `4381.780460` микрона."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3099
+msgid ""
+"Put either way, the result is absurdly precise. Our truck is not _exactly_ "
+"`12000` millimeters long. We did not measure its length with such a "
+"precision, so stating we need a pinhole with the diameter of `4.381780460` "
+"millimeters is, well, deceiving. `4.4` millimeters would do just fine."
+msgstr ""
+"Как ни посмотри, результат абсурдно точен. Наш грузовик не имеет _точно_ "
+"`12000` миллиметров в длину. Мы не измеряли его длину с такой точностью, "
+"поэтому утверждение, что нам нужна отверстие диаметром `4,381780460` "
+"миллиметра, мягко говоря, вводит в заблуждение. `4,4` миллиметра будет "
+"вполне достаточно."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3104
+msgid ""
+"I \"only\" used ten digits in the above example. Imagine the absurdity of "
+"going for all 18!"
+msgstr ""
+"Я \"всего лишь\" использовал десять цифр в приведенном выше примере. "
+"Представьте абсурдность попытки использовать все 18!"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3110
+msgid ""
+"We need to limit the number of significant digits of our result. One way of "
+"doing it is by using an integer representing microns. So, our truck would "
+"need a pinhole with the diameter of `4382` microns. Looking at that number, "
+"we still decide that `4400` microns, or `4.4` millimeters is close enough."
+msgstr ""
+"Нам нужно ограничить количество значащих цифр в нашем результате. Один из "
+"способов сделать это — использовать целое число, представляющее микроны. "
+"Таким образом, нашему грузовику потребуется отверстие диаметром `4382` "
+"микрона. Глядя на это число, мы всё же решаем, что `4400` микрон, или `4.4` "
+"миллиметра, достаточно близко."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3113
+msgid ""
+"Additionally, we can decide that no matter how big a result we get, we only "
+"want to display four significant digits (or any other number of them, of "
+"course). Alas, the FPU does not offer rounding to a specific number of "
+"digits (after all, it does not view the numbers as decimal but as binary)."
+msgstr ""
+"Кроме того, мы можем решить, что независимо от размера результата, мы хотим "
+"отображать только четыре значащих цифры (или любое другое их количество, "
+"конечно). Увы, FPU не поддерживает округление до определенного количества "
+"цифр (в конце концов, он воспринимает числа не как десятичные, а как "
+"двоичные)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3115
+msgid ""
+"We, therefore, must devise an algorithm to reduce the number of significant "
+"digits."
+msgstr ""
+"Следовательно, мы должны разработать алгоритм для уменьшения количества "
+"значащих цифр."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3117
+msgid ""
+"Here is mine (I think it is awkward-if you know a better one, _please_, let "
+"me know):"
+msgstr ""
+"Вот мой (я думаю, он неуклюжий — если у вас есть вариант лучше, "
+"_пожалуйста_, дайте мне знать):"
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3120
+msgid "Initialize a counter to `0`."
+msgstr "Инициализировать счетчик значением `0`."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3121
+msgid ""
+"While the number is greater than or equal to `10000`, divide it by `10` and "
+"increase the counter."
+msgstr ""
+"Пока число больше или равно `10000`, делим его на `10` и увеличиваем счётчик."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3122
+msgid "Output the result."
+msgstr "Вывести результат."
+
+#. type: .procedure
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3123
+msgid ""
+"While the counter is greater than `0`, output `0` and decrease the counter."
+msgstr "Пока счетчик больше `0`, выводить `0` и уменьшать счетчик."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3128
+msgid ""
+"The `10000` is only good if you want _four_ significant digits. For any "
+"other number of significant digits, replace `10000` with `10` raised to the "
+"number of significant digits."
+msgstr ""
+"`10000` подходит только если вам нужно _четыре_ значащих цифры. Для любого "
+"другого количества значащих цифр замените `10000` на `10` в степени, равной "
+"количеству значащих цифр."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3131
+msgid ""
+"We will, then, output the pinhole diameter in microns, rounded off to four "
+"significant digits."
+msgstr ""
+"Мы затем выведем диаметр отверстия в микронах, округлённый до четырёх "
+"значащих цифр."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3134
+msgid ""
+"At this point, we know the _focal length_ and the _pinhole diameter_. That "
+"means we have enough information to also calculate the _f-number_."
+msgstr ""
+"На этом этапе нам известны _фокусное расстояние_ и _диаметр отверстия_. Это "
+"означает, что у нас достаточно информации для расчёта _диафрагменного числа_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3138
+msgid ""
+"We will display the f-number, rounded to four significant digits. Chances "
+"are the f-number will tell us very little. To make it more meaningful, we "
+"can find the nearest _normalized f-number_, i.e., the nearest power of the "
+"square root of 2."
+msgstr ""
+"Мы отобразим число f, округлённое до четырёх значащих цифр. Скорее всего, "
+"само число f мало что нам скажет. Чтобы придать ему больше смысла, мы можем "
+"найти ближайшее _нормализованное число f_, то есть ближайшую степень "
+"квадратного корня из 2."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3144
+msgid ""
+"We do that by multiplying the actual f-number by itself, which, of course, "
+"will give us its `square`. We will then calculate its base-2 logarithm, "
+"which is much easier to do than calculating the base-square-root-of-2 "
+"logarithm! We will round the result to the nearest integer. Next, we will "
+"raise 2 to the result. Actually, the FPU gives us a good shortcut to do "
+"that: We can use the `fscale` op code to \"scale\" 1, which is analogous to "
+"``shift``ing an integer left. Finally, we calculate the square root of it "
+"all, and we have the nearest normalized f-number."
+msgstr ""
+"Мы делаем это, умножая фактическое значение диафрагмы на само себя, что, "
+"конечно же, даст нам его `квадрат`. Затем мы вычислим его логарифм по "
+"основанию 2, что намного проще, чем вычисление логарифма по основанию "
+"квадратного корня из 2! Мы округлим результат до ближайшего целого числа. "
+"Далее мы возведём 2 в полученную степень. На самом деле, FPU предоставляет "
+"нам удобный способ сделать это: мы можем использовать код операции `fscale` "
+"для \"масштабирования\" 1, что аналогично ``сдвигу`` целого числа влево. "
+"Наконец, мы вычисляем квадратный корень из всего этого и получаем ближайшее "
+"нормализованное значение диафрагмы."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3147
+msgid ""
+"If all that sounds overwhelming-or too much work, perhaps-it may become much "
+"clearer if you see the code. It takes 9 op codes altogether:"
+msgstr ""
+"Если всё это звучит ошеломляюще — или, возможно, слишком сложно — всё может "
+"стать гораздо понятнее, если увидеть код. Вместе это занимает всего 9 "
+"инструкций процессора:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3159
+#, no-wrap
+msgid ""
+"fmul\tst0, st0\n"
+"\tfld1\n"
+"\tfld\tst1\n"
+"\tfyl2x\n"
+"\tfrndint\n"
+"\tfld1\n"
+"\tfscale\n"
+"\tfsqrt\n"
+"\tfstp\tst1\n"
+msgstr ""
+"fmul\tst0, st0\n"
+"\tfld1\n"
+"\tfld\tst1\n"
+"\tfyl2x\n"
+"\tfrndint\n"
+"\tfld1\n"
+"\tfscale\n"
+"\tfsqrt\n"
+"\tfstp\tst1\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3163
+msgid ""
+"The first line, `fmul st0, st0`, squares the contents of the TOS (top of the "
+"stack, same as `st`, called `st0` by nasm). The `fld1` pushes `1` on the "
+"TOS."
+msgstr ""
+"Первая строка, `fmul st0, st0`, возводит в квадрат содержимое TOS (вершина "
+"стека, то же что `st`, называется `st0` в nasm). Команда `fld1` помещает `1` "
+"на вершину стека."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3167
+msgid ""
+"The next line, `fld st1`, pushes the square back to the TOS. At this point "
+"the square is both in `st` and `st(2)` (it will become clear why we leave a "
+"second copy on the stack in a moment). `st(1)` contains `1`."
+msgstr ""
+"Следующая строка, `fld st1`, помещает квадрат обратно в TOS. На этом этапе "
+"квадрат находится и в `st`, и в `st(2)` (скоро станет ясно, зачем мы "
+"оставляем вторую копию в стеке). В `st(1)` содержится `1`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3170
+msgid ""
+"Next, `fyl2x` calculates base-2 logarithm of `st` multiplied by `st(1)`. "
+"That is why we placed `1` on `st(1)` before."
+msgstr ""
+"Далее, `fyl2x` вычисляет логарифм по основанию 2 от `st`, умноженный на "
+"`st(1)`. Именно поэтому мы ранее поместили `1` в `st(1)`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3172
+msgid ""
+"At this point, `st` contains the logarithm we have just calculated, `st(1)` "
+"contains the square of the actual f-number we saved for later."
+msgstr ""
+"На этом этапе `st` содержит логарифм, который мы только что вычислили, а "
+"`st(1)` содержит квадрат фактического значения диафрагменного числа, который "
+"мы сохранили для последующего использования."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3176
+msgid ""
+"`frndint` rounds the TOS to the nearest integer. `fld1` pushes a `1`. "
+"`fscale` shifts the `1` we have on the TOS by the value in `st(1)`, "
+"effectively raising 2 to `st(1)`."
+msgstr ""
+"`frndint` округляет TOS до ближайшего целого числа. `fld1` помещает `1` в "
+"стек. `fscale` сдвигает `1`, находящееся на TOS, на значение в `st(1)`, "
+"фактически возводя 2 в степень `st(1)`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3178
+msgid ""
+"Finally, `fsqrt` calculates the square root of the result, i.e., the nearest "
+"normalized f-number."
+msgstr ""
+"Наконец, `fsqrt` вычисляет квадратный корень из результата, т.е. ближайшее "
+"нормализованное число f."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3181
+msgid ""
+"We now have the nearest normalized f-number on the TOS, the base-2 logarithm "
+"rounded to the nearest integer in `st(1)`, and the square of the actual f-"
+"number in `st(2)`. We are saving the value in `st(2)` for later."
+msgstr ""
+"У нас теперь есть ближайшее нормализованное число f на вершине стека (TOS), "
+"округлённый до ближайшего целого двоичный логарифм в `st(1)` и квадрат "
+"фактического число f в `st(2)`. Мы сохраняем значение в `st(2)` для "
+"последующего использования."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3187
+msgid ""
+"But we do not need the contents of `st(1)` anymore. The last line, `fstp "
+"st1`, places the contents of `st` to `st(1)`, and pops. As a result, what "
+"was `st(1)` is now `st`, what was `st(2)` is now `st(1)`, etc. The new `st` "
+"contains the normalized f-number. The new `st(1)` contains the square of "
+"the actual f-number we have stored there for posterity."
+msgstr ""
+"Но нам больше не нужно содержимое `st(1)`. Последняя строка, `fstp st1`, "
+"помещает содержимое `st` в `st(1)` и выполняет извлечение. В результате, то, "
+"что было `st(1)`, теперь становится `st`, то, что было `st(2)`, теперь "
+"становится `st(1)`, и так далее. Новый `st` содержит нормализованное число "
+"f. Новый `st(1)` содержит квадрат фактического число f, который мы сохранили "
+"для потомков."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3190
+msgid ""
+"At this point, we are ready to output the normalized f-number. Because it "
+"is normalized, we will not round it off to four significant digits, but will "
+"send it out in its full precision."
+msgstr ""
+"На этом этапе мы готовы вывести нормализованное число f. Поскольку оно "
+"нормализовано, мы не будем округлять его до четырёх значащих цифр, а "
+"отправим его с полной точностью."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3193
+msgid ""
+"The normalized f-number is useful as long as it is reasonably small and can "
+"be found on our light meter. Otherwise we need a different method of "
+"determining proper exposure."
+msgstr ""
+"Нормализованное диафрагменное число полезно, пока оно достаточно мало и "
+"может быть найдено на нашем экспонометре. В противном случае нам нужен "
+"другой метод определения правильной экспозиции."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3195
+msgid ""
+"Earlier we have figured out the formula of calculating proper exposure at an "
+"arbitrary f-number from that measured at a different f-number."
+msgstr ""
+"Ранее мы вывели формулу для расчёта правильной экспозиции при произвольной "
+"диафрагме на основе измерений, сделанных при другой диафрагме."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3198
+msgid ""
+"Every light meter I have ever seen can determine proper exposure at f5.6. "
+"We will, therefore, calculate an _\"f5.6 multiplier,\"_ i.e., by how much we "
+"need to multiply the exposure measured at f5.6 to determine the proper "
+"exposure for our pinhole camera."
+msgstr ""
+"Каждый экспонометр, который я когда-либо видел, может определить правильную "
+"экспозицию при f5.6. Поэтому мы рассчитаем _\"множитель f5.6\"_, то есть "
+"насколько нужно умножить экспозицию, измеренную при f5.6, чтобы определить "
+"правильную экспозицию для нашей камеры-обскуры."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3200
+msgid ""
+"From the above formula we know this factor can be calculated by dividing our "
+"f-number (the actual one, not the normalized one) by `5.6`, and squaring the "
+"result."
+msgstr ""
+"Из приведённой формулы мы знаем, что этот коэффициент можно вычислить, "
+"разделив наше число f (фактическое, а не нормализованное) на `5.6` и возведя "
+"результат в квадрат."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3202
+msgid ""
+"Mathematically, dividing the square of our f-number by the square of `5.6` "
+"will give us the same result."
+msgstr ""
+"Математически, деление квадрата нашего числа f на квадрат `5.6` даст нам тот "
+"же результат."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3205
+msgid ""
+"Computationally, we do not want to square two numbers when we can only "
+"square one. So, the first solution seems better at first."
+msgstr ""
+"С вычислительной точки зрения, нам не нужно возводить в квадрат два числа, "
+"когда можно возвести только одно. Таким образом, первое решение на первый "
+"взгляд кажется лучше."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3213
+msgid ""
+"`5.6` is a _constant_. We do not have to have our FPU waste precious "
+"cycles. We can just tell it to divide the square of the f-number by "
+"whatever `5.6²` equals to. Or we can divide the f-number by `5.6`, and then "
+"square the result. The two ways now seem equal."
+msgstr ""
+"`5.6` — это _константа_. Нам не нужно заставлять наш FPU тратить драгоценные "
+"циклы. Мы можем просто указать ему разделить квадрат f-числа на то, чему "
+"равно `5.6²`. Или мы можем разделить f-число на `5.6`, а затем возвести "
+"результат в квадрат. Теперь оба способа кажутся равнозначными."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3215
+msgid "But, they are not!"
+msgstr "Но они не такие!"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3219
+msgid ""
+"Having studied the principles of photography above, we remember that the "
+"`5.6` is actually square root of 2 raised to the fifth power. An "
+"_irrational_ number. The square of this number is _exactly_ `32`."
+msgstr ""
+"Изучив принципы фотографии выше, мы помним, что `5.6` — это квадратный "
+"корень из 2, возведённый в пятую степень. Это _иррациональное_ число. "
+"Квадрат этого числа _ровно_ `32`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3225
+msgid ""
+"Not only is `32` an integer, it is a power of 2. We do not need to divide "
+"the square of the f-number by `32`. We only need to use `fscale` to shift "
+"it right by five positions. In the FPU lingo it means we will `fscale` it "
+"with `st(1)` equal to `-5`. That is _much faster_ than a division."
+msgstr ""
+"`32` — это не просто целое число, это степень двойки. Нам не нужно делить "
+"квадрат числа f на `32`. Достаточно использовать `fscale` для сдвига вправо "
+"на пять позиций. В терминологии FPU это означает, что мы применим `fscale` "
+"со значением `st(1)` равным `-5`. Это _гораздо быстрее_, чем деление."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3228
+msgid ""
+"So, now it has become clear why we have saved the square of the f-number on "
+"the top of the FPU stack. The calculation of the f5.6 multiplier is the "
+"easiest calculation of this entire program! We will output it rounded to "
+"four significant digits."
+msgstr ""
+"Итак, теперь стало ясно, зачем мы сохранили квадрат числа f на вершине стека "
+"FPU. Расчёт множителя для f5.6 — это самое простое вычисление во всей "
+"программе! Мы выведем его, округлив до четырёх значащих цифр."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3231
+msgid ""
+"There is one more useful number we can calculate: The number of stops our f-"
+"number is from f5.6. This may help us if our f-number is just outside the "
+"range of our light meter, but we have a shutter which lets us set various "
+"speeds, and this shutter uses stops."
+msgstr ""
+"Есть ещё одно полезное число, которое мы можем вычислить: количество "
+"ступеней, на которые наше значение диафрагмы отличается от f5.6. Это может "
+"помочь, если наше значение диафрагмы находится чуть за пределами диапазона "
+"нашего экспонометра, но у нас есть затвор, который позволяет устанавливать "
+"различные выдержки, и этот затвор использует ступени."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3234
+msgid ""
+"Say, our f-number is 5 stops from f5.6, and the light meter says we should "
+"use 1/1000 sec. Then we can set our shutter speed to 1/1000 first, then "
+"move the dial by 5 stops."
+msgstr ""
+"Предположим, наше число диафрагмы на 5 ступеней отличается от f5.6, а "
+"экспонометр показывает, что нужно использовать выдержку 1/1000 сек. Тогда мы "
+"можем сначала установить выдержку на 1/1000, а затем повернуть диск на 5 "
+"ступеней."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3239
+msgid ""
+"This calculation is quite easy as well. All we have to do is to calculate "
+"the base-2 logarithm of the f5.6 multiplier we had just calculated (though "
+"we need its value from before we rounded it off). We then output the result "
+"rounded to the nearest integer. We do not need to worry about having more "
+"than four significant digits in this one: The result is most likely to have "
+"only one or two digits anyway."
+msgstr ""
+"Этот расчет также довольно прост. Все, что нам нужно сделать, это вычислить "
+"логарифм по основанию 2 от множителя f5.6, который мы только что рассчитали "
+"(хотя нам нужно его значение до округления). Затем мы выводим результат, "
+"округленный до ближайшего целого числа. Нам не нужно беспокоиться о наличии "
+"более четырех значащих цифр в этом случае: скорее всего, результат будет "
+"содержать только одну или две цифры."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3241
+#, no-wrap
+msgid "FPU Optimizations"
+msgstr "Оптимизации FPU"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3244
+msgid ""
+"In assembly language we can optimize the FPU code in ways impossible in high "
+"languages, including C."
+msgstr ""
+"В ассемблерном коде мы можем оптимизировать инструкции FPU способами, "
+"невозможными в языках высокого уровня, включая C."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3248
+msgid ""
+"Whenever a C function needs to calculate a floating-point value, it loads "
+"all necessary variables and constants into FPU registers. It then does "
+"whatever calculation is required to get the correct result. Good C "
+"compilers can optimize that part of the code really well."
+msgstr ""
+"Всякий раз, когда функции на языке C требуется вычислить значение с "
+"плавающей запятой, она загружает все необходимые переменные и константы в "
+"регистры FPU. Затем выполняются все необходимые вычисления для получения "
+"правильного результата. Хорошие компиляторы C могут очень эффективно "
+"оптимизировать эту часть кода."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3252
+msgid ""
+"It \"returns\" the value by leaving the result on the TOS. However, before "
+"it returns, it cleans up. Any variables and constants it used in its "
+"calculation are now gone from the FPU."
+msgstr ""
+"Он \"возвращает\" значение, оставляя результат на вершине стека (TOS). "
+"Однако перед возвратом он выполняет очистку. Все переменные и константы, "
+"использованные в вычислениях, теперь удалены из FPU."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3254
+msgid ""
+"It cannot do what we just did above: We calculated the square of the f-"
+"number and kept it on the stack for later use by another function."
+msgstr ""
+"Он не может сделать то, что мы только что сделали выше: мы вычислили квадрат "
+"числа f и оставили его в стеке для последующего использования другой "
+"функцией."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3257
+msgid ""
+"We _knew_ we would need that value later on. We also knew we had enough "
+"room on the stack (which only has room for 8 numbers) to store it there."
+msgstr ""
+"Мы _знали_, что это значение понадобится позже. Мы также знали, что у нас "
+"достаточно места в стеке (в котором помещается только 8 чисел), чтобы "
+"сохранить его там."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3259
+msgid ""
+"A C compiler has no way of knowing that a value it has on the stack will be "
+"required again in the very near future."
+msgstr ""
+"Компилятор C не может знать, что значение, находящееся в стеке, потребуется "
+"снова в ближайшем будущем."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3262
+msgid ""
+"Of course, the C programmer may know it. But the only recourse he has is to "
+"store the value in a memory variable."
+msgstr ""
+"Конечно, программист на C может это знать. Но единственное средство, которое "
+"у него есть, — это сохранить значение в переменной памяти."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3264
+msgid ""
+"That means, for one, the value will be changed from the 80-bit precision "
+"used internally by the FPU to a C _double_ (64 bits) or even _single_ (32 "
+"bits)."
+msgstr ""
+"Это означает, что значение будет изменено с 80-битной точности, используемой "
+"внутри FPU, на тип _double_ (64 бита) или даже _single_ (32 бита) в C."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3267
+msgid ""
+"That also means that the value must be moved from the TOS into the memory, "
+"and then back again. Alas, of all FPU operations, the ones that access the "
+"computer memory are the slowest."
+msgstr ""
+"Это также означает, что значение должно быть перемещено из TOS в память, а "
+"затем обратно. Увы, среди всех операций с FPU, доступ к памяти компьютера "
+"является самым медленным."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3269
+msgid ""
+"So, whenever programming the FPU in assembly language, look for the ways of "
+"keeping intermediate results on the FPU stack."
+msgstr ""
+"Итак, при программировании FPU на языке ассемблера ищите способы хранения "
+"промежуточных результатов в стеке FPU."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3271
+msgid ""
+"We can take that idea even further! In our program we are using a _constant_ "
+"(the one we named `PC`)."
+msgstr ""
+"Мы можем развить эту идею еще дальше! В нашей программе мы используем "
+"_константу_ (ту, которую назвали `PC`)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3274
+msgid ""
+"It does not matter how many pinhole diameters we are calculating: 1, 10, 20, "
+"1000, we are always using the same constant. Therefore, we can optimize our "
+"program by keeping the constant on the stack all the time."
+msgstr ""
+"Не имеет значения, сколько диаметров отверстий мы рассчитываем: 1, 10, 20, "
+"1000, мы всегда используем одну и ту же константу. Следовательно, мы можем "
+"оптимизировать нашу программу, сохраняя константу в стеке всё время."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3277
+msgid ""
+"Early on in our program, we are calculating the value of the above "
+"constant. We need to divide our input by `10` for every digit in the "
+"constant."
+msgstr ""
+"В начале нашей программы мы вычисляем значение указанной константы. Нам "
+"нужно разделить наш вход на `10` для каждой цифры в константе."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3280
+msgid ""
+"It is much faster to multiply than to divide. So, at the start of our "
+"program, we divide `10` into `1` to obtain `0.1`, which we then keep on the "
+"stack: Instead of dividing the input by `10` for every digit, we multiply it "
+"by `0.1`."
+msgstr ""
+"Гораздо быстрее умножать, чем делить. Поэтому в начале нашей программы мы "
+"делим `1` на `10`, чтобы получить `0.1`, который затем сохраняем в стеке: "
+"вместо того чтобы делить ввод на `10` для каждой цифры, мы умножаем его на "
+"`0.1`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3284
+msgid ""
+"By the way, we do not input `0.1` directly, even though we could. We have a "
+"reason for that: While `0.1` can be expressed with just one decimal place, "
+"we do not know how many _binary_ places it takes. We, therefore, let the "
+"FPU calculate its binary value to its own high precision."
+msgstr ""
+"Кстати, мы не вводим `0.1` напрямую, хотя могли бы. У нас есть причина для "
+"этого: хотя `0.1` можно выразить всего одним десятичным знаком, мы не знаем, "
+"сколько _двоичных_ разрядов для этого потребуется. Поэтому мы позволяем FPU "
+"вычислить его двоичное значение с собственной высокой точностью."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3289
+msgid ""
+"We are using other constants: We multiply the pinhole diameter by `1000` to "
+"convert it from millimeters to microns. We compare numbers to `10000` when "
+"we are rounding them off to four significant digits. So, we keep both, "
+"`1000` and `10000`, on the stack. And, of course, we reuse the `0.1` when "
+"rounding off numbers to four digits."
+msgstr ""
+"Мы используем другие константы: умножаем диаметр отверстия на `1000`, чтобы "
+"перевести его из миллиметров в микроны. Мы сравниваем числа с `10000`, когда "
+"округляем их до четырёх значащих цифр. Таким образом, мы оставляем и `1000`, "
+"и `10000` в стеке. И, конечно же, мы повторно используем `0.1` при "
+"округлении чисел до четырёх цифр."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3295
+msgid ""
+"Last but not least, we keep `-5` on the stack. We need it to scale the "
+"square of the f-number, instead of dividing it by `32`. It is not by "
+"coincidence we load this constant last. That makes it the top of the stack "
+"when only the constants are on it. So, when the square of the f-number is "
+"being scaled, the `-5` is at `st(1)`, precisely where `fscale` expects it to "
+"be."
+msgstr ""
+"И последнее, но не менее важное: мы оставляем `-5` в стеке. Он нам нужен для "
+"масштабирования квадрата числа f вместо деления его на `32`. Не случайно мы "
+"загружаем эту константу последней. Это делает её вершиной стека, когда в нём "
+"находятся только константы. Таким образом, при масштабировании квадрата "
+"число f `-5` находится в `st(1)`, именно там, где `fscale` ожидает его "
+"увидеть."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3298
+msgid ""
+"It is common to create certain constants from scratch instead of loading "
+"them from the memory. That is what we are doing with `-5`:"
+msgstr ""
+"Это обычная ситуация, когда некоторые константы создаются с нуля, вместо "
+"загрузки их из памяти. Именно это мы делаем с `-5`:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3307
+#, no-wrap
+msgid ""
+"\tfld1\t\t\t; TOS = 1\n"
+"\tfadd\tst0, st0\t; TOS = 2\n"
+"\tfadd\tst0, st0\t; TOS = 4\n"
+"\tfld1\t\t\t; TOS = 1\n"
+"\tfaddp\tst1, st0\t; TOS = 5\n"
+"\tfchs\t\t\t; TOS = -5\n"
+msgstr ""
+"\tfld1\t\t\t; TOS = 1\n"
+"\tfadd\tst0, st0\t; TOS = 2\n"
+"\tfadd\tst0, st0\t; TOS = 4\n"
+"\tfld1\t\t\t; TOS = 1\n"
+"\tfaddp\tst1, st0\t; TOS = 5\n"
+"\tfchs\t\t\t; TOS = -5\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3310
+msgid ""
+"We can generalize all these optimizations into one rule: _Keep repeat values "
+"on the stack!_"
+msgstr ""
+"Мы можем обобщить все эти оптимизации в одном правиле: _Держите "
+"повторяющиеся значения в стеке!_"
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3315
+msgid ""
+"_PostScript(R)_ is a stack-oriented programming language. There are many "
+"more books available about PostScript(R) than about the FPU assembly "
+"language: Mastering PostScript(R) will help you master the FPU."
+msgstr ""
+"_PostScript(R)_ — это стековая язык программирования. Существует гораздо "
+"больше книг о PostScript(R), чем о языке ассемблера FPU: освоение "
+"PostScript(R) поможет вам овладеть FPU."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3318
+#, no-wrap
+msgid "pinhole-The Code"
+msgstr "Код pinhole"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3333
+#, no-wrap
+msgid ""
+";;;;;;; pinhole.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+";\n"
+"; Find various parameters of a pinhole camera construction and use\n"
+";\n"
+"; Started:\t 9-Jun-2001\n"
+"; Updated:\t10-Jun-2001\n"
+";\n"
+"; Copyright (c) 2001 G. Adam Stanislav\n"
+"; All rights reserved.\n"
+";\n"
+";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+msgstr ""
+";;;;;;; pinhole.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+";\n"
+"; Find various parameters of a pinhole camera construction and use\n"
+";\n"
+"; Started:\t 9-Jun-2001\n"
+"; Updated:\t10-Jun-2001\n"
+";\n"
+"; Copyright (c) 2001 G. Adam Stanislav\n"
+"; All rights reserved.\n"
+";\n"
+";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3368
+#, no-wrap
+msgid ""
+"section\t.data\n"
+"align 4\n"
+"ten\tdd\t10\n"
+"thousand\tdd\t1000\n"
+"tthou\tdd\t10000\n"
+"fd.in\tdd\tstdin\n"
+"fd.out\tdd\tstdout\n"
+"envar\tdb\t'PINHOLE='\t; Exactly 8 bytes, or 2 dwords long\n"
+"pinhole\tdb\t'04,', \t\t; Bender's constant (0.04)\n"
+"connors\tdb\t'037', 0Ah\t; Connors' constant\n"
+"usg\tdb\t'Usage: pinhole [-b] [-c] [-e] [-p <value>] [-o <outfile>] [-i <infile>]', 0Ah\n"
+"usglen\tequ\t$-usg\n"
+"iemsg\tdb\t\"pinhole: Can't open input file\", 0Ah\n"
+"iemlen\tequ\t$-iemsg\n"
+"oemsg\tdb\t\"pinhole: Can't create output file\", 0Ah\n"
+"oemlen\tequ\t$-oemsg\n"
+"pinmsg\tdb\t\"pinhole: The PINHOLE constant must not be 0\", 0Ah\n"
+"pinlen\tequ\t$-pinmsg\n"
+"toobig\tdb\t\"pinhole: The PINHOLE constant may not exceed 18 decimal places\", 0Ah\n"
+"biglen\tequ\t$-toobig\n"
+"huhmsg\tdb\t9, '???'\n"
+"separ\tdb\t9, '???'\n"
+"sep2\tdb\t9, '???'\n"
+"sep3\tdb\t9, '???'\n"
+"sep4\tdb\t9, '???', 0Ah\n"
+"huhlen\tequ\t$-huhmsg\n"
+"header\tdb\t'focal length in millimeters,pinhole diameter in microns,'\n"
+"\tdb\t'F-number,normalized F-number,F-5.6 multiplier,stops '\n"
+"\tdb\t'from F-5.6', 0Ah\n"
+"headlen\tequ\t$-header\n"
+msgstr ""
+"section\t.data\n"
+"align 4\n"
+"ten\tdd\t10\n"
+"thousand\tdd\t1000\n"
+"tthou\tdd\t10000\n"
+"fd.in\tdd\tstdin\n"
+"fd.out\tdd\tstdout\n"
+"envar\tdb\t'PINHOLE='\t; Exactly 8 bytes, or 2 dwords long\n"
+"pinhole\tdb\t'04,', \t\t; Bender's constant (0.04)\n"
+"connors\tdb\t'037', 0Ah\t; Connors' constant\n"
+"usg\tdb\t'Usage: pinhole [-b] [-c] [-e] [-p <value>] [-o <outfile>] [-i <infile>]', 0Ah\n"
+"usglen\tequ\t$-usg\n"
+"iemsg\tdb\t\"pinhole: Can't open input file\", 0Ah\n"
+"iemlen\tequ\t$-iemsg\n"
+"oemsg\tdb\t\"pinhole: Can't create output file\", 0Ah\n"
+"oemlen\tequ\t$-oemsg\n"
+"pinmsg\tdb\t\"pinhole: The PINHOLE constant must not be 0\", 0Ah\n"
+"pinlen\tequ\t$-pinmsg\n"
+"toobig\tdb\t\"pinhole: The PINHOLE constant may not exceed 18 decimal places\", 0Ah\n"
+"biglen\tequ\t$-toobig\n"
+"huhmsg\tdb\t9, '???'\n"
+"separ\tdb\t9, '???'\n"
+"sep2\tdb\t9, '???'\n"
+"sep3\tdb\t9, '???'\n"
+"sep4\tdb\t9, '???', 0Ah\n"
+"huhlen\tequ\t$-huhmsg\n"
+"header\tdb\t'focal length in millimeters,pinhole diameter in microns,'\n"
+"\tdb\t'F-number,normalized F-number,F-5.6 multiplier,stops '\n"
+"\tdb\t'from F-5.6', 0Ah\n"
+"headlen\tequ\t$-header\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3374
+#, no-wrap
+msgid ""
+"section .bss\n"
+"ibuffer\tresb\tBUFSIZE\n"
+"obuffer\tresb\tBUFSIZE\n"
+"dbuffer\tresb\t20\t\t; decimal input buffer\n"
+"bbuffer\tresb\t10\t\t; BCD buffer\n"
+msgstr ""
+"section .bss\n"
+"ibuffer\tresb\tBUFSIZE\n"
+"obuffer\tresb\tBUFSIZE\n"
+"dbuffer\tresb\t20\t\t; decimal input buffer\n"
+"bbuffer\tresb\t10\t\t; BCD buffer\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3385
+#, no-wrap
+msgid ""
+"section\t.text\n"
+"align 4\n"
+"huh:\n"
+"\tcall\twrite\n"
+"\tpush\tdword huhlen\n"
+"\tpush\tdword huhmsg\n"
+"\tpush\tdword [fd.out]\n"
+"\tsys.write\n"
+"\tadd\tesp, byte 12\n"
+"\tret\n"
+msgstr ""
+"section\t.text\n"
+"align 4\n"
+"huh:\n"
+"\tcall\twrite\n"
+"\tpush\tdword huhlen\n"
+"\tpush\tdword huhmsg\n"
+"\tpush\tdword [fd.out]\n"
+"\tsys.write\n"
+"\tadd\tesp, byte 12\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3394
+#, no-wrap
+msgid ""
+"align 4\n"
+"perr:\n"
+"\tpush\tdword pinlen\n"
+"\tpush\tdword pinmsg\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+"\tpush\tdword 4\t\t; return failure\n"
+"\tsys.exit\n"
+msgstr ""
+"align 4\n"
+"perr:\n"
+"\tpush\tdword pinlen\n"
+"\tpush\tdword pinmsg\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+"\tpush\tdword 4\t\t; return failure\n"
+"\tsys.exit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3403
+#, no-wrap
+msgid ""
+"align 4\n"
+"consttoobig:\n"
+"\tpush\tdword biglen\n"
+"\tpush\tdword toobig\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+"\tpush\tdword 5\t\t; return failure\n"
+"\tsys.exit\n"
+msgstr ""
+"align 4\n"
+"consttoobig:\n"
+"\tpush\tdword biglen\n"
+"\tpush\tdword toobig\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+"\tpush\tdword 5\t\t; return failure\n"
+"\tsys.exit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3412
+#, no-wrap
+msgid ""
+"align 4\n"
+"ierr:\n"
+"\tpush\tdword iemlen\n"
+"\tpush\tdword iemsg\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+"\tpush\tdword 1\t\t; return failure\n"
+"\tsys.exit\n"
+msgstr ""
+"align 4\n"
+"ierr:\n"
+"\tpush\tdword iemlen\n"
+"\tpush\tdword iemsg\n"
+"\tpush\tdword stderr\n"
+"\tsys.write\n"
+"\tpush\tdword 1\t\t; return failure\n"
+"\tsys.exit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3436
+#, no-wrap
+msgid ""
+"align 4\n"
+"global\t_start\n"
+"_start:\n"
+"\tadd\tesp, byte 8\t; discard argc and argv[0]\n"
+"\tsub\tesi, esi\n"
+msgstr ""
+"align 4\n"
+"global\t_start\n"
+"_start:\n"
+"\tadd\tesp, byte 8\t; discard argc and argv[0]\n"
+"\tsub\tesi, esi\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3441
+#, no-wrap
+msgid ""
+".arg:\n"
+"\tpop\tecx\n"
+"\tor\tecx, ecx\n"
+"\tje\tnear .getenv\t\t; no more arguments\n"
+msgstr ""
+".arg:\n"
+"\tpop\tecx\n"
+"\tor\tecx, ecx\n"
+"\tje\tnear .getenv\t\t; no more arguments\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3449
+#, no-wrap
+msgid ""
+"\tinc\tecx\n"
+"\tmov\tax, [ecx]\n"
+"\tinc\tecx\n"
+msgstr ""
+"\tinc\tecx\n"
+"\tmov\tax, [ecx]\n"
+"\tinc\tecx\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3467
+#, no-wrap
+msgid ""
+"\tor\tah, ah\n"
+"\tjne\t.openoutput\n"
+"\tpop\tecx\n"
+"\tjecxz\tusage\n"
+msgstr ""
+"\tor\tah, ah\n"
+"\tjne\t.openoutput\n"
+"\tpop\tecx\n"
+"\tjecxz\tusage\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3494
+#, no-wrap
+msgid ""
+"\t; Find the path to the input file\n"
+"\tor\tah, ah\n"
+"\tjne\t.openinput\n"
+"\tpop\tecx\n"
+"\tor\tecx, ecx\n"
+"\tje near usage\n"
+msgstr ""
+"\t; Find the path to the input file\n"
+"\tor\tah, ah\n"
+"\tjne\t.openinput\n"
+"\tpop\tecx\n"
+"\tor\tecx, ecx\n"
+"\tje near usage\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3510
+#, no-wrap
+msgid ""
+".p:\n"
+"\tcmp\tal, 'p'\n"
+"\tjne\t.c\n"
+"\tor\tah, ah\n"
+"\tjne\t.pcheck\n"
+msgstr ""
+".p:\n"
+"\tcmp\tal, 'p'\n"
+"\tjne\t.c\n"
+"\tor\tah, ah\n"
+"\tjne\t.pcheck\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3514
+#, no-wrap
+msgid ""
+"\tpop\tecx\n"
+"\tor\tecx, ecx\n"
+"\tje\tnear usage\n"
+msgstr ""
+"\tpop\tecx\n"
+"\tor\tecx, ecx\n"
+"\tje\tnear usage\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3516
+#, no-wrap
+msgid "\tmov\tah, [ecx]\n"
+msgstr "\tmov\tah, [ecx]\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3524
+#, no-wrap
+msgid ""
+".pcheck:\n"
+"\tcmp\tah, '0'\n"
+"\tjl\tnear usage\n"
+"\tcmp\tah, '9'\n"
+"\tja\tnear usage\n"
+"\tmov\tesi, ecx\n"
+"\tjmp\t.arg\n"
+msgstr ""
+".pcheck:\n"
+"\tcmp\tah, '0'\n"
+"\tjl\tnear usage\n"
+"\tcmp\tah, '9'\n"
+"\tja\tnear usage\n"
+"\tmov\tesi, ecx\n"
+"\tjmp\t.arg\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3532
+#, no-wrap
+msgid ""
+".c:\n"
+"\tcmp\tal, 'c'\n"
+"\tjne\t.b\n"
+"\tor\tah, ah\n"
+"\tjne\tnear usage\n"
+"\tmov\tesi, connors\n"
+"\tjmp\t.arg\n"
+msgstr ""
+".c:\n"
+"\tcmp\tal, 'c'\n"
+"\tjne\t.b\n"
+"\tor\tah, ah\n"
+"\tjne\tnear usage\n"
+"\tmov\tesi, connors\n"
+"\tjmp\t.arg\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3540
+#, no-wrap
+msgid ""
+".b:\n"
+"\tcmp\tal, 'b'\n"
+"\tjne\t.e\n"
+"\tor\tah, ah\n"
+"\tjne\tnear usage\n"
+"\tmov\tesi, pinhole\n"
+"\tjmp\t.arg\n"
+msgstr ""
+".b:\n"
+"\tcmp\tal, 'b'\n"
+"\tjne\t.e\n"
+"\tor\tah, ah\n"
+"\tjne\tnear usage\n"
+"\tmov\tesi, pinhole\n"
+"\tjmp\t.arg\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3553
+#, no-wrap
+msgid ""
+".e:\n"
+"\tcmp\tal, 'e'\n"
+"\tjne\tnear usage\n"
+"\tor\tah, ah\n"
+"\tjne\tnear usage\n"
+"\tmov\tal, ','\n"
+"\tmov\t[huhmsg], al\n"
+"\tmov\t[separ], al\n"
+"\tmov\t[sep2], al\n"
+"\tmov\t[sep3], al\n"
+"\tmov\t[sep4], al\n"
+"\tjmp\t.arg\n"
+msgstr ""
+".e:\n"
+"\tcmp\tal, 'e'\n"
+"\tjne\tnear usage\n"
+"\tor\tah, ah\n"
+"\tjne\tnear usage\n"
+"\tmov\tal, ','\n"
+"\tmov\t[huhmsg], al\n"
+"\tmov\t[separ], al\n"
+"\tmov\t[sep2], al\n"
+"\tmov\t[sep3], al\n"
+"\tmov\t[sep4], al\n"
+"\tjmp\t.arg\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3560
+#, no-wrap
+msgid ""
+"align 4\n"
+".getenv:\n"
+"\t; If ESI = 0, we did not have a -p argument,\n"
+"\t; and need to check the environment for \"PINHOLE=\"\n"
+"\tor\tesi, esi\n"
+"\tjne\t.init\n"
+msgstr ""
+"align 4\n"
+".getenv:\n"
+"\t; If ESI = 0, we did not have a -p argument,\n"
+"\t; and need to check the environment for \"PINHOLE=\"\n"
+"\tor\tesi, esi\n"
+"\tjne\t.init\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3562
+#, no-wrap
+msgid "\tsub\tecx, ecx\n"
+msgstr "\tsub\tecx, ecx\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3567
+#, no-wrap
+msgid ""
+".nextenv:\n"
+"\tpop\tesi\n"
+"\tor\tesi, esi\n"
+"\tje\t.default\t; no PINHOLE envar found\n"
+msgstr ""
+".nextenv:\n"
+"\tpop\tesi\n"
+"\tor\tesi, esi\n"
+"\tje\t.default\t; no PINHOLE envar found\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3573
+#, no-wrap
+msgid ""
+"\t; check if this envar starts with 'PINHOLE='\n"
+"\tmov\tedi, envar\n"
+"\tmov\tcl, 2\t\t; 'PINHOLE=' is 2 dwords long\n"
+"rep\tcmpsd\n"
+"\tjne\t.nextenv\n"
+msgstr ""
+"\t; check if this envar starts with 'PINHOLE='\n"
+"\tmov\tedi, envar\n"
+"\tmov\tcl, 2\t\t; 'PINHOLE=' is 2 dwords long\n"
+"rep\tcmpsd\n"
+"\tjne\t.nextenv\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3581
+#, no-wrap
+msgid ""
+"\t; Check if it is followed by a digit\n"
+"\tmov\tal, [esi]\n"
+"\tcmp\tal, '0'\n"
+"\tjl\t.default\n"
+"\tcmp\tal, '9'\n"
+"\tjbe\t.init\n"
+"\t; fall through\n"
+msgstr ""
+"\t; Check if it is followed by a digit\n"
+"\tmov\tal, [esi]\n"
+"\tcmp\tal, '0'\n"
+"\tjl\t.default\n"
+"\tcmp\tal, '9'\n"
+"\tjbe\t.init\n"
+"\t; fall through\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3588
+#, no-wrap
+msgid ""
+"align 4\n"
+".default:\n"
+"\t; We got here because we had no -p argument,\n"
+"\t; and did not find the PINHOLE envar.\n"
+"\tmov\tesi, pinhole\n"
+"\t; fall through\n"
+msgstr ""
+"align 4\n"
+".default:\n"
+"\t; We got here because we had no -p argument,\n"
+"\t; and did not find the PINHOLE envar.\n"
+"\tmov\tesi, pinhole\n"
+"\t; fall through\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3597
+#, no-wrap
+msgid ""
+"align 4\n"
+".init:\n"
+"\tsub\teax, eax\n"
+"\tsub\tebx, ebx\n"
+"\tsub\tecx, ecx\n"
+"\tsub\tedx, edx\n"
+"\tmov\tedi, dbuffer+1\n"
+"\tmov\tbyte [dbuffer], '0'\n"
+msgstr ""
+"align 4\n"
+".init:\n"
+"\tsub\teax, eax\n"
+"\tsub\tebx, ebx\n"
+"\tsub\tecx, ecx\n"
+"\tsub\tedx, edx\n"
+"\tmov\tedi, dbuffer+1\n"
+"\tmov\tbyte [dbuffer], '0'\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3606
+#, no-wrap
+msgid ""
+"\t; Convert the pinhole constant to real\n"
+".constloop:\n"
+"\tlodsb\n"
+"\tcmp\tal, '9'\n"
+"\tja\t.setconst\n"
+"\tcmp\tal, '0'\n"
+"\tje\t.processconst\n"
+"\tjb\t.setconst\n"
+msgstr ""
+"\t; Convert the pinhole constant to real\n"
+".constloop:\n"
+"\tlodsb\n"
+"\tcmp\tal, '9'\n"
+"\tja\t.setconst\n"
+"\tcmp\tal, '0'\n"
+"\tje\t.processconst\n"
+"\tjb\t.setconst\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3608
+#, no-wrap
+msgid "\tinc\tdl\n"
+msgstr "\tinc\tdl\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3615
+#, no-wrap
+msgid ""
+".processconst:\n"
+"\tinc\tcl\n"
+"\tcmp\tcl, 18\n"
+"\tja\tnear consttoobig\n"
+"\tstosb\n"
+"\tjmp\tshort .constloop\n"
+msgstr ""
+".processconst:\n"
+"\tinc\tcl\n"
+"\tcmp\tcl, 18\n"
+"\tja\tnear consttoobig\n"
+"\tstosb\n"
+"\tjmp\tshort .constloop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3620
+#, no-wrap
+msgid ""
+"align 4\n"
+".setconst:\n"
+"\tor\tdl, dl\n"
+"\tje\tnear perr\n"
+msgstr ""
+"align 4\n"
+".setconst:\n"
+"\tor\tdl, dl\n"
+"\tje\tnear perr\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3623
+#, no-wrap
+msgid ""
+"\tfinit\n"
+"\tfild\tdword [tthou]\n"
+msgstr ""
+"\tfinit\n"
+"\tfild\tdword [tthou]\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3627
+#, no-wrap
+msgid ""
+"\tfld1\n"
+"\tfild\tdword [ten]\n"
+"\tfdivp\tst1, st0\n"
+msgstr ""
+"\tfld1\n"
+"\tfild\tdword [ten]\n"
+"\tfdivp\tst1, st0\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3630
+#, no-wrap
+msgid ""
+"\tfild\tdword [thousand]\n"
+"\tmov\tedi, obuffer\n"
+msgstr ""
+"\tfild\tdword [thousand]\n"
+"\tmov\tedi, obuffer\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3633
+#, no-wrap
+msgid ""
+"\tmov\tebp, ecx\n"
+"\tcall\tbcdload\n"
+msgstr ""
+"\tmov\tebp, ecx\n"
+"\tcall\tbcdload\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3637
+#, no-wrap
+msgid ""
+".constdiv:\n"
+"\tfmul\tst0, st2\n"
+"\tloop\t.constdiv\n"
+msgstr ""
+".constdiv:\n"
+"\tfmul\tst0, st2\n"
+"\tloop\t.constdiv\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3644
+#, no-wrap
+msgid ""
+"\tfld1\n"
+"\tfadd\tst0, st0\n"
+"\tfadd\tst0, st0\n"
+"\tfld1\n"
+"\tfaddp\tst1, st0\n"
+"\tfchs\n"
+msgstr ""
+"\tfld1\n"
+"\tfadd\tst0, st0\n"
+"\tfadd\tst0, st0\n"
+"\tfld1\n"
+"\tfaddp\tst1, st0\n"
+"\tfchs\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3649
+#, no-wrap
+msgid ""
+"\t; If we are creating a CSV file,\n"
+"\t; print header\n"
+"\tcmp\tbyte [separ], ','\n"
+"\tjne\t.bigloop\n"
+msgstr ""
+"\t; If we are creating a CSV file,\n"
+"\t; print header\n"
+"\tcmp\tbyte [separ], ','\n"
+"\tjne\t.bigloop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3654
+#, no-wrap
+msgid ""
+"\tpush\tdword headlen\n"
+"\tpush\tdword header\n"
+"\tpush\tdword [fd.out]\n"
+"\tsys.write\n"
+msgstr ""
+"\tpush\tdword headlen\n"
+"\tpush\tdword header\n"
+"\tpush\tdword [fd.out]\n"
+"\tsys.write\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3658
+#, no-wrap
+msgid ""
+".bigloop:\n"
+"\tcall\tgetchar\n"
+"\tjc\tnear done\n"
+msgstr ""
+".bigloop:\n"
+"\tcall\tgetchar\n"
+"\tjc\tnear done\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3664
+#, no-wrap
+msgid ""
+"\t; Skip to the end of the line if you got '#'\n"
+"\tcmp\tal, '#'\n"
+"\tjne\t.num\n"
+"\tcall\tskiptoeol\n"
+"\tjmp\tshort .bigloop\n"
+msgstr ""
+"\t; Skip to the end of the line if you got '#'\n"
+"\tcmp\tal, '#'\n"
+"\tjne\t.num\n"
+"\tcall\tskiptoeol\n"
+"\tjmp\tshort .bigloop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3671
+#, no-wrap
+msgid ""
+".num:\n"
+"\t; See if you got a number\n"
+"\tcmp\tal, '0'\n"
+"\tjl\t.bigloop\n"
+"\tcmp\tal, '9'\n"
+"\tja\t.bigloop\n"
+msgstr ""
+".num:\n"
+"\t; See if you got a number\n"
+"\tcmp\tal, '0'\n"
+"\tjl\t.bigloop\n"
+"\tcmp\tal, '9'\n"
+"\tja\t.bigloop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3675
+#, no-wrap
+msgid ""
+"\t; Yes, we have a number\n"
+"\tsub\tebp, ebp\n"
+"\tsub\tedx, edx\n"
+msgstr ""
+"\t; Yes, we have a number\n"
+"\tsub\tebp, ebp\n"
+"\tsub\tedx, edx\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3680
+#, no-wrap
+msgid ""
+".number:\n"
+"\tcmp\tal, '0'\n"
+"\tje\t.number0\n"
+"\tmov\tdl, 1\n"
+msgstr ""
+".number:\n"
+"\tcmp\tal, '0'\n"
+"\tje\t.number0\n"
+"\tmov\tdl, 1\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3691
+#, no-wrap
+msgid ""
+".number0:\n"
+"\tor\tdl, dl\t\t; Skip leading 0's\n"
+"\tje\t.nextnumber\n"
+"\tpush\teax\n"
+"\tcall\tputchar\n"
+"\tpop\teax\n"
+"\tinc\tebp\n"
+"\tcmp\tebp, 19\n"
+"\tjae\t.nextnumber\n"
+"\tmov\t[dbuffer+ebp], al\n"
+msgstr ""
+".number0:\n"
+"\tor\tdl, dl\t\t; Skip leading 0's\n"
+"\tje\t.nextnumber\n"
+"\tpush\teax\n"
+"\tcall\tputchar\n"
+"\tpop\teax\n"
+"\tinc\tebp\n"
+"\tcmp\tebp, 19\n"
+"\tjae\t.nextnumber\n"
+"\tmov\t[dbuffer+ebp], al\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3702
+#, no-wrap
+msgid ""
+".nextnumber:\n"
+"\tcall\tgetchar\n"
+"\tjc\t.work\n"
+"\tcmp\tal, '#'\n"
+"\tje\t.ungetc\n"
+"\tcmp\tal, '0'\n"
+"\tjl\t.work\n"
+"\tcmp\tal, '9'\n"
+"\tja\t.work\n"
+"\tjmp\tshort .number\n"
+msgstr ""
+".nextnumber:\n"
+"\tcall\tgetchar\n"
+"\tjc\t.work\n"
+"\tcmp\tal, '#'\n"
+"\tje\t.ungetc\n"
+"\tcmp\tal, '0'\n"
+"\tjl\t.work\n"
+"\tcmp\tal, '9'\n"
+"\tja\t.work\n"
+"\tjmp\tshort .number\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3706
+#, no-wrap
+msgid ""
+".ungetc:\n"
+"\tdec\tesi\n"
+"\tinc\tebx\n"
+msgstr ""
+".ungetc:\n"
+"\tdec\tesi\n"
+"\tinc\tebx\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3711
+#, no-wrap
+msgid ""
+".work:\n"
+"\t; Now, do all the work\n"
+"\tor\tdl, dl\n"
+"\tje\tnear .work0\n"
+msgstr ""
+".work:\n"
+"\t; Now, do all the work\n"
+"\tor\tdl, dl\n"
+"\tje\tnear .work0\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3714
+#, no-wrap
+msgid ""
+"\tcmp\tebp, 19\n"
+"\tjae\tnear .toobig\n"
+msgstr ""
+"\tcmp\tebp, 19\n"
+"\tjae\tnear .toobig\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3716
+#, no-wrap
+msgid "\tcall\tbcdload\n"
+msgstr "\tcall\tbcdload\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3718
+#, no-wrap
+msgid "\t; Calculate pinhole diameter\n"
+msgstr "\t; Calculate pinhole diameter\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3725
+#, no-wrap
+msgid ""
+"\tfld\tst0\t; save it\n"
+"\tfsqrt\n"
+"\tfmul\tst0, st3\n"
+"\tfld\tst0\n"
+"\tfmul\tst5\n"
+"\tsub\tebp, ebp\n"
+msgstr ""
+"\tfld\tst0\t; save it\n"
+"\tfsqrt\n"
+"\tfmul\tst0, st3\n"
+"\tfld\tst0\n"
+"\tfmul\tst5\n"
+"\tsub\tebp, ebp\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3735
+#, no-wrap
+msgid ""
+"\t; Round off to 4 significant digits\n"
+".diameter:\n"
+"\tfcom\tst0, st7\n"
+"\tfstsw\tax\n"
+"\tsahf\n"
+"\tjb\t.printdiameter\n"
+"\tfmul\tst0, st6\n"
+"\tinc\tebp\n"
+"\tjmp\tshort .diameter\n"
+msgstr ""
+"\t; Round off to 4 significant digits\n"
+".diameter:\n"
+"\tfcom\tst0, st7\n"
+"\tfstsw\tax\n"
+"\tsahf\n"
+"\tjb\t.printdiameter\n"
+"\tfmul\tst0, st6\n"
+"\tinc\tebp\n"
+"\tjmp\tshort .diameter\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3738
+#, no-wrap
+msgid ""
+".printdiameter:\n"
+"\tcall\tprintnumber\t; pinhole diameter\n"
+msgstr ""
+".printdiameter:\n"
+"\tcall\tprintnumber\t; pinhole diameter\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3740
+#, no-wrap
+msgid "\t; Calculate F-number\n"
+msgstr "\t; Calculate F-number\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3743
+#, no-wrap
+msgid ""
+"\tfdivp\tst1, st0\n"
+"\tfld\tst0\n"
+msgstr ""
+"\tfdivp\tst1, st0\n"
+"\tfld\tst0\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3745
+#, no-wrap
+msgid "\tsub\tebp, ebp\n"
+msgstr "\tsub\tebp, ebp\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3754
+#, no-wrap
+msgid ""
+".fnumber:\n"
+"\tfcom\tst0, st6\n"
+"\tfstsw\tax\n"
+"\tsahf\n"
+"\tjb\t.printfnumber\n"
+"\tfmul\tst0, st5\n"
+"\tinc\tebp\n"
+"\tjmp\tshort .fnumber\n"
+msgstr ""
+".fnumber:\n"
+"\tfcom\tst0, st6\n"
+"\tfstsw\tax\n"
+"\tsahf\n"
+"\tjb\t.printfnumber\n"
+"\tfmul\tst0, st5\n"
+"\tinc\tebp\n"
+"\tjmp\tshort .fnumber\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3757
+#, no-wrap
+msgid ""
+".printfnumber:\n"
+"\tcall\tprintnumber\t; F number\n"
+msgstr ""
+".printfnumber:\n"
+"\tcall\tprintnumber\t; F number\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3768
+#, no-wrap
+msgid ""
+"\t; Calculate normalized F-number\n"
+"\tfmul\tst0, st0\n"
+"\tfld1\n"
+"\tfld\tst1\n"
+"\tfyl2x\n"
+"\tfrndint\n"
+"\tfld1\n"
+"\tfscale\n"
+"\tfsqrt\n"
+"\tfstp\tst1\n"
+msgstr ""
+"\t; Calculate normalized F-number\n"
+"\tfmul\tst0, st0\n"
+"\tfld1\n"
+"\tfld\tst1\n"
+"\tfyl2x\n"
+"\tfrndint\n"
+"\tfld1\n"
+"\tfscale\n"
+"\tfsqrt\n"
+"\tfstp\tst1\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3771
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3799
+#, no-wrap
+msgid ""
+"\tsub\tebp, ebp\n"
+"\tcall\tprintnumber\n"
+msgstr ""
+"\tsub\tebp, ebp\n"
+"\tcall\tprintnumber\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3773
+#, no-wrap
+msgid "\t; Calculate time multiplier from F-5.6\n"
+msgstr "\t; Calculate time multiplier from F-5.6\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3776
+#, no-wrap
+msgid ""
+"\tfscale\n"
+"\tfld\tst0\n"
+msgstr ""
+"\tfscale\n"
+"\tfld\tst0\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3782
+#, no-wrap
+msgid ""
+"\t; Round off to 4 significant digits\n"
+".fmul:\n"
+"\tfcom\tst0, st6\n"
+"\tfstsw\tax\n"
+"\tsahf\n"
+msgstr ""
+"\t; Round off to 4 significant digits\n"
+".fmul:\n"
+"\tfcom\tst0, st6\n"
+"\tfstsw\tax\n"
+"\tsahf\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3787
+#, no-wrap
+msgid ""
+"\tjb\t.printfmul\n"
+"\tinc\tebp\n"
+"\tfmul\tst0, st5\n"
+"\tjmp\tshort .fmul\n"
+msgstr ""
+"\tjb\t.printfmul\n"
+"\tinc\tebp\n"
+"\tfmul\tst0, st5\n"
+"\tjmp\tshort .fmul\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3790
+#, no-wrap
+msgid ""
+".printfmul:\n"
+"\tcall\tprintnumber\t; F multiplier\n"
+msgstr ""
+".printfmul:\n"
+"\tcall\tprintnumber\t; F multiplier\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3792
+#, no-wrap
+msgid "\t; Calculate F-stops from 5.6\n"
+msgstr "\t; Calculate F-stops from 5.6\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3796
+#, no-wrap
+msgid ""
+"\tfld1\n"
+"\tfxch\tst1\n"
+"\tfyl2x\n"
+msgstr ""
+"\tfld1\n"
+"\tfxch\tst1\n"
+"\tfyl2x\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3803
+#, no-wrap
+msgid ""
+"\tmov\tal, 0Ah\n"
+"\tcall\tputchar\n"
+"\tjmp\t.bigloop\n"
+msgstr ""
+"\tmov\tal, 0Ah\n"
+"\tcall\tputchar\n"
+"\tjmp\t.bigloop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3807
+#, no-wrap
+msgid ""
+".work0:\n"
+"\tmov\tal, '0'\n"
+"\tcall\tputchar\n"
+msgstr ""
+".work0:\n"
+"\tmov\tal, '0'\n"
+"\tcall\tputchar\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3812
+#, no-wrap
+msgid ""
+"align 4\n"
+".toobig:\n"
+"\tcall\thuh\n"
+"\tjmp\t.bigloop\n"
+msgstr ""
+"align 4\n"
+".toobig:\n"
+"\tcall\thuh\n"
+"\tjmp\t.bigloop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3816
+#, no-wrap
+msgid ""
+"align 4\n"
+"done:\n"
+"\tcall\twrite\t\t; flush output buffer\n"
+msgstr ""
+"align 4\n"
+"done:\n"
+"\tcall\twrite\t\t; flush output buffer\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3825
+#, no-wrap
+msgid "\tfinit\n"
+msgstr "\tfinit\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3838
+#, no-wrap
+msgid ""
+"align 4\n"
+"skiptoeol:\n"
+"\t; Keep reading until you come to cr, lf, or eof\n"
+"\tcall\tgetchar\n"
+"\tjc\tdone\n"
+"\tcmp\tal, 0Ah\n"
+"\tjne\t.cr\n"
+"\tret\n"
+msgstr ""
+"align 4\n"
+"skiptoeol:\n"
+"\t; Keep reading until you come to cr, lf, or eof\n"
+"\tcall\tgetchar\n"
+"\tjc\tdone\n"
+"\tcmp\tal, 0Ah\n"
+"\tjne\t.cr\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3843
+#, no-wrap
+msgid ""
+".cr:\n"
+"\tcmp\tal, 0Dh\n"
+"\tjne\tskiptoeol\n"
+"\tret\n"
+msgstr ""
+".cr:\n"
+"\tcmp\tal, 0Dh\n"
+"\tjne\tskiptoeol\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3856
+#, no-wrap
+msgid ""
+".fetch:\n"
+"\tlodsb\n"
+"\tdec\tebx\n"
+"\tclc\n"
+"\tret\n"
+msgstr ""
+".fetch:\n"
+"\tlodsb\n"
+"\tdec\tebx\n"
+"\tclc\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3873
+#, no-wrap
+msgid ""
+".read:\n"
+"\tpush\tdword BUFSIZE\n"
+"\tmov\tesi, ibuffer\n"
+"\tpush\tesi\n"
+"\tpush\tdword [fd.in]\n"
+"\tsys.read\n"
+"\tadd\tesp, byte 12\n"
+"\tmov\tebx, eax\n"
+"\tor\teax, eax\n"
+"\tje\t.empty\n"
+"\tsub\teax, eax\n"
+"\tret\n"
+msgstr ""
+".read:\n"
+"\tpush\tdword BUFSIZE\n"
+"\tmov\tesi, ibuffer\n"
+"\tpush\tesi\n"
+"\tpush\tdword [fd.in]\n"
+"\tsys.read\n"
+"\tadd\tesp, byte 12\n"
+"\tmov\tebx, eax\n"
+"\tor\teax, eax\n"
+"\tje\t.empty\n"
+"\tsub\teax, eax\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3879
+#, no-wrap
+msgid ""
+"align 4\n"
+".empty:\n"
+"\tadd\tesp, byte 4\n"
+"\tstc\n"
+"\tret\n"
+msgstr ""
+"align 4\n"
+".empty:\n"
+"\tadd\tesp, byte 4\n"
+"\tstc\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3908
+#, no-wrap
+msgid ""
+"align 4\n"
+"bcdload:\n"
+"\t; EBP contains the number of chars in dbuffer\n"
+"\tpush\tecx\n"
+"\tpush\tesi\n"
+"\tpush\tedi\n"
+msgstr ""
+"align 4\n"
+"bcdload:\n"
+"\t; EBP contains the number of chars in dbuffer\n"
+"\tpush\tecx\n"
+"\tpush\tesi\n"
+"\tpush\tedi\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3912
+#, no-wrap
+msgid ""
+"\tlea\tecx, [ebp+1]\n"
+"\tlea\tesi, [dbuffer+ebp-1]\n"
+"\tshr\tecx, 1\n"
+msgstr ""
+"\tlea\tecx, [ebp+1]\n"
+"\tlea\tesi, [dbuffer+ebp-1]\n"
+"\tshr\tecx, 1\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3914
+#, no-wrap
+msgid "\tstd\n"
+msgstr "\tstd\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3920
+#, no-wrap
+msgid ""
+"\tmov\tedi, bbuffer\n"
+"\tsub\teax, eax\n"
+"\tmov\t[edi], eax\n"
+"\tmov\t[edi+4], eax\n"
+"\tmov\t[edi+2], ax\n"
+msgstr ""
+"\tmov\tedi, bbuffer\n"
+"\tsub\teax, eax\n"
+"\tmov\t[edi], eax\n"
+"\tmov\t[edi+4], eax\n"
+"\tmov\t[edi+2], ax\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3929
+#, no-wrap
+msgid ""
+".loop:\n"
+"\tlodsw\n"
+"\tsub\tax, 3030h\n"
+"\tshl\tal, 4\n"
+"\tor\tal, ah\n"
+"\tmov\t[edi], al\n"
+"\tinc\tedi\n"
+"\tloop\t.loop\n"
+msgstr ""
+".loop:\n"
+"\tlodsw\n"
+"\tsub\tax, 3030h\n"
+"\tshl\tal, 4\n"
+"\tor\tal, ah\n"
+"\tmov\t[edi], al\n"
+"\tinc\tedi\n"
+"\tloop\t.loop\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3931
+#, no-wrap
+msgid "\tfbld\t[bbuffer]\n"
+msgstr "\tfbld\t[bbuffer]\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3938
+#, no-wrap
+msgid ""
+"\tcld\n"
+"\tpop\tedi\n"
+"\tpop\tesi\n"
+"\tpop\tecx\n"
+"\tsub\teax, eax\n"
+"\tret\n"
+msgstr ""
+"\tcld\n"
+"\tpop\tedi\n"
+"\tpop\tesi\n"
+"\tpop\tecx\n"
+"\tsub\teax, eax\n"
+"\tret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3944
+#, no-wrap
+msgid ""
+"align 4\n"
+"printnumber:\n"
+"\tpush\tebp\n"
+"\tmov\tal, [separ]\n"
+"\tcall\tputchar\n"
+msgstr ""
+"align 4\n"
+"printnumber:\n"
+"\tpush\tebp\n"
+"\tmov\tal, [separ]\n"
+"\tcall\tputchar\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3948
+#, no-wrap
+msgid ""
+"\t; Print the integer at the TOS\n"
+"\tmov\tebp, bbuffer+9\n"
+"\tfbstp\t[bbuffer]\n"
+msgstr ""
+"\t; Print the integer at the TOS\n"
+"\tmov\tebp, bbuffer+9\n"
+"\tfbstp\t[bbuffer]\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3954
+#, no-wrap
+msgid ""
+"\t; Check the sign\n"
+"\tmov\tal, [ebp]\n"
+"\tdec\tebp\n"
+"\tor\tal, al\n"
+"\tjns\t.leading\n"
+msgstr ""
+"\t; Check the sign\n"
+"\tmov\tal, [ebp]\n"
+"\tdec\tebp\n"
+"\tor\tal, al\n"
+"\tjns\t.leading\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3958
+#, no-wrap
+msgid ""
+"\t; We got a negative number (should never happen)\n"
+"\tmov\tal, '-'\n"
+"\tcall\tputchar\n"
+msgstr ""
+"\t; We got a negative number (should never happen)\n"
+"\tmov\tal, '-'\n"
+"\tcall\tputchar\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3967
+#, no-wrap
+msgid ""
+".leading:\n"
+"\t; Skip leading zeros\n"
+"\tmov\tal, [ebp]\n"
+"\tdec\tebp\n"
+"\tor\tal, al\n"
+"\tjne\t.first\n"
+"\tcmp\tebp, bbuffer\n"
+"\tjae\t.leading\n"
+msgstr ""
+".leading:\n"
+"\t; Skip leading zeros\n"
+"\tmov\tal, [ebp]\n"
+"\tdec\tebp\n"
+"\tor\tal, al\n"
+"\tjne\t.first\n"
+"\tcmp\tebp, bbuffer\n"
+"\tjae\t.leading\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3972
+#, no-wrap
+msgid ""
+"\t; We are here because the result was 0.\n"
+"\t; Print '0' and return\n"
+"\tmov\tal, '0'\n"
+"\tjmp\tputchar\n"
+msgstr ""
+"\t; We are here because the result was 0.\n"
+"\t; Print '0' and return\n"
+"\tmov\tal, '0'\n"
+"\tjmp\tputchar\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3984
+#, no-wrap
+msgid ""
+".first:\n"
+"\t; We have found the first non-zero.\n"
+"\t; But it is still packed\n"
+"\ttest\tal, 0F0h\n"
+"\tjz\t.second\n"
+"\tpush\teax\n"
+"\tshr\tal, 4\n"
+"\tadd\tal, '0'\n"
+"\tcall\tputchar\n"
+"\tpop\teax\n"
+"\tand\tal, 0Fh\n"
+msgstr ""
+".first:\n"
+"\t; We have found the first non-zero.\n"
+"\t; But it is still packed\n"
+"\ttest\tal, 0F0h\n"
+"\tjz\t.second\n"
+"\tpush\teax\n"
+"\tshr\tal, 4\n"
+"\tadd\tal, '0'\n"
+"\tcall\tputchar\n"
+"\tpop\teax\n"
+"\tand\tal, 0Fh\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3988
+#, no-wrap
+msgid ""
+".second:\n"
+"\tadd\tal, '0'\n"
+"\tcall\tputchar\n"
+msgstr ""
+".second:\n"
+"\tadd\tal, '0'\n"
+"\tcall\tputchar\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:3992
+#, no-wrap
+msgid ""
+".next:\n"
+"\tcmp\tebp, bbuffer\n"
+"\tjb\t.done\n"
+msgstr ""
+".next:\n"
+"\tcmp\tebp, bbuffer\n"
+"\tjb\t.done\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4002
+#, no-wrap
+msgid ""
+"\tmov\tal, [ebp]\n"
+"\tpush\teax\n"
+"\tshr\tal, 4\n"
+"\tadd\tal, '0'\n"
+"\tcall\tputchar\n"
+"\tpop\teax\n"
+"\tand\tal, 0Fh\n"
+"\tadd\tal, '0'\n"
+"\tcall\tputchar\n"
+msgstr ""
+"\tmov\tal, [ebp]\n"
+"\tpush\teax\n"
+"\tshr\tal, 4\n"
+"\tadd\tal, '0'\n"
+"\tcall\tputchar\n"
+"\tpop\teax\n"
+"\tand\tal, 0Fh\n"
+"\tadd\tal, '0'\n"
+"\tcall\tputchar\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4005
+#, no-wrap
+msgid ""
+"\tdec\tebp\n"
+"\tjmp\tshort .next\n"
+msgstr ""
+"\tdec\tebp\n"
+"\tjmp\tshort .next\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4010
+#, no-wrap
+msgid ""
+".done:\n"
+"\tpop\tebp\n"
+"\tor\tebp, ebp\n"
+"\tje\t.ret\n"
+msgstr ""
+".done:\n"
+"\tpop\tebp\n"
+"\tor\tebp, ebp\n"
+"\tje\t.ret\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4016
+#, no-wrap
+msgid ""
+".zeros:\n"
+"\tmov\tal, '0'\n"
+"\tcall\tputchar\n"
+"\tdec\tebp\n"
+"\tjne\t.zeros\n"
+msgstr ""
+".zeros:\n"
+"\tmov\tal, '0'\n"
+"\tcall\tputchar\n"
+"\tdec\tebp\n"
+"\tjne\t.zeros\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4019
+#, no-wrap
+msgid ""
+".ret:\n"
+"\tret\n"
+msgstr ""
+".ret:\n"
+"\tret\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4022
+msgid ""
+"The code follows the same format as all the other filters we have seen "
+"before, with one subtle exception:"
+msgstr ""
+"Код следует тому же формату, что и все остальные фильтры, которые мы видели "
+"ранее, с одним небольшим исключением:"
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4025
+msgid ""
+"We are no longer assuming that the end of input implies the end of things to "
+"do, something we took for granted in the _character-oriented_ filters."
+msgstr ""
+"Мы больше не предполагаем, что конец ввода означает конец задач, как мы "
+"привыкли в фильтрах, _ориентированных на символы_."
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4027
+msgid ""
+"This filter does not process characters. It processes a _language_ (albeit a "
+"very simple one, consisting only of numbers)."
+msgstr ""
+"Этот фильтр не обрабатывает символы. Он обрабатывает _язык_ (хотя и очень "
+"простой, состоящий только из чисел)."
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4029
+msgid "When we have no more input, it can mean one of two things:"
+msgstr ""
+"Когда у нас больше нет входных данных, это может означать одно из двух:"
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4031
+msgid "We are done and can quit. This is the same as before."
+msgstr "Мы закончили и можем выйти. Это то же самое, что и раньше."
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4032
+msgid ""
+"The last character we have read was a digit. We have stored it at the end of "
+"our ASCII-to-float conversion buffer. We now need to convert the contents of "
+"that buffer into a number and write the last line of our output."
+msgstr ""
+"Последний прочитанный символ был цифрой. Мы сохранили его в конце буфера "
+"преобразования ASCII в число с плавающей точкой. Теперь нам нужно "
+"преобразовать содержимое этого буфера в число и записать последнюю строку "
+"нашего вывода."
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4034
+msgid ""
+"For that reason, we have modified our `getchar` and our `read` routines to "
+"return with the `carry flag` _clear_ whenever we are fetching another "
+"character from the input, or the `carry flag` _set_ whenever there is no "
+"more input."
+msgstr ""
+"По этой причине мы изменили наши подпрограммы `getchar` и `read`, чтобы они "
+"возвращались с _сброшенным_ флагом `carry`, когда получают очередной символ "
+"из ввода, или с _установленным_ флагом `carry`, когда ввода больше нет."
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4037
+msgid ""
+"Of course, we are still using assembly language magic to do that! Take a "
+"good look at `getchar`. It _always_ returns with the `carry flag` _clear_."
+msgstr ""
+"Конечно, мы по-прежнему используем магию ассемблера для этого! Внимательно "
+"посмотрите на `getchar`. Он _всегда_ возвращает _очищенный_ `флаг переноса`."
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4039
+msgid ""
+"Yet, our main code relies on the `carry flag` to tell it when to quit-and it "
+"works."
+msgstr ""
+"Тем не менее, наш основной код использует `флаг переноса` для определения "
+"момента завершения — и это работает."
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4042
+msgid ""
+"The magic is in `read`. Whenever it receives more input from the system, it "
+"just returns to `getchar`, which fetches a character from the input buffer, "
+"_clears_ the `carry flag` and returns."
+msgstr ""
+"Волшебство кроется в `read`. Каждый раз, когда он получает больше входных "
+"данных от системы, он просто возвращается к `getchar`, который извлекает "
+"символ из входного буфера, _сбрасывает_ флаг переноса (`carry flag`) и "
+"возвращает управление."
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4045
+msgid ""
+"But when `read` receives no more input from the system, it does _not_ return "
+"to `getchar` at all. Instead, the `add esp, byte 4` op code adds `4` to "
+"`ESP`, _sets_ the `carry flag`, and returns."
+msgstr ""
+"Но когда `read` больше не получает входных данных от системы, он _не_ "
+"возвращается к `getchar` вообще. Вместо этого, инструкция `add esp, byte 4` "
+"добавляет `4` к `ESP`, _устанавливает_ флаг переноса (`carry flag`) и "
+"возвращает управление."
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4049
+msgid ""
+"So, where does it return to? Whenever a program uses the `call` op code, the "
+"microprocessor ``push``es the return address, i.e., it stores it on the top "
+"of the stack (not the FPU stack, the system stack, which is in the memory). "
+"When a program uses the `ret` op code, the microprocessor ``pop``s the "
+"return value from the stack, and jumps to the address that was stored there."
+msgstr ""
+"Итак, куда же она возвращается? Каждый раз, когда программа использует "
+"операцию `call`, микропроцессор делает ``push`` для адрес возврата, то есть "
+"сохраняет его на вершине стека (не стека FPU, а системного стека, который "
+"находится в памяти). Когда программа использует операцию `ret`, "
+"микропроцессор делает ``pop`` для значения возврата из стека и переходит по "
+"адресу, который там был сохранён."
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4051
+msgid ""
+"But since we added `4` to `ESP` (which is the stack pointer register), we "
+"have effectively given the microprocessor a minor case of _amnesia_: It no "
+"longer remembers it was `getchar` that ``call``ed `read`."
+msgstr ""
+"Но поскольку мы добавили `4` к `ESP` (который является регистром указателя "
+"стека), мы фактически вызвали у микропроцессора лёгкий случай _амнезии_: он "
+"больше не помнит, что именно `getchar` ``вызвал`` `read`."
+
+#. type: delimited block _ 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4053
+msgid ""
+"And since `getchar` never ``push``ed anything before ``call``ing `read`, the "
+"top of the stack now contains the return address to whatever or whoever "
+"``call``ed `getchar`. As far as that caller is concerned, he ``call``ed "
+"`getchar`, which ``ret``urned with the `carry flag` set!"
+msgstr ""
+"И поскольку `getchar` не делал ``push`` ни для чего перед вызовом `read`, "
+"верхушка стека теперь содержит адрес возврата к тому, что или кто вызывал "
+"`getchar`. С точки зрения этого вызывающего, он вызывал `getchar`, который "
+"вызвал ``ret`` с установленным `флагом переноса`!"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4056
+msgid ""
+"Other than that, the `bcdload` routine is caught up in the middle of a "
+"Lilliputian conflict between the Big-Endians and the Little-Endians."
+msgstr ""
+"Помимо этого, процедура `bcdload` оказывается втянута в лилипутский конфликт "
+"между Биг-Эндианцами и Литл-Эндианцами."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4058
+msgid ""
+"It is converting the text representation of a number into that number: The "
+"text is stored in the big-endian order, but the _packed decimal_ is little-"
+"endian."
+msgstr ""
+"Он преобразует текстовое представление числа в само число: текст хранится в "
+"порядке big-endian, но _упакованный десятичный_ формат имеет порядок little-"
+"endian."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4061
+msgid ""
+"To solve the conflict, we use the `std` op code early on. We cancel it with "
+"`cld` later on: It is quite important we do not `call` anything that may "
+"depend on the default setting of the _direction flag_ while `std` is active."
+msgstr ""
+"Для разрешения конфликта мы используем инструкцию процессора `std` в самом "
+"начале. Позже мы отменяем его с помощью `cld`: очень важно не вызывать "
+"ничего, что может зависеть от стандартного значения _флага направления_, "
+"пока активен `std`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4063
+msgid ""
+"Everything else in this code should be quit eclear, providing you have read "
+"the entire chapter that precedes it."
+msgstr ""
+"Всё остальное в этом коде должно быть достаточно понятным, при условии, что "
+"вы прочитали всю предшествующую главу."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4066
+msgid ""
+"It is a classical example of the adage that programming requires a lot of "
+"thought and only a little coding. Once we have thought through every tiny "
+"detail, the code almost writes itself."
+msgstr ""
+"Это классический пример поговорки о том, что программирование требует много "
+"размышлений и лишь немного кодирования. Как только мы продумаем каждую "
+"мельчайшую деталь, код практически напишется сам."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4068
+#, no-wrap
+msgid "Using pinhole"
+msgstr "Использование программы pinhole"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4072
+msgid ""
+"Because we have decided to make the program _ignore_ any input except for "
+"numbers (and even those inside a comment), we can actually perform _textual "
+"queries_. We do not _have to_, but we _can_."
+msgstr ""
+"Поскольку мы решили сделать так, чтобы программа _игнорировала_ любой ввод, "
+"кроме чисел (и даже их внутри комментария), мы можем выполнять _текстовые "
+"запросы_. Мы не _обязаны_ этого делать, но _можем_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4074
+msgid ""
+"In my humble opinion, forming a textual query, instead of having to follow a "
+"very strict syntax, makes software much more user friendly."
+msgstr ""
+"По моему скромному мнению, формирование текстового запроса вместо "
+"необходимости следовать очень строгому синтаксису делает программное "
+"обеспечение гораздо более дружелюбным к пользователю."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4080
+msgid ""
+"Suppose we want to build a pinhole camera to use the 4x5 inch film. The "
+"standard focal length for that film is about 150mm. We want to _fine-tune_ "
+"our focal length so the pinhole diameter is as round a number as possible. "
+"Let us also suppose we are quite comfortable with cameras but somewhat "
+"intimidated by computers. Rather than just have to type in a bunch of "
+"numbers, we want to _ask_ a couple of questions."
+msgstr ""
+"Предположим, мы хотим построить камеру-обскуру для использования плёнки "
+"размером 4x5 дюймов. Стандартное фокусное расстояние для такой плёнки "
+"составляет около 150 мм. Мы хотим _точно настроить_ фокусное расстояние, "
+"чтобы диаметр отверстия был как можно более круглым числом. Допустим также, "
+"что мы хорошо разбираемся в фотоаппаратах, но немного боимся компьютеров. "
+"Вместо того чтобы просто вводить кучу цифр, мы хотим _задать_ пару вопросов."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4082
+msgid "Our session might look like this:"
+msgstr "Наша сессия может выглядеть так:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4086
+#, no-wrap
+msgid "% pinhole\n"
+msgstr "% pinhole\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4088
+#, no-wrap
+msgid "Computer,\n"
+msgstr "Computer,\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4101
+#, no-wrap
+msgid ""
+"What size pinhole do I need for the focal length of 150?\n"
+"150\t490\t306\t362\t2930\t12\n"
+"Hmmm... How about 160?\n"
+"160\t506\t316\t362\t3125\t12\n"
+"Let's make it 155, please.\n"
+"155\t498\t311\t362\t3027\t12\n"
+"Ah, let's try 157...\n"
+"157\t501\t313\t362\t3066\t12\n"
+"156?\n"
+"156\t500\t312\t362\t3047\t12\n"
+"That's it! Perfect! Thank you very much!\n"
+"^D\n"
+msgstr ""
+"What size pinhole do I need for the focal length of 150?\n"
+"150\t490\t306\t362\t2930\t12\n"
+"Hmmm... How about 160?\n"
+"160\t506\t316\t362\t3125\t12\n"
+"Let's make it 155, please.\n"
+"155\t498\t311\t362\t3027\t12\n"
+"Ah, let's try 157...\n"
+"157\t501\t313\t362\t3066\t12\n"
+"156?\n"
+"156\t500\t312\t362\t3047\t12\n"
+"That's it! Perfect! Thank you very much!\n"
+"^D\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4104
+msgid ""
+"We have found that while for the focal length of 150, our pinhole diameter "
+"should be 490 microns, or 0.49 mm, if we go with the almost identical focal "
+"length of 156 mm, we can get away with a pinhole diameter of exactly one "
+"half of a millimeter."
+msgstr ""
+"Мы выяснили, что при фокусном расстоянии 150 мм диаметр отверстия должен "
+"составлять 490 микрон, или 0,49 мм, но если взять почти идентичное фокусное "
+"расстояние 156 мм, можно использовать отверстие диаметром ровно половину "
+"миллиметра."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4106
+#, no-wrap
+msgid "Scripting"
+msgstr "Скриптинг"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4109
+msgid ""
+"Because we have chosen the `+#+` character to denote the start of a comment, "
+"we can treat our pinhole software as a _scripting language_."
+msgstr ""
+"Поскольку мы выбрали символ `+#+` для обозначения начала комментария, мы "
+"можем рассматривать наше программное обеспечение pinhole как _скриптовый "
+"язык_."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4111
+msgid "You have probably seen shell _scripts_ that start with:"
+msgstr "Вы, вероятно, видели _сценарии_ оболочки, которые начинаются с:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4115
+#, no-wrap
+msgid "#! /bin/sh\n"
+msgstr "#! /bin/sh\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4118
+msgid "...or..."
+msgstr "...или..."
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4122
+#, no-wrap
+msgid "#!/bin/sh\n"
+msgstr "#!/bin/sh\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4125
+msgid "...because the blank space after the `#!` is optional."
+msgstr "...потому что пробел после `#!` необязателен."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4128
+msgid ""
+"Whenever UNIX(R) is asked to run an executable file which starts with the `#!"
+"`, it assumes the file is a script. It adds the command to the rest of the "
+"first line of the script, and tries to execute that."
+msgstr ""
+"Когда UNIX(R) получает запрос на выполнение исполняемого файла, который "
+"начинается с `#!`, он предполагает, что это скрипт. Он добавляет команду к "
+"остальной части первой строки скрипта и пытается выполнить её."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4130
+msgid ""
+"Suppose now that we have installed pinhole in /usr/local/bin/, we can now "
+"write a script to calculate various pinhole diameters suitable for various "
+"focal lengths commonly used with the 120 film."
+msgstr ""
+"Предположим, что мы установили pinhole в /usr/local/bin/, теперь мы можем "
+"написать скрипт для расчёта различных диаметров отверстий, подходящих для "
+"различных фокусных расстояний, обычно используемых с плёнкой 120."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4132
+msgid "The script might look something like this:"
+msgstr "Скрипт может выглядеть примерно так:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4138
+#, no-wrap
+msgid ""
+"#! /usr/local/bin/pinhole -b -i\n"
+"# Find the best pinhole diameter\n"
+"# for the 120 film\n"
+msgstr ""
+"#! /usr/local/bin/pinhole -b -i\n"
+"# Find the best pinhole diameter\n"
+"# for the 120 film\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4141
+#, no-wrap
+msgid ""
+"### Standard\n"
+"80\n"
+msgstr ""
+"### Standard\n"
+"80\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4144
+#, no-wrap
+msgid ""
+"### Wide angle\n"
+"30, 40, 50, 60, 70\n"
+msgstr ""
+"### Wide angle\n"
+"30, 40, 50, 60, 70\n"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4147
+#, no-wrap
+msgid ""
+"### Telephoto\n"
+"100, 120, 140\n"
+msgstr ""
+"### Telephoto\n"
+"100, 120, 140\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4150
+msgid "Because 120 is a medium size film, we may name this file medium."
+msgstr ""
+"Поскольку 120 — это плёнка среднего размера, мы можем назвать этот файл "
+"`medium`."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4152
+msgid ""
+"We can set its permissions to execute, and run it as if it were a program:"
+msgstr ""
+"Мы можем установить права на выполнение и запустить его, как если бы это "
+"была программа:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4157
+#, no-wrap
+msgid ""
+"% chmod 755 medium\n"
+"% ./medium\n"
+msgstr ""
+"% chmod 755 medium\n"
+"% ./medium\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4160
+msgid "UNIX(R) will interpret that last command as:"
+msgstr "UNIX(R) интерпретирует последнюю команду следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4164
+#, no-wrap
+msgid "% /usr/local/bin/pinhole -b -i ./medium\n"
+msgstr "% /usr/local/bin/pinhole -b -i ./medium\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4167
+msgid "It will run that command and display:"
+msgstr "Он выполнит эту команду и отобразит:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4179
+#, no-wrap
+msgid ""
+"80\t358\t224\t256\t1562\t11\n"
+"30\t219\t137\t128\t586\t9\n"
+"40\t253\t158\t181\t781\t10\n"
+"50\t283\t177\t181\t977\t10\n"
+"60\t310\t194\t181\t1172\t10\n"
+"70\t335\t209\t181\t1367\t10\n"
+"100\t400\t250\t256\t1953\t11\n"
+"120\t438\t274\t256\t2344\t11\n"
+"140\t473\t296\t256\t2734\t11\n"
+msgstr ""
+"80\t358\t224\t256\t1562\t11\n"
+"30\t219\t137\t128\t586\t9\n"
+"40\t253\t158\t181\t781\t10\n"
+"50\t283\t177\t181\t977\t10\n"
+"60\t310\t194\t181\t1172\t10\n"
+"70\t335\t209\t181\t1367\t10\n"
+"100\t400\t250\t256\t1953\t11\n"
+"120\t438\t274\t256\t2344\t11\n"
+"140\t473\t296\t256\t2734\t11\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4182
+msgid "Now, let us enter:"
+msgstr "Теперь введем:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4186
+#, no-wrap
+msgid "% ./medium -c\n"
+msgstr "% ./medium -c\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4189
+msgid "UNIX(R) will treat that as:"
+msgstr "UNIX(R) интерпретирует это следующим образом:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4193
+#, no-wrap
+msgid "% /usr/local/bin/pinhole -b -i ./medium -c\n"
+msgstr "% /usr/local/bin/pinhole -b -i ./medium -c\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4197
+msgid ""
+"That gives it two conflicting options: `-b` and `-c` (Use Bender's constant "
+"and use Connors' constant). We have programmed it so later options override "
+"early ones-our program will calculate everything using Connors' constant:"
+msgstr ""
+"Это дает ему два конфликтующих параметра: `-b` и `-c` (Использовать "
+"константу Бендера и использовать константу Коннорса). Мы запрограммировали "
+"его так, что более поздние параметры переопределяют ранние — наша программа "
+"будет вычислять все, используя константу Коннорса:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4209
+#, no-wrap
+msgid ""
+"80\t331\t242\t256\t1826\t11\n"
+"30\t203\t148\t128\t685\t9\n"
+"40\t234\t171\t181\t913\t10\n"
+"50\t262\t191\t181\t1141\t10\n"
+"60\t287\t209\t181\t1370\t10\n"
+"70\t310\t226\t256\t1598\t11\n"
+"100\t370\t270\t256\t2283\t11\n"
+"120\t405\t296\t256\t2739\t11\n"
+"140\t438\t320\t362\t3196\t12\n"
+msgstr ""
+"80\t331\t242\t256\t1826\t11\n"
+"30\t203\t148\t128\t685\t9\n"
+"40\t234\t171\t181\t913\t10\n"
+"50\t262\t191\t181\t1141\t10\n"
+"60\t287\t209\t181\t1370\t10\n"
+"70\t310\t226\t256\t1598\t11\n"
+"100\t370\t270\t256\t2283\t11\n"
+"120\t405\t296\t256\t2739\t11\n"
+"140\t438\t320\t362\t3196\t12\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4213
+msgid ""
+"We decide we want to go with Bender's constant after all. We want to save "
+"its values as a comma-separated file:"
+msgstr ""
+"Мы решаем, что всё же выбираем константу Бендера. Мы хотим сохранить её "
+"значения в виде файла с разделителями-запятыми:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4229
+#, no-wrap
+msgid ""
+"% ./medium -b -e > bender\n"
+"% cat bender\n"
+"focal length in millimeters,pinhole diameter in microns,F-number,normalized F-number,F-5.6 multiplier,stops from F-5.6\n"
+"80,358,224,256,1562,11\n"
+"30,219,137,128,586,9\n"
+"40,253,158,181,781,10\n"
+"50,283,177,181,977,10\n"
+"60,310,194,181,1172,10\n"
+"70,335,209,181,1367,10\n"
+"100,400,250,256,1953,11\n"
+"120,438,274,256,2344,11\n"
+"140,473,296,256,2734,11\n"
+"%\n"
+msgstr ""
+"% ./medium -b -e > bender\n"
+"% cat bender\n"
+"focal length in millimeters,pinhole diameter in microns,F-number,normalized F-number,F-5.6 multiplier,stops from F-5.6\n"
+"80,358,224,256,1562,11\n"
+"30,219,137,128,586,9\n"
+"40,253,158,181,781,10\n"
+"50,283,177,181,977,10\n"
+"60,310,194,181,1172,10\n"
+"70,335,209,181,1367,10\n"
+"100,400,250,256,1953,11\n"
+"120,438,274,256,2344,11\n"
+"140,473,296,256,2734,11\n"
+"%\n"
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4232
+#, no-wrap
+msgid "Caveats"
+msgstr "Предостережения"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4236
+msgid ""
+"Assembly language programmers who \"grew up\" under MS-DOS(R) and Windows(R) "
+"often tend to take shortcuts. Reading the keyboard scan codes and writing "
+"directly to video memory are two classical examples of practices which, "
+"under MS-DOS(R) are not frowned upon but considered the right thing to do."
+msgstr ""
+"Программисты на ассемблере, которые \"выросли\" на MS-DOS(R) и Windows(R), "
+"часто склонны искать короткие пути. Чтение скан-кодов клавиатуры и запись "
+"напрямую в видеопамять — это два классических примера подходов, которые в MS-"
+"DOS(R) не только не порицаются, но и считаются правильными."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4238
+msgid ""
+"The reason? Both the PC BIOS and MS-DOS(R) are notoriously slow when "
+"performing these operations."
+msgstr ""
+"Причина? И BIOS ПК, и MS-DOS(R) печально известны своей медленной работой "
+"при выполнении этих операций."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4241
+msgid ""
+"You may be tempted to continue similar practices in the UNIX(R) "
+"environment. For example, I have seen a web site which explains how to "
+"access the keyboard scan codes on a popular UNIX(R) clone."
+msgstr ""
+"Вас может возникнуть соблазн продолжить подобные практики в среде UNIX(R). "
+"Например, я видел веб-сайт, который объясняет, как получить доступ к скан-"
+"кодам клавиатуры на популярном клоне UNIX(R)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4243
+msgid ""
+"That is generally a _very bad idea_ in UNIX(R) environment! Let me explain "
+"why."
+msgstr ""
+"Это, как правило, *очень плохая идея* в среде UNIX(R)! Позвольте объяснить "
+"почему."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4245
+#, no-wrap
+msgid "UNIX(R) Is Protected"
+msgstr "UNIX(R) защищен"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4252
+msgid ""
+"For one thing, it may simply not be possible. UNIX(R) runs in protected "
+"mode. Only the kernel and device drivers are allowed to access hardware "
+"directly. Perhaps a particular UNIX(R) clone will let you read the keyboard "
+"scan codes, but chances are a real UNIX(R) operating system will not. And "
+"even if one version may let you do it, the next one may not, so your "
+"carefully crafted software may become a dinosaur overnight."
+msgstr ""
+"Прежде всего, это может быть просто невозможно. UNIX(R) работает в "
+"защищённом режиме. Только ядро и драйверы устройств имеют прямой доступ к "
+"аппаратному обеспечению. Возможно, какой-то конкретный клон UNIX(R) позволит "
+"вам читать скан-коды клавиатуры, но скорее всего настоящая операционная "
+"система UNIX(R) этого не допустит. И даже если одна версия разрешает это, "
+"следующая может запретить, так что ваше тщательно разработанное программное "
+"обеспечение может в одночасье устареть."
+
+#. type: Title ===
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4254
+#, no-wrap
+msgid "UNIX(R) Is an Abstraction"
+msgstr "UNIX(R) — это работа с абстракциями"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4257
+msgid ""
+"But there is a much more important reason not to try accessing the hardware "
+"directly (unless, of course, you are writing a device driver), even on the "
+"UNIX(R) like systems that let you do it:"
+msgstr ""
+"Но существует гораздо более важная причина не пытаться обращаться к "
+"оборудованию напрямую (если, конечно, вы не пишете драйвер устройства), даже "
+"в UNIX(R)-подобных системах, которые позволяют это делать:"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4259
+msgid "_UNIX(R) is an abstraction!_"
+msgstr "_UNIX(R) — это работа с абстракциями!_"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4265
+msgid ""
+"There is a major difference in the philosophy of design between MS-DOS(R) "
+"and UNIX(R). MS-DOS(R) was designed as a single-user system. It is run on "
+"a computer with a keyboard and a video screen attached directly to that "
+"computer. User input is almost guaranteed to come from that keyboard. Your "
+"program's output virtually always ends up on that screen."
+msgstr ""
+"Существует фундаментальное различие в философии проектирования между MS-"
+"DOS(R) и UNIX(R). MS-DOS(R) разрабатывалась как однопользовательская "
+"система. Она работает на компьютере, к которому напрямую подключены "
+"клавиатура и монитор. Ввод пользователя практически гарантированно поступает "
+"с этой клавиатуры. Вывод вашей программы почти всегда отображается на этом "
+"экране."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4268
+msgid ""
+"This is NEVER guaranteed under UNIX(R). It is quite common for a UNIX(R) "
+"user to pipe and redirect program input and output:"
+msgstr ""
+"Это НИКОГДА не гарантируется в UNIX(R). Довольно часто пользователь UNIX(R) "
+"перенаправляет ввод и вывод программы с помощью конвейеров и перенаправлений:"
+
+#. type: delimited block . 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4272
+#, no-wrap
+msgid "% program1 | program2 | program3 > file1\n"
+msgstr "% program1 | program2 | program3 > file1\n"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4276
+msgid ""
+"If you have written program2, your input does not come from the keyboard but "
+"from the output of program1. Similarly, your output does not go to the "
+"screen but becomes the input for program3 whose output, in turn, goes to "
+"[.filename]#file1#."
+msgstr ""
+"Если вы написали program2, ваш ввод поступает не с клавиатуры, а из вывода "
+"program1. Аналогично, ваш вывод не выводится на экран, а становится вводом "
+"для program3, чей вывод, в свою очередь, отправляется в [.filename]#file1#."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4279
+msgid ""
+"But there is more! Even if you made sure that your input comes from, and "
+"your output goes to, the terminal, there is no guarantee the terminal is a "
+"PC: It may not have its video memory where you expect it, nor may its "
+"keyboard be producing PC-style scan codes. It may be a Macintosh(R), or any "
+"other computer."
+msgstr ""
+"Но это еще не все! Даже если вы убедились, что ваш ввод поступает с "
+"терминала, а вывод отправляется на терминал, нет гарантии, что терминал "
+"является ПК: его видеопамять может находиться не там, где вы ожидаете, а "
+"клавиатура может генерировать не PC-совместимые скан-коды. Это может быть "
+"Macintosh(R) или любой другой компьютер."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4281
+msgid ""
+"Now you may be shaking your head: My software is in PC assembly language, "
+"how can it run on a Macintosh(R)? But I did not say your software would be "
+"running on a Macintosh(R), only that its terminal may be a Macintosh(R)."
+msgstr ""
+"Вот вы, возможно, покачаете головой: мое программное обеспечение написано на "
+"языке ассемблера для ПК, как оно может работать на Macintosh(R)? Но я не "
+"говорил, что ваше программное обеспечение будет работать на Macintosh(R), а "
+"лишь что его терминалом может быть Macintosh(R)."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4285
+msgid ""
+"Under UNIX(R), the terminal does not have to be directly attached to the "
+"computer that runs your software, it can even be on another continent, or, "
+"for that matter, on another planet. It is perfectly possible that a "
+"Macintosh(R) user in Australia connects to a UNIX(R) system in North America "
+"(or anywhere else) via telnet. The software then runs on one computer, "
+"while the terminal is on a different computer: If you try to read the scan "
+"codes, you will get the wrong input!"
+msgstr ""
+"В UNIX(R) терминал не обязательно должен быть напрямую подключён к "
+"компьютеру, на котором работает ваше программное обеспечение — он может "
+"находиться даже на другом континенте или, например, на другой планете. "
+"Вполне возможно, что пользователь Macintosh(R) в Австралии подключается к "
+"системе UNIX(R) в Северной Америке (или где-либо ещё) через telnet. "
+"Программное обеспечение работает на одном компьютере, а терминал находится "
+"на другом: если попытаться считать скан-коды, будут получены неверные данные!"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4288
+msgid ""
+"Same holds true about any other hardware: A file you are reading may be on a "
+"disk you have no direct access to. A camera you are reading images from may "
+"be on a space shuttle, connected to you via satellites."
+msgstr ""
+"То же самое относится и к любому другому оборудованию: файл, который вы "
+"читаете, может находиться на диске, к которому у вас нет прямого доступа. "
+"Камера, с которой вы считываете изображения, может находиться на космическом "
+"корабле, соединённом с вами через спутники."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4291
+msgid ""
+"That is why under UNIX(R) you must never make any assumptions about where "
+"your data is coming from and going to. Always let the system handle the "
+"physical access to the hardware."
+msgstr ""
+"Вот почему в UNIX(R) никогда нельзя делать никаких предположений о том, "
+"откуда поступают ваши данные и куда они направляются. Всегда позволяйте "
+"системе управлять физическим доступом к оборудованию."
+
+#. type: delimited block = 4
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4299
+msgid ""
+"These are caveats, not absolute rules. Exceptions are possible. For "
+"example, if a text editor has determined it is running on a local machine, "
+"it may want to read the scan codes directly for improved control. I am not "
+"mentioning these caveats to tell you what to do or what not to do, just to "
+"make you aware of certain pitfalls that await you if you have just arrived "
+"to UNIX(R) form MS-DOS(R). Of course, creative people often break rules, "
+"and it is OK as long as they know they are breaking them and why."
+msgstr ""
+"Это предостережения, а не абсолютные правила. Возможны исключения. Например, "
+"если текстовый редактор определил, что работает на локальной машине, он "
+"может захотеть читать скан-коды напрямую для улучшенного управления. Я "
+"упоминаю эти предостережения не для того, чтобы сказать вам, что делать или "
+"чего не делать, а просто чтобы вы осознавали определённые подводные камни, "
+"которые ждут вас, если вы только что перешли с MS-DOS(R) на UNIX(R). "
+"Конечно, творческие люди часто нарушают правила, и это нормально, пока они "
+"осознают, что нарушают их, и понимают почему."
+
+#. type: Title ==
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4302
+#, no-wrap
+msgid "Acknowledgements"
+msgstr "Благодарности"
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4305
+msgid ""
+"This tutorial would never have been possible without the help of many "
+"experienced FreeBSD programmers from the {freebsd-hackers}, many of whom "
+"have patiently answered my questions, and pointed me in the right direction "
+"in my attempts to explore the inner workings of UNIX(R) system programming "
+"in general and FreeBSD in particular."
+msgstr ""
+"Это руководство никогда бы не было создано без помощи многих опытных "
+"программистов FreeBSD из {freebsd-hackers}, которые терпеливо отвечали на "
+"мои вопросы и направляли меня в моих попытках изучить внутренние механизмы "
+"программирования в системе UNIX(R) в целом и в FreeBSD в частности."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4308
+msgid ""
+"Thomas M. Sommers opened the door for me . His https://web.archive.org/web/"
+"20090914064615/http://www.codebreakers-journal.com/content/view/262/27[How "
+"do I write \"Hello, world\" in FreeBSD assembler?] web page was my first "
+"encounter with an example of assembly language programming under FreeBSD."
+msgstr ""
+"Томас М. Соммерс открыл дверь для меня. Его https://web.archive.org/web/"
+"20090914064615/http://www.codebreakers-journal.com/content/view/262/27[Как "
+"написать \"Hello, world\" на ассемблере в FreeBSD?] веб-страница стала моей "
+"первой встречей с примером программирования на ассемблере под FreeBSD."
+
+#. type: Plain text
+#: documentation/content/en/books/developers-handbook/x86/_index.adoc:4310
+msgid ""
+"Jake Burkholder has kept the door open by willingly answering all of my "
+"questions and supplying me with example assembly language source code."
+msgstr ""
+"Джейк Буркхолдер держал дверь открытой, охотно отвечая на все мои вопросы и "
+"предоставляя примеры исходного кода на языке ассемблера."