linux裝置驅動歸納總結(一):核心的相關基礎概念
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一.linux裝置驅動的作用
核心:用于管理軟硬體資源,并提供運作環境。如配置設定4G虛拟空間等。
linux裝置驅動:是連接配接硬體和核心之間的橋梁。
linux系統按個人了解可按下劃分:
應用層:包括POSIX接口,LIBC,圖形庫等,用于給使用者提供通路 核心的接口。屬于使用者态,ARM運作在使用者模式(usr)或 者系統模式(sys)下。
核心層:應用程式調用相關接口後,會通過系統調用,執行SWI指 令切換ARM的工作模式到超級使用者(svc)模式下,根據用 戶函數的要求執行相應的操作。
硬體層:硬體裝置,當使用者需要操作硬體時,核心會根據驅動接口 操作硬體裝置
圖結構如下:
舉一個相對比較邪惡的類比:
在深圳的酒店經常會在門縫看到一些卡片,上面說可以通過打電話送貨上門提供某中服務。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iNyEjMyAzMzYjY1QTY5ADMzYzX3MDNyYTMwIzLchDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二.核心代碼樹介紹
linux-2.6.29
|-arch : 包含和硬體體系結構相關的代碼
|-block : 硬碟排程算法,不是驅動
|-firmware : 固件,如BOIS
|-Documentation: 标準官方文檔
|-dirver : linux裝置驅動
|-fs : 核心所支援的檔案體系
|-include :頭檔案。linux/module.h linux/init.h 常用庫。
|-init :庫檔案代碼,C庫函數在核心中的實作。
init/main.c ->start_kernel->核心執行第一條代碼
|-ipc : 程序件通信
|-mm :記憶體管理
|-kernel : 核心核心部分,包括程序排程等
|-net :網絡協定
|-sound : 所有音頻相關
|
其中,跟裝置驅動有關并且經常查閱的檔案夾有:
init
include : linux, asm-arm
drivers:
arch:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
三.核心更新檔:
更新檔一般都是基于某個版本核心生成的,用于更新舊核心。
打更新檔需要注意:
1.對應版本的更新檔隻能用于對應版本的核心。
2.如果在已打更新檔的核心再打更新檔,需要先解除安裝原來更新檔。
打更新檔的方法:
1.制作更新檔:
diff -Nur linux-2.6.30/ linux-2.6.30.1/ > linux-2.6.30.1.patch
//N為新加的檔案全部修改
//linux-2.6.30 舊版本
//linux-2.6.30.1 新版本
//目标更新檔
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 //撤銷更新檔
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
四.核心中的Makefile:
對于核心,Makefile分為5類:
Documentation/kbuild/makefiles.txt描述如下:
50 The Makefiles have five parts:
51
52 Makefile 總Makefile,控制核心的編譯
53 .config 核心配置檔案,配置核心時生成, 如make menuconfig後
54 arch/$(ARCH)/Makefile 對應體系結構的Makefile
55 scripts/Makefile.* Makefile共用的規則
56 kbuild Makefiles 各子目錄下的Makefile,被上層的Makefile調用。
簡單來說,編譯核心會執行以下兩步驟,它們分别幹了以下的事情。
1一般的,我們會拷貝一個對應體系結構的配置檔案到主目錄下并改名為 .config,這樣就在make menuconfig生成的圖形配置中 已經有了一些預設的配置,減少使用者的勞動量。不過這一步不做也沒關系的。
2.make menuconfig
2.1、由總Makefile決定編譯的體系結構(ARCH). 編譯工具(CROSS_COMPILE),并知道需要進去哪些核心根下的哪些目錄進行編譯。
2.2、由arch/$(ARCH)/Makefile,決定arch/$(ARCH)下還有 的哪些目錄和檔案需要編譯。
2.3、知道了需要編譯的目錄後,遞歸的進入哪些目錄下,讀取每一個Kconfig的資訊,生成了圖形配置的界面。
2.4、通過我們在圖形配置界面中選項為[*]、[M]或者[]。
2.5、儲存并退出配置,會根據配置生成一份新的配置檔案.config,并在同時生成include/config/auto.conf(這是.config的去注釋版)。檔案裡面儲存着CONFIG_XXXX等變量應該取y還是取m。
3.make
3.1、根據Makefile包含的目錄和配置檔案的要求,進去個子目錄進行編譯,最後會在各子目錄下 生成一個.o或者.a檔案,然後總Makefile指定的連接配接腳本 arch/$(ARCH)/kernel/vmlinux.lds生成vmlinux,并通過壓縮程式設計bzImage,或者按要求在對應的子目錄下編譯成 子產品。。
但是,具體是怎麼生成配置檔案的呢?
注:我使用的核心是2.6.29。
1.在總Makefile中,根據以下語句進入需要編譯的目錄
470 # Objects we will link into vmlinux / subdirs we need to visit
471 init-y := init/
472 drivers-y := drivers/ sound/ firmware/
473 net-y := net/
474 libs-y := lib/
475 core-y := usr/
476 endif # KBUILD_EXTMOD
639 core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
上面說明了,根目錄下的init、driver、sound、firmware、net、lib、usr等目錄,在編譯時都會進去讀取目錄下的Makefile并進行編譯。
2.在總Makefile中包含的目錄還是不夠的,核心還需要根據對應的CPU體系架構,
決定還需要将哪些子目錄将要編譯進核心。在總Makefile中有一個語句:
529 include $(srctree)/arch/$(SRCARCH)/Makefile //在這裡,我定義SRCARCH = arm
可以看出,在總Makefile中進去讀取相應體系 結構的Makefile->arch/$(SRCARCH)/Makefile。
arch/$(SRCARCH)/Makefile中指定arch/$(SRCARCH)路徑下的哪些子目錄需要被編譯。
在 arch/arm/Makefile 下:
95 head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
187 # If we have a machine-specific directory, then include it in the build.
188 core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
189 core-y += $(machdirs) $(platdirs)
190 core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/
191 core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ)
192 core-$(CONFIG_VFP) += arch/arm/vfp/
193
194 drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/
195
196 libs-y := arch/arm/lib/ $(libs-y)
上面看到,指定需要進入arch/arm/kernel/、arch/arm/mm/、arch/arm/common/ 等目錄編譯,至于core-y、 core-$(CONFIG_FPE_NWFPE)這些是什麼東西呢?
其中,y表示編譯成子產品,m表示編譯進核心(上面沒有,因為預設情況下ARM全部編譯進 核心),但$(CONFIG_OPROFILE)又是什麼呢? 這些是根據使用者在make menuconfig中設定後,生成的值賦給了CONFIG_OPROFILE。
3.那make menuconfig後的配置資訊是怎麼來的?
這是由各子目錄下的Kconfig提供選項功使用者選擇并配置。
如arch/arm/Kconfig。 所有的配置都是根據arch/$(ARCH)/Kconfig檔案通過Kconfig的文法source讀取 各個包含的子目錄Kconfig來生成一個配置界面。每個Makefile目錄下都有一個 對應的Kconfig檔案,用于生成配置界面來給使用者決定核心如何配置,配置後會确定一個。 CONFIG_XXX的的值(如上面的CONFIG_OPROFILE),來決定編譯進核心,還是編譯成子產品或者不編譯。
如在arch/arm/Kconfig下:
595 source "arch/arm/mach-clps711x/Kconfig"
596
597 source "arch/arm/mach-ep93xx/Kconfig"
598
599 source "arch/arm/mach-footbridge/Kconfig"
600
601 source "arch/arm/mach-integrator/Kconfig"
602
603 source "arch/arm/mach-iop32x/Kconfig"
604
605 source "arch/arm/mach-iop33x/Kconfig"
這些就是用來指定,需要讀取以下目錄下的Kconfig檔案來生成一個使用make menuconfig時的配置界面。
至于子目錄下的Kconfig是怎麼樣的,待會介紹。
總結Kconfig的作用:
3.1.在make menuconfig下可以配置選項;
3.2.在.config中确定CONFIG_XXX的的值。
4.隻是讀取以上的兩個Makefile還是不夠了,核心還會把包含的子目錄一層一層的 讀取它裡面的Makefile和Kconfig。
上面啰啰嗦嗦地講了這麼久,無非就是想說,核心的編譯并不是一個Makefile搞定的,需要通過根目錄下的總Makefile來包含一下子Makefile(不管是根目錄下的子目錄還是/arch/arm中的子目錄)。而Kconfig,為使用者提供一個互動界面來選擇如何配置并生成配置選項。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
五、子目錄下的Makefile和Kconfig
上面我一直介紹的都是兩個比較大的Makefile——總Makefile和 arch/$(ARCH)/Makefile。接下來看一下執行個體。
一、在makefile中,y表示編譯進核心,m表示編譯成子產品,不寫代表不編譯。 是以,配置最簡單的方法就是,直接修改子目錄的Makefile 。
先看看arch/arm/Makefile:
/*arch/arm/mach-s3c2440/Makefile */
12 obj-$(CONFIG_CPU_S3C2440) += s3c2440.o dsc.o
13 obj-$(CONFIG_CPU_S3C2440) += irq.o
14 obj-$(CONFIG_CPU_S3C2440) += clock.o //配置2440的時鐘進入子產品
15 obj-$(CONFIG_S3C2440_DMA) += dma.o
如果我要取消s3c2440的時鐘(當然這是必須要開的,隻是舉例)。 可以直接修改arch/arm/mach-s3c2440/Makefile 将obj-$( CONFIG_CPU_S3C2440) += clock.o改為
obj- += clock.o
如果你想編譯成子產品也可以修改成:
obj-m += clock.o
在這裡 CONFIG_CPU_S3C2440的值預設是y,是以核心是要将時鐘編譯進核心的。也許有人會問,那我直接修改 CONFIG_CPU_S3C2440的值為m不就可以将時鐘編譯成子產品了,何必修改Makefile這麼麻煩呢?的确是這樣,隻要我們通過在”make menuconfig”的界面中配置後就能夠改變 CONFIG_CPU_S3C2440的值。接下來看看如何實作。
二、在一般的編譯核心時,我們都是通過”make menuconfig”進入圖形界面面配置的, 接下來我實作一下如何将一個選項加入到圖形配置界面中。
看看具體實作的步驟:
以下的執行環境是在PC機上,我使用的核心是linux-2.6.29:
2.1.進入核心目錄
cd linux-2.6.29
2.2. 在driver目錄下模拟一個名為test1驅動的檔案夾
mkdir driver/test1
2.3. 在目錄下随便些一個C檔案,隻要不報錯。
vim test1.c
我的test1.c如下:
1 void foo()
2 {
3 ;
4 }
2.4vim Makefile //在目錄下編寫一個簡單的Makefile
Makefile檔案編寫如下:
obj-$(CONFIG_TEST1) += test1.o
CONFIG_TEST1是決定test1是否編譯進核心或者編譯成子產品的。這就是通 過同一目錄下的Kconfig來在配置界面中生成選項,由使用者在make menuconfig中選擇。
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中已經包含了 目錄driver,但是driver目錄的Makefile中并沒有包含test目錄。是以 需要在driver/Makefile中添加:
103 obj-$(CONFIG_PPC_PS3) += ps3/
104 obj-$(CONFIG_OF) += of/
105 obj-$(CONFIG_SSB) += ssb/
106 obj-$(CONFIG_VIRTIO) += virtio/
107 obj-$(CONFIG_STAGING) += staging/
108 obj-y += platform/
109 obj-$(CONFIG_TEST1) += test1/ //這是我添加的
雖然Makefile中已經包含了,但這樣還是不行。因為當需要配置ARM時, ARM結構下的Kconfig并沒有包含test的Kconfig。這樣的話就不會出現在 圖形配置界面中,是以在arch/arm/Kconfig中添加:
1230 menu "Device Drivers" //要在Device Drivers這個選項裡面添加
1231
1232 source "drivers/base/Kconfig"
1233
1234 source "drivers/connector/Kconfig"
。。。。。。。。
1330 source "drivers/test/Kconfig" //這是我添加的
1331
1332 endmenu
大功告成!
這樣,make menuconfig界面寫的Driver Devices下就多了一個 "test1 friver here"的目錄,裡面有一個配置選項"xiaobai test1 driver"。
Kconfig檔案的文法在documentation/kbuild/kconfig-language.txt檔案中 有詳細的講解,上面我隻是簡單實作了一下,都是皮毛。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
六.核心和子產品的編譯
編譯核心很簡單,隻需要配置完畢後執行make指令,将指定的檔案編譯進核心
bzImage或者編譯成子產品。
make = make bzImage + make modules
是以如果值編譯核心,即隻編譯配置檔案中-y選項,可以直接用指令
make bzImage
如果值編譯子產品,即隻編譯配置檔案中的-m選項,可以之直接使用指令
make modules
子產品可以編譯當然也可以清除,使用指令
make modules clean
如果隻想單獨編譯一個子產品,可以使用指令
make M=drivers/test/ modules //隻單獨編譯drivers/test中的.ko
make M=drivers/test/ modules clean //清除
上面的是在核心目錄下的操作,但當我寫驅動時,我并不可能在核心目錄下編
寫,但我編譯時卻要依賴核心中的規則和Makefile,是以就有了以下的方法,
同時這也是一般的編寫驅動時Makefile的格式。
指定核心Makefile并單獨編譯
make -C /root/linux-2.6.29 M=`pwd` module
make -C /root/linux-2.6.29 M=`pwd` module clean
//-C 指定核心Makefile的路徑,可以使用相對路徑。
//-M 指定要編譯的檔案的路徑,同樣課使用相對路徑。
編譯生成的子產品可以指定存放的目錄
make -C /root/linux-2.6.29 M=`pwd` modules_install INSTALL_MOD_PATH=/nfsroot
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
七、總結
說了這麼久估計都說糊塗了,其實我隻是想表達一下核心編譯時大體上究竟是怎麼樣的一個過程。
我以編譯S3C2440的核心為例再說一遍:
1一般我們會想将一份S3C2440的預設配置拷貝到核心跟目錄下并改名為.config。
2.make menuconfig
2.1、由總Makefile決定編譯的體系結構(ARCH). 編譯工具(CROSS_COMPILE),并知道需要進去哪些核心根下的哪些目錄進行編譯。
2.2、由arch/$(ARCH)/Makefile,決定arch/$(ARCH)下還有 的哪些目錄和檔案需要編譯。
2.3、知道了需要編譯的目錄後,遞歸的進入哪些目錄下,讀取每一個Kconfig的資訊,生成了圖形配置的界面。
2.4、通過我們在圖形配置界面中選項為[*]、[M]或者[]。
2.5、儲存并退出配置,會根據配置生成一份新的配置檔案.config,并在同時生成include/config/auto.conf(這是.config的去注釋版)。檔案裡面儲存着CONFIG_XXXX等變量應該取y還是取m。
3.make
3.1、根據Makefile包含的目錄和配置檔案的要求,進去個子目錄進行編譯,最後會在各子目錄下 生成一個.o或者.a檔案,然後總Makefile指定的連接配接腳本 arch/$(ARCH)/kernel/vmlinux.lds生成vmlinux,并通過 壓縮程式設計bzImage,或者按要求在對應的子目錄下編譯成 子產品。