日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

字符设备驱动基础篇1——简单的驱动源码分析

發(fā)布時(shí)間:2023/12/20 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字符设备驱动基础篇1——简单的驱动源码分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

以下內(nèi)容源于朱有鵬嵌入式課程的學(xué)習(xí),如有侵權(quán),請(qǐng)告知?jiǎng)h除。

參考資料: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//這里手動(dòng)地定義主設(shè)備號(hào),之前必須確認(rèn)200沒(méi)有被用,查看方法是cat /proc/devices。比較好的方法是內(nèi)核自動(dòng)分配。 #define MYNAME "testchar"static int test_chrdev_open(struct inode *inode, struct file *file) {// 這個(gè)函數(shù)中真正應(yīng)該放置的是打開這個(gè)設(shè)備的硬件操作代碼部分// 但是現(xiàn)在暫時(shí)我們寫不了這么多,所以用一個(gè)printk打印個(gè)信息來(lái)做代表。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; }// 自定義一個(gè)file_operations結(jié)構(gòu)體變量,并且去填充. //這里的.只是結(jié)構(gòu)體變量初始化的一種方法而已。file_operations是內(nèi)核中已經(jīng)定義好的結(jié)構(gòu)體。 static const struct file_operations test_fops = {.owner = THIS_MODULE, // 慣例,直接寫即可.open = test_chrdev_open, // 將來(lái)應(yīng)用open打開這個(gè)設(shè)備時(shí)實(shí)際調(diào)用的.release = test_chrdev_release, // 就是這個(gè).open對(duì)應(yīng)的函數(shù) };// 模塊安裝函數(shù) static int __init chrdev_init(void) { int ret = -1;printk(KERN_INFO "chrdev_init helloworld init\n");// 在module_init宏調(diào)用的函數(shù)中去注冊(cè)字符設(shè)備驅(qū)動(dòng)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; }// 模塊卸載函數(shù) static void __exit chrdev_exit(void) {printk(KERN_INFO "chrdev_exit helloworld exit\n");// 在module_exit宏調(diào)用的函數(shù)中去注銷字符設(shè)備驅(qū)動(dòng)unregister_chrdev(MYMAJOR, MYNAME);}module_init(chrdev_init); module_exit(chrdev_exit);// MODULE_xxx這種宏作用是用來(lái)添加模塊描述信息 MODULE_LICENSE("GPL"); // 描述模塊的許可證 MODULE_AUTHOR("aston"); // 描述模塊的作者 MODULE_DESCRIPTION("module test"); // 描述模塊的介紹信息 MODULE_ALIAS("alias xxx"); // 描述模塊的別名信息

1、常用的模塊操作命令

  • lsmod(list module,將模塊列表顯示),功能是打印出當(dāng)前內(nèi)核中已經(jīng)安裝的模塊列表;
  • insmod(install module,安裝模塊),功能是向當(dāng)前內(nèi)核中安裝一個(gè)模塊,用法是insmod xxx.ko
  • modinfo(module information,模塊信息),功能是打印出一個(gè)內(nèi)核模塊的自帶信息,用法是modinfo xxx.ko;
  • rmmod(remove module,卸載模塊),功能是從當(dāng)前內(nèi)核中卸載一個(gè)已經(jīng)安裝的模塊,用法是rmmod xxx(只需要輸入模塊名即可,不能加.ko后綴);

2、模塊的安裝

insmod與module_init宏

  • insmod與module_init宏有關(guān)聯(lián),即執(zhí)行insmod命令時(shí),insmod內(nèi)部實(shí)際執(zhí)行的是使用module_init宏所聲明的函數(shù)。
  • 比如insmod module_test.ko時(shí),insmod命令內(nèi)部實(shí)際執(zhí)行的操作是調(diào)用chrdev_init函數(shù)。
  • 由上表明,模塊的安裝函數(shù)得自己編寫,內(nèi)核自身沒(méi)有此模塊的安裝函數(shù),內(nèi)核只是幫忙調(diào)用你所編寫的模塊安裝函數(shù)。
  • 除了調(diào)用綁定的函數(shù)外,在內(nèi)部還執(zhí)行了另外一些事情。

3、模塊卸載

rmmod和module_exit宏

  • 和上面闡述的是同一個(gè)道理;
  • 使用lsmod查看rmmod前后系統(tǒng)的模塊記錄變化。

4、模塊的版本信息


(1)使用modinfo查看模塊的版本信息;

(2)內(nèi)核zImage中也有一個(gè)確定的版本信息;

(3)insmod時(shí),驅(qū)動(dòng)的版本信息和內(nèi)核的版本信息要一致

  • 否則不能安裝,報(bào)錯(cuò)信息為:insmod: ERROR: could not insert module module_test.ko: Invalid module format;
  • 模塊的版本信息是為了保證模塊和內(nèi)核的兼容性,是一種安全措施;

(4)如何保證模塊的vermagic和內(nèi)核的vermagic一致?

  • 確保某個(gè)內(nèi)核源碼樹,即用來(lái)生成用于燒錄的zImage,也用來(lái)編譯驅(qū)動(dòng)模塊。

5、模塊中常用宏

MODULE_xxx這種宏作用是用來(lái)添加模塊描述信息,見(jiàn)代碼的解釋。

(1)MODULE_LICENSE,模塊的許可證。

  • 一般聲明為GPL許可證,而且最好不要少,否則可能會(huì)出現(xiàn)莫名其妙的錯(cuò)誤(譬如一些明顯存在的函數(shù)提升找不到)。
(2)MODULE_AUTHOR 編寫作者

