Linux设备驱动归纳总结(一):内核的相关基础概念
目錄
一.Linux設備驅(qū)動的作用:
二.內(nèi)核代碼樹介紹:
三.內(nèi)核補丁:
四.內(nèi)核中的Makefile:
五.子目錄下的Makefile和Kconfig:
六.內(nèi)核和模塊的編譯
七.總結
一.Linux設備驅(qū)動的作用:
1.1 內(nèi)核:?用于管理軟硬件資源,并提供運行環(huán)境。如分配4G虛擬空間等。linux設備驅(qū)動:是連接硬件和內(nèi)核之間的橋梁。?
1.2?應用層:包括POSIX接口,LIBC,圖形庫等,用于給用戶提供訪問?內(nèi)核的接口。屬于用戶態(tài),ARM運行在用戶模式(usr)或?者系統(tǒng)模式(sys)下。
1.3 內(nèi)核層:應用程序調(diào)用相關接口后,會通過系統(tǒng)調(diào)用,執(zhí)行SWI指令切換ARM的工作模式到超級用戶(svc)模式下,根據(jù)用?戶函數(shù)的要求執(zhí)行相應的操作。
1.4 硬件層:硬件設備,當用戶需要操作硬件時,內(nèi)核會根據(jù)驅(qū)動接口?操作硬件設備
1.5 圖結構:
1.6 對比生活酒店服務圖解:
二.內(nèi)核代碼樹介紹:
?
|-arch :?包含和硬件體系結構相關的代碼
|-block :?硬盤調(diào)度算法,不是驅(qū)動
|-firmware :?固件,如BOIS
|-Documentation:?標準官方文檔
|-dirver : linux設備驅(qū)動
|-fs :?內(nèi)核所支持的文件體系
|-include?:頭文件。linux/module.h linux/init.h?常用庫。
|-init?:庫文件代碼,C庫函數(shù)在內(nèi)核中的實現(xiàn)。
? ? ? ?init/main.c ->start_kernel->內(nèi)核執(zhí)行第一條代碼
|-ipc :?進程件通信
|-mm?:內(nèi)存管理
|-kernel :?內(nèi)核核心部分,包括進程調(diào)度等
|-net?:網(wǎng)絡協(xié)議
|-sound :?所有音頻相關
其中,跟設備驅(qū)動有關并且經(jīng)常查閱的文件夾有:
init,include (linux, asm-arm),drivers,arch 等文件夾。
三.內(nèi)核補丁:
3.1 補丁說明:
一般都是基于某個版本內(nèi)核生成的,用于升級舊內(nèi)核。
打補丁需要注意:
1.對應版本的補丁只能用于對應版本的內(nèi)核。
2.如果在已打補丁的內(nèi)核再打補丁,需要先卸載原來補丁。
3.2 打補丁的方法:
1.制作補丁:
diff -Nur linux-2.6.30/ linux-2.6.30.1/ > linux-2.6.30.1.patch
命令:diff -Nur 舊版本? 新版本 > 目標補丁.patch
2.打補丁:
cd linux-2.6.30? //注意在原文件夾的目錄中打補丁
patch -p1 < ../linux-2.6.30.1.patch //-p1是忽略一級目錄
3.恢復:
cd linux-2.6.30 //注意在原文件夾的目錄中打補丁
patch -R < ../linux-2.6.30.1.patch //撤銷補丁
四.內(nèi)核中的Makefile:
對于內(nèi)核,Makefile分為5類:
Documentation/kbuild/makefiles.txt描述如下:
Makefile?總Makefile,控制內(nèi)核的編譯
.config??內(nèi)核配置文件,配置內(nèi)核時生成,?如make menuconfig后
arch/$(ARCH)/Makefile??對應體系結構的Makefile
scripts/Makefile.*? ? ? ? ? ?Makefile共用的規(guī)則
kbuild Makefiles?各子目錄下的Makefile,被上層的Makefile調(diào)用。
簡單來說,編譯內(nèi)核會執(zhí)行以下兩步驟,它們分別干了以下的事情。
4.1?如果第一次編譯menuconfig進行配置,就會生成.config,保存好配置,下次編譯就直接拷貝在內(nèi)核文件夾根目錄即可,減少重復工作量。
4.2 make menuconfig
4.2.1、由總rcMakefile決定編譯的體系結構(ARCH).?編譯工具(CROSS_COMPILE),并知道需要進去哪些內(nèi)核根下的哪些目錄進行編譯。
4.2.2、由arch/$(ARCH)/Makefile,決定arch/$(ARCH)下還有?的哪些目錄和文件需要編譯。
4.2.3、知道了需要編譯的目錄后,遞歸的進入哪些目錄下,讀取每一個Kconfig的信息,生成了圖形配置的界面。
4.2.4、通過我們在圖形配置界面中選項為[*]、[M]或者[]。
4.2.5、保存并退出配置,會根據(jù)配置生成一份新的配置文件.config,并在同時生成include/config/auto.conf(這是.config的去注釋版)。文件里面保存著CONFIG_XXXX等變量應該取y還是取m。
4.3 make
根據(jù)Makefile包含的目錄和配置文件的要求,進去個子目錄進行編譯,最后會在各子目錄下?生成一個.o或者.a文件,然后總Makefile指定的連接腳本?arch/$(ARCH)/kernel/vmlinux.lds生成vmlinux,并通過?壓縮編程bzImage,或者按要求在對應的子目錄下編譯成?模塊。。
但是,具體是怎么生成配置文件的呢?
4.3.1?在總Makefile中,根據(jù)以下語句進入需要編譯的目錄
# Objects we will link into vmlinux / subdirs we need to visit
init-y := init/
drivers-y := drivers/ sound/ firmware/
net-y := net/
libs-y := lib/
core-y := usr/
endif # KBUILD_EXTMOD
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
上面說明了,根目錄下的init、driver、sound、firmware、net、lib、usr等目錄,在編譯時都會進去讀取目錄下的Makefile并進行編譯。
4.3.2 在總Makefile中包含的目錄還是不夠的,內(nèi)核還需要根據(jù)對應的CPU體系架構,
決定還需要將哪些子目錄將要編譯進內(nèi)核。在總Makefile中有一個語句:
include $(srctree)/arch/$(SRCARCH)/Makefile //在這里,我定義SRCARCH = arm
可以看出,在總Makefile中進去讀取相應體系?結構的Makefile->arch/$(SRCARCH)/Makefile。
arch/$(SRCARCH)/Makefile中指定arch/$(SRCARCH)路徑下的哪些子目錄需要被編譯。
在?arch/arm/Makefile?下:
head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
# If we have a machine-specific directory, then include it in the build.
core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
core-y += $(machdirs) $(platdirs)
core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/
core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ)
core-$(CONFIG_VFP) += arch/arm/vfp/
drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/
libs-y := arch/arm/lib/ $(libs-y)
上面看到,指定需要進入arch/arm/kernel/、arch/arm/mm/、arch/arm/common/?等目錄編譯,至于core-y、?core-$(CONFIG_FPE_NWFPE)這些是什么東西呢?
?
其中,y表示編譯成模塊,m表示編譯進內(nèi)核(上面沒有,因為默認情況下ARM全部編譯進?內(nèi)核),但$(CONFIG_OPROFILE)又是什么呢??這些是根據(jù)用戶在make menuconfig中設置后,生成的值賦給了CONFIG_OPROFILE。
?
4.3.3?那make menuconfig后的配置信息是怎么來的?
這是由各子目錄下的Kconfig提供選項功用戶選擇并配置。
如arch/arm/Kconfig。?所有的配置都是根據(jù)arch/$(ARCH)/Kconfig文件通過Kconfig的語法source讀取?各個包含的子目錄Kconfig來生成一個配置界面。每個Makefile目錄下都有一個?對應的Kconfig文件,用于生成配置界面來給用戶決定內(nèi)核如何配置,配置后會確定一個。?CONFIG_XXX的的值(如上面的CONFIG_OPROFILE),來決定編譯進內(nèi)核,還是編譯成模塊或者不編譯。
如在arch/arm/Kconfig下:
source "arch/arm/mach-clps711x/Kconfig"
source "arch/arm/mach-ep93xx/Kconfig"
source "arch/arm/mach-footbridge/Kconfig"
source "arch/arm/mach-integrator/Kconfig"
source "arch/arm/mach-iop32x/Kconfig"
source "arch/arm/mach-iop33x/Kconfig"
這些就是用來指定,需要讀取以下目錄下的Kconfig文件來生成一個使用make menuconfig時的配置界面。
至于子目錄下的Kconfig是怎么樣的,待會介紹。
總結Kconfig的作用:
1.在make menuconfig下可以配置選項;
2.在.config中確定CONFIG_XXX的的值。
3.只是讀取以上的兩個Makefile還是不夠了,內(nèi)核還會把包含的子目錄一層一層的?讀取它里面的Makefile和Kconfig。
?
內(nèi)核的編譯并不是一個Makefile搞定的,需要通過根目錄下的總Makefile來包含一下子Makefile(不管是根目錄下的子目錄還是/arch/arm中的子目錄)。而Kconfig,為用戶提供一個交互界面來選擇如何配置并生成配置選項。
五.子目錄下的Makefile和Kconfig:
上面我一直介紹的都是兩個比較大的Makefile——總Makefile和?arch/$(ARCH)/Makefile。接下來看一下實例。
?
5.1 在makefile中,y表示編譯進內(nèi)核,m表示編譯成模塊,不寫代表不編譯。?所以,配置最簡單的方法就是,直接修改子目錄的Makefile?。
先看看arch/arm/Makefile:
/*arch/arm/mach-projectName/Makefile?*/
obj-$(CONFIG_CPU_projectName) += irq.o
obj-$(CONFIG_CPU_projectName) += clock.o //配置時鐘進入模塊
obj-$(CONFIG_projectName_DMA) += dma.o
如果我要取消時鐘(當然這是必須要開的,只是舉例)。?可以直接修改arch/arm/mach-projectName/Makefile?將obj-$( CONFIG_CPU_projectName) += clock.o改為
obj- += clock.o
如果你想編譯成模塊也可以修改成:
obj-m += clock.o
5.2 在一般的編譯內(nèi)核時,我們都是通過”make menuconfig”進入圖形界面面配置的,?接下來我實現(xiàn)一下如何將一個選項加入到圖形配置界面中。
5.2.1 進入內(nèi)核目錄
cd linux-2.6.29
5.2.2 在driver目錄下模擬一個名為test1驅(qū)動的文件夾
mkdir driver/test1
5.2.3 在目錄下隨便些一個C文件,只要不報錯。
vim test1.c
我的test1.c如下:
?void foo()
{
?;
}
5.2.4?vim Makefile //在目錄下編寫一個簡單的Makefile
Makefile文件編寫如下:
obj-$(CONFIG_TEST1) += test1.o
CONFIG_TEST1是決定test1是否編譯進內(nèi)核或者編譯成模塊的。這就是通?過同一目錄下的Kconfig來在配置界面中生成選項,由用戶在make menuconfig中選擇。
5.2.5所以還要同一目錄下寫一個Kconfig:
vim Kconfig
Kconfig修改如下:
menu "test1 driver here" //這是在圖形配置顯示的
config TEST1
bool "xiaobai test1 driver" //這同樣也是在圖形配置顯示的
help
This is test1 //這個也是在圖形配置顯示的。
說白了,就是在圖形配置的driver下多了一個配置選項,用戶配置后將?CONFIG_TEST1的值存放在.config中,Makefile通過讀取.config的去注?釋版include/config/auto.conf讀取到CONFIG_TEST的值,再進行編譯。
?
但是,以上幾步還不能達到目的,因為雖然在總Makefile中已經(jīng)包含了?目錄driver,但是driver目錄的Makefile中并沒有包含test目錄。因此?需要在driver/Makefile中添加:
obj-$(CONFIG_STAGING) += staging/
obj-y += platform/
obj-$(CONFIG_TEST1) += test1/ //這是我添加的
雖然Makefile中已經(jīng)包含了,但這樣還是不行。因為當需要配置ARM時,?ARM結構下的Kconfig并沒有包含test的Kconfig。這樣的話就不會出現(xiàn)在?圖形配置界面中,因此在arch/arm/Kconfig中添加:
menu "Device Drivers" //要在Device Drivers這個選項里面添加
source "drivers/base/Kconfig"
source "drivers/connector/Kconfig"
source "drivers/test/Kconfig" //這是我添加的
大功告成!
這樣,make menuconfig界面寫的Driver Devices下就多了一個?"test1 friver here"的目錄,里面有一個配置選項"xiaobai test1 driver"。
?
Kconfig文件的語法在documentation/kbuild/kconfig-language.txt文件中?有詳細的講解,上面我只是簡單實現(xiàn)了一下,都是皮毛。
六.內(nèi)核和模塊的編譯
?
編譯內(nèi)核很簡單,只需要配置完畢后執(zhí)行make命令,將指定的文件編譯進內(nèi)核
bzImage或者編譯成模塊。
make = make bzImage + make modules
因此如果值編譯內(nèi)核,即只編譯配置文件中-y選項,可以直接用命令
make bzImage
如果值編譯模塊,即只編譯配置文件中的-m選項,可以之直接使用命令
make modules
模塊可以編譯當然也可以清除,使用命令
make modules clean
如果只想單獨編譯一個模塊,可以使用命令
make M=drivers/test/ modules //只單獨編譯drivers/test中的.ko
make M=drivers/test/ modules clean //清除
上面的是在內(nèi)核目錄下的操作,但當我寫驅(qū)動時,我并不可能在內(nèi)核目錄下編
寫,但我編譯時卻要依賴內(nèi)核中的規(guī)則和Makefile,所以就有了以下的方法,
同時這也是一般的編寫驅(qū)動時Makefile的格式。
指定內(nèi)核Makefile并單獨編譯
make -C /root/linux-2.6.29 M=`pwd` module
make -C /root/linux-2.6.29 M=`pwd` module clean
//-C?指定內(nèi)核Makefile的路徑,可以使用相對路徑。
//-M?指定要編譯的文件的路徑,同樣課使用相對路徑。
編譯生成的模塊可以指定存放的目錄
make -C /root/linux-2.6.29 M=`pwd` modules_install INSTALL_MOD_PATH=/nfsroot
七、總結
內(nèi)核編譯時大體上究竟是怎么樣的一個過程。
7.1 一般我們會想將一份項目的默認配置拷貝到內(nèi)核跟目錄下并改名為.config。
7.2 make menuconfig
7.2.1、由總Makefile決定編譯的體系結構(ARCH).?編譯工具(CROSS_COMPILE),并知道需要進去哪些內(nèi)核根下的哪些目錄進行編譯。
7.2.2、由arch/$(ARCH)/Makefile,決定arch/$(ARCH)下還有?的哪些目錄和文件需要編譯。
7.2.3、知道了需要編譯的目錄后,遞歸的進入哪些目錄下,讀取每一個Kconfig的信息,生成了圖形配置的界面。
7.2.4、通過我們在圖形配置界面中選項為[*]、[M]或者[]。
7.2.5、保存并退出配置,會根據(jù)配置生成一份新的配置文件.config,并在同時生成include/config/auto.conf(這是.config的去注釋版)。文件里面保存著CONFIG_XXXX等變量應該取y還是取m。
7.3?make
根據(jù)Makefile包含的目錄和配置文件的要求,進去個子目錄進行編譯,最后會在各子目錄下?生成一個.o或者.a文件,然后總Makefile指定的連接腳本?arch/$(ARCH)/kernel/vmlinux.lds生成vmlinux,并通過?壓縮編程bzImage,或者按要求在對應的子目錄下編譯成?模塊。
?
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Linux设备驱动归纳总结(一):内核的相关基础概念的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android APP层 ShellUt
- 下一篇: Android NDK 学习汇总