天堂国产午夜亚洲专区-少妇人妻综合久久蜜臀-国产成人户外露出视频在线-国产91传媒一区二区三区

當(dāng)前位置:主頁 > 論文百科 > 英文數(shù)據(jù)庫 >

《Linux內(nèi)核修煉之道》精華分享與討論(7)

發(fā)布時間:2016-08-30 19:20

  本文關(guān)鍵詞:Linux內(nèi)核修煉之道,由筆耕文化傳播整理發(fā)布。


推薦博文: Linux內(nèi)核“問題門”——學(xué)習(xí)問題、經(jīng)驗集錦

推薦下載:Linux內(nèi)核修煉之道》精華版之方法論

 

下面的分析,米盧教練說了,內(nèi)容不重要,重要的是態(tài)度。就像韓局長對待日記的態(tài)度那樣,嚴(yán)謹(jǐn)而細(xì)致。

只要你使用這樣的態(tài)度開始分析內(nèi)核,那么無論你選擇內(nèi)核的哪個部分作為切入點(diǎn),比如USB,比如進(jìn)程管理,在花費(fèi)相對不算很多的時間之后,你就會發(fā)現(xiàn)你對內(nèi)核的理解會上升到另外一個高度,一個抱著情景分析,抱著0.1內(nèi)核完全注釋,抱著各種各樣的內(nèi)核書籍翻來覆去的看很多遍又忘很多遍都無法達(dá)到的高度。請相信我!

讓我們在Linux社區(qū)里發(fā)出號召:學(xué)習(xí)內(nèi)核源碼,從學(xué)習(xí)韓局長開始!

態(tài)度決定一切:從初始化函數(shù)開始
任小強(qiáng)們說房價高漲從現(xiàn)在開始,股評家們說牛市從5000點(diǎn)開始。他們的開始需要我們的錢袋,我們的開始只需要一臺電腦,最好再有一杯茶,伴著幾支小曲兒,不盯著錢總是會比較愜意的。生容易,活容易,生活不容易,因為總要盯著錢。

有了地圖Kconfig和Makefile,我們可以在龐大復(fù)雜的內(nèi)核代碼中定位以及縮小了目標(biāo)代碼的范圍。那么現(xiàn)在,為了研究內(nèi)核對USB子系統(tǒng)的實現(xiàn),我們還需要在目標(biāo)代碼中找到一個突破口,這個突破口就是USB子系統(tǒng)的初始化代碼。

針對某個子系統(tǒng)或某個驅(qū)動,內(nèi)核使用subsys_initcall或module_init宏指定初始化函數(shù)。在drivers/usb/core/usb.c文件中,我們可以發(fā)現(xiàn)下面的代碼。

940 subsys_initcall(usb_init);
941 module_exit(usb_exit);

我們看到一個subsys_initcall,它也是一個宏,我們可以把它理解為module_init,只不過因為這部分代碼比較核心,開發(fā)者們把它看作一個子系統(tǒng),而不僅僅是一個模塊。這也很好理解,usbcore這個模塊它代表的不是某一個設(shè)備,而是所有USB設(shè)備賴以生存的模塊,Linux中,像這樣一個類別的設(shè)備驅(qū)動被歸結(jié)為一個子系統(tǒng)。比如PCI子系統(tǒng),比如SCSI子系統(tǒng),基本上,drivers/目錄下面第一層的每個目錄都算一個子系統(tǒng),因為它們代表了一類設(shè)備。

subsys_initcall(usb_init)的意思就是告訴我們usb_init是USB子系統(tǒng)真正的初始化函數(shù),而usb_exit()將是整個USB子系統(tǒng)的結(jié)束時的清理函數(shù)。于是為了研究USB子系統(tǒng)在內(nèi)核中的實現(xiàn),我們需要從usb_init函數(shù)開始看起。