(3)MODULE_DESCRIPTION 模塊描述

(4)MODULE_ALIAS 別名信息

6、函數(shù)修飾符


(1)__init

  • 本質(zhì)上是宏定義,在內(nèi)核源代碼中有#define __init xxxx。
  • 作用:將所修飾的函數(shù)放入.init.text段(默認(rèn)情況下函數(shù)是被放入.text段中)。
  • 這類函數(shù)都被鏈接器鏈接放入.init.text段中,因此這類函數(shù)被統(tǒng)一放在一起。
  • 內(nèi)核啟動(dòng)時(shí),會(huì)統(tǒng)一加載.init.text段中的這些模塊安裝函數(shù),加載完后就會(huì)把這個(gè)段給釋放掉以節(jié)省內(nèi)存。因此安裝完后這些函數(shù)就沒(méi)用了。
  • 下劃線越多,越深入內(nèi)核深處。

(2)__exit

7、static

8、printk函數(shù)詳解

(1)printk在內(nèi)核源碼中用來(lái)打印信息的函數(shù)。

(2)printk和printf的差別

  • printf是C庫(kù)函數(shù),在應(yīng)用層編程中使用,不能在linux內(nèi)核源代碼中使用;
  • printk是內(nèi)核源碼中的一個(gè)普通函數(shù),只能在內(nèi)核源碼范圍內(nèi)使用,不能在應(yīng)用編程中使用。

(3)printk可以設(shè)置打印級(jí)別


  • printk的打印級(jí)別,用來(lái)控制printk打印的這條信息是否在終端上顯示的;
  • 應(yīng)用程序中的調(diào)試信息要么全部打開,要么全部關(guān)閉,一般用條件編譯來(lái)實(shí)現(xiàn)(DEBUG宏);
  • 但是在內(nèi)核非常龐大,打印信息非常多,因此需要設(shè)置打印級(jí)別。
  • 可以用cat /proc/sys/kernel/printk查看打印級(jí)別;
  • 可以用echo 4 /proc/sys/kernel/printk來(lái)設(shè)置打印級(jí)別為4。?

(4)操作系統(tǒng)的命令行中也有一個(gè)打印信息級(jí)別屬性,值為0-7。

  • 執(zhí)行printk的時(shí)候,對(duì)比printk中的打印級(jí)別與命令行中設(shè)置的打印級(jí)別;
  • 小于命令行設(shè)置級(jí)別的信息會(huì)被放行打印出來(lái),大于的就被攔截的。
  • 但ubuntu中不管如何設(shè)置級(jí)別,都不能直接打印出來(lái),必須使用dmesg命令去查看。

9、關(guān)于驅(qū)動(dòng)模塊中的頭文件

  • 驅(qū)動(dòng)源代碼中包含的頭文件,和應(yīng)用編程程序中包含的頭文件不同。
  • 應(yīng)用編程中,所包含的頭文件是應(yīng)用層的頭文件,是應(yīng)用程序的編譯器帶來(lái)的(譬如gcc的頭文件路徑在 /usr/include下,與操作系統(tǒng)無(wú)關(guān))。
  • 驅(qū)動(dòng)源碼屬于內(nèi)核源碼的一部分,驅(qū)動(dòng)源碼中的頭文件,即內(nèi)核源代碼目錄下的include目錄下的頭文件。
  • 內(nèi)核頂層目錄下的include目錄下的linux目錄下,有init.h。

10、Makefile分析

#ubuntu的內(nèi)核源碼樹,如果要編譯在ubuntu中安裝的模塊就打開這2個(gè) #KERN_VER = $(shell uname -r) #KERN_DIR = /lib/modules/$(KERN_VER)/build //這是ubuntu提供的在本Ubuntu環(huán)境下開發(fā)驅(qū)動(dòng)的內(nèi)核源碼樹,因此如果想在此Ubuntu中開發(fā)驅(qū)動(dòng),則內(nèi)核源碼樹目錄就是它# 開發(fā)板的linux內(nèi)核的源碼樹目錄 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,表示用來(lái)編譯這個(gè)模塊的內(nèi)核源碼樹的目錄;

(2)obj-m += module_test.o,-m表示將module_test.c文件編譯成一個(gè)單獨(dú)的模塊;

(3)make -C $(KERN_DIR) M=`pwd` modules,此命令用來(lái)編譯模塊。

  • 利用make -C $(KERN_DIR)進(jìn)入指定的內(nèi)核源碼樹目錄,然后在源碼目錄樹下,借用內(nèi)核源碼中定義的模塊編譯規(guī)則,去編譯該模塊modules;
  • 其實(shí)就是make modules,modules是內(nèi)核中的一個(gè)目標(biāo);中間的是參數(shù),表明到某個(gè)目錄下進(jìn)行編譯,編譯完后回到當(dāng)前目錄(反引號(hào)表示它是一個(gè)命令)。
(4)編譯完成后,把生成的文件拷貝到當(dāng)前目錄下,完成編譯。

(5)make clean ,用來(lái)清除編譯痕跡。

(5)總結(jié)

  • 模塊的makefile非常簡(jiǎn)單,本身并不能完成模塊的編譯,而是通過(guò)make -C進(jìn)入到內(nèi)核源碼樹下,借用內(nèi)核源碼的體系來(lái)完成模塊的編譯鏈接。
  • 此Makefile非常模式化,(3)和(4)(5)是永遠(yuǎn)不用動(dòng)的,只有(1)和(2)需要?jiǎng)印?/li>

總結(jié)

以上是生活随笔為你收集整理的字符设备驱动基础篇1——简单的驱动源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。