Linux模塊編程框架

来源:http://www.cnblogs.com/xiaojiang1025/archive/2016/12/13/6142548.html
-Advertisement-
Play Games

Linux是單內核系統,可通用計算平臺的外圍設備是頻繁變化的,不可能將所有的(包括將來即將出現的)設備的驅動程式都一次性編譯進內核,為瞭解決這個問題,Linux提出了可載入內核模塊(Loadable Kernel Module,LKM)的概念,允許一個設備驅動通過模塊載入的方式,在內核運行起來之後" ...


Linux是單內核系統,可通用計算平臺的外圍設備是頻繁變化的,不可能將所有的(包括將來即將出現的)設備的驅動程式都一次性編譯進內核,為瞭解決這個問題,Linux提出了可載入內核模塊(Loadable Kernel Module,LKM)的概念,允許一個設備驅動通過模塊載入的方式,在內核運行起來之後"融入"內核,載入進內核的模塊和本身就編譯進內核的模塊一模一樣。
一個程式在編譯的地址的相對關係就已經確定了,運行的時候只是進行簡單的偏移,為了使模塊載入進內核後能夠被放置在正確的地址,並正確的調用系統的運行的導出符號表,編譯模塊的時候必須要使用系統的編譯地址,並調用系統編譯出得靜態的導出符號表。即模塊必須使用系統的配置環境:Makefile+.config,一旦這兩個文件任意一個發生了變化,都很可能導致模塊的編譯地址與系統的編譯地址不匹配,造成運行時的錯誤甚至宕機。

導出符號表

從提供系統運行效率的角度,一個模塊不是也不應該是完全獨立的,即一個模塊往往會調用其他模塊提供的功能來實現自己的功能,這樣做能更好實現系統的分工並提高效率。Linux為了實現模塊間的相互調用,設計了導出符號表,每個模塊都可以將自己的一個私有的標號導出到系統層級,以使該標號對其他模塊可見,系統在編譯一個模塊的時候會自動導出這個模塊的導出符號表到modules.syms文件(如果沒有導出任何符號,可以為空),併在載入一個模塊的時候會自動將該模塊的導出符號表與系統自身的導出符號表合併。一個系統的源碼的導出符號表一般在源碼頂層目錄的modules.syms文件中,查看正在運行的系統導出符號表使用cat /proc/kallsyms。註意,正如前面解釋的,我們的模塊之所以能夠正常運行,一個重要原因就是編譯我們模塊使用的符號地址就是編譯內核時使用的符號地址,所以運行起來雖然地址會有偏移,但是模塊中相關的符號的地址也會和內核地址一起偏移,也就還能找得到。基於這種思想,我們也可以直接查看系統當前運行的地址,將地址賦值給一個函數指針並使用,也是沒有問題的,當然,這隻是闡述原理,並不建議這麼寫模塊。
下麵這個例子可以看出編譯出的地址和運行時的地址是不一樣的:

導出符號表可以大大的提高系統的運行效率,這也是只有開源系統才能提供的一個強大的功能,但是,導出符號表的引入會導致一個小小的麻煩--模塊的依賴,當我們使用lsmod的時候,就可以查看系統當前的模塊,其最後兩列分別是該模塊被引用的次數以及引用該模塊的內核模塊,當一個模塊被其他模塊引用時,我們是不能進行卸載的,同樣,如果模塊A依賴於模塊B,那麼如果模塊B不載入的時候模塊A也載入不了。在編寫多模塊的時候尤其要註意這個問題,可以寫一個腳本管理多個依賴模塊。Linux內核使用兩個巨集來導出一個模塊的符號

EXPORT_SYMBOL(符號名)
EXPORT_SYMBOL_GPL(符號名)

模塊編譯方法

藉助內核的Makefile,編譯出的XXX.ko(Kernel Object)就是可載入到該內核的外部模塊,為了利用內核的Makefile,我們可以將編譯外部模塊的Makefile寫成如下的格式:

