驅動之路-設備模型(上)底層模型

一、重要知識點

 

         1.Sysfs文件系統

       Sysfs文件系統是一種類似於proc文件系統的特殊文件系統,它存在於內存當中,當系統啓動時由內核掛載於內存當中。用於將系統中的設備組織成層次結構,並向用戶模式程序提供詳細的數據結構信息。

 

    2.Linux設備底層模型

 

       1)爲什麼要使用設備模型

       隨着系統的拓撲結構越來越複雜,以及要支持諸如電源管理等新特性的要求,於是在2.6的內核中出現了設備模型。設備模型其實就是一套數據結構建立起來的模型。內核使用該模型支持了多種不同的任務,包括:

       a.電源管理和系統關機

       設備模型使操作系統能夠以正確的順序遍歷系統硬件。

       b.與用戶空間通信

       Sysfs文件系統向用戶空間提供系統信息以及改變操作參數的結構。

       c.熱插拔事件

       d.設備類型

       系統中許多部分對設備如何連接不感興趣,但是他們需要知道哪些類型設備時可用的。設備模型提供了將設備分類的機制。

       e.對象的生命週期

       上述的許多功能,包括熱插拔支持和sysfs,使得內核中管理對象的工作更爲複雜。設備模型需要創造一套機制管理對象的生命週期。

 

       2)Kobject

       如果說設備模型是一套房子的話,Kobject就是構造房子的磚塊。每個註冊的Kobject的都對應與Sysfs文件系統中的一個目錄。Kobject是組成設備模型的基本結構。類似於C++的基類,它潛入於更大的對象中——所謂的容器,用來描述設備模型的組件。如bus,device,drivers都是典型的容器。這些容器就是通過kobject連接起來,形成一個樹狀結構。這個樹狀結構就與/sys文件系統對應。不過kobject只能建立單層結構,也就是隻能建立一級目錄,要建立多級目錄,還要使用後面要介紹的Kset。

Kobject結構定義爲:

struct kobject {

char * k name; 指向設備名稱的指針

char name[KOBJ NAME LEN]; 設備名稱

struct kref kref; 對象引用計數

struct list head entry; 掛接到所在kset中去的單元

struct kobject * parent; 指向父對象的指針

struct kset * kset; 所屬kset的指針

struct kobj type * ktype; 指向其對象類型描述符的指針

struct dentry * dentry; sysfs文件系統中與該對象對應的文件節點路徑指針

};

相關操作函數:

void kobjet_init(struct kobject*kobj)

初始化Kobject

int kobject_add(struct kobject*kobj)

將Kobject對象註冊到linux系統,如果失敗則返回一個錯誤碼.

int kobject_init_and_add(structkobject *kobj, kobj_type *ktype, struct kobject *parent, const *fmt…)

初始化並註冊kobject,kobject傳入要初始化的Kobject對象,ktype將在後面介紹到,parent指向上級的kobject對象,如果指定位NULL,將在/sys的頂層創建一個目錄。*fmt爲kobject對象的名字。

kobject的ktype對象是一個指向kobject_type結構的指針,該結構記錄了kobject對象的一些屬性。每個kobject都需要對應一個相應的kobject結構。

struct kobj_type{

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

       structsysfs_ops *sysfs_ops;

       structattribute **default_attrs;

};

release方法用於釋放kobject佔用的資源,當kobject引用計數爲0時被調用。

kobje_type的attribute成員:

struct attribute{

              char*name;//屬性文件名

       structmodule *owner;

       mode_tmode;

}

struct attribute(屬性):對應於kobject的目錄下一個文件,name就是文件名。

kobje_type的struct sysfs_ops成員:

struct sysfs_ops

