日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux内核品读 /基础组件/ 模块机制快速入门

發(fā)布時間:2023/12/20 linux 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux内核品读 /基础组件/ 模块机制快速入门 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

哈嘍,我是杰克吳,繼續(xù)記錄我的學習心得。

一、關(guān)于興趣的幾點思考

1. 享受不是興趣,愿意付出才是:

  • 興趣很容易跟享受混淆。享受是被動的,無需付出;而興趣則要求你甘愿為了這件事情付出努力。

2.任何事情,接觸皮毛的時候不要談興趣:

  • 在我開始公眾號寫文章之前,只是粗淺地覺得這個事不難我可以嘗試一下,而事實上,持續(xù)寫作的難度和意義超乎大多數(shù)人的想象。

  • 任何事情,先做到 60 分,再談是否喜歡

3. 興趣和愛好不太一樣:

  • 區(qū)別在于你是否需要且愿意通過刻意練習以收獲這個興趣,以及這件事是否能給你帶來持續(xù)的成就感。

  • 吃喝玩樂(旅游,逛街,買買買)是愛好,不是興趣。純粹的看電影是愛好,但是認真地寫影評(經(jīng)歷了思考與分享)則算是興趣。表面看上去都是同一件事,但是不同人會發(fā)展成不一樣的結(jié)果

  • 最開始時可能只是愛好,但是隨著你的持續(xù)思考和投入,可能會發(fā)展為你的理想職業(yè)

4. 興趣可以帶有功利性:

  • 那些看似功利的標準(例如高考、面試),存在很多偏差的部分,但不可否認,在絕大多數(shù)情況下,它們提供了較為高效和正確的努力方向

  • 把自己熱愛的事情用來掙錢,非常好。只憑自己的興致去做,確實會有更多愉悅,但這也是最廉價、最輕易的喜歡了,問題是,你很難真正做得好。你真的喜歡這個事,你會主動爭取做好,贏得市場才會給你帶來更長久的愉悅感


二、模塊機制快速入門 (1)

目錄:

1.?內(nèi)核模塊的使用 2.?內(nèi)核模塊的文件格式 3. EXPORT_SYMBOL 是如何實現(xiàn)符號導出的? 4.?相關(guān)參考

基于 Linux-4.14 + Arm-v7。

1. 內(nèi)核模塊的使用

最簡單的內(nèi)核模塊:

#include?<linux/init.h> #include?<linux/module.h>static?char?*name?=?"embedded?hacker"; module_param(name,?charp,?S_IRUGO);????//?指定模塊可以接收的參數(shù)static?void?print_hello(void) {printk(KERN_INFO?"Hello?World,?%s\n",?name); }static?int?__init?hello_init(void) {printk(KERN_INFO?"Hello?World?init\n");print_hello();return?0; } module_init(hello_init);static?void?__exit?hello_exit(void) {printk(KERN_INFO?"Hello?World?exit\n?"); } module_exit(hello_exit);EXPORT_SYMBOL(print_hello);???//?導出符號?print_hello MODULE_AUTHOR("es-hacker");???//?指定作者 MODULE_LICENSE("GPL?v2");?????//?指定?license MODULE_DESCRIPTION("A?simple?Hello?World?Module");??//?指定模塊的描述信息 MODULE_ALIAS("a?simplest?module");??//?指定模塊的別名

運行效果:

$?insmod?hello.ko???//?加載模塊 Hello?World?init????//?加載模塊時,module_init()?里的函數(shù)被調(diào)用 Hello?World,?embedded?hacker$?rmmod?hello???????//?卸載模塊 Hello?World?exit????//?卸載模塊時,module_exit()?里的函數(shù)被調(diào)用$?insmod?hello.ko?name=Jack?//?指定模塊參數(shù) Hello?World?init Hello?World,?Jack$?rmmod?hello Hello?World?exit

到此,內(nèi)核模塊的使用方法就介紹完畢了,非常簡單易用。

接下來是痛苦的部分:探索一下背后的實現(xiàn)機制

2. 內(nèi)核模塊的文件格式

可以用 file 命令確定一個文件的格式:

$?file?hello.ko? hello.ko:?ELF?32-bit?LSB?relocatable,?ARM,?EABI5?version?1?(SYSV),?BuildID[sha1]=2feb2cb1328c0a9113658d6e90ac20d7e4c56384,?not?stripped

