如何編寫一個簡單的Linux驅動(一)
前言
最近在學習Linux驅動,記錄下自己學習的歷程。
驅動的基本框架
Linux驅動的基本框架包含兩部分,「模組入口、出口的註冊」和「模組入口、出口函數的實現」,如下方程式碼。
1 static int __init shanwuyan_init(void) //驅動入口函數 2 { 3 return 0; 4 } 5 6 static void __exit shanwuyan_exit(void) //驅動出口函數 7 { 8 9 } 10 11 module_init(shanwuyan_init); //註冊入口函數 12 module_exit(shanwuyan_exit); //註冊出口函數
其中,module_init()和module_exit()兩個函數的作用是註冊驅動的入口「shanwuyan_init」和出口「shanwuyan_exit」。載入驅動時會運行入口函數,卸載驅動時會運行出口函數。入口函數的作用是載入驅動時做一些初始化工作,比如註冊設備、申請設備號、生成設備節點等等,其返回值為int類型;出口函數的作用是卸載驅動時做一些善後操作,比如註銷設備、註銷設備號、銷毀類等等。
一個基本驅動的編寫
本文的主要目的是讓讀者了解驅動的基本框架,我們先不實現註冊設備、申請設備號、註銷設備等複雜的工作。
為了讓驅動的載入和卸載工作更直觀得為程式設計師所觀察,我們可以在入口函數和出口函數中添加列印語句,這樣每次載入和卸載驅動的時候,程式設計師都能在終端觀察到相應的資訊,如下方程式碼。
1 static int __init shanwuyan_init(void) //驅動入口函數 2 { 3 printk(KERN_EMERG "shanwuyan_init\r\n"); 4 return 0; 5 } 6 7 static void __exit shanwuyan_exit(void) //驅動出口函數 8 { 9 printk(KERN_EMERG "shanwuyan_exit\r\n"); 10 }
「printk」函數是什麼?說到列印,有C語言基礎的讀者首先想到的可能就是「printf」函數,但是「printf」只能在應用層面工作,而設備驅動是工作在內核態下的,所以「printf」不能在設備驅動中工作。在內核態下的列印函數是「printk」函數。KERN_EMERG是列印優先順序,這裡採用了最高優先順序。
再加上頭文件以及註冊用的函數,可以得到一個相對完整的程式碼。
1 /* 源程式碼文件名為:shanwuyan.c */ 2 #include <linux/module.h> 3 #include <linux/kernel.h> 4 #include <linux/init.h> 5 #include <linux/fs.h> 6 #include <linux/uaccess.h> 7 8 static int __init shanwuyan_init(void) //驅動入口函數 9 { 10 printk(KERN_EMERG "shanwuyan_init\r\n"); 11 return 0; 12 } 13 14 static void __exit shanwuyan_exit(void) //驅動出口函數 15 { 16 printk(KERN_EMERG "shanwuyan_exit\r\n"); 17 } 18 19 module_init(shanwuyan_init); //註冊入口函數 20 module_exit(shanwuyan_exit); //註冊出口函數
該設備驅動實現的功能是:載入驅動時列印字元串「shanwuyan_init」,卸載驅動時列印字元串「shanwuyan_exit」。
Makefile文件的編寫
Makefile文件沒什麼可說的,程式碼如下。
1 #!/bin/bash 2 3 obj-m += shanwuyan.o #此處要和你的驅動源文件同名 4 5 KDIR := /home/topeet/Android/iTop4412_Kernel_3.0 #這裡是你的內核目錄 6 7 PWD ?= $(shell pwd) 8 9 all: 10 make -C $(KDIR) M=$(PWD) modules #make操作 11 12 clean: 13 make -C $(KDIR) M=$(PWD) clean #make clean操作
應用
編譯,並載入生成的「shanwuyan.ko」文件,載入驅動和卸載驅動的命令如下。
1 insmod shanwuyan.ko #載入驅動 2 rmmod shanwuyan.ko #卸載驅動,如果該命令不起作用,請用下方的命令 3 rmmod shanwuyan #卸載驅動
進入到驅動文件所在的路徑下,並在命令行輸入載入驅動的命令「insmod shanwuyan.ko」,可以看到驅動入口函數列印出來的字元串資訊「shanwuyan_init」。
但是終端還列印了兩行警告資訊「shanwuyan: module license ‘unspecified’ taints kernel」和「Disabling lock debugging due to kernel」,這是因為我們沒有在程式碼中加入同意開源協議,所以終端列印該資訊。需要注意的是,該警告資訊只有在系統啟動後第一次載入驅動時才會列印,卸載掉之後,如果不重啟系統,再載入驅動時就不會再列印這兩行警告資訊了。
打開源文件,加入GPL開源協議,一個完整的基本驅動框架就完成了,全部程式碼如下。
1 /* 源程式碼文件名為:shanwuyan.c */ 2 #include <linux/module.h> 3 #include <linux/kernel.h> 4 #include <linux/init.h> 5 #include <linux/fs.h> 6 #include <linux/uaccess.h> 7 8 static int __init shanwuyan_init(void) //驅動入口函數 9 { 10 printk(KERN_EMERG "shanwuyan_init\r\n"); 11 return 0; 12 } 13 14 static void __exit shanwuyan_exit(void) //驅動出口函數 15 { 16 printk(KERN_EMERG "shanwuyan_exit\r\n"); 17 } 18 19 module_init(shanwuyan_init); //註冊入口函數 20 module_exit(shanwuyan_exit); //註冊出口函數 21 22 MODULE_LICENSE("GPL"); //同意GPL開源協議,就不會列印警告資訊了 23 MODULE_AUTHOR("shanwuyan"); //還可以再添加上作者名稱
再次編譯,重啟系統,並載入驅動,這次不會再列印警告資訊了,只列印了我們在入口函數中寫的字元串,如下圖。
使用「rmmod shanwuyan」命令卸載驅動,出現錯誤,如下圖。
這是我們需要創建文件夾「/lib/modules」,創建後再次卸載驅動,又出現了錯誤,如下圖。
我們按照錯誤資訊,創建文件夾「/lib/modules/3.0.15」(根據內核版本的不同而不同),再次卸載驅動,成功,列印出來我們想要的字元串資訊「shanwuyan_exit」。