Linux 启动详解¶
http://c.biancheng.net/view/1029.html
开机自检 POST
开机自检
主引导记录 MBR
主引导记录只有512个字节,位于0柱面,0磁道,1扇区。
它的主要作用是,告诉计算机到硬盘的那一个位置去找操作系统
启动管理器 Grub
Grub介绍
BIOS 在进行完成系统检测之后,就会找到第一个可以启动的设备,并读取该设备的 MBR(主引导记录)以及加载 MBR 中的 boot loader(启动引导程序),这个启动引导程序可以具有菜单功能、直接加载核心文件以及控制权移交的功能等。
另外,系统要借助启动引导程序,才能加载内核,那么问题来了,446 字节 MBR 怎么启动引导程序?
Linux 系统将启动引导程序的程序代码运行与配置数据加载分为以下 2 个阶段:
- 第一个阶段负责运行启动引导的主程序,
该主程序必须要被安装在启动区,或者是 MBR,或者是引导扇区;MBR 的容量实在太小啦,因此,MBR 或引导扇区通常只安装启动引导程序的最小主程序,并不安装相关的配置数据; - 第二个阶段是为主程序加载配置文件,
包括相关的环境参数文件(文件系统定义以及主要配置文件 menu.1st);
/boot/grub/目录分析
与 GRUB(启动引导程序)相关的配置文件,都放置在 /boot/grub 目录中
;# cd /boot/grub/
;# ll -h
总用量274K
device.map
;#GRUB中硬盘的设备文件名与系统的设备文件名的对应文件
iso9660_stage1_5 #iso9660文件系统的Stage 1.5文件
jfs_stage1_5 #JFS文件系统的Stage 1.5文件
ufs2_stage1_5 #UFS文件系统的Stage 1.
vstafs_stage1_5 #vstafs文件系统的Stage 1.5文件
xfs_stage1_5 #XFS文件系统的Stage 1.5文件
e2fs_stage1_5 #ext2/ext3文件系统的Stage 1.5文件
fat_stage1_5 #FAT文件系统的Stage 1文件
ffs_stage1_5 #FFS文件系统的Stage 1.5文件
minix_stage1_5 ;#MINIX文件系统的Stage 1.5文件
reiserfs_stage1_5 #ReiserFS文件系统的Stage 1.5文件
grub.conf
;#GRUB的配置文件
menu.lst ->./grub.conf ;#GRUB的配置文件。是grub.conf的软链接;
splash.xpm.gz #系统启动时,GRUB程序的背景图像
可以看到,这个目录中主要就是 GRUB 的配置文件和各种文件系统的 Stage1.5 文件。
GRUB磁盘分区表示法
Linux 系统分区的设备文件名的命名是有严格规范的,类似于 /dev/sda1 代表第一块 SCSI 硬盘的第一个主分区。但是在 GRUB 中分区的表示法却完全不同,采用了类似 hd(0,0) 的方式来表示分区。
其中:
hd 代表硬盘,不再区分是 SCSI ,IDE 接口;
第一个 0 代表 Linux 系统查找到的第一块硬盘,第二块硬盘为 2,以此类推;
第二个 0 代表这块硬盘的第一个分区,以此类推;
注意 GRUB 的表示方式只在 GRUB 的配置文件中生效,
一旦离开了 GRUB 的配置文件,就要使用 Linux 中的设备文件名来表示分区了。
注意: 逻辑分区不能占用主分区与扩展分区的分区号
Grub 3阶段加载
- stage 1:
存在启动硬盘的0柱面,0磁道,第一个扇区中,
即MBR中,MBR的前446个字节为以引导代码,也就是grub stage 1,
所谓的stage1 ,作用只有一个,就是找到grubstage1.5,然后将其加载的到内存 - stage 1.5:
用来识别常见的不同类型的文件系统,
从而找到/boot 目录所在分区对应的文件系统
有了boot 目录所在分钟的文件系统驱动后
/boot/grub/stage2 这一大的文件就可以直接操作了; - stage 2:
Grub 的真正核心程序,能让用户以菜单方式将操作系统加载、新增参数、修改选项。
然后由stage2 借助 grub.conf 再去引导系统启动;
加载过程
Grub 是引导操作系统的程序,它根据自己的配置文件,去引导内核,
当内核被加载到内存以后,内核会根据grub配置文件中的配置,
找到根分区所使用的文件系统对应的驱动,
通过根分区文件系统对应的驱动,挂载根分区,从而启动系统,
内核:/boot/vmlinuz-xxx
grub就是引导它,将其到内存;
内核挂载根分区前,必须获取到根分区文件系统的驱动:
/boot/initramfs-xxx
存在多分区时,内核需要grub指示
/boot/grub/grub.conf
/boot/grub/grub.conf(GRUB配置文件)
;# vi /boot/grub/grub.conf
default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
;#以上为GRUB的整体设置
title CentOS (2.6.32-279.el6.i686)
root (hdO,0)
kernel /vmlinuz-2.6.32-279.el6.i686 ro root=OOID=b9a7ala8-767f-4a87-8a2b-a535edb362c9 rd_NO_LUKS KEYBOARDTYPE=pc KEYTABLE=us rd_NO_MD crashkernel= auto LANG=zh_CN.UTF-8 rd_NO_LVM rd_NO_DM rhgb quiet
initrd /initramfs-2.6.32-279.el6.i686.img
前 4 行为 GRUB 的整体设置:
default=0:默认启动第一个系统。
timeout=5:等待时间,默认是 5 秒。结束后进入default指定的系统;
splashimage=(hd0,0)/gnjb/splash.xpm.gz:
用来指定 GRUB 启动时的背景图像的保存位置。
hiddenmenu:隐藏菜单。
只能看到读秒,而不能看到菜单;按任意键才能显示菜单
CentOS 系统的具体配置
title CentOS(2.6.32-279.d6.i686):title 就是标题的意思;
root(hd0,0):是指启动程序的保存分区。
kernel /vmlinuz-2.6.32-279.el6.i686 ro root=UUID=b9a7a1a8-767f-4a87-8a2b-a535edb362c9 rd_NO_LUKS KEYBOARDTYPE=pc KEYTABLE=us rd_NO_MD crashkernel=auto LANG=zh_CN.UTF-8 rd_NO_LVM rd_NO_DM rhgb quiet。其中:
- /vmlinuz-2.6.32-279.el6.i686:指定了内核文件的位置,这里的 / 是指 boot 分区。
- ro:启动时以只读方式挂载根文件系统,避免启动过程影响磁盘内的文件系统。
- root=UUID=b9a7a1 a8-767f-4a87-8a2b-a535edb362c9:指定根文件系统的所在位置。
以下禁用都只是在启动过程中禁用,是为了加速系统启动的:
rd_NO_LUKS:禁用 LUKS,LUKS 用于给磁盘加密。
rd_NO_MD:禁用软 RAID。
rd_NO_DM:禁用硬 RAID。
rd_NO_LVM:禁用 LVM。
除了以上这样,命令输出信息中还包含以下内容:
KEYBOARDTYPE=pc KEYTABLE=us:键盘类型。
crashkernel=auto:自动为crashkernel预留内存。
LANG=zh_CN.UTF-8:语言环境。
rhgb:(redhatgraphics boot)用图片来代替启动过程中的文字信息。
启动完成之后可以使用dmesg命令来查看这些文字信息。
quiet:隐藏启动信息,只显示重要信息。
initrd/initramfs-2.6.32-279.el6.i686.img:
指定了initramfs虚拟文件系统镜像文件的所在位置。
GRUB手动安装方法
grub-install 操作
;# grub-install [选项] 设备文件名
--root-directory=DIR:指定grub映像文件(主要是“stage 2”)的存放位置,
默认为当前系统根目录。(不指定时就是当前系统的根目录)
--grub-shell 使用指定文件作为GRUB Shell
--no-floppy 不探测任何软盘驱动器
功能:
grub-install会在指定的目录下创建boot/grub/的层级目录,
并将生成的“stage 2”置于DIR/boot/grub/下
Demo:
mount /dev/sdb1 /mnt
ls /mnt
grub-install --root-directory=/mnt /dev/sdb
ls /mnt/boot/grub/
命令标准格式:
;# gmb-install /dev/sda
;#因为默认 GRUB 就是安装到 /dev/sda 中的,
所以不需要指定 --root-directory 选项
GRUB 软件不存在时
如果系统中没有 grul-install 命令,则说明没有 GRUB 软件,
这时可以源码包安装,也可以 RPM 包安装。
RPM 包的安装命令如下:
;# rpm -ivh /mnt/cdrom/Packages/ grub-0.97-77.el6.i686.rpm
系统中没有GRUB,需要手工安装
Linux 系统中没有安装 GRUB,我们需要重新安装 GRUB;或者打算把不是启动分区的某个分区变成启动分区。比如我的系统中新建了一个分区 /dev/sdb1,并挂载到了 /tdisk/ 目录上,我们查看一下新建立的分区,
;# df
文件系统 1K-块 已用 可用 已用% 挂载点
/dev/sda3 19923216 1787736 17123412 10% /
tmpfs 312672 0 312672 0% /dev/shm
/dev/sda1 198337 26359 161738 15% /boot
/dev/sdb1 2071384 68632 1897528 4% /tdisk
操作目标: /boot 分区可以启动系统,/tdisk 分区也可以启动系统
使用 grul-install 命令在要启动的分区中安装 GRUB 相关文件
在 /boot 分区中有一个 /boot/grub/ 目录,
这个目录中保存的就是 GRUB 的相关文件(如文件系统的 Stage 1.5 文件)。
查看一下 /boot 分区中的这个目录,
ls /boot/grub/
但在 /tdisk 目录中并不存在这些文件,
所以第一步就是要在 /tdisk 目录中安装这些 GRUB 相关文件,
具体采用 grub-install 命令。
把 GRUB 安装到 /tdisk 分区:
;# grub-install --root-directory=/tdisk /dev/sdb1
ll /tdisk/boot/grub/
GRUB 的相关文件已经安装到 /tdisk/boot/grub/ 目录中。
当然,这些文件还是需要 GRUB 的配置文件来读取的。
/tdisk/boot/grub/ 目录中没有 grub.conf 或 menu.lst 配置文件,
这些配置文件还是需要依赖 /boot/grub/ 目录的。
修改GRUB的配置文件
/boot/grub/grub.conf:
[root@localhost ~]# vi /boot/gmb/grub.conf
default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.32-279.el6.i686)
root(hd0,0)
kemel/vmlinuz-2.6.32-279.el6.i686 ro
root=UUID=b9a7a1 a8-767f-4a87-8a2ba535edb362c9 rd_NO_LUKS
KEYBOARDTYPE=pc KEYTABLE=us rd_NO_MD crashkemel=auto
LANG=zh_CN.UTF-8 rd_NO_LVM rd_NO_DM rhgb quiet
initrd/initramfs-2.6.32-279.el6.i686.img title CentOS tdisk #给自己这个启动分区起个名字吧
root(hd1,0)
;#注意启动分区的位置是/dev/sdb1,也就是/tdisk目录
chainloader +1
;#使用当前分区所在的启动扇区启动系统
在 title CentOS tdisk 段中不能指定内核镜像和 initramfs 虚拟文件系统,因为在 /tdisk/boot/ 目录中只有 grub 目录,而没有内核镜像文件和 initramfs 虚拟文件系统的镜像文件,所以需要通过 chainloader 来调用启动扇区。
安装GRUB到/dev/sdb1分区的启动扇区中
刚刚通过 GRUB 配置文件中的 chainloader 来调用启动扇区,但是 /dev/sdb1 这个分区是新建立的,它的启动扇区中并没有 GRUB 程序。所以最后一步就是要在 /dev/sdb1 分区的启动扇区中安装 GRUB。
这时就要利用 GRUB 交互模式:
[root@localhost boot]# grub
;#启动进入GRUB交互模式
grub> root (hd0,0)
root (hd0,0)
Filesystem type is ext2fs, partition type 0x83
;#设定GRUB的主目录,这里只能是(hd0,0),因为内核和虚拟文件系统安装在/dev/sdal中,也就是/boot分区中
grub> find /boot/gnab/stagel
find /boot/grub/stagel (hd1,0)
;#查询一下Stage 1安装的分区
;#好像有一点问题,我们在/boot和/tdisk分区中都安装了GRUB,只看到了/tdisk分区
grub> find /grub/stagel
find /grub/stagel (hd0,0)
;#只有这样才能看到/boot分区中的Stage 1。因为/boot分区是一个单独分区。上面能看到是因为/tdisk才是分区,而/boot/grub/只是/tdisk分区中的目录
grub> find /vmlinuz-2.6.32-279.el6.1686
find /vmlinuz-2.6.32-279.el6.i686 (hd0,0)
;#能够查到内核位置。注意不能通过/boot/vmlinuz-2.6.32-279.el6.i686查询,还是因为/boot是单独分区。但是这次/tdisk分区中没有内核
grub> setup (hd1,0)
setup (hd1,0)
Checking if "/boot/grub/stagel" exists... no
Checking if "/grub/stagel" exists... yes
Checking if "/grub/stage2" exists... yes
Checking if "/grub/e2fs_stage1_5" exists... yes
Running "embed /grub/e2fs_stage1_5 (hd1,0)"... failed (this is not fatal)
Running "embed /grub/e2fs_stage1_5 (hd0,0)"... failed (this is not fatal)
Running "install /grub/stagel d (hd1,0) /grub/stage2 p /grub/grub.conf "...
succeeded
Done
;#在/tdisk分区的启动扇区中安装GRUB吧。虽然有两个failed,但这只是两个文件系统的Stage 1.5文件没有安装,并不影响
grub> quit
;#退出GRUB交互界面
这时 GRUB 安装完成了,可以重新启动试验一下了。重启后可以看到图 1 所示的界面。
重新安装GRUB
需要安装 GRUB 的相关文件和修改 GRUB 的配置文件,也就是第一种情况的第一步和第二步不需要执行,因为这是已经安装和修改好的。只需要执行第三步,覆盖 MBR 中的启动引导程序即可。
sh-4.1#grub
;#启动GRUB交互界面。
因为系统是从光盘启动的,所以环境变量没有生效;导致提示符不一样;
grub>root (hd0.0)
;#同样需要设置GRUB的主目录
grub>setup (hd0)
;#直接把GRUB安装到MBR中,所以不需要指定分区
grub>quit
;#退出
加载内核
根据grub设定的内核映像所在路径,系统读取内存映像,并进行解压缩操作。此时,屏幕一般会输出“Uncompressing Linux”的提示。当解压缩内核完成后,屏幕输出“OK, booting the kernel”。
系统将解压后的内核放置在内存之中,并调用start_kernel()函数来启动一系列的初始化函数并初始化各种设备,完成Linux核心环境的建立。至此,Linux内核已经建立起来了,基于Linux的程序应该可以正常运行了。
/boot文件夹内容:
config-2.6.32-279.el6.i686
;#内核的配置文件,内核编译时选择的功能与模块
efi
;#可扩展固件接口,是英特尔为全新PC固件的体系结构、接口和服务提出的建议标准
grub
;#启动引导程GTUB的数据目录
initramfe-2.6.32-279.el6.i686.img
;#虚拟文件系统(CentOS 6.x 中用initramfs替代了initrd,但功能是一样的)
lost+found
;#故boot分区的备份目录
symvers-2_6.32-279.el6.i686.gz
;#模块符号信息
System.map-2.6.32-279.el6.i686
;#内核功能和内存地址的对应列表
vmlinuz-2.6.32-279.el6.i686
;#用于启动的Linux内核。这个文件是一个压缩的内核镜像
内核与模块 /lib/modules
Linux 把不重要的功能编译成内核模块,在需要时再调用,从而保证了内核不会过大。
在多数 Linux 中,都会把硬件的驱动程序编译为模块, 这些模块保存在 /lib/modules 目录中。常见的 USB、SATA 和 SCSI 等硬盘设备的驱动,还有一些特殊的文件系统(如 LVM、RAID 等)的驱动,都是以模块的方式来保存的。
initramfs 虚拟文件系统
initramfs 虚拟文件系统的作用
如果 Linux 安装在 IDE 硬盘之上,并且采用的是默认的 ext3/4 文件系统,那么内核启动后加载根分区和模块的加载都没有什么问题,系统会顺利启动。但是,如果 Linux 安装在 SCSI 硬盘之上,或者采用的是 LVM 文件系统,那么内核在加载根目录之前是需要加载 SCSI 硬盘或 LVM 文件系统的驱动的。
SCSI 硬盘和 LVM 文件系统的驱动都放在硬盘的 /lib/modules 目录中,既然内核没有办法识别 SCSI 硬盘或 LVM 文件系统,那怎么可能读取 /lib/modules 目录中的驱动呢?Linux 给出的解决办法是使用 initramfs 这个虚拟文件系统来处理这个问题。
initramfe虚拟文件系统
CentOS 6.x 中使用 initramfs 虚拟文件系统取代了 CentOS 5.x 中的 initrd RAM Disk。它们的作用类似,可以通过启动引导程序加载到内存中,然后会解压缩并在内存中仿真成一个根目录,并且这个仿真的文件系统能够提供一个可执行程序,通过该程序来加载启动过程中所需的内核模块,比如 USB、SATA. SCSI 硬盘的驱动和 LVM、RAID 文件系统的驱动。
也就是说,通过 initramfs 虚拟文件系统在内存中模拟出一个根目录,然后在这个模拟根目录中加载 SCSI 等硬件的驱动,就可以加载真正的根目录了,之后才能调用 Linux 的第一个进程 /sbin/init。
initramfs 是为了在内核中建立一个模拟根目录,这个模拟根目录是为了可以调用 USB、SATA、SCSI、LVM、RAID 等硬盘接口或文件系统的驱动模块,加载了驱动模块后才可以加载真正的系统根目录。
initramfe镜像内容解读
initramfs 是一个仿真根目录,查询其内容:
;# mkdir /tmp/initramfs
;# 建立测试目录
;# cp/boot/initramfs-2.6.32-279.el6.i686.img/tmp/initramfs/
;# 复制initramfs文件
;# cd /tmp/initramfs/
;# file
initramfs-2.6.32-279.el6.i686.img
initramfe-2.6.32-279.el6.i686.img:gzip compressed
;#查看文件类型,发现这个文件是一个使用gzip命令打包的压缩包
;# mv initramfs-2.6.32-279.el6.i686.imginitramfs-2.6.32-279.el6.i686.img.gz
;#修改文件的扩展名为.gz
;# gunzip initramfs-2.6.32-279.el6.i686.img.gz
;# ls
initramfs-2.6.32-279.el6.i686.img
;# file
initramfs-2.6.32-279.el6.i686.img
initramfe-2.6.32-279.el6.i686.img: ASCII cpio archive (SVR4withnoCRC)
;##查看文件类型,使用cpio命令的压缩文件
;# cpio -ivcdu <initramfs-2.6.32-279.el6.i686.img
;#解压缩
;#ll
/sbin/init初始化系统环境
在内核加载完毕,并完成硬件检测与驱动程序加载后,此时主机硬件已经准备完毕,内核会主动呼叫第一个进程,也就是 /sbin/init,此配置文件最主要的功能就是准备软件执行的环境,包括系统的主机名、网络设定、语言、文件系统格式及其他服务的启动等。
由 /sbin/init 进程可通过 /etc/init/rcS.conf 配置文件,分别找到 /etc/rc.d/rc.sysinit 配置文件和 /etc/inittab 配置文件,前者用于初始化系统,配置计算机的初始环境;后者用于确定系统的默认运行级别。
/etc/rc.d/rc.sysinit 配置文件
- 获得网络环境和主机类型;
- 测试设备:
除了挂载内存设备 /proc 之外,
主动侦测系统上是否具有 usb 设备,
如有,则会主动加载 usb 的驱动程序,并尝试挂载 usb 文件系统; - 开机启动画面 Plymouth(代替了以往的 RHGB);
- 判断是否启用 SELinux;
- 显示开机过程中的欢迎画面;
- 初始化硬件;
- 用户自定义模块的加载,
用户在 /etc/sysconfig/modules/*.modules 中的自订的模块,
则此时会被加载到系统当中; - 配置内核的参数,
系统会主动去读取 /etc/sysctl.conf 这个文件的配置参数。 - 设置主机名。
- 同步存储器。
- 设备映射器及相关的初始化。
- 初始化软件磁盘阵列 (RAID)。
- 初始化 LVM 的文件系统功能。
- 检验磁盘文件系统 (fsck)。
- 设置磁盘配额 (quota)。
- 重新以可读写模式挂载系统磁盘。
- 更新 quota (非必要)。
- 启动系统虚拟随机数生成器。
- 配置机器(非必要)。
- 清除开机过程中的临时文件。
- 创建 ICE 目录。
- 启动交换分区(swap)。
- 将开机信息写入 /var/log/dmesg 文件中。
在以上过程中,比较值得注意的是自定模块的加载。在 CentOS 中,如果我们想要加载核心模块的话,就可以将整个模块写入到 /etc/sysconfig/modules/*.modules 中。当然,此过程并不是必须的,通常我们的默认模块已经够用,除非对硬件太新,不得不自己加载模块,否则无需刻意添加任何模块。
可以看到,/etc/rc.d/rc.sysinit 配置文件已经将基本的系统配置数据都写好了,我们可以查询 /var/log/dmesg 文件或使用 dmesg 命令查看系统在启动时到底发生了什么。当然,我们也可以通过这个命令来看看 Linux 服务器的硬件信息。
/etc/inittab:设置/修改系统默认运行级别
/etc/inittab 配置文件只能用来设置系统的默认运行级别。
可以使用 runlevel 命令来查看系统的运行级别,命令如下:
;# runlevel
返回两个数组,后者3 代表当前级别。前者是进入当前级别前的级别,
如为"N" 就是 None 代表系统是开机直接进入 3 运行级别的,没有上一个运行级别。
系统默认运行级别:
...
id:3:initdefault:
;#这就是系统的默认运行级别,也就是系统开机后直接进入哪个运行级别
;这里的默认运行级别只能写 3 或 5
init进程执行rc.sysinit
在设定了运行等级后,Linux系统执行的第一个用户层文件就是 /etc/rc.d/rc.sysinit脚本程序,它做的工作非常多,包括设定PATH、设定网络配置(/etc/sysconfig/network)、启动swap分区、设定/proc等等。如果你有兴趣,可以到/etc/rc.d中查看一下rc.sysinit文件。
启动内核模块
执行不同运行级别的脚本程序
执行/etc/rc.d/rc.local
用户进行个性化的地方。你可以把你想设置和启动的东西放到这里。
在 /etc/rc[06].d/ 目录中的程序启动之后,系统的启动就已经完成。不过,我们总有一些程序是需要在系统启动之后随着系统一起启动的。这时我们并不需要自己把需要启动的服务链接到 /etc/rc3.d/ 目录中,因为系统给我们准备了 /etc/rc.d/rc.local 配置文件。
这个配置文件会在用户登陆之前读取,这个文件中写入了什么命令,在每次系统启动时都会执行一次。也就是说,如果有任何需要在系统启动时运行的工作,则只需写入 /etc/rc.d/rc.local 配置文件即可。
rc.local Demo:
;# ll /etc/rc.local Irwxrwxrwx. 1 root root 13 4月10 21:46 /etc/rc.local -> rc.d/rc.local
有一个链接文件,两个文件修改哪一个都可以¶
;# vi /etc/rc.d/rc.local
;#!/bin/sh
;#
;#This script will be executed after all the other init scripts.
;#You can put your own initialization stuff in here if you don't
;#want to do the full Sys V style init stuff.
touch /var/lock/subsys/local
;#默认会touch这个文件,每次系统启动时都会touch这个文件,
;#这个文件的修改时间就是系统的启动时间
/etc/rc.d/init.d/httpd start
;#如果写入RPM包安装的apache服务的启动命令,
;#apache服务就会在开机时自动启动