Базовые классы

Набор I/O является объектно-ориентированной платформой, состоящей прежде всего из десятков, если не сотни, из классов C++. Эти классы могут быть организованы на основании их отношений наследования в иерархии классов. Как со всеми иерархиями классов, Набор I/O может быть изображен как инвертированное дерево с бездетными узлами — классами без любых подклассов — как листы дерева. Перенося аналогию далее, классы в соединительной линии и, особенно, корень дерева - те, от которых наследовалось большинство классов иерархии. Это базовые классы.

Рисунок 5-1 показывает общую схему иерархии классов Набора I/O и позиции базовых классов в этой иерархии.

Рисунок 5-1  базовые классы иерархии классов Набора I/O
The base classes of the I/O Kit class hierarchy

Поскольку схема иллюстрирует, базовые классы, определенные для Набора I/O, являются IOService и IORegistryEntry; также включенный как базовые классы — посредством наследования — OSObject libkern библиотеки и (в специальном смысле) OSMetaClass.

Учитывая центрированность этих классов, очевидно, как важный это должно понять их. Они обеспечивают не только поведение и структуры данных, которые наследовали все другие классы Набора I/O. Они определяют структуру поведения для объектов драйвера и ядра: как объекты создают и избавляются, как информация о метаклассе получена и показана, как объекты драйвера должны вести себя в динамической среде выполнения, и как динамично установлены отношения клиента/провайдера среди объектов драйвера. Если Вы пишете драйверы устройств с помощью Набора I/O, Вы оказываетесь перед необходимостью иметь дело с базовыми классами в Вашем коде вначале и часто после того, таким образом, это - хорошая идея познакомиться с ними.

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

libkern Базовые классы

Набор I/O создается поверх libkern библиотеки C++, записанной в подмножестве C++, подходящего для использования в загружаемых модулях ядра. В частности libkern среда C++ не поддерживает множественное наследование, шаблоны, средство обработки исключений C++ и информацию о типах во время выполнения (RTTI). Средство RTTI C++ опущено, потому что оно не поддерживает динамическое выделение классов по имени, функция, требуемая для загрузки расширений ядра. RTTI также делает значительное использование исключений. Однако libkern среда C++ определяет свою собственную систему типов во время выполнения, действительно поддерживающую динамическую загрузку.

Исключения запрещаются в ядре по причинам и стоимости и устойчивости. Они увеличивают размер кода, таким образом используя драгоценную память ядра, и представляют непредсказуемые задержки. Далее, потому что код Набора I/O может быть вызван многими клиентскими потоками, нет никакого способа гарантировать, что будет поймано исключение. Используя try, throw, или catch в любом расширении ядра не поддерживается и приведет к ошибке компиляции. Несмотря на то, что Вы не можете использовать исключения в драйвере Набора I/O, Ваш драйвер должен всегда проверять коды возврата, где это необходимо.

Apple настоятельно рекомендует, чтобы Вы базировали весь код C++ ядра, включая это для драйверов устройств, на libkern базовых классах, OSObject и OSMetaClass, и наблюдали соглашения, предписанные теми классами (см. Преобразование типа, Объектный Самоанализ и информацию о Классе). Классы, которые являются абсолютно частными к Вашему драйверу, не должны основываться на OSObject и не должны следовать этим соглашениям. Такие классы, однако, будут ограничены в их взаимодействии с libkern классами. Например, все libkern классы набора хранят объекты, наследовавшиеся от OSObject. Пользовательские классы, не наследовавшиеся от OSObject, не могут быть сохранены в libkern наборах, таких как объекты OSArray или OSDictionary.

Создание объекта и размещение (OSObject)

OSObject в корне расширенной иерархии Набора I/O. Это не наследовалось ни от какого (общедоступного) суперкласса, и весь другой libkern и классы Набора I/O (за исключением OSMetaClass) наследовались от него. Реализации OSObject функции динамического контроля типов и выделения должны были поддерживать загружаемые модули ядра. Его виртуальные функции и переопределенные операторы определяют, как объекты создают, сохраняют и избавляются в ядре. OSObject является абстрактным базовым классом, и поэтому не может самостоятельно быть инстанцирован или скопирован.

Объектная конструкция

Типичные конструкторы C++ не могут использоваться в libkern, потому что эти конструкторы используют исключения для создания отчетов об отказах; как можно вспомнить, ограниченная форма C++, выбранного для libkern, исключает исключения. Таким образом, основная цель класса OSObject (и также класса OSMetaClass) состоит в том, чтобы повторно реализовать объектную конструкцию.

