已閱讀5頁,還剩29頁未讀, 繼續(xù)免費閱讀
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
一:前言Linux設(shè)備模型是一個極其復(fù)雜的結(jié)構(gòu)體系,在編寫驅(qū)動程序的時候,通常不會用到這方面的東西,但是。理解這部份內(nèi)容,對于我們理解linux設(shè)備驅(qū)動的結(jié)構(gòu)是大有裨益的。我們不但可以在編寫程序程序的時候知其然,亦知其所以然。又可以學(xué)習(xí)到一種極其精致的架構(gòu)設(shè)計方法。由于之前已經(jīng)詳細分析了sysfs文件系統(tǒng)。所以本節(jié)的討論主要集中在設(shè)備模型的底層實現(xiàn)上。上層的接口,如pci.,usb ,網(wǎng)絡(luò)設(shè)備都可以看成是底層的封裝。二:kobject ,kset和ktypeKobject,kset,kypte這三個結(jié)構(gòu)是設(shè)備模型中的下層架構(gòu)。模型中的每一個元素都對應(yīng)一個kobject.kset和ktype可以看成是kobject在層次結(jié)構(gòu)與屬性結(jié)構(gòu)方面的擴充。將三者之間的關(guān)系用圖的方示描述如下:如上圖所示:我們知道。在sysfs中每一個目錄都對應(yīng)一個kobject.這些kobject都有自己的parent。在沒有指定parent的情況下,都會指向它所屬的kset-object。其次,kset也內(nèi)嵌了kobject.這個kobject又可以指它上一級的parent。就這樣。構(gòu)成了一個空間上面的層次關(guān)系。其實,每個對象都有屬性。例如,電源管理,執(zhí)插撥事性管理等等。因為大部份的同類設(shè)備都有相同的屬性,因此將這個屬性隔離開來,存放在ktype中。這樣就可以靈活的管理了.記得在分析sysfs的時候。對于sysfs中的普通文件讀寫操作都是由kobject-ktype-sysfs_ops來完成的.經(jīng)過上面的分析,我們大概了解了kobject.kset與ktype的大概架設(shè)與相互之間的關(guān)系。下面我們從linux源代碼中的分析來詳細研究他們的操作。三:kobject,kset和ktype的操作為了說明kobject的操作,先寫一個測試模塊,代碼如下:#include #include #include #include #include #include #include MODULE_AUTHOR(eric xiao);MODULE_LICENSE(Dual BSD/GPL);void obj_test_release(struct kobject *kobject);ssize_t eric_test_show(struct kobject *kobject, struct attribute *attr,char *buf);ssize_t eric_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count);struct attribute test_attr = .name = eric_xiao, .mode = S_IRWXUGO,;static struct attribute *def_attrs = &test_attr, NULL,;struct sysfs_ops obj_test_sysops = .show = eric_test_show, .store = eric_test_store,;struct kobj_type ktype = .release = obj_test_release, .sysfs_ops=&obj_test_sysops, .default_attrs=def_attrs,;void obj_test_release(struct kobject *kobject) printk(eric_test: release .n);ssize_t eric_test_show(struct kobject *kobject, struct attribute *attr,char *buf) printk(have show.n); printk(attrname:%s.n, attr-name); sprintf(buf,%sn,attr-name); return strlen(attr-name)+2;ssize_t eric_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count) printk(havestoren); printk(write: %sn,buf); return count;struct kobject kobj;static int kobject_test_init() printk(kboject test init.n); kobject_init_and_add(&kobj,&ktype,NULL,eric_test); return 0;static int kobject_test_exit() printk(kobject test exit.n); kobject_del(&kobj); return 0;module_init(kobject_test_init);module_exit(kobject_test_exit);加載模塊之后,會發(fā)現(xiàn),在/sys下多了一個eric_test目錄。該目錄下有一個叫eric_xiao的文件。如下所示:rootlocalhost eric_test# lseric_xiao用cat察看此文件:rootlocalhost eric_test# cat eric_xiao eric_xiao再用echo往里面寫點東西;rootlocalhost eric_test# echo hello eric_xiaoDmesg的輸出如下:have show.attrname:eric_xiao.havestorewrite: hello如上所示。我們看到了kobject的大概建立過程.我們來看一下kobject_init_and_add()的實現(xiàn)。在這個函數(shù)里,包含了對kobject的大部份操作。int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, .) va_list args; int retval; /初始化kobject kobject_init(kobj, ktype); va_start(args, fmt); /為kobjcet設(shè)置名稱,在sysfs中建立相關(guān)信息 retval = kobject_add_varg(kobj, parent, fmt, args); va_end(args); return retval;上面的流程主要分為兩部份。一部份是kobject的初始化。在這一部份,它將kobject與給定的ktype關(guān)聯(lián)起來。初始化kobject中的各項結(jié)構(gòu)。另一部份是kobject的名稱設(shè)置??臻g層次關(guān)系的設(shè)置,具體表現(xiàn)在sysfs文件系統(tǒng)中.對于第一部份,代碼比較簡單,這里不再贅述。跟蹤第二部份,也就是kobject_add_varg()的實現(xiàn).static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs) va_list aq; int retval; va_copy(aq, vargs); /設(shè)置kobject的名字。即kobject的name成員 retval = kobject_set_name_vargs(kobj, fmt, aq); va_end(aq); if (retval) printk(KERN_ERR kobject: can not set name properly!n); return retval; /設(shè)置kobject的parent。在上面的例子中,我們沒有給它指定父結(jié)點 kobj-parent = parent; /在sysfs中添加kobjcet信息 return kobject_add_internal(kobj);設(shè)置好kobject-name后,轉(zhuǎn)入kobject_add_internal()。在sysfs中創(chuàng)建空間結(jié)構(gòu).代碼如下:static int kobject_add_internal(struct kobject *kobj) int error = 0; struct kobject *parent; if (!kobj) return -ENOENT; /如果kobject的名字為空.退出 if (!kobj-name | !kobj-name0) pr_debug(kobject: (%p): attempted to be registered with empty name!n, kobj); WARN_ON(1); return -EINVAL; /取kobject的父結(jié)點 parent = kobject_get(kobj-parent); /如果kobject的父結(jié)點沒有指定,就將kset-kobject做為它的父結(jié)點 /* join kset if set, use it as parent if we do not already have one */ if (kobj-kset) if (!parent) parent = kobject_get(&kobj-kset-kobj); kobj_kset_join(kobj); kobj-parent = parent; /調(diào)試用 pr_debug(kobject: %s (%p): %s: parent: %s, set: %sn, kobject_name(kobj), kobj, _FUNCTION_, parent ? kobject_name(parent) : , kobj-kset ? kobject_name(&kobj-kset-kobj) : ); /在sysfs中創(chuàng)建kobject的相關(guān)元素 error = create_dir(kobj); if (error) /v如果創(chuàng)建失敗。減少相關(guān)的引用計數(shù) kobj_kset_leave(kobj); kobject_put(parent); kobj-parent = NULL; /* be noisy on error issues */ if (error = -EEXIST) printk(KERN_ERR %s failed for %s with -EEXIST, dont try to register things with the same name in the same directory.n, _FUNCTION_, kobject_name(kobj); else printk(KERN_ERR %s failed for %s (%d)n, _FUNCTION_, kobject_name(kobj), error); dump_stack(); else /如果創(chuàng)建成功。將state_in_sysfs建為1。表示該object已經(jīng)在sysfs中了 kobj-state_in_sysfs = 1; return error;這段代碼比較簡單,它主要完成kobject父結(jié)點的判斷和選定,然后再調(diào)用create_dir()在sysfs創(chuàng)建相關(guān)信息。該函數(shù)代碼如下:static int create_dir(struct kobject *kobj) int error = 0; if (kobject_name(kobj) /為kobject創(chuàng)建目錄 error = sysfs_create_dir(kobj); if (!error) /為kobject-ktype中的屬性創(chuàng)建文件 error = populate_dir(kobj); if (error) sysfs_remove_dir(kobj); return error;我們在上面的示例中看到的/sys下的eric_test目錄,以及該目錄下面的eric_xiao的這個文件就是這里被創(chuàng)建的。我們先看一下kobject所表示的目錄創(chuàng)建過程。這是在sysfs_create_dir()中完成的。代碼如下:int sysfs_create_dir(struct kobject * kobj) struct sysfs_dirent *parent_sd, *sd; int error = 0; BUG_ON(!kobj); /*如果kobject的parnet存在。就在目錄點的目錄下創(chuàng)建這個目錄。如果沒有父結(jié)點不存在,就在/sys下面創(chuàng)建結(jié)點。在上面的流程中,我們可能并沒有為其指定父結(jié)點,也沒有為其指定kset。*/ if (kobj-parent) parent_sd = kobj-parent-sd; else parent_sd = &sysfs_root; /在sysfs中創(chuàng)建目錄 error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd); if (!error) kobj-sd = sd; return error;在這里,我們就要聯(lián)系之前分析過的sysfs文件系統(tǒng)的研究了。如果不太清楚的,可以在找到那篇文章仔細的研讀一下。create_dir()就是在sysfs中創(chuàng)建目錄的接口,在之前已經(jīng)詳細分析過了。這里不再講述。接著看為kobject-ktype中的屬性創(chuàng)建文件。這是在populate_dir()中完成的。代碼如下:static int populate_dir(struct kobject *kobj) struct kobj_type *t = get_ktype(kobj); struct attribute *attr; int error = 0; int i; if (t & t-default_attrs) for (i = 0; (attr = t-default_attrsi) != NULL; i+) error = sysfs_create_file(kobj, attr); if (error) break; return error;這段代碼比較簡單。它遍歷ktype中的屬性。然后為其建立文件。請注意:文件的操作最后都會回溯到ktype-sysfs_ops的show和store這兩個函數(shù)中.Kobject的創(chuàng)建已經(jīng)分析完了,接著分析怎么將一個kobject注銷掉。注意過程是在kobject_del()中完成的。代碼如下:void kobject_del(struct kobject *kobj) if (!kobj) return; sysfs_remove_dir(kobj); kobj-state_in_sysfs = 0; kobj_kset_leave(kobj); kobject_put(kobj-parent); kobj-parent = NULL;該函數(shù)會將在sysfs中的kobject對應(yīng)的目錄刪除。請注意,屬性文件是建立在這個目錄下面的。只需要將這個目錄刪除。屬性文件也隨之刪除。是后,減少相關(guān)的引用計數(shù),如果kobject的引用計數(shù)為零。則將其所占空間釋放./kset 中的kobject是獨立的!Kset的操作與kobject類似,因為kset中內(nèi)嵌了一個kobject結(jié)構(gòu),所以,大部份操作都是集中在kset-kobject上.具體分析一下kset_create_and_add()這個接口,類似上面分析的kobject接口,這個接口也包括了kset的大部分操作.代碼如下:struct kset *kset_create_and_add(const char *name, struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj) struct kset *kset; int error; /創(chuàng)建一個kset kset = kset_create(name, uevent_ops, parent_kobj); if (!kset) return NULL; /注冊kset error = kset_register(kset); if (error) /如果注冊失敗,釋放kset kfree(kset); return NULL; return kset;Kset_create()用來創(chuàng)建一個struct kset結(jié)構(gòu).代碼如下:static struct kset *kset_create(const char *name, struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj) struct kset *kset; kset = kzalloc(sizeof(*kset), GFP_KERNEL); if (!kset) return NULL; kobject_set_name(&kset-kobj, name); kset-uevent_ops = uevent_ops; kset-kobj.parent = parent_kobj; kset-kobj.ktype = &kset_ktype; kset-kobj.kset = NULL; return kset;我們注意,在這里創(chuàng)建kset時.為其內(nèi)嵌的kobject指定其ktype結(jié)構(gòu)為kset_ktype.這個結(jié)構(gòu)的定義如下:static struct kobj_type kset_ktype = .sysfs_ops = &kobj_sysfs_ops, .release = kset_release,;屬性文件的讀寫操作全部都包含在sysfs_ops成員里.kobj_sysfs_ops的定義如下:struct sysfs_ops kobj_sysfs_ops = .show = kobj_attr_show, .store = kobj_attr_store,;Show,store成員對應(yīng)的函數(shù)代碼如下所示:static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) struct kobj_attribute *kattr; ssize_t ret = -EIO; kattr = container_of(attr, struct kobj_attribute, attr); if (kattr-show) ret = kattr-show(kobj, kattr, buf); return ret;static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) struct kobj_attribute *kattr; ssize_t ret = -EIO; kattr = container_of(attr, struct kobj_attribute, attr); if (kattr-store) ret = kattr-store(kobj, kattr, buf, count); return ret;從上面的代碼看以看出.會將struct attribute結(jié)構(gòu)轉(zhuǎn)換為struct kobj_attribte結(jié)構(gòu).也就是說struct kobj_attribte內(nèi)嵌了一個struct attribute.實際上,這是和宏_ATTR配合在一起使用的.經(jīng)常用于group中.在這里并不打算研究group.原理都是一樣的.這里列出來只是做個說明而已.創(chuàng)建好了kset之后,會調(diào)用kset_register().這個函數(shù)就是kset操作的核心代碼了.如下:int kset_register(struct kset *k) int err; if (!k) return -EINVAL; kset_init(k); err = kobject_add_internal(&k-kobj); if (err) return err; kobject_uevent(&k-kobj, KOBJ_ADD); return 0;在kset_init()里會初始化kset中的其它字段.然后調(diào)用kobject_add_internal()為其內(nèi)嵌的kobject結(jié)構(gòu)建立空間層次結(jié)構(gòu).之后因為添加了kset.會產(chǎn)生一個事件.這個事件是通過用戶空間的hotplug程序處理的.這就是kset明顯不同于kobject的地方.詳細研究一下這個函數(shù).這對于我們研究hotplug的深層機理是很有幫助的.它的代碼如下;int kobject_uevent(struct kobject *kobj, enum kobject_action action) return kobject_uevent_env(kobj, action, NULL);之后,會調(diào)用kobject_uevent_env().這個函數(shù)中的三個參數(shù)含義分別為:引起事件的kobject.事件類型(add,remove,change,move,online,offline等).第三個參數(shù)是要添加的環(huán)境變量.代碼篇幅較長,我們效仿情景分析上面的做法.分段分析如下:int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext) struct kobj_uevent_env *env; const char *action_string = kobject_actionsaction; const char *devpath = NULL; const char *subsystem; struct kobject *top_kobj; struct kset *kset; struct kset_uevent_ops *uevent_ops; u64 seq; int i = 0; int retval = 0; pr_debug(kobject: %s (%p): %sn, kobject_name(kobj), kobj, _FUNCTION_); /* search the kset we belong to */ top_kobj = kobj; while (!top_kobj-kset & top_kobj-parent) top_kobj = top_kobj-parent; if (!top_kobj-kset) pr_debug(kobject: %s (%p): %s: attempted to send uevent without kset!n, kobject_name(kobj), kobj, _FUNCTION_); return -EINVAL; 因為對事件的處理函數(shù)包含在kobject-kset- uevent_ops中.要處理事件,就必須要找到上層的一個不為空的kset.上面的代碼就是順著kobject-parent找不到一個不為空的kset.如果不存在這樣的kset.就退出 kset = top_kobj-kset; uevent_ops = kset-uevent_ops; /* skip the event, if the filter returns zero. */ if (uevent_ops & uevent_ops-filter) if (!uevent_ops-filter(kset, kobj) pr_debug(kobject: %s (%p): %s: filter function caused the event to drop!n, kobject_name(kobj), kobj, _FUNCTION_); return 0; /* originating subsystem */ if (uevent_ops & uevent_ops-name) subsystem = uevent_ops-name(kset, kobj); else subsystem = kobject_name(&kset-kobj); if (!subsystem) pr_debug(kobject: %s (%p): %s: unset subsystem caused the event to drop!n, kobject_name(kobj), kobj, _FUNCTION_); return 0; 找到了不為空的kset.就跟kset- uevent_ops-filter()匹配.看這個事件是否被過濾.如果沒有被過濾掉.就會調(diào)用kset- uevent_ops-name()得到子系統(tǒng)的名稱,如果不存在kset- uevent_ops-name().就會以kobject-name做為子系統(tǒng)名稱. /* environment buffer */ env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL); if (!env) return -ENOMEM; /* complete object path */ devpath = kobject_get_path(kobj, GFP_KERNEL); if (!devpath) retval = -ENOENT; goto exit; /* default keys */ retval = add_uevent_var(env, ACTION=%s, action_string); if (retval) goto exit; retval = add_uevent_var(env, DEVPATH=%s, devpath); if (retval) goto exit; retval = add_uevent_var(env, SUBSYSTEM=%s, subsystem); if (retval) goto exit; /* keys passed in from the caller */ if (envp_ext) for (i = 0; envp_exti; i+) retval = add_uevent_var(env, envp_exti); if (retval) goto exit; 接下來,就應(yīng)該設(shè)置為調(diào)用hotplug設(shè)置環(huán)境變量了.首先,分配一個struct kobj_uevent_env結(jié)構(gòu)用來存放環(huán)境變量的值.然后調(diào)用kobject_get_path()用來獲得引起事件的kobject在sysfs中的路徑.再調(diào)用add_uevent_var()將動作代表的字串,kobject路徑,子系統(tǒng)名稱填充到struct kobj_uevent_env中,如果有指定環(huán)境變量,也將其添加進去. kobject_get_path()和add_uevent_var()都比較簡單.這里不再詳細分析了.請自行查看源代碼 /* let the kset specific function add its stuff */ if (uevent_ops & uevent_ops-uevent) retval = uevent_ops-uevent(kset, kobj, env); if (retval) pr_debug(kobject: %s (%p): %s: uevent() returned %dn, kobject_name(kobj), kobj, _FUNCTION_, retval); goto exit; /* * Mark add and remove events in the object to ensure proper * events to userspace during automatic cleanup. If the object did * send an add event, remove will automatically generated by * the core, if not already done by the caller. */ if (action = KOBJ_ADD) kobj-state_add_uevent_sent = 1; else if (action = KOBJ_REMOVE) kobj-state_remove_uevent_sent = 1; /* we will send an event, so request a new sequence number */ spin_lock(&sequence_lock); seq = +uevent_seqnum; spin_unlock(&sequence_lock); retval = add_uevent_var(env, SEQNUM=%llu, (unsigned long long)seq); if (retval) goto exit;在這里還會調(diào)用kobject-kset- uevent_ops-uevent().讓產(chǎn)生事件的kobject添加環(huán)境變量.最后將事件序列添加到環(huán)境變量中去.#if defined(CONFIG_NET) /* send netlink message */ if (uevent_sock) struct sk_buff *skb; size_t len; /* allocate message with the maximum possible size */ len = strlen(action_string) + strlen(devpath) + 2; skb = alloc_skb(len + env-buflen, GFP_KERNEL); if (skb) char *scratch; /* add header */ scratch = skb_put(skb, len); sprintf(scratch, %s%s, action_string, devpath); /* copy keys to our continuous event payload buffer */ for (i = 0; i envp_idx; i+) len = strlen(env-envpi) + 1; scratch = skb_put(skb, len); strcpy(scratch, env-envpi); NETLINK_CB(skb).dst_group = 1; netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL); #endif /* call uevent_helper, usually only enabled during early boot */ if (uevent_helper0) char *argv 3; argv 0 = uevent_helper; argv 1 = (char *)subsystem; argv 2 = NULL; retval = add_uevent_var(env, HOME=/); if (retval) goto exit; retval = add_uevent_var(env, PATH=/sbin:/bin:/usr/sbin:/usr/bin); if (retval) goto exit; call_usermodehelper(argv0, argv, env-envp, UMH_WAIT_EXEC); exit: kfree(devpath); kfree(env); return retval;忽略一段選擇編譯的代碼.再后就是調(diào)用用戶空間的hotplug了.添加最后兩個環(huán)境變量.HOME和PATH.然后調(diào)用hotplug.以子系統(tǒng)名稱為參數(shù).現(xiàn)在我們終于知道hotplug處理程序中的參數(shù)和環(huán)境變量是怎么來的了._使用完了kset.再調(diào)用kset_unregister()將其注銷.這個函數(shù)很簡單,請自行查閱代碼.為了印證一下上面的分析,寫一個測試模塊。如下:#include #include #include #include #include #include #include #include MODULE_AUTHOR(eric xiao);MODULE_LICENSE(Dual BSD/GPL);int kset_filt
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 教學(xué)質(zhì)量提升工作方案計劃
- 2024-2025學(xué)年年七年級數(shù)學(xué)人教版下冊專題整合復(fù)習(xí)卷27.3 位似(含答案)-
- 人造板類家具相關(guān)行業(yè)投資方案范本
- 空調(diào)安裝維修合同三篇
- 洗發(fā)水運輸合同三篇
- 無縫管熱連軋機相關(guān)行業(yè)投資方案
- 農(nóng)業(yè)機械相關(guān)項目投資計劃書范本
- 《操作風(fēng)險的度量》課件
- 董事會授權(quán)代理合同三篇
- 委托銷售協(xié)議三篇
- 【MOOC】信號與系統(tǒng)-北京郵電大學(xué) 中國大學(xué)慕課MOOC答案
- 電光調(diào)制實驗報告
- 收款憑證(自制Word打印版)
- 鑄鐵閘門檢驗標(biāo)準(zhǔn)
- 某公司項目部質(zhì)量管理體系及制度
- 關(guān)于開展全員營銷活動的實施方案
- 碩士開題報告和文獻綜述模板-北京理工大學(xué)研究生院
- 俄語視聽說基礎(chǔ)教程1
- 5-10萬山平塘設(shè)計報告
- 單片機程序源代碼
- 城鎮(zhèn)燃氣室內(nèi)施工及質(zhì)量驗收規(guī)范(完整版)
評論
0/150
提交評論