#在裝置模型中,Bus、Device、Device driver等等都比較好理解,因為它們都對應著實實在在的東西,所有的邏輯都是圍繞著這些實體展開的。然而,本文所要描述的Class則有些不同,因為它是虛擬出來的,只是為了抽象設備的共通性。
#舉個例子,一些年齡相仿、需要獲取相似知識的人聚在一起學習,就構成了一個班級(Class)。這個班級可以有自己的名稱(如“295”),但如果離開構成它的學生(Device),它就沒有任何存在意義。另外,班級存在的最大意義是什麼呢?是由老師講授的每一個課程!因為老師只要講一遍,一個班的學生都可以聽到。如果每個學生都在家學習,就要為每個人請一個老師,這樣講授一遍。而講的內容大多是一樣的,這就是極大的浪費。
設備模型中的Class提供了類似的功能。例如,一些相似的Device(學生)需要向使用者空間提供相似的介面(課程)。如果每個裝置的驅動都要實現一遍的話,就會導致核心中存在大量的冗餘程式碼,這就是極大的浪費。所以,Class說了:「我來幫你們實現吧,你們會用就可以了。」
這就是設備模型中Class的功能。再結合內核的註解:A class is a higher-level view of a device that abstracts out low-level implementation details(include/linux/device.h line326),就容易理解了。
struct class是class的抽象,它的定義如下:
1: /* include/linux/device.h, line 332 */ 2: struct class { 3: const char *name; 4: struct module *owner; 5: 6: struct class_attribute *class_attrs; 7: struct device_attribute *dev_attrs; 8: struct bin_attribute *dev_bin_attrs; 9: struct kobject *dev_kobj; 10: 11: int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); 12: char *(*devnode)(struct device *dev, umode_t *mode); 13: 14: void (*class_release)(struct class *class); 15: void (*dev_release)(struct device *dev); 16: 17: int (*suspend)(struct device *dev, pm_message_t state); 18: int (*resume)(struct device *dev); 19: 20: const struct kobj_ns_type_operations *ns_type; 21: const void *(*namespace)(struct device *dev); 22: 23: const struct dev_pm_ops *pm; 24: 25: struct subsys_private *p; 26: };
「
#其實struct class和struct bus很類似,解釋如下:
name,class的名稱,會在「/sys/class/」目錄下體現。
class_atrrs,該class的預設attribute,會在class註冊到核心時,自動在「/sys/class/xxx_class」下建立對應的attribute檔案。
dev_attrs,該class下每個裝置的attribute,會在裝置註冊到核心時,自動在該裝置的sysfs目錄下建立對應的attribute檔案。
dev_bin_attrs,類似dev_attrs,只不過是二進位類型attribute。
dev_kobj,表示該class下的裝置在/sys/dev/下的目錄,現在一般有char和block兩個,如果dev_kobj為NULL,則預設選擇char。
dev_uevent,當該class下有裝置發生變化時,會呼叫class的uevent回呼函數。
class_release,用於release本身的回呼函數。
dev_release,用於release class內裝置的回呼函數。在device_release介面中,會依序檢查Device、Device Type以及Device所在的class,是否註冊release接口,若有則呼叫對應的release接口release設備指標。
p,和「Linux設備模型(6)_Bus」中struct bus結構一樣,不再說明。
」
#struct class_interface是這樣的一個結構:它允許class driver在class下有裝置加入或移除的時候,呼叫預先設定好的回調函數(add_dev和remove_dev)。那調用它們做什麼呢?想做什麼都行(例如修改設備的名稱),由具體的class driver實作。
該結構的定義如下:
1: /* include/linux/device.h, line 434 */ 2: struct class_interface { 3: struct list_head node; 4: struct class *class; 5: 6: int (*add_dev) (struct device *, struct class_interface *); 7: void (*remove_dev) (struct device *, struct class_interface *); 8: };
看完上面的东西,蜗蜗依旧糊里糊涂的,class到底提供了什么功能?怎么使用呢?让我们先看一下现有Linux系统中有关class的状况(这里以input class为例):
“
root@android:/ # ls /sys/class/input/ -l
lrwxrwxrwx root root 2014-04-23 03:39 event0 -> ../../devices/platform/i2c-gpio.17/i2c-17/17-0066/max77693-muic/input/input0/event0
lrwxrwxrwx root root 2014-04-23 03:39 event1 -> ../../devices/platform/gpio-keys.0/input/input1/event1
lrwxrwxrwx root root 2014-04-23 03:39 event10 -> ../../devices/virtual/input/input10/event10
lrwxrwxrwx root root 2014-04-23 03:39 event2 -> ../../devices/platform/s3c2440-i2c.3/i2c-3/3-0048/input/input2/event2
…
lrwxrwxrwx root root 2014-04-23 03:39 event8 -> ../../devices/platform/soc-audio/sound/card0/input8/event8
lrwxrwxrwx root root 2014-04-23 03:39 event9 -> ../../devices/platform/i2c-gpio.8/i2c-8/8-0020/input/input9/event9
lrwxrwxrwx root root 2014-04-23 03:39 input0 -> ../../devices/platform/i2c-gpio.17/i2c-17/17-0066/max77693-muic/input/input0
…
lrwxrwxrwx root root 2014-04-23 03:39 mice -> ../../devices/virtual/input/miceroot@android:/ # ls /sys/devices/platform/s3c2440-i2c.3/i2c-3/3-0048/input/input2/event2/ -l
-r–r–r– root root 4096 2014-04-23 04:08 dev
lrwxrwxrwx root root 2014-04-23 04:08 device -> ../../input2
drwxr-xr-x root root 2014-04-23 04:08 power
lrwxrwxrwx root root 2014-04-23 04:08 subsystem -> ../../../../../../../../class/input
-rw-r–r– root root 4096 2014-04-23 04:08 ueventroot@android:/ # ls /sys/devices/virtual/input/mice/ -l
-r–r–r– root root 4096 2014-04-23 03:57 dev
drwxr-xr-x root root 2014-04-23 03:57 power
lrwxrwxrwx root root 2014-04-23 03:57 subsystem -> ../../../../class/input
-rw-r–r– root root 4096 2014-04-23 03:57 uevent”
看上面的例子,发现input class也没做什么实实在在的事儿,它(input class)的功能,仅仅是:
算了,我们还是先分析一下Class的核心逻辑都做了哪些事情,至于class到底有什么用处,可以在后续具体的子系统里面(如input子系统),更为细致的探讨。
“
class的注册,是由__class_register接口(它的实现位于”drivers/base/class.c, line 609″)实现的,它的处理逻辑和bus的注册类似,主要包括:
- 為class結構中的struct subsys_private類型的指標(cp)分配空間,並初始化其中的字段,包括cp->subsys.kobj.kset、cp->subsys.kobj.ktype等等
- 呼叫kset_register,註冊該class(回憶「Linux裝置模型(6)_Bus」中的描述,一個class就是一個子系統,因此註冊class也是註冊子系統)。過程結束後,在/sys/class/目錄下,就會建立對應該class(子系統)的目錄
- # 呼叫add_class_attrs接口,將class結構中class_attrs指標所指向的attribute,加入核心。執行完後,在/sys/class/xxx_class/目錄下,就會看到這些attribute對應的檔案
」
在」Linux裝置模型(5)_device和device driver」中,我們有講過struct device和struct device_driver這兩個資料結構,其中struct device結構會包含一個struct class指標(這從側面說明了class是device的集合,甚至,class可以是device的driver)。當某個class driver向核心註冊了一個class後,需要使用該class的device,透過把自身的class指標指向該class即可,剩下的事情,就由核心在註冊device時處理了。
本節,我們講一下在device註冊時,和class有關的動作:
「
#device的註冊最終是由device_add介面(drivers/base/core.c)實現了,該介面中和class有關的動作包括:
- 呼叫device_add_class_symlinks接口,建立3.1小節所描述的各種符號鏈接,即:在對應class的目錄下,建立指向device的符號連結;在device的目錄下,建立名稱為subsystem、指向對應class目錄的符號連結
- 呼叫device_add_attrs,新增由class指定的attributes(class->dev_attrs)
- 如果存在對應該class的add_dev回呼函數,呼叫該回呼函數
#」
#其實在這篇文章結束後,蝸蝸依舊沒有弄清楚class在核心到底是怎麼使用的。不過沒關係,在後續的子系統的分析中(如input子系統、RTC子系統等),我們會看到很多class的使用用例。到時候,再回過頭總結,就會很清楚了。
以上是詳解Linux設備模型(7)_Class的詳細內容。更多資訊請關注PHP中文網其他相關文章!