首先下載樹莓派linux內核源碼:
- 下載網址:https://github.com/raspberrypi/linux
- 在樹莓派使用指令:
uname -r
查看當前樹莓派的版本號,然后選擇對應的linux內核版本號進行下載。
- 將linux內核源碼從共享文件夾拷貝到SYSTEM文件夾:
cp linux-rpi-4.19.y.zip /home/fhn/SYSTEM/
然后使用指令對該壓縮文件進行解壓:unzip linux-rpi-4.19.y.zip
樹莓派等芯片帶操作系統的啟動過程:
之前學習的51和32(裸機)是直接使用C語言操控底層寄存器實現相關業務,這是業務流程型(就是設備有什么功能,要實現什么功能)裸機代碼,而linux的BootLoader是超級裸機(BootLoader的主要作用是引導操作系統啟動),BootLoader: 剛開始一階段學習就是讓CPU和內存、FLASH、串口、IIC、IIS、數據段打交道,就是驅動這些設備(主要用到匯編和C);二階段就是對于引導linux內核啟動(純C)。X86架構(比如:Intel),跑的是windows操作系統,上電啟動的時候: 首先是啟動BIOS然后是啟動windows內核,然后就是加載C、D等盤,最后是啟動應用程序。對于其他的嵌入式產品(比如:樹莓派、mini24440、mini6410、nanopi、海思、RK(瑞芯微)),這些產品上電后啟動過程是:首先啟動BootLoader(相當于windows的BIOS吧,但這里叫做BootLoader),然后引導linux內核,linux內核啟動完成后就是加載文件系統(和C、D盤不太一樣,在linux下是根據功能性來組織文件夾,帶訪問權限(這就是linux文件系統),就是根目錄下的那些東西,可以使用指令:cd /
進入),文件系統起來以后才能跑應用程序(比如:KTV點歌機界面等)。BootLoader、linux內核、文件系統構成操作系統,就像之前所說的在目的平臺還沒有建立的時候,需要通過交叉編譯讓這些東西可以使用。 安卓的啟動過程: 有很多嵌入式設備都開始運行安卓操作系統,首先硬件上電后,啟動fastboot(也叫BootLoader,意思和BootLoader一樣),然后引導linux內核,linux內核起來以后加載文件系統(因為底層還是linux),文件系統起來以后開始跑一個運行java代碼的虛擬機,虛擬機起來以后開始運行home應用程序(就相當于手機的界面),點擊home界面的某圖標就可以打開相應的app。
-
簡單介紹,根目錄下一些文件夾存放的東西,在嵌入式體統中,為了精簡系統。/bin,/dev,/etc,/lib,/proc,/var,/usr 對于根文件系統來說是必須具有的,其他目錄都是可選的。首先使用指令:
cd /
進入到根目錄,然后可以看到下面的一些文件夾。這篇博文更詳細 -
dev: 該目錄下存放的是設備文件,設備文件是Linux中特有的文件類型。在Linux系統下,以文件的方式訪問各種設備,即通過讀寫某個設備文件操作某個具體硬件。譬如通過"dev/ttySAC0"文件可以操作串口0。(在linux下一切皆文件,就像之前樹莓派的串口就是存放在dev下面)
-
lib: 該目錄下存放共享庫和可加載驅動程序模塊。共享庫用于啟動系統、支持可執行程序的運行。
-
etc: 該目錄下存放著各種配置文件,對于PC上的Linux系統,/etc目錄下的文件和目錄非常多,這些目錄文件是可選的,它們依賴于系統中所擁有的應用程序是否需要配置文件。在嵌入式系統中,這些內容可以大為精減。
-
root: 根用戶的目錄,與此對應,普通用戶的目錄是/home下的某個子目錄。
-
usr: 該目錄存放的是共享、只讀的程序和數據,/usr目錄下的內容可以在多個主機間共享。
-
var: 與/usr目錄相反,/var目錄中存放可變的數據,比如spool目錄(mail,news),log文件,臨時文件。
-
proc: 該是一個空目錄,常作為proc文件系統的掛載點,proc文件系統是個虛擬的文件系統,它沒有實際的存儲設備,里面的文件都是由內核臨時生成的,用來表示系統的運行狀態,也可以操作其中的文件控制系統。
-
mnt: 用于臨時掛載某個文件系統的掛接點,通常是空目錄,也可以在里面創建子目錄來臨時掛載光盤、硬盤,譬如/mnt/cdram /mnt/hda1 。
-
home: 用戶目錄,它是可選的,對于每個普通用戶,在/home目錄下都有一個以用戶名命名的子目錄,里面存放用戶相關的配置文件
-
opt: 存放的是和內核底層有些關系的東西
-
bin: 該目錄下存放所有用戶都可以使用的、基本的命令,譬如cd,ls,cp等。這些命令在掛載其它文件系統之前就可以使用,所以/bin目錄必須和根文件系統在同一個分區中,比如:
which ls
可以查找ls指令的路徑 -
sbin: 該目錄下存放的是基本的系統命令,它們用于啟動系統,修復系統等,與/bin目錄相似,在掛接其他文件系統之前就可以使用/sbin,所以/sbin目錄必須和根文件系統在同一個分區中
-
boot: 存放的是啟動時候的一些數據,包括啟動的時候加載的內容、一些命令和命令行的配置等等。
-
tmp: 用于存放臨時文件,通常是空目錄,一些需要生成臨時文件的程序用到的/tmp目錄下,所以/tmp目錄必須存在并可以訪問。
linux內核源碼樹:其實就是目錄的組織形式
- 找到之前下載的linux內核源碼
- 使用
tree
指令,在以后做第三方工具安裝的時候會去檢查包里面的內容是否完整,或者是有一個文件夾目錄下文件比較多的時候使用tree指令方便查看,然后如果出現識別不了表示沒有下載,然后使用指令sudo apt install tree
進行下載。
- linux內核大約有1.3w個c文件,1100w行代碼,是因為linux是一個開源的,支持多架構多平臺代碼,可移植性非常高。但是linux內核編譯出來一般就若干M,因為支持多平臺,多架構,所以編譯之前要配置,配置成適合目標平臺來用。代碼編譯出來是給一個平臺一個架構來用的,很多代碼是不參與編譯的。例如:ARM架構(海思、RK、樹莓派、nanoPi)、X86架構、powerpc、mips等等架構
Linux內核源代碼目錄樹結構:
-
arch:包含和硬件體系結構相關的代碼,每種平臺占一個相應的目錄。和32位PC相關的代碼存放在i386目錄下,其中比較重要的包括kernel(內核核心部分)、mm(內存管理)、math-emu(浮點單元仿真)、lib(硬件相關工具函數)、boot(引導程序)、pci(PCI總線)和power(CPU相關狀態)。進入arm這個文件夾,這個里面存放的是arm架構相關的設備。mach表示架構相關的,mach-exynos: 是三星公司的架構; mach-hisi : 是海思公司的架構,就單單arm架構下就有很多公司的架構。
-
block:部分塊設備(跟存儲設備相關的,像內存、flash等)驅動程序。
-
crypto:常用加密和散列算法(如AES、SHA等),還有一些壓縮和CRC校驗算法。
-
Documentation:關于內核各部分的通用解釋和注釋。
-
drivers:設備驅動程序,每個不同的驅動占用一個子目錄。
-
fs:各種支持的文件系統,如ext、fat、ntfs等。
-
include:頭文件。其中,和系統相關的頭文件被放置在linux子目錄下。
-
init:內核初始化代碼(注意不是系統引導代碼)。
-
ipc:進程間通信的代碼(之前學習的進程間通信:管道、消息隊列、共享內存,在調用的時候都會觸發一個異常,系統調用然后進入到內核態。那些API就是支配內核干活)。
-
kernel:內核的最核心部分,包括進程調度、定時器等,和平臺相關的一部分代碼放在arch/*/kernel目錄下,根目錄下的kernel一般都放一些通用的內核的最核心部分,包括進程調度、定時器的代碼。
-
lib:庫文件代碼。
-
mm:內存管理代碼,和平臺相關的一部分代碼放在arch/*/mm目錄下。
-
net:網絡相關代碼,實現了各種常見的網絡協議。
-
scripts:用于配置內核文件的腳本文件。
-
security:主要是一個SELinux的模塊。
-
sound:常用音頻設備的驅動程序等。
-
usr:實現了一個cpio(cpio就是復制入和復制出的意思。cpio可以向一個歸檔文件(或單個文件)復制文件、列表,還可以從中提取文件)。
-
在i386體系下,系統引導將從arch/i386/kernel/head.s開始執行,并進而轉移到init/main.c中的main()函數初始化內核。
配置Linux內核適合樹莓派相關操作:
以后可能涉及驅動代碼的編寫,驅動代碼的編譯需要一個提前編譯好的內核,編譯內核就需要對內核進行配置。配置的最終目標是生成.config
文件,該文件指導Makefile
去把有用的東西組織成內核。配置的方式有以下幾種方式:
- 第一種方式:廠家會配Linux內核源碼,比如說:買了樹莓派,廠家會提供Linux內核源碼,然后這樣就可以將廠家的
.config
文件里面的代碼拷貝到要生成的.config
文件即可。進入到Linux內核文件夾下,然后使用指令find . -name *defconfig
查找以defconfig結尾的文件。比如樹莓派的2和3就是使用的下圖的bcm2709_defconfig
,然后只需要將它拷貝到要生成的.config
文件。 - 第二種方式:使用
make menuconfig(menuconfig、defconfig都是用來配置內核的)
一項項配置,通常是基于廠家的config來配置。 - 第三種方式:完全自己來。
樹莓派內核編譯:
-
首先配置好ubuntu交叉編譯的環境,環境的搭建在另一篇文章有配置過程。
-
然后開始配置config,linux源碼中有很多工程:樹莓派1的工程是bcmrpi_defconfig;樹莓派2、3的工程是bcm2709_defconfig。配置過程如下:
-
使用源碼里面自帶的config
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make bcm2709_defconfig
此命令功能是獲取bcm2709_defconfig的配置到 .config里。這行命令的意思是:指定arch(架構)是arm架構的-------ARCH=arm ;指定編譯器是系統里面arm-linux-gnueabihf-這個編譯器,這就是之前配好的交叉編譯器--------CROSS_COMPILE=arm-linux-gnueabihf- ;指定樹莓派的kernel樹莓派里面特別指定的---------KERNEL=kernel7;用的config是bcm2709_defconfig所以是-------make bcm2709_defconfig,這就相當于直接拷貝廠家的config到.config。 -
如果使用指令:
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make bcm2709_defconfig
出現下圖報錯,則表示該錯誤的原因是缺少某些需要的庫,需要安裝一下需要的庫就行了,sudo apt-get install bison
,sudo apt-get install flex
,如果在安裝上面庫時提示找不到安裝庫,就需要更新一下source addr,sudo apt-get update
#更新;sudo apt-get upgrade
#升級,需要用update更新完才能upgrade;更詳細的,請$man apt-get
,記錄更新源的文件:/etc/apt/sources.list
,默認sources.list中的更新源為官方的,下載速度慢,從而需要尋找符合自己需要的更新源。
-
出現下圖表示復制成功,configuration written to .config
-
如果想要添加某些驅動進來,就要:
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make menuconfig
,如果沒什么改的就不用執行這一步。這時候它就會讀取現有.config,給menuconfig做一些基本的項的填充。出現下圖錯誤表示沒有ncurses 庫,要進行安裝:sudo apt-get install libncurses5-dev libncursesw5-dev
,sudo apt-get install libc6-i386 lib32stdc++6 lib32gcc1 lib32ncurses5
,編譯的時候還會用到一些其他的必要的庫需要進行安裝:sudo apt-get install bc
,sudo apt-get install zlib1g:i386
-
執行完執行menuconfig會出現以下Ncurses界面,一般就是在Device Drivers進行設備驅動的配置,用方向鍵操作,用enter表示確定選擇, Pressin < Y > includes表示要將前面帶< Y >的編譯進了內核,< M > modularizes features意思都是模塊化特性,表示驅動以模塊的方式生成驅動文件。< N >表示不需要的略過的就是下圖的< >。驅動兩種加載方式:① * 編譯進內核 zImage包含了驅動 ②M 驅動以模塊方式生成驅動文件(xxx.ko)系統啟動后,通過命令
inmosd xxx.ko
加載,下圖的< * >就是< Y >。在這個Ncurses界面可以使用空格切換< >中的選項。
-
我們可以直接用工程里的配置,但這樣的話可能會丟失原來使用的樹莓派的配置,這里提供一個方法可以獲取當前正在使用的樹莓派的config。獲取當前樹莓派的config,已經開機的樹莓派上會有這個節點:/proc/config.gz,從這個節點可以獲取本樹莓派的config。如果沒有這個節點的話則需要先加載模塊:
sudo modprobe configs
把 config.gz 內容復制到要編譯的電腦上:scp pi@[ip]:/proc/config.gz .
解壓,保存為.confg文件。zcat config.gz > .config注:必須在linux環境下解壓,在mac下會亂碼。把此config文件復制到linux源碼的根目錄,這種方法我沒嘗試過。
已經生成.config文件了,然后就可以該文件指導Makefile
去把有用的東西組織成內核:
-
使用指令:
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make -j4 zImage modules dtbs 2>&1 | tee build.log
,其中2>&1 | tee build.log
是錯誤處理可以不關心不要。make -j4 zImage modules dtbs
這段指令中的-j4
是指定電腦(虛擬機)用多少的資源來編譯,4表示4線程。zImage
表示要生成zImage(內核鏡像);modules
表示要生成驅動模塊;dtbs
表示要生成配置文件等等。我編譯的過程出現以下錯誤:原因是:都是沒有安裝libssl-dev~,libssl-dev包含libraries, header files and manpages,他是openssl的一部分,而openssl對ssl進行了實現。解決方案:使用sudo apt-get install libssl-dev
來安裝libssl-dev即可,這個編譯過程大概持續20分鐘。
-
在編譯過程中沒有報錯,并且在linux源碼樹目錄下生成vmlinx,vmlinux是沒有壓縮的linux,而真正生成的linux內核是在
arch/arm/boot
下面的zImage
-
編譯完成后就需要將編譯后linux內核放到樹莓派去運行,首先將zImage文件打包,直接用linux源碼包(要在linux源碼包下執行)里的工具:
./scripts/mkknlimg arch/arm/boot/zImage ./kernel_new.img
在本目錄生成一個kernel_new.img文件,這個文件就是要放到sd卡中的文件。scripts文件夾下面存放的是一些腳本文件,這里的mkknlimg會將zImage打包成kernel_new.img。注:網上很多地方說的用tools/mkimage/imagetool-uncompressd.py
的方法不行!!
-
dmesg
是查看底層的硬件信息。當有設備接入的時候,內核會打印設備驅動的一些東西。可使用該指令查看U盤是否接入到ubuntu,并且可以看到SD的兩個分區:sdb1(boot)和sdb2(根目錄相關文件)。
-
掛載樹莓派sd卡,并安裝編譯出的DIRECTLY 到sd卡: 把樹莓派的sd卡插入ubuntu系統電腦,樹莓派的sd卡有兩個分區:一個fat分區,是boot相關的內容,kernel的img文件就放在這個分區里;一個是ext4分區,也就是系統的根目錄分區。我們生成的文件涉及到這兩個分區的內容,一般插入ubuntu后會自動掛載,fat分區可以不用root權限操作,ext4分區需要root權限操作。兩個分區具體掛載在什么地方可以自己決定,以下用[fat]表示boot掛載的路徑,[ext4]表示ext4掛載的路徑。自己創建兩個文件夾:fat和ext4
-
掛載U盤:
sudo mount /dev/sdb1 fat
將內存卡中的sdb1掛載到fat文件夾夾,這樣fat里面就有sdb1(boot相關)的內容了,sudo mount /dev/sdb2 ext4
將內存卡中的sdb2(根目錄分區)掛載到fat文件夾夾,這樣fat里面就有sdb1的內容了
-
掛載分區后,安裝modules:
sudo ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make INSTALL_MOD_PATH=[ext4] modules_install
執行指令需要進入到源碼下的那個文件夾。操作ext4分區,需要root權限(因為是安裝到根目錄分區)。自己創建的文件夾名稱不同,就將對應的[ext4]換成自己掛載根目錄分區的文件夾路徑即可。在這過程中會生成好多xxx.ko文件(HDMI、usb、wifi、io等的驅動),如果少了這步操作那些設備可能不會正常工作。下圖表示安裝成功:
-
更新 kernel.img 文件: 在更新之前可以選擇先備份鏡像(注意鏡像的名字是kernel7.img),使用指令:
cp kernel7.img kernel7oled.img
,下面對于更新有兩種方式:①前面已經用mkknlimg 工具打包了kernel_new.img文件了,把它復制到boot分區并配置使用即可:cp kernel_new.img [fat]/
,編輯 [fat]/config.txt 文件,在最后加入一行:kernel=kernel_new.img(在配置文件里面指定內核鏡像是kernel_new.img)。②因為前面已經備份了kernel7.img所以可以直接將 kernel_new.img的內容復制到kernel7.img將原有的東西覆蓋掉:cp kernel_new.img /home/fhn/fat/kernel7.img
。拷貝完成后可以使用指令:du kernel7.img -h
查看文件大小,然后可以使用指令:md5sum kernel_new.img
查看原鏡像的MD5值(每個文件都有它唯一的編碼號就是md5的值),然后再查看拷貝后的鏡像的MD5值進行比較,若拷貝過程中文件沒有損壞,則md5的值相同,否則可能在拷貝過程中文件損壞。
-
復制其他相關文件(配置文件,是為了加載驅動和其他配置文件):
cp arch/arm/boot/dts/.*dtb* [fat]/
將[fat]換成自己內存卡boot分區掛載的對應的路徑,cp arch/arm/boot/dts/overlays/.*dtb* [fat]/overlays/
、cp arch/arm/boot/dts/overlays/README [fat]/overlays/
這兩個指令同上也需要將[fat]換成自己內存卡boot分區掛載的對應的路徑。 -
以上內容配置完成后,然后修改config.txt和cmdline.txt文件里面的內容,通過串口觀察系統的啟動過程。
-
更新完成后插回樹莓派即可開機,開機后可以用 uname -a 命令查看kernel信息已經改變。和從官網下載的內核版本相同。
-
樹莓派內核編譯部分參考博文