Для построения объектов OSObject определяет initфункция и переопределения new оператор. new оператор выделяет память для объекта и устанавливает подсчет ссылок объекта в один. После того, как это использует new оператор, клиент должен вызвать init функция на новом объекте выполнить все инициализации, требуемые сделать его применимым объектом. Если init вызовите сбои, тогда клиент должен сразу выпустить объект.

В поддержку OSObject’s init и new, класс OSMetaClass реализует макросы, связанные с объектной конструкцией. Они макросы связывают класс во ввод ядра во время выполнения средства и автоматически определяют функции, действующие как конструктор и деструктор для класса. Посмотрите Информацию о типах во время выполнения (OSMetaClass) для получения дополнительной информации о них макросы и реализация OSMetaClass RTTI.

Подклассы OSObject явно не реализуют своих конструкторов и деструкторы, так как они по существу создаются через макросы OSMetaClass. Кроме того, Вы обычно не вызываете ни конструктора и функций деструктора, ни C++ new и deleteоператоры. Эти функции и операторы резервируются для использования средствами динамического контроля типов и выделения, неявно определяющими их для класса. В их месте OSObject определяет соглашение для создания и инициализации объектов. Подклассы действительно, однако, обычно переопределяют init функция для выполнения инициализаций, определенных для класса.

Большая часть libkern и классы Набора I/O определяют одну или более статических функций для создания экземпляров. Соглашение о присвоении имен варьируется от класса до класса, но имя является обычно любой базовым именем самого класса (с нижним регистром, сначала обозначают буквами), или некоторая форма with... где имя описывает параметры инициализации. Например, OSArray определяет статические функции создания withCapacity, withObjects, и withArray; IOTimerEventSource определяет timerEventSource; и IOMemoryCursor определяет withSpecification. Если класс не имеет статических функций создания, необходимо использовать new и затем вызовите метод инициализации, занимающий место конструктора C++, как показано в Перечислении 5-2

Поскольку обзор шаблона кодирует Вас, должен указать конструктора Вашего класса и функции деструктора, видеть Преобразование типа, Объектный Самоанализ и информацию о Классе

Объектное отставание и размещение

OSObject определяет подсчет ссылок и механизм автоматического освобождения для поддержки безопасной разгрузки расширений ядра. Для этого механизма это использует три виртуальных функции членства retain, release, и free— и переопределения deleteоператор. Из них единственные функции, которые необходимо вызвать в коде, retain и release, и необходимо следовать определенным соглашениям, диктующим, когда вызвать их.

Недавно созданные объекты и скопированные объекты имеют подсчет ссылок одного. Если Вы создали или скопировали объект libkern и не имеете никакой потребности сохранить его вне текущего контекста, необходимо вызвать release на нем. Это постепенно уменьшает подсчет ссылок объекта. Если то количество является нулем, объект освобожден; в частности, release метод вызывает альтернативный деструктор, названный free, и наконец вызывает delete оператор. Если Вам не принадлежит объект — т.е. Вы не создавали или копировали его — и Вы хотите сохранить его мимо текущего контекста, вызвать retain на нем для постепенного увеличения его подсчета ссылок. Если Вы не создавали, скопируйте, или вызов retain на объекте Вы никогда не должны вызывать release на нем.

Кроме того, некоторые функции, что эхо-сигналы передают владение вызывающей стороне, означая вызывающую сторону, должны выпустить объект, когда это закончено с ним, в то время как другие не делают. См. справочную документацию для заданной функции, чтобы узнать, должен ли Ваш код сохранить или выпустить объект, это получает.

Никогда не вызывайте delete оператор явно для освобождения объекта. Кроме того, никогда не вызывайте free непосредственно освободить объект; однако, Вы можете (и если, при большинстве обстоятельств) переопределяют free функция для освобождения памяти, выделенной в Вашем init функция.

Информация о типах во время выполнения (OSMetaClass)

Несмотря на то, что ограниченная форма libkern C++ исключает собственную информацию о типах во время выполнения (RTTI) средство, OSMetaClass реализует альтернативное средство ввода времени выполнения, действительно поддерживающее динамическое выделение классов по имени. OSMetaClass не является базовым классом в истинном смысле; никакая общественность libkern или класс Набора I/O не наследовались от него. Однако OSMetaClass обеспечивает APIs и функциональность, которые важны для объектной конструкции и уничтожения. Сам OSMetaClass является абстрактным классом и не может быть непосредственно создан.

