《Linux內核修鍊之道》精華分享與討論(11)——設備模型(上)

火星人 @ 2014-03-09 , reply:0
←手機掃碼閱讀

對於驅動開發來說,設備模型的理解是根本,毫不誇張得說,理解了設備模型,再去看那些五花八門的驅動程序,你會發現自己站在了另一個高度,從而有了一種俯視的感覺,就像鳳姐俯視知音和故事會,韓峰同志俯視女下屬.

顧名而思義就知道設備模型是關於設備的模型,既不是任小強們的房模,也不是張導的炮模.對咱們寫驅動的和不寫驅動的人來說,設備的概念就是匯流排和與其相連的各種設備了.電腦城的IT工作者都會知道設備是通過匯流排連到計算機上的,而且還需要對應的驅動才能用,可是匯流排是如何發現設備的,設備又是如何和驅動對應起來的,它們經過怎樣的艱辛才找到命里註定的那個他,它們的關係如何,白頭偕老型的還是朝三暮四型的,這些問題就不是他們關心的了,而是咱們需要關心的.在房市股市千錘百鍊的咱們還能夠驚喜的發現,這些疑問的中心思想中心辭彙就是匯流排、設備和驅動,沒錯,它們就是咱們這裡要聊的Linux設備模型的名角.

匯流排、設備、驅動,也就是bus、device、driver,既然是名角,在內核里都會有它們自己專屬的結構,在include/linux/device.h里定義.

52 struct bus_type {


53 const char * name;
54 struct module * owner;
55
56 struct kset subsys;
57 struct kset drivers;
58 struct kset devices;
59 struct klist klist_devices;
60 struct klist klist_drivers;
61
62 struct blocking_notifier_head bus_notifier;
63
64 struct bus_attribute * bus_attrs;
65 struct device_attribute * dev_attrs;
66 struct driver_attribute * drv_attrs;
67 struct bus_attribute drivers_autoprobe_attr;
68 struct bus_attribute drivers_probe_attr;
69
70 int (*match)(struct device * dev, struct device_driver * drv);
71 int (*uevent)(struct device *dev, char **envp,
72 int num_envp, char *buffer, int buffer_size);
73 int (*probe)(struct device * dev);
74 int (*remove)(struct device * dev);
75 void (*shutdown)(struct device * dev);
76
77 int (*suspend)(struct device * dev, pm_message_t state);
78 int (*suspend_late)(struct device * dev, pm_message_t state);
79 int (*resume_early)(struct device * dev);
80 int (*resume)(struct device * dev);
81
82 unsigned int drivers_autoprobe:1;
83 };

124 struct device_driver {
125 const char * name;
126 struct bus_type * bus;
127
128 struct kobject kobj;
129 struct klist klist_devices;
130 struct klist_node knode_bus;


131
132 struct module * owner;
133 const char * mod_name; /* used for built-in modules */
134 struct module_kobject * mkobj;
135
136 int (*probe) (struct device * dev);
137 int (*remove) (struct device * dev);
138 void (*shutdown) (struct device * dev);
139 int (*suspend) (struct device * dev, pm_message_t state);
140 int (*resume) (struct device * dev);
141 };

407 struct device {
408 struct klist klist_children;
409 struct klist_node knode_parent; /* node in sibling list */
410 struct klist_node knode_driver;
411 struct klist_node knode_bus;
412 struct device *parent;
413
414 struct kobject kobj;
415 char bus_id[BUS_ID_SIZE]; /* position on parent bus */
416 struct device_type *type;
417 unsigned is_registered:1;
418 unsigned uevent_suppress:1;
419
420 struct semaphore sem; /* semaphore to synchronize calls to
421 * its driver.
422 */
423
424 struct bus_type * bus; /* type of bus device is on */
425 struct device_driver *driver; /* which driver has allocated this
426 device */
427 void *driver_data; /* data private to the driver */
428 void *platform_data; /* Platform specific data, device
429 core doesn't touch it */
430 struct dev_pm_info power;
431
432 #ifdef CONFIG_NUMA
433 int numa_node; /* NUMA node this device is close to */
434 #endif


435 u64 *dma_mask; /* dma mask (if dma'able device) */
436 u64 coherent_dma_mask;/* Like dma_mask, but for
437 alloc_coherent mappings as
438 not all hardware supports
439 64 bit addresses for consistent
440 allocations such descriptors. */
441
442 struct list_head dma_pools; /* dma pools (if dma'ble) */
443
444 struct dma_coherent_mem *dma_mem; /* internal for coherent mem
445 override */
446 /* arch specific additions */
447 struct dev_archdata archdata;
448
449 spinlock_t devres_lock;
450 struct list_head devres_head;
451
452 /* class_device migration path */
453 struct list_head node;
454 struct class *class;
455 dev_t devt; /* dev_t, creates the sysfs "dev" */
456 struct attribute_group **groups; /* optional groups */
457
458 void (*release)(struct device * dev);
459 };

