嵌入式Linux學習筆記(一) 啟航、計划和內核模塊初步體驗


1.總結

  從事嵌入式行業多年,雖然因為工作原因接觸過嵌入式Linux,也參與過相關產品的底層和應用功能開發,但對於嵌入式Linux的內核,驅動,以及上層開發,仍然停留在初級的水平,沒有過系統深入的去總結整理,隨着工作年限的遞增,越來越感受到這種浮躁感帶來的技術面瓶頸。既然發現了問題,自然就要去解決,回想起我踏入嵌入式行業來的經歷,正是對STM32芯片以及網絡部分的學習總結筆記支撐我走到如今的地步,那么沉淀下來,從嵌入式Linux入門開始整理,層層深入,對嵌入式Linux進行系統的總結也是最符合我目前現狀的解決辦法,這也是我下定決心放棄日常娛樂,開始本系列的由來。

  嵌入式Linux的掌握學習是很復雜的過程,從最基礎的Linux安裝,shell指令的學習和應用,交叉編譯環境搭建,C語言開發,Linux內核接口,Linux系統接口,在掌握了前面所有知識后,才只是完成了產品開發的基礎構建,這些知識不僅對於學習是難點,對於已經掌握的人來用文字描述清楚,特別是系統/軟件版本引發的編譯,調試問題,如果沒有總結和整理,這部分經驗是文字很難描述的,嵌入式Linux是一門應用開發技術,多練多總結才能積累足夠的知識。另外如果遇到問題,不要着急,要善於使用搜索引擎,嵌入式Linux開發遇到的問題基本都能找到答案,但找到解決方法只是目的之一,如何從這些方法中總結經驗,也是學習中的重要部分,這部分對於開發者更加重要,切記!這是我做嵌入式軟件開發來最重要的經驗。按照正常的預期流程,嵌入式Linux的學習應該是講如何注冊字符型設備,然后按照從易到難的順序在掌握中斷和時鍾,文件系統,塊設備,I2C驅動,LCD驅動,攝像頭驅動,網絡設備驅動,設備樹,然后在講述涉及上層的QT界面,遠程訪問的網絡socket(B/S, C/S框架),以及應用端的Android平台開發,多線程,多進程同步等知識,這也是大部分開發板的例程方案,可從我經驗來看,如果按照上面的流程是可以覆蓋嵌入式Linux的主要工作需求的(可能部分知識是溢出的)。但是對於開發產品來說,這些只是基礎的技術,而不是應用的產品方案,事實上,對於剛入門的來說,如何從學習思維轉變為工程師開發思維這部分更加重要,從更高維的角度了解嵌入式Linux開發,這也是本系列的目的。我們先制定一個產品目標(可能不符合現有的產品模型),所有學習都圍繞着此產品來開發。這個系列將不僅僅講述學習嵌入式,而且也講述我根據工作積累的開發經驗,如何完成項目,也方便未踏入行業的人員什么是嵌入式軟件開發。

題目1:基於串口(RS485/RS232)的局域網管理設備

系統架構

       

硬件說明

  正點原子的I.MX6U-ALPHA開發平台,256MB(DDR3)+256MB/512MB(NAND)核心板。涉及硬件 RS232,GPIO,I2C,SPI, ADC, DAC

學習筆記章節

  嵌入式Linux學習筆記(一) 啟航、計划和體驗

  嵌入式Linux學習筆記(二) 交叉編譯環境和Linux系統編譯、下載

  嵌入式Linux學習筆記(三) 字符型設備驅動--LED的驅動開發

  嵌入式Linux學習筆記(四) 設備樹和UART驅動開發

   嵌入式Linux學習筆記(五) 通訊協議制定和下位機代碼實現

  嵌入式Linux學習筆記(六) 上位機QT界面實現和通訊實現 

代碼路徑

  詳細代碼見:https://github.com/zc110747/remote_manage