Функциональность, что OSMetaClass предлагает весь находящийся в libkern код, включает следующее:

  • Механизм для отслеживания иерархии классов динамично

  • Безопасная загрузка и разгрузка модулей ядра

    Средство ввода времени выполнения позволяет системе отследить, сколько экземпляров каждого libkern (и Набор I/O) класс является в настоящее время существующим и присваивать каждый из этих экземпляров к модулю ядра (KMOD).

  • Автоматическая конструкция и разрушение экземпляров класса

  • Макросы и функции для динамического преобразования типа, введите открытие, оценку членства и подобное самосозерцательное поведение

  • Динамическое выделение libkern экземпляров класса на основе некоторой индикации относительно их типа класса, включая имена струны до

Во вводе libkern во время выполнения средства один статический экземпляр метакласса (дериватив OSMetaClass) создается для каждого класса в модуле ядра (KMOD), загруженный в ядро. Экземпляр инкапсулирует информацию об имени класса, размере, суперклассе, модуле ядра и текущем количестве экземпляров того класса. Процесс загрузки модуля ядра имеет место в двух фазах, первое, инициируемое preModLoad функция членства и второе postModLoad функция. Во время preModLoad фаза, OSMetaClass статически создает, в контексте единственного, защищенного от блокировки потока, экземпляра метакласса для каждого класса в модуле. В postModLoad фаза, OSMetaClass соединяет иерархию наследования созданных объектов метакласса, вставляет экземпляры метакласса в глобальный регистр классов и записывает для каждого экземпляра модуль ядра, из которого это произошло. См. справочную документацию OSMetaClass для больше на preModLoad, postModLoad, и связанные функции.

Создаваемое хранилище информации о метаклассе формирует основание для возможностей упомянутого выше OSMetaClass. Следующие разделы исследуют более важные из этих возможностей в некоторой подробности.

Объектная конструкция и динамическое выделение

Одна из функций OSMetaClass является своей возможностью выделить объекты libkern, основанные на некоторой индикации относительно типа класса. Подклассы OSMetaClass могут сделать это динамично путем реализации allocфункция; тип класса предоставляется самим подклассом OSMetaClass. Можно также выделить экземпляр любого libkern класса путем вызова одного из allocClassWithNameфункции, предоставляя надлежащую идентификацию типа класса (OSSymbol, OSString или струна до).

Недавно выделенные объекты имеют сохранить количество 1 как их единственная переменная экземпляра и являются иначе неинициализированными. После выделения клиент должен сразу вызвать функцию инициализации объекта (который является init или некоторый вариант init).

OSMetaClass определяет много макросов описания типа во время выполнения и макросов объектной конструкции на основе alloc функция. На основе типа класса (виртуальный или иначе), необходимо вставить один из них макросы как первый оператор в объявлениях класса и реализациях:

OSDeclareDefaultStructors

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

OSDeclareAbstractStructors

Объявляет данные и интерфейсы виртуального класса, которые необходимы как информация о типах во время выполнения. Условно этот макрос должен сразу следовать за вводной фигурной скобкой в объявлении класса. Используйте этот макрос, когда класс будет иметь один или несколько чистые виртуальные методы.

OSDefineMetaClassAndStructors

Определяет подкласс OSMetaClass и основных конструкторов и деструкторы для неабстрактного подкласса OSObject. Этот макрос должен появиться наверху файла реализации непосредственно перед тем, как первая функция реализована для определенного класса.

OSDefineMetaClassAndAbstractStructors

Определяет подкласс OSMetaClass и основных конструкторов и деструкторы для подкласса OSObject, который является абстрактным классом. Этот макрос должен появиться наверху файла реализации непосредственно перед тем, как первая функция реализована для определенного класса.

OSDefineMetaClassAndStructorsWithInit

Определяет подкласс OSMetaClass и основных конструкторов и деструкторы для неабстрактного подкласса OSObject. Этот макрос должен появиться наверху файла реализации непосредственно перед тем, как первая функция реализована для определенного класса. Указанную подпрограмму инициализации вызывают, как только экземпляр OSMetaClass был создан во время загрузки.

OSDefineMetaClassAndAbstractStructorsWithInit

Определяет подкласс OSMetaClass и основных конструкторов и деструкторы для подкласса OSObject, который является абстрактным классом. Этот макрос должен появиться наверху файла реализации непосредственно перед тем, как первая функция реализована для определенного класса. Указанную подпрограмму инициализации вызывают, как только экземпляр OSMetaClass был создан во время загрузки.

Посмотрите Преобразование типа, Объектный Самоанализ и информацию о Классе для получения дополнительной информации об использовании их макросы, включая примеры использования.

Преобразование типа, объектный самоанализ и информация о классе

OSMetaClass определяет многих макросы и функции, которые можно использовать в почти любой ситуации. Они помогают Вам безопасно бросить от одного типа до другого, обнаружить класс произвольного объекта, определить, наследовался ли объект от данного суперкласса, узнайте, сколько экземпляров данного класса все еще выделяется и приводит к другой полезной информации. Таблица 5-1 суммирует их макросы и функции.

