kobject && kset

17907 ワード

Linux 2です.6その後、新しいデバイスモデルが提案され、新しいデバイスモデルの核心概念はカーネルオブジェクトとカーネルの集合であり、その上で、オブジェクト向けの思想を採用して多くの新しいデータ型、例えばデバイス、バスなどを提出し、各種の周辺デバイスに対して有効な管理を行う.
一、引用数:
Linuxカーネルの各オブジェクトには、linux/krefで参照カウンタstrut krefが含まれている.hファイル内:
struct kref {
    atomic_t refcount;
};

リファレンスカウンタは、原子操作を使用してリファレンスカウントの減算操作を完了します.基本的な操作は次のとおりです.
void kref_init(struct kref *kref); //  1
void kref_get(struct kref *kref); //  1
int kref_put(struct kref *kref, void (*release) (struct kref *kref)); //  1, 0, release 

実際のアプリケーションでは、参照カウントのデータ型がサポートされ、struct krefタイプのメンバーがネストされ、解放関数が提供されます.カーネルオブジェクトを後続の解析では、参照カウントがどのように使用されているかがよくわかります.
二、カーネルオブジェクトkobject:
カーネルオブジェクトはデバイスモデルの最も基本的なデータ型であり、各カーネルオブジェクトはsysfsファイルシステムのディレクトリに対応し、カーネルオブジェクトの親子関係はディレクトリの階層関係に対応している.
カーネルオブジェクトのデータ型はlinux/kobject.hヘッダファイル宣言:
struct kobject {
    const char  *name; //  , sysfs 
    struct list_head entry; //  
    struct kobject  *parent; // kobject 
    struct kset  *kset; //  
    struct kobj_type *ktype; // 
    struct sysfs_dirent *sd; //  sysfs 
    struct kref  kref; //  
    unsigned int state_initialized:1;
    unsigned int state_in_sysfs:1;
    unsigned int state_add_uevent_sent:1;
    unsigned int state_remove_uevent_sent:1;
};

カーネルオブジェクトを使用する場合は、まずカーネルオブジェクトを初期化し、カーネルに追加します.一般的な操作は次のとおりです.
//  
void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
//  
int kobject_set_name(struct kobject * kobj, const char * fmt, ...);
//  
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...);
 
//  3 , :
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
 struct kobject *parent, const char *fmt, ...);
 
//  kobj : 1, 0, kobject_put () 
void kobject_del(struct kobject *kobj);

また、kobjectを動的に作成するインタフェースも用意されています.
struct kobject *kobject_create(void);
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent);

カーネルオブジェクトの参照カウント操作:
1)参照数を増やす:
struct kobject *kobject_get(struct kobject *kobj)
{
    if (kobj)
        kref_get(&kobj->kref); // get 
    return kobj;
}

