Chinaunix首页 | 论坛 | 博客
  • 博客访问: 383384
  • 博文数量: 85
  • 博客积分: 1504
  • 博客等级: 上尉
  • 技术积分: 928
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-04 12:20
文章分类

全部博文(85)

文章存档

2011年(66)

2010年(19)

分类: LINUX

2010-12-05 15:04:15

    从按下PC电源,到出现熟悉的bash提示符"$"或进入漂亮的KDE/GNOME桌面,这是我们每天开机必经的过程。那么,在这短短几十秒内,Linux是怎样启动的呢?本文介绍Linux的启动过程。
    平台:PC机, Ubuntu 5.10

基础知识
BIOS (Basic I/O System,基本输入/输出系统)
    BIOS,完整地说应该是ROM-BIOS,是只读存储器基本输入/输出系统的简写,它实际上是被固化到计算机中的一组程序,为计算机提供最低级的、最直接的硬件控制。准确地说,BIOS是硬件与软件程序之间的一个“转换器”或者说是接口(虽然它本身也只是一个程序),负责解决硬件的即时需求,并按软件对硬件的操作要求具体执行。
  从功能上看,BIOS分为三个部分:
  1.自检及初始化程序;
  2.硬件中断处理;
  3.程序服务请求。
    这里我们主要关注第一部分——自检及初始化程序:这部分负责启动计算机,具体有三个部分,第一个部分是用于计算机刚接通电源时对硬件部分的检测,也叫做加电自检(POST),功能是检查计算机是否良好,例如内存有无故障等。第二个部分是初始化,包括创建中断向量、设置寄存器、对一些外部设备进行初始化和检测等,其中很重要的一部分是BIOS设置,主要是对硬件设置的一些参数,当计算机启动时会读取这些参数,并和实际硬件设置进行比较,如果不符合,会影响系统的启动。
  最后一个部分是引导程序,功能是引导DOS或其他操作系统。BIOS先从软盘或硬盘的开始扇区读取引导记录,如果没有找到,则会在显示器上显示没有引导设备,如果找到引导记录会把计算机的控制权转给引导记录,由引导记录把操作系统装入计算机,在计算机启动成功后,BIOS的这部分任务就完成了。
    关于BIOS的详细介绍,可以google一下,就不错。

硬盘


    就物理组成来说,一个硬盘封装里有多个盘片(platter),每个盘面有两个(surface)。在盘片上都有一个磁头(head)来进行硬盘盘片的读/写,盘片绕(spinder)旋转一周时磁头所走过的轨迹即磁道(track),所有盘片的同一磁道构成了磁柱(cylinder)。磁道又被分为多个扇区(sector),扇区是最小的磁盘存储单位,即硬盘分区时的最小单位——通常为512KB。磁道由缝隙(gap)分开,gap中存储的不是数据位,而是用来确认扇区的格式位。

MBR
    主引导扇区(MBR, Master Boot Recorder)是硬盘中最重要的部分,它记录了硬盘的分区信息、引导信息。CU上面有一篇介绍MBR的文章
   
注意这里所说的MBR是指BIOS中指定的启动设备中的MBR。如果以软盘启动,则MBR是软盘的第一个扇区。如果是硬盘,则是硬盘的第一个扇区。如果有多个硬盘呢?那么就是BIOS中指定启动硬盘的第一个扇区!

run-level
运行  $ less /etc/inittab
显示下列信息:
# /etc/init.d executes the S and K scripts upon change
# of runlevel.
#
# Runlevel 0 is halt.
# Runlevel 1 is single-user.
# Runlevels 2-5 are multi-user.
# Runlevel 6 is reboot.
    上面显示的就是当前可用的登录模式,共有0~6中级别。常用的是3和5。