Таблица 5-1  преобразование типа OSMetaClass и самоанализ APIs

Функция или макрос

Описание

OSTypeID

Этот макрос возвращает идентификатор типа класса на основе его имени.

OSTypeIDInst

Этот макрос возвращает идентификатор типа класса, из которого создается приведенный пример.

OSCheckTypeInst

Этот макрос проверяет, имеет ли один экземпляр тот же тип класса как другой экземпляр.

OSDynamicCast

Этот макрос динамично бросает тип класса экземпляра к подходящему классу. Это в основном эквивалентно RTTI’s dynamic_cast.

isEqualTo

Эта функция проверяет, совпадает ли вызов экземпляр OSMetaClass (который представляет класс) с другим экземпляром OSMetaClass. Реализация по умолчанию выполняет мелкое сравнение указателя.

metaCast (многократный)

Этот набор функций определяет, ли экземпляр OSMetaClass (который представляет класс) или наследовался от, данный тип класса. Тип может быть указан как OSMetaClass, OSSymbol, OSString или струна до.

modHasInstance

Возвраты, имеет ли модуль ядра какие-либо выдающиеся экземпляры. Эта функция обычно вызывается, чтобы определить, может ли быть разгружен модуль.

getInstanceCount

Эта функция возвращает число экземпляров класса, представленного получателем.

getSuperClass

Эта функция возвращает суперкласс получателя.

getClassName

Эта функция возвращает имя (как струна до) получателя.

getClassSize

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

Определение Классов C++ в libkern

При реализации класса C++ на основе OSObject Вы вызываете пару макросов, основанных на классе OSMetaClass. Они связь макросов Ваш класс в libkern средство ввода времени выполнения путем определения метакласса и путем определения конструктора и деструктора для класса, выполняющих бухгалтерские задачи RTTI через метакласс.

Первый макрос, OSDeclareDefaultStructors объявляет конструкторов C++; условно Вы вставляете этот макрос как первый элемент объявления класса в заголовочном файле. Например:

class MyDriver : public IOEthernetController
{
    OSDeclareDefaultStructors(MyDriver);
    /* ... */
};

Ваша реализация класса тогда использует сопутствующий макрос, OSDefineMetaClassAndStructors, определить конструктора и деструктор, а также метакласс, предоставляющий информацию ввода времени выполнения. OSDefineMetaClassAndStructors берет в качестве параметров имя Вашего драйвера и имя его суперкласса. Это использует их для генерации кода, позволяющего классу драйвера быть загруженным и инстанцированным, в то время как работает ядро. Например, MyDriver.cpp мог бы начаться как это:

#include "MyDriver.h"
 
// This convention makes it easy to invoke superclass methods.
#define super    IOEthernetController
 
// You cannot use the "super" macro here, however, with the
//  OSDefineMetaClassAndStructors macro.
OSDefineMetaClassAndStructors(MyDriver, IOEthernetController);

Определение superмакрос предоставляет удобный доступ к методам суперкласса, не имея необходимость вводить целое имя суперкласса каждый раз. Это - общая идиома libkern и реализаций класса Набора I/O.

Вместо конструктора C++ и деструктора, Ваш класс реализует метод инициализации и a freeметод. Для классов Кита non–I/O метод инициализации берет любые параметры, необходимы, может иметь любое имя (несмотря на то, что это обычно начинается init), и возвраты C++ bool значение. free метод всегда не берет параметров и возвратов void.

Если суперкласс возвращается, метод инициализации для Вашего класса драйвера должен вызвать надлежащий метод инициализации суперкласса прежде, чем сделать что-либо еще, как показано в Перечислении 5-1 false, метод инициализации Вашего класса должен прервать, высвободить любые выделенные средства и возврат false. Иначе Ваш класс может выполнить свою инициализацию и возврат true. Когда libkern система времени выполнения C++ создает экземпляр класса, она заполняет нулями все задействованные переменные, таким образом, Вы не должны явно инициализировать ничего для обнуления, ложь или нулевые значения.

Перечисление 5-1  Реализовывая init метод

bool MyDriver::init(IOPhysicalAddress * paddr)
{
    if (!super::init()) {
        // Perform any required clean-up, then return.
        return false;
    }
    physAddress = paddr;  // Set an instance variable.
    return true;
}

Для создания экземпляра с помощью метода инициализации Вы пишете код, такой как это:

Перечисление 5-2  , Создающее экземпляр и вызывающее его init метод

