一下只是個人學習后的理解,可能有很多不對的地方。
要學習linux的設備驅動模型,首先必須要知道kobject和kset的概念,下面是kobject在2.6.38的源碼中的實現。
struct kobject {const char *name; //名稱,可能在sysfs中創建對應的目錄struct list_head entry; //標準鏈表,用于被kset連接起來struct kobject *parent; //指向父kobject的指針struct kset *kset; //指向所屬的kset的指針struct kobj_type *ktype; //包含的kobj_type, 用指向不同的ktype來表示不同的objectstruct 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;unsigned int uevent_suppress:1; };
kobject里面比較重要的一個是kobj_type結構的指針:
struct kobj_type {void (*release)(struct kobject *kobj); //當此kobj被完全釋放(引用為0)時調用const struct sysfs_ops *sysfs_ops; //與sysfs相關的操作struct attribute **default_attrs; //代表了一系列屬性。const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);const void *(*namespace)(struct kobject *kobj); };
kobject中的ktype指向不同的kobj_type可以代表著不同的類型,以及所執行的不同操作。kobject可能在sysfs中表現為一個目錄,而它指向的kobj_type中包含的一系列struct attribute則代表著這個目錄下的文件。每一個屬性對應了一個文件,可以設置它的讀寫權限,名稱(文件名)還有就是當讀或者寫這些文件時,調用的show和store函數。可以利用這些屬性文件,進行信息的展示和配置的修改等。
kobject只是代表著某個結點,并不能構成特定的結構,所以還存在一個kset,使得它們可以構成一個樹狀結構。kset的實現如下:
struct kset {struct list_head list; //鏈表頭,用以將下層的kobject串起來spinlock_t list_lock; //鏈表的鎖,只能串行操作struct kobject kobj; //kset包含一個kobjectconst struct kset_uevent_ops *uevent_ops; //為了支持熱插拔所提供的結構 };
kset里面包含了一個kobject,而不止是一個指針。所以可以把kset看成是一個kobject的擴展,所有對kobject的操作,都適用與kset(操作其中的kobject,但是內核為kobject和kset分別提供了一套類似的操作)。如果用C++表述,kobject就是一個基類,而kset是它的派生類。kset可以通過它的list包含住很多子kobject,而這些子kobject又可以通過parent和kset指針指向它的父kobject(kset);并且kset也是(包含了)一個kobject,所以它還可以被其它的kset當做子kobject。這樣就構成了一個樹狀結構,其中kobject是葉子結點,而kset是非葉子結點或者根結點,正好和目錄與文件的關系類似。目錄本身是一個文件,但是它可以包含其它文件或目錄,同時又被其它目錄含(根目錄除外)。所以sysfs中的文件結構通常對應了內核中的kobject結構。每一個目錄代表一個kobject(或者kset),而此目錄里面的子目錄又代表了這個kobject下面的子kobject,目錄里的文件代表了kobject的一系列屬性。
而kset_uevent_ops(對應老版內核中的hotplug,熱插拔)指向了一組操作,包括過濾,熱插拔等,當往一個kset中添加新的kobject時,便會觸發這些操作,首先會調用filter表示是否過濾這個事件,然后在調用熱插拔處理函數來向環境變量里添加值,從而達到通知用戶空間的目的。假如次kset沒有設置這個uevent_ops指針,便會往它的父kobject迭代查找,直到找到為止(若到根kobject都沒有,就忽略)。再進行調用。
kobject通常不單獨使用,而是嵌入到其他結構,代表就是pci core和usb core,它們是內核提供的,利用kobject來構造的樹狀結構驅動框架。
linux內核利用以上的框架,包裝出了三個概念,那就是總線,設備,和驅動。總線可以看成是樹狀結構中的非葉子結點(包括根結點),設備和驅動可以看成其中的葉子結點;當有設備插入,便可利用上面uevent_ops的機制,添加環境變量,然后調用udev(hotplug)來判斷插入設備的類型,然后根據類型查找modules.***map表,找到合適的驅動程序,加載其入內核;當驅動程序加載到內核的時候,同樣會想總線注冊,也會激活uevent_ops的操作,這時系統會對設備和驅動進行匹配,若匹配正確,就會調用驅動程序里的probe函數。當設備取下時,系統會調用驅動程序的remove函數。這樣就可以支持熱插拔。
linux內核還提供了usb和pci等總線的抽象,它們是在bus,device和driver之上進行進一步的包裝。內核已經把總線和設備的部分完成了,驅動作者只需要關注驅動程序的實現即可。例如,linux內核提供的usb core已經完成了usb的大部分工作,包括總線驅動,以及usb框架,還有設備的識別和初始化等,以及設備和驅動之間進行通信的urb方式,甚至還包含了大部分設備的驅動程序,但是有的設備驅動內核并沒有。我們會在windows經常看到一種情況,某個設備被識別了,但是沒有驅動程序而不能被使用,其中識別出設備就是內核所完成的,而這樣的驅動往往就必須由設備提供者自己實現了。總之在內核抽象出一套驅動框架之上,驅動作者可以免去很多的工作。