865 static int __init usb_init(void)
866 {
867   int retval;
868   if (nousb) {
869    pr_info("%s: USB support disabled/n", usbcore_name);
870    return 0;
871   }
872
873   retval = ksuspend_usb_init();
874   if (retval)
875    goto out;
876   retval = bus_register(&usb_bus_type);
877   if (retval)
878    goto bus_register_failed;
879   retval = usb_host_init();
880   if (retval)
881    goto host_init_failed;
882   retval = usb_major_init();
883   if (retval)
884    goto major_init_failed;
885   retval = usb_register(&usbfs_driver);
886   if (retval)
887    goto driver_register_failed;
888   retval = usb_devio_init();
889   if (retval)
890    goto usb_devio_init_failed;
891   retval = usbfs_init();
892   if (retval)
893    goto fs_init_failed;
894   retval = usb_hub_init();
895   if (retval)
896    goto hub_init_failed;
897   retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
898   if (!retval)
899    goto out;
900
901   usb_hub_cleanup();
902  hub_init_failed:
903   usbfs_cleanup();
904 fs_init_failed:
905   usb_devio_cleanup();
906 usb_devio_init_failed:
907   usb_deregister(&usbfs_driver);
908 driver_register_failed:
909   usb_major_cleanup();
910 major_init_failed:
911   usb_host_cleanup();
912 host_init_failed:
913   bus_unregister(&usb_bus_type);
914 bus_register_failed:
915   ksuspend_usb_cleanup();
916 out:
917   return retval;
918 } 

(1)__init標(biāo)記。

關(guān)于usb_init,第一個問題是,第865行的__init標(biāo)記具有什么意義?

寫過驅(qū)動的應(yīng)該不會陌生,它對內(nèi)核來說就是一種暗示,表明這個函數(shù)僅在初始化期間使用,在模塊被裝載之后,它占用的資源就會釋放掉用作它處。它的暗示你懂,可你的暗示,她卻不懂或者懂裝不懂,多么讓人感傷。它在自己短暫的一生中一直從事繁重的工作,吃的是草吐出的是牛奶,留下的是整個USB子系統(tǒng)的繁榮。

受這種精神所感染,我覺得有必要為它說的更多些。__init的定義在include/linux/init.h文件里

43 #define __init          __attribute__ ((__section__ (".init.text")))

好像這里引出了更多的疑問,__attribute__是什么?Linux內(nèi)核代碼使用了大量的GNU C擴(kuò)展,以至于GNU C成為能夠編譯內(nèi)核的唯一編譯器,GNU C的這些擴(kuò)展對代碼優(yōu)化、目標(biāo)代碼布局、安全檢查等方面也提供了很強(qiáng)的支持。而__attribute__就是這些擴(kuò)展中的一個,它主要被用來聲明一些特殊的屬性,這些屬性主要被用來指示編譯器進(jìn)行特定方面的優(yōu)化和更仔細(xì)的代碼檢查。GNU C支持十幾個屬性,section是其中的一個,我們查看GCC的手冊可以看到下面的描述

‘section ("section-name")'
   Normally, the compiler places the code it generates in the `text'
 section. Sometimes, however, you need additional sections, or you
need certain particular functions to appear in special sections.
   The `section' attribute specifies that a function lives in a
   particular section. For example, the declaration:

     extern void foobar (void) __attribute__ ((section ("bar")));

   puts the function ‘foobar' in the ‘bar' section.

   Some file formats do not support arbitrary sections so the
   ‘section' attribute is not available on all platforms. If you
   need to map the entire contents of a module to a particular
   section, consider using the facilities of the linker instead.

通常編譯器將函數(shù)放在.text節(jié),變量放在.data或.bss節(jié),使用section屬性,可以讓編譯器將函數(shù)或變量放在指定的節(jié)中。那么前面對__init的定義便表示將它修飾的代碼放在.init.text節(jié)。連接器可以把相同節(jié)的代碼或數(shù)據(jù)安排在一起,比如__init修飾的所有代碼都會被放在.init.text節(jié)里,初始化結(jié)束后就可以釋放這部分內(nèi)存。

問題可以到此為止,也可以更深入,即內(nèi)核又是如何調(diào)用到這些__init修飾的初始化函數(shù)?要回答這個問題,還需要回顧一下subsys_initcall宏,它也在include/linux/init.h里定義

125 #define subsys_initcall(fn)             __define_initcall("4",fn,4)

這里又出現(xiàn)了一個宏__define_initcall,,它用于將指定的函數(shù)指針fn放到initcall.init節(jié)里 而對于具體的subsys_initcall宏,則是把fn放到.initcall.init的子節(jié).initcall4.init里。要弄清楚.initcall.init、.init.text和.initcall4.init這樣的東東,我們還需要了解一點(diǎn)內(nèi)核可執(zhí)行文件相關(guān)的概念。

