* [.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#:
+Прежде чем изучать сервер BTX подробно, давайте рассмотрим, как создаётся единый, всеобъемлющий файл [.filename]#boot#. Способ сборки [.filename]#boot# определен в его [.filename]#Makefile# ([.filename]#stand/i386/boot2/Makefile#). Рассмотрим правило, которое создаёт файл [.filename]#boot#:
-Для применения правила создания [.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]#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#:
Напомним, что [.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]#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#.
@@ -651,8 +651,8 @@
Сервер BTX подготавливает простое окружение и переключается из 16-битного реального режима в 32-битный защищённый режим, непосредственно перед передачей управления клиенту. Это включает инициализацию и обновление следующих структур данных:
* Изменяет `Таблицу Векторов Прерываний (IVT)`. IVT предоставляет обработчики исключений и прерываний для кода в Реальном Режиме.
-* Создается `Таблица дескрипторов прерываний (IDT)`. В ней предусмотрены записи для исключений процессора, аппаратных прерываний, двух системных вызовов и интерфейса V86. IDT предоставляет обработчики исключений и прерываний для кода в защищенном режиме.
-* Создается `Сегмент состояния задачи (TSS)`. Это необходимо, потому что процессор работает на _наименее_ привилегированном уровне при выполнении клиента ([.filename]#boot2#), но на _наиболее_ привилегированном уровне при выполнении сервера BTX.
+* Создаётся `Таблица дескрипторов прерываний (IDT)`. В ней предусмотрены записи для исключений процессора, аппаратных прерываний, двух системных вызовов и интерфейса V86. IDT предоставляет обработчики исключений и прерываний для кода в защищенном режиме.
+* Создаётся `Сегмент состояния задачи (TSS)`. Это необходимо, потому что процессор работает на _наименее_ привилегированном уровне при выполнении клиента ([.filename]#boot2#), но на _наиболее_ привилегированном уровне при выполнении сервера BTX.
* Устанавливается GDT (Глобальная Таблица Дескрипторов). Создаются записи (дескрипторы) для кода и данных супервизора, кода и данных пользователя, а также кода и данных реального режима. footnote:[Код и данные реального режима необходимы при переключении обратно в реальный режим из защищённого режима, как указано в руководствах Intel.]
Приступим к изучению фактической реализации. Напомним, что [.filename]#boot1# выполнил переход на адрес `0x9010` — точку входа сервера BTX. Прежде чем изучать выполнение программы там, обратите внимание, что сервер BTX имеет специальный заголовок в диапазоне адресов `0x9000-0x900f`, непосредственно перед точкой входа. Этот заголовок определён следующим образом:
-Следующий блок создает IDT (таблицу дескрипторов прерываний). IDT в защищенном режиме аналогична IVT в реальном режиме. То есть, IDT описывает различные обработчики исключений и прерываний, используемые, когда процессор работает в защищенном режиме. По сути, она также состоит из массива пар сегмент/смещение, хотя структура несколько сложнее, поскольку сегменты в защищенном режиме отличаются от реального режима, и применяются различные механизмы защиты:
+Следующий блок создаёт IDT (таблицу дескрипторов прерываний). IDT в защищенном режиме аналогична IVT в реальном режиме. То есть, IDT описывает различные обработчики исключений и прерываний, используемые, когда процессор работает в защищенном режиме. По сути, она также состоит из массива пар сегмент/смещение, хотя структура несколько сложнее, поскольку сегменты в защищенном режиме отличаются от реального режима, и применяются различные механизмы защиты:
[.programlisting]
....
@@ -1195,7 +1195,7 @@
Хотя фреймворк sysinit описан в extref:{developers-handbook}[Руководстве разработчика], я рассмотрю его внутреннее устройство.
-Каждый объект инициализации системы (объект sysinit) создается путем вызова макроса SYSINIT(). Возьмем, к примеру, объект sysinit `announce`. Этот объект выводит сообщение об авторских правах:
+Каждый объект инициализации системы (объект sysinit) создаётся путем вызова макроса SYSINIT(). Возьмем, к примеру, объект sysinit `announce`. Этот объект выводит сообщение об авторских правах:
Теги организованы в иерархию в виде дерева с наследованием свойств. Дочерний тег наследует все требования родительского тега и может делать их более строгими, но никогда более мягкими.
-Обычно создается один корневой тег (без родителя) для каждого устройства. Если для каждого устройства требуется несколько областей памяти с разными требованиями, то для каждой из них может быть создан тег как дочерний по отношению к родительскому тегу.
+Обычно создаётся один корневой тег (без родителя) для каждого устройства. Если для каждого устройства требуется несколько областей памяти с разными требованиями, то для каждой из них может быть создан тег как дочерний по отношению к родительскому тегу.
Теги могут быть использованы для создания карты двумя способами.
-Загрузить буфер в карту (карта должна быть предварительно создана с помощью `bus_dmamap_create()` или `bus_dmamem_alloc()`). Все страницы буфера проверяются на соответствие требованиям тега, и для несоответствующих выделяются промежуточные страницы. Создается массив дескрипторов физических сегментов и передаётся в подпрограмму обратного вызова. Ожидается, что эта подпрограмма обработает его каким-либо образом. Количество промежуточных буферов в системе ограничено, поэтому, если эти буферы требуются, но недоступны немедленно, запрос будет поставлен в очередь, и обратный вызов будет выполнен, когда промежуточные буферы станут доступны. Возвращает 0, если обратный вызов был выполнен немедленно, или `EINPROGRESS`, если запрос был поставлен в очередь для выполнения в будущем. В последнем случае синхронизация с подпрограммой обратного вызова, поставленной в очередь, является обязанностью драйвера.
+Загрузить буфер в карту (карта должна быть предварительно создана с помощью `bus_dmamap_create()` или `bus_dmamem_alloc()`). Все страницы буфера проверяются на соответствие требованиям тега, и для несоответствующих выделяются промежуточные страницы. Создаётся массив дескрипторов физических сегментов и передаётся в подпрограмму обратного вызова. Ожидается, что эта подпрограмма обработает его каким-либо образом. Количество промежуточных буферов в системе ограничено, поэтому, если эти буферы требуются, но недоступны немедленно, запрос будет поставлен в очередь, и обратный вызов будет выполнен, когда промежуточные буферы станут доступны. Возвращает 0, если обратный вызов был выполнен немедленно, или `EINPROGRESS`, если запрос был поставлен в очередь для выполнения в будущем. В последнем случае синхронизация с подпрограммой обратного вызова, поставленной в очередь, является обязанностью драйвера.
+
** _dmat_ - тег
** _map_ - карта
@@ -479,7 +479,7 @@
-> bus_dmamap_destroy
....
-При загрузке карты, созданной `bus_dmamem_alloc()`, переданные адрес и размер буфера должны быть такими же, как использованные в `bus_dmamem_alloc()`. В этом случае гарантируется, что весь буфер будет отображен как один сегмент (так что обратный вызов может основываться на этом предположении) и запрос будет выполнен немедленно (EINPROGRESS никогда не будет возвращен). Все, что нужно сделать обратному вызову в этом случае, — это сохранить физический адрес.
+При загрузке карты, созданной `bus_dmamem_alloc()`, переданные адрес и размер буфера должны быть такими же, как использованные в `bus_dmamem_alloc()`. В этом случае гарантируется, что весь буфер будет отображён как один сегмент (так что обратный вызов может основываться на этом предположении) и запрос будет выполнен немедленно (EINPROGRESS никогда не будет возвращен). Все, что нужно сделать обратному вызову в этом случае, — это сохранить физический адрес.
-На большинстве систем UNIX(R) пользователь `root` обладает неограниченной властью. Это не способствует безопасности. Если злоумышленник получит права `root` в системе, у него окажутся все функции под рукой. В FreeBSD существуют sysctl-параметры, которые ограничивают власть `root`, чтобы минимизировать ущерб от действий злоумышленника. В частности, одна из таких функций называется `уровни безопасности`. Аналогично, другая функция, доступная начиная с FreeBSD 4.0, — это утилита man:jail[8] — клетка. Клетка создает chroot-окружение и накладывает определённые ограничения на процессы, запущенные внутри `клетки`. Например, процесс в `клетке` не может влиять на процессы вне её, использовать определённые системные вызовы или наносить какой-либо ущерб основной системе.
+На большинстве систем UNIX(R) пользователь `root` обладает неограниченной властью. Это не способствует безопасности. Если злоумышленник получит права `root` в системе, у него окажутся все функции под рукой. В FreeBSD существуют sysctl-параметры, которые ограничивают власть `root`, чтобы минимизировать ущерб от действий злоумышленника. В частности, одна из таких функций называется `уровни безопасности`. Аналогично, другая функция, доступная начиная с FreeBSD 4.0, — это утилита man:jail[8] — клетка. Клетка создаёт chroot-окружение и накладывает определённые ограничения на процессы, запущенные внутри `клетки`. Например, процесс в `клетке` не может влиять на процессы вне её, использовать определённые системные вызовы или наносить какой-либо ущерб основной системе.
Клетка становится новой моделью безопасности. Пользователи запускают потенциально уязвимые серверы, такие как Apache, BIND и sendmail, внутри клеток, так что если злоумышленник получит права `root` внутри клетки, это будет лишь неудобством, а не катастрофой. Данная статья в основном сосредоточена на внутреннем устройстве (исходном коде) клетки. Для получения информации о настройке клетки см. extref:{handbook}jails[раздел о клетках Руководства FreeBSD, jails-synopsis].
@@ -277,7 +277,7 @@
};
....
-В файле [.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` тоже со свойством клетки.
+В файле [.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]
....
@@ -313,7 +313,7 @@
}
....
-Когда процесс создается из родительского процесса, системный вызов man:fork[2] использует `crhold()` для поддержания учётных данных нового процесса. Это автоматически сохраняет учётные данные нового дочернего процесса согласованными с родительским, поэтому дочерний процесс также остаётся в клетке.
+Когда процесс создаётся из родительского процесса, системный вызов man:fork[2] использует `crhold()` для поддержания учётных данных нового процесса. Это автоматически сохраняет учётные данные нового дочернего процесса согласованными с родительским, поэтому дочерний процесс также остаётся в клетке.
[.programlisting]
....
@@ -359,7 +359,7 @@
[.filename]#/usr/src/sys/kern/sysv_sem.c#:
* `semctl(semid, semnum, cmd, ...)`: `semctl` выполняет указанную команду `cmd` для очереди семафоров, указанной в `semid`.
-* `semget(key, nsems, flag)`: `semget` создает массив семафоров, соответствующих `key`.
+* `semget(key, nsems, flag)`: `semget` создаёт массив семафоров, соответствующих `key`.
+
`key и flag имеют то же значение, как и в msgget.`
* `semop(semid, array, nops)`: `semop` выполняет набор операций, указанных в `array`, для набора семафоров, идентифицируемых `semid`.
@@ -369,7 +369,7 @@
[.filename]#/usr/src/sys/kern/sysv_shm.c#:
* `shmctl(shmid, cmd, buf)`: `shmctl` выполняет различные управляющие операции над областью разделяемой памяти, идентифицируемой `shmid`.
-* `shmget(key, size, flag)`: `shmget` обращается к существующей или создает новую область разделяемой памяти размером `size` байт.
+* `shmget(key, size, flag)`: `shmget` обращается к существующей или создаёт новую область разделяемой памяти размером `size` байт.
* `shmat(shmid, addr, flag)`: `shmat` присоединяет область разделяемой памяти, идентифицируемую `shmid`, к адресному пространству процесса.
* `shmdt(addr)`: `shmdt` отсоединяет ранее присоединенную область разделяемой памяти по адресу `addr`.
Kobj работает путем генерации описаний методов. Каждое описание содержит уникальный идентификатор, а также функцию по умолчанию. Адрес описания используется для однозначной идентификации метода в таблице методов класса.
-Класс создается путем построения таблицы методов, связывающей одну или несколько функций с описаниями методов. Перед использованием класс компилируется. В процессе компиляции выделяется кэш и связывается с классом. Уникальный идентификатор назначается каждому описанию метода в таблице методов класса, если это ещё не было сделано другой компиляцией, ссылающейся на этот класс. Для каждого используемого метода скриптом генерируется функция для проверки аргументов и автоматического обращения к описанию метода для поиска. Сгенерированная функция ищет метод, используя уникальный идентификатор, связанный с описанием метода, в качестве хэша для доступа к кэшу, связанному с классом объекта. Если метод не найден в кэше, сгенерированная функция использует таблицу класса для поиска метода. Если метод найден, используется связанная с ним функция внутри класса; в противном случае используется функция по умолчанию, связанная с описанием метода.
+Класс создаётся путем построения таблицы методов, связывающей одну или несколько функций с описаниями методов. Перед использованием класс компилируется. В процессе компиляции выделяется кэш и связывается с классом. Уникальный идентификатор назначается каждому описанию метода в таблице методов класса, если это ещё не было сделано другой компиляцией, ссылающейся на этот класс. Для каждого используемого метода скриптом генерируется функция для проверки аргументов и автоматического обращения к описанию метода для поиска. Сгенерированная функция ищет метод, используя уникальный идентификатор, связанный с описанием метода, в качестве хэша для доступа к кэшу, связанному с классом объекта. Если метод не найден в кэше, сгенерированная функция использует таблицу класса для поиска метода. Если метод найден, используется связанная с ним функция внутри класса; в противном случае используется функция по умолчанию, связанная с описанием метода.
Эти перенаправления можно визуализировать следующим образом:
-Поскольку набор активных политик может изменяться во время выполнения, а вызов точек входа не является атомарным, требуется синхронизация для предотвращения загрузки или выгрузки политик во время выполнения вызова точки входа, фиксируя набор активных политик на время выполнения. Это достигается с помощью счетчика занятости фреймворка: при входе в точку входа счетчик увеличивается; при выходе из нее — уменьшается. Пока счетчик занятости повышен, изменения списка политик запрещены, и потоки, пытающиеся изменить список политик, будут ждать, пока список не освободится. Счетчик занятости защищается мьютексом, а условная переменная используется для пробуждения потоков, ожидающих изменений списка политик. Побочным эффектом этой модели синхронизации является то, что рекурсивный вход в MAC Framework из модуля политики разрешен, хотя обычно не используется.
+Поскольку набор активных политик может изменяться во время выполнения, а вызов точек входа не является атомарным, требуется синхронизация для предотвращения загрузки или выгрузки политик во время выполнения вызова точки входа, фиксируя набор активных политик на время выполнения. Это достигается с помощью счетчика занятости фреймворка: при входе в точку входа счетчик увеличивается; при выходе из неё — уменьшается. Пока счетчик занятости повышен, изменения списка политик запрещены, и потоки, пытающиеся изменить список политик, будут ждать, пока список не освободится. Счетчик занятости защищается мьютексом, а условная переменная используется для пробуждения потоков, ожидающих изменений списка политик. Побочным эффектом этой модели синхронизации является то, что рекурсивный вход в MAC Framework из модуля политики разрешен, хотя обычно не используется.
Для снижения накладных расходов счётчика занятости используются различные оптимизации, включая избегание полной стоимости увеличения и уменьшения, если список пуст или содержит только статические записи (политики, загруженные до старта системы, которые нельзя выгрузить). Также предоставляется опция на этапе компиляции, которая предотвращает любые изменения в наборе загруженных политик во время выполнения, что устраняет затраты на блокировку мьютексов, связанные с поддержкой динамически загружаемых и выгружаемых политик, поскольку синхронизация больше не требуется.
@@ -770,7 +770,7 @@
|
|===
-Уничтожить метку на удаленном интерфейсе. В этой точке входа модуль политики должен освободить любое внутреннее хранилище, связанное с `label`, чтобы её можно было уничтожить.
+Уничтожить метку на удалённом интерфейсе. В этой точке входа модуль политики должен освободить любое внутреннее хранилище, связанное с `label`, чтобы её можно было уничтожить.
[[mac-mpo-destroy-ipq-label]]
==== `mpo_destroy_ipq_label`
@@ -1605,7 +1605,7 @@
|
|`devfs_dirent`
-|Запись в каталоге devfs, для которой создается метка.
+|Запись в каталоге devfs, для которой создаётся метка.
|
|`label`
@@ -2178,7 +2178,7 @@
|
|===
-Установите метку однорангового узла на потоковом UNIX-сокете из переданной конечной точки удаленного сокета. Этот вызов будет выполнен при соединении пары сокетов и будет произведен для обеих конечных точек.
+Установите метку однорангового узла на потоковом UNIX-сокете из переданной конечной точки удалённого сокета. Этот вызов будет выполнен при соединении пары сокетов и будет произведен для обеих конечных точек.
int FOO_DOIT_TO_CHILD(device_t dev, device_t child);
....
-Исходный файл `[.filename]#foo_if.c#` также создается для сопровождения автоматически сгенерированного заголовочного файла; он содержит реализации функций, которые ищут расположение соответствующих функций в таблице методов объекта и вызывают эту функцию.
+Исходный файл `[.filename]#foo_if.c#` также создаётся для сопровождения автоматически сгенерированного заголовочного файла; он содержит реализации функций, которые ищут расположение соответствующих функций в таблице методов объекта и вызывают эту функцию.
Система определяет два основных интерфейса. Первый фундаментальный интерфейс называется _"device"_ (устройство) и включает методы, которые относятся ко всем устройствам. Методы в интерфейсе _"device"_ включают _"probe"_ (обнаружение), _"attach"_ (присоединение) и _"detach"_ (отсоединение) для управления обнаружением оборудования, а также _"shutdown"_ (выключение), _"suspend"_ (приостановка) и _"resume"_ (возобновление) для уведомления о критических событиях.
Затем настройте команду SCSI. Хранилище команды может быть указано в CCB различными способами, определяемыми флагами CCB. Буфер команды может содержаться в CCB или указываться на него; в последнем случае указатель может быть физическим или виртуальным. Поскольку оборудованию обычно требуется физический адрес, мы всегда преобразуем адрес в физический, как правило, используя API busdma.
-В случае, если запрашивается физический адрес, допустимо вернуть CCB со статусом `CAM_REQ_INVALID`, текущие драйверы так и делают. При необходимости физический адрес также может быть преобразован или отображен обратно в виртуальный, но с большими трудностями, поэтому мы этого не делаем.
+В случае, если запрашивается физический адрес, допустимо вернуть CCB со статусом `CAM_REQ_INVALID`, текущие драйверы так и делают. При необходимости физический адрес также может быть преобразован или отображён обратно в виртуальный, но с большими трудностями, поэтому мы этого не делаем.
[.programlisting]
....
@@ -902,7 +902,7 @@
static void xxx_poll(struct cam_sim *);
....
-Функция poll используется для имитации прерываний, когда подсистема прерываний не функционирует (например, когда система аварийно завершила работу и создает дамп памяти). Подсистема CAM устанавливает соответствующий уровень прерывания перед вызовом процедуры poll. Таким образом, все, что ей нужно сделать, — это вызвать процедуру прерывания (или наоборот, процедура poll может выполнять реальные действия, а процедура прерывания просто вызывает процедуру poll). Зачем тогда нужна отдельная функция? Это связано с различными соглашениями о вызовах. Процедура `xxx_poll` получает указатель на структуру cam_sim в качестве аргумента, в то время как процедура прерывания PCI по общему соглашению получает указатель на структуру `xxx_softc`, а процедура прерывания ISA получает только номер устройства. Таким образом, процедура poll обычно выглядит следующим образом:
+Функция poll используется для имитации прерываний, когда подсистема прерываний не функционирует (например, когда система аварийно завершила работу и создаёт дамп памяти). Подсистема CAM устанавливает соответствующий уровень прерывания перед вызовом процедуры poll. Таким образом, все, что ей нужно сделать, — это вызвать процедуру прерывания (или наоборот, процедура poll может выполнять реальные действия, а процедура прерывания просто вызывает процедуру poll). Зачем тогда нужна отдельная функция? Это связано с различными соглашениями о вызовах. Процедура `xxx_poll` получает указатель на структуру cam_sim в качестве аргумента, в то время как процедура прерывания PCI по общему соглашению получает указатель на структуру `xxx_softc`, а процедура прерывания ISA получает только номер устройства. Таким образом, процедура poll обычно выглядит следующим образом:
Существует два возможных способа работы с устройствами, не поддерживающими PnP:
-* Используйте метод `device_identify()` (пример: [.filename]#sound/isa/es1888.c#). Метод `device_identify()` проверяет наличие оборудования по известным адресам и, если находит поддерживаемое устройство, создает новое pcm-устройство, которое затем передаётся для probe/attach.
+* Используйте метод `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`, чтобы управление питанием и выгрузка модулей работали корректно.
-Макрос `SYSINIT()` создает необходимые данные SYSINIT в наборе данных инициализации системы, чтобы SYSINIT мог отсортировать и выполнить функцию при запуске системы и загрузке модуля. `SYSINIT()` принимает уникальный идентификатор, который SYSINIT использует для идентификации конкретных данных вызова функции, порядок подсистемы, порядок элемента подсистемы, функцию для вызова и данные для передачи в функцию. Все функции должны принимать аргумент в виде константного указателя.
+Макрос `SYSINIT()` создаёт необходимые данные SYSINIT в наборе данных инициализации системы, чтобы SYSINIT мог отсортировать и выполнить функцию при запуске системы и загрузке модуля. `SYSINIT()` принимает уникальный идентификатор, который SYSINIT использует для идентификации конкретных данных вызова функции, порядок подсистемы, порядок элемента подсистемы, функцию для вызова и данные для передачи в функцию. Все функции должны принимать аргумент в виде константного указателя.