字符设备驱动基础篇1——简单的驱动源码分析
以下內容源于朱有鵬嵌入式課程的學習,如有侵權,請告知刪除。
參考資料:http://www.cnblogs.com/biaohc/p/6575074.html
module_test.c代碼
#include <linux/module.h> // module_init module_exit #include <linux/init.h> // __init __exit #include <linux/fs.h>#define MYMAJOR 200//這里手動地定義主設備號,之前必須確認200沒有被用,查看方法是cat /proc/devices。比較好的方法是內核自動分配。 #define MYNAME "testchar"static int test_chrdev_open(struct inode *inode, struct file *file) {// 這個函數中真正應該放置的是打開這個設備的硬件操作代碼部分// 但是現在暫時我們寫不了這么多,所以用一個printk打印個信息來做代表。printk(KERN_INFO "test_chrdev_open\n");return 0; }static int test_chrdev_release(struct inode *inode, struct file *file) {printk(KERN_INFO "test_chrdev_release\n");return 0; }// 自定義一個file_operations結構體變量,并且去填充. //這里的.只是結構體變量初始化的一種方法而已。file_operations是內核中已經定義好的結構體。 static const struct file_operations test_fops = {.owner = THIS_MODULE, // 慣例,直接寫即可.open = test_chrdev_open, // 將來應用open打開這個設備時實際調用的.release = test_chrdev_release, // 就是這個.open對應的函數 };// 模塊安裝函數 static int __init chrdev_init(void) { int ret = -1;printk(KERN_INFO "chrdev_init helloworld init\n");// 在module_init宏調用的函數中去注冊字符設備驅動ret = register_chrdev(MYMAJOR, MYNAME, &test_fops);if (ret){printk(KERN_ERR "register_chrdev fail\n");return -EINVAL;}printk(KERN_INFO "register_chrdev success...\n");return 0; }// 模塊卸載函數 static void __exit chrdev_exit(void) {printk(KERN_INFO "chrdev_exit helloworld exit\n");// 在module_exit宏調用的函數中去注銷字符設備驅動unregister_chrdev(MYMAJOR, MYNAME);}module_init(chrdev_init); module_exit(chrdev_exit);// MODULE_xxx這種宏作用是用來添加模塊描述信息 MODULE_LICENSE("GPL"); // 描述模塊的許可證 MODULE_AUTHOR("aston"); // 描述模塊的作者 MODULE_DESCRIPTION("module test"); // 描述模塊的介紹信息 MODULE_ALIAS("alias xxx"); // 描述模塊的別名信息1、常用的模塊操作命令
- lsmod(list module,將模塊列表顯示),功能是打印出當前內核中已經安裝的模塊列表;
- insmod(install module,安裝模塊),功能是向當前內核中安裝一個模塊,用法是insmod xxx.ko;
- modinfo(module information,模塊信息),功能是打印出一個內核模塊的自帶信息,用法是modinfo xxx.ko;
- rmmod(remove module,卸載模塊),功能是從當前內核中卸載一個已經安裝的模塊,用法是rmmod xxx(只需要輸入模塊名即可,不能加.ko后綴);
2、模塊的安裝
insmod與module_init宏
- insmod與module_init宏有關聯,即執行insmod命令時,insmod內部實際執行的是使用module_init宏所聲明的函數。
- 比如insmod module_test.ko時,insmod命令內部實際執行的操作是調用chrdev_init函數。
- 由上表明,模塊的安裝函數得自己編寫,內核自身沒有此模塊的安裝函數,內核只是幫忙調用你所編寫的模塊安裝函數。
- 除了調用綁定的函數外,在內部還執行了另外一些事情。
3、模塊卸載
rmmod和module_exit宏
- 和上面闡述的是同一個道理;
- 使用lsmod查看rmmod前后系統的模塊記錄變化。
4、模塊的版本信息
(1)使用modinfo查看模塊的版本信息;
(2)內核zImage中也有一個確定的版本信息;
(3)insmod時,驅動的版本信息和內核的版本信息要一致
- 否則不能安裝,報錯信息為:insmod: ERROR: could not insert module module_test.ko: Invalid module format;
- 模塊的版本信息是為了保證模塊和內核的兼容性,是一種安全措施;
(4)如何保證模塊的vermagic和內核的vermagic一致?
- 確保某個內核源碼樹,即用來生成用于燒錄的zImage,也用來編譯驅動模塊。
5、模塊中常用宏
MODULE_xxx這種宏作用是用來添加模塊描述信息,見代碼的解釋。
(1)MODULE_LICENSE,模塊的許可證。
- 一般聲明為GPL許可證,而且最好不要少,否則可能會出現莫名其妙的錯誤(譬如一些明顯存在的函數提升找不到)。
(3)MODULE_DESCRIPTION 模塊描述
(4)MODULE_ALIAS 別名信息
6、函數修飾符
(1)__init
- 本質上是宏定義,在內核源代碼中有#define __init xxxx。
- 作用:將所修飾的函數放入.init.text段(默認情況下函數是被放入.text段中)。
- 這類函數都被鏈接器鏈接放入.init.text段中,因此這類函數被統一放在一起。
- 內核啟動時,會統一加載.init.text段中的這些模塊安裝函數,加載完后就會把這個段給釋放掉以節省內存。因此安裝完后這些函數就沒用了。
- 下劃線越多,越深入內核深處。
(2)__exit
7、static
8、printk函數詳解
(1)printk在內核源碼中用來打印信息的函數。
(2)printk和printf的差別
- printf是C庫函數,在應用層編程中使用,不能在linux內核源代碼中使用;
- printk是內核源碼中的一個普通函數,只能在內核源碼范圍內使用,不能在應用編程中使用。
(3)printk可以設置打印級別
- printk的打印級別,用來控制printk打印的這條信息是否在終端上顯示的;
- 應用程序中的調試信息要么全部打開,要么全部關閉,一般用條件編譯來實現(DEBUG宏);
- 但是在內核非常龐大,打印信息非常多,因此需要設置打印級別。
- 可以用cat /proc/sys/kernel/printk查看打印級別;
- 可以用echo 4 /proc/sys/kernel/printk來設置打印級別為4。?
(4)操作系統的命令行中也有一個打印信息級別屬性,值為0-7。
- 執行printk的時候,對比printk中的打印級別與命令行中設置的打印級別;
- 小于命令行設置級別的信息會被放行打印出來,大于的就被攔截的。
- 但ubuntu中不管如何設置級別,都不能直接打印出來,必須使用dmesg命令去查看。
9、關于驅動模塊中的頭文件
- 驅動源代碼中包含的頭文件,和應用編程程序中包含的頭文件不同。
- 應用編程中,所包含的頭文件是應用層的頭文件,是應用程序的編譯器帶來的(譬如gcc的頭文件路徑在 /usr/include下,與操作系統無關)。
- 驅動源碼屬于內核源碼的一部分,驅動源碼中的頭文件,即內核源代碼目錄下的include目錄下的頭文件。
- 內核頂層目錄下的include目錄下的linux目錄下,有init.h。
10、Makefile分析
#ubuntu的內核源碼樹,如果要編譯在ubuntu中安裝的模塊就打開這2個
#KERN_VER = $(shell uname -r)
#KERN_DIR = /lib/modules/$(KERN_VER)/build //這是ubuntu提供的在本Ubuntu環境下開發驅動的內核源碼樹,因此如果想在此Ubuntu中開發驅動,則內核源碼樹目錄就是它# 開發板的linux內核的源碼樹目錄
KERN_DIR = /root/driver/kernelobj-m += module_test.oall:make -C $(KERN_DIR) M=`pwd` modules cp:cp *.ko /root/porting_x210/rootfs/rootfs/driver_test.PHONY: clean
clean:make -C $(KERN_DIR) M=`pwd` modules clean
(1)KERN_DIR,表示用來編譯這個模塊的內核源碼樹的目錄;
(2)obj-m += module_test.o,-m表示將module_test.c文件編譯成一個單獨的模塊;
(3)make -C $(KERN_DIR) M=`pwd` modules,此命令用來編譯模塊。
- 利用make -C $(KERN_DIR)進入指定的內核源碼樹目錄,然后在源碼目錄樹下,借用內核源碼中定義的模塊編譯規則,去編譯該模塊modules;
- 其實就是make modules,modules是內核中的一個目標;中間的是參數,表明到某個目錄下進行編譯,編譯完后回到當前目錄(反引號表示它是一個命令)。
(5)make clean ,用來清除編譯痕跡。
(5)總結
- 模塊的makefile非常簡單,本身并不能完成模塊的編譯,而是通過make -C進入到內核源碼樹下,借用內核源碼的體系來完成模塊的編譯鏈接。
- 此Makefile非常模式化,(3)和(4)(5)是永遠不用動的,只有(1)和(2)需要動。
總結
以上是生活随笔為你收集整理的字符设备驱动基础篇1——简单的驱动源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2017全国计算机二级office题库,
- 下一篇: 原码、反码、补码,以及负数的位操作