內(nèi)核可執(zhí)行文件由許多鏈接在一起的對象文件組成。對象文件有許多節(jié),如文本、數(shù)據(jù)、init數(shù)據(jù)、bass等等。這些對象文件都是由一個稱為鏈接器腳本的文件鏈接并裝入的。這個鏈接器腳本的功能是將輸入對象文件的各節(jié)映射到輸出文件中;換句話說,它將所有輸入對象文件都鏈接到單一的可執(zhí)行文件中,將該可執(zhí)行文件的各節(jié)裝入到指定地址處。 vmlinux.lds是存在于arch/<target>/ 目錄中的內(nèi)核鏈接器腳本,它負(fù)責(zé)鏈接內(nèi)核的各個節(jié)并將它們裝入內(nèi)存中特定偏移量處。

我可以負(fù)責(zé)任的告訴你,要看懂vmlinux.lds這個文件是需要一番功夫的,不過大家都是聰明人,聰明人做聰明事,所以你需要做的只是搜索initcall.init,然后便會看到似曾相識的內(nèi)容

__inicall_start = .;
.initcall.init : AT(ADDR(.initcall.init) – 0xC0000000) {
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}
__initcall_end = .;

這里的__initcall_start指向.initcall.init節(jié)的開始,__initcall_end指向它的結(jié)尾。而.initcall.init節(jié)又被分為了7個子節(jié),分別是

.initcall1.init
.initcall2.init
.initcall3.init
.initcall4.init
.initcall5.init
.initcall6.init
.initcall7.init

我們的subsys_initcall宏便是將指定的函數(shù)指針放在了.initcall4.init子節(jié)。其它的比如core_initcall將函數(shù)指針放在.initcall1.init子節(jié),device_initcall將函數(shù)指針放在了.initcall6.init子節(jié)等等,都可以從include/linux/init.h文件找到它們的定義。各個字節(jié)的順序是確定的,即先調(diào)用.initcall1.init中的函數(shù)指針再調(diào)用.initcall2.init中的函數(shù)指針,等等。__init修飾的初始化函數(shù)在內(nèi)核初始化過程中調(diào)用的順序和.initcall.init節(jié)里函數(shù)指針的順序有關(guān),不同的初始化函數(shù)被放在不同的子節(jié)中,因此也就決定了它們的調(diào)用順序。

至于實際執(zhí)行函數(shù)調(diào)用的地方,就在/init/main.c文件里,內(nèi)核的初始化么,不在那里還能在哪里,里面的do_initcalls函數(shù)會直接用到這里的__initcall_start、__initcall_end來進(jìn)行判斷。

 (2)模塊參數(shù)。

關(guān)于usb_init函數(shù),第二個問題是,第868行的nousb表示什么?

知道C語言的人都會知道nousb是一個標(biāo)志,只是不同的標(biāo)志有不一樣的精彩,這里的nousb是用來讓我們在啟動內(nèi)核的時候通過內(nèi)核參數(shù)去掉USB子系統(tǒng)的,Linux社會是一個很人性化的世界,它不會去逼迫我們接受USB,一切都只關(guān)乎我們自己的需要。不過我想我們一般來說是不會去指定nousb的吧。如果你真的指定了nousb,那它就只會幽怨的說一句“USB support disabled”,然后退出usb_init。

nousb在drivers/usb/core/usb.c文件中定義為:

static int nousb; /* Disable USB when built into kernel image */
module_param_named(autosuspend, usb_autosuspend_delay, int, 0644);
MODULE_PARM_DESC(autosuspend, "default autosuspend delay");

 從中可知nousb是個模塊參數(shù)。關(guān)于模塊參數(shù),我們都知道可以在加載模塊的時候可以指定,但是如何在內(nèi)核啟動的時候指定?
打開系統(tǒng)的grub文件,然后找到kernel行,比如:

kernel  /boot/vmlinuz-2.6.18-kdb root=/dev/sda1 ro splash=silent vga=0x314

 其中的root,splash,vga等都表示內(nèi)核參數(shù)。當(dāng)某一模塊被編譯進(jìn)內(nèi)核的時候,它的模塊參數(shù)便需要在kernel行來指定,格式為“模塊名.參數(shù)=值”,比如:

 modprobe usbcore autosuspend=2