ifneq ($(KERNELRELEASE),)
    export-objs = demo.o
    obj-m = extern.o
else
    KERNELDIR :=  /lib/modules/$(shell uname -r)/build
    PWD       := $(shell pwd)
endif
all:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:
     .tmp_versions Module.symvers modules.order .tmp_versions .*.cmd *.o *.ko *.mod.c

這個簡單的Makfile是利用ubuntu主機的源碼Makefile來編譯模塊,學習模塊編程的開始階段在主機進行編譯調試更方便一點,下麵我解釋一下這個Makefile,首先,我們的思路還是通過內核的Makefile來準備我們的模塊,而內核的Makefile一旦執行,就會給KERNELRELEASE這個變數賦值,所以第一次進入我們這個Makfile的時候,這個變數還是空,所以執行else的部分——給相關的變數賦值,make預設編譯第一個目標allmake -C $(KERNELRELEASE)就是進入到KERNELRELEASE指定的目錄並執行裡面的Makefile,顯然,這就是我們內核源碼的頂層Makefile,接下來的選項M=$(PWD) modules都是傳入這個頂層Makefile的參數,表示我要編譯一個模塊,這個模塊位於M指定的目錄,所以內核會進行相關的配置並最終進入到"這個模塊所在的目錄",此時,我們的這個Makefile會再被進入一次,這一次是從內核Makefile中跳入這裡的,,KERNELRELEASE已經被定義過,內核Makefile想要的就是obj-m後面指定的要編譯的目標文件,所以內核Makfile就會找到我們寫的模塊源文件進行編譯。如此我們就得到了能在ubuntu下執行的xxx.ko文件,如果需要在開發板上運行,只需要將內核路徑改成開發板運行系統的源碼路徑即可,同時記得要導出相關的環境變數( ARCH, CROSS_COMPILE )

註冊/註銷模塊

Linux為每個模塊都預留了相應的地址,註冊模塊即讓該模塊對內核可見,這也是模塊工作的先決條件。註冊之後,我們就可以通過查看內核輸出信息dmesg命令來查看模塊的運行情況。經常使用內核函數printk()來輸出系統信息進行列印調試。使用insmod XXX.ko載入一個模塊,使用rmmod XXX.ko卸載一個模塊,使用lsmod查看當前系統中的模塊及其引用情況
insmod使用的是init_module()系統調用,這個系統調用的實現是sys_init_module()
rmmod使用delete_module()系統調用,這個系統調用的實現是sys_delete_module()

模塊的程式框架

#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>

/* 構造/析構函數 */
static int __init mydemo_init(void)
{
    //構造設備/驅動對象
    //初始化設備/驅動對象
    //註冊設備/驅動對象
    //必要的硬體初始化
}   
static void __exit mydemo_exit(void)
{
    //回收資源
    //註銷設備/驅動對象
}   

/* 載入/卸載模塊 */
module_init(mydemo_init);                   
module_exit(mydemo_exit);                   

/* 授權 */
MODULE_LICENSE("GPL");                  
MODULE_AUTHOR("XJ");
MODULE_DESCRIPTIPON("mymydemo");

/* 導出符號 */
EXPORT_SYMBOL(data);

註意這裡的授權是必須的,如果一個模塊沒有授權,那麼很多需要該授權的函數甚至都不能使用,同理,不合適的授權也會導致模塊運行或載入的錯誤,所以初學者一定不要忽視這個授權,相關授權的選項在"linux/module.h"中,這裡我把相關的說明貼出來供大家參考

/*
 * The following license idents are currently accepted as indicating free
 * software modules
 *
 *  "GPL"               [GNU Public License v2 or later]
 *  "GPL v2"            [GNU Public License v2]
 *  "GPL and additional rights" [GNU Public License v2 rights and more]
 *  "Dual BSD/GPL"          [GNU Public License v2
 *                   or BSD license choice]
 *  "Dual MIT/GPL"          [GNU Public License v2
 *                   or MIT license choice]
 *  "Dual MPL/GPL"          [GNU Public License v2
 *                   or Mozilla license choice]
 *
 * The following other idents are available
 *
 *  "Proprietary"           [Non free products]
 */

