作 者:道哥,10+年嵌入式開發老兵,專注於:C/C++、嵌入式、Linux。
關注下方公眾號,回復【書籍】,獲取 Linux、嵌入式領域經典書籍;回復【PDF】,獲取所有原創文章( PDF 格式)。
目錄
別人的經驗,我們的階梯!
大家好,我是道哥。今天給大家分享一些筆記本里的一些存貨: Linux 系統中的驅動和中斷相關。
大概會用 6~7 篇的文章,由淺入深的為大家介紹Linux
中驅動程序的編寫方法。
文章的順序,也是我之前自己學習時的順序。
以前的學習記錄比較零散,現在只是把它們按照一定的順序重新梳理一下。
這幾篇文章,理論知識會少一些,更注重實際的操作。
我會把操作用引導的代碼,全部上傳到網盤上,在文末有下載說明。
只要根據文中介紹的步驟進行操作,就一定可以操作成功。
學習的困惑
記得以前我在開始學習驅動開發的時候,找來很多文章、資料來學習,但是總是覺得缺少了點全局視角。
就好像:我想看清一座山的全貌,但總是被困在一個、又一個山谷中一樣。
主要的困惑有 3 點:
每一篇文章的介紹都是正確的,但是如果把很多文章放在一起看,就會發現怎么說的都不一樣啊?
有些文章注重函數的介紹,但是缺乏一個全局的視角,從整體上來觀察驅動程序的結構;
對於一個新手來說,能夠邊學習、邊實踐,這是最好的學習方式,但是很多文章不會注意這方面。雖然文章內容很漂亮,但是不知道怎么去實踐、驗證。
因此,這幾篇文章我們就從最簡單的驅動模塊編譯開始,然后介紹字符設備驅動程序。
在這部分,會以 GPIO 為例子,重點描述其中的關鍵節點。
最后再介紹在中斷處理程序中,如何利用信號量、小任務、工作隊列,把內核事件傳遞到應用層來處理。
作為第一個開篇文章,從最簡單的內核編譯開始。
實際操作一下:如何把一個最簡單的驅動程序(hello
),按照 2 種方式進行編譯:
編譯進內核;
編譯為一個獨立的驅動模塊;
實踐環境
為了便於測試,以下操作都是在 Ubuntu16.04 操作系統里完成的。
編譯Linux
驅動程序,肯定需要內核源碼,這里選擇的是 linux-4.15 版本,可以在官網下載。
文末有下載方式。
下載之后,把linux-4.15.tar.gz
解壓到Ubuntu
中任意目錄即可,例如:解壓到~/tmp/
目錄下:
$ tar -zxvf linux-4.15.tar.gz -C ~/tmp/
編譯進內核
創建驅動程序目錄
linux
中的驅動,一般都放在 linux-4.15/drivers/ 目錄下,因此在這個目錄中創建一個hello
文件夾。
$ mkdir linux-4.15/drivers/hello
對於一個驅動來說,最重要的就是3
個文件:
源代碼
Kconfig
Makefile
只要按照固定的格式來編寫這3
個文件,linux
內核的編譯腳本就可以確保把我們的驅動程序編譯進去。
創建源文件
首先是源碼,在hello
文件夾中創建源文件 hello.c
:
$ cd linux-4.15/drivers/hello
$ touch hello.c
源文件hello.c
的內容是:
#include <linux module.h="">
#include <linux init.h="">
// 當驅動被加載的時候,執行此函數
static int __init hello_init(void)
{
printk(KERN_ALERT "welcome, hello"\n");
return 0;
}
// 當驅動被卸載的時候,執行此函數
static void __exit hello_exit(void)
{
printk(KERN_ALERT "bye, hello\n");
}
// 版權聲明
MODULE_LICENSE("GPL");
// 以下兩個函數屬於 Linux 的驅動框架,只要把驅動兩個函數地址注冊進去即可。
module_init(hello_init);
module_exit(hello_exit);
有兩個小地方注意一下:
在內核中,打印函數是 printk,而不是 printf;
打印信息的級別有好幾個,從 DEBUG 到 EMERG,這里使用的是 KERN_ALERT,方便查看打印信息。
創建 Kconfig 文件
這個文件是用來對內核進行配置的,當執行 make menuconfig 指令的時候,這個文件就被解析。
先創建文件:
$ cd linux-4.15/drivers/hello
$ touch Kconfig
添加如下內容:
config HELLO
tristate "hello driver"
help
just a simplest driver.
default y
第一行內容 config HELLO ,在執行配置的時候,將會生成一個變量 CONFIG_HELLO ,而這個變量,將會在編譯的時候,被 Makefile 引用。
最后一行的 default y ,就表示把 CONFIG_HELLO 的值設置成 y,從而讓這個驅動被編譯到內核中。
現在,hello
驅動中的KConfig
配置文件已經准備好了,但是還需要這個配置文件登記到 Linux
內核的整體配置文件中。
也就是把它登記在 linux-4.15/drivers/Kconfig 文件的末尾:
source "drivers/hello/Kconfig"
endmenu // 加在這一句的上面
現在,可以來執行下面指令,看一下具體的配置界面:
$ cd linux-4.15/
$ make distclean
$ make ARCH=x86_64 defconfig
$ make ARCH=x86_64 menuconfig
第2
條指令,是用來把默認的配置保存到當前目錄下的 .config 配置文件,也就是把一個默認的配置文件復制過來,作為我們自己的配置文件。
以后再修改配置參數時,修改的內容就會存儲在 .config 文件中,
第3
條指令,是用來配置內核的,可以進入 Device Drivers
菜單,然后在最底層看到我們的 hello driver
被標記成星號,
這表示被編譯進內核。
按向下方向鍵,把高亮定位到 Device Drivers ---> ,然后敲回車鍵,進入到 Device Drivers 的配置界面。
按向下方向鍵,一直到最后一個條目,就可以看到我們的 hello 驅動了,如下:
可以看到 hello driver 前面顯示的是型號 *,這表示:該驅動將會編譯進內核。
我們可以按下空格鍵試一下,會在三種標記中切換:型號,M,空值。M 標記意思是編譯成驅動模塊。
我們這里選擇星號(編譯進內核),然后按下右方向鍵,最下方的幾個按鍵的焦點移動到
按下回車鍵,就會彈出保存對話框,選擇默認保存文件 .config 即可,然后在
此時,在彈出的確認窗口中,選擇 <exit>
,按下回車鍵即可:
此時,返回到 Device Drivers 的配置界面,在最下面的按鈕中,選擇讓<exit>
高亮,然后一路退出即可。
創建 Makefile 文件
Makefile
文件是make
工具的腳本,首先創建它:
$ cd linux-4.15/drivers/hello
$ touch Makefile
其中的內容只有一行:
obj-$(CONFIG_HELLO) += hello.o
CONFIG_HELLO 可以看做一個變量,在編譯的時候,這個變量的值可能是:y, n 或者 m。
在剛才的 Kconfig 參數配置中,CONFIG_HELLO 被設置為 y,於是這句話就被翻譯成: obj-y += hello,表示把 hello 驅動編譯進內核。
現在,hello
驅動程序的Makefile
已經創建好了,我們還要讓linux
內核的編譯框架知道這個文件才行。
在文件 linux-4.15/drivers/Makefile 中的末尾,添加如下內容:
obj-$(CONFIG_HELLO) += hello/
編譯
萬事俱備,只欠編譯!依次執行如下指令:
$ cd linux-4.15/
$ make -j4
make
指令執行結束之后,編譯得到的內核中(vmlinux
)就包含了我們的hello
驅動。
編譯為驅動模塊
編譯為驅動模塊,也有兩種 操作方式:
編譯所有的驅動模塊
在執行 make ARCH=x86_64 menuconfig 指令的時候,把 hello 配置成 M;
然后在 linux-4.15 中執行編譯模塊指令:make -j4 modules。
編譯成功之后,就可以得到文件: linux-4.15/drivers/hello/hello.ko。
這樣的編譯指令,是把所有的模塊都編譯了一次(在輸出信息中,可以看到編譯了很多模塊)。
只編譯 hello 這一個驅動模塊
另外一種編譯驅動模塊的方式是:進入hello
目錄,只編譯這一個驅動模塊。
這種編譯方法,就需要修改hello
目錄下的Makefile
文件了,內容如下:
可以把 hello 目錄下的所有文件刪除,只保留源文件 hello.c,然后新建 Makefile 文件。
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNEL_PATH) M=$(PWD) clean
endif
然后,在hello
文件夾中執行make
指令,即可得到驅動模塊 hello.ko 。
驗證一下
加載驅動:
$ cd linux-4.15/drivers/hello
$ sudo insmod ./hello.ko
此時終端窗口是沒有任何輸出的,需要輸入指令 dmesg | tail ,可以看到 hello_init 函數的輸出內容:
卸載驅動:
$ sudo rmmod hello
再次輸入 dmesg | tail ,可以看到 hello_exit 函數的輸出內容:
資料下載
在公眾號【IOT物聯網小鎮】的后台回復關鍵字:1112,獲取下列文件的網盤地址:
linux-4.15.tar.gz
hello文件夾壓縮包
推薦閱讀
【2】C語言指針-從底層原理到花式技巧,用圖文和代碼幫你講解透徹