Helloworld 驅動模塊加載

介紹

本文引用《linux設備驅動開發》書中部分解釋,記錄開篇第一章helloworld程序

以下內容需要掌握如下基礎信息linux模塊概念、鏈接編譯、c語言基礎

內容

helloworld.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

static int __init hellowolrd_init(void) {
    pr_info("Hello world!\n");
    return 0;
}

static void __exit hellowolrd_exit(void) {
    pr_info("End of the world\n");
}

module_init(hellowolrd_init);
module_exit(hellowolrd_exit);
MODULE_AUTHOR("John Madieu <[email protected]>");
MODULE_LICENSE("GPL");

運行make命令之後,它會生成兩個模塊

After running make command, there will be two modules:

  • helloworld.ko
  • helloworld-params.ko

第一個模塊是基本helloworld驅動程序,第二個也是相同的,但是它接收一些參數,並在內核調試消息中打印這些參數,加載第一個模塊後,將在內核中添加兩個調試消息,

安裝模塊

# insmod ./helloworld.ko

卸載模塊

# rmmod -f helloworld

內核消息

#dmesg
[...]
[38535.487568] Hello world!
[38542.391099] End of the world

對於第二個模塊,可以使用下面方式加載

# insmod  ./helloworld-params.ko

如果未提供任何參數,將使用默認值:

$ dmesg
[...]
[37858.595126] Hello world with parameters!
[37858.595129] The *mystr* parameter: hello
[37858.595130] The *myint* parameter: 1
[37858.595131] The *myarr* parameter: 0, 1, 2
[37887.232643] End of the world

我們傳入一些參數之後,將打印出如下消息

# insmod  ./helloworld-params.ko  mystr="packtpub" myint=255 myarr=23,4,7
# dmesg
[...]
[37892.417968] Hello world with parameters!
[37892.417970] The *mystr* parameter: packtpub
[37892.417971] The *myint* parameter: 255
[37892.417972] The *myarr* parameter: 23, 4, 7
[37895.222808] End of the world

模塊的入點和出點

​ 內核驅動程序都有入點和出點:前者對應於模塊加載時調用的函數(modprobe和insmod,該內容在書中有介紹),後者是模塊卸載時執行的函數(在執行rmmod和modprobe -r 時,該內容書中有介紹)。

​ main()函數使用C/C++編寫的每個用戶空間程序的入點,當這個函數返回時,程序將退出。而對於內核模塊,情況就不一樣了:入點可以隨意命名,它也不像用戶空間程序那樣在main()返回是退出,其出點在另一個函數中定義,開發人員 要做的就是通知內核把 哪些函數作爲入點或出點來執行。實際上,唯一必須要做的是把它們作爲參數提供爲module_init()module_exit()宏,將它們標識爲相應的加載和刪除函數。

​ 綜上所述,module_init()用於聲明模塊加載(使用insmod或modprobe)時應該調用的函數。初始化函數中要完成的操作是定義模塊的行爲。module_exit()用於聲明模塊卸載(使用rmmod)時應該調用的函數。

代碼中__init__exit屬性

__init和__exit實際上是在include/linux/init.h中定義的內核宏,如下所示:

#define __init__section(.init.text)
#define __exir__section(.exit.text)

ELF目標文件

編譯文件工作方式,ELF目標文件有不同的命名部分組成,其中一部分是必需的,它們稱爲ELF標準的基礎,但也可以根據自己的需要構建任一部分,並由特殊程序使用,內核就是這樣做的。如上所屬的內核宏的工作方式,如需要了解它的原理ELF目標文件的可執行和可鏈接格式說明需要了解

可以通過下列命令打印出指定內核模塊module.ko的不同組成部分

~/.../LinuxDeviceDriversDevelopment_Code/Chapter02 >>> objdump -h helloworld.ko                                            

helloworld.ko:     文件格式 elf64-x86-64

節:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00000000  0000000000000000  0000000000000000  00000040  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .init.text    00000015  0000000000000000  0000000000000000  00000040  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  2 .exit.text    0000000c  0000000000000000  0000000000000000  00000055  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  3 .rodata.str1.1 00000024  0000000000000000  0000000000000000  00000061  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 __mcount_loc  00000008  0000000000000000  0000000000000000  00000085  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
  5 .rodata       0000008c  0000000000000000  0000000000000000  000000a0  2**5
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
  6 .rodata.str1.8 00000058  0000000000000000  0000000000000000  00000130  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .modinfo      000000af  0000000000000000  0000000000000000  00000188  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .orc_unwind   0000001e  0000000000000000  0000000000000000  00000237  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .orc_unwind_ip 00000014  0000000000000000  0000000000000000  00000255  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
 10 .note.gnu.property 00000030  0000000000000000  0000000000000000  00000270  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 11 .note.gnu.build-id 00000024  0000000000000000  0000000000000000  000002a0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 12 .note.Linux   00000030  0000000000000000  0000000000000000  000002c4  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 13 .data         00000000  0000000000000000  0000000000000000  000002f4  2**0
                  CONTENTS, ALLOC, LOAD, DATA
 14 .printk_index 00000010  0000000000000000  0000000000000000  000002f8  2**3
                  CONTENTS, ALLOC, LOAD, RELOC, DATA
 15 .gnu.linkonce.this_module 000003c0  0000000000000000  0000000000000000  00000340  2**6
                  CONTENTS, ALLOC, LOAD, RELOC, DATA, LINK_ONCE_DISCARD
 16 .bss          00000000  0000000000000000  0000000000000000  00000700  2**0
                  ALLOC
 17 .debug_info   000038ec  0000000000000000  0000000000000000  00000700  2**0
                  CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
 18 .debug_abbrev 00000609  0000000000000000  0000000000000000  00003fec  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 19 .debug_aranges 00000060  0000000000000000  0000000000000000  000045f5  2**0
                  CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
 20 .debug_ranges 00000030  0000000000000000  0000000000000000  00004655  2**0
                  CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
 21 .debug_line   000005a7  0000000000000000  0000000000000000  00004685  2**0
                  CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
 22 .debug_str    00003206  0000000000000000  0000000000000000  00004c2c  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 23 .comment      00000026  0000000000000000  0000000000000000  00007e32  2**0
                  CONTENTS, READONLY
 24 .note.GNU-stack 00000000  0000000000000000  0000000000000000  00007e58  2**0
                  CONTENTS, READONLY
 25 .debug_frame  00000048  0000000000000000  0000000000000000  00007e58  2**3
                  CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
 26 .BTF          00000050  0000000000000000  0000000000000000  00007ea0  2**0
                  CONTENTS, READONLY

如上只有少部分屬於ELF標準

  • .text: 包含程序代碼,也稱爲代碼。
  • .data: 包含初始化數據,也稱爲數據段。
  • .rodata: 用於只讀數據。
  • .comment: 註釋。
  • 未初始化化的數據段,也稱爲有符號開始的塊(block started by symbol,bss)。

ELF鏈接器

​ 對於代碼程序中內核添加的其他部分,討論爲時過早,我們首先要討論鏈接器是如何將目標程序文件排列,並將相關.init.text鏈接到程序中hellowolrd_init.exit.text鏈接到程序中hellowolrd_exit

​ 要解釋這一行爲,首先我們要了解一個叫做鏈接器(Linux系統上的ld)程序,該程序負責將符號(數據、代碼等)放置到生成的二進制文件中的適當部分,以便在程序執行時可以被加載器處理。二進制文件中的這些部分可以自定義、更改它們的默認位置,甚至可以通過提供鏈接器腳本[稱爲鏈接器定義文件(LDF)或鏈接器定義腳本(LDS)]來添加其他部分。要實現這些操作只需通過編譯器指令把符號的位置告知鏈接器即可,GUN C編譯器爲此提供了一些屬性。Linux內核提供了一個自定義LDS文件,它位於arch/<arch>/kernel/vmlinux.lds,.s中。對於要放置在內核LDS文件所映射的專用部分中的符號,使用__init__exit進行標記。

​ 總之,__init__exit是Linux指令(實際上是宏),他們使用C編譯器屬性指定符號的位置。這些指令指示編譯器將以它們爲前綴的代碼分別放在.init.text.exit.text部分,雖然內核可以訪問不同的對象部分。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章