Второе издание
Скачать 3.09 Mb.
|
Экспортируемые символы При загрузке модули динамически компонуются с ядром. Так же как и в случае динамически загружаемых бинарных файлов пространства пользователя, в коде мо- дулей могут вызываться только те функции ядра (основного образа или других мо- дулей), которые явно экспортируются для использования. В ядре экспортирование осуществляется с помощью специальных директив EXPORT_SYMBOL () и EXPORT_ SYMBOL_GPL(). Функции, которые экспортируются, доступны для использования модулями. Функции, которые не экспортируются, не могут быть вызваны из модулей. Правила компоновки и вызова функций для модулей значительно более строгие, чем для основного образа ядра. Код ядра может использовать любые интерфейсы ядра (кро- ме тех, которые определены с ключевым словом s t a t i c ) , потому что код ядра ком- понуется в один выполняемый образ. Экспортируемые символы, конечно, тоже не должны определяться как s t a t i c . Набор символов ядра, которые экспортируются, называется экспортируемым ин- терфейсом ядра или даже (здесь не нужно удивляться) API ядра. Модули 353 Экспортировать символы просто. После того как функция определена, необходи- мо вызвать директиву EXPORT_SYMBOL (). /* * get_pirate_beard_color — возвратить значение цвета бороды текущего * пирата pirate — это глобальная переменная, доступная из данной * функции цвета определены в файле */ int get_pirate_beard_color(void) { return pirate->beard->color; } EXPORT_SYMBOL(get_pirate_beard_color); Допустим, что функция g e t _ p i r a t e _ b e a r d _ c o l o r ( ) объявлена в заголовочном файле и ее может использовать любой модуль. Некоторые разработчики хотят, чтобы их интерфейсы были доступны только для модулей с лицензией GPL. Такая возможность обеспечивается компоновщиком ядра с помощью макроса MODULE_LICENSE (). Если есть желание, чтобы рассматриваемая функция была доступна только для модулей, которые помеченные как соответствую- щие лицензии GPL, то экспортировать функцию можно следующим образом. EXPORT_SYMBOL_GPL(get_pirate_beard_color); Если код ядра конфигурируется для компиляции в виде модуля, то необходимо гарантировать, что все используемые интерфейсы экспортируются. В противном случае будут возникать ошибки компоновщика и загружаемый модуль не будет рабо- тать. Вокруг модулей В этой главе были рассмотрены особенности написания, сборки, загрузки и вы- грузки модулей ядра. Мы обсудили, что такое модули и каким образом ядро опе- рационной системы Linux, несмотря на то что оно является монолитным, может загружать код динамически. Были также рассмотрены параметры модулей и экспор- тируемые символы. На примере воображаемого модуля ядра (драйвера устройства) управления удочкой был показан процесс написания модуля и процесс добавления к нему различных возможностей, таких как внешние параметры. В следующей главе будут рассмотрены объекты k o b j e c t и файловая система sysf's, которые являются основным интерфейсом к драйверам устройств и, следовательно, к модулям ядра. 354 Глава 16 17 Объекты kobject и файловая система sysfs У нифицированная модель представления устройств — это существенно новая осо- бенность, которая появилась в ядрах серии 2.6. Модель устройств— это еди- ный механизм для представления устройств и описания их топологии в системе. Использование единого представления устройств позволяет получить следующие преимущества. • Уменьшается дублирование кода. • Используется механизм для выполнения общих, часто встречающихся функ- ций, таких как счетчики использования. • Появляется возможность систематизации всех устройств в системе, возмож- ность просмотра состояний устройств и определения, к какой шине то или другое устройство подключено. • Появляется возможность генерации полной и корректной информации о древо- видной структуре всех устройств в системе, включая все шины и соединения. • Обеспечивается возможность связывания устройств с их драйверами и наоборот. • Появляется возможность разделения устройств ва категории в соответствии с различными классификациями, таких как устройства ввода, без знания физиче- ской топологии устройств. • Обеспечивается возможность просмотра иерархии устройств от листьев к кор- ню и выключения питания устройств в правильном порядке. Последний пункт был самой первой мотивацией необходимости создания общей модели представления устройств. Для того чтобы реализовать интеллектуальное управление электропитанием в ядре, необходимо построить дерево, которое пред- ставляет топологию устройств в системе. Для выключения питания устройств, ко- торые организованы в виде древовидной топологии, ориентированной сверху вниз, ядро должно выключить питание нижних узлов (листьев) перед выключением пита- ния верхних узлов. Например, ядро должно выключить питание USB-мыши перед тем, как выключать питание контроллера шины USB, а питание контроллера шины USB должно быть выключено перед выключением питания шины PCI. Чтобы делать это эффективно и правильно для всей системы, ядру необходимо отслеживать топо- логию дерева всех устройств в системе. Объекты kobject Сердцем модели представления устройств являются объекты kobject, которые представляются с помощью структуры s t r u c t k o b j e c t , определенной в файле < l i n u x / k o b j e c t . h > . Тип kobject аналогичен классу Object таких объектно-ори- ентированных языков программирования, как С# и Java. Этот тип определяет общую функциональность, такую как счетчик ссылок, имя, указатель на родительский объ- ект, что позволяет создавать объектную иерархию. Структура, с помощью которой реализованы объекты kobject, имеет следующий вид. struct kobject { char *k_name; char name[KOBJ_NAME_LEN]; struct kref kref; struct list_head entry; struct kobject *parent; struct kset *kset; struct kobj_type *ktype; struct dentry *dentry; }; Поле k_name содержит указатель на имя объекта. Если длина имени меньше KOBJ_NAME_LEN, что сейчас составляет 20 байт, то имя хранится в массиве name, a ноле kname указывает на первый элемент этого массива. Если длина имени больше KOBJ_NAME_LEN байт, то динамически выделяется буфер, размер которого достато- чен для хранения строки символов имени, имя записывается в этот буфер, а поле k_name указывает на него. Указатель p a r e n t указывает на родительский объект данного объекта kobject. Таким образом, с помощью структур k o b j e c t может быть создана иерархия объ- ектов в ядре, которая позволяет устанавливать соотношения родства между раз- личными объектами. Как будет видно дальше, с помощью файловой системы sysfs осуществляется представление в пространстве пользователя той иерархии объектов kobject, которая существует в ядре. Указатель d e n t r y содержит адрес структуры s t r u c t dentry, которая представ- ляет этот объект в файловой системе sysfs. Поля kref, ktype и k s e t указывают на экземпляры структур, которые использу- ются для поддержки объектов kobject. Поле e n t r y используется совместно с полем kset. Сами эти структуры и их использование будут обсуждаться ниже. Обычно структуры kobject встраиваются в другие структуры данных и сами по себе не используются. Например, такая важная структура, как s t r u c t cdev, имеет поле kobj. 356 Глава 17 /* структура cdev - объект для представления символьных устройств */ struct cdev { struct kobject kobj; struct module *owner; struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; }; Когда структуры kobject встраиваются в другие структуры данных, то последние получают те стандартизированные возможности, которые обеспечиваются структу- рами kobject. Еще более важно, что структуры, которые содержат в себе объекты kobject, становятся частью объектной иерархии. Например, структура cdev пред- ставляется в объектной иерархии с помощью указателя на родительский объект cdev->kobj->parent и списка cdev->kobj->entry. Типы ktype Объекты kobject могут быть связаны с определенным типом, который называ- ется ktype. Типы ktype представляются с помощью структуры s t r u c t kobj_type, определенной в файле struct kobj_type { void (*release)(struct kobject * ) ; struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; }; Тип ktype имеет простое назначение— представлять общее поведение для не- которого семейства объектов kobject. Вместо того чтобы для каждого отдельного объекта задавать особенности поведения, эти особенности связываются с их полем ktype, и объекты одного "типа" характеризуются одинаковым поведением. Поле r e l e a s e содержит указатель на деструктор, который вызывается, когда ко- личество ссылок на объект становится равным нулю. Эта функция отвечает за осво- бождение памяти, связанной с объектом, и за другие операции очистки. Поле sysfs_ops указывает на структуру sysfs_ops. Эта структура определяет поведение файлов на файловой системе sysfs при выполнении операций записи и чтения. Более детально она рассматривается в разделе "Добавлепие файлов на фай- ловой системе sysfs". Наконец, поле d e f a u l t _ a t t r s указывает на массив структур a t t r i b u t e . Эти структуры определяют атрибуты, которые связаны с объектом kobject и использу- ются но умолчанию. Атрибуты соответствуют свойствам данного объекта. Если не- который объект kobject экспортируется через файловую систему sysfs, то атрибуты экспортируются как отдельные файлы. Последний элемент этого массива должен со- держать значению NULL. Объекты kobject и файловая система sysfs 357 Множества объектов k s e t Множества kset представляют собой коллекции объектов kobject. Множество kset работает как базовый контейнерный класс для объектов, например, "все блоч- ные устройства". Множества kset очень похожи на типы ktype, и возникает вопрос: "Для чего нужны два разных обобщения?" Множество kset объединяет несколько объектов kobject, а типы ktype определяют общие свойства, которые связаны с объектами k o b j e c t одного типа. Существует возможность объединить объекты одного типа ktype в различные множества kset. Поле kset объекта kobject указывает на связанное с данным объектом множе- ство kset. Множество объектов k s e t представляется с помощью структуры kset, которая определена в файле struct kset { struct subsystem *subsys; struct kobj_type *ktype; struct list_head list; struct kobject kobj; struct kset_hotplug_ops *hotplug_ops; }; Указатель ktype указывает на структуру ktype, которая определяет тип всех объ- ектов данного множества, поле l i s t - список всех объектов kobject данного мно- жества, поле kobj — объект kobject, который представляет базовый класс для всех объектов данного множества, а поле hotplug_ops указывает на структуру, которая определяет поведение объектов kobject при горячем подключении устройств, свя- занных с данным множеством. Наконец, поле sybsys указывает на структуру s t r u c t subsystem, которая связа- на с данным множеством kset. Подсистемы Подсистемы используются для представления высокоуровневых концепций ядра и являются коллекцией одного или нескольких множеств kset. Множества kset со- держат объекты kobject, подсистемы — множества kset, но связь между множества- ми в подсистеме значительно более слабая, чем связь между объектами kobject в множестве. Множества kset одной подсистемы могут иметь только наиболее общие объединяющие факторы. Несмотря на их важную роль, подсистемы представляются с помощью очень про- стой структуры данных — s t r u c t subsystem. struct subsystem { struct kset ksot; struct rw_semaphore rwsem; }; Структура subsystem содержит только одно множество kset, тем не менее не- сколько множеств kset могут указывать на общую структуру subsystem с помощью 358 Глава 17 поля subsys. Такие однонаправленные взаимоотношения означают, что нет возмож- ности определить все множестпа подсистемы, только имея ее структуру subsystem. Поле k s e t , которое содержится в структуре subsystem, — это множество k s e t подсистемы, которое используется по умолчанию, чтобы зафиксировать положение этой подсистемы в иерархии объектов. Поле rwsem структуры s u b s y s t e m — это семафор чтения-записи (см. главу 9, "Средства синхронизации в ядре"), который используется для защиты подсистемы и ее множеств k s e t от конкурентного доступа. Все множества k s e t должны принадле- жать какой-нибудь подсистеме, поскольку они используют семафор подсистемы для защиты своих данных от конкурентного доступа. Путаница со структурами Те несколько структур, которые только что были описаны, приводят к путани- це не потому, что их много (только четыре) или они сложные (все они достаточно просты), а потому что они сильно друг с другом переплетаются. При использовании объектов k o b j e c t достаточно сложно рассказать об одной структуре, не упоминая другие. Тем не менее, на основании рассмотренных особенностей этих структур можно построить прочное понимание их взаимоотношений. Самым важным является объект k o b j e c t , который представляется с помощью структуры s t r u c t k o b j e c t . Структура k o b j e c t используется для представления наиболее общих объектных свойств структур данных ядра, таких как счетчик ссы- лок, взаимоотношения родитель-порожденный и имя объекта. С помощью структуры k o b j e c t эти свойства можно обеспечить одинаковым для всех стандартным спосо- бом. Сами по себе структуры k o b j e c t не очень полезны, они обычно встраиваются в другие структуры данных. С каждым объектом k o b j e c t связан один определенный тип данных— k t y p e , который представляется с помощью структуры s t r u c t k o b j _ t y p e . На экземпляр такой структуры указывает поле ktype каждого объекта kobject. С помощью типов ktype определяются некоторые общие свойства объектов: поведение при удалении объекта, поведение, связанное с файловой системой sysfs, а также атрибуты объекта. Объекты k o b j e c t группируются в множества, которые называются k s e t . Множества k s e t представляются с помощью структур данных s t r u c t k s e t . Эти множества предназначены для двух целей. Во-первых, они позволяют использовать встроенный в них объект k o b j e c t в качестве базового класса для группы других объектов k o b j e c t . Во-вторых, они позволяют объединять вместе несколько связан- ных между собой объектов k o b j e c t . На файловой системе sysfs объекты k o b j e c t представляются отдельными каталогами файловой системы. Связанные между собой каталоги, например все подкаталоги одного каталога, могут быть включены в одно множество k s e t . Подсистемы соответствуют большим участкам ядра и являются набором мно- жеств k s e t . Подсистемы представляются с помощью структур s t r u c t subsystem. Все каталоги, которые находятся в корне файловой системы sysfs, соответствуют подсистемам ядра. На рис. 17.1 показаны взаимоотношения между этими структурами данных. Объекты kobject и файловая система sysfs 359 Подсистема Подсистема Рис. 17.1. Взаимоотношения между объектами kobject, множествами kset и подсистемами Управление и манипуляции с объектами k o b j e c t Теперь, когда у нас уже есть представление о внутреннем устройстве объектов kobject и связанных с ними структурах данных, самое время рассмотреть экспор- тируемые интерфейсы, которые дают возможность управлять объектами kobject и выполнять с ними другие манипуляции. В основном, разработчикам драйверов не- посредственно не приходится иметь дело с объектами kobject. Структуры kobject встраиваются в некоторые специальные структуры данных (как это было в приме- ре структуры устройства посимвольного ввода-вывода) и управляются "за кадром" с помощью соответствующей подсистемы драйверов. Тем не менее, объекты kobject не всегда могут оставаться невидимыми, иногда с ними приходится иметь дело, как при разработке кода драйверов, так и при разработке кода управления подсистема- ми ядра. Первый шаг при работе с объектами kobject - это их декларация и инициали- зация. Инициализируются объекты kobject с помощью функции k o b j e c t _ i n i t () , которая определена в файле void kobject_init(struct kobject *kobj); Единственным параметром этой функции является объект kobject, который не- обходимо проинициализировать. Перед вызовом этой функции область памяти, в ко- торой хранится объект, должна быть заполнена нулевыми значениями. Обычно это делается при инициализации большой структуры данных, в которую встраивается объект kobject. В других случаях просто необходимо вызвать функцию memset (). memset(kobj, 0, sizeof (*kobj)); После заполнения нулями безопасным будет инициализация полей p a r e n t и kset, как показано в следующем примере. 360 Глава 17 kset kobj kobj kobj kobj kobj kset kobj kobj kobj kobj = kmalloc(sizeof (*kobj), GFP_KERNEL); if (!kobj) return -ENOMEM; memset(kobj, 0, sizeof (*kobj)); kobj->ksct - kset; kobj->parent = parent_kobj; kobject_init (kobj); После инициализации необходимо установить имя объекта с помощью функции k o b j e c t _ s e t _ n a m e ( ) , которая имеет следующий прототип. int kobject_set_name(struct kobject * kobj, const char * fmt, , . . . ) ; Эта функция принимает переменное количество параметров, по аналогии с функ- циями p r i n t f () и p r i n t k (). Как уже было сказано, на имя объекта указывает поле k_name структуры k o b j e c t . Если это имя достаточно короткое, то оно хранится в статически выделенном массиве name, поэтому есть смысл без необходимости не указывать длинные имена. После того как для объекта выделена память и объекту присвоено имя, нужно установить значение его поля k s e t , а также опционально поле k t y p e . Последнее необходимо делать только в том случае, если множество k s e t не предоставляет типа ktype для данного объекта, в противном случае значение поля ktype, которое указано в структуре k s e t , имеет преимущество. Если интересно, почему объекты k o b j e c t имеют свое поле ktype, то добро пожаловать в клуб! Счетчики ссылок Одно из главных свойств, которое реализуется с помощью объектов k o b j e c t , — это унифицированная система поддержки счетчиков ссылок. После инициализации количество ссылок на объект устанавливается равным единице. Пока значение счет- чика ссылок на объект не равно нулю, объект существует в памяти, и говорят, что он захвачен (pinned, буквально, пришпилен). Любой код, который работает с объектом, вначале должен увеличить значение счетчика ссылок. После того как код закончил работу с объектом, он должен уменьшить значение счетчика ссылок. Увеличение значения счетчика называют захватом (getting), уменьшение — освобождением (putting) ссылки на объект. Когда значение счетчика становится равным нулю, объект может быть уничтожен, а занимаемая им память освобождена. Увеличение значения счетчика ссылок выполняется с помощью функции kobject_get(). struct kobject * kobject_get(struct kobject *kobj); Эта функция возвращает указатель на объект kobject в случае успеха и значение NULL в случае ошибки. Уменьшение значения счетчика ссылок выполняется с помощью функции kobject_put(). void kobject put(struct kobject *kobj); Объекты kobject и файловая система sysfs 361 Если значение счетчика ссылок объекта, который передается в качестве параме- тра, становится равным нулю, то вызывается функция, на которую указывает указа- тель r e l e a s e поля ktype этого объекта. Структуры k r e f Внутреннее представление счетчика ссылок выполнено с помощью структуры kref, которая определена в файле struct kref{ atomic_t refcount; }; Единственное поле этой структуры — атомарная переменная, в которой хранится значение счетчика ссылок. Структура используется просто для того, чтобы выпол- нять проверку типов. Чтобы воспользоваться структурой kref, необходимо ее ини- циализировать с помощью функции k r e f _ i n i t () . void kref_init(struct kref *krcf) { atomic_set(&kref->refcount, 1 ) ; } Как видно из определения, эта функция просто инициализирует атомарную пере- менную тина a t o m i c _ t в значение, равное единице. Следовательно, структура kref является захваченной сразу же после инициализа- ции, так же ведут себя и объекты kobject. Для того чтобы захватить ссылку на структуру kref, необходимо использовать функцию kref_get (). void kref_get(struct kref *kref) { WARN_ON(!atomic_read(&kref->refcount)); atomic_inc(&kref->refcount); } Эта функция увеличивает значение счетчика ссылок на единицу. Она не возвра- щает никаких значений. Чтобы освободить ссылку на структуру kref, необходимо использовать функцию kref_put (). void kref_put(struct kref *kref, void (*release) (struct kref *kref)) { WARN_ON(release == NULL); WARN_ON(release == (void (*)(struct kref *))kfree); if (atomic dec_and_test (&kref->refcount)) release (kref); } Эта функция уменьшает значение счетчика ссылок на единицу и вызывает функ- цию r e l e a s e (), которая передастся ей в качестве параметра, когда значение счет- чика ссылок становится равным нулю. Как видно из использованного выражения WARM_ON (), функция r e l e a s e () не может просто совпадать с функцией k f r е е (), 362 Глава 17 а должна быть специальной функцией, которая принимает указатель на структуру s t r u c t kref в качестве своего единственного параметра и не возвращает никаких значений. Вместо того чтобы разрабатывать свои функции управления счетчиками ссылок на основании типа данных atomic_t, настоятельно рекомендуется использовать тип данных kref и соответствующие функции, которые обеспечивают общий и правиль- но работающий механизм поддержки счетчиков ссылок в ядре. Все эти функции определены в файле l i b / k r e f . c и объявлены в файле < l i n u x / k r e f . h > . Файловая система sysfs Файловая система sysfs — это виртуальная файловая система, которая существу- ет только в оперативной памяти и позволяет просматривать иерархию объектов kobject. Она позволяет пользопателям просматривать топологию устройств опера- ционной системы в виде простой файловой системы. Атрибуты объектов k o b j e c t могут экспортироваться в виде файлов, которые позволяют считывать значения пе- ременных ядра, а также опционально записывать их. Хотя изначально целью создания модели представления устройств было описа- ние топологии устройств системы для управления электропитанием, файловая си- стема sysfs стала удачным продолжением этой идеи. Для того чтобы упростить отлад- ку, разработчик унифицированной модели устройств решил экспортировать дерево устройств в виде файловой системы. Такое решение показало свою полезность вна- чале в качестве замены файлов, связанных с устройствами, которые раньше экспор- тировались через файловую систему /ргос, а позже в качестве мощного инструмен- та просмотра информации о системной иерархии объектов. Вначале, до появления объектов kobject, файловая система sysfs называлась driverfs. Позже стало ясно — новая объектная модель была бы очень кстати, и в результате этого появилась кон- цепция объектов kobject. Сегодня каждая система, на которой работает ядро 2.6, имеет поддержку файловой системы sysfs, и практически во всех случаях эта файло- вая система монтируется. Основная идея работы файловой системы sysfs — это привязка объектов kobject к структуре каталогов с помощью поля dentry, которое есть в структуре kobject. Вспомните из материала главы 12, "Виртуальная файловая система", что структура dentry используется для представления элементов каталогов. Связывание объектов с элементами каталогов проявляется в том, что каждый объект просто видится как каталог файловой системы. Экспортирование объектов k o b j e c t в виде файловой системы выполняется путем построения дерева элементов каталогов в оперативной памяти. Но обратите внимание, объекты kobject уже образуют древовидную струк- туру — нашу модель устройств! Поэтому простое назначение каждому объекту иерар- хии, которые уже образуют дерево в памяти, соответствующего элемента каталога позволяет легко построить файловую систему sysfs. На рис. 17.2 показан частичный вид файловой системы sysfs, которая смонтиро- вана на каталог /sys. Корневой каталог файлопой системы sysfs содержит семь подкаталогов: block, bus, c l a s s , devices, firmware, module и power. В каталоге block содержатся каталоги для каждого зарегистрированного в системе устройства блочного ввода-вывода. Объекты kobject и файловая система sysfs 363 |