<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 189,comments - 115,trackbacks - 0

    linux設(shè)備模型深探(1) 2009-06-05 15:37

    字號:    
    ------------------------------------------
    本文系本站原創(chuàng),歡迎轉(zhuǎn)載!
    轉(zhuǎn)載請注明出處:http://ericxiao.cublog.cn/
    ------------------------------------------
    一:前言
    Linux設(shè)備模型是一個極其復(fù)雜的結(jié)構(gòu)體系,在編寫驅(qū)動程序的時候,通常不會用到這方面的東西,但是。理解這部份內(nèi)容,對于我們理解linux設(shè)備驅(qū)動的結(jié)構(gòu)是大有裨益的。我們不但可以在編寫程序程序的時候知其然,亦知其所以然。又可以學(xué)習(xí)到一種極其精致的架構(gòu)設(shè)計方法。由于之前已經(jīng)詳細(xì)分析了sysfs文件系統(tǒng)。所以本節(jié)的討論主要集中在設(shè)備模型的底層實現(xiàn)上。上層的接口,如pci.,usb ,網(wǎng)絡(luò)設(shè)備都可以看成是底層的封裝。
    二:kobject ,kset和ktype
    Kobject,kset,kypte這三個結(jié)構(gòu)是設(shè)備模型中的下層架構(gòu)。模型中的每一個元素都對應(yīng)一個kobject.kset和ktype可以看成是kobject在層次結(jié)構(gòu)與屬性結(jié)構(gòu)方面的擴充。將三者之間的關(guān)系用圖的方示描述如下:
    linux設(shè)備模型深探(1) - challengezcy - challengezcy的博客
     
    如上圖所示:我們知道。在sysfs中每一個目錄都對應(yīng)一個kobject.這些kobject都有自己的parent。在沒有指定parent的情況下,都會指向它所屬的kset->object。其次,kset也內(nèi)嵌了kobject.這個kobject又可以指它上一級的parent。就這樣。構(gòu)成了一個空間上面的層次關(guān)系。
    其實,每個對象都有屬性。例如,電源管理,執(zhí)插撥事性管理等等。因為大部份的同類設(shè)備都有相同的屬性,因此將這個屬性隔離開來,存放在ktype中。這樣就可以靈活的管理了.記得在分析sysfs的時候。對于sysfs中的普通文件讀寫操作都是由kobject->ktype->sysfs_ops來完成的.
    經(jīng)過上面的分析,我們大概了解了kobject.kset與ktype的大概架設(shè)與相互之間的關(guān)系。下面我們從linux源代碼中的分析來詳細(xì)研究他們的操作。
     
    三:kobject,kset和ktype的操作
    為了說明kobject的操作,先寫一個測試模塊,代碼如下:
    #include <linux/device.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/string.h>
    #include <linux/sysfs.h>
    #include <linux/stat.h>
     
    MODULE_AUTHOR("eric xiao");
    MODULE_LICENSE("Dual BSD/GPL");
     
    void obj_test_release(struct kobject *kobject);
    ssize_t eric_test_show(struct kobject *kobject, struct attribute *attr,char *buf);
    ssize_t eric_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count);
     
    struct attribute test_attr = {
            .name = "eric_xiao",
            .mode = S_IRWXUGO,
    };
     
    static struct attribute *def_attrs[] = {
            &test_attr,
            NULL,
    };
     
     
    struct sysfs_ops obj_test_sysops =
    {
            .show = eric_test_show,
            .store = eric_test_store,
    };
     
    struct kobj_type ktype =
    {
            .release = obj_test_release,
            .sysfs_ops=&obj_test_sysops,
            .default_attrs=def_attrs,
    };
     
    void obj_test_release(struct kobject *kobject)
    {
            printk("eric_test: release .\n");
    }
     
    ssize_t eric_test_show(struct kobject *kobject, struct attribute *attr,char *buf)
    {
            printk("have show.\n");
            printk("attrname:%s.\n", attr->name);
            sprintf(buf,"%s\n",attr->name);
            return strlen(attr->name)+2;
    }
     
    ssize_t eric_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count)
    {
            printk("havestore\n");
            printk("write: %s\n",buf);
            return count;
    }
     
    struct kobject kobj;
    static int kobject_test_init()
    {
            printk("kboject test init.\n");
            kobject_init_and_add(&kobj,&ktype,NULL,"eric_test");
            return 0;
    }
     
    static int kobject_test_exit()
    {
            printk("kobject test exit.\n");
            kobject_del(&kobj);
            return 0;
    }
     
    module_init(kobject_test_init);
    module_exit(kobject_test_exit);
     
    加載模塊之后,會發(fā)現(xiàn),在/sys下多了一個eric_test目錄。該目錄下有一個叫eric_xiao的文件。如下所示:
    [root@localhost eric_test]# ls
    eric_xiao
    用cat察看此文件:
    [root@localhost eric_test]# cat eric_xiao
    eric_xiao
    再用echo往里面寫點東西;
    [root@localhost eric_test]# echo  hello > eric_xiao
    Dmesg的輸出如下:
     
    have show.
    attrname:eric_xiao.
    havestore
    write: hello
     
    如上所示。我們看到了kobject的大概建立過程.我們來看一下kobject_init_and_add()的實現(xiàn)。在這個函數(shù)里,包含了對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
         kobject_init(kobj, ktype);
     
         va_start(args, fmt);
         //為kobjcet設(shè)置名稱,在sysfs中建立相關(guān)信息
         retval = kobject_add_varg(kobj, parent, fmt, args);
         va_end(args);
     
         return retval;
    }
    上面的流程主要分為兩部份。一部份是kobject的初始化。在這一部份,它將kobject與給定的ktype關(guān)聯(lián)起來。初始化kobject中的各項結(jié)構(gòu)。另一部份是kobject的名稱設(shè)置。空間層次關(guān)系的設(shè)置,具體表現(xiàn)在sysfs文件系統(tǒng)中.
    對于第一部份,代碼比較簡單,這里不再贅述。跟蹤第二部份,也就是kobject_add_varg()的實現(xiàn).
     
    static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
                      const char *fmt, va_list vargs)
    {
         va_list aq;
         int retval;
     
         va_copy(aq, vargs);
         //設(shè)置kobject的名字。即kobject的name成員
         retval = kobject_set_name_vargs(kobj, fmt, aq);
         va_end(aq);
         if (retval) {
             printk(KERN_ERR "kobject: can not set name properly!\n");
             return retval;
         }
         //設(shè)置kobject的parent。在上面的例子中,我們沒有給它指定父結(jié)點
         kobj->parent = parent;
         //在sysfs中添加kobjcet信息
         return kobject_add_internal(kobj);
    }
    設(shè)置好kobject->name后,轉(zhuǎn)入kobject_add_internal()。在sysfs中創(chuàng)建空間結(jié)構(gòu).代碼如下:
    static int kobject_add_internal(struct kobject *kobj)
    {
         int error = 0;
         struct kobject *parent;
     
         if (!kobj)
             return -ENOENT;
         //如果kobject的名字為空.退出
         if (!kobj->name || !kobj->name[0]) {
             pr_debug("kobject: (%p): attempted to be registered with empty "
                   "name!\n", kobj);
             WARN_ON(1);
             return -EINVAL;
         }
     
         //取kobject的父結(jié)點
         parent = kobject_get(kobj->parent);
         //如果kobject的父結(jié)點沒有指定,就將kset->kobject做為它的父結(jié)點
         /* 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;
         }
         //調(diào)試用
         pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
              kobject_name(kobj), kobj, __FUNCTION__,
              parent ? kobject_name(parent) : "<NULL>",
              kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
     
         //在sysfs中創(chuàng)建kobject的相關(guān)元素
         error = create_dir(kobj);
         if (error) {
             //v如果創(chuàng)建失敗。減少相關(guān)的引用計數(shù)
             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",
                         __FUNCTION__, kobject_name(kobj));
             else
                  printk(KERN_ERR "%s failed for %s (%d)\n",
                         __FUNCTION__, kobject_name(kobj), error);
             dump_stack();
         } else
             //如果創(chuàng)建成功。將state_in_sysfs建為1。表示該object已經(jīng)在sysfs中了
             kobj->state_in_sysfs = 1;
     
         return error;
    }
    這段代碼比較簡單,它主要完成kobject父結(jié)點的判斷和選定,然后再調(diào)用create_dir()在sysfs創(chuàng)建相關(guān)信息。該函數(shù)代碼如下:
    static int create_dir(struct kobject *kobj)
    {
         int error = 0;
         if (kobject_name(kobj)) {
              //為kobject創(chuàng)建目錄
             error = sysfs_create_dir(kobj);
             if (!error) {
                  //為kobject->ktype中的屬性創(chuàng)建文件
                  error = populate_dir(kobj);
                  if (error)
                       sysfs_remove_dir(kobj);
             }
         }
         return error;
    }
    我們在上面的示例中看到的/sys下的eric_test目錄,以及該目錄下面的eric_xiao的這個文件就是這里被創(chuàng)建的。我們先看一下kobject所表示的目錄創(chuàng)建過程。這是在sysfs_create_dir()中完成的。代碼如下:
    int sysfs_create_dir(struct kobject * kobj)
    {
         struct sysfs_dirent *parent_sd, *sd;
         int error = 0;
     
         BUG_ON(!kobj);
         /*如果kobject的parnet存在。就在目錄點的目錄下創(chuàng)建這個目錄。如果沒有父結(jié)點不存在,就在/sys下面創(chuàng)建結(jié)點。在上面的流程中,我們可能并沒有為其指定父結(jié)點,也沒有為其指定kset。
    */
         if (kobj->parent)
             parent_sd = kobj->parent->sd;
         else
             parent_sd = &sysfs_root;
     
         //在sysfs中創(chuàng)建目錄
         error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);
         if (!error)
             kobj->sd = sd;
         return error;
    }
    在這里,我們就要聯(lián)系之前分析過的sysfs文件系統(tǒng)的研究了。如果不太清楚的,可以在找到那篇文章仔細(xì)的研讀一下。create_dir()就是在sysfs中創(chuàng)建目錄的接口,在之前已經(jīng)詳細(xì)分析過了。這里不再講述。
    接著看為kobject->ktype中的屬性創(chuàng)建文件。這是在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;
    }
    這段代碼比較簡單。它遍歷ktype中的屬性。然后為其建立文件。請注意:文件的操作最后都會回溯到ktype->sysfs_ops的show和store這兩個函數(shù)中.
     
    Kobject的創(chuàng)建已經(jīng)分析完了,接著分析怎么將一個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;
    }
    該函數(shù)會將在sysfs中的kobject對應(yīng)的目錄刪除。請注意,屬性文件是建立在這個目錄下面的。只需要將這個目錄刪除。屬性文件也隨之刪除。
    是后,減少相關(guān)的引用計數(shù),如果kobject的引用計數(shù)為零。則將其所占空間釋放.
     
    Kset的操作與kobject類似,因為kset中內(nèi)嵌了一個kobject結(jié)構(gòu),所以,大部份操作都是集中在kset->kobject上.具體分析一下kset_create_and_add()這個接口,類似上面分析的kobject接口,這個接口也包括了kset的大部分操作.代碼如下:
    struct kset *kset_create_and_add(const char *name,
                        struct kset_uevent_ops *uevent_ops,
                        struct kobject *parent_kobj)
    {
         struct kset *kset;
         int error;
         //創(chuàng)建一個kset
         kset = kset_create(name, uevent_ops, parent_kobj);
         if (!kset)
             return NULL;
         //注冊kset
         error = kset_register(kset);
         if (error) {
             //如果注冊失敗,釋放kset
             kfree(kset);
             return NULL;
         }
         return kset;
    }
    Kset_create()用來創(chuàng)建一個struct kset結(jié)構(gòu).代碼如下:
    static struct kset *kset_create(const char *name,
                       struct kset_uevent_ops *uevent_ops,
                       struct kobject *parent_kobj)
    {
         struct kset *kset;
     
         kset = kzalloc(sizeof(*kset), GFP_KERNEL);
         if (!kset)
             return NULL;
         kobject_set_name(&kset->kobj, name);
         kset->uevent_ops = uevent_ops;
         kset->kobj.parent = parent_kobj;
     
         kset->kobj.ktype = &kset_ktype;
         kset->kobj.kset = NULL;
     
         return kset;
    }
    我們注意,在這里創(chuàng)建kset時.為其內(nèi)嵌的kobject指定其ktype結(jié)構(gòu)為kset_ktype.這個結(jié)構(gòu)的定義如下:
    static struct kobj_type kset_ktype = {
         .sysfs_ops    = &kobj_sysfs_ops,
         .release = kset_release,
    };
    屬性文件的讀寫操作全部都包含在sysfs_ops成員里.kobj_sysfs_ops的定義如下:
    struct sysfs_ops kobj_sysfs_ops = {
         .show    = kobj_attr_show,
         .store   = kobj_attr_store,
    };
    Show,store成員對應(yīng)的函數(shù)代碼如下所示:
    static ssize_t kobj_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);
         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;
     
         kattr = container_of(attr, struct kobj_attribute, attr);
         if (kattr->store)
             ret = kattr->store(kobj, kattr, buf, count);
         return ret;
    }
    從上面的代碼看以看出.會將struct attribute結(jié)構(gòu)轉(zhuǎn)換為struct kobj_attribte結(jié)構(gòu).也就是說struct kobj_attribte內(nèi)嵌了一個struct attribute.實際上,這是和宏__ATTR配合在一起使用的.經(jīng)常用于group中.在這里并不打算研究group.原理都是一樣的.這里列出來只是做個說明而已.
    創(chuàng)建好了kset之后,會調(diào)用kset_register().這個函數(shù)就是kset操作的核心代碼了.如下:
    int kset_register(struct kset *k)
    {
         int err;
     
         if (!k)
             return -EINVAL;
     
         kset_init(k);
         err = kobject_add_internal(&k->kobj);
         if (err)
             return err;
         kobject_uevent(&k->kobj, KOBJ_ADD);
         return 0;
    }
    在kset_init()里會初始化kset中的其它字段.然后調(diào)用kobject_add_internal()為其內(nèi)嵌的kobject結(jié)構(gòu)建立空間層次結(jié)構(gòu).之后因為添加了kset.會產(chǎn)生一個事件.這個事件是通過用戶空間的hotplug程序處理的.這就是kset明顯不同于kobject的地方.詳細(xì)研究一下這個函數(shù).這對于我們研究hotplug的深層機理是很有幫助的.它的代碼如下;
    int kobject_uevent(struct kobject *kobj, enum kobject_action action)
    {
         return kobject_uevent_env(kobj, action, NULL);
    }
    之后,會調(diào)用kobject_uevent_env().這個函數(shù)中的三個參數(shù)含義分別為:引起事件的kobject.事件類型(add,remove,change,move,online,offline等).第三個參數(shù)是要添加的環(huán)境變量.
    代碼篇幅較長,我們效仿情景分析上面的做法.分段分析如下:
    int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                    char *envp_ext[])
    {
         struct kobj_uevent_env *env;
         const char *action_string = kobject_actions[action];
         const char *devpath = NULL;
         const char *subsystem;
         struct kobject *top_kobj;
         struct kset *kset;
         struct kset_uevent_ops *uevent_ops;
         u64 seq;
         int i = 0;
         int retval = 0;
     
         pr_debug("kobject: '%s' (%p): %s\n",
              kobject_name(kobj), kobj, __FUNCTION__);
     
         /* search the kset we belong to */
         top_kobj = kobj;
         while (!top_kobj->kset && top_kobj->parent)
             top_kobj = top_kobj->parent;
     
         if (!top_kobj->kset) {
             pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
                   "without kset!\n", kobject_name(kobj), kobj,
                   __FUNCTION__);
             return -EINVAL;
         }
    因為對事件的處理函數(shù)包含在kobject->kset-> uevent_ops中.要處理事件,就必須要找到上層的一個不為空的kset.上面的代碼就是順著kobject->parent找不到一個不為空的kset.如果不存在這樣的kset.就退出
     
         kset = top_kobj->kset;
         uevent_ops = kset->uevent_ops;
     
         /* skip the event, if the filter returns zero. */
         if (uevent_ops && uevent_ops->filter)
             if (!uevent_ops->filter(kset, kobj)) {
                  pr_debug("kobject: '%s' (%p): %s: filter function "
                        "caused the event to drop!\n",
                        kobject_name(kobj), kobj, __FUNCTION__);
                  return 0;
             }
     
         /* originating subsystem */
         if (uevent_ops && uevent_ops->name)
             subsystem = uevent_ops->name(kset, kobj);
         else
             subsystem = kobject_name(&kset->kobj);
         if (!subsystem) {
             pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
                   "event to drop!\n", kobject_name(kobj), kobj,
                   __FUNCTION__);
             return 0;
         }
     
    找到了不為空的kset.就跟kset-> uevent_ops->filter()匹配.看這個事件是否被過濾.如果沒有被過濾掉.就會調(diào)用kset-> uevent_ops->name()得到子系統(tǒng)的名稱,如果不存在kset-> uevent_ops->name().就會以kobject->name做為子系統(tǒng)名稱.
     
         /* environment buffer */
         env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
         if (!env)
             return -ENOMEM;
     
         /* complete object path */
         devpath = kobject_get_path(kobj, GFP_KERNEL);
         if (!devpath) {
             retval = -ENOENT;
             goto exit;
         }
     
         /* default keys */
         retval = add_uevent_var(env, "ACTION=%s", action_string);
         if (retval)
             goto exit;
         retval = add_uevent_var(env, "DEVPATH=%s", devpath);
         if (retval)
             goto exit;
         retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
         if (retval)
             goto exit;
     
         /* keys passed in from the caller */
         if (envp_ext) {
             for (i = 0; envp_ext[i]; i++) {
                  retval = add_uevent_var(env, envp_ext[i]);
                  if (retval)
                       goto exit;
             }
         }
     
    接下來,就應(yīng)該設(shè)置為調(diào)用hotplug設(shè)置環(huán)境變量了.首先,分配一個struct kobj_uevent_env結(jié)構(gòu)用來存放環(huán)境變量的值.然后調(diào)用kobject_get_path()用來獲得引起事件的kobject在sysfs中的路徑.再調(diào)用add_uevent_var()將動作代表的字串,kobject路徑,子系統(tǒng)名稱填充到struct kobj_uevent_env中,如果有指定環(huán)境變量,也將其添加進去. kobject_get_path()和add_uevent_var()都比較簡單.這里不再詳細(xì)分析了.請自行查看源代碼
     
         /* let the kset specific function add its stuff */
         if (uevent_ops && uevent_ops->uevent) {
             retval = uevent_ops->uevent(kset, kobj, env);
             if (retval) {
                  pr_debug("kobject: '%s' (%p): %s: uevent() returned "
                        "%d\n", kobject_name(kobj), kobj,
                        __FUNCTION__, retval);
                  goto exit;
             }
         }
     
         /*
          * Mark "add" and "remove" events in the object to ensure proper
          * events to userspace during automatic cleanup. If the object did
          * send an "add" event, "remove" will automatically generated by
          * the core, if not already done by the caller.
          */
         if (action == KOBJ_ADD)
             kobj->state_add_uevent_sent = 1;
         else if (action == KOBJ_REMOVE)
             kobj->state_remove_uevent_sent = 1;
     
         /* we will send an event, so request a new sequence number */
         spin_lock(&sequence_lock);
         seq = ++uevent_seqnum;
         spin_unlock(&sequence_lock);
         retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
         if (retval)
             goto exit;
    在這里還會調(diào)用kobject->kset-> uevent_ops->uevent().讓產(chǎn)生事件的kobject添加環(huán)境變量.最后將事件序列添加到環(huán)境變量中去.
     
    #if defined(CONFIG_NET)
         /* send netlink message */
         if (uevent_sock) {
             struct sk_buff *skb;
             size_t len;
     
             /* allocate message with the maximum possible size */
             len = strlen(action_string) + strlen(devpath) + 2;
             skb = alloc_skb(len + env->buflen, GFP_KERNEL);
             if (skb) {
                  char *scratch;
     
                  /* add header */
                  scratch = skb_put(skb, len);
                  sprintf(scratch, "%s@%s", action_string, devpath);
     
                  /* copy keys to our continuous event payload buffer */
                  for (i = 0; i < env->envp_idx; i++) {
                       len = strlen(env->envp[i]) + 1;
                       scratch = skb_put(skb, len);
                       strcpy(scratch, env->envp[i]);
                  }
     
                  NETLINK_CB(skb).dst_group = 1;
                  netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL);
             }
         }
    #endif
     
         /* call uevent_helper, usually only enabled during early boot */
         if (uevent_helper[0]) {
             char *argv [3];
     
             argv [0] = uevent_helper;
             argv [1] = (char *)subsystem;
             argv [2] = NULL;
             retval = add_uevent_var(env, "HOME=/");
             if (retval)
                  goto exit;
             retval = add_uevent_var(env,
                            "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
             if (retval)
                  goto exit;
     
             call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC);
         }
     
    exit:
         kfree(devpath);
         kfree(env);
         return retval;
    }
    忽略一段選擇編譯的代碼.再后就是調(diào)用用戶空間的hotplug了.添加最后兩個環(huán)境變量.HOME和PATH.然后調(diào)用hotplug.以子系統(tǒng)名稱為參數(shù).
    現(xiàn)在我們終于知道hotplug處理程序中的參數(shù)和環(huán)境變量是怎么來的了.^_^
     
    使用完了kset.再調(diào)用kset_unregister()將其注銷.這個函數(shù)很簡單,請自行查閱代碼.
    為了印證一下上面的分析,寫一個測試模塊。如下:
    #include <linux/device.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/string.h>
    #include <linux/sysfs.h>
    #include <linux/stat.h>
    #include <linux/kobject.h>
     
    MODULE_AUTHOR("eric xiao");
    MODULE_LICENSE("Dual BSD/GPL");
     
    int kset_filter(struct kset *kset, struct kobject *kobj);
    const char *kset_name(struct kset *kset, struct kobject *kobj);
    int kset_uevent(struct kset *kset, struct kobject *kobj,
                          struct kobj_uevent_env *env);
     
    struct kset kset_p;
    struct kset kset_c;
     
    struct kset_uevent_ops uevent_ops =
    {
            .filter = kset_filter,
            .name   = kset_name,
            .uevent = kset_uevent,
    };
     
    int kset_filter(struct kset *kset, struct kobject *kobj)
    {
            printk("UEVENT: filter. kobj %s.\n",kobj->name);
            return 1;
    }
     
    const char *kset_name(struct kset *kset, struct kobject *kobj)
    {
            static char buf[20];
            printk("UEVENT: name. kobj %s.\n",kobj->name);
            sprintf(buf,"%s","kset_test");
            return buf;
    }
     
    int kset_uevent(struct kset *kset, struct kobject *kobj,
                          struct kobj_uevent_env *env)
    {
            int i = 0;
            printk("UEVENT: uevent. kobj %s.\n",kobj->name);
     
            while( i< env->envp_idx){
                    printk("%s.\n",env->envp[i]);
                    i++;
            }
     
            return 0;
    }
     
     
    int kset_test_init()
    {
            printk("kset test init.\n");
            kobject_set_name(&kset_p.kobj,"kset_p");
            kset_p.uevent_ops = &uevent_ops;
            kset_register(&kset_p);
     
           kobject_set_name(&kset_c.kobj,"kset_c");
            kset_c.kobj.kset = &kset_p;
            kset_register(&kset_c);
            return 0;
    }
     
    int kset_test_exit()
    {
            printk("kset test exit.\n");
            kset_unregister(&kset_p);
            kset_unregister(&kset_c);
            return 0;
    }
     
    module_init(kset_test_init);
    module_exit(kset_test_exit);
    在這里,定義并注冊了二個kset.第二個kset的kobj->kset域指向第一個kset.這樣,當(dāng)?shù)诙€kset注冊或者卸載的時候就會調(diào)用第一個kset中的uevent_ops的相關(guān)操作.
    kset_p.uevent_ops->filter函數(shù)中,使其返回1.使其匹配成功。
    在kset_p.uevent_ops->name中。使其返回的子系統(tǒng)名為引起事件的kobject的名稱,即:kset_c.
    最后在kset_p.uevent_ops->uevent中將環(huán)境變量全部打印出來。
    下面是dmesg的輸出結(jié)果:
    kset test init.
    UEVENT: filter. kobj kset_c.
    UEVENT: name. kobj kset_c.
    UEVENT: uevent. kobj kset_c.
    ACTION=add.
    DEVPATH=/kset_p/kset_c.
    SUBSYSTEM=kset_test.
    輸出結(jié)果跟我們的分析是吻合的.
    在這里,值得我們注意的是。注冊一個kobject不會產(chǎn)生事件,只有注冊kset才會.
     
    四:bus,device和device_driver
    上面分析了kobject.kset,ktype.這三個結(jié)構(gòu)聯(lián)合起來一起構(gòu)成了整個設(shè)備模型的基石.而bus.device.device_driver.則是基于kobject.kset.ktype之上的架構(gòu).在這里,總線,設(shè)備,驅(qū)動被有序的組和在一起.
    Bus.device.device_driver三者之間的關(guān)系如下圖所示:
    linux設(shè)備模型深探(1) - challengezcy - challengezcy的博客
     
    如上圖所示.struct bus_typep->drivers_kset指向注冊在上面的驅(qū)動程序.它的p->device_kset上掛著注冊在上面的設(shè)備.每次有一個新的設(shè)備注冊到上面,都會去匹配右邊的驅(qū)動,看是否能匹配上.如果匹配成功,則將設(shè)備結(jié)構(gòu)的is_registerd域置為0.然后將設(shè)備添加到驅(qū)動的p->klist_devices.同理,每注冊一個驅(qū)動,都會去匹配左邊的設(shè)備,.如果匹配成功,將則設(shè)備加到驅(qū)動的p->klist_devices.再將設(shè)備的is_registerd置為0/
    這就是linux設(shè)備模型用來管理設(shè)備和驅(qū)動的基本架構(gòu). 我們來跟蹤一下代碼來看下詳細(xì)的操作.
     
    注冊一個總線的接口為bus_register().我們照例分段分析:
    int bus_register(struct bus_type *bus)
    {
         int retval;
         struct bus_type_private *priv;
     
         priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
         if (!priv)
             return -ENOMEM;
     
         priv->bus = bus;
         bus->p = priv;
     
         BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
     
         retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
         if (retval)
             goto out;
     
         priv->subsys.kobj.kset = bus_kset;
         priv->subsys.kobj.ktype = &bus_ktype;
         priv->drivers_autoprobe = 1;
     
         retval = kset_register(&priv->subsys);
         if (retval)
             goto out;
    首先,先為struct bus_type的私有區(qū)分配空間,然后將其和struct bus_type關(guān)聯(lián)起來.由于struct bus_type也要在sysfs文件中表示一個節(jié)點,因此,它也內(nèi)嵌也一個kset的結(jié)構(gòu).這就是priv->subsys.
    首先,它為這個kset的名稱賦值為bus的名稱,然后將priv->subsys.kobj.kset指向bus_kset. priv->subsys.kobj.ktype指向bus_ktype;然后調(diào)用kset_reqister()將priv->subsys注冊.這里涉及到的接口都在之前分析過.注冊過后,應(yīng)該會在bus_kset所表示的目錄下創(chuàng)建一個總線名稱的目錄.并且用戶空間的hotplug應(yīng)該會檢測到一個add事件.我們來看一下bus_kset到底指向的是什么:
         bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
    從此可以看出.這個bus_kset在sysfs中的結(jié)點就是/sys/bus.在這里注冊的struct bus_types就會在/sys/bus/下面出現(xiàn).
     
         retval = bus_create_file(bus, &bus_attr_uevent);
         if (retval)
             goto bus_uevent_fail;
    bus_create_file()就是在priv->subsys.kobj的這個kobject上建立一個普通屬性的文件.這個文件的屬性對應(yīng)在bus_attr_uevent.讀寫操作對應(yīng)在priv->subsys.ktype中.我們到后面才統(tǒng)一分析bus注冊時候的文件創(chuàng)建
     
         priv->devices_kset = kset_create_and_add("devices", NULL,
                                 &priv->subsys.kobj);
         if (!priv->devices_kset) {
             retval = -ENOMEM;
             goto bus_devices_fail;
         }
     
         priv->drivers_kset = kset_create_and_add("drivers", NULL,
                                 &priv->subsys.kobj);
         if (!priv->drivers_kset) {
             retval = -ENOMEM;
             goto bus_drivers_fail;
         }
     
         klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
         klist_init(&priv->klist_drivers, NULL, NULL);
    這段代碼會在bus所在的目錄下建立兩個目錄,分別為devices和drivers.并初始化掛載設(shè)備和驅(qū)動的鏈表
     
         retval = add_probe_files(bus);
         if (retval)
             goto bus_probe_files_fail;
     
         retval = bus_add_attrs(bus);
         if (retval)
             goto bus_attrs_fail;
     
         pr_debug("bus: '%s': registered\n", bus->name);
         return 0;
    在這里,會為bus_attr_drivers_probe, bus_attr_drivers_autoprobe.注冊bus_type中的屬性建立文件
     
    bus_attrs_fail:
         remove_probe_files(bus);
    bus_probe_files_fail:
         kset_unregister(bus->p->drivers_kset);
    bus_drivers_fail:
         kset_unregister(bus->p->devices_kset);
    bus_devices_fail:
         bus_remove_file(bus, &bus_attr_uevent);
    bus_uevent_fail:
         kset_unregister(&bus->p->subsys);
         kfree(bus->p);
    out:
         return retval;
    }
    這段代碼為出錯處理
    posted on 2009-06-11 21:31 MEYE 閱讀(918) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲国产成人爱av在线播放 | 亚洲综合国产成人丁香五月激情| 亚洲国产日韩在线一区| 亚洲欧美自偷自拍另类视| 羞羞视频免费网站入口| 美女无遮挡拍拍拍免费视频| 99re6免费视频| 情侣视频精品免费的国产| 亚洲综合色视频在线观看| 91亚洲精品视频| 亚洲国产成人久久精品软件| 久久精品免费网站网| 全免费毛片在线播放| vvvv99日韩精品亚洲| 久久久无码精品亚洲日韩蜜桃| 亚洲国产成人超福利久久精品| 特级aaaaaaaaa毛片免费视频| 日本免费A级毛一片| 性色av无码免费一区二区三区| yy6080久久亚洲精品| 久久精品a亚洲国产v高清不卡| 日本亚洲欧美色视频在线播放| 香蕉视频在线免费看| 99精品全国免费观看视频| a级亚洲片精品久久久久久久| 亚洲日韩乱码中文无码蜜桃臀| 黄页网站在线免费观看| 一级做a爰全过程免费视频| 日韩一级免费视频| 亚洲av鲁丝一区二区三区| 亚洲爆乳大丰满无码专区| 国产精品99久久免费观看| 免费看的成人yellow视频| 亚洲AV无码一区二区乱子伦| 亚洲爆乳少妇无码激情| 亚洲成人免费在线| 亚洲AⅤ视频一区二区三区 | 亚洲成a人片77777群色| 一级做a爰黑人又硬又粗免费看51社区国产精品视 | 亚洲精品综合在线影院| 成人免费一区二区三区|