MyDriver * pDrv = new MyDriver; // This invokes the predefined constructor
                                //  of MyDriver itself
 
if (!pDrv) {
    // Deal with error.
}
 
if (!pDrv->init(memAddress)) {
    // Deal with error.
    pDrv->release();    // Dispose of the driver object.
}

Поскольку это делает экземпляры создания более громоздкими, можно хотеть записать удобный метод способом многих классов C++ ядра, что касается примера:

MyDriver * MyDriver::withAddress(IOPhysicalAddress *paddr)
{
    MyDriver * pDrv = new MyDriver;
 
 
    if (pDrv && !pDrv->init(paddr)) {
        pDrv->release();
        return 0;
    }
    return pDrv;
}

Используя этот удобный метод, можно создать экземпляр драйвера с кодом как следующее:

MyDriver * pDrv = MyDriver::withAddress(paddr);
 
if (!pDrv) {
    // Deal with error of not being able to create driver object.
}
else {
    // Go on after successful creation of driver object.
}

Класс free метод должен высвободить любые средства, сохраненные экземпляром, и затем вызвать суперкласс free метод, как в Перечислении 5-3:

Перечисление 5-3  Реализовывая свободную функцию

void MyDriver::free(void)
{
    deviceRegisterMap->release();
    super::free();
    return;
}

Снова, обратите внимание на то, что Ваш код никогда не должен вызывать free или delete оператор непосредственно с объектами на основе класса OSObject. Всегда вызывайте release на таких объектах избавиться от них.

Базовые классы Набора I/O

Все объекты драйвера на основе Набора I/O наследовали от этих двух базовых классов IORegistryEntry и IOService. Второй из этих классов, IOService, непосредственно наследовался от IORegistryEntry, и все объекты драйвера в конечном счете наследовались от IOService. Класс IORegistryEntry определяет объект драйвера как узел в Реестре I/O, и IOService определяет жизненный цикл объекта драйвера, а также реализации другого поведения, характерного для драйверов.

Близкое отношение наследования между IORegistryEntry и IOService могло бы пригласить спекуляцию относительно того, почему эти классы не были разработаны как один класс. Причиной является производительность. Имея IORegistryEntry, поскольку суперкласс IOService является оптимизацией, потому что с точки зрения объема потребляемой памяти объект IORegistryEntry намного более легок.

Динамическая регистрация драйвера (IORegistryEntry)

Объект IORegistryEntry определяет узел (или запись) в Реестре I/O. Поскольку глава, которую Реестр I/O объясняет подробно, Реестр I/O, является базой динамических данных, получающей текущий график «живых» объектов драйвера, отслеживая отношения клиента/провайдера среди этих объектов и записывая свойства, описывающие их лица. Реестр I/O играет существенную роль в динамических функциях OS X; когда пользователи добавляют или демонтируют аппаратные средства, система использует Реестр в соответствующем драйвер процессе и сразу обновляет их для отражения новой конфигурации устройств.

Каждый объект IORegistryEntry имеет два словаря (т.е. экземпляры OSDictionary) связанный с ним. Каждый - таблица свойства для объекта, который является обычно также объектом драйвера. Эта таблица свойства является соответствующим словарем, указывающим одно из водительских лиц. (См. Лица Драйвера и Соответствие Языков для получения информации о лицах.) Другой словарь объекта IORegistryEntry является плоским словарем, указывающим, как объект подключен к другим объектам в реестре.

В дополнение к отражению всех отношений клиента/провайдера среди объектов драйвера Реестр I/O идентифицирует подмножества этих отношений. И все количество дерева Реестра и подмножества его вызывают плоскостями. Каждый плоские экспрессы различный провайдер/связи с потребителями между объектами в Реестре I/O путем показа только тех соединений, существующих в том отношении. Часто плоское отношение является одной из цепочки зависимости. Самая общая плоскость является плоскостью Службы, выводящей на экран общую иерархию ключей реестра. Каждый объект в Реестре является клиентом услуг, предоставленных его родителем, таким образом, соединение каждого объекта с его наследователем в дереве Реестра видимо на плоскости Службы. В дополнение к плоскости Службы существует Питание, Аудио, Устройство, FireWire и плоскости USB. Для получения дополнительной информации о плоскостях посмотрите Архитектуру Реестра I/O и Конструкцию

Возможно сделать, чтобы IORegistryEntry возразил, что это не также объект IOService. Такой объект мог использоваться просто для содержания информации, связанной с тем узлом в Реестре. Однако существует мало фактической потребности для таких объектов.

