Первый шаг при работе с объектами kobject — это их декларация и инициализация. Инициализируются объекты kobject с помощью функции kobject_init(), которая определена в файле <linux/kobject.h> следующим образом.
void kobject_init(struct kobject *kobj);
Единственным параметром этой функции является объект kobject, который необходимо проинициализировать. Перед вызовом этой функции область памяти, в которой хранится объект, должна быть заполнена нулевыми значениями. Обычно это делается при инициализации большой структуры данных, в которую встраивается объект kobject. В других случаях просто необходимо вызвать функцию memset().
memset(kobj, 0, sizeof(*kobj));
После заполнения нулями безопасным будет инициализация полей parent и kset, как показано в следующем примере.
kobj = kmalloc(sizeof(*kobj), GFP_KERNEL);
if (!kobj)
return -ENOMEM;
memset(kobj, 0, sizeof(*kobj));
kobj->kset = kset;
kobj->parent = parent_kobj;
kobject_init(kobj);
После инициализации необходимо установить имя объекта с помощью функции kobject_set_name(), которая имеет следующий прототип.
int kobject_set_name(struct kobject* kobj,
const char* fmt, ...);
Эта функция принимает переменное количество параметров, по аналогии с функциями printf() и printk(). Как уже было сказано, на имя объекта указывает поле k_name структуры kobject. Если это имя достаточно короткое, то оно хранится в статически выделенном массиве name, поэтому есть смысл без необходимости не указывать длинные имена.
После того как для объекта выделена память и объекту присвоено имя, нужно установить значение его поля kset, а также опционально поле ktype. Последнее необходимо делать только в том случае, если множество kset не предоставляет типа ktype для данного объекта, в противном случае значение поля ktype, которое указано в структуре kset, имеет преимущество. Если интересно, почему объекты kobject имеют свое поле ktype, то добро пожаловать в клуб!
Счетчики ссылок
Одно из главных свойств, которое реализуется с помощью объектов kobject, — это унифицированная система поддержки счетчиков ссылок. После инициализации количество ссылок на объект устанавливается равным единице. Пока значение счетчика ссылок на объект не равно нулю, объект существует в памяти, и говорят, что он захвачен (pinned, буквально, пришпилен). Любой код, который работает с объектом, вначале должен увеличить значение счетчика ссылок. После того как код закончил работу с объектом, он должен уменьшить значение счетчика ссылок. Увеличение значения счетчика называют захватом (getting), уменьшение — освобождением (putting) ссылки на объект. Когда значение счетчика становится равным нулю, объект может быть уничтожен, а занимаемая им память освобождена.
Увеличение значения счетчика ссылок выполняется с помощью функции kobject_get().
struct kobject* kobject_get(struct kobject *kobj);
Эта функция возвращает указатель на объект kobject в случае успеха и значение NULL в случае ошибки.
Уменьшение значения счетчика ссылок выполняется с помощью функции kobject_put().
void kobject put(struct kobject *kobj);
Если значение счетчика ссылок объекта, который передается в качестве параметра, становится равным нулю, то вызывается функция, на которую указывает указатель release поля ktype этого объекта.
Структуры kref
Внутреннее представление счетчика ссылок выполнено с помощью структуры kref, которая определена в файле <linux/kref.h> следующим образом.
struct kref {
atomic_t refcount;
};
Единственное поле этой структуры — атомарная переменная, в которой хранится значение счетчика ссылок. Структура используется просто для того, чтобы выполнять проверку типов. Чтобы воспользоваться структурой kref, необходимо ее инициализировать с помощью функции kref_init().
void kref_init(struct kref *kref) {
atomic_set(&kref->refcount, 1);
}
Как видно из определения, эта функция просто инициализирует атомарную переменную тина atomic_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);
}
Эта функция уменьшает значение счетчика ссылок на единицу и вызывает функцию release(), которая передастся ей в качестве параметра, когда значение счетчика ссылок становится равным нулю. Как видно из использованного выражения WARN_ON(), функция release() не может просто совпадать с функцией kfrее(), а должна быть специальной функцией, которая принимает указатель на структуру struct kref в качестве своего единственного параметра и не возвращает никаких значений.
Вместо того чтобы разрабатывать свои функции управления счетчиками ссылок на основании типа данных atomic_t, настоятельно рекомендуется использовать тип данных kref и соответствующие функции, которые обеспечивают общий и правильно работающий механизм поддержки счетчиков ссылок в ядре.
Все эти функции определены в файле lib/kref.c и объявлены в файле <linux/kref.h>.
Файловая система sysfs
Файловая система sysfs — это виртуальная файловая система, которая существует только в оперативной памяти и позволяет просматривать иерархию объектов kobject. Она позволяет пользователям просматривать топологию устройств операционной системы в виде простой файловой системы. Атрибуты объектов kobject могут экспортироваться в виде файлов, которые позволяют считывать значения переменных ядра, а также опционально записывать их.