0:关机
1:单用户模式(系统有问题时的登录模式,相当于WINDOWS的"安全模式“)
2:对于Debian/Ubuntuare来说,2~5都是相同的——多用户图形界面模式。对于其他发行版来说,3可能是多用户文本模式,4为系统保留,5为多用户图形模式,具体的定义可以查看该发行版对应的/etc/inittab文件内容。
6:重新启动
另外,还可能有"S"级,它等同于1的单用户级别。
    运行 $ runlevel 可以查看系统当前运行级别

如果把运行级别设成了0或6,想象会出现什么情况?如何解决呢?

    WINDOWS在启动时,如果按下F8,会出现“安全模式“、”正常启动“、”MS-DOS“模式的选择。相当于Linux run-level的1,5,3(不对应于Debian/Ubuntu)。

关于Debian/Ubuntu中的run-level,看

基本流程
1, 加载BIOS硬件信息,并取得第一个开机装置的代号。
2,加载第一个开机装置中MBR的boot loader(即lilo, grub, spfdisk等)引导信息。
3,加载Linux内核,内核开始解压缩,并驱动硬件。
4,内核执行init程序,并获得run-level信息;
5,init 执行 /etc/init.d/rcS 程序;
6,加载内核模块(module)
7,init 执行 对应run-level 级的脚本文件( Scripts );
8,执行 /bin/login 程序,等待用户登入;
9,用户登入之后,开始以shell控制系统(如果以图形界面登录,则运行图形界面)。

下面具体介绍流程中的步骤:
1,加载BIOS
    系统上电时,最先读取BIOS信息。BIOS(Basic Input/Output System)是计算机与外设最底层的接口,它存储了计算机启动时最先加载的数据,包括:CPU类型、启动设备顺序、硬盘大小/类型、芯片组工作状态、外设I/O地址、PnP (Plug and Play,既插既用设备)的开启与否、内存时钟等。
    读取了BIOS设定值后,系统根据BIOS数据进行开机自我检测(Power On Self Test, POST),对硬件进行初始化,并设定PnP设备,指定启动设备,之后从磁盘的MBR中读取Bootloader数据。

2,加载Boot Loader
    系统读完BIOS之后,接着加载第一个引导磁盘的第一个扇区(MBR),boot loader就位于MBR中。此时,启动工作的接力棒就交到了boot loader的手中。

常用的boot loader有lilo, grub, spfdisk等,现在最流行的是grub,我用的Ubuntu中,boot loader就是grub,本文假设boot loader是grub,其实基本原理都是一样的。

    为什么要在MBR中安装boot loader呢?它到底有什么作用?实际上,boot loader的作用就是加载OS内核。系统在启动时,要读取文件以加载内核,必须能够识别硬盘文件系统,但这时候系统还在启动过程中,对文件系统信息一无所知。boot loader就辅佐系统识别文件格式,加载内核。boot loader不仅不光能够识别Linux内核,而且能识别WINDOWS内核.所以,如果要安装多系统,那么要在MBR中安装能支持这些系统文件系统的 boot loader.
    如果是以grub启动,加载它后,会有个选择启动那个OS的菜单,当你作出选择后,grub就从被选定OS所在的扇区中加载相应的内核.

3,加载内核
    在grub的菜单选定启动Linux后,系统从Linux所在的磁盘载入内核。内核一般位于/boot目录中,比如,我的系统中内核为 /boot/vmlinuz-2.6.12-10-686。当然,可以有不同版本的内核位于/boot目录中,可以通过grub菜单选择启动的内核版本。
   
$ uname -r  : 显示当前运行的内核版本
   
    vmlinuz是可引导的、压缩的内核。“vm”代表“Virtual Memory”。vmlinux是未压缩的内核,vmlinuz是vmlinux的压缩文件.
    加载内核时还应该注意”虚拟硬盘“,即RAM DISK。以后有机会再归纳。


图1 有建立RAM Disk可能的启动流程

 
图2 更明了的启动流程

总之,boot loader现将Linux内核加载到内存中(可能基于initrd建立RAM DISK),然后将BIOS中关于设备的数据传递给内核,内核建设设备,加载相应的驱动程序.

4,init进程
    内核被加载之后,它执行的第一个程序就是/sbin/init.init 它利用 /etc/inittab配置文件获取开机等级 ( Run level ) 之外, 并基于run level 选择开机时启动的服务.
    通过 $ less /etc/inittab 查看开机init读取的开机配置文件内容.注意下面这行:
    # The default runlevel.
    id:2:initdefault:
    Ubuntu默认的run level是2.正如前面所说,把它设置成2,3,4,5都是多用户图形模式登录.千万别设成0或6!
      
关于init进程,这里多说几句(针对一般UNIX系统)
pid=0 : swapper进程,用于进程调度.它位于内核内部,是系统进程.
pid=1 : init进程, 有对应的程序/sbin/init,尽管运行它需要管理员权限,但实际上它是个用户进程,不位于内核中.系统启动时,内核被加载后调用init进程.
pid=2 : pagedaemon, 用于支持虚拟内存的页.

    init进程还负责处理孤儿进程(父进程在子进程之前终止,则子进程成为孤儿进程, 此时,init继承为他们的父进程).

5,init 执行 /etc/init.d/rcS 程序 (重点)

在Debian/Ubuntu系统中,初始化脚本是/etc/init.d/rcS,在Rad Hat中是/etc/rc.d/rc.sysinit。这里面包含了装入文件系统,设置时间,打开交换分区,得到主机名等等内容。

    在前面提到的inittab文件中,紧接runlevel之后有如下内容:
# Boot-time system configuration/initialization script.
# This is run first except when booting in emergency (-b) mode.
si::sysinit:/etc/init.d/rcS

    inittab的主要功能是描述引导及正常操作时,应该在何种运行等级下启动什么程序,每个运行等级的具体项目完全可以通常/etc/inittab来定义,但Debian有一个更健壮的方案sysvinit,它被认为是init最强大的应用程序之一。Debian组织inittab的方式是把运行等级的大部分定义从inittab中移出来,移到一个脚本层次中去。惟一直接从inittab启动的程序只有getty,它用于虚拟设备上启动登录提示符,保留它因为它们要求特殊处理,在inittab之外处理要困难得多。

  inittab来启动所有软件当然是可能的,但将所有配置写在同一个文件既不方便查看也不方便维护,所以文件里会加上这许多行:

  l0:0:wait:/etc/init.d/rc 0
  l1:1:wait:/etc/init.d/rc 1
  l2:2:wait:/etc/init.d/rc 2
  l3:3:wait:/etc/init.d/rc 3
  l4:4:wait:/etc/init.d/rc 4
  l5:5:wait:/etc/init.d/rc 5
  l6:6:wait:/etc/init.d/rc 6

  这些行实际决定了系统在各个运行等级下的行为。它们如何做到的也许并不明显,但至少我们知道主要意思:首先每行都有个符号ID lx,lx表示runlevel x;其次,每行只在一个运行等级下激活,该运行等级对应着符号ID中的数字x。命令执行时,init停下来,直到进程结束。最后,每个命令行调用一个脚本 /etc/init.d/rc x,这里x代表当前运行等级的数字。显然各运行等级的具体任务在/etc/init.d/rcS脚本中安排。
      
init把从inittab中获取的run-level值作为参数传递给rc

rc与rcS脚本的区别 (rc = run command)
rc   : This file is responsible for starting/stopping services when the runlevel changes.
rcS : Call all S??* scripts in /etc/rcS.d in numerical/alphabetical order.

6,加载内核模块(module)
    2.6的内核支持动态模块的加载,关于内核模块,我另外写一篇。

7,init 执行 对应run-level 级的脚本文件( Scripts )
    到目前为止,内核及其模块被加载了,也完成了系统的初始化。现在要做的工作就是开启系统服务。由于不同的run-level需要开启的服务不同,所以系统为不同的run-level设定了不同的脚本。
   
$ ls -d /etc/rc*.d
   
        可以看到有rc0~rc6及rcS,共8个目录。他们所包含的文件都是连接,指向/etc/init.d/目录中的文件。比较各目录中的文件你会发现, rc2~rc5中的内容是相同的。正好验证了Debian/Ubuntu中run-level 2~5是等效的。

以S开头的表示在对应级别Start的服务,以K开头表示Kill的服务。紧跟S或K之后的两位数字决定了启动顺序,数字小的先运行。

关于rcS/rcS.d,我还不是很清楚,哪位大侠指点?

8,执行 /bin/login 程序,等待用户登入
    这个就不用多说了。看过APUE的都知道login要读取/etc/passwd文件。关于passwd文件,请参考APUE,p161 (第二版新版哦!)
   
OK,完毕,谢谢收看。有不足之处,恳请指正!

 在系统加电引导时,init从run-level = 0开始,一级一级往上运行到inittab中定义的默认run-level。在run-level过渡时,init将run-level值作为参数传递给rc,进而执行启动脚本。
 

阅读(1148) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-12-07 09:55:12

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com