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