有沒有發現它們的共性是什麼?對,不是很傻很天真,而是很長很複雜.不過不妨把它們看成藝術品,既然是藝術,當然不會讓你那麼容易的就看懂了,不然怎麼稱大師稱名家.這麼想想咱們就會比較的寬慰了,阿Q是魯迅對咱們80后最大的貢獻.

我知道進入了21世紀,最缺的就是耐性,房價股價都讓咱們沒有耐性,內核的代碼也讓人沒有耐性.不過做為最沒有耐性的一代人,還是要平心靜氣的掃一下上面的結構,我們會發現,struct bus_type中有成員struct kset drivers 和struct kset devices,同時struct device中有兩個成員struct bus_type * bus和struct device_driver *driver,struct device_driver中有兩個成員struct bus_type * bus和struct klist klist_devices.先不說什麼是klist、kset,光從成員的名字看,它們就是一個完美的三角關係.我們每個人心中是不是都有兩個她?一個夢中的她,一個現實中的她.

憑一個男人的直覺,我們可以知道,struct device中的bus表示這個設備連到哪個匯流排上,driver表示這個設備的驅動是什麼,struct device_driver中的bus表示這個驅動屬於哪個匯流排,klist_devices表示這個驅動都支持哪些設備,這裡device是複數,又是list,更一個驅動可以支持多個設備,而一個設備只能綁定一個驅動.當然,struct bus_type中的drivers和devices分別表示了這個匯流排擁有哪些設備和哪些驅動.

單憑直覺,張鈺紅不了.我們還需要看看什麼是klist、kset.還有上面device和driver結構里出現的kobject結構是什麼?作為一個五星紅旗下長大的孩子,我可以肯定的告訴你,kobject和kset都是Linux設備模型中最基本的元素,匯流排、設備、驅動是西瓜,kobjcet、klist是種瓜的人,沒有幕後種瓜人的汗水不會有清爽解渴的西瓜,我們不能光知道西瓜的的甜,還要知道種瓜人的辛苦.kobject和kset不會在意自己的得失,它們存在的意義在於把匯流排、設備和驅動這樣的對象連接到設備模型上.種瓜的人也不會在意自己的汗水,在意的只是能不能送出甜蜜的西瓜.

一般來說應該這麼理解,整個Linux的設備模型是一個OO的體系結構,匯流排、設備和驅動都是其中鮮活存在的對象,kobject是它們的基類,所實現的只是一些公共的介面,kset是同種類型kobject對象的集合,也可以說是對象的容器.只是C里不可能會有C 里類的class繼承、組合等的概念,只有通過kobject嵌入到對象結構里來實現.這樣,內核使用kobject將各個對象連接起來組成了一個分層的結構體系,就好像馬列主義將我們13億人也連接成了一個分層的社會體系一樣.kobject結構里包含了parent成員,指向了另一個kobject結構,也就是這個分層結構的上一層結點.而kset是通過鏈表來實現的,這樣就可以明白,struct bus_type結構中的成員drivers和devices表示了一條匯流排擁有兩條鏈表,一條是設備鏈表,一條是驅動鏈表.我們知道了匯流排對應的數據結構,就可以找到這條匯流排關聯了多少設備,又有哪些驅動來支持這類設備.

