標籤: linuxlinux內核flash嵌入式buffer數據結構node
2011-10-21 01:19 4976人閱讀 評論(0) 收藏 舉報linux
分類:編程
編程技巧(9) bootstrap
摘要緩存
咱們在這裏討論的是對嵌入式linux系統的啓動過程的輸出信息的註釋,經過咱們的討論,你們會對嵌入式linux啓動過程當中出現的、之前感受熟悉的、但卻又似是而非的東西有一個確切的瞭解,而且能瞭解到這些輸出信息的前因後果。網絡
嵌入式linux的啓動信息是一個很值得咱們去好好研究的東西,它能將一幅縮影圖呈如今咱們面前,來指導咱們更加深刻地理解linux內核。數據結構
關鍵字:linux,嵌入式,啓動,bootloaderapp
正文dom
做爲一名嵌入系統開發者,你必定遇到過下面的情景:socket
在某論壇上看到一篇帖子,上面貼着嵌入式linux開發板啓動時的有關信息,而後你們在帖子裏討論着這個啓動過程當中出現的問題,隨機舉例以下:
Linux version 2.4.20-uc0 (root@Local) (gcc version 2.95.3 |
上面的這些輸出信息,也可能包括你本身正在作的嵌入式linux開發板的輸出信息,其中的每一行,每個字的含義,你是否深究過,或者說大部分的含義你能確切地知道的?本人想在這裏結合本人在實踐中一些體會來和廣大嵌入式linux的開發者一塊兒讀懂這些信息。
咱們在這裏將以一個真實的嵌入式linux系統的啓動過程爲例,來分析這些輸出信息。啓動信息的原始內容將用標記標出,以區別與註釋。
嵌入式linux的啓動主要分爲兩個階段:
① 第一部分bootloader啓動階段
② 第二部分linux 內核初始化和啓動階段
第一節:start_kernel
第二節:用戶模式( user_mode )開始,start_kernel結束
第三節:加載linux內核完畢,轉入cpu_idle進程
第一部分 : bootloader啓動
Boot loader v0.12
NOTE: this boot loader is designed to boot kernels made with the
2.4.xx releases
bootloader for XV
Built at Nov 20 2005 10:12:35
Bootloader頭信息,版本,編譯時間等,這個因不一樣的bootloader的設計而有所不一樣,由此你能看出bootloader的版本信息,有不少使用的是通用的bootloader,如u-boot,redboot等。
Loaded to 0x90060000 |
將bootloader加載到內存ram中的0x90060000處,即將bootloader加載到內存的高端地址處。
Linux內核將被bootloader加載到0x90090000處。
Found boot configuration |
查找到了啓動boot的配置信息
Booted from parallel flash
從flash中啓動代碼,此處的flash爲並行閃存。Flash的分類列舉以下:
閃存分三類:並行,串行,不可擦除。
①並行Parallel flash
NOR Flash,Intel於1988年發明.隨機讀取的速度比較快,隨機按字節寫,每次能夠傳輸8Bit。通常適合應用於數據/程序的存貯應用中.NOR還能夠片內執行(execute-in-place)XIP.寫入和擦除速度很低。
NAND Flash,1989年,東芝公司發明.是以塊和頁爲單位來讀寫的,不能隨機訪問某個指定的點.於是相對來講讀取速度較慢,而擦除和寫入的速度則比較快,每次能夠傳輸16Bit,通常適用在大容量的多媒體應用中,容量大。如:CF,SM.
②串行Serial Flash 是以字節進行傳輸的,每次能夠傳輸1-2Bit.如:MMC,SD,MS卡.串行閃存器件體積小,引腳也少,成本相對也更低廉。
③不可擦除Mask Rom Flash的特色是一次性錄入數據,具備不可更改性,常常運用於遊戲和需版權保護文件等的錄入。其顯著特色是成本低。
注意:任何flash器件的寫入操做只能在空或已擦除的單元內進行,因此大多數狀況下,在進行寫入操做以前必須先執行擦除。NAND器件執行擦除操做是十分簡單的,而NOR則要求在進行擦除前先要將目標塊內全部的位都寫爲0。
從上面的信息,咱們能夠對flash類型特色有個比較明確的瞭解。
CPU clock rate: 200 MHz |
開發板上所使用的CPU的主頻爲200MHZ.
DRAM size is 128MB (128MB/0MB) |
動態內存ram大小爲128M。這裏咱們列舉一下內存的類型及工做原理。
根據內存的工做原理能夠劃分出兩種內存:DRAM和SRAM
①DRAM表示動態隨機存取存儲器。這是一種以電荷形式進行存儲的半導體存儲器。DRAM中的每一個存儲單元由一個晶體管和一個電容器組成。數據存儲在電容器中。電容器會因爲漏電而致使電荷丟失,於是DRAM器件是不穩定的。爲了將數據保存在存儲器中,DRAM器件必須有規律地進行刷新。
②SRAM是靜態的,所以只要供電它就會保持一個值。通常而言,SRAM 比DRAM要快,這是由於SRAM沒有刷新週期。每一個SRAM存儲單元由6個晶體管組成,而DRAM存儲單元由一個晶體管和一個電容器組成。相比而言,DRAM比SRAM每一個存儲單元的成本要高。照此推理,能夠判定在給定的固定區域內DRAM的密度比SRAM 的密度要大。
SRAM經常用於高速緩衝存儲器,由於它有更高的速率;而DRAM經常用於PC中的主存儲器,由於其擁有更高的密度。
在嵌入式系統中使用DRAM內存的設計比較普遍。
地址輔助說明:
先說明一下內存地址數字狀況,主要是爲了方便記憶。
能夠訪問的內存爲4G。
0x40000000是1GB處;0x00040000是256K處,0x00020000是128K處,0x90000000是2GB多的地方。
1M->0x00100000,
2M->0x00200000,
8M->0x00800000
16M->0x01000000,
32M->0x02000000
256M->0x10000000
64K->0x00010000
4K->0x00001000
這個是個快速記憶的方法,你能夠根據地址中1的位置和其後0的個數來快速知道換算後的地址是在多少兆的地方。好比,1的後面5個0,表明1M的大小,6個0,表明16M,以此類推。
ROMFS found at 0x46040000, Volume name = rom 43f291aa
romfs,只讀文件系統所在的地址爲:0x46040000 (flash映射後的第3分區)。
卷名爲rom。
romfs和rootfs概念上有所區別。
flash在內存中的的起始地址爲0x46000000,而ROMFS在flash分區上的起始位置爲0x00040000,因此ROMFS在內存地址中的位置就爲0x46040000。這個細節的部分能夠參考flash分區時的地方,Creating 3 MTD partitions。
romfs中包括kernel和app應用,不包括bootloader和firmware信息頭。romfs只讀文件系統裏的內容有不少種分類方法,咱們能夠將kernel和app同時放裏面,做爲根文件系統下的一個文件,也能夠在flash上另外劃分區域來分別存放。
VFS虛擬文件系統交換器
在linux系統中,目前已經開發出多種文件系統,那麼如何讓這些文件系統能共存在一個系統中呢,從linux 2.0開始,引入了虛擬文件系統管理器 VFS的概念。
Linux 下的文件系統主要可分爲三大塊:
① 一是上層的文件系統的系統調用,
② 二是虛擬文件系統交換器 VFS(Virtual Filesystem Switch),
③ 三是掛載到 VFS 中的各實際文件系統,例如 ext2,jffs 等。
VFS的確切叫法是Virtual Filesystem Switch虛擬文件系統交換器,這裏的VFS中的「S」是指的switch,這個須要強調一下的,它很容易被混淆成「system」,若是理解成「system」將是不正確的,請多加註意。
VFS是具體文件系統filesystem的一個管理器。
VFS是Linux內核中的一個軟件層,一種軟件機制,它也提供了內核中的一個抽象功能,容許不一樣的文件系統共存,能夠稱它爲 Linux 的文件系統管理者,與它相關的數據結構只存在於物理內存當中。因此在每次系統初始化期間,Linux 都首先要在內存當中構造一棵 VFS 的目錄樹。VFS 中的各目錄其主要用途是用來提供實際文件系統的掛載點。而rootfs將是這個目錄樹的根結點的(root),即 "/"目錄,VFS的結構就是從這個rootfs開始的。有了VFS,那麼對文件的操做將使用統一的接口,未來經過文件系統調用對 VFS 發起的文件操做等指令將被 rootfs 文件系統中相應的函數接口所接管。
注意:rootfs並非一個具體的文件系統類型,如jffs。它只是一個理論上的概念。在具體的嵌入系統實例中,能夠將某種具體的文件系統設置爲根文件系統rootfs,如咱們能夠設置romfs爲根文件系統,也能夠設置jffs爲根文件系統。
這裏的ROMFS只讀文件系統只是一種具體的文件系統類型,也是在嵌入系統中常用到的類型。
看完了上面的內容,之後你對出現的相似「kernel Panic:VFS:Unable to mount root fs on 0:00」的含義應該已經瞭解了。其中「VFS:」就是虛擬文件系統管理器操做時的輸出信息了。
File linux.bin.gz found |
linux kernel內核文件名,它是在只讀文件系統romfs上的一個組成部分。
Unzipping image from 0x4639DE60 to 0x90090000, size = 1316021 |
將romfs中的linux kernel解壓縮到0x90090000,以後會從這個內存地址啓動內核。romfs爲壓縮格式文件,使用壓縮的只讀文件系統,是爲了保持製做出來的整個系統所佔用的flash空間減少。這個內核的大小爲1.3M左右,這也是目前大多數嵌入系統所使用的方法。
Inptr = 0x00000014(20) Inflating.... |
釋放,解壓中。。。(變大,充氣, 膨脹)
Outcnt = 0x0030e7c8(3205064) Final Inptr = 0x001414ad(1316013) Original CRC = 0xcbd73adb Computed CRC = 0xcbd73adb |
作釋放後的CRC檢查
Boot kernel at 0x90090000 with ROMFS at 0x46040000 |
kernel已經被從romfs中釋放到內存地址0x90090000處,能夠跳轉到此處啓動kernel了,這裏是指定的kernel的起始地址
Press 'enter' to boot |
系統等待啓動,後面將看到linux kernel的啓動過程了。
第二部分 : linux內核初始化以及啓動
第一節:start_kernel
Linux的源代碼能夠從www.kernel.org獲得,或者你能夠查看linux代碼交叉引用網站:http://lxr.linux.no/ 進行在線的代碼查看,這是一個很好的工具網站。
在start_kernel中將調用到大量的init函數,來完成內核的各類初始化。如:
page_address_init();
sched_init();
page_alloc_init();
init_IRQ();
softirq_init();
console_init();
calibrate_delay();
vfs_caches_init(num_physpages);
rest_init();
具體內容能夠參考[http://lxr.linux.no/source/init/main.c]
Linux version 2.4.22-uc0 (root@local) (gcc version 2.95.3 20010315 (release)) #33 .?1.. 20 12:09:106 |
上面的代碼輸出信息,是跟蹤linux代碼分析後獲得的,進入init目錄下的main.c的start_kernel啓動函數.
嵌入式linux使用的是linux內核版本爲2.4.22
linux source code代碼中start_kernel中輸出的linux_banner信息。這個信息是每一個linux kernel都會打印一下的信息,若是你沒有把這句去掉的話。
Found bootloader memory map at 0x10000fc0.
bootloader通過內存映射後的地址爲:0x10000fc0, 按上面的地址換算方法,1後面有7個0,那麼虛擬地址256M左右處。
Processor: ARM pt110 revision 0 |
pT110是ARM微處理器arm核的一種,另外一種爲pT100。此處爲顯示ARM的類型。
On node 0 totalpages: 20480 zone(0): 20480 pages. zone(0): Set minimum memory threshold to 12288KB Warning: wrong zone alignment (0x90080000, 0x0000000c, 0x00001000) zone(1): 0 pages. zone(2): 0 pages. |
預留內存大小,在節點0上總共20頁, zone(0) 設置最小內存爲12MB, zone(1)和zone(2)爲0頁。警告:對齊不正確
Kernel command line: root=/dev/mtdblock3 |
Kernel 啓動命令設爲:/dev/mtdblock3(在後面的說明中會看到mtdblock3是指的flash上的romfs分區。),用來指定根文件系統所在的位置,kernel會將塊設備mtdblock3看成文件系統來處理。
也就是說,內核會根據上面的kernel命令行,知道只讀文件系統romfs將是根文件系統rootfs。
start_kernel(void)中輸出的上面的這句信息。
這行命令是在linux內核啓動過程當中都會輸出的一句。
Console: colour dummy device 80x30 |
代碼中console_init()的輸出信息, 顯示控制檯屬性:通常使用VGA text console,標準是80 X 25行列的文本控制檯,這裏是對屬性進行了設置。
serial_xx: setup_console @ 115 |
串口設置值爲115200,此爲波特率輸出信息。對串口設置的信息作一個打印的動做,在調試時會很是有用。
Calibrating delay loop... 82.94 BogoMIPS |
Calibrate:校準, 進入時延校準循環。檢查CPU的MIPS(每秒百萬條指令),Bogo是Bogus(僞)的意思。這裏是對CPU進行一個實時測試,來獲得一個大致的MIPS數值
Bogomips,是由linus Torvalds寫的, 是Linux操做系統中衡量計算機處理器運行速度的一種尺度。提供這種度量的程序被稱爲BogoMips,當啓動計算機時,BogoMips能顯示系統選項是否處於最佳性能。
linux內核中有一個函數calibrate_delay(),它能夠計算出cpu在一秒鐘內執行了多少次一個極短的循環,計算出來的值通過處理後獲得BogoMIPS值
你能夠將計算機的bogomips與計算機處理器的bogomips進行比較。Torvalds稱這個程序爲BogoMips來暗示兩臺計算機間的性能度量是錯誤的,由於並不是全部起做用因素都能被顯示出來或被承認。儘管計算機基準中常常用到MIPS,但環境的變化容易致使度量的錯誤。Bogomips能測出一秒鐘內某程序運行了多少次。
察看/proc/cpuinfo文件中的最後一行也能獲得這個數值。
上面這個輸出,在全部的linux系統啓動中都會打印出來。
進入內存初始化
mem_init(void), [arch/i386/mm/init.c]
Memory: 80MB = 80MB total Memory: 76592KB available (1724K code, 2565K data, 72K init) |
當前內存使用狀況,將列出總的內存大小, 及分配給內核的內存大小:包括代碼部分,數據部分,初始化部分,總共恰好4M。請留意此處的內核的內存大小的各個值。
進入虛擬文件系統VFS初始化
vfs_caches_init()
Dentry cache hash table entries: 16384 (order: 5, 131072 bytes) Inode cache hash table entries: 8192 (order: 4, 65536 bytes) Mount cache hash table entries: 512 (order: 0, 4096 bytes) Buffer cache hash table entries: 4096 (order: 2, 16384 bytes) Page-cache hash table entries: 32768 (order: 5, 131072 bytes) |
名詞:
① Dentry:目錄數據結構
② Inode:i節點
③ Mount cache:文件系統加載緩衝
④ buffer cache:內存緩衝區
⑤ Page Cache:頁緩衝區
Dentry目錄數據結構(目錄入口緩存),提供了一個將路徑名轉化爲特定的dentry的一個快的查找機制,Dentry只存在於RAM中;
i節點(inode)數據結構存放磁盤上的一個文件或目錄的信息,i節點存在於磁盤驅動器上;存在於RAM中的i節點就是VFS的i節點,dentry所包含的指針指向的就是它;
buffer cache內存緩衝區,相似kupdated,用來在內存與磁盤間作緩衝處理;
Page Cache 用來加快對磁盤上映像和數據的訪問。
在內存中創建各個緩衝hash表,爲kernel對文件系統的訪問作準備。
VFS(virtual filesystem switch)虛擬文件切換目錄樹有用到相似這樣的結構表。
上面的輸出信息,在通常的linux啓動過程當中都會看到。
POSIX conformance testing by UNIFIX |
conformance:順應, 一致。即POSIX適應性檢測。UNIFIX是一家德國的技術公司,Linux 本來要基於 POSIX.1 的, 可是 POSIX 不是免費的, 並且 POSIX.1 證書至關昂貴. 這使得 Linux 基於 POSIX 開發至關困難. Unifix公司(Braunschweig, 德國) 開發了一個得到了 FIPS 151-2 證書的 Linux 系統. 這種技術用於 Unifix 的發行版 Unifix Linux 2.0 和 Lasermoon 的 Linux-FT。
在2.6的內核中就將上面的這句輸出給拿掉了。
第二節:用戶模式( user_mode )開始,start_kernel結束
PCI: bus0: Fast back to back transfers disabled PCI: Configured XX as a PCI slave with 128MB PCI memory PCI: Each Region size is 16384KB PCI: Reserved memory from 0x10080000 to 0x15080000 for DMA and mapped to 0x12000000 |
設備的初始化 init()--->do_basic_init()--->pci_init(),初始化PCI,檢測系統的PCI設備。
Linux NET4.0 for Linux 2.4 Based upon Swansea University Computer Society NET3.039 |
英國威爾士,斯旺西大學的NET3.039, TCP/IP 協議棧
此信息,在linux啓動過程當中都會出現。
Initializing RT netlink socket |
對Socket的初始化,socket_init(),Netlink 一種路由器管理協議(linux-2.4.22\net\core\Rtnetlink.c,Routing netlink socket interface: protocol independent part。 其中RT是route路由的意思。這句輸出是在create產生rtnetlink的socket套接字時的一個調試輸出。)
此信息,在linux啓動過程當中都會出現。
Starting kswapd |
啓動交換守護進程kswapd,進程IO操做例程kpiod
kswapd能夠配合kpiod運行。進程有時候無事可作,當它運行時也不必定須要把其全部的代碼和數據都放在內存中。這就意味着咱們能夠經過把運行中程序不用的內容切換到交換分區來更好的是利用內存。大約每隔1秒,kswapd醒來並檢查內存狀況。若是在硬盤的東西要讀入內存,或者內存可用空間不足,kpiod就會被調用來作移入/移出操做。kswapd負責檢查,kpiod負責移動。
Journalled Block Device driver loaded |
加載日誌塊設備驅動。
日誌塊設備是用來對文件系統進行日誌記錄的一個塊設備。日誌文件系統是在傳統文件系統的基礎上,加入文件系統更改的日誌記錄。
它的設計思想是:跟蹤記錄文件系統的變化,並將變化內容記錄入日誌。日誌文件系統在磁盤分區中保存有日誌記錄,寫操做首先是對記錄文件進行操做,若整個寫操做因爲某種緣由(如系統掉電)而 中斷,系統重啓時,會根據日誌記錄來恢復中斷前的寫操做。在日誌文件系統中,全部的文件系統的變化都被記錄到日誌,每隔必定時間,文件系統會將更新後的元 數據及文件內容寫入磁盤。在對元數據作任何改變之前,文件系統驅動程序會向日志中寫入一個條目,這個條目描述了它將要作些什麼,而後它修改元數據。
devfs: v1.12c (20020818) Richard Gooch (rgooch@atnf.csiro.au) devfs: boot_options: 0x1 |
Devfs模塊的輸出信息。
設備文件系統devfs,版本1.12c,
pty: 256 Unix98 ptys configured |
Pty模塊的輸出信息,與控制檯操做有關的設置。
將經過 devpts 文件系統使用 Unix98 PTYs,(Pseudo-ttys (telnet etc) device是僞ttys設備的縮寫。
① TTY(/dev/tty)是TeleTYpe的一個老縮寫,爲用戶輸入提供不一樣控制檯的設備驅動程序。它的名字來源於實際掛接到 UNIX系統的、被稱爲電傳打字機(teletype)的終端。在Linux下,這些文件提供對虛擬控制檯的支持,能夠經過按<Alt-F1>到<Alt-F6>鍵來訪問這些虛擬控制檯。這些虛擬控制檯提供獨立的、同時進行的本地登陸對話過程
② ttys(/dev/ttys)是計算機終端的串行接口。/dev/ttyS0對應MS-DOS下的 COM1。
使用 make dev腳本MAKEDEV來創建pty文件。這樣系統內核就支持Unix98風格的pty了。在進行Telnet登陸時將要用到/dev/pty設備。 pty是僞終端設備,在遠程登陸等須要以終端方式進行鏈接,但又並不是真實終端的應用程序中必須使用這種設備,如telnet或xterm等程序。Linux 2.2之後增添了UNIX98風格的Pty設備,它使用一個新的文件系統(devpts針對僞終端的文件系統)和一個克隆的設備cloning device來實現其功能。
linux-2.4.22\drivers\char\Pty.c, 在devfs_mk_dir (NULL, "pts", NULL);時會輸出上面的信息。
loop: loaded (max 8 devices) |
加載返還塊設備驅動,最多支持8個設備
8139too Fast Ethernet driver 0.9.27 eth0: RealTek RTL8139 at 0x60112000, 00:10:0d:42:a0:03, IRQ 14 eth0: Identified 8139 chip type 'RTL-8100B/8139D' |
網卡驅動,基地址爲:0x60112000, MAC地址:00:10:0d:42:a0:03, 中斷號:14
RTL8139 的 接收路徑設計成一個環形緩衝區(一段線性的內存,映射成一個環形內存)。當設備接收到數據時,數據的內容就保存在這個環形緩衝區內並更新下個存儲數據的地 址(第一個數據包的開始地址+第一個數據包的長度)。設備會一直保留緩衝區內的數據直到整個緩衝區耗盡。這樣,設備會再次重寫緩衝區內起始位置的內容,就 像一個環那樣。
從 2.2 版內核升級到 2.4 版時, RTL-8139 支持模塊已再也不叫 rtl8139,而叫它 8139too,如今你再看到8139too就不會不明白它的來由了吧。
SCSI subsystem driver Revision: 1.00 |
USB設備信息,USB會被當作SCSI來處理。
mumk_register_tasklet: (1) tasklet 0x905bf9c0 status @0x9025e974 |
軟中斷信息輸出。Tasklet是在2.4中才出現,它是爲了更好地利用多CPU。
Probing XX Flash Memory |
探測 XX的閃存(Flash Memory),"NOR NAND Flash Memory Technology"
Amd/Fujitsu Extended Query Table v1.3 at 0x0040
number of CFI chips: 1
AMD與富士通合資設立的Flash供貨商Spansion。AMD因獲利不佳,已經退出Flash市場,後續由Spansion合資公司經營.主要生產NOR類型的flash,特色是容量小,速度快。Spansion商標的flash,在咱們開發中會常常看到。之後你們看到Spansion的芯片,就能瞭解到它和AMD還有富士通的前因後果了。
Common flash Interface (CFI)是指一個統一的flash訪問接口,表示這種flash是這種接口類型的。
Using buffer write method |
使用flash寫緩衝方式
flash提供了寫BUFFER的命令來加快對flash上塊的操做。對Flash擦除和寫數據是很慢的。若是用寫BUFFER的命令會快一點。據手冊上說,會快20倍。Buffer Size :5 bytes的buffer緩衝不是每一個塊都有,是整個flash只有一個5 bytes的buffer,用寫BUFFER命令對全部的塊進行寫操做,都要用同一個buffer,寫Buffer是主要檢查buffer是否available,其實buffer起緩衝做用,來提升工做效率。
好比某flash有128個128K字節塊。容許用戶對任意塊進行字節編程和寫緩衝器字節編程操做,每字節編程時間爲210μs;若採用寫緩衝器字節編程方式,32字節編程共需218μs,每字節編程時間僅爲6.8μs。芯片的塊擦除時間爲1s,容許在編程或塊擦除操做的同時進行懸掛中斷去進行讀操做,待讀操做完成後,寫入懸掛恢復命令,再繼續編程或塊擦除。
Creating 3 MTD partitions on "XX mapped flash": 0x00000000-0x00020000 : "BootLoader" 0x00020000-0x00040000 : "Config" 0x00040000-0x01000000 : "Romfs" |
此處爲重要信息部分,須要特別留意。
在內存中映射過的flash,建立三個MTD分區:
flash上的內容將被映射到內存中的對應地址
前128K爲BootLoader--->0x00000000-0x00020000
接着的128K爲系統配置信息Config存放的位置--->0x00020000-0x00040000
再後面的 16M - 2X128K 爲romfs的存放處.--->0x00040000-0x01000000
上面的內容,你們能夠根據前面的換算公式獲得。
A> 編譯的bootloader通常大小約50K左右;
B> 在此處就知道了配置信息config是放在第2分區中的;
C> 製做的romfs的大小,通常爲8M或10M左右,因此能放得下;
嵌入式Linux內核的塊設備驅動:
對於linux 的根文件系統,目前有三種塊設備的驅動能夠選擇,它們分別是:
a) Blkmem 驅動
b) MTD 驅動
c) RAM disk 驅動
Blkmem 驅動是專門爲嵌入式linux 開發的一種塊設備驅動,它是嵌入式linux系統中最爲古老和通用的塊設備驅動。它原理相對簡單可是配置比較複雜,須要根據你即的Flash的分區使用狀況來修改代碼。固然修改的結果是它能夠對一些NOR型的Flash進行讀寫操做。不過目前支持的Flash類型不夠多。若是新加入對一種Flash的支持須要做的工做量比較大。
Linux的MTD驅動是標準Linux的Flash驅動。它支持大量的設備,有足夠的功能來定義Flash的分區,進行地址映射等等。使用MTD你能夠在一個系統中使用不一樣類型的Flash。它能夠將不一樣的Flash組合成一個線性的地址讓你來使用。
在標準的Linux 2.4內核中MTD有一系列的選項,你能夠根據我的系統的須要來選擇,定製。
另一種選擇就是RAM disk 驅動。在PC上它常常用於沒有硬盤的Linux的啓動過程。它和Flash沒有直接的關係。不過當Flash上啓動的是通過壓縮的內核時。RAM disk 能夠做爲根文件系統。
MTD 驅動提供了對Flash強大的支持,你經過它甚至能夠在Flash上運行一個能夠讀寫的真正的文件系統,好比JFFS2。而Blkmem驅動則可望不可即。
NET4: Linux TCP/IP 1.0 for NET4.0 |
調用inet_init [ linux-2.4.22\net\ipv4\Af_inet.c ]時的輸出信息, 在啓動過程當中被socket.c調用到。
IP Protocols: ICMP, UDP, TCP, IGMP |
列出能夠支持的IP協議,此處爲kernel源代碼inet_add_protocol(p);的輸出。
在linux啓動過程當中,都會看到這句的輸出。
IP: routing cache hash table of 512 buckets, 4Kbytes |
IP路由代碼的輸出信息。
ip_rt_init [ linux-2.4.22\net\ipv4\Route.c ],Set the IP module up,路由緩衝hash表
TCP: Hash tables configured (established 8192 bind 8192) |
TCP協議初始化輸出信息。tcp_init [ linux-2.4.22\net\ipv4\Tcp.c ],
NET4: Unix domain sockets 1.0/SMP for Linux NET4.0. |
UNIX網絡協議信息。
af_unix_init[ linux-2.4.22\net\unix\Af_unix.c ], 多種鏈接的一種(IPv4, UNIX domain sockets, IPv6和IrDA). SMP 對稱多處理器—Symmetrical Multi Processing,這裏主要是指UNIX的一些網絡協議.
上面的關於網絡的輸出信息是在linux啓動信息中都會出現的。
加載各類文件系統
cramfs: wrong magic |
會出現「cramfs: wrong magic」,別擔憂這沒有什麼害處,這個是kernel的書寫bug,在2.6中有修改之,它是一個警告信息,用來檢查cramfs的superblock超級塊的。superblock也是VFS要用到的數據結構。
代碼linux-2.4.22\fs\cramfs\Inode.c:
2.4 cramfs_read_super(。。。) /* Do sanity checks on the superblock */ if (super.magic != CRAMFS_MAGIC) { /* check at 512 byte offset */ memcpy(&super, cramfs_read(sb, 512, sizeof(super)), sizeof(super)); if (super.magic != CRAMFS_MAGIC) { printk(KERN_ERR "cramfs: wrong magic\n"); goto out; } } |
2.6 if (super.magic != CRAMFS_MAGIC) { if (!silent) printk(KERN_ERR "cramfs: wrong magic\n"); goto out; } |
超級塊是文件系統的「頭部」。它包含文件系統的狀態、尺寸和空閒磁盤塊等信息。若是損壞了一個文件系統的超級塊(例如不當心直接將數據寫到了文件系統的超級塊分區中),那麼系統可能會徹底不識別該文件系統,這樣也就不能安裝它了,即便採用e2fsck 命令也不能處理這個問題。
Cramfs文件系統:
cramfs 是 Linus Torvalds 本人開發的一個適用於嵌入式系統的小文件系統。因爲它是隻讀的,因此,雖然它採起了 zlib 作壓縮,可是它仍是能夠作到高效的隨機讀取。 cramfs 不會影響系統讀取文件的速度,又是一個高度壓縮的文件系統。
咱們製做image文件以後,咱們還要考慮怎樣才能在系統運行的時候,把這個 image 文件 mount 上來,成爲一個可用的文件系統。因爲這個 image 文件不是一個一般意義上的 block 設備,咱們必須採用 loopback 設備來完成這一任務,如:
mount -o loop -t cramfs /usr.img /usr
這樣,就能夠經由 loopback 設備,把 usr.img 這個 cramfs 的 image 文件 mount 到 /usr 目錄上去了。內核中須要對loopback這個設備的支持。
cramfs 的壓縮效率通常都能達到將近 50%。
Cramfs經過優化索引節點表的尺寸和除去傳統文件系統中文件之間的空間浪費來達到節約空間的目的。它還使用了zlib壓縮,實現優於2:1的壓縮比例。解壓縮過程的系統開銷並非很大,由於Cramfs支持指定單塊的解壓,而並沒必要解壓縮整個文件。
Cramfs不只能節省空間,還能避免非正常關機致使的等待fsck或手工進行fsck的麻煩。它經過只讀的方式達到這一目的。
RamDisk有三種實現方式:
在Linux中能夠將一部份內存mount爲分區來使用,一般稱之爲RamDisk,分爲:
Ramdisk, ramfs, tmpfs.
① 第一種就是傳統意義上的,能夠格式化,而後加載。
這在Linux內核2.0/2.2就已經支持,其不足之處是大小固定,以後不能改變。
爲了可以使用Ramdisk,咱們在編譯內核時須將block device中的Ramdisk支持選上,它下面還有兩個選項,一個是設定Ramdisk的大小,默認是4096k;另外一個是initrd的支持。
若是對Ramdisk的支持已經編譯進內核,咱們就可使用它了:
首先查看一下可用的RamDisk,使用ls /dev/ram*
首先建立一個目錄,好比test,運行mkdir /mnt/test;
而後對/dev/ram0 建立文件系統,運行mke2fs /dev/ram0;
最後掛載 /dev/ram0,運行mount /dev/ram /mnt/test,就能夠象對普通硬盤同樣對它進行操做了。
② 另兩種則是內核2.4才支持的,經過Ramfs或者Tmpfs來實現:
它們不需通過格式化,用起來靈活,其大小隨所須要的空間而增長或減小。
Ramfs顧名思義是內存文件系統,它處於虛擬文件系統(VFS)層,而不像ramdisk那樣基於虛擬在內存中的其餘文件系統(ex2fs)。
於是,它無需格式化,能夠建立多個,只要內存足夠,在建立時能夠指定其最大能使用的內存大小。
若是你的Linux已經將Ramfs編譯進內核,你就能夠很容易地使用Ramfs了。建立一個目錄,加載Ramfs到該目錄便可:
# mkdir /testRam
# mount -t ramfs none /testRAM
缺省狀況下,Ramfs被限制最多可以使用內存大小的一半。能夠經過maxsize(以kbyte爲單位)選項來改變。
# mount -t ramfs none /testRAM -o maxsize=2000 (建立了一個限定最大使用內存爲2M的ramdisk)
③ Tmpfs是一個虛擬內存文件系統,它不一樣於傳統的用塊設備形式來實現的Ramdisk,也不一樣於針對物理內存的Ramfs。
Tmpfs可使用物理內存,也可使用交換分區。在Linux內核中,虛擬內存資源由物理內存(RAM)和交換分區組成,這些資源是由內核中的虛擬內存子系統來負責分配和管理。
Tmpfs向虛擬內存子系統請求頁來存儲文件,它同Linux的其它請求頁的部分同樣,不知道分配給本身的頁是在內存中仍是在交換分區中。同Ramfs同樣,其大小也不是固定的,而是隨着所須要的空間而動態的增減。
使用tmpfs,首先你編譯內核時得選擇"虛擬內存文件系統支持(Virtual memory filesystem support)" 。
而後就能夠加載tmpfs文件系統了:
# mkdir -p /mnt/tmpfs
# mount tmpfs /mnt/tmpfs -t tmpfs
一樣能夠在加載時指定tmpfs文件系統大小的最大限制:
# mount tmpfs /mnt/tmpfs -t tmpfs -o size=32m
FAT: bogus logical sector size 21072 |
具體的文件系統FAT格式。
虛擬邏輯扇區大小爲20K,linux-2.4.22\fs\fat\Inode.c。
在初始化MS-DOS文件系統時,讀MS-DOS文件系統的superblock,函數fat_read_super中輸出的上面的信息。
UMSDOS: msdos_read_super failed, mount aborted. |
UMSDOS:一種文件系統,特色容量大但相對而言不大穩定。是Linux 使用的擴展了的DOS文件系統。它在 DOS 文件系統下增長了長文件名、 UID/GID、POSIX 權限和特殊文件 (設備、命名管道等)功能,而不犧牲對 DOS 的兼容性。容許一個普通的msdos文件系統用於Linux,並且無須爲它創建單獨的分區,特別適合早期的硬盤空間不足的硬件條件。
VFS: Mounted root (romfs filesystem) readonly |
虛擬文件系統VFS(Virtual Filesystem Switch)的輸出信息。
再次強調一下一個概念。VFS 是一種軟件機制,也可稱它爲 Linux 的文件系統管理者,它是用來管理實際文件系統的掛載點,目的是爲了能支持多種文件系統。kernel會先在內存中創建一顆 VFS 目錄樹,是內存中的一個數據對象,而後在其下掛載rootfs文件系統,還能夠掛載其餘類型的文件系統到某個子目錄上。
Mounted devfs on /dev |
加載devfs設備管理文件系統到dev安裝點上。
/dev是咱們常常會用到的一個目錄。
在2.4的kernel中才有使用到。每次啓動時內核會自動掛載devfs。
devfs提供了訪問內核設備的命名空間。它並非創建或更改設備節點,devfs只是爲你的特別文件系統進行維護。通常咱們能夠手工mknod創件設備節點。/dev目錄最初是空的,裏面特定的文件是在系統啓動時、或是加載模組後驅動程序載入時創建的。當模組和驅動程序卸載時,文件就消失了。
Freeing init memory: 72K |
釋放1號用戶進程init所佔用的內存。
第三節:加載linux內核完畢,轉入cpu_idle進程
系統啓動過程當中進程狀況:
①init進程
通常來講, 系統在跑完 kernel bootstrapping 內核引導自舉後(被裝入內存、已經開始運行、已經初始化了全部的設備驅動程序和數據結構等等), 就去運行 init『萬process之父』, 有了它, 才能開始跑其餘的進程,所以,init進程,它是內核啓動的第一個用戶級進程,它的進程號老是1。
你能夠用進程查看命令來驗證 # ps aux PID Uid VmSize Stat Command 1 0 SW init 2 0 SW [keventd] 3 0 SWN [ksoftirqd_CPU0] 4 0 SW [kswapd] 5 0 SW [bdflush] 6 0 SW [kupdated] 7 0 SW [rbwdg] 9 0 SW [mtdblockd] 10 0 SW [khubd] 80 0 SW [loop0] |
另外 Linux 有兩個 kernel 類的 process 也開始跑了起來,一個是 kflushd/bdflush,另外一個是 kswapd;
只有這個 init 是徹底屬於 user 類的進程, 後二者是 kernel假借 process 進程之名掛在進程上。
init有許多很重要的任務,好比象啓動getty(用於用戶登陸)、實現運行級別、以及處理孤立進程。
init 一開始就去讀 /etc/inittab (init初始化表),初始化表是按必定格式排列的關於進程運行時的有關信息的。init程序須要讀取/etc/inittab文件做爲其行爲指針。這個 inittab 中對於各個runlevel運行級別要跑哪些 rc 或 spawn 生出什麼有很清楚的設定。
通常, 在Linux中初始化腳本在/etc/inittab 文件(或稱初始化表)中能夠找到關於不一樣運行級別的描述。inittab是以行爲單位的描述性(非執行性)文本,每個指令行都是固定格式
inittab中有respawn項,但若是一個命令運行時失敗了,爲了不重運行的頻率過高,init將追蹤一個命令重運行了多少次,而且若是重運行的頻率過高,它將被延時五分鐘後再運行。
② kernel進程
A> 請注意init是1號進程,其餘進程id分別是kflushd/ bdflush, kupdate, kpiod and kswapd。這裏有一個要指出的:你會注意到虛擬佔用(SIZE)和實際佔用(RSS)列都是0,進程怎麼會不使用內存呢?
這些進程就是內核守護進程。大部份內核並不顯示在進程列表裏。守護進程在init以後啓動,因此他們和其餘進程同樣有進程ID,可是他們的代碼和數據都存放在內核佔有的內存中。在列表中使用中括號來區別與其餘進程。
B> 輸入和輸出是經過內存中的緩衝來完成的,這讓事情變得更快,程序的寫入會存放在內存緩衝中,而後再一塊兒寫入硬盤。守護進程kflushd和kupdate 管理這些工做。kupdate間斷的工做(每5秒)來檢查是否有寫過的緩衝,如過有,就讓kflushd把它們寫入磁盤。
C> 進程有時候無事可作,當它運行時也不必定須要把其全部的代碼和數據都放在內存中。這就意味着咱們能夠經過把運行中程序不用的內容切換到交換分區來更好的是利用內存。把這些進程數據移入/移出內存經過進程IO管理守護進程kpiod和交換守護進程kswapd,大約每隔1秒,kswapd醒來並檢查內存狀況。若是在硬盤的東西要讀入內存,或者內存可用空間不足,kpiod就會被調用來作移入/移出操做。
D> bdflush - BUF_DIRTY, 將dirty緩存寫回到磁盤的核心守護進程。 對於有許多髒的緩衝區(包含必須同時寫到磁盤的數據的緩衝區)的系統提供了動態的響應。它在系統啓動的時候做爲一個核心線程啓動,它叫本身爲「kflushd」,而這是你用ps顯示系統中的進程的時候你會看得的名字。即按期(5秒)將髒(dirty)緩衝區的內容寫入磁盤,以騰出內存;
E> ksoftirqd_CPUx 是一個死循環, 負責處理軟中斷的。它是用來對軟中斷隊列進行緩衝處理的進程。當發生軟中斷時,系統並不急於處理,只是將相應的cpu的中斷狀態結構中的active 的相應的位,置位,並將相應的處理函數掛到相應的隊列,而後等待調度時機來臨,再來處理.
ksoftirqd_CPUx是由cpu_raise_softirq()即cpu觸發中斷,喚醒的內核線程,這涉及到軟中斷,ksoftirqd的代碼參見 [kernel/softirq.c]
F> keventd,它的任務就是執行 scheduler 調度器隊列中的任務,keventd 爲它運行的任務提供了可預期的進程上下文。
G> khubd, 是用來檢測USB hub設備的,當usb有動態插拔時,將交由此內核進程來處理。在檢測到有hub事件時會有相應的動做(usb_hub_events())
H> mtdblockd是用來對flash塊設備進行寫操做的守護進程。
NAND類型的Flash須要MTD(Memory Technology Devices 內存技術驅動程序)驅動的支持才能被linux所使用。
NAND的特色是不能在芯片內執行(XIP,eXecute In Place),須要把代碼讀到系統RAM中再執行,傳輸效率不是最高,最大擦寫次數量爲一百萬次,但寫入和擦除的速度很快,擦除單元小,是高數據存儲密度的最佳選擇。
NAND須要I/O接口,所以使用時須要驅動程序。
I> loop0 是負責處理loop塊設備的(迴環設備)。loopback device指的就是拿文件來模擬塊設備, 在咱們這裏,loop設備主要用來處理須要mount到板上的文件系統,相似mount /tmp/rootfs /mnt -o loop。.咱們的實例有:mount -o loop -t cramfs /xxx.bin /xxx 也就是將xxx.bin這個文件mount到板上來模擬cramfs壓縮ram文件系統。loop0進程負責對loop設備進行操做。
loopback設備和其餘的塊設備的使用方法相同。特別的是,能夠在該設備上創建一個文件系統,而後利用mount命令把該系統映射到某個目錄下以便訪問。這種整個創建在一個普通磁盤文件上的文件系統,就是虛擬文件系統 (virtual file system)。
總結:
上面的內容是本人爲了在實際開發中更加清楚地瞭解嵌入式linux的啓動過程而作的一個總結性的文章。
在對嵌入式linux的啓動過程作了一個詳細註釋後,你們會對涉及到嵌入系統的各個概念有了一個更加明確的認識,並能對嵌入系統的軟硬件環境的有關設置更加清楚。當你本身動手結合linux源代碼來分析時,將會有一個清楚的全局觀。