国产精品天干天干,亚洲毛片在线,日韩gay小鲜肉啪啪18禁,女同Gay自慰喷水

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Linux設(shè)備管理(二)_從cdev_add說起(超詳細(xì))

2022-05-07 20:03 作者:補(bǔ)給站Linux內(nèi)核  | 我要投稿
  • 這里我們來探討一下Linux內(nèi)核(以4.8.5內(nèi)核為例)是怎么管理字符設(shè)備的,即當(dāng)我們獲得了設(shè)備號,分配了cdev結(jié)構(gòu),注冊了驅(qū)動的操作方法集,最后進(jìn)行cdev_add()的時候,究竟是將哪些內(nèi)容告訴了內(nèi)核,內(nèi)核又是怎么管理我的cdev結(jié)構(gòu)的,這就是本文要討論的內(nèi)容。我們知道,Linux內(nèi)核對設(shè)備的管理是基于kobject的(參見Linux設(shè)備管理(一)_kobject,、kset、ktype分析(超詳細(xì))),這點(diǎn)從我們的cdev結(jié)構(gòu)中就可以看出,所以,接下來,你將看到"fs/char_dev.c"中實(shí)現(xiàn)的操作字符設(shè)備的函數(shù)都是基于"lib/kobject.c"以及"drivers/base/map.c"中對kobject操作的函數(shù)。好,現(xiàn)在我們從cdev_add()開始一層層的扒。

cdev_map對象

內(nèi)核中關(guān)于字符設(shè)備的操作函數(shù)的實(shí)現(xiàn)放在"fs/char_dev.c"中,打開這個文件,首先注意到就是這個在內(nèi)核中不常見的靜態(tài)全局變量cdev_map(27),我們知道,為了提高軟件的內(nèi)聚性,Linux內(nèi)核在設(shè)計的時候盡量避免使用全局變量作為函數(shù)間數(shù)據(jù)傳遞的方式,而建議多使用形參列表,而這個結(jié)構(gòu)體變量在這個文件中到處被使用,所以它應(yīng)該是描述了系統(tǒng)中所有字符設(shè)備的某種信息,帶著這樣的想法,我們可以在"drivers/base/map.c"中找到kobj_map結(jié)構(gòu)的定義:

  • 從中可以看出,kobj_map的核心就是一個struct probe類型、大小為255的數(shù)組,而在這個probe結(jié)構(gòu)中,第一個成員next(21)顯然是將這些probe結(jié)構(gòu)通過鏈表的形式連接起來,dev_t類型的成員dev顯然是設(shè)備號,get(25)和lock(26)分別是兩個函數(shù)接口,最后的重點(diǎn)來了,void作為C語言中的萬金油類型,在這里就是我們cdev結(jié)構(gòu)(通過后面的分析可以看出),所以,這個cdev_map是一個struct kobj_map類型的指針,其中包含著一個struct probe*類型、大小為255的數(shù)組,數(shù)組的每個元素指向的一個probe結(jié)構(gòu)封裝了一個設(shè)備號和相應(yīng)的設(shè)備對象(這里就是cdev),下圖中體現(xiàn)兩種常見的對設(shè)備號和cdev管理的方式,其一是一個cdev對象對應(yīng)這一個/多個設(shè)備號的情況, 在cdev_map中, 一個probes對象就對應(yīng)一個主設(shè)備號,多個設(shè)備號對應(yīng)一個cdev時,其實(shí)只是次設(shè)備號在變,主設(shè)備號還是一樣的,所以是同一個probes對象;其二是當(dāng)主設(shè)備號超過255時,會進(jìn)行probe復(fù)用,此時probe->next就派上了用場,比如probe[200],可以表示設(shè)備號200,455...3895等所有對255取余是200的數(shù)字, 參見下文的kobj_map--58--。

【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。∏?00名進(jìn)群領(lǐng)取,額外贈送一份價值699的內(nèi)核資料包(含視頻教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)? ??


cdev_add

  • 了解了cdev_map的功能,我們就可以一探cdev_add()。從中可以看出,其工作顯然是交給了kobj_map()

cdev_add()--460-->就是將我們之前獲得設(shè)備號和設(shè)備號長度填充到cdev結(jié)構(gòu)中, --468-->kobject_get()將kobject的計數(shù)減一,并返回struct kobject*

kobj_map()

  • 這個函數(shù)在內(nèi)核的設(shè)備管理中占有重要的地位,這里我們只從字符設(shè)備的角度分析它的功能,這個函數(shù)的設(shè)計也很單純,就是封裝好一個probe結(jié)構(gòu)并將它的地址放入probes數(shù)組進(jìn)而封裝進(jìn)cdev_map,。

kobj_map()--48-55-->根據(jù)傳入的設(shè)備號的個數(shù),將設(shè)備號和cdev依次封裝到kmalloc_array()分配的n個probe結(jié)構(gòu)中 --57-63-->就是遍歷probs數(shù)組,直到找到一個值為NULL的元素,再將probe的地址存入probes, 將設(shè)備號對255取余后與probes的下標(biāo)對應(yīng)。至此,我們就將我們的cdev放入的內(nèi)核的數(shù)據(jù)結(jié)構(gòu)