Класс IORegistryEntry включает много функций членства, которые объекты драйвера могли бы счесть полезным; эти функции попадают в несколько категорий:

  • Функции таблицы свойства позволяют Вам устанавливать, получать, и удалять свойства таблицы свойства объекта IORegistryEntry, а также таблиц свойства сериализации. Некоторые getPropertyфункции выполняют синхронизируемый, рекурсивный поиск через Реестр для свойства данного ключа.

  • Позиционные функции позволяют объекту IORegistryEntry управлять своей позицией в дереве Реестра. Это может определить местоположение, идентифицировать, и присоединить к или отсоединиться от другого объекта IORegistryEntry.

  • Итеративные функции позволяют Вашему коду пересечь все дерево Реестра, или часть или это, и дополнительно вызвать «applier» функцию обратного вызова на объекты IORegistryEntry, с которыми встречаются.

См. справочную документацию для IORegistryEntry для подробных данных.

Основное поведение драйвера (IOService)

Каждый объект драйвера в Наборе I/O является экземпляром класса, в конечном счете наследовавшегося от класса IOService. IOService самое главное определяет, через дополнительных пар виртуальных функций, водительского жизненного цикла в динамической среде выполнения. Это управляет соответствием и зондированием процесса, реализации поведение при сравнении по умолчанию, и регистрирует драйверы и другие службы. Но класс IOService также обеспечивает богатство функциональности во многих других целях, включая:

  • Получая доступ к водительскому провайдеру, клиентам, состоянию и циклу работы

  • Регистрация уведомлений и отправка сообщений к другим объектам драйвера или службам

  • Управление питанием в устройствах

  • Реализующие пользовательские клиенты (интерфейсы устройства)

  • Доступ к памяти устройства

  • Регистрация и управление обработчиками прерываний

Этот раздел сначала описывает жизненный цикл объекта драйвера и роли АЙОЗЕРВАЙСА в том жизненном цикле. Тогда это суммирует каждого другого майора Айозервайса APIs.

Жизненный цикл объекта драйвера

Драйвер Набора I/O может быть загружен и разгружен, или активирован и деактивирован в любое время. Каждый водительский жизненный цикл следует за тем же образцом, как размечено в ряде функций, определяемых стандартным суперклассом драйвера, IOService. Некоторые из этих функций должны быть реализованы драйвером; другие реализованы IOService, но могут быть переопределены драйвером для дополнительной гибкости.

  Драйвер рисунка 5-2 возражает функциям жизненного цикла
Driver object life-cycle functions

Рисунок 5-2 показывает последовательность функций, вызывающуюся во время жизни объекта драйвера. Строки заключения в скобки показывают, как эти функции сгруппированы в дополнительных пар. Класс объекта драйвера может переопределить любую из этих функций, но, несомненно, должен будет вызвать реализацию суперкласса той же самой функции в надлежащей точке в ее собственной реализации. Например, когда Вы переопределяете вводную функцию дополнительной пары, такой как init или start, Ваша версия должна вызвать соответствующую функцию своего суперкласса прежде, чем сделать ее собственную инициализацию, как показано в Перечислении 5-1 при переопределении заключительной функции, такой как free или stop, необходимо выполнить собственную очистку прежде, чем вызвать соответствующую функцию в суперклассе, как показано в Перечислении 5-3

Драйвер, соответствующий и загружающийся

Первая группа функций —init, attach, probe, и detach— вызывается во время процесса соответствия драйвера и загрузки. Этот процесс происходит во время начальной загрузки, и в любое время устройства добавлены или демонтированы. Следующие параграфы суммируют процесс соответствия, обращая особое внимание на включенные функции; посмотрите, что Драйвер Соответствует и Загружается для расширенного обсуждения процесса.

Когда поставщик услуг обнаруживает устройство, процесс соответствия начат. Обычно этот провайдер является драйвером контроллера для шины (такой как шина PCI), который обнаруживает устройство путем сканирования его шины. Провайдер (обычно через его семью) тогда создает и регистрирует любые требуемые куски путем вызывания функции IOService, registerService; этот вызов, в свою очередь, инициировал процесс соответствия.

Как организовано IOService, Набор I/O находит и загружает драйвер для куска в трех отличных фазах, с помощью отнимающего процесса. В каждой фазе драйверы, не считающиеся вероятными кандидатами на соответствие, вычтены из общего пула возможных кандидатов, пока не найден успешный кандидат. Фазы:

  1. Класс, соответствующий — Набор I/O, устраняет любые драйверы неправильного провайдера (кусок) класс.

  2. Пассивное соответствие — Набор I/O исследует лица остающихся драйверов на специфичные для семьи свойства.

  3. Активное соответствие — IOService вызывает каждый из остающихся драйверов probe функции в отношении объекта драйвер являются соответствующими против. Эта функция позволяет драйверу связываться с устройством и проверять, что это может фактически управлять тем устройством. Тестовый счет возвращается, который отражает, как хорошо подходящий драйвер должен управлять устройством.

