Kobjects

Piotr Leszczyński

Wstęp

Struktura Kobject pojawiła się po raz pierwszy w jądrze linuxa w wersji 2.5.45. Początkowo miał być to zunifikowany sposób na (prawie) automatyczne liczenie referencji do dowolnych obiektów. Jednak z czasem Kobject'y doczekały się dodatkowych funkcjonalności, takich jak reprezentowanie posiadanych w sobie obiektów w systemie sysfs. Generalnie w kodzie jądra linuxa nie spotyka się samych Kobject'ów, tylko złożone struktury, w skład których wchodzi Kobject.

Podstawowe zastosowanie

Kobject'y stosuje się przede wszystkim jako:

Dołączanie Kobject'ów do innych struktur

Kobject jest tak naprawdę typu struct kobject i jest zdefiniowany w pliku <linux/kobject.h> wraz ze wszystkimi pomocnymi funkcjami.

Aby dołączyć Kobject do naszej struktury dodajemy do niej dodatkowe pole typu struct kobject. Przykładowa struktura z dołączonym do niej Kobject'em reprezentująca urządzenie znakowe w jądrze:

struct cdev {
    struct kobject kobj;
    struct module *owner;
    struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};
Sposób dołączania pola kobject jest analogiczny do dołączania pola list_head i w analogiczny sposób możemy konwertować wskaźniki typu struct kobject* do interesującej nas struktury. Makro za to odpowiedzialne ma następującą składnię:
container_of(pointer, type, member)
Dla przykładowej struktury cdev jego wykorzystanie wygląda następująco:
struct cdev *device = container_of(kp, struct cdev, kobj);

Inicjalizacja Kobject'ów

Kobject przed pierwszym musi zostać zaincjalizowany. Realizuje się to używając poniższej funkcji:

void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
Jedną z najważniejszych rzeczy, jakie robi funkcja kobject_init jest ustawienie licznika referencji na 1.

Kolejnym krokiem koniecznym do pełnego zainicjalizowania Kobjecta jest ustawienie mu nazwy używanej w systemie sysfs. Realizuje się to w sposób następujący:

int kobject_set_name(struct kobject *kobj, const char *format, ...);
gdzie format jest listą argumentów analogiczną do tej występującej w funkcji printf.

Zliczanie referencji

W języku C nie da się automatycznie zliczać referencji, poniewąż nie jesteśmy w stanie automatycznie wychwycić momentu, w którym dany obiekt przestaje być używany, bądź też zaczyna być używany w innym miejscu. W związku z tym musimy zliczać referencje ręcznie. W przypadku mechanizmu Kobject wykonuje się to w sposób następujący:

Zwalnianie obiektów przypisanych do Kobject'ów

Ponieważ funkcja kobject_put przyjmuje argument struct kobject*, nie jest ona w stanie dowiedzieć się, jakiego typu jest przypisany do niego obiekt, a co za tym idzie nie potrafi go poprawnie zwolnić. Metoda rozwiązywania tego problemu polega na zdefiniowaniu funkcji zwalniającej przypisany obiekt w drugim argumencie typu struct ktype* przekazywanym podczas inicjalizacji Kobject'a.

Przykładowa realizacja tej metody może wyglądać następująco. Przypuśćmy, że obiekt nad którym pracujemy ma typ struct my_object. Najbardziej standardowa funkcja wyłuskująca nasz obiekt z Kobject'a, a następnie usuwająca go wygląda następująco:

void my_object_release(struct kobject *kobj)
{
    struct my_object *mine = container_of(kobj, struct my_object, kobj);
    kfree(mine);
}
Powyższą funkcję przekazujemy jako pole release struktury kobj_type, która wygląda następująco:
struct kobj_type {
    void (*release)(struct kobject *);
    struct sysfs_ops *sysfs_ops;
    struct attribute **default_attrs;
};
A następnie obiekt typu struct kobj_type* przekazujemy do kobject_init jako drugi argument.

Pełny przykład na wykorzystanie zliczania referencji

struct my_object* mine = kmalloc(sizeof(struct my_object), GFP_KERNEL);
struct kobj_type* ktype = kmalloc(sizeof(struct kobj_type), GFP_KERNEL);
ktype->release = my_object_release;
kobject_init(&mine->kobj, ktype);

// tutaj wykonujemy jakieś operacje z wykorzytaniem mine

kobject_put(mine);

Zbiory Kobject'ów - Kset

Kset jest po prostu kontenerem na Kobject'y. Kobject'y należące do tego samego Kset'a mogą być różnego typu, jednak fakt, że na zewnątrz są widoczne jako obiektu typu struct kobject* sprawia, że nie można ich wtedy rozróżnić, co bardzo utrudnia pracę z takim modelem.

Kset dodatkowo po utworzeniu i zarejestrowaniu reprezentuje folder w systemie sysfs, w którym leżą pliki będące Kobject'ami należącymi do tego Kset'a.

Tworzenie Kset'ów

Obiekt Kset tworzy się i rejestruje przy pomocy funkcji:

struct kset *kset_create_and_add(char *name, struct kset_uevent_ops *u, struct kobject *parent);
która tworzy katalog o nazwie name będącym podkatalogiem reprezentowanym przez parent.

Aby usunąć nieużywanego Kset'a należy użyć:

void kset_unregister(struct kset *kset);

Dodawanie Kobject'a do Kset'a

Dodawanie Kobject'a do Kset'a odbywa się w dwóch krokach:

Dodatkowe operacje na Kset'ach

Dla Kseta istnieją podobne funkcje jak dla Kobject'a: