hello world驅動設計(x86)

1. 這個驅動有啥不同

        最近發現很多教程,上來就是往寫led驅動,對於新手來說,理解起來是十分費勁的,因爲一個led的驅動涉及到許多的知識,加之寫led驅動還需要開發板,這就受不了了,哪來那麼多錢!所以最近在複習驅動設計時看到了陳莉君老師的視頻教程Linux內核分析與應用[1],看到了一種更適合作爲驅動設計Hello world的寫法,因此有了這篇博文。事不宜遲主要介紹一下這個驅動設計有啥不同:

  • 不需要硬件操作
  • 不需要開發板
  • 不需要編譯內核源碼
  • 不需要寫應用程序
  • 只需要一個ubuntu系統

[1] 其中P5有詳細的流程介紹

2. 驅動的實現

2.1 驅動開發的架構
圖2-1 驅動開發架構示意圖
  • 驅動程序源程序:驅動的具體實現代碼,同時包含驅動的基本信息;
  • 內核源碼:由於驅動需要動態的加載到內核中,Linux的內核版本非常多,爲了更好的兼容當前的內核版本,驅動的編譯需要藉助相對應的內核源碼,進入內核源碼獲取相關的makefile和系統參數;
  • 編譯腳本Makefile:Makefile的的功能是自動化編譯,主要用在大型的工程項目中,屬於初學者的我們也應該接觸Makefile的相關知識,用起來很爽😋;
  • 驅動模塊:Linux中“一切皆文件”,驅動模塊也僅僅是個文件而已;
  • insmod:屬於Linux提供的驅動模塊安裝的命令,常用的驅動模塊相關的命令有rmmod、lsmod和modinfo;
  • 操作系統內核:驅動運行的載體,也是整個操作系統的核心。
2.2 驅動源文件
#include <linux/init.h>
#include <linux/module.h>

// 定義模塊入口函數
static int __init hello_world_init(void)
{
	printk("Hello world!\n");
	return 0;
}

// 定義模塊出口函數
static void __exit hello_world_exit(void)
{
	printk("Goodbye world!\n");
}

// 註冊模塊出口、入口函數
module_init(hello_world_init);
module_exit(hello_world_exit);

// 模塊其他信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.czw1998.icu");
MODULE_DESCRIPTION("This is a hello world module!");

        編譯驅動並非使用系統自帶的gcc編譯器,而是使用內核源碼中的編譯器,我們指定的頭文件目錄也有所差別。驅動程序設計所用到的頭文件通常位於內核源碼中的include/linux/。對於ubuntu中的內核源碼位於 /usr/src/。但是你會發現這個目錄下可能還有多個文件夾如圖2-2,那麼怎麼確定自己的內核版本呢?可以使用 uname -r查看當前系統的內核版本如圖2-2

圖2-2 如何確定內核源碼包以及版本

        驅動模塊必須通過模塊安裝命令(insmod)才能安裝到內核中,同時已安裝的驅動模塊也只能通過模塊卸載命令(rmmod)才能從內核中卸載,在模塊的安裝/卸載中,通常涉及到硬件資源的申請/釋放,爲了完成這一操作,Linux引入模塊入口以及出口函數的機制,模塊安裝系統將調用module_init註冊的函數,模塊的卸載系統將調用module_exit註冊的函數

        模塊除了具體的實現代碼之外,模塊還需要添加一些模塊的其他信息(不是必須),方便模塊的使用者更好的瞭解模塊,可以通過modinfo查看模塊信息,比如如下查看hello_world.ko信息

圖2-3 使用modinfo查看模塊信息
2.3 驅動編譯腳本Makefile
obj-m=hello_world.o
# 定義一些變量
PWD=$(shell pwd)
KERNEL_VERSION=$(shell uname -r)
KERNEL_PATH=/usr/src/linux-headers-$(KERNEL_VERSION)

default:
	make -C $(KERNEL_PATH) M=$(PWD) modules
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

        編譯驅動模塊最好藉助Makefile,可以大大的簡化你重複編譯的繁瑣性,同時可以使得編譯的操作流程更加清晰,簡單的解讀一下如上的Makefile文件:

  • obj-m:指定生成的目標文件;
  • PWD、KERNEL_VERSION、KERNEL_PATH:自定義shell變量(名字理論上任意必須符合變量命名規則,但原則上應該通俗易懂),使用變量將必要的環境參數記錄,方便後面重複使用;
  • default、clean:使用 make 命令時可以加上許多參數,不帶參數則是 default ,我們可以使用 make clean 清除項目生成文件;
  • -C、M:可以發現這裏壓根就沒有引入編譯器命令,原因是我們藉助的是內核源碼中的Makefile完成我們的編譯,-C表示進入內核目錄,M生成文件的存儲目錄,我麼從編譯過程的輸出信息可以很直觀的看到這個過程如圖2-4
  • modules、clean:上面我們說了,make可以帶參數,這兩個則是當前Makefile傳到內核源碼Makefile中的參數。
圖2-4 make輸出信息

3. 實驗

3.1 驅動模塊的編譯
圖3-1 編譯
3.2 驅動模塊的安裝及信息查看
圖3-2 驅動模塊的安裝及信息查看
  • grep介紹:用於查找文件中相對應的字符串,這裏使用此命令可以更方面的展示,避免截圖過長,這個命令同學們也可以去接觸一下,非常好用;
  • dmesg:由於printk輸出的信息並不會顯示在終端中,我們需要查看系統日誌文件,dmesg命令可以方便的輸出系統開機以來全部的日誌信息。
3.1 驅動模塊的卸載及信息查看
圖3-3 驅動模塊的卸載及信息查看
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章