手把手教Linux驅動1-模塊化編程,玩轉module


大家好,從本篇起,一口君將手把手教大家如何來學習Linux驅動,預計會有20篇關於驅動初級部分知識點。本專題會一直更新,有任何疑問,可以留言或者加我微信。

 

一、什么是模塊化編程?

    Linux的開發者,遍布世界各地,他們相互之間覺大數估計都不認識。如果真的是對這些開發者進行統一管理,那是很難做到的。所以大牛們,在設計Linux內核的時候,融入了模塊化的思想。也就是說,現在大家已經有一個現成的Linux操作系統了,所有的開發者寫的代碼對於這個Linux操作系統而言都是一個模塊,開發者可以模塊的形式將自己的代碼添加到內核,也可以從操作系統中卸載自己的模塊。這種思想,在實際的開發中特別別有用。

 

   例如:在你的設備上已經運行了一個成熟的Limux操作系統,由於客戶的需求變化,你需要向這個操作系統上添加一些功能。現在你有兩種做法:

 

第一種:獲得Linux源代碼,然后修改,添加功能,貌似挺牛,但是如果你寫的代碼不能一次性到達效果,你就必須去修改,這樣就每次必須重新編譯內核,是不是很麻煩。最可怕的是你一不小心,把內核源碼給修改錯了,那該怎么辦呀?

 

第二種:快速編寫自己的功能代碼,然后以模塊的形式添加到Linux操作系統中,然后測試,發現不行,卸載模塊,繼續修改代碼,添加模塊(高富帥的干活方式),。。是不是比使用第一種方法的苦逼程序員要輕松很多呀!

大家需要注意的是,一般我們都是通過模塊化的方法向Linux操作系統添加驅動程序,那些Linux核心的代碼,我個人覺得沒有幾個人會覺得不好,需要重新修改。

 

Linux 內核模塊主要由以下幾個部分組成:

 

      模塊加載函數(必須):當通過insmod命令加載內核模塊時,模塊的加載函數會自動被內核執行,完成本模塊相關初始化工作;
      模塊卸載函數(必須):當通過rmmod命令卸載模塊時,模塊的卸載函數會自動被內核執行,完成與模塊加載函數相反的功能;
      模塊許可證聲明(必須):模塊許可證(LICENCE)聲明描述內核模塊的許可權限,如果不聲明LICENCE,模塊被加載時將收到內核被污染的警告。大多數        

      模塊參數(可選):模塊參數是模塊被加載的時候可以被傳遞給他的值,它本身對應模塊內部的全局變量;

      模塊導出符號(可選):內核模塊可以導出符號(symbol,對應於函數或變量),這樣其他模塊可以使用本模塊中的變量或函數;
      模塊作者等信息聲明(可選)。

 

二、進行模塊化編程

直接看代碼,no code no bb!

 

 

 

    看完上面的代碼,請相信,你已經對模塊化編程有了一個基本的認識。上面這段代碼雖然很簡單,但是他包含了Linux內核模塊化編程需要的所有信息。

    我們來一起總結一下Linux內核模塊化編程必備的步驟:

第一步:

      包含linux/init.h和inux/module.h這兩個頭文件;

    通過MODULE LICENSE("GPL"),告訴內核你的模塊遵從"GPL"協議,這個事情必須得做。如果不知道GPL的讀者自己去查找相關資料;Linux能夠成功一個關鍵因素就是遵循了GPL,從而一發不可收拾,在全球蔓延開來。

  MODULE_AUTHOR("yikoulinux")用來指定編寫這個模塊的作者,可以不寫。

第二步:

    編寫功能代碼,注意這里沒有main函數,這里叫模塊的入口函數,函數名一般叫“xxx_init”,這里叫"hello_init"。名字是不是一定要好了,你可以把模塊的入口函數當做你的main兩數,你的代碼就從這個地方起步吧!

 

那這個函數什么時候被調用呢?

    在模塊加載到Linux內核的時候,Linux內核會調用這個函數模塊的退出函數,這個函數的名字一般叫“xxx_exit”,這里叫“hello_exit”。這個函數里,我們一般會做些資源的釋放。在模塊卸載的時候會被調用到。當一個模塊卸載的時候,我們肯定要把它占用的資源釋放掉,不然不就造成資源浪費了。

第三步:

    告訴內核,你的模塊入入口和模塊出口。Linux內核提供了兩個宏,分別是:module_init 和 module_exit.

 

 

下面我們就來詳細說一下printk函數。

三、Linux內核打印函數printk

    printk的用法和printf類似,print用於用戶空間,printk用於內核空間。用printk函數時,內核會根據日志級別,可能把消息打印到當前控制台上,這個控制台通常是一個字符模式的終端、一個串口打印機或是一個並口打印機。

這些消息正常輸出的前提是:日志輸出級別小於console_loglevel(在內核中數字越小優先級越高)。

日志級別一共有8個級別,printk的日志級別定義如下(在include/linux/kemel.h中);

 

 

 

沒有指定日志級別的printk語句默認采用的級別是DEFAULT_MESSAGE_LOGLEVEL(這個默認級別一般為<4>,即與KERN_WARNING在一個級別上)。

我們可以通過cat/proc/sys/kemel/printk這個文件,查看系統默認的日志級別

 

 

 

 

 

 

 

 

 

