一、重要知識點
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測試模塊:
測試結果:
在/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模塊被加載和卸載時都產生了熱插拔事件。