對應(yīng)到kernel行,即為 :

 usbcore.autosuspend=2

 通過命令“modinfo -p ${modulename}”可以得知一個模塊有哪些參數(shù)可以使用。同時,對于已經(jīng)加載到內(nèi)核里的模塊,它們的模塊參數(shù)會列舉在/sys/module/${modulename}/parameters/目錄下面,可以使用“echo -n ${value} > /sys/module/${modulename}/parameters/${parm}”這樣的命令去修改。

 (3)可變參數(shù)宏。

 關(guān)于usb_init函數(shù),第三個問題是,pr_info如何實現(xiàn)與使用?

pr_info只是一個打印信息的可辨參數(shù)宏,printk的變體,在include/linux/kernel.h里定義:

242 #define pr_info(fmt,arg...) /
243         printk(KERN_INFO fmt,##arg)

99年的ISO C標(biāo)準(zhǔn)里規(guī)定了可變參數(shù)宏,和函數(shù)語法類似,比如

#define debug(format, ...) fprintf (stderr, format, __VA_ARGS__)

里面的“…”就表示可變參數(shù),調(diào)用時,它們就會替代宏體里的__VA_ARGS__。GCC總是會顯得特立獨(dú)行一些,它支持更復(fù)雜的形式,可以給可變參數(shù)取個名字,比如

#define debug(format, args...) fprintf (stderr, format, args)

有了名字總是會容易交流一些。是不是與pr_info比較接近了?除了‘##’,它主要是針對空參數(shù)的情況。既然說是可變參數(shù),那傳遞空參數(shù)也總是可以的,空即是多,多即是空,股市里的哲理這里同樣也是適合的。如果沒有‘##’,傳遞空參數(shù)的時候,比如

debug ("A message");

展開后,里面的字符串后面會多個多余的逗號。這個逗號你應(yīng)該不會喜歡,而‘##’則會使預(yù)處理器去掉這個多余的逗號。

 關(guān)于usb_init函數(shù),上面的三個問題之外,余下的代碼分別完成usb各部分的初始化,接下來就需要圍繞它們分別進(jìn)行深入分析。因為這里只是演示如何入手分析,展示的只是一種態(tài)度,所以具體的深入分析就免了吧。

 


  本文關(guān)鍵詞:Linux內(nèi)核修煉之道,由筆耕文化傳播整理發(fā)布。



本文編號:105729

資料下載
論文發(fā)表

本文鏈接:http://sikaile.net/wenshubaike/mishujinen/105729.html


Copyright(c)文論論文網(wǎng)All Rights Reserved | 網(wǎng)站地圖 |

版權(quán)申明:資料由用戶4c211***提供,本站僅收錄摘要或目錄,作者需要刪除請E-mail郵箱bigeng88@qq.com
国产精品一区二区成人在线| 日韩精品少妇人妻一区二区| 日韩中文字幕在线不卡一区| 伊人天堂午夜精品草草网| 久热香蕉精品视频在线播放| 欧美成人久久久免费播放| 丝袜美女诱惑在线观看| 久久精品国产亚洲av久按摩| 国产亚洲欧美自拍中文自拍| 九九热在线视频精品免费| 99国产一区在线播放| 日韩一级免费中文字幕视频| 日本少妇三级三级三级| 国内外免费在线激情视频| 精品一区二区三区乱码中文| 麻豆欧美精品国产综合久久| 熟妇人妻av中文字幕老熟妇| 蜜桃av人妻精品一区二区三区| 自拍偷女厕所拍偷区亚洲综合| 一区二区日本一区二区欧美| 久久热中文字幕在线视频| 精品国自产拍天天青青草原| 色偷偷偷拍视频在线观看| 欧美丰满人妻少妇精品| 欧美人妻盗摄日韩偷拍| 激情少妇一区二区三区| 久久一区内射污污内射亚洲| 91人妻人人精品人人爽| 国产高清三级视频在线观看| 视频在线观看色一区二区| 亚洲最新av在线观看| 国产一区二区三区午夜精品| 亚洲国产精品国自产拍社区| 日韩精品一区二区三区含羞含羞草| 中文字幕高清不卡一区| 欧美综合色婷婷欧美激情| 伊人久久五月天综合网| 日韩精品视频一二三区| 99一级特黄色性生活片| 亚洲熟女诱惑一区二区| 国产一区二区三区午夜精品|