軟件說明

  1.上位機軟件支持串口通訊,雙機通訊需要制定協議(可使用自定義協議或者Modbus),支持界面化管理(目前定義使用QT開發, 與后續的完善計划有關)
  2.支持文件傳輸,文件傳輸支持斷點重傳(傳輸后文件位於指定文件夾,初步定義為/usr/download)
  3.能夠查詢內部的一些數據,除顯示已經列出狀態外,支持后期擴展查詢其他狀態

任務分解

  1. uboot,內核和文件系統的編譯,下載和調試,並集成ssh方便傳輸應用文件調試
  2. 分模塊完成驅動的開發調試,不過為了方便測試及后期集成,需要同步完成串口驅動,串口通訊協議定義及上位機的軟件框架
  3. 后期的綜合性功能調試和應用開發(如協議擴展問題,狀態查詢到界面顯示,考慮到協議數據的復用, 后期該數據可能用於網頁界面的狀態顯示或者QT界面的控制)

參考資料

  1. 宋寶華《Linux設備驅動開發詳解 -- 基於最新的Linux4.0內核》第四章 Linux內核模塊

內核模塊初探

  本節作為整個系列的起點,重點當然是上面的項目規划和任務分解,不過為了讓文章更豐富,我們可以初步體驗下Linux下的應用和編程,下面代碼將執行在Ubuntu系統,PC端,事實上PC端的Ubuntu可以驗證很多實現,如加載驅動和設備,實現QT界面,進行網絡通訊的應用端測試,所以一定不要忽略這個優勢,本小節的代碼都是在PC端測試完成,用於體驗內核模塊開發的特征。作為內核模塊,可以通關Kernel編譯時加入到內核中,也可以通過insmod/rmmod動態的加載到系統中,為了滿足Linux系統的訪問,內核模塊就需要實現接口用於Linux訪問,開發者只要按照規則用C語言實現這些需要的接口,在按照一定的規則編譯后,就可以使用lsmod/rmmod來加載和移除自定義的模塊,這套規則就是我們掌握內核模塊需要學習的知識,按照功能分為以下接口:

必須模塊

  模塊加載函數:module_init(func)

  模塊卸載函數: module_exit(func)

  模塊許可聲明:MODULE_LICENSE("xxx") 支持的許可有: "GPL", "GPL V2", "GPL and additional right", "Dual BSCD/GPL", "DUAL MPL/GPL", "Proprietary"

可選模塊

  模塊參數 -- 模塊加載時傳遞變量 module_param(name, charp, S_IRUGO);

  模塊導出符號 --用於將符號導出,用於其它內核模塊使用。

    EXPORT_SYSMBOL(func)/EXPORT_SYSMBOL_GPL(func)

    注意:Linux內核2.6增加了函數校驗機制,后續模塊需要引入時要在Module.symvers下添加導入函數內核的路徑和symbol。

  模塊作者 -- MODULE_AUTHOR("xxx")

  模塊描述 -- MODULE_DESCRIPTION("xxx")

  模塊版本 -- MODULE_VERSION("xxx")

  模塊別名 -- MODULE_ALIAS("xxx")

  模塊設備表 -- MODULE_DEVICE_TABLE, 對於USB或者PCI設備需要支持,表示支持的設備,這部分比較復雜,這里就不在多說,后續如果用到,在詳細去說明。

  在了解上述模塊的基礎上,就可以實現如下的模塊代碼:

 1 //hello.ko
 2 #include <linux/init.h>
 3 #include <linux/module.h>
 4 
 5 
 6 //extern int add_integar(int a, int b);
 7 static char *buf = "driver";
 8 module_param(buf, charp, S_IRUGO); //模塊參數
 9 
10 static int __init hello_init(void)
11 {
12         int dat = 3; //int dat = add_integar(5, 6);
13         printk(KERN_WARNING "hello world enter, %s, %d\n", buf, dat);
14         return 0;
15 }
16 module_init(hello_init);  //模塊加載函數
17 
18 static void __exit hello_exit(void)
19 {
20     printk(KERN_WARNING "hello world exit\n");
21 }
22 module_exit(hello_exit);              //模塊卸載函數
23 
24 MODULE_AUTHOR("ZC");                //模塊作者
25 MODULE_LICENSE("GPL v2");                     //模塊許可協議
26 MODULE_DESCRIPTION("a simple hello module");  //模塊許描述
27 MODULE_ALIAS("a simplest module");            //模塊別名
View Code

