linux内核唤醒过程,Linux内核启动过程分析
1、Linux內(nèi)核啟動協(xié)議
閱讀文檔\linux-2.6.35\Documentation\x86\boot.txt
傳統(tǒng)支持Image和zImage內(nèi)核的啟動裝載內(nèi)存布局(2.4以前的內(nèi)核裝載就是這樣的布局):
|? ? ? ? ? ? |
0A0000? ? +------------------------+
|? Reserved for BIOS? ? |? ? Do not use.? Reserved for BIOS EBDA.
09A000? ? +------------------------+
|? Command line? ? ? ? |
|? Stack/heap? ? ? ? |? ? For use by the kernel real-mode code.
098000? ? +------------------------+
|? Kernel setup? ? ? ? |? ? The kernel real-mode code.
090200? ? +------------------------+
|? Kernel boot sector? ? |? ? The kernel legacy boot sector.
090000? ? +------------------------+
|? Protected-mode kernel |? ? The bulk of the kernel image.
010000? ? +------------------------+
|? Boot loader? ? ? ? |? ?
001000? ? +------------------------+
|? Reserved for MBR/BIOS |
000800? ? +------------------------+
|? Typically used by MBR |
000600? ? +------------------------+
|? BIOS use only? ? |
000000? ? +------------------------+
當(dāng)使用bzImage時,保護模式的內(nèi)核會被重定位到0x1000000(高端內(nèi)存),內(nèi)核實模式的代碼(boot sector,setup和stack/heap)會被編譯成可重定位到0x100000與低端內(nèi)存底端之間的任何地址處。不幸的是,在2.00和2.01版的引導(dǎo)協(xié)議中,0x90000+的內(nèi)存區(qū)域仍然被使用在內(nèi)核的內(nèi)部。2.02版的引導(dǎo)協(xié)議解決了這個問題。boot loader應(yīng)該使BIOS的12h中斷調(diào)用來檢查低端內(nèi)存中還有多少內(nèi)存可用。
人們都希望“內(nèi)存上限”,即boot loader觸及的低端內(nèi)存最高處的指針,盡可能地低,因為一些新的BIOS開始分配一些相當(dāng)大的內(nèi)存,所謂的擴展BIOS數(shù)據(jù)域,幾乎快接近低端內(nèi)存的最高處了。
不幸的是,如果BIOS 12h中斷報告說內(nèi)存的數(shù)量太小了,則boot loader除了報告一個錯誤給用戶外,什么也不會做。因此,boot loader應(yīng)該被設(shè)計成占用盡可能少的低端內(nèi)存。對zImage和以前的bzImage,這要求數(shù)據(jù)能被寫到x090000段,boot loader應(yīng)該確保不會使用0x9A000指針以上的內(nèi)存;很多BIOS在這個指針以上會終止。
對一個引導(dǎo)協(xié)議>=2.02的現(xiàn)代bzImage內(nèi)核,其內(nèi)存布局使用以下格式:
|? Protected-mode kernel |
100000? +------------------------+
|? I/O memory hole? ? |
0A0000? ? +------------------------+
|? Reserved for BIOS? ? |? ? Leave as much as possible unused
~? ? ? ? ? ? ? ? ? ? ? ? ~
|? Command line? ? ? ? |? ? (Can also be below the X+10000 mark)
X+10000? ? +------------------------+
|? Stack/heap? ? ? ? |? ? For use by the kernel real-mode code.
X+08000? ? +------------------------+
|? Kernel setup? ? ? ? |? ? The kernel real-mode code.
|? Kernel boot sector? ? |? ? The kernel legacy boot sector.
X? ? ? +------------------------+
|? Boot loader? ? ? ? |? ?
001000? ? +------------------------+
|? Reserved for MBR/BIOS |
000800? ? +------------------------+
|? Typically used by MBR |
000600? ? +------------------------+
|? BIOS use only? ? |
000000? ? +------------------------+
這里程序段地址是由grub的大小來決定的。地址X應(yīng)該在bootloader所允許的范圍內(nèi)盡可能地低。
2、BIOS POST過程
傳統(tǒng)意義上,由于CPU加電之后,CPU只能訪問ROM或者RAM里的數(shù)據(jù),而這個時候是沒有計算機操作系統(tǒng)的,所以需要有一段程序能夠完成加載存儲在非易失性存儲介質(zhì)(比如硬盤)上的操作系統(tǒng)到RAM中的功能。這段程序存儲在ROM里,BIOS就是這類程序中的一種。對于BIOS,主要由兩家制造商制造,駐留在主板的ROM里。有了BIOS,硬件制造商可以只需要關(guān)注硬件而不需要關(guān)注軟件。BIOS的服務(wù)程序,是通過調(diào)用中斷服務(wù)程序來實現(xiàn)的。BIOS加載bootloader程序,Bootloader也可以通過BIOS提供的中斷,向BIOS獲取系統(tǒng)的信息。整個過程如下:
(1)電源啟動時鐘發(fā)生器并在總線上產(chǎn)生一個#POWERGOOD的中斷。
(2)產(chǎn)生CPU的RESET中斷(此時CPU處于8086工作模式)。
(3)進入BIOS POST代碼處:%ds=%es=%fs=%gs=%ss=0,%cs=0xFFFF0000,%eip = 0x0000FFF0 (ROM BIOS POST code,指令指針eip,數(shù)據(jù)段寄存器ds,代碼段寄存器cs)。
(4)在中斷無效狀態(tài)下執(zhí)行所有POST檢查。
(5)在地址0初始化中斷向量表IVT。
(6)0x19中斷:以啟動設(shè)備號為參數(shù)調(diào)用BIOS啟動裝載程序。這個程序從啟動設(shè)備(硬盤)的0扇面1扇區(qū)讀取數(shù)據(jù)到內(nèi)存物理地址0x7C00處開始裝載。這個0扇面1扇區(qū)稱為Boot sector(引導(dǎo)扇區(qū)),共512字節(jié),也稱為MBR。
就是說,CPU 在? BIOS 的入口(CS:IP=FFFF:0000)處執(zhí)行BIOS的匯編程序,BIOS程序功能有系統(tǒng)硬件的檢測,提供中斷訪問接口以訪問硬件。而后被BIOS程序通過中斷0x19調(diào)用磁盤MBR上的bootloader程序,將bootloader程序加載到ox7c00處,而后跳轉(zhuǎn)到0x7c00,這樣,位于 0x7c00處的bootloader程序,就可以執(zhí)行了。
從BIOS執(zhí)行MBR中的bootloader程序開始,就是linux的代碼在做的事情了。
3、Bootloader過程
bootloader程序是為計算機加載(load)計算機操作系統(tǒng)的。boot(引導(dǎo))是bootstrap的簡寫,bootstrap是引導(dǎo)指令的意思。bootloader程序通常位于硬盤上,被BIOS調(diào)用,用于加載內(nèi)核。在PC機上常見的bootloader主要有g(shù)rub、lilo、syslinux等。
GRUB(GRand Unified Bootloader)是當(dāng)前l(fā)inux諸多發(fā)行版本默認(rèn)的引導(dǎo)程序。嵌入式系統(tǒng)上,最常見的bootloader是U-BOOT。這樣的bootloader一般位于MBR的最前部。在linux系統(tǒng)中,bootloader也可以寫入文件系統(tǒng)所在分區(qū)中。比如,grub程序就非常強大。Gurb運行后,將初始化設(shè)置內(nèi)核運行所需的環(huán)境。然后加載內(nèi)核鏡像。
grub磁盤引導(dǎo)全過程:
(1)stage1: grub讀取磁盤第一個512字節(jié)(硬盤的0道0面1扇區(qū),被稱為MBR(主引導(dǎo)記錄),也稱為bootsect)。MBR由一部分bootloader的引導(dǎo)代碼、分區(qū)表和魔數(shù)三部分組成。
(2)stage1_5: 識別各種不同的文件系統(tǒng)格式。這使得grub識別到文件系統(tǒng)。
(3)stage2: 加載系統(tǒng)引導(dǎo)菜單(/boot/grub/menu.lst或grub.lst),加載內(nèi)核vmlinuz和RAM磁盤initrd。
4、內(nèi)核啟動過程
內(nèi)核映像文件vmlinuz:包含有l(wèi)inux內(nèi)核的靜態(tài)鏈接的可執(zhí)行文件,傳統(tǒng)上,vmlinux被稱為可引導(dǎo)的內(nèi)核鏡像。vmlinuz是vmlinux的壓縮文件。其構(gòu)成如下:
(1)第一個512字節(jié)(以前是在arch/i386/boot/bootsect.S);
(2)第二個,一段代碼,若干個不多于512字節(jié)的段(以前是在arch/i386/boot/setup.S);
(3)保護模式下的內(nèi)核代碼(在arch/x86/boot/main.c)。
bzImage文件:使用make bzImage命令編譯內(nèi)核源代碼,可以得到采用zlib算法壓縮的zImage文件,即big zImage文件。老的zImage解壓縮內(nèi)核到低端內(nèi)存,bzImage則解壓縮內(nèi)核到高端內(nèi)存(1M(0x100000)以上),在保護模式下執(zhí)行。bzImage文件一般包含有vmlinuz、bootsect.o、setup.o、解壓縮程序misc.o、以及其他一些相關(guān)文件(如piggy.o)。注意,在Linux 2.6內(nèi)核中,bootsect.S和setup.S被整合為header.S。
initramfs(或initrd)文件:initrd是initialized ram disk的意思。主要用于加載硬件驅(qū)動模塊,輔助內(nèi)核的啟動,掛載真正的根文件系統(tǒng)。
例如,我電腦上的grub啟動項如下(在/boot/grub/grub.lst中):
title Fedora (2.6.35.10-74.fc14.i686)
root (hd0,0)
kernel /vmlinuz-2.6.35.10-74.fc14.i686 ro root=/dev/mapper/VolGroup-lv_root rd_LVM_LV=VolGroup/lv_root rd_LVM_LV=VolGroup/lv_swap rd_NO_LUKS rd_NO_MD rd_NO_DM LANG=zh_CN.UTF-8 KEYBOARDTYPE=pc KEYTABLE=us rhgb quiet
initrd /initramfs-2.6.35.10-74.fc14.i686.img
內(nèi)核的執(zhí)行參數(shù)可以控制內(nèi)核的行為,比如ro參數(shù)告訴內(nèi)核,以只讀方式掛載根分區(qū),而quiet則告訴內(nèi)核,啟動的時候不要打印任何信息。這些參數(shù)不光影響內(nèi)核的執(zhí)行,大多數(shù)的發(fā)行版也使用這些參數(shù)控制啟動完畢以后后續(xù)的動作。這些參數(shù)可以在任何時候從/proc/cmdline 這個文件中獲得。現(xiàn)在,grub找到了內(nèi)核(hd0,0)/boot/vmlinuz-2.6.35.10-74.fc14.i686,它將整個電腦的控制權(quán)交給了這個程序,內(nèi)核開始進行各種初始化的動作,你可以將quiet參數(shù)去掉,以便看看內(nèi)核都做了哪些事情,也可以在系統(tǒng)啟動成功以后,使用dmesg這個命令查看內(nèi)核啟動的時候,都打印了哪些東西。
啟動過程是和體系結(jié)構(gòu)相關(guān)的,對于2.6內(nèi)核,x86體系結(jié)構(gòu),CPU在上電初始化時,指令寄存器CS:EIP總是被初始化為固定值,這就是CPU復(fù)位后的第一條指令的地址。對于32位地址總線的系統(tǒng)來說,4GB的物理空間至少被劃分為兩個部分,一部分是內(nèi)存的地址空間,另外一部分地址空間用于對BIOS芯片存儲單元進行尋址。x86復(fù)位后工作在實模式下,該模式下CPU的尋址空間為1MB。CS:IP的復(fù)位值是FFFF:0000,物理地址為FFFF0。主板設(shè)計者必須保證把這個物理地址映射到BIOS芯片上,而不是RAM上。
裝載Linux內(nèi)核的第一步應(yīng)該是加載實模式代碼(boot sector和setup代碼),然后檢查偏移0x01f1處的頭部(header)中的各個參數(shù)值。實模式的代碼總共有32K,但是boot loader可以選擇只裝載前面的兩個扇區(qū)(1K),然后檢查bootup扇區(qū)的大小。
header���各個域的格式如下:
Offset/Size? Proto? Name? ? Meaning
01F1/1? ALL(1? setup_sects? The size of the setup in sectors
01F2/2? ALL? root_flags? ? If set, the root is mounted readonly
01F4/4? 2.04+? syssize? ? The size of the 32-bit code in 16-byte paras
01F8/2? ALL? ram_size? DO NOT USE - for bootsect.S use only
01FA/2? ALL? vid_mode? Video mode control
01FC/2? ALL? root_dev? Default root device number
01FE/2? ALL? boot_flag? 0xAA55 magic number
0200/2? 2.00+? jump? ? Jump instruction
0202/4? 2.00+? header? ? Magic signature "HdrS"
0206/2? 2.00+? version? ? Boot protocol version supported
0208/4? 2.00+? realmode_swtch? Boot loader hook (see below)
020C/2? 2.00+? start_sys_seg? The load-low segment (0x1000) (obsolete)
020E/2? 2.00+? kernel_version? Pointer to kernel version string
0210/1? 2.00+? type_of_loader? Boot loader identifier
0211/1? 2.00+? loadflags? Boot protocol option flags
0212/2? 2.00+? setup_move_size? Move to high memory size (used with hooks)
0214/4? 2.00+? code32_start? Boot loader hook (see below)
0218/4? 2.00+? ramdisk_image? initrd load address (set by boot loader)
021C/4? 2.00+? ramdisk_size? initrd size (set by boot loader)
0220/4? 2.00+? bootsect_kludge? DO NOT USE - for bootsect.S use only
0224/2? 2.01+? heap_end_ptr? Free memory after setup end
0226/1? 2.02+? ext_loader_ver? Extended boot loader version
0227/1? 2.02+? ext_loader_type? Extended boot loader ID
0228/4? 2.02+? cmd_line_ptr? 32-bit pointer to the kernel command line
022C/4? 2.03+? ramdisk_max? Highest legal initrd address
0230/4? 2.05+? kernel_alignment? Physical addr alignment required for kernel
0234/1? 2.05+? relocatable_kernel? Whether kernel is relocatable or not
0235/1? 2.10+? min_alignment? Minimum alignment, as a power of two
0236/2? N/A? pad3? ? ? ? ? Unused
0238/4? 2.06+? cmdline_size? Maximum size of the kernel command line
023C/4? 2.07+? hardware_subarch? Hardware subarchitecture
0240/8? 2.07+? hardware_subarch_data? Subarchitecture-specific data
0248/4? 2.08+? payload_offset? Offset of kernel payload
024C/4? 2.08+? payload_length? Length of kernel payload
0250/8? 2.09+? setup_data? 64-bit physical pointer to linked list of struct setup_data
0258/8? 2.10+? pref_address? Preferred loading address
0260/4? 2.10+? init_size? Linear memory required during initialization
總結(jié)
以上是生活随笔為你收集整理的linux内核唤醒过程,Linux内核启动过程分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux命令的详情描述,linux 常
- 下一篇: 交叉路口红绿灯控制程序linux,PLC