手機(jī)網(wǎng)站的做電商最好賣的十大產(chǎn)品
文章目錄
- 開發(fā)驅(qū)動準(zhǔn)備工作
- 1.正常運行的Linux系統(tǒng)的開發(fā)板
- 2.內(nèi)核源碼樹
- 3.nfs掛載的rootfs
- 4.得心趁手的IDE
- 第一個Hello world 驅(qū)動程序
- 常見模塊的操作命令
- 模塊的初始化和清理
- 模塊的版本信息
- 模塊中的各種宏
- 示例Hello World代碼
- printk函數(shù)解析
- 使用MakeFile編譯驅(qū)動模塊
- 使用insmod加載模塊
開發(fā)驅(qū)動準(zhǔn)備工作
1.正常運行的Linux系統(tǒng)的開發(fā)板
開發(fā)板中的Linux的Image必須是自己編譯的,不能是別人編譯的。筆者用的是IMX8QM的開發(fā)板,任何開發(fā)板都是一樣的流程。
2.內(nèi)核源碼樹
其實就是一個經(jīng)過配置編譯之后的內(nèi)核源碼。編譯驅(qū)動的過程中就要用到內(nèi)核源碼樹。編寫代碼的時候,需要用到系統(tǒng)內(nèi)核的頭文件,因此一定要有內(nèi)核源碼,
3.nfs掛載的rootfs
盡管內(nèi)核是linux的核心,但文件卻是**用戶與操作系統(tǒng)交互所采用的主要工具。**這對linux來說尤其如此,這是因為在UNIX傳統(tǒng)中,它使用文件I/O機(jī)制管理硬件設(shè)備和數(shù)據(jù)文件。根文件系統(tǒng)首先是一種文件系統(tǒng),該文件系統(tǒng)不僅具有普通文件系統(tǒng)的存儲數(shù)據(jù)文件的功能,但是相對于普通的文件系統(tǒng),它的特殊之處在于,它是內(nèi)核啟動時所掛載(mount)的第一個文件系統(tǒng),內(nèi)核代碼的映像文件保存在根文件系統(tǒng)中,系統(tǒng)引導(dǎo)啟動程序會在根文件系統(tǒng)掛載之后從中把一些初始化腳本(如rcS,inittab)和服務(wù)加載到內(nèi)存中去運行。我們要明白文件系統(tǒng)和內(nèi)核是完全獨立的兩個部分。在嵌入式中移植的內(nèi)核下載到開發(fā)板上,是沒有辦法真正的啟動Linux操作系統(tǒng)的,會出現(xiàn)無法加載文件系統(tǒng)的錯誤。一套linux體系,只有內(nèi)核本身是不能工作的,必須要rootfs(上的etc目錄下的配置文件、/bin /sbin等目錄下的shell命令,還有/lib目錄下的庫文件等···)相配合才能工作。Linux啟動時,第一個必須掛載的是根文件系統(tǒng);若系統(tǒng)不能從指定設(shè)備上掛載根文件系統(tǒng),則系統(tǒng)會出錯而退出啟動。成功之后可以自動或手動掛載其他的文件系統(tǒng)。因此,一個系統(tǒng)中可以同時存在不同的文件系統(tǒng)。在 Linux 中將一個文件系統(tǒng)與一個存儲設(shè)備關(guān)聯(lián)起來的過程稱為掛載(mount)。使用 mount 命令將一個文件系統(tǒng)附著到當(dāng)前文件系統(tǒng)層次結(jié)構(gòu)中(根)。在執(zhí)行掛裝時,要提供文件系統(tǒng)類型、文件系統(tǒng)和一個掛裝點。根文件系統(tǒng)被掛載到根目錄下“/”上后,在根目錄下就有根文件系統(tǒng)的各個目錄,文件:/bin /sbin /mnt等,再將其他分區(qū)掛接到/mnt目錄上,/mnt目錄下就有這個分區(qū)的各個目錄和文件。
4.得心趁手的IDE
這里使用的是Vscode,配置過程如下:
在Ubuntu桌面新建文件夾
mkdir 8qm_driver
用Vscode打開新建的文件夾,可以使用在新建的文件夾內(nèi)使用終端命令
code .
配置頭文件目錄
使用快捷鍵 Ctrl+Shift+P 選擇編輯配置
輸入以下內(nèi)容
{"configurations": [{"name": "Linux","includePath": ["/home/marxist/8qm源碼/imx_4.14.98_2.0.0_ga/include","${workspaceFolder}/**"],"defines": ["__GNUC__","__KERNEL__"],"compilerPath": "/usr/bin/gcc","cStandard": "c17","cppStandard": "gnu++14","intelliSenseMode": "linux-gcc-arm64"}],"version": 4
}
注意:在includePath 塊中,換成你的Linux內(nèi)核源碼的路徑,一定要是和你的目標(biāo)機(jī)的源碼一致
比如我的目標(biāo)機(jī)的Linux版本為 4.14.98
那么編譯的時候內(nèi)核源碼也為4.14.98
當(dāng)年初學(xué)驅(qū)動的時候,就因為版本不一致,dmesg一直報錯,提示vermagic不一致,查了一天資料才發(fā)現(xiàn)這個問題,┭┮﹏┭┮
第一個Hello world 驅(qū)動程序
常見模塊的操作命令
在Linux系統(tǒng)中,管理內(nèi)核模塊的常見命令包括lsmod
、insmod
、rmmod
、modinfo
等。這些命令用于列出、加載、卸載和查看內(nèi)核模塊的詳細(xì)信息。
-
lsmod
- 作用:列出當(dāng)前加載到內(nèi)核中的所有模塊。
- 用法:直接運行
lsmod
命令,無需參數(shù)。 - 輸出:模塊名稱、使用次數(shù)及相關(guān)模塊。
lsmod
-
insmod
- 作用:加載已編譯的內(nèi)核模塊。
- 用法:
insmod <module_file.ko>
- 注意:加載時需要提供模塊的完整路徑。常用于開發(fā)階段,手動加載模塊測試。
sudo insmod my_driver.ko
-
modinfo
- 作用:顯示模塊的詳細(xì)信息,包括依賴、版本信息、許可證等。
- 用法:
modinfo <module_file.ko>
- 輸出示例:
depends
:顯示模塊依賴的其他模塊。vermagic
:顯示模塊編譯時的內(nèi)核版本和相關(guān)信息。
modinfo my_driver.ko
-
rmmod
- 作用:卸載已加載的模塊。
- 用法:
rmmod <module_name>
- 注意:模塊名稱不帶文件擴(kuò)展名
.ko
,卸載時只需提供模塊名。
sudo rmmod my_driver
模塊的初始化和清理
模塊源代碼通常使用module_init
宏聲明初始化函數(shù)。這個宏指定的函數(shù)在模塊加載時(使用insmod
命令)自動調(diào)用,例如:
static int __init chrdev_init(void)
{printk(KERN_INFO "Initializing character device\n");return 0;
}module_init(chrdev_init);
module_init
的作用是將chrdev_init
函數(shù)與insmod
命令綁定。當(dāng)運行insmod
時,這個函數(shù)被調(diào)用。初始化函數(shù)中的輸出通常使用printk
打印,可以通過dmesg
命令查看內(nèi)核消息。
模塊的版本信息
- 內(nèi)核模塊的版本信息由
vermagic
字段表示。vermagic
包含了內(nèi)核版本號、編譯器版本等信息。加載模塊時,vermagic
必須與當(dāng)前運行的內(nèi)核版本一致,否則insmod
會失敗。這是為了確保模塊與內(nèi)核的兼容性和系統(tǒng)的穩(wěn)定性。
模塊中的各種宏
-
MODULE_LICENSE
- 作用:聲明模塊的許可證類型。常見的值包括"GPL"(開源)和"Proprietary"(私有)。
- 示例:
MODULE_LICENSE("GPL");
- 注意:使用GPL許可證表示模塊遵循開源協(xié)議,加載時不會受到限制。
-
MODULE_AUTHOR
- 作用:聲明模塊的作者信息。
- 示例:
MODULE_AUTHOR("Author Name");
-
module_init 和 module_exit
-
作用:
module_init
宏用于指定模塊的初始化函數(shù),module_exit
宏用于指定模塊的清理函數(shù)。 -
示例:
module_init(chrdev_init); module_exit(chrdev_exit);
-
-
__init 和 __exit
-
作用:這些宏用于標(biāo)記初始化和清理函數(shù)。
__init
標(biāo)記的函數(shù)在模塊加載后被釋放,以節(jié)省內(nèi)存。__exit
標(biāo)記的函數(shù)在模塊卸載時調(diào)用。 -
示例:
static int __init chrdev_init(void) {printk(KERN_INFO "Initializing character device\n");return 0; }static void __exit chrdev_exit(void) {printk(KERN_INFO "Exiting character device\n"); }
-
示例Hello World代碼
新建一個kernel_test.c
#include <linux/init.h>
#include <linux/module.h>//在模塊加載到內(nèi)核程序中被執(zhí)行一次
int __init test_init(void){printk("Hello World\n");return 0;
}
//在模塊從內(nèi)核驅(qū)動中卸載時執(zhí)行一次
void __exit test_exit(void)
{printk("Goodbye World\n");}
module_init(test_init); //注冊此模塊加載的回調(diào)函數(shù)
module_exit(test_exit); //注冊此模塊卸載的回調(diào)函數(shù)
MODULE_LICENSE("GPL"); //聲明遵循協(xié)議
printk函數(shù)解析
(1)printk在內(nèi)核源碼中用來打印信息的函數(shù),用法和printf非常相似。
(2)printk和printf最大的差別:printf是C庫函數(shù),是在應(yīng)用層編程中使用的,不能在linux內(nèi)核源代碼中使用;printk是linux內(nèi)核源代碼中自己封裝出來的一個打印函數(shù),是內(nèi)核源碼中的一個普通函數(shù),只能在內(nèi)核源碼范圍內(nèi)使用,不能在應(yīng)用編程中使用。
(3)printk相比printf來說還多了個:打印級別的設(shè)置。printk的打印級別是用來控制printk打印的這條信息是否在終端上顯示的。應(yīng)用程序中的調(diào)試信息要么全部打開要么全部關(guān)閉,一般用條件編譯來實現(xiàn)(DEBUG宏),但是在內(nèi)核中,因為內(nèi)核非常龐大,打印信息非常多,有時候整體調(diào)試內(nèi)核時打印信息要么太多找不到想要的要么一個沒有沒法調(diào)試。所以才有了打印級別這個概念。
(4)操作系統(tǒng)的命令行中也有一個打印信息級別屬性,值為0-7。當(dāng)前操作系統(tǒng)中執(zhí)行printk的時候會去對比printk中的打印級別和我的命令行中設(shè)置的打印級別,小于我的命令行設(shè)置級別的信息會被放行打印出來,大于的就被攔截的。譬如我的ubuntu中的打印級別默認(rèn)是4,那么printk中設(shè)置的級別比4小的就能打印出來,比4大的就不能打印出來。
(5)ubuntu中這個printk的打印級別控制沒法實踐,ubuntu中不管你把級別怎么設(shè)置都不能直接打印出來,必須dmesg命令去查看
使用MakeFile編譯驅(qū)動模塊
在源碼同級別目錄新建一個MakeFile文件,內(nèi)容如下:
obj-m += kernel_test.o
all:make -C /home/marxist/8qm源碼/imx_4.14.98_2.0.0_ga M=$(PWD) modules
clean:make -C /home/marxist/8qm源碼/imx_4.14.98_2.0.0_ga M=$(PWD) clean
(1)KERN_DIR,變量的值就是我們用來編譯這個模塊的內(nèi)核源碼樹的目錄
(2)obj-m += kernel_test.o,這一行就表示我們要將kernel_test.c文件編譯成一個模塊
(3)make -C $(KERN_DIR) M=pwd modules 這個命令用來實際編譯模塊,工作原理就是:利用make -C進(jìn)入到我們指定的內(nèi)核源碼樹目錄下,然后在源碼目錄樹下借用內(nèi)核源碼中定義的模塊編譯規(guī)則去編譯這個模塊,編譯完成后把生成的文件還拷貝到當(dāng)前目錄下,完成編譯。
(4)make clean ,用來清除編譯痕跡
模塊的makefile非常簡單,本身并不能完成模塊的編譯,而是通過make -C進(jìn)入到內(nèi)核源碼樹下借用內(nèi)核源碼的體系來完成模塊的編譯鏈接的,這個Makefile本身是非常模式化的,3和4 部分是永遠(yuǎn)不用動的,只有1和2需要動。1是內(nèi)核源碼樹的目錄,必須根據(jù)自己的編譯環(huán)境來調(diào)整
保存文件之后,在MakeFile的同級目錄,直接執(zhí)行 make
命令即可
成功之后,出現(xiàn)很多的文件,只有ko文件,才是最終能夠被加載進(jìn)內(nèi)核的模塊,其余的都是中間編譯連接生成的文件
使用insmod加載模塊
將編譯好的模塊傳輸?shù)侥繕?biāo)機(jī)器上 使用insmod
加載,成功之后,使用dmesg
命令可以查看打印的信息
使用rmmod
命令可以卸載模塊,同時執(zhí)行exit函數(shù)
使用modinfo
查看模塊信息
至此,第一個Hello world 驅(qū)動就編寫成功了