{

ssize_t (*show)(structkobejct *,  struct attribute *,  char  *name);

ssize_t (*store)(structkobejct *,  struct attribute *,  char  *name);

}

       show:當用戶讀屬性文件時,該函數被調用,該函數將屬性值存入buffer中返回給用戶態;

       store:當用戶寫屬性文件時,該函數被調用,用於存儲用戶存入的屬性值。

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("David Xie");   MODULE_LICENSE("Dual BSD/GPL");       void obj_test_release(struct kobject *kobject);   ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf);   ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count);       struct attribute test_attr = {           .name = "kobj_config",           .mode = S_IRWXUGO,   };       static struct attribute *def_attrs[] = {           &test_attr,           NULL,   };           struct sysfs_ops obj_test_sysops =   {           .show = kobj_test_show,           .store = kobj_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 kobj_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 kobj_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 kobj_test_init()   {           printk("kboject test init.\n");           kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");           return 0;   }       static int kobj_test_exit()   {           printk("kobject test exit.\n");           kobject_del(&kobj);           return 0;   }       module_init(kobj_test_init);   module_exit(kobj_test_exit);  

 

測試結果:



 

在/sys目錄下創建了kobject_test目錄

 

在kobject_test目錄下有kobj_config文件

 

讀kobject_config文件則調用了show函數。並在用戶空間顯示了show返回的kobject對象名字。

 

寫kobject_config文件調用了store函數。

 

 

 

3)Kset

 

kset的主要功能是包容;我們可以認爲他他是kobject的頂層容器。實際上,在每個kset對象的內部,包含了自己的kobject,並且可以用多種處理kobject的方法處理kset。如果說kobject是基類的話,那麼kset就是派送類。kobject通過kset組織成層次化的結構,kset是相同類型的組合。通俗的講,kobject建立一級的子目錄,kset可以爲kobject建立多級的層次性的父目錄。

 

struct kset {

 

struct subsystem * subsys; 所在的subsystem的指針

 

struct kobj type * ktype; 指向該kset對象類型描述符的指針

 

struct list head list; 用於連接該kset中所有kobject的鏈表頭

 

struct kobject kobj; 嵌入的kobject

 

struct  kset_uevent_ops * uevent_ops; 指向熱插拔操作表的指針

 

};

包含在kset中的所有kobject被組織成一個雙向循環鏈表,list域正是該鏈表的頭。Ktype域指向一個kobj type結構,被該kset中的所有kobject共享,表示這些對象的類型。Kset數據結構還內嵌了一個kobject對象(由kobj域表示),所有屬於這個kset 的kobject對象的parent域均指向這個內嵌的對象。此外,kset還依賴於kobj維護引用計數:kset的引用計數實際上就是內嵌的kobject對象的引用計數。

kset與kobject的關係圖


 

 

Kset操作:

int kset_register(struct kset*kset)

註冊kset

void kset_unregister(struct kset*kset)

註銷kset

       熱插拔事件:在linux系統中,當系統配置發生變化時,如添加kset到系統或移動kobject,一個通知會從內核空間發送到用戶空間,這就是熱插拔事件。熱插拔事件會導致用戶空間中的處理程序(如udev,mdev)被調用,這些處理程序會通過加載驅動程序,創建設備節點等來響應熱插拔事件。

       對熱插拔事件的實際控制是由struct kset_uevent_ops結構中的函數完成的。

       struct kset_uevnt_ops{

       int (*filter)(struct kset *kset,struct  kobject *kobj);

       const char *(*name)(struct kset *kset, struct kobject *kobj );

       int (*uevent)(struct kset *kset,struct  kobject *kobj,struct kobj_uevent *env);

}

filter決定是否產生事件,如果返回0,將不產生事件。

name向用戶空間傳遞一個合適的字符串

uevent通過環境變量傳遞任何熱插拔腳本需要的信息,他會在(udev或mdev)調用之前,提供添加環境變量的機會。

 

       kset測試模塊:

#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("David Xie");   MODULE_LICENSE("Dual BSD/GPL");       struct kset kset_p;   struct kset kset_c;      int kset_filter(struct kset *kset, struct kobject *kobj)   {           printk("Filter: kobj %s.\n",kobj->name);           return 1;   }       const char *kset_name(struct kset *kset, struct kobject *kobj)   {           static char buf[20];           printk("Name: kobj %s.\n",kobj->name);           sprintf(buf,"%s","kset_name");           return buf;   }       int kset_uevent(struct kset *kset, struct kobject *kobj,struct kobj_uevent_env *env)   {           int i = 0;           printk("uevent: kobj %s.\n",kobj->name);              while( i < env->envp_idx){                   printk("%s.\n",env->envp[i]);                   i++;           }              return 0;   }      struct kset_uevent_ops uevent_ops =    {           .filter = kset_filter,           .name   = kset_name,           .uevent = kset_uevent,   };       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加載時,在/sys下創建了一個kset_p,在kset_p下面創建了kset_c,當kset模塊被加載和卸載時都產生了熱插拔事件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章