2)参照カウントを減らす:参照カウントを0に減らすとkobject_が自動的に呼び出されます.release()メソッド
void kobject_put(struct kobject *kobj)
{
    if (kobj) {
        if (!kobj->state_initialized)
            WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
               "initialized, yet kobject_put() is being "
               "called.
", kobject_name(kobj), kobj);         kref_put(&kobj->kref, kobject_release); //  put     } }

カーネルオブジェクトの登録と解放関数の内部実装を簡単に分析します.
    1.カーネルオブジェクトkobjectの登録_add():登録する前にkobject_を呼び出す必要があります.Init()関数の初期化
int kobject_add(struct kobject *kobj, struct kobject *parent,
        const char *fmt, ...)
{
    va_list args;
    int retval;                                                                                                                           

    if (!kobj)
        return -EINVAL;

    if (!kobj->state_initialized) { // 
        printk(KERN_ERR "kobject '%s' (%p): tried to add an "
               "uninitialized object, something is seriously wrong.
",                kobject_name(kobj), kobj);         dump_stack();         return -EINVAL;     }             va_start(args, fmt);     retval = kobject_add_varg(kobj, parent, fmt, args);     va_end(args);     return retval; } static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,                 const char *fmt, va_list vargs) {     int retval;     //      retval = kobject_set_name_vargs(kobj, fmt, vargs);     if (retval) {         printk(KERN_ERR "kobject: can not set name properly!
");         return retval;     }     kobj->parent = parent;     return kobject_add_internal(kobj); }

カーネルオブジェクト登録の本当の操作はkobject_にあるようです.add_internal()関数の内部で完了しました.
static int kobject_add_internal(struct kobject *kobj)
{
    int error = 0;
    struct kobject *parent;

    if (!kobj)
        return -ENOENT;

    if (!kobj->name || !kobj->name[0]) { //  , 
        WARN(1, "kobject: (%p): attempted to be registered with empty "
             "name!
", kobj);         return -EINVAL;     }     parent = kobject_get(kobj->parent); //     /* join kset if set, use it as parent if we do not already have one */     //  , kset     if (kobj->kset) {         if (!parent)             parent = kobject_get(&kobj->kset->kobj);         kobj_kset_join(kobj);         kobj->parent = parent;     }     pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'
",          kobject_name(kobj), kobj, __func__,          parent ? kobject_name(parent) : "<NULL>",          kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");     error = create_dir(kobj); // sysfs kobj     if (error) {         kobj_kset_leave(kobj);         kobject_put(parent);         kobj->parent = NULL;         /* be noisy on error issues */         if (error == -EEXIST)             printk(KERN_ERR "%s failed for %s with "                    "-EEXIST, don't try to register things with "                    "the same name in the same directory.
",                    __func__, kobject_name(kobj));         else             printk(KERN_ERR "%s failed for %s (%d)
",                    __func__, kobject_name(kobj), error);         dump_stack();     } else         kobj->state_in_sysfs = 1; //  sysfs     return error; }

大まかなコードロジックにはコメントがあり、各kobjectタイプのカーネルオブジェクトがsysfsファイルシステムのsysfs_と表示されます.direntオブジェクト対応!この関係は後続の分析sysfsファイルシステムで見られますが、ここでは分析しません.
2.カーネルオブジェクトkobject_を解放するput(): 
前にkobject_が見えましたput()メソッドは、カーネルオブジェクトの参照カウントが0の場合にkobejct_を呼び出すリリース()メソッド:
static void kobject_release(struct kref *kref)                                                                                            
{
    kobject_cleanup(container_of(kref, struct kobject, kref));
}

/*
 * kobject_cleanup - free kobject resources.
 * @kobj: object to cleanup
 */
static void kobject_cleanup(struct kobject *kobj)
{
    struct kobj_type *t = get_ktype(kobj);
    const char *name = kobj->name;

    pr_debug("kobject: '%s' (%p): %s
", kobject_name(kobj), kobj, __func__);     //  release , !     if (t && !t->release)         pr_debug("kobject: '%s' (%p): does not have a release() "              "function, it is broken and must be fixed.
", kobject_name(kobj), kobj);     /* send "remove" if the caller did not do it but sent "add" */     if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) {         pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event
", kobject_name(kobj), kobj);         kobject_uevent(kobj, KOBJ_REMOVE); //  KOBJ_REMOVE     }     /* remove from sysfs if the caller did not do it */     if (kobj->state_in_sysfs) {         pr_debug("kobject: '%s' (%p): auto cleanup kobject_del
", kobject_name(kobj), kobj);         kobject_del(kobj); // sysfs_dirent ,     }     if (t && t->release) {         pr_debug("kobject: '%s' (%p): calling ktype release
", kobject_name(kobj), kobj);         t->release(kobj); //      }     /* free name if we allocated it */     if (name) {         pr_debug("kobject: '%s': free name
", name);         kfree(name);     } }

カーネルオブジェクトの解放関数はkobj_type構造体で定義されている:
struct kobj_type {
    void (*release)(struct kobject *kobj); // 
    struct sysfs_ops *sysfs_ops; //  
    struct attribute **default_attrs; // , NULL 
};

各プロパティはstrut attribute構造体で表されます.
struct attribute {
    const char  *name; // 
    struct module  *owner;// , 
    mode_t   mode;// 
};

各プロパティはsysfsのカーネルオブジェクトディレクトリの下にあるファイルに対応します.ファイル名はname、ファイル権限はmodeです.属性がファイル形式で表現されている以上、必ず読み書きできます.アプリケーションがプロパティファイルを読み書きすると、カーネルはメンバーsysfs_にコールバックされます.opsが指す操作:
struct sysfs_ops {
    ssize_t (*show)(struct kobject *kobj, struct attribute *attr, char *buf); // read
    ssize_t (*store)(struct kobject *kobj, struct attribute *attr, const char *buf,  size_t size); // write
};

アプリケーションがプロパティファイルを読み込むと、showが指すコールバック関数が呼び出されます.アプリケーションがプロパティファイルを書くとstoreが実行するコールバック関数が呼び出されます.ファイルの読み取りreadに比べてshow操作はバッファアドレスが1つしか転送されず、バッファの長さは転送されません.実際、bufが実行するバッファはカーネルによって自動的に割り当てられ、長さは1ページのメモリであり、一般的には4 KBである.上記のshowとstore操作のbufは、ユーザ状態メモリポインタではないので、直接アクセスできます.
実際の操作では、初期化時にすべての属性を追加できない可能性があります.実行中に属性ファイルを動的に作成する可能性があります.そのため、カーネルは属性インタフェースを動的に追加および削除します.
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);

上記のインタフェースを使用すると、カーネルコードでカーネルオブジェクトのプロパティを動的に追加または除去できます.
よく見るとstruct kobj_typeタイプ、sysfs_が見つかりますopsはshowとstore操作、すなわちカーネルオブジェクトのすべての属性へのアクセスのみを提供し、sysfs_に呼び出されます.opsが提供するshowとstore操作.Linuxカーネルは、プロパティを定義するときに柔軟性を高めるために、次のデータ型を提供しています.
struct kobj_attribute {
    struct attribute attr;
    ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
    ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count);
};

これにより、各プロパティに対応するshowメソッドとstoreメソッドを指定できます.カーネルはどのように実現されていますか?コアはcontainerにあるofマクロの柔軟な使用:
/* default kobject attribute operations */
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
    struct kobj_attribute *kattr;
    ssize_t ret = -EIO;
 
    //  attr kobj_attribute 
    kattr = container_of(attr, struct kobj_attribute, attr);
    //  kobj_attribute show 
    if (kattr->show)
        ret = kattr->show(kobj, kattr, buf);
    return ret;
}
 
static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
    struct kobj_attribute *kattr;
    ssize_t ret = -EIO;
     
    //  attr kobj_attribute 
    kattr = container_of(attr, struct kobj_attribute, attr);
    //  kobj_attribute store 
    if (kattr->store)
        ret = kattr->store(kobj, kattr, buf, count);
    return ret;
}
 
struct sysfs_ops kobj_sysfs_ops = {
    .show = kobj_attr_show,
    .store = kobj_attr_store,
};

上記のソースコードから,属性を定義する際にstruct attributeをカスタム属性タイプにネストし,統一showとstore操作を記述し,統一showとstore操作の内部で属性の真のshowとstore方法をコールバックすることができる.もちろんstruct kobjを直接attributeは、カスタマイズしたプロパティタイプにネストされています.
カーネルはstruct kobjを初期化するのに便利です.attributeオブジェクトは、次のマクロを提供します.
#define __ATTR(_name,_mode,_show,_store) { \
    .attr = {.name = __stringify(_name), .mode = _mode }, \
    .show = _show,     \
    .store = _store,     \
}

このマクロはdevice,device_driverやbusでは、バス、デバイス、駆動を後で分析するときに使用するのに役立ちます.
三、カーネル集合kset:
カーネルセットはカーネルオブジェクトに基づいて設計され,カーネルオブジェクトを収容し,収容したカーネルオブジェクトをチェーンテーブルに追加して保存し,その収容したカーネルオブジェクトが送信するユーザ状態イベントを管理する.
struct kset {
    struct list_head list; // 
    spinlock_t list_lock; // 
    struct kobject kobj; // 
    struct kset_uevent_ops *uevent_ops;// 
};

カーネルコレクションオブジェクトを使用する場合は、まずカーネルコレクションオブジェクトを初期化し、カーネルに登録します.一般的なインタフェースは次のとおりです.
//  
void kset_init(struct kset *k);
//  
int kset_register(struct kset *k); // kset_init(),
//  , 
struct kset *kset_create_and_add(const char *name, struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj);
//  
void kset_unregister(struct kset *k);
//  1
static inline struct kset *kset_get(struct kset *k)
//  1
static inline void kset_put(struct kset *k);
//  name 
struct kobject *kset_find_obj(struct kset *kset, const char *name);

ここで注意したいのは、kset_を使用するとregister()メソッドはカーネルセットを登録し、登録する前にuevent_を初期化する必要があります.opsとkobjオブジェクトのnameメンバーは、kset_Init()メソッドの内部では、このメンバーは初期化されていません.
カーネルセットの登録実装ソースコードは複雑ではなく,kset自体もkobjectであることを理解すれば,そのプロセスは容易である.
四、簡単な例:
/sysディレクトリの下にpersonsディレクトリを作成します.3つのサブディレクトリが含まれています.構造は以下の通りです.nameは読み書き可能で、sexは読み取り専用です.
persons
    | person-0
        | sex
        | name
    | person-1
        | sex
        | name
    | person-2
        | sex
        | name

    example:
#include <linux/module.h>
#include <linux/kobject.h>


#define PERSON_NUMS     3


#define ENTER() printk(KERN_DEBUG "%s() Enter", __func__)
#define EXIT() printk(KERN_DEBUG "%s() Exit", __func__)
#define ERR(fmt, args...) printk(KERN_ERR "%s()-%d: " fmt "
", __func__, __LINE__, ##args) #define DBG(fmt, args...) printk(KERN_DEBUG "%s()-%d: " fmt "
", __func__, __LINE__, ##args) struct person {     struct kobject kobj;     char name[16];     char sex; }; // call when we kobject_put() to let kref to be 0 static void person_release(struct kobject *kobj) {     struct person *per = container_of(kobj, struct person, kobj);     ENTER();     kfree(per);     EXIT(); } // generic attr show function static ssize_t attr_show(struct kobject *kobj, struct attribute *attr, char *buf) {     struct kobj_attribute *kattr;     ssize_t ret = -EIO;     kattr = container_of(attr, struct kobj_attribute, attr);     ENTER();     if (kattr->show) {         ret = kattr->show(kobj, kattr, buf);     }     EXIT();     return ret; } // generic attr store function static ssize_t attr_store(struct kobject *kobj, struct attribute *attr,     const char *buf, size_t count) {     struct kobj_attribute *kattr;     ssize_t ret = -EIO;     kattr = container_of(attr, struct kobj_attribute, attr);     ENTER();     if (kattr->store) {         ret = kattr->store(kobj, kattr, buf, count);     }          EXIT();     return ret; } static struct sysfs_ops person_attr_ops = {     .show = attr_show,     .store = attr_store, }; /*  show and store function for spcific attributes, like sex and name. */ static ssize_t sex_show(struct kobject *kobj, struct kobj_attribute *kattr, char *buf) {     ENTER();     struct person *per = container_of(kobj, struct person, kobj);     ssize_t ret = sprintf(buf, "%c
", per->sex);     EXIT();     return ret; } static ssize_t sex_store(struct kobject *kobj, struct kobj_attribute *kattr,                                 const char *buf, size_t len) {     ENTER();     DBG("no prividge");     return -EACCES; // it means no prividge. } static ssize_t name_show(struct kobject *kobj, struct kobj_attribute *kattr, char *buf) {     ENTER();     struct person *per = container_of(kobj, struct person, kobj);     ssize_t len = strlen(per->name);     memcpy(buf, per->name, len);     EXIT();     return len; } static ssize_t name_store(struct kobject *kobj, struct kobj_attribute *kattr,                                 const char *buf, size_t len) {     ENTER();     DBG("buf: %s, len:%u", buf, len);     struct person *per = container_of(kobj, struct person, kobj);          snprintf(per->name, sizeof(per->name), "%s", buf);     EXIT();     return len; } static struct kobj_attribute attr_sex = \     __ATTR(sex, S_IRUGO, sex_show, sex_store); static struct kobj_attribute attr_name = \     __ATTR(name, S_IRUGO | S_IWUGO, name_show, name_store); static struct attribute *person_default_attrs[] = {     &attr_sex.attr,     &attr_name.attr,     NULL, }; static struct kobj_type person_kobj_type = {     .release = person_release,     .sysfs_ops = &person_attr_ops,     .default_attrs = person_default_attrs, }; static struct kset *persons = NULL; /*  persons     | person-0         | sex         | name     | person-1         | sex         | name     | person-2         | sex         | name  */ static __init int sysfs_demo_init(void) {     struct person *per;     int i;     int err;     struct list_head *cur, *next;     struct kobject *p_cur;     ENTER();     persons = kset_create_and_add("persons", NULL, NULL);     if (!persons) {         ERR("kset_create_and_add fail");         return -ENOMEM;     }     for (i = 0; i < PERSON_NUMS; ++i) {         per = kzalloc(sizeof(struct person), GFP_KERNEL);         if (!per) {             ERR("kzalloc fail");             goto _fail;         }         per->kobj.kset = persons;         per->sex = ((i % 2) == 0) ? 'M' : 'F';         snprintf(per->name, sizeof(per->name), "person-%d", i);         DBG("name: %s", per->name);         err = kobject_init_and_add(&per->kobj, &person_kobj_type, NULL, per->name);         if (err) {             ERR("kobject_init_and_add fail");             goto _fail;         }         DBG("kobject_init_and_add success");         kobject_uevent(&per->kobj, KOBJ_ADD);     }     EXIT();     return 0; _fail:     if (persons) {         DBG("in fail");         list_for_each_safe(cur, next, &persons->list) {             p_cur = container_of(cur, struct kobject, entry);             kobject_put(p_cur);         }         kset_unregister(persons);         persons = NULL;     }          return err; } static __exit void sysfs_demo_exit(void) {     struct list_head *cur, *next;     struct kobject *p_cur;     if (persons) {         ENTER();         list_for_each_safe(cur, next, &persons->list) {             p_cur = container_of(cur, struct kobject, entry);             DBG("kobject_put begin");             kobject_put(p_cur);         }         kset_unregister(persons);         persons = NULL;     }     EXIT(); } module_init(sysfs_demo_init); module_exit(sysfs_demo_exit); MODULE_LICENSE("GPL");