Chinaunix首页 | 论坛 | 博客
  • 博客访问: 696879
  • 博文数量: 183
  • 博客积分: 2650
  • 博客等级: 少校
  • 技术积分: 1428
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-22 17:02
文章分类
文章存档

2017年(1)

2015年(46)

2014年(4)

2013年(8)

2012年(2)

2011年(27)

2010年(35)

2009年(60)

分类: LINUX

2011-05-10 21:35:53

开机——很多人觉得很简单的事情,只要按下电源开关,然后系统就会自然启动,没有什么需要学习的。其实不然,如果系统没有什么问题,可以正常登陆的时候,当然开机很简单。但更多的时候,我们需要知道当机子不能正常开机的如何解决,这才是我们学习的目的。
那么我们就来分析一下Linux的开机流程
简单来说,系统开机的过程可以汇整成下面的流程:
1.加载BIOS的硬件信息、进行自我测试,并依据设定获得第一个可开机的设备;
2.读取并执行第一个开机设备内MBR的boot 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的组成图
MBR 的组成

Boot Loader 的功能

Boot Loader最主要功能是认识操作系统的文件格式并加载内核到主存储器中去执行。 由于不同操作系统的文件格式不一致,因此每种操作系统都有自己的boot loader,用自己的loader才有办法载入内核文件。
问题就来了:如果在一部主机上面安装多种不同的操作系统。 既然必须要使用自己的 loader才能够加载属于自己的操作系统内核,而系统的MBR只有一个,那怎么会有办法同时在一部主机上面安装多系统呢?下面就来看看如何实现的。

每个文件系统(filesystem, 或者是 partition) 都会保留一块启动扇区 (boot sector) 提供操作系统安装 boot loader,而通常操作系统默认都会安装一份 loader 到他根目录所在的文件系统的boot sector 上。如果我们在一部主机上面安装 Windows 与 Linux 后,该 boot sector, boot loader 与 MBR 的相关性会有点像下图:
loader MBR sector

如上图所示,每个操作系统默认是会安装一套 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的缘故。
loader MBR sector2

如上图所示,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=single-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<===虚拟文件系统文件(RHEL6用initramfs代替了initrd,他们的目的是一样的,只是本身处理的方式有点不同--精通initramfs构建step by step--http://blogold.chinaunix.net/u2/63991/showart_1856670.html
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//boot中的压缩镜像.

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
看到结果了吧,和我们系统中的/是不是很类似。有兴趣的朋友,慢慢了解吧!哈哈……

init及配置文件 /etc/inittab 与 runlevel

在内核加载完毕、进行完硬件检测与驱动程序加载后,此时主机硬件已经准备就绪了,这时候内核会主动的呼叫第一支程序,那就是 /sbin/init

/sbin/init 最主要的功能就是准备软件执行的环境,包括系统的主机名、网络设定、语言、文件系统格式及其他服务的启动等。 而所有的动作都会通过 init的配置文件/etc/inittab来规划,而inittab 内还有一个很重要的设定内容,那就是默认的 runlevel (开机运行级别)。

先来看看运行级别Run level

Linux就是通过设定run level来规定系统使用不同的服务来启动,让Linux的使用环境不同。我们来看看这个inittab文件里面的支持级别(RHEL6系统里面的,和以前的其它版本有很大的差别)
[root@yufei ~]# vim /etc/inittab
……
17 # Default runlevel. The runlevels used are:
18 #   0 - halt (Do NOT set 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/这个目录里面的文件看看。

/etc/rc.sysinit 这个文件干了哪些工作?

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]

运行级别的切换

通过上面的学习,我们知道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来实现。哈哈……

想编译内核,突然想知道linux的启动过程,之后就想起来byr论坛有个人问过这个问题,翻了一下,找到了,地址在。

当用户打开PC的电源,BIOS进行开机自检,按BIOS中设置的启动设备(通常是硬盘)启动,接着启动设备上安装的引导程序lilo或grub开始引导Linux,Linux首先进行内核的引导,接下来执行init程序,init程序调用了rc.sysinit和rc等程序,当rc.sysinit和rc完成系统初始化和运行服务的任务后,返回init;init启动了mingetty后,打开了终端供用户登录系统,用户登录成功后进入了Shell,这样就完成了从开机到登录的整个启动过程。
1027001
感觉这段写的挺易懂的,之后帖子里边还有一个链接,是一个pdf文档,讲linux启动过程的,看了一遍,现记录如下:

