http://blog.csdn.net/dreambegin/article/details/6904822node
原來文章叫——編譯內核之初體驗。後來想了想,這篇文章讓我體驗了好多遍。不應叫這麼大氣的名字,仍是改了吧。中間記錄了不少在內核移植中可能遇到的問題。linux
仍是把名字改成:愚人的內核移植札記(超曲折版)git
千呼萬喚始出來,讓咱們開始移植內核吧!web
環境:Ubuntu + EABI-4.3.3(就是前邊配置好的環境)shell
內核版本:linux-2.6.34.10 /*這個版本有什麼特殊意義?木有!恰好電腦上有它就拿來用了,還不知道里邊有沒有bug。*/ubuntu
何爲內核移植?windows
Linux內核移植就是對linux內核源碼進行修改,使其能夠移植到對應的平臺上,並能夠正常運行。實際上就是作一些必要的修改,並編譯出一個能在開發板上跑起來的.bin的二進制文件。工具
開始:測試
一、 將源碼放置到虛擬機中,你能夠選擇FTP上傳、共享文件目錄、經過VMwaretools的直接複製粘貼、甚至其餘任何你想使用的方式(這些方法都在前邊文章中有所介紹)ui
二、 解壓源碼,獲得源碼樹(注意,這裏的源碼樹千萬不要和前邊將linux內核編譯模塊時的源碼樹搞混了,你能夠認爲這個源碼樹就是一個開源裸板的應用程序的源代碼目錄)
/*個人工做目錄依然是 「/home/jun/arm」 */
$ tar –xjvf linux-2.6.34.10.tar.bz2 //由於在用戶目錄下,因此不用sudo
三、 在獲得源碼目錄後,就能夠對源碼進行相應的修改,使之知足運行條件。
修改ing :
一、 第一個要修改的是Makefile。
源碼根目錄下的Makefile是整個內核工程的總Makefile文件,向下聯繫着每一個子目錄中的Makefile。子目錄中的Makefile又控制着所在目錄的編譯,並聯繫着它的下級目錄,就這樣循環往復。
Makefile文件控制着linux下工程開發中,整個項目的編譯連接順序和選項以及規則。也是一個自動編譯工具,使得部分文件改動時只用對改動文件的進行編譯,而不用再次編譯鏈接整個工程。
默認狀況下,源碼根目錄下的Makefile中定義着編譯成的目標平臺爲x86,編譯器選項爲Gcc;而咱們的需求是ARM平臺和arm-linux-gcc編譯器。知道了咱們想要什麼,就知道該改些什麼了。
/*進入源碼目錄:*/
$ cd linux-2.6.34.10/
$ vi Makefile
在vi的底行名利模式中輸入:set number 能夠顯示出行號來,而後,咱們找到第18九、190行:
ARCH ?= $(SUBARCH)
CROSS_COMPILE ?=
咱們把它改成:
ARCH ?=arm
CROSS_COMPILE ?=arm-linux-
一、 第二個要修改的是.config文件
源碼根目錄下的.config是整個內核工程的總配置單,向下聯繫着每一個子目錄的Kconfig。子目錄中的Kconfig又聯繫着它的下級目錄,就這樣循環往復。
剛解壓出來的源碼目錄並無.config文件。咱們經過下面命令從內核已經包含的一些平臺的配置文件中copy來一份。
$ cparch/arm/configs/s3c2410_defconfig .config
$ makemenuconfig /*咱們來預覽一下拷貝過來的配置單*/
Oops!報錯了,哈哈,我喜歡,報錯預示着能夠學到新的知識。報錯內容:
make: ***/home/jun/arm/linux-2.6.34.10/arch/arm: Is a directory. Stop.
哦,疏忽,這個錯誤比較常見,就是在上邊配置Makefile時:
ARCH ?= $(SUBARCH)
這一行後邊不當心加了有了空格了,改過來,try again。(在vi的底行模式:num 189,這樣能夠直接跳至189行)
Ok,預覽成功。在這樣的menu菜單裏,就能夠比較方便的配置內核了。(默認狀況下Ubuntu下是不能夠直接menuconfig的,不過咱們在前面構建內核源碼樹的文章中已經安裝了相應的支持包,這裏主要依賴的是ncurses包,參照Ubuntu+下構建內核源碼樹_圖文教程剛開始安裝的幾個工具。)
由於咱們用的是EABI編譯的,因此要在內核中加上EABI支持:
在上述的配置菜單中配置:
KernelFeatures --->
Memory split (3G/1G user/kernel split) ---> | |
|| Preemption Model (No ForcedPreemption (Server)) ---> | |
|| [*] Use the ARM EABI to compile thekernel | |
|| [*] Allow old ABI binaries to run with this kernel (EXPERIMENTAL) (| |
|| [ ] High Memory Support(EXPERIMENTAL) | |
|| Memory model (Flat Memory) ---> | |
|| [ ] Enable KSM for page merging | |
(4096) Low address space to protect from userallocation | |
[ ] Use kernelmem{cpy,set}() for {copy_to,clear}_user() (EXPERIMENTA|
一、 第三要改的是晶振。
默認的晶振是16934400,而個人板子TQ2440的晶振是12000000,據我所知當前主流的2440板子的外部時鐘晶振都是12000000吧,TQ2440、mini2440、精智2440的板子都是。
要改的在文件arch/arm/mach-s3c2440/mach-smdk2440.c 中的第163行。
OK,不出意外的話,如今的內核已經能夠跑起來了,咱們make一下,下載到板子裏邊試試:
$make zImage -j4
一番等待以後,個人編譯經過了,在arch/arm/boot/ 下會生成適用於arm平臺的壓縮格式內核zImage。
我經過cp命令把它拷貝到虛擬機和我本機的共享目錄中,這樣,它已經在個人windows下了,大小1.92M,多麼驚人的尺寸,這就見證了linux強悍的可移植性。
此時已經能夠在開發板裏跑起來了,可是能跑並不表明會跑,這樣的結果是會摔跟頭。那麼咱們就邊摔邊跑吧,不經歷風雨,怎麼見彩虹?!
四、第四要改的是機器ID(板子的ID)。
將上邊編譯出來的zImage用bootloader(TQ2440自帶的出場Uboot),經過dnw燒到nand flash裏,而後boot the system:(讓咱們看看它到底摔了什麼跟頭?)
出現:
這種問題一般是系統不認識uboot傳過來的機器ID,要修改內核中的機器ID與uboot的一致,由於開啓的過程是,先加載bootloader-----》bootloader準備好硬件信息(以及OS的運行環境)-----》OS檢測傳遞過來的硬件信息(cpu信息、開發板信息等)本身是否支持……支持就繼續下一步工做,不支持就直接停掉,不浪費時間了。而TQ2440出場的uboot很是蛋疼的設置爲了168,因此咱們進到內核源碼,把對應文件修改一下,在內核源碼的「arch/arm/tools/mach-types」文件中,大約379行,原來是362,咱們把它改的和uboot中默認的一致就OK了:
而後從新編譯,並下載到開發板,結果:
這證實系統已經能夠跑了,只是尚未作好分區與文件系統。
五、第五要改的是Nand分區。(緣由是不一樣的鏡像,如內核鏡像、文件系統鏡像…要燒寫到不一樣的分區中,由於雖然咱們不知道分區內部究竟是什麼,可是咱們知道分區的起始地址和大小,這樣只要按照約定把相應的鏡像放到相應的分區(對應的地址),這樣咱們就能夠正常啓動系統)。
既然是分區的事,那下一步就來修改分區。這個分區通常稱做MTD分區(MemoryTechnology Device),是linux抽象出來的存儲設備層。它提供了底層存儲設備的抽象,封裝了底層硬件的讀、寫、擦除等操做。直入主題,咱們要改的就是內核源碼「arch/arm/plat-s3c24xx/commonsmdk.c」文件的smdk_default_nand_part[]結構體,這裏,由於本人用的是TQ2440出場Uboot(固然這是linux移植開篇,沒有用本身移植的Uboot,無論用哪一個,Uboot和linux都是要配合好才能夠啓動的),而TQ2440出廠Uboot的默認啓動參數是mtdblock1,也就是第二個分區,它的nand flash分區設計是,第一個分區存放Nand下的Uboot鏡像,第二個是kernel鏡像,第三個是文件系統。而分區的約定又是和Uboot的下載命令中設置的默認下載地址是對應的,因此,這裏咱們仍是得按着這個約定來。
首先先改smdk_nand_info結構體(大約在commonsmdk.c 的141行):
修改後如圖:(原來)(後邊我作測試的時候把名字都改成了「jun_uboot」,"jun_kernel"和"jun_yaffs2")
下邊是咱們要定義的smdk_default_nand_part[]結構體:
static structmtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "nand_uboot",
.offset = 0x00000000,
.size = 0x00040000,
},
[1] = {
.name = "nand_kernel",
.offset = 0x00200000,
.size = 0x00200000,
},
[2] = {
.name = "nand_yaffs2",
.offset = 0x00400000,
.size = 0x0FB80000,
}
};
由於這裏,nandflash是256M的,給Uboot分了0x00040000(256k),kernel分了0x00200000(2M),剩餘的0x0FB80000(251M)給了yaffs分區。
編輯好之後,保存,在編譯運行一下,看看效果。。。。
在源碼目錄下再make一下獲得zImage,再次測試,結果爲:
看吧,雖然分區調整成三個了,他仍是不認識咱們的分區依然是mtdblock0( driver? ),
雖然識別了分區,可是識別不出來分區的名字。爲何呢?(由於咱們尚未添加文件系統的支持) 不過不能否認的是分區已經被咱們調整成3個,且kernel已經知道了如今是三個,以及他們的大小。因此全宇宙我沒法阻止咱們繼續下去。
主要要看內核此時崩潰的緣由(kernel是經過panic反饋給咱們的):
No filesystem could mount root, tried: ext3 ext2 cramfs vfat msdos iso9660 romfs
Kernel panic - not syncing: VFS: Unable tomount root fs on unknown-block(31,2)。。。
這個panic的緣由一目瞭然,由於咱們尚未添加文件系統的支持。兵來將擋,水來土掩。咱們這就添加內核對yaffs2文件系統的支持。
這裏,發現好多資料文檔裏給的下載源碼地址http://www.aleph1.co.uk/cgi-bin/viewcvs.cgi/這個網址已經失效了,下載yaffs源碼,找到了http://www.yaffs.net官網。看了一番說明……額時代變了,yaffs和yaffs2早已分開維護了,小看了一下,應該是這樣下載的:http://www.aleph1.co.uk/gitweb?p=yaffs2.git;a=tree ,他這裏是用git做的版本控制,打開網頁直接點擊「snapshot」會下載到一個源碼快照,固然,這裏下到的是yaffs2。你手頭有原來的老版本的包的話,也能夠用老的。這裏獲得的是「yaffs2-HEAD-d43e901.tar.gz」,和老版本(仍是用的cvs版本控制時的)源碼包比較,少了yaffs目錄。咱們把它解壓到工程目錄中(你作開發的目錄,不用放到kernel目錄裏)。而後執行下邊一系列命令,將yaffs2補丁打到咱們的內核中:
$ tar -xzvf yaffs2-HEAD-d43e901.tar.gz # 解壓到當前目錄。
$ cd yaffs2-HEAD-d43e901/ # 進入該目錄
$ ./patch-ker.sh c s ../linux-2.6.34.10/ # 這裏是打補丁的命令,要加上c和s選項
打補丁和打後效果如圖,證實Makefile和內核配置單都已經修改過了。
而後咱們makemenuconfig 在配置中加入yaffs選項,給內核添加yaffs文件系統支持。
在配置單的這裏添加:
Filesystems --->
[*]Miscellaneous filesystems --->
<*> yaffs2 file system support(new)
多人性化,他會有個(new),告訴你這就是你才添加的。
另外:
在256MB 以及更大容量的Nand Flash 須要選擇上硬件ECC 校驗的選項,不然會出現yaffs2 文件系統不能掛載而致使系統起不來的狀況。咱們照辦就是了:
Device Drivers --->
<*> MemoryTechnology Device (MTD) support --->
<*> NAND Device Support --->
<*> NAND Flash support for S3C2410/S3C2440 SoC
[*] S3C2410 NAND Hardware ECC
好的,咱們再次make 一下內核進行測試。。。。。$ make zImage -j5 ……..
編譯報錯了:
fs/yaffs2/yaffs_vfs.c: In function'yaffs_setattr':
fs/yaffs2/yaffs_vfs.c:522: error: implicitdeclaration of function 'setattr_copy'
fs/yaffs2/yaffs_vfs.c:525: error: implicitdeclaration of function 'truncate_setsize'
fs/yaffs2/yaffs_vfs.c: At top level:
fs/yaffs2/yaffs_vfs.c:871: warning:initialization from incompatible pointer type
fs/yaffs2/yaffs_vfs.c:902: warning:initialization from incompatible pointer type
fs/yaffs2/yaffs_vfs.c: In function'yaffs_evict_inode':
fs/yaffs2/yaffs_vfs.c:1062: error: implicitdeclaration of function 'end_writeback'
fs/yaffs2/yaffs_vfs.c: At top level:
fs/yaffs2/yaffs_vfs.c:1952: error: unknownfield 'evict_inode' specified in initializer
fs/yaffs2/yaffs_vfs.c:1952: warning: initializationfrom incompatible pointer type
make[2]: *** [fs/yaffs2/yaffs_vfs.o] Error1
應該是版本問題,那就不用新版本的,咱們退而求其次,還用老的吧,這裏用的是嵌入式Linux開發徹底手冊裏附帶的版本,【下載地址】。
先把已經打的補丁去除了,再按照一樣的方法打該版本的補丁。這裏的補丁不是經過patch打的,而是用的shell,那就笨着來吧,手動去除,手動去除的話你得明白它到底對你的內核作了哪些手腳:執行patch-ker.sh,主要作了一下工做:
<1>修改內核文件/fs/Kconfig,增長下面兩行(在177行附近):
if MISC_FILESYSTEMS
source "fs/adfs/Kconfig"
source "fs/affs/Kconfig"
source "fs/ecryptfs/Kconfig"
source "fs/hfs/Kconfig"
source "fs/hfsplus/Kconfig"
source "fs/befs/Kconfig"
source "fs/bfs/Kconfig"
source "fs/efs/Kconfig"
source"fs/yaffs2/Kconfig"
source "fs/jffs2/Kconfig"
# UBIFS File system configuration
<2>修改內核文件/fs/Makefile,增長下面兩行(在129行附近):
obj-$(CONFIG_GFS2_FS) += gfs2/
obj-$(CONFIG_EXOFS_FS) += exofs/
obj-$(CONFIG_YAFFS_FS) += yaffs2/
<3>在內核文件的fs目錄下建立yaffs2子目錄,而後複製以下文件:
將yaffs2源碼目錄下的Makefile.kernel文件複製爲內核fs/yaffs2/Makefile文件。
將yaffs2源碼目錄下的Kconfig文件複製爲內核fs/yaffs2/目錄下。
將yaffs2源碼目錄下的*.c、*.h文件(不包括子目錄下的文件)複製爲內核fs/yaffs2/目錄下。
笨人有笨法,咱們手動的把它修改回去,而後記着編譯一下,確保沒有其餘錯誤,而後再繼續。。。。。。。。。。。。。。測試中。。。。。。。。。。。。。。。。。。。
OK,編譯正常,證實去除沒有干擾到其餘部分,能夠繼續了。
一樣,以相同的方式將上邊給出版本的yaffs安裝到內核,再也不嗷述。(不過這裏的安裝後確實和上邊的有些出入,初步估計是上邊的源碼下載有誤,非徹底版。)
就緒後,咱們從新編譯一下。。。。。。。。。。。。。。。。編譯中。。。。。。。。。。。。。。。。。。。
我勒個去,報瞭如此多的錯誤。。。。。都是yaffs的。。。。。。
fs/yaffs2/yaffs_fs.c:212: error: unknownfield 'prepare_write' specified in initializer
fs/yaffs2/yaffs_fs.c:212: warning:initialization from incompatible pointer type
fs/yaffs2/yaffs_fs.c:213: error: unknownfield 'commit_write' specified in initializer
fs/yaffs2/yaffs_fs.c:213: warning: initializationfrom incompatible pointer type
fs/yaffs2/yaffs_fs.c:287: error: unknownfield 'read_inode' specified in initializer
fs/yaffs2/yaffs_fs.c:287: warning:initialization from incompatible pointer type
fs/yaffs2/yaffs_fs.c:288: error: unknownfield 'put_inode' specified in initializer
fs/yaffs2/yaffs_fs.c: In function'yaffs_get_inode':
fs/yaffs2/yaffs_fs.c:847: error: implicitdeclaration of function 'iget'
fs/yaffs2/yaffs_fs.c:847: warning:assignment makes pointer from integer without a cast
fs/yaffs2/yaffs_fs.c: In function'yaffs_mknod':
fs/yaffs2/yaffs_fs.c:1021: error: 'structtask_struct' has no member named 'fsuid'
fs/yaffs2/yaffs_fs.c:1022: error: 'structtask_struct' has no member named 'fsgid'
fs/yaffs2/yaffs_fs.c: In function 'yaffs_symlink':
fs/yaffs2/yaffs_fs.c:1201: error: 'structtask_struct' has no member named 'fsuid'
fs/yaffs2/yaffs_fs.c:1202: error: 'structtask_struct' has no member named 'fsgid'
fs/yaffs2/yaffs_fs.c: In function'yaffs_internal_read_super':
fs/yaffs2/yaffs_fs.c:1676: warning: format'%d' expects type 'int', but argument 2 has type 'uint64_t'
fs/yaffs2/yaffs_fs.c: In function'init_yaffs_fs':
從報錯的內容來看,應該是版本問題,這個內核是比較新的,而yaffs的版本比較老。
咱們從新把內核還原回去(固然仍是手動的完成那三步了)。真蛋疼。。。。
(如今官網上不用cvs作版本控制了,用的git,ubuntu下安裝git工具,能夠直接clone到最新的yaffs2,用$ sudo apt-get install git-core安裝,而後用 $ git clone git://www.aleph1.co.uk/yaffs2 命令會直接在當前命令下clone一份,並在當前目錄下建立yaffs2目錄,是當前目錄下,因此最好不要在內核目錄下執行該命令)
實際測試是用git在官網上clone下來的最新版本,也報錯了。。。。。。。
fs/yaffs2/yaffs_vfs.c: In function'yaffs_setattr':
fs/yaffs2/yaffs_vfs.c:522: error: implicitdeclaration of function 'setattr_copy'
fs/yaffs2/yaffs_vfs.c:525: error: implicitdeclaration of function 'truncate_setsize'
fs/yaffs2/yaffs_vfs.c: At top level:
fs/yaffs2/yaffs_vfs.c:871: warning:initialization from incompatible pointer type
fs/yaffs2/yaffs_vfs.c:902: warning:initialization from incompatible pointer type
fs/yaffs2/yaffs_vfs.c: In function'yaffs_evict_inode':
fs/yaffs2/yaffs_vfs.c:1062: error: implicitdeclaration of function 'end_writeback'
fs/yaffs2/yaffs_vfs.c: At top level:
fs/yaffs2/yaffs_vfs.c:1952: error: unknownfield 'evict_inode' specified in initializer
fs/yaffs2/yaffs_vfs.c:1952: warning:initialization from incompatible pointer type
make[2]: *** [fs/yaffs2/yaffs_vfs.o] Error1
make[1]: *** [fs/yaffs2] Error 2
make[1]: *** Waiting for unfinishedjobs....
想了想,內核版本是比較新的,yaffs2版本也是比較新的……應該沒有太大落差,是否是嘗試本身用新內核往板子上作移植的上輩子真的是折翼的天使。記得上次用的是2.6.30的內核用的老版本yaffs2一下就過了。。。。。。。。
待我用2.6.30的內核再從新作一遍上面所有的工做,來測試一下這個新版本的yaffs。。。(真的很悲催…………)
………………………工做中,等待………………………
對於2.6.30的內核添加yaffs2最新版也會報出和2.6.34一樣的錯誤,因而我猜想'setattr_copy' 、'truncate_setsize'……等這些應該是新內核(3.0)的新特性,不過該如何選擇適合本身內核的yaffs2版本呢……確實使人糾結。
而後對2.6.30添加yaffs2老版本進行測試(記着去除當前版本先)……
至此,嵌入式linux開發徹底手冊裏的版本也會報錯,我不淡定了。我決定,必須把這個問題解決,因此我去追溯一個和linux2.6.30 時間比較溫和的版本,在這裏,我決定選擇2009年聖誕節的版本 http://www.aleph1.co.uk/gitweb?p=yaffs2.git;a=shortlog;pg=4 ,2009-12-25,希望聖誕老人給我面子,讓它過了吧……(我不淡定了,編譯時我都開始-j6,開六個線程了,其實時間都是在等待中給浪費掉了……)
………………………編譯中,等待………………………
OK,聖誕節仍是神聖的,它編譯經過了……如今測試經過的版本是:2.6.30.4的kernel + 2009-12-25的yaffs2 。你是否是也感受很糾結。咱們燒乳板子測試下,剛剛編譯出來的zImage。
運行後效果:
好的,到這裏,內核的移植已經暫時告一段落,我該把文章帖出來了……事實證實,雖然笨,可是花些時間仍是能夠作到的……
(有時候版本問題仍是很使人頭疼的!!!!)
下一步是進行跟文件系統的構建…… (待續……)