如何將系統在開發板上運行起來。
4.0 交叉編譯器的擷取。廠家提供 網上下載(廠家確認)
4.1 uboot進行操作。
1,解壓廠家源碼
2,進入源碼
3,make distclean 徹底刪除源碼的目標、臨時檔案
4,make xxx_config 針對某個CPU和開發板進行配置
5,make編譯
6,結果產生u-boot.bin
7,利用廠家提供的燒寫方法進行燒寫
8,uboot源碼/include/configs/xxx.h,硬體相關的標頭檔資訊,也是移植的重點關注的檔案,瞭解當有uboot當前的支援的硬體資訊。
4.2 kernel進行操作
1,解壓廠家源碼
2,進入源碼
3,make distclean 徹底刪除源碼的目標、臨時檔案,擷取最乾淨的原始碼,不要讓上一次的編譯結果影響到這一次編譯
4,make xxx_defconfig 針對某個CPU和開發板進行配置, xxx_defconfig位於arch/arm/configs/目錄下面找同名的設定檔.
5,make menuconfig 做三個檢查:
檢查當前核心是否支援當前CPU架構;
檢查當前核心是否支援當前處理器;
The System type(處理器) -> ….
檢查當前核心是否支援當前的開發板;
Board selection(開發板)
6,make zImage/make uImage
7,編譯結果;arch/arm/boot/zImage(uImage)
8,平台代碼檔案 arch/arm/mach-處理器名/mach-開發板名.c
多多看這個目錄下對應平台下面的檔案,這裡的檔案很多與硬體相關;
4.3 掛接rootfs(根檔案系統)
方法一:可以使用廠家提供(廠家的提供的根檔案系統非常龐大,裡面可能有很多我們用不到的服務);
方法二:鼓勵自己製作rootfs,利用busybox;
系統啟動時間:uboot時間+核心啟動時間+掛接根檔案系統時間;
4.4 設定系統啟動的參數;
最關鍵的參數:
bootcmd : 用於載入和引導核心
bootargs:用於給核心傳遞參數,指示核心將來掛接根檔案系統。
給核心傳遞的方法。切記
方法一:利用uboot的bootargs ,
方法一:如果有的boardloader沒有bootargs,可以核心自身傳遞參數(在源碼裡面);
在原始碼中執行:
make menuconfig
Boot options ->
(console = xxx)Default kernel command string //核心自己給自己傳遞參數,把游標移動到這個位置,按斷行符號進行修改,例如利用NFS網路啟動:root = /dev/nfs nfsroot = 192.168.1.5 儲存即可
[] Alwasy use the default dernel commad string //如果選擇為*,uboot的bootargs無效,核心自己傳遞有效;如果不選擇,則相反;
4.5 NFS網路檔案系統啟動的注意事項
1,ubuntu系統必須搭建好NFS網路服務
vim /etc/exports
sudo /etc/init.d/nfs-kernel-server restart
2,配置核心支援NFS網路檔案系統
make menuconfig
File system —>
[*] Network File systems —>
[*] Root file system on NFS //必須選中,否則無法掛接NFS網路檔案系統
Linux裝置驅動開發相關內容:
ARM&Linux工作模式。
SVC(管理)模式、USR模式;
USR模式切換到SVC模式。通過軟體中斷切換;
當代碼在USR模式運行,軟體就對應在使用者空間運行;
當代碼在SVC模式運行,軟體就對應在核心空間運行;
使用者空間與核心空間:
使用者空間:
包含的軟體:應用程式(軟體)、C庫、自己封裝製作的動態庫
CPU的工作模式:USR
虛擬位址空間範圍:0x000,0000~ 0XBFFF,FFFF (3G記憶體)
採用虛擬記憶體技術前提是CPU必須整合MMU
使用者不能訪問核心地址空間,包括代碼和資料;
使用者不能直接操作硬體資源;
要和核心空間通訊必須採用系統調用
要和核心空間進行通訊,其實就是CPU工作模式的切換
USR模式與SVC模式的切換採用非強制中斷
核心空間:
包含的軟體:進程管理、記憶體管理、裝置驅動、檔案系統、TCP/IP網路通訊協定棧
CPU的工作模式:SVC
虛擬位址空間範圍為:0xC000,0000 ~ 0xFFF,FFFF(核心1G的虛擬位址空間對於所有進程共用)
能夠直接存取硬體資源
總結:使用者空間與核心空間劃分的目的:實現作業系統的安全保護
使用者空間與核心空間劃分的依據本質要依賴ARM工作模式
使用者空間與核心空間進行資料通訊必須利用系統調用
使用者 軟體編程格式
#?include<stdio.h> //標準C的標頭檔int main(int argc,char *argv[])//程式入口{ printf("hello word! \n"); return 0;//程式出口}
核心空間 軟體編程格式
#include <linux/module.h>#include <linux/init.h>//標頭檔位於核心源碼static int fifth_drv_init(void){ printk("hello word,%s \n!",__func__);//其中k代表kernel return 0;}static void fifth_drv_exit(void){}module_init(fifth_drv_init);//module_init 入module_exit(fifth_drv_exit);//module_exit 出
入口:通過module_init修飾的函數
出口:通過module_exit修飾的函數
module_init的定義也位於核心源碼;
實驗:
1,mkdir /opt/drivers/day01/1.0 -p // -p, --parents 需要時建立上層目錄,如目錄早已存在則不當作錯誤2, cd /opt/drivers/day01/1.0 3, vim helloword4,儲存退出
核心基本編程規範:
1,不允許使用標準C的庫和標頭檔,核心使用的標頭檔都是位於核心源碼
2,核心列印函數不是printf,而是使用核心提供的列印函數printk();列印輸出的格式與C一樣
3,核心模組代碼沒有main函數,取而代之的是module_init宏指定的函數為核心模組代碼的入口函數;整個軟體在運行時,首先啟動並執行是入口函數。
入口函數的傳回值:成功返回0 失敗返回負值
4,核心模組代碼對應的出口是module_exit宏指定的函數;
5,module_init、module_exit的源碼同樣位於核心源碼
6,核心驅動編程一定要注意記憶體的合法訪問,否則會造成系統崩潰
7,核心驅動編程不允許處理float或double
8,進程在使用者空間的棧和核心空間的棧是不一樣的,核心空間分配8K
9,在編譯核心驅動程式時,需要額外編寫Makefile,來關聯源碼。
10,驅動對應的可執行檔為ko結尾,相關操作命令
insmod rmmod lsmmod modinfo
11,核心模組資訊 許可聲明必須添加 : MODULE_LICENSE(“GPL”);
12,模組的命令列傳參
本質目的:在載入模組或模組運行期間(卸載模組之前)能夠給模組傳遞參數資訊
筆試題:闡述你對static的認識。
核心模組學源碼編譯問題:
明確:核心模組源碼編譯必須要結合核心原始碼 /opt/kernel
編譯只需在 /opt/drivers/day01/1.0 添加一個Makefile即可:
obj -m += helloword.o #將helloword.c編譯成對應的二進位可執行模組檔案helloword.koKDIR = /opt/kernel #指定核心源碼路徑all : #偽目標,這裡沒有依賴 #make前是tab鍵而非空格 make -C $(KDIR) SUBDIRS = $(PWD) modules # -C表示到某個指定的目錄下去編譯,類似: cd /opt/kernel; make # SUBDIRS:指定一個子目錄,這個子目錄為當前路徑。 其中PWD為系統全域環境變數,目前的目錄;告訴核心原始碼,在這個目錄下面的檔案需要編譯成模組 #採用模組化編譯,將.c編譯成.ko檔案 #展開為:make -C /opt/kernel SUBDIRS=/opt/drivers/day01/1.0 modulesclean: make -C $(KDIR) SUBDIRS = $(PWD) clean
編譯:
只需執行make命令即可
結果helloworld.c編譯成對應的helloworld.ko檔案
注意:在編譯helloworld.c之前,如果核心源碼沒有配置、編譯,必須先對其進行配置、編譯
調試宏:
FILE,FUNCTION,LINE,DATE,TIME
vim技巧:
如果在同一個檔案裡面,有重複出現的函數或變數,可以ctrl+n自動補全
行複製:shift+v 選中行,然後上下選擇
如何運行使用核心模組可執行檔helloword.ko?核心空間其實就是我們的zImage;
insmod helloword.ko #載入模組到核心中,一旦載入成功,核心首先執行對應的入口函數,如果入口函數返回0,模組載入成功,模組正式投入運行,如果返回一個負數,模組載入失敗rmmod helloword #用於從核心中卸載模組,當卸載模組時,對應的出口函數被核心執行,一旦卸載,核心就無此模組lsmod #查看當前核心有哪些載入的模組modinfo helloword #查看模組.ko本身的屬性或特性
實驗步驟:
1,file helloworld.ko查看模組的屬性 1,cp helloworld.ko /opt/rootfs / //拷貝到開發板的根檔案系統 2,insmod helloworld.ko 4,lsmod 5,rmmod
可能遇到的問題
問題1:
helloworld: module license ‘unspecified’ taints kernel。
原因:helloworld模組沒有指定具體的許可聲明。
解決方案:添加 MODULE_LICENSE(“GPL”) ;添加許可資訊必須加
問題2:
卸載模組時提示以下資訊,無法卸載:rmmod chdir(/lib/modules) no such file….
原因:沒有目錄
解決方案:
在開發板中執行:mkdir /lib/modules
如果又提示:rmmod chdir(/lib/modules/2.6.3.5) no such file….
在開發板中執行:mkdir /lib/modules/2.6.3.5
核心模組參數
應用程式的命令列傳參:
int main(int argc,char *argv[]){//argc 表示參數個數,argv 參數資訊 int a,b,c; if(argc<4){ return -1; } //將字元轉為整數 a = strtoul(argv[1],NULL,0); b = strtoul(argv[2],NULL,0); c = strtoul(argv[3],NULL,0); printf("a = %d,b=%b,c=%c",a,b,c);}
運行 ./a.out 100 200 300
問:核心模組源碼對應的ko檔案,當在載入時,能夠像應用程式的命令列傳參一樣也傳遞參數。可以,需要利用核心的模組參數。
核心模組的使用:
目的:能夠在載入模組或模組運行期間(只要模組不卸載)能夠給模組傳遞額外的參數資訊
static int irq;//0static char *pstr;//NULL/*** module_param(name,type,perm)* 功能:指定模組參數,用於載入模組時或載入以後傳遞參數給模組* name:模組參數的名稱* type:模組參數的資料類型,不允許傳浮點數據,如果一定要* bool inbool charp short ushort int uint long ulong* perm:模組參數的存取權限。許可權一般用八進位表示,如 0664 (0表示八進位)* 如果perm(許可權)非0,那麼載入模組以後,會在/sys/modules目錄下產生一個跟模組名同名的目錄,在這個目錄的paramter目錄下會有一個跟變數名同名的檔案,通過修改這個檔案可以修改變數的值 * 如果perm(許可權)為0,則不會產生許可權檔案**/module_param(irq,int,0664);//模組參數的聲明,0664可讀可寫module_param(pstr,charp,0);//模組參數的聲明/* * 如irq的許可權為 0664,則會產生 /sys/modules/helloworld/parameters/變數名,檔案的內容為變數的內容* 可以通過 "echo 要寫入的內容 > /sys/modules/helloworld/parameters/變數名" 修改變數(會修改檔案內容)*/ static int fifth_drv_init(void){ printk("init irq = %d,pstr = %s \n",irq,pstr); return 0;}static void fifth_drv_exit(void){ printk("exit irq = %d,pstr = %s \n",irq,pstr);}module_init(fifth_drv_init);//module_init 入module_exit(fifth_drv_exit);//module_exit 出
將上述儲存退出,然後將檔案拷貝到。。。,實驗步驟:
insmod helloworld.kormmod helloworldinsmod helloworld.ko irq = 100 pstr = tarena #傳參cat /sys/modules/helloworld/parameters/irq #查看irq檔案的內容echo 255 > /sys/modules/helloworld/parameters/irq #向檔案寫入新值rmmod helloworld #查看輸出的值是否發生了變化