爱生活,爱阅读
分类: LINUX
2012-08-31 20:21:06
Linux启动过程
----By Joseph Griffiths
这篇文档将从Linux的角度记录x86 的引导过程。它将试图提供一个技术性的概述。所以,如果对十进制,八进制或者二进制不熟悉,你可以先温习一下它们。
启动顺序
基本输入输出(BIOS)完成自检(内存,中央处理器,磁盘)。
BIOS执行MBR中的主引导代码。
主引导代码中有两个功能(function):识别所有活跃分区(active partition)与扩展分区(extended partition)。
如果主引导代码识别了扩展分区,那么它将跟随扩展分区直到没有额外的分区为止。
主引导代码跳转到活跃分区,并将引导过程移交给该分区。
引导过程进入阶段1。
引导过程进入阶段1.5,并显示菜单。
引导过程进入阶段2,等待用户输入或者等待超时的默认选择。
内核初始化硬件。
引导过程从/boot/initrd中的initrd加载驱动与模块。
引导过程将引导控制权交给内核。
/sbin/init执行剩下的系统启动任务。
init启动运行级别的脚本。
引导过程是从一个512字节大小,叫做主引导记录的代码开始执行的。MBR存储在磁盘的第一个512字节。BIOS存取该扇区,该扇区包含指向剩余的引导过程。MBR包含分区表(partition table),引导加载器(bootloader)以及幻数(magic number)。引导加载器(bootloader)占446字节。分区表占64字节,幻数占最后两字节。
幻数作为crc来校验MBR。它应该总是0XAA55。你可以通过以下命令来查看你的系统上MBR的内容:
dd if=/dev/hda of=/mbr.dump bs=512 count=1
这将从hdr磁盘上获取前512个字节到文件/mbr.dump中。另外,你可以通过以下命令重写mbr到磁盘文件:
dd if=/mbr.dump of=/dev/hda bs=512 count=1
你可以使用字符串的形式查看当前的引导加载器(bootloader):strings /mbr.dump
linuxmoney:~ # strings /mbr.dump
ZRrK
D|f1
GRUB
Geom
Hard Disk
Read
Error
可以通过file /mbr.dump查看磁盘分区。它将列出分区(listing partition)以及起始与终止扇区:
x86 boot sector;
partition 1: ID=0×83, starthead 1, startsector 63, 417627
sectors;
partition 2:ID=0×82, starthead 0,startsector 417690, 2104515 sectors;
partition 3: ID=0×83, starthead 0, startsector 2522205, 4209030 sectors;
partition 4: ID=0xf, active, starthead 0, startsector 6731235, 149565150 sectors, code offset 0×48.
你可以看到分区4是活跃的,其ID表明了分区类型。你可以从HERE查找分区ID代码。由于每一个扇区有512字节,我们可以确定每个分区的大小:例如分区1是417627个扇区。
通过如下命令获取分区大小:
可以与df –k的输出信息进行比较:
Filesystem 1K-blocks Used Available
Use% Mounted on
/dev/hda5 20641788 5464224 14128924 28% /
/dev/hda6
52964408 4147160 46126764 9% /home
/dev/hdc1 244076732 100537572 143539160
42% /data
通过以下命令显示16进制的
也可以通过hexdump显示之
Key |
|
Color |
Description |
RED |
BootLoader |
GREEN |
1st Partition table |
YELLOW |
2nd Partition table |
BROWN |
3rd Partition table |
PINK |
4rd Partition table |
BLUE |
Magic Number |
还可以通过xxd /mbr.dump显示
linuxmoney:~ # xxd mbr.dump
0000000: eb48 90d0 66bc 007c 0000 8ec0 8ed8 89e6 .H..f..|........
0000010: 66bf 0006 0000 66b9 0001 0000 f3a5 ea23 f.....f........#
0000020: 0600 0080 fa80 7c05 80fa 877e 02b2 8088 ......|....~....
0000030: 1649 0766 bfbe 0700 0031 f666 b904 0302 .I.f.....1.f....
0000040: ff00 0020 0100 0000 0002 fa90 90f6 c280 ... ............
0000050: 7502 b280 ea59 7c00 0031 c08e d88e d0bc u....Y|..1......
0000060: 0020 fba0 407c 3cff 7402 88c2 52be 817d . ..@|<.t...R..}
0000070: e836 01f6 c280 7456 b441 bbaa 55cd 135a .6....tV.A..U..Z
0000080: 5272 4b81 fb55 aa75 45a0 417c 84c0 783e RrK..U.uE.A|..x>
0000090: 7505 83e1 0174 3766 8b4c 10be 057c c644 u....t7f.L...|.D
00000a0: ff01 668b 1e44 7cc7 0410 00c7 4402 0100 ..f..D|.....D...
00000b0: 6689 5c08 c744 0600 7066 31c0 8944 0466 f.\..D..pf1..D.f
00000c0: 8944 0cb4 42cd 1372 05bb 0070 eb7d b408 .D..B..r...p.}..
00000d0: cd13 730a f6c2 800f 84e8 00e9 8d00 be05 ..s.............
00000e0: 7cc6 44ff 0066 31c0 88f0 4066 8944 0431 |
00000f0: d288 cac1 e202 88e8 88f4 4089 4408 31c0
0000100: 88d0 c0e8 0266 8904 66a1 447c 6631 d266 .....f..f.D|f1.f
0000110: f734 8854 0a66 31d2 66f7 7404 8854 0b89 .4.T.f1.f.t..T..
0000120: 440c 3b44 087d 3c8a 540d c0e2 068a 4c0a D.;D.}<.T.....L.
0000130: fec1 08d1 8a6c 0c5a 8a74 0bbb 0070 8ec3 .....l.Z.t...p..
0000140: 31db b801 02cd 1372 2a8c c38e 0648 7c60 1......r*....H|`
0000150: 1eb9 0001 8edb 31f6 31ff fcf3 a51f 61ff ......1.1.....a.
0000160: 2642 7cbe 877d e840 00eb 0ebe 8c7d e838 &B|..}}.8
0000170: 00eb 06be 967d e830 00be 9b7d e82a 00eb .....}.0...}.*..
0000180: fe47 5255 4220 0047 656f 6d00 4861 7264 .GRUB .Geom.Hard
0000190: 2044 6973 6b00 5265 6164 0020 4572 726f Disk.Read. Erro
00001a0: 7200 bb01 00b4 0ecd 10ac 3c00 75f4 c300 r.........<.u...
00001b0: 0000 0000 0000 0000 5147 0a00 0000 0001 ........QG......
00001c0: 0100 83fe 3f19 3f00 0000 5b5f 0600 0000 ....?.?...[_....
00001d0: 011a 82fe 3f9c 9a5f 0600 c31c 2000 0000 ....?.._.... ...
00001e0: 019d 83fe 7fa2 5d7c 2600 8639 4000 8000 ......]|&
00001f0: 41a3 0ffe ffff e3b5 6600 de2e ea08 55aa A.......f.....U.
linuxmoney:~ # hexdump mbr.dump
0000000 48eb d090 bc66 7c00 0000 c08e d88e e689
0000010 bf66 0600 0000 b966 0100 0000 a5f3 23ea
0000020 0006 8000 80fa 057c fa80 7e87 b202 8880
0000030 4916 6607 bebf 0007 3100 66f6 04b9 0203
0000040 00ff 2000 0001 0000 0200 90fa f690 80c2
0000050 0275 80b2 59ea 007c 3100 8ec0 8ed8 bcd0
0000060 2000 a0fb 7c40 ff3c 0274 c288 be52 7d81
0000070 36e8 f601 80c2 5674 41b4 aabb cd55 5a13
0000080 7252 814b 55fb 75aa a045 7c41 c084 3e78
0000090 0575 e183 7401 6637 4c8b be10 7c05 44c6
00000a0 01ff 8b66 441e c77c 1004 c700 0244 0001
00000b0 8966 085c 44c7 0006 6670 c031 4489 6604
00000c0 4489 b40c cd42 7213 bb05 7000 7deb 08b4
00000d0 13cd 0a73 c2f6 0f80 e884 e900 008d 05be
00000e0 c67c ff44 6600 c031 f088 6640 4489 3104
00000f0 88d2 c1ca 02e2 e888 f488 8940 0844 c031
0000100 d088 e8c0 6602 0489 a166 7c44 3166 66d2
0000110 34f7 5488 660a d231 f766 0474 5488 890b
0000120 0c44 443b 7d08 8a3c 0d54 e2c0 8a06 0a4c
0000130 c1fe d108 6c8a 5a0c 748a bb0b 7000 c38e
0000140 db31 01b8 cd02 7213 8c2a 8ec3 4806 607c
0000150 b91e 0100 db8e f631 ff31 f3fc 1fa5 ff61
0000160 4226 be7c 7d87 40e8 eb00 be0e 7d8c 38e8
0000170 eb00 be06 7d96 30e8 be00 7d9b 2ae8 eb00
0000180 47fe 5552 2042 4700 6f65 006d 6148 6472
0000190 4420 7369 006b 6552 6461 2000 7245 6f72
00001a0 0072 01bb b400 cd0e ac10 003c f475 00c3
00001b0 0000 0000 0000 0000 4751 000a 0000 0100
00001c0 0001 fe83 193f 003f 0000 5f5b 0006 0000
00001d0 1a01 fe82 9c3f 5f9a 0006 1cc3 0020 0000
00001e0 9d01 fe83 a27f 7c5d 0026 3986 0040 0080
00001f0 a341 fe0f ffff b5e3 0066 2ede 08ea aa55
你可以使用如下信息来手动(manually)解析分区表。记住调整字节序,例如:0080为8000。[注:这里为大字节序,需要进行转化]
Offset |
Size |
Description |
0x00 |
1 |
Active flag 0x80 active otherwise 0x00 |
0x01 |
3 byte |
Cylinder-head-sector address of the first sector in the partition |
0x04 |
1 byte |
Partition type |
0x05 |
3 byte |
Cylinder-head-sector address of the last sector in the partition |
0x08 |
4 byte |
Logical block address of the first sector in the partition |
0x0c |
4 byte |
Length of Parition in sectors |
+--- Active partition flag 80H for active partition
| 活跃分区标志,0x80表示活跃分区
| +--- Cylinder-head-sector address of the first sector in the partition
| | 第一个地址
| | +--- Partition Type List
| | | 分区类型
| | | +--- Cylinder-head-sector address of the last sector.
| | | | 最后一个柱面磁头扇区
| | | | +--- Logical block address of the first sector.
| | | | | 第一个扇区的逻辑块地址
| | | | | +--- Size of Parition in sectors.
| | | | | | 扇区分区大小
-- -------- -- -------- -------- -------------------------------------------------
DL DH CL CH TB DH CL CH LBA SIZE
00 01 01 00 83 fe 3f 19 3f000000 5b5f0600 1st Partition
00 00 01 1a 82 fe 3f 9c 9a5f0600 c31c2000 2nd Partition
00 00 01 9d 83 fe 7f a2 5d7c2600 86394000 3rd Partition
80 00 41 a3 0f fe ff ff e3b56600 de2eea08 4th Partition
解析CHS
CHS用于解析第一个分区的位置,如果该位置存在并位于硬盘的第一个1024柱面内。当该位置超出了上述范围,则CHS的值通常设定为最大值1024,254,63或者FE FF FF。
如果在没有将CHS的值转换为二进制的情况下,去解析CHS的值,这将是一个挑战。它们按照“head”,“sector”,“cylinder”的顺序存储。其中柱面(cylinder)值需要占用超过8位(1字节),分区值占用小于8位,所以你需要将该值转换为二进制来解析:
如果柱面(cylinder)的最终值(ending value)为1023或者1023以上,此时,你必须通过将size与起始地址(starting location)相加来计算出终止位置。记住,在每个硬盘上,我们仅可以有四个分区。这就是为什么要使用连接表(link table)来创建一个没有大小限制的扩展分区的原因。表中的分区记录(partition entry)是上下颠倒(top down)的。物理磁盘上的第一个分区是MBR分区表中的最后一条记录。
扩展分区
扩展分区是一种绕开(get around)文件系统中仅有四个分区限制的一种方式。扩展分区不能标识为活跃或者作为启动设备(boot device)。MBR中的扩展分区部分(extended partition section)可以用来描述至少(up to at least)23个额外的分区,在Linux下,可能的分区数量更高。扩展分区类型有05h,0fh,这取决于磁盘的大小。扩展分区的引导记录是从MBR中复制的。
通常情况下,扩展分区的前446个字节是空的(LILO与GRUB均用之作为内部码)。在0xaa55之后均是分区记录。在扩展分区中,总数量(LBA)是所有扩展分区的总和。
GRUB2
Grub2代表GRAND UNIFIED BOOTLOADER 2。它是当今Linux最常见的引导加载器。使用GRUB2的启动过程如下:
1. 启动引导程序(GRUB 阶段1)(boot.img)。
2. 加载程序跳转至下一启动阶段的扇区。阶段1.5位于紧接MBR之后的“DOS compat space”。
3. 阶段1.5加载文件系统并使得整个硬盘可加载(diskboot.img+kernel.img+pc.mod+ext2.mod)。
4. 阶段2接管加载过程并加载启动菜单(normal.mod+_chain.mod)。
5. 在你选择之后,操作系统被加载。
Grub文件在/boot/grub,在这里你可以发现stage1,stage2,以及menu.lst
或者grub.conf。配置文件位于menu.lst或者grub.conf。
linuxmoney:/ # ls -al /boot/grub/
total 228
drwxr-xr-x 2 root root 4096 Sep 27 18:12 .
drwxr-xr-x 3 root root 4096 Jun 30 17:43 ..
-rw------- 1 root root 30 Jun 30 17:43 device.map
-rw------- 1 root root 30 Jun 30 17:37 device.map.old
-rw-r--r-- 1 root root 7552 Nov 25 2006 e2fs_stage1_5
-rw-r--r-- 1 root root 7424 Nov 25 2006 fat_stage1_5
-rw-r--r-- 1 root root 6688 Nov 25 2006 ffs_stage1_5
-rw-r--r-- 1 root root 6688 Nov 25 2006 iso9660_stage1_5
-rw-r--r-- 1 root root 8160 Nov 25 2006 jfs_stage1_5
-rw------- 1 root root 1385 Jun 30 17:43 menu.lst
-rw------- 1 root root 1188 Jun 30 17:36 menu.lst.joe
-rw------- 1 root root 1385 Jun 30 17:37 menu.lst.old
-rw-r--r-- 1 root root 6848 Nov 25 2006 minix_stage1_5
-rw-r--r-- 1 root root 9216 Nov 25 2006 reiserfs_stage1_5
-rw-r--r-- 1 root root 512 Nov 25 2006 stage1
-rw-r--r-- 1 root root 104042 May 19 11:13 stage2
-rw-r--r-- 1 root root 7040 Nov 25 2006 ufs2_stage1_5
-rw-r--r-- 1 root root 6240 Nov 25 2006 vstafs_stage1_5
-rw-r--r-- 1 root root 8904 Nov 25 2006 xfs_stage1_5
为了重新安装grub到mbr中,在命令行中输入 grub-install /dev/hda。
Grub的配置文件在grub.lst之中。该文件有如下设置:
# Comments inside grub.lst ae done with a hash mark (#)
# default defines the default choice to boot without user
interaction
default 0
# Time out sets how long the boot menu will display before it loads default
timeout 30
# fallback provides a another choice in case default fails.
fallback 1
# hiddenmenu allows you to choose not to display the boot menu instead boot the
default
# hiddenmenu
# OS definitions begin with a title title is what is displayed on the screen to
the user
title openSUSE 10.2 – 2.6.18.8-0.3
# After the title description everything that follows is part of the same boot
loader until the title tag appears again.
# Common entries in linux are root, kernel, and initrd
# root defines the root partition and tries to get the size of the partition
hd0 partition 4
root (hd0,4)
# kernel attempts to load the kernel image off the root device
kernel /boot/vmlinuz-2.6.18.8-0.3-bigsmp root=/dev/hda5 vga=0x31a
resume=/dev/hda2 splash=silent showopts
# initrd Load an initial ramdisk (allows you to modify the kernel without a
recompile
initrd /boot/initrd-2.6.18.8-0.3-bigsmp
命令行选项
在出现引导菜单时,你可以设置命令行变量,比如以什么运行级别启动,或
者额外的选项。为了启动内核选择运行级别:
在图形化的菜单上高亮(highlight)你希望启动的内核
按下e键编辑选择的内核
在运行级别提示符下输入你想运行的运行级别(1-5或者紧急状态)
一旦返回到了grub菜单,按b键来启动内核与选择的运行级别
你可以在.读取更多关于grub的选项。
Linux加载器(LILO)
LILO(LInux LOader)是一种Linux通用加载程序。Lilo是较早期的加载器。它与GRUB遵从相同的过程(process)。不幸的是,在你每次想更改加载参数的时候,Lilo并不提供像grub修改MBR那样的命令。同样的,修改LILO能够导致系统引导失败。正是这种原因,GRUB已经成为Linux的标准引导程序。Lilo在/boot中保存一些文件。但其配置是在/etc/lilo.conf。为了重新安装lilo作为引导程序,执行:/sbin/lilo 命令行参数。在出现引导菜单时,选择你需要的运行级别并按键:
Ctrl-X to get boot:
输入linux运行级别
内核
一旦引导程序进入了第二个阶段,它将读取其配置文件来显示一个可用的内核菜单。一旦用户或者引导程序确定了加载的内核,阶段二将内核文件从/boot中加载。
一旦内核加载完成,首先初始化硬件。然后读取initrd镜像,该文件包含内核加载scsi设备与ext3文件系统的驱动。待initrd镜像完全加载后,加载程序将控制权交给内核文件。
内核创建并安装只读的root设备。此时,内核已经加载,但是由于还没有加载用户空间文件,你不能与之交互。这就是/sbin/init接管控制权后需要做的事情。
/sbin/init
其余的引导过程是通过init完成的,它提供用户环境。Init成为系统中所有其它进程的父进程或者祖先进程,其进程id总是为1。
Init首先运行/etc/rc.d/rc.sysinit脚本,该脚本启动交换区(swap),系统时钟,检查文件系统和其它多个进程。它通过读取/etc/inittab建立运行级别。
运行级别
A runlevel is a collection of scripts used to start applications and services used by a system. Linux supports multiple runlevels. You can change between runlevels very quickly on a Linux system dismounting file systems as you go. The configuration for the runlevels is done inside the /etc/inittab file. You can find the default runlevel inside inittab:
运行级别是一系列脚本的集合。该脚本用于启动系统需要的应用与服务。Linux支持多运行级别。在Linux系统上,你可以在不同的运行级别之间快速地切换。运行级别的配置文件在:/etc/inittab。你可以在其中发现默认的运行级别:
id:5:initdefault:
默认的运行级别是5,该级别是多用户图像化接口界面。Inittab也可以定义:
在运行级别/etc/init.d/boot之前执行的第一个脚本。
定义在每个运行级别中需要执行的RC脚本。
定义特殊的键盘命令。
为每一个运行级别执行getty程序。
/etc/init.d/boot文件定义了如下设置:
定义了终端大小与dimention。
初始化启动信息与颜色。
建立/proc /sys /dev /sys/kernel/debug。
启动用户定义脚本boot.local。
Linux默认的运行级别是:
Runlevel |
State |
0 |
Shutdown |
1 |
Single User Mode |
2 |
Multiuser without network |
3 |
Multiuser text based |
4 |
Unused |
5 |
Multiuser with Graphical X |
你可以通过执行下面的命令,快速地切换系统的运行级别:
init runlevel
每一个运行级别执行在/etc/init.d/rc_runlevel中的脚本。该脚本通常为/etc/init.d中脚本的符号连接。这些脚本至少可以使用两种参数:stop与start。
/etc/init.d/rc_runlevel中的连接包含以K开头的终止脚本以及以S开头的启动脚本。K(或者S)之后跟着两位数字,该数字表明该运行级别下脚本的执行顺序。例如:
# ls -al
total 8
drwxr-xr-x 2 root root 4096 Sep 2 21:52 .
drwxr-xr-x 11 root root 4096 Nov 16 20:25 ..
lrwxrwxrwx 1 root root 9 Sep 2 21:52 K02single
-> ../single
lrwxrwxrwx 1 root root 12 Sep 2 21:52 K13microcode
-> ../microcode
lrwxrwxrwx 1 root root 9 Sep 2 21:52 K13splash
-> ../splash
lrwxrwxrwx 1 root root 8 Sep 2 21:52 K21fbset
-> ../fbset
lrwxrwxrwx 1 root root 15 Sep 2 21:52 K21irq_balancer
-> ../irq_balancer
lrwxrwxrwx 1 root root 8 May 19 10:46 S01fbset ->
../fbset
lrwxrwxrwx 1 root root 15 May 19 10:45 S01irq_balancer ->
../irq_balancer
lrwxrwxrwx 1 root root 6 May 19 10:47 S09kbd ->
../kbd
lrwxrwxrwx 1 root root 12 May 19 10:51 S09microcode ->
../microcode
lrwxrwxrwx 1 root root 9 May 19 10:47 S09splash ->
../splash
lrwxrwxrwx 1 root root 9 May 19 10:47 S20single ->
../single
你可以看到运行级别1下有很多启动文件,且S09splash比S20single启动的早。
在Linux下,通过chkconfig向一个运行级别中自动添加条目非常简单。例如,如果我想查看/etc/init.d中的一个脚本是否在运行级别中启动,可以通过如下命令:
# chkconfig -l apache2
apache2 0:off 1:off 2:off 3:on 4:off
5:on 6:off
chkconfig也可以为指定的条目设定运行级别,通过如下命令:
#chkconfig service_name runlevel/runlevels
例如:
#chkconfig apache2 235
将在运行级别为2,3,5时,运行/etc/init.d下的apache2脚本。
你可以通过ln命令手动添加链接。单独运行chkconfig命令将显示当前运行级别下所有的脚本及其状态,或者 chkconfig –l将显示所有的运行级别。
2010, Joseph Griffiths. All rights reserved.
1、对部分笔误以及理解错误地方进行修改,红色字体标注。2012-09-24 20:45