printk,其實不用想那么復雜,你就把它當做printf使用也可以的,在這里我們還不能測試printk輸出的消息,是否能到控制台上,因為我們不知道如何編譯我們的模塊代碼、如何加載我們的模塊、如何卸載我們的模塊。

好,接下來我們來看看,如何編譯我們的模塊。

 

四、模塊的編譯

這里,先給出Linux模塊化編譯的流程:

 

 

 

 

 

 

 

 

 

 

 

 

模塊的編譯分兩步:

第一步:調用linux源碼樹的Makefile進行收集編譯一個模塊所需要的信息

第二步:linux源碼樹的Makefile在收集完信息后,調用模塊的Makefile。獲取需要編譯成模塊的“.c”文件,最后生成模塊文件

 

明白了模塊的編譯流程,接下來我們就來看具體如何編寫模塊的Makefile

 

# KERNELRELEASE :在內核源碼樹的Makefile中定義,在當前的Makefile中,它的值為空#$(shell uname-r) :獲得當系統的Linux內核版本

#KDIR      :制定當前Linux操作系統源代碼路徑,即編譯生成的模塊是在當前系統中使用#如果想將你寫的模塊,用在你的開發板上運行的Linux系統中,

只需在KDIR變量中指定你開發板Linux系統源碼樹的路徑

#PWD:=$(shell pwd)獲得當前路徑

 

 

 

我們來分析Makefile的執行過程

1. 在模塊的源代碼目錄下執行make,此時,宏“KERNELRELEASE”【內核源碼樹的Makefile會定義】沒有定義,因此進入else;

2. 記錄內核路徑KDIR和當前工作目錄PWD;

 

 

3. 由於make 后面沒有目標,所以make會在Makefile中的第一個不是以.開頭的目標作為默認的目標執行,於是all成為make的目標;

all:的第一個命令

 

 

 類似於printf函數,編譯經過此處會打印提示信息。

 4. make的第二條命令會執行 make -C $(KDIR) M=$(PWD) modules

翻譯下就是:

make -C /lib/modules/3.2.0-29-generic-pae/build M=/home/peng/driver/1/module modules

-C 表示到存放內核的目錄執行其Makefile,

M=$(PWD)表示返回到當前目錄,

modules表示編譯成模塊的意思

之所以這么寫是由內核源碼樹的頂層Makefile告訴我們的,當我們調用Linux內核源碼樹頂層的Makefile時,找到的是頂層Makefile的“modules”目標。我們來看下頂層Makefile的modules目標寫了什么:

 

 

 

 

 

 

 

 

 

 

【截取了部分內容,我們沒有必要全部了解,只需要關心紅色部分即可,特別是對應的英文注釋】

 

5. 找到modules目標后,接下來Linux源碼樹的頂層Makeflle就需要知道是將那些".c"文件編譯成模塊。誰告訴它呢?是的,模塊的Makefile文件。所以接下來就會回調模塊的Makefile。需要注意的是,此時KERNELRELEASE已經在Linux內核源碼樹的頂層Makefile中定義過了,所以此時它獲得信息是:

obj-m:=hello.o

obj-m表示會將hello.o目標編譯成.ko模塊;它告訴linux源碼樹頂層Makefile是動態編譯(編譯成模塊)而不是編譯進內核(obj-y),linux源碼樹頂層Makefile會根據hello.o找到hello.c文件

 

6. 將模塊文件hello.c編譯為.o,然后再將多個目標鏈接為.ko。

 

最終編譯結果如下:

 

 

 

 

 

由執行結果可知,Makefile最終被調用了三次

  1) 執行命令make調用

  2) 被linux內核源碼樹的頂層Makefile調用,產生.o文件

  3) 被linux源碼樹頂層Makefile調用,將.o文件鏈接生成.ko文件

 

五、模塊的加載、卸載

如何將編譯好的模塊添加到Linux內核?如何從Linux內核將我們的模塊卸載下來?

1.模塊的加載命令

insmod xxx.ko

 例如:在ubuntu系統中添加自己寫的模塊

sudo insmod hello.ko

注意:在Linux系統中只有超級用戶權限才可以添加模塊到內核。

2.查看系統中的模塊命令

lsmod

例如:在系統中搜索自己添加的hello模塊

 

sudo lsmod | grep hello

 

3.卸載模塊命令

sudo rmmod 模塊名

 

例如:卸載系統中的hello模塊
sudo mmod hello

 

4.查看加載模塊和卸載模塊通過printk打印的信息命令

dmesg或dmesg|tail

 

這個命令主要是從Linux內核的ring buffer(環形緩沖區)中讀取信息的。

 

那什么是ring buffer呢?

在Limux系統中,所有通過printk打印出來的信息都會送到ring buffer中。我們知道,我們打印出來的信息是需要在控制台設備上顯示的。在Linux內核初始化的時候,控制台設備並沒有初始化的時候,使用printk會不會有問題

控制台設備,因為此時printk只是把信息輸送到ring buffer中,等控制台設備初始化好后,在根據ring buffer中消息的優先級決定是否需要輸送到控制台設備上。

 

如何清空ring buffer呢?

 sudo dmesg -c

操作結果如下:

 

 


 

一口君操作全部在特權模式下,如果在普通用戶權限下前面加sudo。

更多信息請關注公眾號:一口Linux


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM