<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Linux內(nèi)核品讀 /基礎(chǔ)組件/ 模塊機(jī)制快速入門(mén)

          共 8956字,需瀏覽 18分鐘

           ·

          2020-09-20 01:33



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

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

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

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

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

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

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

          3. 興趣和愛(ài)好不太一樣:

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

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

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

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

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

          • 把自己熱愛(ài)的事情用來(lái)掙錢(qián),非常好。只憑自己的興致去做,確實(shí)會(huì)有更多愉悅,但這也是最廉價(jià)、最輕易的喜歡了,問(wèn)題是,你很難真正做得好。你真的喜歡這個(gè)事,你會(huì)主動(dòng)爭(zhēng)取做好,贏得市場(chǎng)才會(huì)給你帶來(lái)更長(zhǎng)久的愉悅感。


          二、模塊機(jī)制快速入門(mén) (1)

          目錄:

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

          基于 Linux-4.14 + Arm-v7。

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

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

          #include?
          #include?

          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);???//?導(dǎo)出符號(hào)?print_hello
          MODULE_AUTHOR("es-hacker");???//?指定作者
          MODULE_LICENSE("GPL?v2");?????//?指定?license
          MODULE_DESCRIPTION("A?simple?Hello?World?Module");??//?指定模塊的描述信息
          MODULE_ALIAS("a?simplest?module");??//?指定模塊的別名

          運(yùn)行效果:

          $?insmod?hello.ko???//?加載模塊
          Hello?World?init????//?加載模塊時(shí),module_init()?里的函數(shù)被調(diào)用
          Hello?World,?embedded?hacker

          $
          ?rmmod?hello???????//?卸載模塊
          Hello?World?exit????//?卸載模塊時(shí),module_exit()?里的函數(shù)被調(diào)用

          $
          ?insmod?hello.ko?name=Jack?//?指定模塊參數(shù)
          Hello?World?init
          Hello?World,?Jack

          $
          ?rmmod?hello
          Hello?World?exit

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

          接下來(lái)是痛苦的部分:探索一下背后的實(shí)現(xiàn)機(jī)制。

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

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

          $?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ù)細(xì)節(jié),只需要結(jié)合 Linux 源碼中定義的 ELF 相關(guān)數(shù)據(jù)結(jié)構(gòu),簡(jiǎn)單了解一下 ELF 的構(gòu)造即可。

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


          • 頭部的 ELF header;

          • 中間的 Section;

          • 尾部的 Section header table

          1) ELF header 部分:

          作用:描述整個(gè) ELF 文件。

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

          typedef?struct?elf32_hdr{
          ??unsigned?char?e_ident[EI_NIDENT];

          ??/*?文件類(lèi)型?*/
          ??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?中有多少個(gè)?entry?*/
          ??Elf32_Half?e_shnum;
          ??Elf32_Half?e_shstrndx;
          }?Elf32_Ehdr;

          實(shí)踐:


          $
          ?#?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:?????????????????????????????ELF32
          ??Data:??????????????????????????????2's?complement,?little?endian
          ??Version:???????????????????????????1?(current)
          ??OS/ABI:????????????????????????????UNIX?-?System?V
          ??ABI?Version:???????????????????????0
          ??Type:??????????????????????????????REL?(Relocatable?file)
          ??Machine:???????????????????????????ARM
          ??Version:???????????????????????????0x1
          ??Entry?point?address:???????????????0x0
          ??Start?of?program?headers:??????????0?(bytes?into?file)
          ??Start?of?section?headers:??????????59648?(bytes?into?file)
          ??Flags:?????????????????????????????0x5000000,?Version5?EABI
          ??Size?of?this?header:???????????????52?(bytes)
          ??Size?of?program?headers:???????????0?(bytes)
          ??Number?of?program?headers:?????????0
          ??Size?of?section?headers:???????????40?(bytes)
          ??Number?of?section?headers:?????????52
          ??Section?header?string?table?index:?51

          2) Section 部分:

          作用:對(duì)應(yīng)人們常說(shuō)的各種數(shù)據(jù)段、代碼段等,術(shù)語(yǔ)是 section。

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

          3) Section header table 部分:

          作用:每一個(gè)條目(術(shù)語(yǔ)叫 entry) 就是一個(gè) Section header,負(fù)責(zé)描述 Section;

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

          typedef?struct?elf32_shdr?{
          ??Elf32_Word?sh_name;
          ??Elf32_Word?sh_type;
          ??Elf32_Word?sh_flags;

          ??/*?對(duì)應(yīng)的 section 在內(nèi)存中的實(shí)際地址。初始值為0,當(dāng)模塊被內(nèi)核加載時(shí),會(huì)被修改為 section 在內(nèi)存中的實(shí)際地址?*/
          ??Elf32_Addr?sh_addr;

          ??/*?section?在文件視圖中的偏移量?*/
          ??Elf32_Off?sh_offset;

          ??/*?section?在文件視圖中的大小?*/
          ??Elf32_Word?sh_size;
          ??Elf32_Word?sh_link;
          ??Elf32_Word?sh_info;
          ??Elf32_Word?sh_addralign;
          ??Elf32_Word?sh_entsize;
          }?Elf32_Shdr;

          實(shí)踐:

          $?readelf?hello.ko?-S?????#?[-S|--section-headers|--sections]
          There?are?52?section?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??1

          Key?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)的部分 section header,現(xiàn)在有個(gè)初步印象就好,后續(xù)使用到了相關(guān)的 secition header,再做進(jìn)一步的研究分析。

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

          3. EXPORT_SYMBOL() 是如何實(shí)現(xiàn)符號(hào)導(dǎo)出的?

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

          為什么要導(dǎo)出符號(hào)?

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

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

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

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

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

          • EXPORT_SYMBOL 的定義部分

          • 鏈接腳本鏈接器部分

          • 使用導(dǎo)出符號(hào)部分

          EXPORT_SYMBOL 的定義:

          //?include/linux/export.h
          #define?EXPORT_SYMBOL(sym)?__EXPORT_SYMBOL(sym,?"")

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

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

          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 []

            • 用于保存符號(hào)名;
            • 被放置在名為 "__ksymtab_strings" 的 section 中;
          • 變量2: struct kernel_symbol

            • 用于保存符號(hào)名與地址;
            • 被放置在名為 "___ksymtab+print_hello" 的 section 中;

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

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

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

          為了讓內(nèi)核可以通過(guò)上述 __ksymtab section 找到被導(dǎo)出的符號(hào),鏈接器必須導(dǎo)出 section 的地址

          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[];
          [...]

          這些變量會(huì)在內(nèi)核或者內(nèi)核模塊查找某個(gè)符號(hào)時(shí)被使用。

          EXPORT_SYMBOL 和 EXPORT_SYMBOL_GPL 導(dǎo)出符號(hào)的可見(jiàn)性


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

          4. 相關(guān)參考

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

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

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

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

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

          • 模塊的加載

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

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

          • 模塊中的版本控制機(jī)制

          • ...


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

          要學(xué)習(xí)技術(shù),更要學(xué)習(xí)如何生活。

          你和我各有一個(gè)蘋(píng)果,如果我們交換蘋(píng)果的話,我們還是只有一個(gè)蘋(píng)果。但當(dāng)你和我各有一個(gè)想法,我們交換想法的話,我們就都有兩個(gè)想法了。



          ? 推薦閱讀:
          ? ??專(zhuān)輯|Linux文章匯總
          ? ??專(zhuān)輯|程序人生
          ? ??專(zhuān)輯|C語(yǔ)言


          嵌入式Linux
          微信掃描二維碼,關(guān)注我的公眾號(hào)?
          瀏覽 70
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人婷婷五月天 | 国产自产视频 | 日韩三级视频网站 | 大鸡八操逼视频免费试试看 | 男女AA免费视频 |