分类: LINUX
2011-09-15 19:26:10
开机——很多人觉得很简单的事情,只要按下电源开关,然后系统就会自然启动,没有什么需要学习的。其实不然,如果系统没有什么问题,可以正常登陆的时候,当然开机很简单。但更多的时候,我们需要知道当机子不能正常开机的如何解决,这才是我们学习的目的。
那么我们就来分析一下的开机流程
简单来说,系统开机的过程可以汇整成下面的流程:
1.加载BIOS的硬件信息、进行自我测试,并依据设定获得第一个可开机的设备;
2.读取并执行第一个开机设备内的 Loader(grub等程序);
3.依据boot loader的设置加载Kernel,Kernel会开始检测硬件与加载驱动程序;
4.内核启动init
5.系统初始化:(/etc/init/rcS.conf exec /etc/rc.d/rc.sysinit)
6.init找到/etc/inittab文件,确定默认的运行级别(X) (/etc/init/rcS.conf exec telinit $runlevel)
7.触发相应的runlevel事件(/etc/init/rc.conf exec /etc/rc.d/rc $RUNLEVEL)
8.开始运行/etc/rc.d/rc,传入参数X
9./etc/rc.d/rc脚本进行一系列设置,最后运行相应的/etc/rcX.d/中的脚本
10./etc/rcX.d/中的脚本按事先设定的优先级依次启动
11.最后执行/etc/rc.d/rc.local
12.加载终端或X-Window接口
BIOS, boot loader与kernel加载 BIOS, 开机自检与MBR
在个人计算机架构下,你想要启动整部系统首先就得要让系统去加载BIOS(Basic Input Output
System),并通过BIOS程序去加载CMOS的信息,并且藉由CMOS内的设定值取得主机的各项硬件配置信息(如CPU与接口设备的沟通频率、开机
设备的搜寻顺序、硬盘的大小与类型、 系统时间、各周边总线的是否启动Plug and Play (PnP, 即插即用设备)、
各接口设备的I/O地址、以及与CPU沟通的IRQ(Interrupt ReQuest)中断等等)。
在取得这些信息后,BIOS还会进行开机自检(Power-on Self Test, POST)。 然后开始执行硬件检测的初始化,并设定PnP设备,之后再定义出可开机的设备顺序, 接下来就会开始进行开机设备的数据读取了(MBR相关的任务开始)。
由于不同的操作系统的文件系统格式不相同,因此我们必须要以一个开机管理程序来处理内核文件的加载问题,因此这个开机管理程序就被称为Boot
Loader。这个Boot Loader程序安装在开机设备的第一个扇区(sector)内,也就是我们一直谈到的 MBR (Master Boot
Record,主要启动记录区)。下图就是MBR的组成图
Boot Loader最主要功能是认识操作系统的文件格式并加载内核到主存储器中去执行。 由于不同操作系统的文件格式不一致,因此每种操作系统都有自己的boot loader,用自己的loader才有办法载入内核文件。
问题就来了:如果在一部主机上面安装多种不同的操作系统。 既然必须要使用自己的 loader才能够加载属于自己的操作系统内核,而系统的MBR只有一个,那怎么会有办法同时在一部主机上面安装多系统呢?下面就来看看如何实现的。
每个文件系统(filesystem, 或者是 partition) 都会保留一块启动扇区 (boot sector) 提供操作系统安装
boot loader,而通常操作系统默认都会安装一份 loader 到他根目录所在的文件系统的boot sector
上。如果我们在一部主机上面安装 Windows 与 Linux 后,该 boot sector, boot loader 与 MBR
的相关性会有点像下图:
如上图所示,每个操作系统默认是会安装一套 boot loader 到他自己的文件系统中
(就是每个filesystem左下角的方框),而在Linux 统安装时,你可以选择将 boot loader
安装到MBR中,也可以选择不安装。如果选择安装到MBR的话,那理论上在 MBR与boot sector都会保有一份boot
loader程序。至于Windows安装时,他预设会主动的将MBR与boot sector都装上一份 boot
loader。所以,你会发现安装多操作系统时,你的MBR常常会被不同的操作系统的 boot loader所覆盖。
现在还是没有解决我们上面的问题。虽然各个操作系统都可以安装一份 boot loader 到他们的boot
sector中,这样操作系统可以通过自己的boot loader 来加载内核了。问题是系统的MBR只有一个,要怎么执行 boot
sector里面的 loader呢?
下面就来看看boot loader主要的功能
提供选项:用户可以选择不同的开机项目,这是多重引导的重要功能。
载入内核文件:直接指向可开机的程序区段来开始操作系统;
转交其他loader:将开机管理功能转交给其他loader负责。
由于具有选项功能,因此我们可以选择不同的内核来开机。而由于具有控制权转交的功能,因此我们可以加载其他 boot
sector内的loader。不过 Windows的loader 预设不具有控制权转交的功能,因此你不能使用 Windows 的 loader
来加载 Linux 的 loader。这也是特别强调先装Windows再装Linux的缘故。
如上图所示,MBR使用Linux的grub 这个开机管理程序,并且里面已经有了三个选项,第一个选项可以直接指向Linux 的内核文件并且直接加载内核来开机;第二个选项可以将开机管理程控权交给 Windows 来管理,此时 Windows 的 loader 会接管开机流程,这个时候他就能够启动 windows 了。第三个选项则是使用 Linux 在 boot sector 内的开机管理程序,此时就会跳出另一个grub的选项。
重点就是要知道“boot loader的功能就是加载kernel文件”
加载内核检测硬件与initrd的功能由boot loader的管理而开始读取内核文件后,接下来,Linux 就会将内核解压缩到主存储器当中, 并且利用内核的功能,开始测试与驱动各个周边设备,包括储存设备、CPU、网卡、声卡等等。 此时 Linux 内核会以自己的功能重新检测一次硬件,而不一定会使用 BIOS 检测到的硬件信息。也就是说,内核此时才开始接管 BIOS 后的工作。 内核档案一般来说,他会被放置到 /boot 里面,并且取名为 /boot/vmlinuz。
[root@yufei ~]# ls –format=-column /boot
config-2.6.32-71.el6.i686 <===系统kernel的配置文件,内核编译完成后保存的就是这个配置文件
efi <===Extensible Firmware Interface(EFI,可扩展固件接口)是 Intel 为全新类型的 PC 固件的体系结构、接口和服务提出的建议标准。
grub <===开机管理程序grub相关数据目录
initramfs-2.6.32-71.el6.i686.img<===虚拟文件系统文件(用initramfs代替了initrd,他们的目的是一样的,只是本身处理的方式有点不同)
initrd-2.6.32-71.el6.i686.img <===此文件是linux系统启动时的模块供应主要来源,initrd的目的就是在kernel加载系统识别cpu和内存等内核信息之后,让系统进一步知道还有那些硬件是启动所必须使用的;
symvers-2.6.32-71.el6.i686.gz <===模块符号信息
System.map-2.6.32-71.el6.i686 <===是系统kernel中的变量对应表;(也可以理解为是索引文件)
vmlinuz-2.6.32-71.el6.i686 <===系统使用kernel,用于启动的压缩内核镜像, 它也就是/arch/
Linux内核是可以通过动态加载内核模块的 (就请想成驱动程序即可),这些内核模块就放在 /lib/modules/目录内。由于模块放到磁盘根目录内(这就是为什么/lib不可以与 / 分别放在不同的分区原因), 因此在开机的过程中内核必须要挂载根目录,这样才能够读取内核模块提供加载驱动程序的功能。 而且为了担心影响到磁盘内的文件系统,开机过程中根目录是以只读的方式来挂载的。
了解/boot/initrd这个文件虚拟文件系统(Initial RAM Disk) 一般使用的文件名为 /boot/initrd ,这个文件的作用是,能够通过 boot loader 来加载到内存中, 然后这个文件会被解压缩并且在内存当中仿真
成一个根目录, 且此仿真在内存当中的文件系统能够提供一支可执行的程序,通过该程序来加载开机过程中所最需要的内核模块,通常这些模块就是 USB,
RAID, LVM, SCSI 等文件系统与磁盘接口的驱动程序。等载入完成后,会帮助内核重新呼叫 /sbin/init
来开始后续的正常开机流程。
下面让我们来看看initrd文件的具体内容,
[root@yufei ~]# mkdir tmp
[root@yufei ~]# cd tmp/
[root@yufei tmp]# cp /boot/initrd-2.6.32-71.el6.i686.img ./
[root@yufei tmp]# file initrd-2.6.32-71.el6.i686.img
initrd-2.6.32-71.el6.i686.img: gzip compressed data, from Unix, last modified: Thu Dec 16 00:29:07 2010, max compression
我们可以看到,这个文件是GZIP压缩的文件,我们把后缀改成gz,然后再进行解压
[root@yufei tmp]# mv initrd-2.6.32-71.el6.i686.img initrd-2.6.32-71.el6.i686.gz
[root@yufei tmp]# gunzip initrd-2.6.32-71.el6.i686.gz
解压后,我们再来看看这个文件是什么类型的
[root@yufei tmp]# file initrd-2.6.32-71.el6.i686
initrd-2.6.32-71.el6.i686: ASCII cpio archive (SVR4 with no CRC)
是cpio压缩成的文件
[root@yufei ~]# cpio -iv < initrd-2.6.32-71.el6.i686
[root@yufei tmp]# ls -l
总用量 30084
drwxr-xr-x. 2 root root 4096 12月 30 19:56 bin
drwxr-xr-x. 2 root root 4096 12月 30 19:56 cmdline
drwxr-xr-x. 3 root root 4096 12月 30 19:56 dev
-rw-r–r–. 1 root root 18 12月 30 19:56 dracut-004-32.el6
drwxr-xr-x. 2 root root 4096 12月 30 19:56 emergency
drwxr-xr-x. 7 root root 4096 12月 30 19:56 etc
-rwxr-xr-x. 1 root root 8088 12月 30 19:56 init
drwxr-xr-x. 2 root root 4096 12月 30 19:56 initqueue
drwxr-xr-x. 2 root root 4096 12月 30 19:56 initqueue-finished
drwxr-xr-x. 2 root root 4096 12月 30 19:56 initqueue-settled
-rw-r–r–. 1 root root 30709760 12月 30 19:54 initrd-2.6.32-71.el6.i686
drwxr-xr-x. 9 root root 4096 12月 30 19:56 lib
drwxr-xr-x. 2 root root 4096 12月 30 19:56 mount
drwxr-xr-x. 2 root root 4096 12月 30 19:56 pre-pivot
drwxr-xr-x. 2 root root 4096 12月 30 19:56 pre-trigger
drwxr-xr-x. 2 root root 4096 12月 30 19:56 pre-udev
drwxr-xr-x. 2 root root 4096 12月 30 19:56 proc
drwxr-xr-x. 2 root root 4096 12月 30 19:56 sbin
drwxr-xr-x. 2 root root 4096 12月 30 19:56 sys
drwxr-xr-x. 2 root root 4096 12月 30 19:56 sysroot
drwxr-xr-x. 2 root root 4096 12月 30 19:56 tmp
drwxr-xr-x. 6 root root 4096 12月 30 19:56 usr
drwxr-xr-x. 3 root root 4096 12月 30 19:56 var
看到结果了吧,和我们系统中的/是不是很类似。有兴趣的朋友,慢慢了解吧!哈哈……
在内核加载完毕、进行完硬件检测与驱动程序加载后,此时主机硬件已经准备就绪了,这时候内核会主动的呼叫第一支程序,那就是 /sbin/init
/sbin/init 最主要的功能就是准备软件执行的环境,包括系统的主机名、网络设定、语言、文件系统格式及其他服务的启动等。 而所有的动作都会通过 init的配置文件/etc/inittab来规划,而inittab 内还有一个很重要的设定内容,那就是默认的 runlevel (开机运行级别)。
先来看看运行级别Run levelLinux就是通过设定run level来规定系统使用不同的服务来启动,让Linux的使用环境不同。我们来看看这个inittab文件里面的支持级别(系统里面的,和以前的其它版本有很大的差别)
[root@yufei ~]# vim /etc/inittab
……
17 # Default runlevel. The runlevels used are:
18 # 0 – halt (Do NOT initdefault to this)
19 # 1 – Single user mode
20 # 2 – Multiuser, without NFS (The same as 3, if you do not have netwo rking)
21 # 3 – Full multiuser mode
22 # 4 – unused
23 # 5 – X11
24 # 6 – reboot (Do NOT set initdefault to this)
……
0 – halt (系统直接关机)
1 – single user mode (单人维护模式,用在系统出问题时的维护)
2 – Multi-user, without NFS (类似底下的 runlevel 3,但无 NFS 服务)
3 – Full multi-user mode (完整含有网络功能的纯文本模式)
4 – unused (系统保留功能)
5 – X11 (与 runlevel 3 类似,但加载使用 X Window)
6 – reboot (重新启动)
0, 4, 6 不是关机、重新启动就是系统保留的,所以不能将预设的run level设定为这三个值。
RHEL6系统上的这个文件和以前的版本有很大的差别,目前这个文件只能设置运行级别,其它的相关配置文件,在此文件中已经做了说明如:
System initialization is started by /etc/init/rcS.conf
Individual runlevels are started by /etc/init/rc.conf
Ctrl-Alt-Delete is handled by /etc/init/control-alt-delete.conf
Terminal gettys are handled by /etc/init/tty.conf and /etc/init/serial.conf,with configuration in /etc/sysconfig/init.
更多的内容,大家可以到/etc/init/里面看看
从这个文件我们已经看出,红帽已经使用新的Upstart启动服务来替换以前的init,想了解更多,请参考
在RHEL6的版本中,我们可以把/etc/init/这个目录里面的内容,看成是以前/etc/inittab这个文件里的拆分。
下面是RHEL6上面Upstart大致的一个启动过程:1.内核启动init
2.系统初始化:(/etc/init/rcS.conf exec /etc/rc.d/rc.sysinit)
3.init找到/etc/inittab文件,确定默认的运行级别(X) (/etc/init/rcS.conf exec telinit $runlevel)
4.触发相应的runlevel事件(/etc/init/rc.conf exec /etc/rc.d/rc $RUNLEVEL)
5.开始运行/etc/rc.d/rc,传入参数X
6./etc/rc.d/rc脚本进行一系列设置,最后运行相应的/etc/rcX.d/中的脚本
7./etc/rcX.d/中的脚本按事先设定的优先级依次启动
8.最后执行/etc/rc.d/rc.local
9.加载终端或X-Window接口
想要了解更多的内容,请大家打开/etc/init/这个目录里面的文件看看。
vim /etc/rc.sysinit
1、获得网络环境
2、挂载设备
3、开机启动画面Plymouth(取替了过往的 RHGB)
4、判断是否启用SELinux
5、显示于开机过程中的欢迎画面
6、初始化硬件
7、用户自定义模块的加载
8、配置内核的参数
9、设置主机名
10、同步存储器
11、设备映射器及相关的初始化
12、初始化软件磁盘阵列(RAID)
13、初始化 LVM 的文件系统功能
14、检验磁盘文件系统(fsck)
15、磁盘配额(quota)
16、重新以可读写模式挂载系统磁盘
17、更新quota(非必要)
18、启动系统虚拟随机数生成器
19、配置机器(非必要)
20、清除开机过程当中的临时文件
21、创建ICE目录
22、启动交换分区(swap)
23、将开机信息写入/var/log/dmesg文件中
这个文件里面的许多预设配置文件在/etc/sysconfig/这个目录当中,要想更多的系统启动信息,大家可以到/var/log/dmesg文件中查看,也可以用dmesg命令来查看。
经过 /etc/rc.sysinit
的系统模块与相关硬件信息的初始化后,我们的RHEL6系统应该已经能顺利工作了。但我们还需要启动一些为我们提供服务的服务。这个时候,依据在/etc
/inittab里面run level的设定值,就可以来决定启动的服务项目了。
大家可以打开/etc/rc这个文件来研究
我们以运行级别3来说明
ls /etc/rc3.d/
在这个目录下的文件主要具有2个特点:
1、全部以Sxx或Kxx(xx为数字)开头
2、全部是连结文件,连结到/etc/init.d/
现在来说明一下这些的目的
S表示启动服务
K表示停止服务
后面的数字是启动的先后顺序
我们以S00microcode_ctl来举例
[root@yufei ~]# ls -l /etc/rc3.d/S00microcode_ctl
lrwxrwxrwx. 1 root root 23 12月 15 22:40 /etc/rc3.d/S00microcode_ctl -> ../init.d/microcode_ctl
他的意思就是
S00microcode_ctl=/etc/init.d/microcode_ctl start
而且是第一个启动的服务。这样我想大家应该明白了吧。
上面讲到的都是一些系统服务,大家知道,我们的Linux系统容许安装其它的软件来提供服务,那我想要自己安装的服务也要在开机启动,那怎么办,没 有关系,找 /etc/rc.local 来完成。这就是我们要讲的用户自定义开机启动脚本。我们只要把想启动的脚本写到这个文件中,开机就能启动了,注意一点,写在这里面的脚本要使用绝对路径。
加载终端或X-Window接口在完成了系统所有服务的启动后,接下来Linux就会启动终端或者是X Window来等待使用者登陆了!
在/etc/init/start-ttys.conf中我们可以看到有6个纯文本终端(tty[1-6])。我们可以用[Ctrl+Alt+F1~F6]来切换这些终端。如果要切换到X window终端我们可以用[Ctrl+Alt+F7]
注:虚拟机个与真实的环境还是有区别的,有很多快捷键是冲突的,所以这个需要自行实验获得。更详细的分析,请看!感谢ice360的支持。
通过上面的学习,我们知道run level是在/etc/rc.sysinit 执行完毕之后,而run level
的不同之处仅是/etc/rc[0-6].d 里面启动的服务不同而已。如果我们切换run
level只需要停止与启动一些服务就OK,那么要如何来实现,其它很简单,用init n(数字)来切换。
比如说,我们要把运行级别5换成3,我们就用init 3来实现,切换的时候系统做了哪些事,我们来看看
先对比 /etc/rc3.d/ 及 /etc/rc5.d 内的 K 与 S 开头的文件;
对比/etc/rc3.d/ 内有多余的 K 开头文件则予以关闭;
对比/etc/rc3.d/ 内有多余的 S 开头文件则予以启动;
两个 run level 都存在的服务就不会被关闭;
是不是很简单,而且还不需要重新启动。如果说我想关机或重新启动系统,是不你已经想到了如何通过init来实现。哈哈……
cat /etc/init/start-ttys.conf
#
# This service starts the configured number of gettys.
start on stopped rc RUNLEVEL=[2345]
env ACTIVE_CONSOLES=/dev/tty[1-6] # 定义了6个终端(terminal)
env X_TTY=/dev/tty1 # 定义了tty1为X终端
task
script
. /etc/sysconfig/init
for tty in $(echo $ACTIVE_CONSOLES) ; do
[ "$RUNLEVEL" = "5" -a "$tty" = "$X_TTY" ] && continue
# 如果运行级别为5, 以及终端为tty1的时候, 不执行接下来的命令(重新开始for循环)
# 如果运行级别为5, 启动时upstart(init)会读取/etc/init/prefdm.conf配置文件来启动X-window.
# prefdm.conf里面会执行/etc/X11/prefdm
# 关于/etc/X11/prefdm脚本, 网上有一篇文章: http://www.cublog.cn/u/12066/showart_485334.html
initctl start tty TTY=$tty
done
end script
图形终端与字符终端之间,以及字符终端之间可以使用 Ctrl+Alt+F[1-6] 来切换, 其中Ctrl+Alt+F1为图形终端
另外, 字符终端下切换也可以直接使用Alt+F[1-6]
#!/bin/sh
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin # 注释 :设置 PATH 变量
# shut down any graphical boot that might exist # 注释 :关闭任何图形界面引导程序
if [ -x /usr/bin/rhgb-client ]; then # 注释 :如果存在该文件且可执行,
/usr/bin/rhgb-client -quit # 则执行该命令,并带 -quit 选项
fi
# 补充 :在 RHEL4 中,第一次安装后会出现一个蓝色的进度条,点击它就可以看到
# 平时的那些启动服务的信息,这个就是由上面的 rhgb-client 命令提供的,
# 而这个命令是由 /etc/rc.d/rc.sysinit 启动的
# We need to source this so that the login screens get translated
[ -f /etc/profile.d/lang.sh ] && . /etc/profile.d/lang.sh
# 注释 : 上面的命令判断是否存在该脚本,是则执行它。
# 该脚本的功能是用于设置登录界面的语言,同样不太长,74 行而已,可以自行分析
# Run preferred X display manager
# 注释 :下面就是重头戏了,启动之前设定的 Display Manager ,
# 显示登录对话框
preferred= # 注释 :该变量的值首先置空
if [ -f /etc/sysconfig/desktop ]; then # 注释 :如果存在 desktop 文件,则执行它
. /etc/sysconfig/desktop # 该文件只有1行,就是 DESKTOP=XXX
if [ "$DISPLAYMANAGER" = GNOME ]; then # 很明显,下面这些语句都不会被
preferred=gdm # 执行,因为 DISPLAYMANAGER
elif [ "$DISPLAYMANAGER" = KDE ]; then # 变量并没有定义,所以为空
preferred=kdm
elif [ "$DISPLAYMANAGER" = XDM ]; then
preferred=xdm
fi
fi
shopt -s execfail
# 注释 :shopt 是 shell 的一个内置命令,-s execfail
# 表示启用 ExecFail 功能,它表示如果下面的 exec 命令失败,不会退出非交互式 shell
# 在这里给出它的简单解释 :
# execfail
If set, a non-interactive shell will not exit if it can-
not execute the file specified as an argument to the
exec builtin command. An interactive shell does not
exit if exec fails.
[ -n "$preferred" ] && exec $preferred $* >/dev/null 2>&1
# 注释 :如果 preferred 变量的值不为空,则使用执行的 Display Mananger
# 并用它的代码覆盖当前 non-interactive shell 的代码
# 基于上面的 desktop 文件的内容,这句话是不会被执行的。
# Fallbacks, in order
# 注释 :既然不执行上面的命令,那么总得启动一个 Display Manager 了吧,
# 所以下面依次查功能是 gdm、kdm、xdm
exec gdm $* >/dev/null 2>&1
exec kdm $* >/dev/null 2>&1
exec xdm $* >/dev/null 2>&1
# catch all exit error
# 注释 :要注意,当你退出 GDM 后者 KDM 时,并不会执行下面的 exit 命令,
# 只有在上面的3个exec 命令都执行失败才会执行下面的 exit 1