另一個細節是Linux內核源碼的預設頭文件路徑是頂層目錄的include目錄,所以包含頭文件的時候include可以省略,

第一個Linux模塊

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>

static int __init demo_init(void)
{
    printk(KERN_INFO"demo_init:%s,%s,%d"__FILE__,__func__,__LINE__);
    return 0;
}

static void __exit demo_exit(void)
{
    printk(KERN_INFO"demo_exit:%s,%s,%d"__FILE__,__func__,__LINE__);
}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");

執行insmod xjDemo.ko,查看執行結果

模塊傳參

我們編寫的模塊還可以在insmod的時候傳入參數,Linux提供了幾個巨集(函數)用於接收外部的參數。模塊內部使用這些函數,只需執行insmod xjDemo.ko num=2insmod mydemo.ko i=10insmod mydemo.ko extstr="hello" 等命令就可以將參數傳入模塊

module_param(num,type,perm);    //接收一個傳入的int數據
module_param(num,type,perm);    //接收一個傳入的charp數據
module_param_array(num,type,nump,perm);               //接收一個數組
module_param_string(name,string,len,perm);            //接收一個字元串
MODULE_PARAM_DESC("parameter description");




您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文地址 分享提綱: 1.查看是否啟動 2. 設置自動啟動 1.【查看是否啟動】 啟動SSH服務 “/etc/init.d/sshd start”。然後用netstat -antulp | grep ssh看是否能看到相關信息就可以了。 2.【設置自動啟動】 如何設置把ssh等一些服務隨系統開機自動 ...
  • nginx代理https站點(親測) 首先,我相信大家已經搞定了nginx正常代理http站點的方法,下麵重點介紹代理https站點的配置方法,以及註意事項,因為目前大部分站點有轉換https的需要所以學習這個方法也是很重要的,我就不啰嗦了 直奔主題了……………… 測試環境 nginx版本 ngin ...
  • 在安裝好的matlab下有MCR(MatlabCompilerRuntime)在matlab2011/toolbox/compiler/deploy/glnxa64下找到MCRInstaller.zip安裝包解壓後執行install命令,選擇路徑進行安裝即可 環境變數設置:setenv LD_LIB ...
  • 最新發佈的開源 Zephyr Project™(Zephyr 項目)是一款小型且可伸縮的實時操作系統,尤其適用於資源受限的系統,可支持多種架構;該系統高度開源,對於開發人員社區完全開放,開發人員可根據需要對該系統進行剪裁,以支持最新硬體、工具和設備驅動程式;該系統高度模塊化平臺,可輕鬆集成任何架構的 ...
  • PS:在伺服器硬體資源額定有限的情況下,最大的壓榨伺服器的性能,提高伺服器的併發處理能力,是很多運維技術人員思考的問題。要提高Linux系統下的負載能力,可以使用nginx等原生併發處理能力就很強的web伺服器,如果使用Apache的可以啟用其Worker模式,來提高其併發處理能力。除此之外,在考慮 ...
  • 1 刪除Mysql yum remove mysql mysql-server mysql-libs mysql-server; find / -name mysql 將找到的相關東西delete掉; rpm -qa|grep mysql(查詢出來的東東yum remove掉) 2 安裝Mysql ...
  • 在Arduino中,可以使用AnalogWrite來使用硬體產生490Hz/980Hz的pwm波,並可根據參數來設定占空比。不瞭解這個的同學可以去 "AnalogWrite" 學習下, "SecretsOfArduinoPWM" 也是講了Arduino在avr的定時/計數器上做的封裝,我們這裡並不講 ...
  • Linux文件和目錄的屬性及許可權 命令: [root@oldboy ~]# ls -lhi total 40K 24973 -rw-------. 1 root root 1.1K Dec 10 16:02 anaconda-ks.cfg 15 -rw-r--r--. 1 root root 22K... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...