使用Makefile文件如下:

 1 ifeq ($(KERNELRELEASE),)
 2 KDIR := /lib/modules/$(shell uname -r)/build
 3 PWD := $(shell pwd)
 4 modules:
 5         $(MAKE) -C $(KDIR) M=$(PWD) modules
 6 modules_install:
 7         $(MAKE) -C $(KDIR) M=$(PWD) modules_install
 8 clean:
 9         rm -rf *.o *.ko .depend *.mod.o *.mod.c modules.*
10 .PHONY:modules modules_install clean
11 else
12 obj-m :=hello.o
13 endif
View Code

保存后,使用Make即可編譯,如果遇到編譯錯誤,請先查看文章最后的備注,未包含問題請搜索或者留言,編譯結果如圖所示。

 

之后執行指令modinfo hello.ko即可查看當前的模塊信息。

如果無法查看信息,可通過dmesg查看加載信息。

內核模塊的跨模塊調用

  上一節可以解決我們遇到的大部分內核實現問題,但某些時候我們可能需要一些公共內核模塊,提供接口給大部分模塊使用,這就涉及到內核模塊的跨模塊調用。

  對於跨核模塊調用的實現,對於調用的模塊,主要包含2步:

    1、在代碼實現中添加extern int add_integar(int a, int b);

    2、在編譯環境下修改Module.symvers, 添加被鏈接模塊的地址,函數校驗值(可通過查看被鏈接模塊編譯環境下的Module.symvers內復制即可)

  對於被鏈接的模塊,代碼實現如下:

 1 //math.ko
 2 #include <linux/init.h>
 3 #include <linux/module.h>
 4 
 5 static int __init math_init(void)
 6 {
 7     printk(KERN_WARNING "math enter\n");
 8     return 0;
 9 }
10 module_init(math_init);
11 
12 static void __exit math_exit(void)
13 {
14     printk(KERN_WARNING "math exit\n");
15 }
16 module_exit(math_exit);
17 
18 int add_integar(int a, int b)
19 {
20         return a+b;
21 }
22 EXPORT_SYMBOL(add_integar);
23 
24 int sub_integar(int a, int b)
25 {
26         return a-b;
27 }
28 EXPORT_SYMBOL(sub_integar);
29 
30 MODULE_LICENSE("GPL V2");
View Code

編譯Makefile同上,需要將obj-m :=hello.o修改為obj-m :=math.o

執行make編譯完成該文件,並通過insmod加載完模塊后,可通過

grep integar /proc/kallsyms 查看加載在內核中的符號,狀態如下:

然后加載insmod hello.ko, 即可跨文件調用該接口。如此,便初步完成對Linux內核模塊的學習。

備注

1.內核編譯名稱必須為Makefile,否則編譯會出錯

 make[2]: *** No rule to make target `/usr/kernel/hello/Makefile'.  Stop.

 make[1]: *** [_module_/usr/kernel/hello] Error 2

 make[1]: Leaving directory `/usr/src/linux-headers-3.5.0-23-generic'

2.Makefile的內容,如果編譯多個文件obj-m :=hello.o test.o

3.Makefile中,指令必須以Tab對齊,否則編譯會異常。

4.printk不打印,一般來說輸出的KERNEL_INFO為超過最大輸出值,可直接通過dmesg,在系統信息內查看。

5.內核跨文件訪問接口

除EXPORT_SYSMBOL外,在編譯時Module.symvers需要包含對應函數的校驗值,路徑

0x13db98c9      sub_integar     /usr/kernel/math/math   EXPORT_SYMBOL

0xe1626dee      add_integar     /usr/kernel/math/math   EXPORT_SYMBOL

否則編譯時報警告

WARNING: "add_integar" [/usr/kernel/hello/hello.ko] undefined!

安裝模塊時出錯

[ 9091.025357] hello: no symbol version for add_integar

[ 9091.025360] hello: Unknown symbol add_integar (err -22)


免責聲明!

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



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