То, когда соответствующий драйвер найден, его код загружается, и экземпляр основного класса перечислен в индивидуальности, создается.

В первых двух фазах ни одна из водительских функций жизненного цикла не вызвана. Это только во время третьей стадии активного соответствия, когда драйвер просят зондировать устройство для пригодности, что вызывается первая группа функций.

Загружают ли драйвер для управления устройством или просто просят зондировать его, первая вызванная функция жизненного цикла init, который является libkern эквивалентом функции конструктора для класса. Для драйверов Набора I/O эта функция берет в качестве собственного параметра OSDictionary, содержащий соответствующие свойства от индивидуальности в XML-файле. Драйвер может использовать это для определения, для какой определенной индивидуальности он был загружен, определите уровень диагностического вывода, чтобы произвести, или иначе установить основные операционные параметры. Однако драйверы Набора I/O обычно не переопределяют init функция, выполняя их инициализацию на более поздних этапах, как описано ниже. Для больше на init, и связанное free функционируйте, посмотрите Создание объекта и Размещение (OSObject)

Прежде чем объект драйвера может или зондировать или запуститься, он должен быть присоединен в Реестр I/O. Чтобы сделать это, кусок вызывает водительское attach функция, присоединяющая драйвер к куску через Реестр I/O. Дополнительная функция detach удаляет драйвер из его куска. IOService дает обе из этих реализаций по умолчанию функций. Драйвер может переопределить их, но редко должен делать так.

Если активное соответствие происходит, кусок затем вызывает функцию зонда объекта драйвера. Тестовая функция возвращает IOService. Это обычно - сам объект драйвера, но драйвер может возвратить экземпляр различного класса, такого как специализированный подкласс, включенный в водительский пакет расширения ядра. Реализация по умолчанию IOSERVICE зонда просто возвращает этот указатель, не изменяя тестовый счет. Переопределение зонда является дополнительным; большинство драйверов получает достаточно информации от соответствия свойства и не должно переопределять его. При переопределении зонда, однако, необходимо удостовериться, что зонд не является разрушительным, оставляя устройство в состоянии, это нашло его. Спецификации оборудования обычно определяют, как провести неразрушающие зонды.

Водительское startфункция, так же, как с реализациями probe, должен выполнить только минимальное необходимое выделение системных ресурсов, чтобы проверить, что оно может управлять аппаратными средствами. Этот консервативный подход задерживает потребление ресурсов ядра, пока они не фактически необходимы.

Каждая семья, такая как PCI, USB, или хранение, определяет пару функций активации и деактивации, чтобы указать, что драйвер должен подготовиться к запросам службы I/O и что больше не необходимы водительские службы. Эти две функции обычно называют open и close. Большинство драйверов реализует эти функции, чтобы выделить и освободить все необходимые буферы и другие структуры в подготовке к обработке I/O.

Некоторые семьи определяют дополнительные уровни активации и деактивации. Сетевой драйвер, например, выполняет в очень мало open и close, вместо этого выполняя установку и разрушение в enable и disable функции. Безотносительно определенных функций активации и деактивации они могут быть вызваны много раз во время водительской продолжительности жизни; драйвер должен быть в состоянии функционировать независимо от того, сколько раз он активируется или деактивировался.

Изменение состояния драйвера

Другая функция, которая может быть вызвана много раз во время водительской продолжительности жизни, messageфункция. Эта функция сообщает драйверу важных изменений состояния системы, такой как тогда, когда диск насильственно удален, когда изменение управления питанием (сон, пробуждение) происходит, или когда закрывается драйвер. Реализация IOSERVICE этой функции ничего не делает и возвращает «неподдерживаемый» код результата. Для больше на уведомлении и обменивающейся сообщениями функциональности, предоставленной IOService, см. Уведомление и Обмен сообщениями

Завершение работы драйвера

Когда драйвер будет постоянно завершением работы, message функция вызывается с оконечным сообщением (kIOMessageServiceIsTerminated). Если драйвер принимает завершение, stop функция тогда вызывается. Драйвер должен реализовать stopфункционируйте, чтобы закрыть, выпустить, или свободный любые ресурсы это открыло или создало в start функция, и оставить аппаратные средства в состоянии драйвером первоначально нашла его. Принятие драйвера реализует функции активации и деактивации, существует обычно мало для выполнения stop функция. Заключительный этап завершения работы драйвера является вызовом free, когда подсчет ссылок объекта драйвера достигает нуля, который происходит. В этой функции драйвер может избавиться от любых ресурсов, которые это создало в init функция.