那麼klist呢?其實它就包含了一個鏈表和一個自旋鎖,我們暫且把它看成鏈表也無妨,本來在2.6.11內核里,struct device_driver結構的devices成員就是一個鏈表類型.這麼一說,咱們上面的直覺都是正確的,如果買股票,摸彩票時直覺都這麼管用,就不會有咱們這被壓扁的一代了.

現在的人都知道,三角關係很難處.那麼匯流排、設備和驅動之間是如何和諧共處那?先說說匯流排中的那兩條鏈表是怎麼形成的.內核要求每次出現一個設備就要向匯流排彙報,或者說註冊,每次出現一個驅動,也要向匯流排彙報,或者說註冊.比如系統初始化的時候,會掃描連接了哪些設備,並為每一個設備建立起一個struct device的變數,每一次有一個驅動程序,就要準備一個struct device_driver結構的變數.把這些變數統統加入相應的鏈表,device 插入devices 鏈表,driver插入drivers鏈表.這樣通過匯流排就能找到每一個設備,每一個驅動.然而,假如計算機里只有設備卻沒有對應的驅動,那麼設備無法工作.反過來,倘若只有驅動卻沒有設備,驅動也起不了任何作用.在他們遇見彼此之前,雙方都如同路埂的野草,一個飄啊飄,一個搖啊搖,誰也不知道未來在哪裡,只能在生命的風裡飄搖.於是匯流排上的兩張表裡就慢慢的就掛上了那許多孤單的靈魂.devices開始多了,drivers開始多了,他們像是來自兩個世界,devices們彼此取暖,drivers們一起狂歡,但他們有一點是相同的,都只是在等待屬於自己的那個另一半.

現在,匯流排上的兩條鏈表已經有了,這個三角關係三個邊已經有了兩個,剩下的那個那?鏈表裡的設備和驅動又是如何聯繫那?先有設備還是先有驅動?很久很久以前,在那激情燃燒的歲月里,先有的是設備,每一個要用的設備在計算機啟動之前就已經插好了,插放在它應該在的位置上,然後計算機啟動,然後操作系統開始初始化,匯流排開始掃描設備,每找到一個設備,就為其申請一個struct device結構,並且掛入匯流排中的devices鏈表中來,然後每一個驅動程序開始初始化,開始註冊其struct device_driver結構,然後它去匯流排的devices鏈表中去尋找(遍歷),去尋找每一個還沒有綁定驅動的設備,即struct device中的struct device_driver指針仍為空的設備,然後它會去觀察這種設備的特徵,看是否是他所支持的設備,如果是,那麼調用一個叫做device_bind_driver的函數,然後他們就結為了秦晉之好.換句話說,把struct device中的struct device_driver driver指向這個驅動,而struct device_driver driver把struct device加入他的那張struct klist klist_devices鏈表中來.就這樣,bus、device和driver,這三者之間或者說他們中的兩兩之間,就給聯繫上了.知道其中之一,就能找到另外兩個.一榮俱榮,一損俱損.

但現在情況變了,在這紅蓮綻放的日子裡,在這櫻花傷逝的日子裡,出現了一種新的名詞,叫熱插拔.設備可以在計算機啟動以後在插入或者拔出計算機了.因此,很難再說是先有設備還是先有驅動了.都有可能.設備可以在任何時刻出現,而驅動也可以在任何時刻被載入,,出現的情況就是,每當一個struct device誕生,它就會去bus的drivers鏈表中尋找自己的另一半,反之,每當一個一個struct device_driver誕生,它就去bus的devices鏈表中尋找它的那些設備.如果找到了合適的,那麼OK,和之前那種情況一下,調用device_bind_driver綁定好.如果找不到,沒有關係,等待吧,等到曇花再開,等到風景看透,心中相信,這世界上總有一個人是你所等的,只是還沒有遇到而已.

推薦博文:Linux內核“問題門”——學習問題、經驗集錦





[火星人 via ] 《Linux內核修鍊之道》精華分享與討論(11)——設備模型(上)已經有362次圍觀

http://www.coctec.com/docs/linux/show-post-51670.html