[原创]Ubunut启动过程详细分析。

Posted on 2010-06-12 01:14  尤里哥  阅读(3138)  评论(1编辑  收藏  举报

Ubuntu-Linux的开机流程

Ubunt Linux的启动过程比较复杂,通过对网上资料的收集和整理,暂记录如下。由于本人刚刚接触Linux,很多东西还不是很明白,希望大家多多指点。
---------------------------------------------------------------------------
1.载入BOIS硬件信息,获取第一启动设备代号;
2.读取第一启动设备MBR中的引导加载程序(Bootloader),如grub lilo spfdisk等等;
3.引导加载程序从磁盘文件中载入操作系统核心;
4.启动管理器upstart启动预定义runlevel的系统服务;
5.启动管理器启动用户定义程序。
6.启动终端和(/或)x-window,等待用户登录。
---------------------------------------------------------------------------

Linux启动流程图

1.计算机加电后,系统将自动读取bois中保存的硬件信息(即开机时按delete或F2进入的bois设置画面的内容),其中指出了第一启动设备以及设备的启动优先级。然后系统开始自检(POST:power on self test)。接下来执行硬件初始化,设置PnP设备。最后开始读取开机设备的数据(MBR)。

2.根据BOIS中设定的开机设备顺序,系统开始尝试从指定的启动设备的主引导记录(MBR:Main Boot Record)载入引导加载程序。对于HDD启动设备来说,MBR位于第一扇区0头0道1扇区,大小为512K。如果在MBR中找到了引导加载程序。则由它负责加载操作系统。

3.引导加载程序一般包括2个部分:主引导加载程序和二级引导加载程序。其中主引导加载程序位于MBR中,体积很小,主要负责载入真正的引导加载程序(二级引导加载程序)。二级引导加载程序从磁盘中载入操作系统,并能够提供更为丰富的人机交互。主要的引导加载程序有LILO,GRUB等。其中Ubuntu默认GRUB为引导加载程序。
由于引导加载程序需要读取文件分区表(DPL),因此它必须能够识别磁盘的文件系统格式如FAT,FAT32,NTFS,ex2,ex4...等等。注意windows提供的loader无法识别linux的文件系统,而linux下的loader如grub,lilo等既能识别windows文件系统,也能识别linux文件系统。
引导加载程序读取操作系统核心文件,并将其加载到内存中,利用核心的功能开始测试硬件并加载相应的驱动程序,包括硬盘、CPU、网卡(对!你没看错,确实有网卡,linux把网卡驱动并入了核心中),声卡等等。
实际核心文件一般为/boot/vmlinuz-2.x.xx-xx-generic,但核心在载入过程中仅载入根目录/而已,因此根目录下存在一个vmlinuz的链接文件指向实际的核心文件。
此外,核心的一部分功能需要使用别的文件来提供,需要在载入核心的同时也载入这些功能文件(如小型linux系统启动时常用内存模拟的虚拟磁盘RAM Disk来完成某些功能)。因此根目录下还有一个链接文件initrd,它指向/boot/initrd.img-2.x.xx-xx-generic的映像文件。Loader加载根目录的时候会同时加载该映像文件创建RamDisk。
initrd.img所创建的RamDisk中包含了一个名为linuxrc的程序,存放在initrd虚拟磁盘的最顶层目录下。RamDisk创建成功后,核心将调用该程序执行硬件驱动模块的加载以驱动硬件设备。硬件驱动加载完成后,initrd创建的RamDisc将被移除,然后操作系统开始运行启动管理器。用于根据需要加载服务程序、用户自定义启动程序,直到进入登录界面。

4.Linux下存在多种启动管理器,如串行加载服务的System V init,通过设置服务依赖实现并行加载的initng。Ubuntu采用的是一种基于事件的启动管理器——Upstart。它实际有3个程序和配置文件目录组成。
----------------------------------------------------------------------------
程序 | /sbin/init            telinit(8)                runlevel
配置 | /ect/init/            /etc/rcx.d/                /etc/init.d/
----------------------------------------------------------------------------

(1)init作为系统启动后的第一个程序(利用命令ps -aux查询进程id,init的PID总是1,实际文件为/sbin/init),读取/etc/init目录下保存的启动配置文件。该目录下的所有文件的文件名都以.conf结尾(也可存在子目录中)。每一个文件都定义了一个单独的服务或任务,服务的名字正是不包括路径名和扩展名的文件名。如/etc/init/rc-sysinit.conf所描述的服务就是rc-sysinit。这些文件都是文本类型的文件,而非可执行文件。
init首先启动rc-sysinit任务。该任务由/etc/init/rc-system.conf脚本指定,目的是兼容旧版串行化的system v init。该脚本读取默认的运行等级(runlevel),并将结果传递给upstart的下一个组件telinit。下面的代码片段节选自rc-system.conf:
----------------------------------------------------------------------------
# ... 省略
env DEFAULT_RUNLEVEL=2
# ... 省略
script
    # 检查/etc/inittab中是否指定了默认runlevel
    if[ -r /etc/inittab ]
    then
    eval "$(sed -nre 's/^[^#][^:]*:([0-6sS]):initdefault:.*/DEFAULT_RUNLEVEL="\1";/p' /etc/inittab || true)"
    fi

    # 检查核心命令行典型参数
    for ARG in $(cat /proc/cmdline)
    do
    case "${ARG}"  in
    -b|emergency)
        # Emergency shell
        [ -n "${FROM_SINGLE_USER_MODE}" ] || sulogin
        ;;
    [0123456sS])
        # Override runlevel
        DEFAULT_RUNLEVEL="${ARG}"
        ;;
    -s|single
        # Single user mode
        [ -n "${FROM_SINGLE_USER_MODE}" ] || DEFAULT_RUNLEVEL=S
        ;;
    esac
    done

    # 运行系统初始化脚本
    [ -n "${FROM_SINGLE_USER_MODE}" || /etc/init.d/rcS

    # 切换至默认runlevel
    telinit "${DEFAULT_RUNLEVEL}"
end script
----------------------------------------------------------------------------
从上面的代码可以看出,rc-sysinit.conf的任务是读取默认的运行等级,然后将其作为参数传递给另一个启动程序telinit。

(2)telinit用于将系统转换到选定的runlevel。telnet通过比较当前runlevel与将要进入的runlevel之间运行服务的不同,关闭不需要的服务项,启动目前未运行的服务,从而实现系统状态的转换。可设置的值包括:2-5为多用户模式,0为系统关机,6为重启,1为单用户模式。runlevel也可以设为s或S,这会使得系统直接进入单用户状态而并非首先停止不需要的进程(这种情况比较少见)。
runlevel的改变由发出事件runlevel开始,此时在环境变量RUNLEVEL中保存这新的runlevel而在PREVLEVEL变量中保存你前一个runlevel。telinit首先相应事件,并将新的runlevel写入/var/run/utmp中,然后在/var/log/wtmp中增加一个入口。
要查看当前runlevel,可直接在命令行中输入指令runlevel。在上面的代码中可以看到默认的runlevel为2,则此时返回的是“N 2”。而与runlevel 2对应的服务设置保存在/etc/rc2.d/目录下,其它以此类推。
查看/etc/rc2.d/目录,里面全是符号链接,实际的目标程序在/etc/init.d/下。要关闭该runlevel下的服务,将该目录下的文件名重新命名为"Kxx"打头(xx为2位数,此数字代表了根据服务依赖性得到的启动顺序),然后运行"update-rc.d script defaults",该指令会根据服务的依赖性重新对启动程序排序。如果当前runlevel与init.d脚本中的LSB头不匹配,系统将打印输出警告信息。重新启动已关闭的服务只需要将其名称改为"S"开头,并再次运行"update-rc.d"。
K开头的脚本执行的是/etc/rcX.d/K??* stop
S开头的脚本执行的是/etc/rcX.d/S??* start

(3)runlevel读取系统UTMP文件(默认为/var/run/utmp),找出最新的runlevel记录。前一个runlevel与当前runlevel之间用一个空格隔开,如果没有前一个runlevel则记为"N"(因此在终端中输入runlevel回车后一般会看到返回N 2。)

5.启动用户定义开机启动程序。
在查看/etc/rcX.d/目录时,应当注意如下文件:/etc/rc2~5.d/S99rc.local。该文件都指向同一个脚本/etc/init.d/rc.loacal。文件以"S99"开头,说明该程序是最后加载的。打开该文件,其中含有如下内容:
----------------------------------------------------------------------------
#...
do_start() {
    if [ -x /etc/rc.local ]; then
          [ "$VERBOSE" != no ] && log_begin_msg "Running local boot scripts (/etc/rc.local)"
        /ect/rc.local
#...
----------------------------------------------------------------------------
说明用户定义的启动内容在/etc/rc.local中。打开rc.local,其注释更证明了这一点。

6.载入终端界面或x-window界面
Upstart启动服务程序与用户定义程序完成后,系统将启动终端或X-window,等待用户登录。