当PC机上电后,会将控制权转移给BIOS的程序,BIOS通常固化在ROM中。BIOS并不是十分强大,但是它会完成两个重要的任务:

  • 第一个任务就是开机自检(POST:Power On Self Test),这是对电脑硬件的一个整体状况的检测和初始化过程。BIOS浏览电脑中的
    所有组件,并且执行所有需要的初始化。在现今的系统中,复杂的组件都提供他们自己的初始化规则,而BIOS的任务就是安排这些规则的执行顺序。
  • 第二个任务是BIOS会浏览启动设备列表来寻找一个启动加载器(bootloader),典型的启动设备列表的内容和顺序如下:软盘(floppy)、CD 和/或DVD,一个或者多个硬盘驱动器。对于每个设备BIOS会读取它的第一个分区(very first sector of the drive,这个分区被称为启动分区),直到找到了一个提供启动加载器(boot loader)的启动分区。boot loader是一个轻量级但是很强大的程序,它的任务就是将操作系统引导到内存中,并且开始运行。
  • 如上的过程只是系统启动的框架,并未详述BIOS或者bootloader具体的工作。

对于Linux来说,通常的bootloader是GRUB(Grand Unified Bootloader)。事实上,GRUB太大,不能放在一个硬盘分区内。所以它被分为两部分:小的那部分放在启动分区内(boot sector),它的主要任务就是找到并且加载大的那个部分,而大的部分的任务就是找到并且加载操作系统。
如果你的PC是多操作系统的(如Linux和Windows),上述的那一步就是选择启动哪个系统的位置。

一旦加载到了内存并且由bootloader启动了,Linux内核就随着它的启动执行一系列的动作:

  • 首先他将浏览硬件配置,初始化设备驱动器,并且将CPU设置为带有虚拟内存的保护模式;
  • 其次,它挂载根文件系统(只读);
  • 最后,一旦这些准备都完成了,内核将启动一个用户进程:/sbin/init,这个用户进程负责启动的所有后续工作。

init程序的进程ID是1,是程序树的根节点。系统中运行的所有程序都是他的后代。init将会一直运行知道系统挂掉(halt),同时我们将看到它全面负责系统的状态。

多年以来的init文件参考的是19世纪80年代的SysV(Unix) System V)的init。最近有了一些变化,但是所有我熟悉的新的实现都考虑到了SysV的框架并且向后兼容SysV的init文件。当init启动,init寻找/etc/inittab文件执行其中的指令使启动过程向下进行。如下是一个来自ubunt工作站的nittab文件,进行了少许修改减小了尺寸。


# /etc/inittab: init(8) configuration.
# longan.csil.sfu.ca, slightly edited for size
# The default runlevel.
id:2:initdefault:
# Boot-time system configuration/initialization script.
# This is run first except when booting in emergency (-b) mode.
si::sysinit:/etc/init.d/rcS
# What to do in single-user mode.
~~:S:wait:/sbin/sulogin
~~:S:respawn:sbin/sulogin
# /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.
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
# Normally not reached, but fallthrough in case of emergency.
z6:6:respawn:/sbin/sulogin
# What to do when CTRL-ALT-DEL is pressed.
ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now
# Action on special keypress (ALT-UpArrow).
#kb::kbrequest:/bin/echo "Keyboard Request--edit /etc/inittab to let this work."
# What to do when the power fails/returns.
pf::powerwait:/etc/init.d/powerfail start
pn::powerfailnow:/etc/init.d/powerfail now
po::powerokwait:/etc/init.d/powerfail stop
# /sbin/getty invocations for the runlevels.
#
# The "id" field MUST be the same as the last
# characters of the device (after "tty").
#
# Note that on most Debian systems tty7 is used by the X Window System,
# so if you want to add more getty’s go ahead but skip tty7 if you run X.
#
1:2345:respawn:/sbin/getty 38400 tty1
2:23:respawn:/sbin/getty 38400 tty2
3:23:respawn:/sbin/getty 38400 tty3
4:23:respawn:/sbin/getty 38400 tty4
5:23:respawn:/sbin/getty 38400 tty5
6:23:respawn:/sbin/getty 38400 tty6

可以看出inittab中每行的格式为:id:runlevel(s):action:command 
其中

  • id只是一个为了方便的标识,对init没有什么特殊意义;
  • command要么是可执行命令,要么是一个程序,要么就是一个脚本;
  • runlevelaction要控制什么时候和怎样执行command,随着我们的深入,这个将会更加明显。

init在inittab中第一个要找的就是action域为’sysinit’的那一行。这个就是在系统启动时应该执行的命令。

  • 在Ubuntu 6发行版中,action域为’sysinit’的那个命令关联的是/etc/init.d/rcS这个脚本。查看一下rcS发现它执行的其实是命令’/etc/init.d/rc S’,rc脚本是一个类属脚本(generic script),这个脚本寻找目录/etc/rcN.d(N为数字),并且执行相应目录中的所有脚本。
  • 所以,如果系统执行level 5,那么rc寻找目录/etc/rc5.d