內(nèi)核模塊的格式為 ELF ( Executable and Linkable Format ):

目前不需要全面了解 ELF 文件格式的所有技術(shù)細節(jié),只需要結(jié)合 Linux 源碼中定義的 ELF 相關(guān)數(shù)據(jù)結(jié)構(gòu),簡單了解一下 ELF 的構(gòu)造即可。

靜態(tài)的 ELF 文件視圖總體上可分為 3 部分


  • 頭部的 ELF header;

  • 中間的 Section;

  • 尾部的 Section header table

1) ELF header 部分:

作用:描述整個 ELF 文件。

組成:Linux 內(nèi)核里的數(shù)據(jù)結(jié)構(gòu)定義如下,注釋部分為內(nèi)核模塊機制相關(guān)的的成員。

typedef?struct?elf32_hdr{unsigned?char?e_ident[EI_NIDENT];/*?文件類型?*/Elf32_Half?e_type;Elf32_Half?e_machine;Elf32_Word?e_version;/*?Entry?point?*/Elf32_Addr?e_entry;Elf32_Off?e_phoff;/*?Section?header?table?在文件中的偏移量?*/Elf32_Off?e_shoff;Elf32_Word?e_flags;Elf32_Half?e_ehsize;Elf32_Half?e_phentsize;Elf32_Half?e_phnum;/*?Section?header?table?中?entry?的大小?*/Elf32_Half?e_shentsize;/*?Section?header?table?中有多少個?entry?*/Elf32_Half?e_shnum;Elf32_Half?e_shstrndx; }?Elf32_Ehdr;

實踐:

$?#?readelf?hello.ko?-h???????#?[-h|--file-header] ELF?Header:Magic:???7f?45?4c?46?01?01?01?00?00?00?00?00?00?00?00?00?Class:?????????????????????????????ELF32Data:??????????????????????????????2's?complement,?little?endianVersion:???????????????????????????1?(current)OS/ABI:????????????????????????????UNIX?-?System?VABI?Version:???????????????????????0Type:??????????????????????????????REL?(Relocatable?file)Machine:???????????????????????????ARMVersion:???????????????????????????0x1Entry?point?address:???????????????0x0Start?of?program?headers:??????????0?(bytes?into?file)Start?of?p?headers:??????????59648?(bytes?into?file)Flags:?????????????????????????????0x5000000,?Version5?EABISize?of?this?header:???????????????52?(bytes)Size?of?program?headers:???????????0?(bytes)Number?of?program?headers:?????????0Size?of?p?headers:???????????40?(bytes)Number?of?p?headers:?????????52Section?header?string?table?index:?51

2) Section 部分:

作用:對應人們常說的各種數(shù)據(jù)段、代碼段等,術(shù)語是 p。

組成:ELF 文件的主體,位于文件視圖中間部分的一個連續(xù)區(qū)域中。但是當模塊被內(nèi)核加載時,會根據(jù)各自屬性被重新分配到新的內(nèi)存區(qū)域。

3) Section header table 部分:

作用:每一個條目(術(shù)語叫 entry) 就是一個 Section header,負責描述 Section;

組成:由若干個 Section header entry 組成,Linux 內(nèi)核里的數(shù)據(jù)結(jié)構(gòu)定義如下 (注釋部分為內(nèi)核模塊機制相關(guān)的的成員):

typedef?struct?elf32_shdr?{Elf32_Word?sh_name;Elf32_Word?sh_type;Elf32_Word?sh_flags;/*?對應的 p 在內(nèi)存中的實際地址。初始值為0,當模塊被內(nèi)核加載時,會被修改為 p 在內(nèi)存中的實際地址?*/Elf32_Addr?sh_addr;/*?p?在文件視圖中的偏移量?*/Elf32_Off?sh_offset;/*?p?在文件視圖中的大小?*/Elf32_Word?sh_size;Elf32_Word?sh_link;Elf32_Word?sh_info;Elf32_Word?sh_addralign;Elf32_Word?sh_entsize; }?Elf32_Shdr;

實踐:

$?readelf?hello.ko?-S?????#?[-S|--p-headers|--ps] There?are?52?p?headers,?starting?at?offset?0xe900:Section?Headers:[Nr]?Name??????????????Type????????????Addr?????Off????Size???ES?Flg?Lk?Inf?Al[?0]???????????????????NULL????????????00000000?000000?000000?00??????0???0??0[?1]?.note.gnu.build-i?NOTE????????????00000000?000034?000024?00???A??0???0??4[?2]?.text?????????????PROGBITS????????00000000?000058?000000?00??AX??0???0??1[...][?5]?.init.text????????PROGBITS????????00000000?000070?00001c?00??AX??0???0??4[...][?7]?.exit.text????????PROGBITS????????00000000?00008c?00000c?00??AX??0???0??4[...][?9]?__ksymtab?????????PROGBITS????????00000000?000098?000008?00???A??0???0??4[...][25]?__ksymtab_strings?PROGBITS????????00000000?0001f1?00000c?00???A??0???0??1[26]?__param???????????PROGBITS????????00000000?000200?000014?00???A??0???0??4[27]?.rel__param???????REL?????????????00000000?00b9e4?000020?08???I?49??26??4[28]?__versions????????PROGBITS????????00000000?000214?000100?00???A??0???0??4[29]?.data?????????????PROGBITS????????00000000?000314?000004?00??WA??0???0??4[...][48]?.ARM.attributes???ARM_ATTRIBUTES??00000000?00b21a?000031?00??????0???0??1[49]?.symtab???????????SYMTAB??????????00000000?00b24c?000520?10?????50??75??4[50]?.strtab???????????STRTAB??????????00000000?00b76c?0001cd?00??????0???0??1[51]?.shstrtab?????????STRTAB??????????00000000?00e6e4?00021b?00??????0???0??1Key?to?Flags:W?(write),?A?(alloc),?X?(execute),?M?(merge),?S?(strings)I?(info),?L?(link?order),?G?(group),?T?(TLS),?E?(exclude),?x?(unknown)O?(extra?OS?processing?required)?o?(OS?specific),?p?(processor?specific)

這里只截取模塊加載相關(guān)的部分 p header,現(xiàn)在有個初步印象就好,后續(xù)使用到了相關(guān)的 secition header,再做進一步的研究分析。

內(nèi)核模塊自身并不會使用到上述數(shù)據(jù)結(jié)構(gòu) (elf32_hdr、elf32_shdr),它們是給內(nèi)核模塊加載器在加載模塊時使用的。

3. EXPORT_SYMBOL() 是如何實現(xiàn)符號導出的?

EXPORT_SYMBOL() 系列宏用來向外界導出一個符號。內(nèi)核和內(nèi)核模塊通過符號表的形式向外部世界導出符號的相關(guān)信息。

為什么要導出符號?

  • 如果沒有獨立存在的內(nèi)核模塊,作為單一的 Linux 內(nèi)核映像,就沒必要導出符號了。對于靜態(tài)編譯鏈接而成的內(nèi)核映像而言,所有的符號引用都會在靜態(tài)鏈接階段完成。

  • 有了內(nèi)核模塊之后,獨立編譯鏈接的內(nèi)核模塊要使用到內(nèi)核提供的基礎(chǔ)設(shè)施(即調(diào)用內(nèi)核函數(shù),例如 printk)的話,就必須要解決符號引用問題 (unresolved symbol)。

  • 可以用 nm 命令來查看一個模塊中出現(xiàn)的未定義符號:

$?nm?hello.o?-u?????????#?[-u|--undefined-only]U?__aeabi_unwind_cpp_pr0U?param_ops_charpU?printkU?__this_module
  • 處理 unresolved symbol 問題的本質(zhì)是在模塊加載期間找到該符號在內(nèi)存中的實際地址。

從全局上看,EXPORT_SYMBOL 的完整實現(xiàn)包括 3 部分:

  • EXPORT_SYMBOL 的定義部分

  • 鏈接腳本鏈接器部分

  • 使用導出符號部分

EXPORT_SYMBOL 的定義:

