LINUX設備驅動之設備模型一--kobject

LINUX設備驅動驅動程序模型的核心數據結構是kobject,kobject數據結構在\linux\kobject.h中定義:

struct kobject {

       const char             *name;

       struct list_head       entry;

       struct kobject         *parent;

       struct kset             *kset;

       struct kobj_type     *ktype;

       struct sysfs_dirent  *sd;

       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;

       unsigned int uevent_suppress:1;

};

每個kobject都有它的父節點parent、kset、kobj_type指針,這三者是驅動模型的基本結構,kset是kobject的集合,在\linux\kobject.h中定義:

struct kset {

       struct list_head list;

       spinlock_t list_lock;

       struct kobject kobj;

       struct kset_uevent_ops *uevent_ops;

};

可以看到每個kset內嵌了一個kobject(kobj字段),用來表示其自身節點,其list字段指向了所包含的kobject的鏈表頭。我們在後面的分析中將看到kobject如果沒有指定父節點,parent將指向其kset內嵌的kobject。

每個kobject都有它的kobj_type字段指針,用來表示kobject在文件系統中的操作方法,kobj_type結構也在\linux\kobject.h中定義:

struct kobj_type {

       void (*release)(struct kobject *kobj);

       struct sysfs_ops *sysfs_ops;

       struct attribute ** default_attrs;

};

release方法是在kobject釋放是調用,sysfs_ops指向kobject對應的文件操作,default_attrskobject的默認屬性,sysfs_ops的將使用default_attrs屬性(在後面的分析中我們將會看到)。

從上面的分析我們可以想象到kobject、kset、kobj_type的層次結構:

<font face="Times New Roman" col=""

我們可以把一個kobject添加到文件系統中去(實際上是添加到其父節點所代表的kset中去),內核提供kobject_create_and_add()接口函數:

struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)

{

       struct kobject *kobj;

       int retval;

 

       kobj = kobject_create();

       if (!kobj)

              return NULL;

 

       retval = kobject_add(kobj, parent, "%s", name);

       if (retval) {

              printk(KERN_WARNING "%s: kobject_add error: %d\n",

                     __func__, retval);

              kobject_put(kobj);

              kobj = NULL;

       }

       return kobj;

}

kobject _create()爲要創建的kobject分配內存空間並對其初始化。

struct kobject *kobject_create(void)

{

       struct kobject *kobj;

 

       kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);

       if (!kobj)

              return NULL;

 

       kobject_init(kobj, &dynamic_kobj_ktype);

       return kobj;

}

kobject_init()對kobject基本字段進行初始化,用輸入參數設置kobj_type屬性。

這裏粘出代碼以供參考:

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)

{

       char *err_str;

 

       if (!kobj) {

              err_str = "invalid kobject pointer!";

              goto error;

       }

       if (!ktype) {

              err_str = "must have a ktype to be initialized properly!\n";

              goto error;

       }

       if (kobj->state_initialized) {

              /* do not error out as sometimes we can recover */

              printk(KERN_ERR "kobject (%p): tried to init an initialized "

                     "object, something is seriously wrong.\n", kobj);

              dump_stack();

       }

 

       kobject_init_internal(kobj);

       kobj->ktype = ktype;

       return;

 

error:

       printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);

       dump_stack();

}

static void kobject_init_internal(struct kobject *kobj)

{

       if (!kobj)

              return;

       kref_init(&kobj->kref);

       INIT_LIST_HEAD(&kobj->entry);

       kobj->state_in_sysfs = 0;

       kobj->state_add_uevent_sent = 0;

       kobj->state_remove_uevent_sent = 0;

       kobj->state_initialized = 1;

}

接着看kobject_add()函數:

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.\n",

                     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;

}

在上面的初始化中已把位變量設位1

va_start(args, fmt)和va_end(args)使用可變參數(可見參數用法不在這裏分析),在kobject_add_varg中將把fmt指向的內容賦給kobject的name字段。下面我們詳細看看kobject_add_varg函數:

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!\n");

              return retval;

       }

       kobj->parent = parent;

       return kobject_add_internal(kobj);

}

kobject_set_name_vargs(kobj, fmt, vargs),如果kobj的name字段指向的內容爲空,則爲分配一個內存空間並用fmt指向的內容初始化,把地址賦給kobj的name字段。

int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,

                              va_list vargs)

{

       const char *old_name = kobj->name;

       char *s;

 

       if (kobj->name && !fmt)

              return 0;

 

       kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);

       if (!kobj->name)

              return -ENOMEM;

 

       /* ewww... some of these buggers have '/' in the name ... */

       while ((s = strchr(kobj->name, '/')))

              s[0] = '!';

 

       kfree(old_name);

       return 0;

}

char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)

{

       unsigned int len;

       char *p;

       va_list aq;

 

       va_copy(aq, ap);

       len = vsnprintf(NULL, 0, fmt, aq);

       va_end(aq);

 

       p = kmalloc(len+1, gfp);

       if (!p)

              return NULL;

 

       vsnprintf(p, len+1, fmt, ap);

 

       return p;

}

繼續kobject_add_varg()返回kobject_add_internal(kobj),就是在這個函數理爲kobj創建文件系統結構:

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!\n", kobj);

              return -EINVAL;

       }

檢查kobj和它的name字段,不存在則返回錯誤信息。

 

       parent = kobject_get(kobj->parent);

獲得其父節點,並增加父節點的計數器,kobject結構中的kref字段用於容器的計數,kobject_get和kobject_put分別增加和減少計數器,如果計數器爲0,則釋放該kobject,kobject_get返回該kobject。

       /* join kset if set, use it as parent if we do not already have one */

       if (kobj->kset) {

              if (!parent)

                     parent = kobject_get(&kobj->kset->kobj);

              kobj_kset_join(kobj);

              kobj->parent = parent;

       }

在這裏我們可以看到,如果調用kobject_create_and_add()時參數parent設爲NULL,則會去檢查kobj的kset是否存在,如果存在就會把kset所嵌套的kobj作爲其父節點,並把kobj添加到kset中去。

              pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",

               kobject_name(kobj), kobj, __func__,

               parent ? kobject_name(parent) : "<NULL>",

               kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");

打印一些調試信息,接着爲kobj創建目錄:

       error = create_dir(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.\n",

                            __func__, kobject_name(kobj));

              else

                     printk(KERN_ERR "%s failed for %s (%d)\n",

                            __func__, kobject_name(kobj), error);

              dump_stack();

       } else

              kobj->state_in_sysfs = 1;

 

       return error;

}

如果創建不成功,則回滾上面的操作,成功的話則設置kobj的state_in_sysfs標誌。

在看看create_dir()函數中具體創建了那些內容:

static int create_dir(struct kobject *kobj)

{

       int error = 0;

       if (kobject_name(kobj)) {

              error = sysfs_create_dir(kobj);

              if (!error) {

                     error = populate_dir(kobj);

                     if (error)

                            sysfs_remove_dir(kobj);

              }

       }

       return error;

}

sysfs_create_dir()先爲kobj創建了一個目錄文件

int sysfs_create_dir(struct kobject * kobj)

{

       struct sysfs_dirent *parent_sd, *sd;

       int error = 0;

 

       BUG_ON(!kobj);

 

       if (kobj->parent)

              parent_sd = kobj->parent->sd;

       else

              parent_sd = &sysfs_root;

 

       error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);

       if (!error)

              kobj->sd = sd;

       return error;

}

如果kobj->parent爲NULL,就把&sysfs_root作爲父節點sd,即在/sys下面創建結點。

然後調用populate_dir:

static int populate_dir(struct kobject *kobj)

{

       struct kobj_type *t = get_ktype(kobj);

       struct attribute *attr;

       int error = 0;

       int i;

 

       if (t && t->default_attrs) {

              for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {

                     error = sysfs_create_file(kobj, attr);

                     if (error)

                            break;

              }

       }

       return error;

}

得到kobj的kobj_type,歷遍kobj_type的default_attrs並創建屬性文件,文件的操作會回溯到sysfs_ops的show和store會調用封裝了attribute的kobj_attribute結構的store和show方法(在後面的代碼中將會分析)。

由於上面kobject_init(kobj, &dynamic_kobj_ktype)用默認dynamic_kobj_ktype作爲kobj_type參數,而dynamic_kobj_ktype的default_attrs爲NULL,所以這裏沒有創建屬性文件。

至此,我們已經知道了kobject_create_and_add()函數創建kobject,掛到父kobject,並設置其kobj_type,在文件系統中爲其創建目錄和屬性文件等。

另外,如果我們已靜態定義了要創建的kobject,則可以調用kobject_init_and_add()來註冊kobject,其函數如下:

int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,

                      struct kobject *parent, const char *fmt, ...)

{

       va_list args;

       int retval;

 

       kobject_init(kobj, ktype);

 

       va_start(args, fmt);

       retval = kobject_add_varg(kobj, parent, fmt, args);

       va_end(args);

 

       return retval;

}

通過上面的分析我們很輕鬆就能理解這個函數。

 

內核提供註銷kobject的函數是kobject_del()

void kobject_del(struct kobject *kobj)

{

       if (!kobj)

              return;

 

       sysfs_remove_dir(kobj);

       kobj->state_in_sysfs = 0;

       kobj_kset_leave(kobj);

       kobject_put(kobj->parent);

       kobj->parent = NULL;

}

<font face="Times New Roman" col=""

刪除kobj目錄及其目錄下的屬性文件,清kobj的state_in_sysfs標誌,把kobj從kset中刪除,減少kobj->parent的計數並設其指針爲空。 <font face="Times New Roman" col=""

發佈了18 篇原創文章 · 獲贊 2 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章