在/etc/rcN.d目录中,都是以S或者K开头的脚本,并且后边紧接数字,格式如下[S|K]dd,其中dd是数字。

  • 以S或者K开头的脚本要么就是启动或者杀死某些服务的脚本,要么就是设置服务的脚本。当进入某启动模式后,rc脚本将先执行杀死脚本,紧接着在执行启动脚本;
  • 两个数字dd范围是00到99.这个数字决定了在启动脚本和杀死脚本中的执行顺序;
  • 文件名中的命令部分用来指示那个系统或者系统组被这个脚本操控。(The command portion of the file name is chosen to indicate which system or group of systems is manipulated by the script.)
  • 如果使用’ls -F‘或者’ls -l‘查看rcN.d文件夹的文件列表的话,将发现这些脚本实际是符号链接,链接到/etc/init.d中的脚本。通常,每个脚本都有四个执行参数:start、stop、status和restart;有一些有额外参数。

让我们回到命令’rc S‘会产生怎样的结果。在rcS.d文件夹中,一个由脚本执行的不完整的函数列表包括:

  • 随着启动一系列的进程,设置系统配置变量,使用的值包含在/etc/default文件中;
  • 继续初始化如下设备:usb,clock,keyboard和disk handling的优化;
  • 清除系统状态文件;
  • 启动网络接口(对于这一步/etc/sysctl.conf文件很重要);
  • 使用NFS挂载远程文件系统。

当rc S完成/etc/rcS.d中脚本的执行后,将控制权返还给init,init在inittab中继续寻找action域是’initdefault‘的那一行。这个入口没有附带的命令,它的作用是说明应该启动哪个run level来完成启动过程。

此时解释run level恰到好处。通常的unix系统,run level就是0-6之间的整数,这些整数用来说明系统的状态(通常叫做模式)。
如下是Ubuntu Linux的run level的典型设置:

  • 0:停止系统;将run level设置为零将导致Linux进行有序的shutdown,并且关掉电脑;
  • 1:单用户模式;在这个状态,之后内核和一些极其重要的服务运行。这个状态用作系统维护、升级和修复,这些工作都不能在普通模式下完成。(通常,当任务正 在进行中时,因为系统状态不会一成不变,因此是不安全的。同时排除了其他用户的不必要的系统行为。)
  • 2-5:多用户模式;系统在这个状态,可以支持多用户和提供GUI界面;
  • 6:重启模式;系统halt,之后紧接着自动进行重启。

这些run level都是惯例;所有的unix系统都遵从相同的启动顺序,具体的run level与系统模式之间的映射关系可能各不相同(如Fedora,分了许多多用户模式,最小启动和最小启动+挂载远程文件系统和GUI界面)。

我们接着解释余下的开机行为。

sysinit在启动时间具体执行的命令
initdefault默认的run level;没有相关的命令
powerfailnow发现掉电时具体执行的命令
ctrlaltdel当用户通过ctl+alt+del组合键强制重启时,需要执行的命令
once只启动一次的命令。init不会等待相应命令的完成(Speci?es that the command is to be started exactly once;
init will not wait for completion)
wait只执行一次的命令,并且init在执行之前会等待响应命令完成。(这是通常的行为,同样应用到sysinit,ctrlaltdel和powerfail命令)
respawn开始命令并且监视它;如果它一直不结束,就再次执行该命令。这个行为用于监视控制台和其他登录端口的行为。

除了列在这里的内容外,inittab还有其他的动作,具体参见man inittab。

一旦init知道了默认的run level,它将找到相应run level的那行,之后执行那行action域的相应命令。

  • 随着对run level S的初始化设置,一个给定的run level的工作将切换到/etc/init.d/rc,它在相应的run level下作为唯一的参数被执行。

其他函数将由脚本/etc/init.d控制。不完整列表如下:

  • Internet服务(DNS,SNMP,SSH,plus the meta-daemon xinetd),和防火墙;
  • 使用Samba的远程的文件共享;
  • 界面管理器(负责开启GUI登录界面)
  • cron守护进程,它可以实现在特定时间内运行某个任务。
  • 打印和登录服务
  • 网络信息服务,也就是我们之前称为的黄页。设置一个由核心服务维护的数据库,可以通过网络发出请求。
  • 远程处理呼叫、远程执行守护进程