//?include/linux/export.h #define?EXPORT_SYMBOL(sym)?__EXPORT_SYMBOL(sym,?"")/*?For?every?exported?symbol,?place?a?struct?in?the?__ksymtab?p?*/ #define?___EXPORT_SYMBOL(sym,?sec)?????\extern?typeof(sym)?sym;??????\__CRC_SYMBOL(sym,?sec)??????\static?const?char?__kstrtab_##sym[]????\__attribute__((p("__ksymtab_strings"),?aligned(1)))?\=?VMLINUX_SYMBOL_STR(sym);?????\static?const?struct?kernel_symbol?__ksymtab_##sym??\__used????????\__attribute__((p("___ksymtab"?sec?"+"?#sym),?used))?\=?{?(unsigned?long)&sym,?__kstrtab_##sym?}

以 hello.ko 為例,EXPORT_SYMBOL(print_hello) 本質(zhì)上就是定義了 2 個變量:

static?const?char?__kstrtab_print_hello[]?=?"print_hello"static?const?struct?kernel_symbol?__ksymtab_print_hello?=?{(unsigned?long)&print_hello,__kstrtab_print_hello, };
  • 變量1: char []

    • 用于保存符號名;

    • 被放置在名為 "__ksymtab_strings" 的 p 中;

  • 變量2: struct kernel_symbol

    • 用于保存符號名與地址;

    • 被放置在名為 "___ksymtab+print_hello" 的 p 中;

根據(jù) scripts/module-common.lds 里的定義:

SECTIONS?{[...]__ksymtab??0?:?{?*(SORT(___ksymtab+*))?}[...] }

"___ksymtab+print_hello" 會被轉(zhuǎn)換為 "__ksymtab",這樣就跟我們用 readelf hello.ko -S 查看到的 p 對應上了。

為了讓內(nèi)核可以通過上述 __ksymtab p 找到被導出的符號,鏈接器必須導出 p 的地址

include/asm-generic/vmlinux.lds.h/*?Kernel?symbol?table:?Normal?symbols?*/???\__ksymtab?????????:?AT(ADDR(__ksymtab)?-?LOAD_OFFSET)?{??\VMLINUX_SYMBOL(__start___ksymtab)?=?.;???\KEEP(*(SORT(___ksymtab+*)))????\VMLINUX_SYMBOL(__stop___ksymtab)?=?.;???\}?/*?Kernel?symbol?table:?strings?*/????\__ksymtab_strings?:?AT(ADDR(__ksymtab_strings)?-?LOAD_OFFSET)?{?\*(__ksymtab_strings)?????\}?

在 kernel/module.c 中,可以看到下列聲明:

/*?Provided?by?the?linker?*/ extern?const?struct?kernel_symbol?__start___ksymtab[]; extern?const?struct?kernel_symbol?__stop___ksymtab[]; [...]

這些變量會在內(nèi)核或者內(nèi)核模塊查找某個符號時被使用。

EXPORT_SYMBOL 和 EXPORT_SYMBOL_GPL 導出符號的可見性


從這里開始重頭戲模塊加載的分析了,鑒于大多數(shù)人的注意力無法在一篇文章里上集中太久,更多的內(nèi)容將放在后面的文章里。建議大家可以先自行閱讀相關(guān)書籍,不是自己理解到的東西是消化不了的。

4. 相關(guān)參考

  • Linux 設(shè)備驅(qū)動開發(fā)詳解,第 4 章節(jié)

  • 深入 Linux 設(shè)備驅(qū)動程序內(nèi)核機制,第 1 章節(jié)

  • 深入 Linux 內(nèi)核架構(gòu),第 7 章節(jié)

  • 深入理解 Linux 內(nèi)核,第20 章節(jié)、附錄2

5. 更多值得關(guān)注的知識點

  • 模塊的加載

  • 模塊的參數(shù)傳遞機制

  • 模塊之間的依賴關(guān)系

  • 模塊中的版本控制機制

  • ...


三、思考技術(shù),也思考人生

要學習技術(shù),更要學習如何生活

你和我各有一個蘋果,如果我們交換蘋果的話,我們還是只有一個蘋果。但當你和我各有一個想法,我們交換想法的話,我們就都有兩個想法了。

? 推薦閱讀:

? ??專輯|Linux文章匯總

? ??專輯|程序人生

? ??專輯|C語言

嵌入式Linux

微信掃描二維碼,關(guān)注我的公眾號?

總結(jié)

以上是生活随笔為你收集整理的Linux内核品读 /基础组件/ 模块机制快速入门的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。