http://blog.csdn.net/dianhuiren/article/details/6892605
摘要
我們在這裏討論的是對嵌入式linux系統的啓動過程的輸出信息的註釋,通過我們的討論,大家會對嵌入式linux啓動過程中出現的、以前感覺熟悉的、但卻又似是而非的東西有一個確切的瞭解,並且能瞭解到這些輸出信息的來龍去脈。
嵌入式linux的啓動信息是一個很值得我們去好好研究的東西,它能將一幅縮影圖呈現在我們面前,來指導我們更加深入地理解linux內核。
關鍵字:linux,嵌入式,啓動,bootloader
正文
作爲一名嵌入系統開發者,你一定遇到過下面的情景:
在某論壇上看到一篇帖子,上面貼着嵌入式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 ([email protected]) 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源代碼來分析時,將會有一個清楚的全局觀。