chrdev_open()

  • 將設(shè)備放入的內(nèi)核,我們再來看看內(nèi)核是怎么找到一個特定的cdev的。 首先,在一個字符設(shè)備文件被創(chuàng)建的時候,內(nèi)核會構(gòu)造相應(yīng)的inode,作為一種特殊文件,其inode初始化的時候,就會做一些準(zhǔn)備工作

由此可見,對一個字符設(shè)備的訪問流程大概是:文件路徑=>inode=>chrdev_open()=>(kobj_lookup=>)inode.i_cdev=>cdev.fops.my_chr_open()。所以只要通過VFS找到了inode,就可以找到chrdev_open(),這里我們就來關(guān)注一個chrdev_open()是怎么從內(nèi)核的數(shù)據(jù)結(jié)構(gòu)中找到我們的cdev并執(zhí)行其中的my_chr_open()的。比較有意思的是,雖然我們有了字符設(shè)備的設(shè)備文件,inode也被構(gòu)造并初始化了, 但是在第一次調(diào)用chrdev_open()之前,這個inode和具體的chr_dev對象并沒有直接關(guān)系,而只是通過設(shè)備號建立的"間接"關(guān)系。在第一次調(diào)用chrdev_open()之后, inode->i_cdev才被根據(jù)設(shè)備號找到的cdev對象賦值,此后inode才和具體的cdev對象直接聯(lián)系在了一起

chrdev_open()--359-->嘗試將inode->i_cdev(一個cdev結(jié)構(gòu)指針)保存在局部變量p中, --360-->如果p為空,即inode->i_cdev為空, --364-->我們就根據(jù)inode->i_rdev(設(shè)備號)通過kobj_lookup()搜索cdev_map,并返回與之對應(yīng)kobj, --367-->由于kobject是cdev的父類,我們根據(jù)container_of很容易找到相應(yīng)的cdev結(jié)構(gòu)并將其保存在inode->i_cdev中, --374-->找到了cdev,我們就可以將inode->devices掛接到inode->i_cdev的管理鏈表中,這樣下次就不用重新搜索, --378-->直接cdev_get()即可。 --386-->找到了我們的cdev結(jié)構(gòu),我們就可以將其中的操作方法集inode->i_cdev->ops傳遞給filp->f_ops(386-390), --392-->這樣,我們就可以回調(diào)我們的設(shè)備打開函數(shù)my_chr_open();如果我們沒有實(shí)現(xiàn)自己的open接口,就什么都不做,也不是錯

  • 扒完了字符設(shè)備的注冊過程,不知各位看官有沒有發(fā)現(xiàn),全程沒有一個初始化cdev.kobj的函數(shù)!到此為止,我們都是通過cdev_map來管理系統(tǒng)里的字符設(shè)備的,所以,我們并不能在sysfs找到我們此時注冊的字符設(shè)備,更深層的原因是內(nèi)核中并不直接使用cdev作為一個設(shè)備,而是將其作為一個設(shè)備接口,使用這個接口我們可以派生出misc設(shè)備,輸入設(shè)備,LCD等等,當(dāng)初始化這些具體的字符設(shè)備的時候,相應(yīng)的list_head對象才可能被打開掛接到相應(yīng)的鏈表,并初始化kobj。即如果希望sysfs中找到我們的字符設(shè)備,我們就必須對cdev.kobj進(jìn)行初始化,掛接到合適的kset,這也就是導(dǎo)出設(shè)備信息到sysfs以便自動創(chuàng)建設(shè)備文件的原理

彩蛋

  • Linux中幾乎所有的"設(shè)備"都是"device"的子類,無論是平臺設(shè)備還是i2c設(shè)備還是網(wǎng)絡(luò)設(shè)備,但唯獨(dú)字符設(shè)備不是,可以看出cdev并不是繼承自device,我們可以看出注冊一個cdev對象到內(nèi)核其實(shí)只是將它放到cdev_map中,直到對device_create的分析才知道此時才創(chuàng)建device結(jié)構(gòu)并將kobj掛接到相應(yīng)的鏈表,,所以,基于歷史原因,當(dāng)下cdev更合適的一種理解是一種接口(使用mknod時可以當(dāng)作設(shè)備),而不是而一個具體的設(shè)備,和platform_device,i2c_device有著本質(zhì)的區(qū)別.


Linux設(shè)備管理(二)_從cdev_add說起(超詳細(xì))的評論 (共 條)

分享到微博請遵守國家法律
湘潭市| 新河县| 怀宁县| 南阳市| 明星| 江门市| 平远县| 小金县| 合川市| 凤台县| 阿合奇县| 玉林市| 慈溪市| 阿克陶县| 葫芦岛市| 姚安县| 永嘉县| 府谷县| 德化县| 波密县| 祁门县| 黄石市| 紫云| 武陟县| 威宁| 左云县| 商水县| 怀远县| 眉山市| 财经| 舟山市| 马山县| 和政县| 永吉县| 临沭县| 沐川县| 祁连县| 芮城县| 石狮市| 东安县| 乐昌市|