linux支持的設備越來越多,種類越來越多,設備本身的功能也是越來越復雜,而操作系統(tǒng)內(nèi)核必須有一種很有效的方式來管理這些設備,最起碼的要控 制它們的開啟關(guān)閉,更進一步要控制它們進行協(xié)同工作,實際上要內(nèi)核僅僅做到這些并不難,關(guān)鍵問題是如何與用戶進行交互,那么多設備怎么以統(tǒng)一的方式提供給 用戶, 畢竟最終要控制設備的還是用戶啊,在2.6內(nèi)核中引出了一個叫做kobject的數(shù)據(jù)結(jié)構(gòu),它的作用和著名的list_head一樣,只不過后者是一條環(huán) 鏈而它卻是一棵樹。學習2.6內(nèi)核的驅(qū)動有兩個意義:1.學會以后寫個驅(qū)動;2.學習這一切的思想,作者為什么能想到這些。我自己寫過一些驅(qū)動,根據(jù)經(jīng)驗 2.6的內(nèi)核框架有兩條線索,一條就是以kobject為中心往上走,一直和vfs相接直取用戶空間;另一條就是內(nèi)核內(nèi)部的一些鏈表,底層意義上把設備分 類,按照設備的性質(zhì)進行匯總。先看看第一條線索的基礎設施: struct kobject { const char * k_name; char name[KOBJ_NAME_LEN]; struct kref kref; struct list_head entry; struct kobject * parent; struct kset * kset; struct kobj_type * ktype; struct dentry * dentry; wait_queue_head_t poll; }; 再看看第二條線索的基礎設施: struct klist { spinlock_t k_lock; struct list_head k_list; void (*get)(struct klist_node *); void (*put)(struct klist_node *); }; struct klist_node { struct klist *n_klist; struct list_head n_node; struct kref n_ref; struct completion n_removed; }; 這
是最底層的數(shù)據(jù)結(jié)構(gòu)了,你可以把它們當作“基類”,基類在面向?qū)ο蟮乃枷胫芯褪鞘裁匆膊蛔鰞H僅提供接口的類,它們更實質(zhì)的意義是為管理設備提供了一個切入
點,這些數(shù)據(jù)結(jié)構(gòu)主要是為了給上層一個統(tǒng)一的操作視圖,不管是內(nèi)核本身使用還是用戶使用,可以參看sysfs,這樣用戶端的操作就靠kobject搞定
了,內(nèi)核的操作就靠klist搞定。接下來設備本身怎么管理呢?所有的設備被分為“類”,叫class,比如一塊via的聲卡和一塊realtek的聲卡
就屬于一個類,余下的就是一堆鏈表了,一條總線有兩個重要鏈表,一條掛載所有設備,一條掛載所有驅(qū)動,類也有一個鏈表,掛載屬于這個類的設備,所有的類串
成串,所有的總線也串成串,不要以為操作系統(tǒng)多復雜,基本就是一堆鏈表,那些在書上看到的十分牛叉的算法在內(nèi)核基本是見不到的,作為一個統(tǒng)一的管理者,內(nèi)
核數(shù)據(jù)結(jié)構(gòu)和算法要在維護開銷,自身開銷,綜合性能之間找到一個平衡點,比如說你把大量的技巧用到了內(nèi)核的設備管理上了,那么結(jié)果有二,不是浪費空間就是
浪費時間,只要你有規(guī)則有技巧,規(guī)則和技巧越復雜你為之付出的代價就越大,這是個真理,結(jié)果用戶的資源全被內(nèi)核耗盡了,主次不分,因果倒置。 int device_add(struct device *dev) { struct device *parent = NULL; struct class_interface *class_intf; int error = -EINVAL; ... parent = get_device(dev->parent); setup_parent(dev, parent); if (parent)//下面的設置時第二條線索相關(guān)的 set_dev_node(dev, dev_to_node(parent)); //設置第一條線索,這是個基礎,一切從kobject開始 error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id); ... ... //設置第一條線索,加入一個事件屬性文件,以便用戶空間寫入數(shù)據(jù)可以觸發(fā)內(nèi)核的一些動作 error = device_create_file(dev, &uevent_attr); ... //設置第一條線索,加入其它屬性文件,以便用戶空間可以讀取或更改設備的屬性 if (MAJOR(dev->devt)) { error = device_create_file(dev, &devt_attr); if (error) goto ueventattrError; error = device_create_sys_dev_entry(dev); if (error) goto devtattrError; } //設置第一條線索,設備所屬的類被導入sysfs,此處正是將設備加入sysfs的相應類 error = device_add_class_symlinks(dev); if (error) goto SymlinkError; error = device_add_attrs(dev);//設置第一條線索,將sysfs的類信息鏈入本設備 if (error) goto AttrsError; error = bus_add_device(dev); //設置第一條線索,將sysfs的總線信息鏈入本設備 if (error) goto BusError; error = dpm_sysfs_add(dev); if (error) goto DPMError; ... kobject_uevent(&dev->kobj, KOBJ_ADD); bus_attach_device(dev); //設置第二條線索,在總線上加入設備 if (parent)//設置第二條線索,將父子關(guān)系確定 klist_add_tail(&dev->knode_parent, ∥ent->klist_children); if (dev->class) {//設置第二條線索,將本設備加入相應的類別 mutex_lock(&dev->class->p->class_mutex); /* tie the class to the device */ list_add_tail(&dev->node, &dev->class->p->class_devices); list_for_each_entry(class_intf, &dev->class->p->class_interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); mutex_unlock(&dev->class->p->class_mutex); } ... } 第二條線索的關(guān)鍵操作就是bus_attach_device
了,它本質(zhì)上就是把本設備加入它所屬總線的klist,然后總線會遍歷它的另一個klist驅(qū)動鏈表來尋找能驅(qū)動本設備的驅(qū)動程序,如果找到便開始
probe本設備。相應的在driver_register的時候會將driver加入bus的driver鏈表,然后遍歷設備鏈表看它能驅(qū)動哪些設備,
要注意的是,雖然device和driver在bus的角度看是如此對稱,實際上它們是不對稱的,因為device才是我們要管理的對象,driver的
出現(xiàn)就是為了我們的設備可以工作,所以代碼中driver_register遠遠沒有device_register復雜,因為它只需要設置第一條線索和
部分第二條線索就可以了。 |
|
來自: rookie > 《技術(shù)帖》