对于inittab还有一件事需要提。inittab文件底部,有几行内容的id域是数字的。这些行控制一些程序,来监听登录请求。

  • 对于每个用户可以登录的端口–典型的就是通过串口登录到终端–必须有个程序监听的登录请求。
  • Debian使用名叫getty的程序;其他unix系统有不同的名字。这个程序负责监听相应端口的行为,并且当它检测到相关行为时,显示一个登录提示符。当你输入了正确的用户名和密码,getty将启动一个命令行shell,开始你的工作。

最近,一个叫做upstart的新的init实现投入了使用。为了兼容SysV系统的init,upstart的二进制执行文件也叫做init。为了避免一些不必要的混淆,这章将指定upstart或者init,而避免使用二进制的名称。upstart使用在Ubuntu7中,安装在网络实验室的虚拟工作站中。

  • upstart的操作是基于事件概念的,并且通过事件的发生与否来触发upstart的行为。
  • 开始阶段,upstart对由/etc/event.d文件夹中的文件定义的工作进行确认。
  • 为了使时间滚动,当upstart意识到自己是进程1时,upstart将执行startup事件。

为了与SysV的nit兼容,upstart的事件定义与SysV的init几乎是一一对应的。

  • 事件rcS,由startup事件触发,执行/etc/init.d/rcS
  • 当rcS结束,执行一个stopped rcS事件,这个事件触发rc-default事件。你可能已经从这个名字猜到了,这个任务决定了默认的run level
。它执行一个run level N事件。
  • run level N事件触发一个执行rcN脚本的任务
  • 最后,完成对getty程序的初始化,并且监听登录请求。
  • 对于init进程有其他变种,如,sun solaris使用的是服务管理套件。

    你可能注意到了,在inittab中有六个入口监听登录请求。

    现在我们来看看在只有一个控制台的情况下为什么会有六个入口。

    • Linux提供了一个工具叫做虚拟控制台(通常指的就是虚拟终端)。大多数Linux系统配置了7个。
    • 每一个都提供了独立的登入端口,就像电脑为终端提供了7个分离的输入/输出连接。特别的,当你在一个虚拟终端登出,并不会使其他虚拟终端也登出。
    • 约定,虚拟终端1-6用作命令行登录,虚拟终端7用作GUI登录。
    • 在各个虚拟终端之间切换用组合键‘alt+Fn’,其中n是1-7。对于摆脱GUI>登录界面有点不同;需要组合键‘ctrl+alt+Fn来切换到其他虚拟终端。

    虚拟控制台1-6提供了命令行登录方式

    • 一旦进入了系统,一个命令行shell就开始工作。unix命令行shell模拟的是DOS命令行shell,但是他们的功能是截然不同的。
    • 有多种不同的shell,你登入系统得到的哪种shell由/etc/passwd文件中你的相应行决定。得到的第一个shell叫做登录shell。
    • 如下是对unix shel的简单分类:
      shellinit fileComments
      sh.profile最原始的shell,但是仍然是重要,因为许多主要系统的shell脚本都是按这个shell写的
      ksh.profile(korn shell)sh的加强版
      bash.bashrc对sh版的极大的加强版
      csh.cshrc“C”shell,可与sh媲美,不过csh有跟人性化的高亮功能
      tcsh.tcshrc”Tenex“ C shell,csh的极大加强版

          
      sh和他的后代ksh、bash具有同样的句法结构。这些shell提供的原始的操作是将文本串分割成独立的字母。他们还能提供出色的输入/输出重定向。
      csh和他的后代tcsh同样具有相同的句法结构,但是不同于sh和sh的后代。csh和他的后代将文本串分割成单词。输入/输出重定向也显得粗陋。

    • 一旦登录shell运行,他将会寻找相应的初始化文件。正如上边所示,不同shell的初始化文件也各不相同。如果家目录中没有初始化文件,通常会使用在/etc中的默认文件。
    • 每当你执行新的命令行shell初始化文件就被执行一次。但是登录shell(当你登入之后的第一个shell)会执行第二个初始化文件,叫做.login(.bash_login for bash).同样,它会在你的系统家目录中找这个文件。
    • 当登录shell完成了自身的初始化,他将提供一个提示符,此时你就可以工作了。

    虚拟终端7不同于其他虚拟终端:它提供了一个GUI登录界面。

    • 这个登录界面看起来很熟悉—它提供了输入用户名和密码的地方,还有一些其他选项
    • 屏幕呈现出一个简洁的X Window System管理界面,Linux可以通过它选择要提供的服务
    • 为了进一步解释这发生了什么,我们需要后退一步谈一谈X Window System
    阅读(2193) | 评论(0) | 转发(2) |
    给主人留下些什么吧!~~