Соответствие провайдера

При реализации объекта драйвера провайдера (т.е. подкласс, который является элементом семьи I/O Kit) можно хотеть переопределить IOService’s matchPropertyTable функция членства. Когда IOService выполняет соответствие для объекта драйвера, это вызывает этот метод, таким образом, класс провайдера может реализовать свои собственные определенные критерии соответствия в дополнение к универсальным, предоставленным IOService. Провайдер должен исследовать соответствующий словарь, переданный, чтобы видеть, содержит ли это свойства, семья понимает для соответствия, и используйте их для соответствия с определенным объектом драйвера, если это действительно понимает их.

Уведомление и обмен сообщениями

IOService обеспечивает два механизма для объектов драйвера связаться друг с другом и с Набором I/O: уведомления и обмен сообщениями. Уведомления поставлены заинтересованным клиентам, когда определенное событие имеет место с активной службой или драйвером, имеющим свойства, соответствующие данный словарь. Сообщения более предназначены и текут в одном направлении от провайдера до клиента. Любой провайдер может отправить сообщение любому из его клиентов для уведомления его относительно некоторого изменения в среде выполнения.

Как обсуждено ранее в драйвере Жизненного цикла Объекта Драйвера клиенты реализуют messageфункция, чтобы получить и реагировать на сообщения от их провайдеров. Эта функция позволяет им адаптироваться к изменениям в среде выполнения. Сообщения могут сообщить им об изменениях в состоянии системы, таких как изменения в состоянии электропитания, приостановке службы или нависших завершениях службы. Провайдеры реализуют messageClient(или messageClients) функции для отправки сообщений путем вызова их клиента message методы. В то время как другие могут быть определены семьями, Набор I/O определяет некоторые сообщения. Посмотрите заголовочный файл Kernel.framework/Headers/IOKit/IOMessage.h для универсальных сообщений, что messageClient и messageClients функции могут поставить драйверу.

Широковещательная передача уведомлений немного более сложна. Любой объект драйвера может установить обработчик уведомления через addNotification или installNotification функции. Обработчик уведомления устанавливается, чтобы быть вызванным, когда определенный объект драйвера (идентифицированный словарем соответствия свойств) испытывает определенный тип изменения состояния, такой как тогда, когда драйвер сначала публикуется, соответствующий в любое время или завершается. Каждому обработчику уведомления также дают приоритетное число в случае, если многократные уведомления о том же типе и для того же объекта инициированы одновременно.

Обработчик уведомления (типа IOServiceNotificationHandler) вызывается, если какой-либо драйвер возражает, чья индивидуальность соответствует предоставленные изменения словаря соответствия в указанном состоянии. Например, когда поставщик услуг вызывает registerServices, это не только запускает процесс регистрации, но и он также поставляет уведомления всем зарегистрированным клиентам, заинтересованным публикацией провайдера. Запрос уведомления идентифицируется экземпляром объекта IONotifier, через который уведомление может быть включено, отключено или удалено.

Средства доступа драйвера

IOService включает, как удобство, много функций членства средства доступа, предоставляющих быстрый доступ к состоянию объекта драйвера и объектам, которые тесно связаны с ним. Эти функции возвращают следующие объекты или значения:

  • Водительское состояние (getState), битовое поле, указывающее, неактивен ли драйвер, зарегистрирован, соответствующий и т.д.

  • Цикл работы, используемый драйвером (getWorkLoop) (см. События Обработки) для получения дополнительной информации),

  • Водительский основной провайдер (getProvider), а также OSIterator возражает для итерации по водительским провайдерам, если многократный (например, устройство RAID)

  • Водительский основной клиент (getClient), а также OSIterator возражает для итерации по водительским клиентам, если многократный

Другие функции IOService

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

  • Пользовательский клиент. newUserClientфункция создает находящееся в IOUserClient соединение для связи с клиентом неядра; клиент вызывает эту функцию путем вызова IOServiceOpenфункция платформы Набора I/O.

  • Память устройства. Несколько функций членства IOService получают, отображают и устанавливают диапазоны физической памяти, выделенные устройству с отображенной памятью. Эти функции предназначаются для объектов драйвера, которые являются клиентами устройств PCI.

  • Обработка прерываний. IOService обеспечивает низкоуровневые функции для регистрации, нерегистрации, управления и доступа к обработчикам прерываний, которых вызывают в основное время прерывания для прерывания устройства. Функции обеспечивают механизм для установки обработчиков прерываний, который не является на основе цикла работы.