Linux系统启动过程剖解及故障排除

系统启动顺序:
加载BIOS的硬件信息
  └  读取MBR的Boot Loader信息 stage1,stage2,grub.conf
        └  Boot Loader加载内核 vminux,initrd
            └  内核执行init程序并取得inittab中运行信息
                    ├  init执行/etc/rc.d/rc.sysinit
                            └  /etc/fstab加载分区等
                    ├  init执行/etc/rc.d/rc
                            ├  启动对应运行级别的守护进程
                            └  最后执行SArrayArraylocal,即/etc/rc.d/rc.local
                    └   init启动了mingetty,打开了终端供用户登录系统
                            └  执行/bin/login程序
                                    └  成功登陆之后启动shell控制主机
                                                └ shell配置脚本
                                                          ├  /etc/profile --->/etc/profile.d/*.sh
                                                          └  ~user/.bash_profile
                                                                    └  ~user/.bashrc
                                                                          └  /etc/bashrc 

※※※BIOS
  当打开一台计算机的电源时,计算机首先执行存储在ROM中的引导代码,即BIOS(Basic Input/Output System)基本输入/输出系统。BIOS在完成外围设备和启动设备检测后确定由什么设备启动。
※※※MBR
  如果电脑BIOS设置为由硬盘启动,它将试图加载磁盘第一个扇区的信息,引导扇区在每个分区里都存在,但是主引导扇区却在硬盘的第一物理扇区。它由两个部分组成,即主引导记录MBR(Master Boot Record)和硬盘分区表DPT。在总共512字节的主引导扇区里MBR占446个字节(偏移0--偏移1BDH),DPT占64个字节(偏移1BEH--偏移1FDH),最后两个字节“55AA”(偏移1FEH--偏移1FFH)是分区的结束标志。
  MBR是用来在系统硬件自检完后引导具有激活标志的分区上的操作系统。它执行到最后的是一条JMP指令跳到操作系统的引导程序去。如果Linux boot loader安装在MBR中,当BIOS读到MBR时,boot loader就被执行。如果把boot loader安装在其它分区super block中,系统将由引导程序加载super block中的Linux boot loader。
备份主分区引导记录
dd /tmp/mbr bs=446 count=1
备份MBR包括主引导记录及分区表
dd /tmp/mbr bs=512 count=1
备份PBR
dd /tmp/pbr bs=1024 count=1 
※※※GRUB
  安装在MBR或PBR中的Linux boot loader通常有两种:LILO和GRUB。LILO是旧版本的Linux boot loader。Grub是Linux boot loader中的“新贵”,它具有很多优点,使引导过程变得非常可靠。①它可以直接从FAT、minix、FFS、ext2或Reiser分区中读取Linux内核,②可以引导如FreeBSD、OpenBSD、NetBSD、GNU HURD、以及DOS、Windows等各种操作系统,③可以在开机时手工编辑启动加载选项,④还可以动态地寻找配置文档。即使GRUB配置不正确,你仍然⑤可能通过手工引导的方法启动计算机。⑥当你升级内核后,安装程序通常还会自动把新安装的内核引导添加到列表中。最主要的,⑦GRUB是一个自由软件!
  加载程序的主要功能是让硬件识别文件系统,并加载内核。Linux的加载程序可以识别Windows的内核文件,而Windows的加载程序却无法识别Linux的内核文件,因此,一个多重启动系统的加载程序不能使用Windows的加载程序。
  Grub的配置文件存储于/boot/grub/grub.conf中,一个典型的配置文件如下:

default=0                               # 在多系统时指定默认启动的系统,0为第一个
timeout=5                              # 以默认系统启动前的时间
hiddenmenu                           # 隐藏启动菜单
splashimage=(hd0,0)/grub/splash.xpm.gz #启动画面背景文件
title Red Hat Enterprise Linux AS (2.6.Array-22.ELsmp)                    # 启动菜单的标签
        root (hd0,0)                   # 指定启动内核所在的磁盘分区,第一个数表示物理驱动器号,第二个数表示分区号,注意这两个都是从0开始计数。
        kernel /vmlinuz-2.6.Array-22.ELsmp ro root=LABEL=/ rhgb quiet        
# 内核位置。在行尾添加vga=0x314更改文本界面的分辩率,0x314为800X600,0x317为1024X768。其它启动选项如单用户、关闭SELinux等都可以在此设置。rhgb显示画面,quiet不显示kernel信息
        initrd /initrd-2.6.Array-22.ELsmp.img        # initrd位置  如果用其它分区的GRUB启动,比如你把RHEL3安装在第四个分区上,并选择把GRUB安装在PBR上(boot在hda5上),编辑grub.conf如下:

        title RHEL3
        rootnoverify (hd0,4)
        chainloader +1 ※※※载入内核
  在GRUB菜单中选择Linux系统后,主机会转到Linux所在的硬盘下并载入内核文件。Linux系统内核文件通常存储在/boot下。
  如果GRUB的配置文件错误的话,可以通过手动指定内核与initrd文件位置的方法加载内核文件。详见本文故障排除2。
  vmlinuz是可引导的、压缩的内核。“vm”代表“Virtual Memory”。Linux 支持虚拟内存,不像老的操作系统比如DOS有640KB内存的限制。Linux能够使用硬盘空间作为虚拟内存,因此得名“vm”。vmlinuz是可执行的Linux内核。
  vmlinuz的建立有两种方式。一是编译内核时通过“make zImage”创建,zImage适用于小内核的情况,它的存在是为了向后的兼容性。二是内核编译时通过命令make bzImage创建,bzImage是压缩的内核映像,需要注意,bzImage不是用bzip2压缩的,bzImage中的bz容易引起误解,bz表示“big zImage”。 bzImage中的b是“big”意思。
  zImage(vmlinuz)和bzImage(vmlinuz)都是用gzip压缩的。它们不仅是一个压缩文件,而且在这两个文件的开头部分内嵌有gzip解压缩代码。所以你不能用gunzip 或 gzip ?dc解包vmlinuz。
  内核文件中包含一个微型的gzip用于解压缩内核并引导它。两者的不同之处在于,老的zImage解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么可以采用zImage 或bzImage之一,两种方式引导的系统运行时是相同的。大的内核采用bzImage,不能采用zImage。
  vmlinux是未压缩的内核,vmlinuz是vmlinux的压缩文件。
※※※载入initrd
什么是initrd?
  initrd = init ramdisk,是一个启动时存在于内存的文件系统。initrd一般被用来临时引导硬件到内核能够接管并继续引导的状态。如用于加载ext3等文件系统及scsi设备的驱动。比如,使用的是scsi硬盘,而内核vmlinuz中并没有这个scsi硬件的驱动,那么在装入scsi模块之前,内核不能加载根文件系统,但scsi模块存储在根文件系统的/lib/modules下。为了解决这个问题,可以引导一个能够读实际内核的initrd内核并用initrd修正scsi引导问题。
initrd的最初的目的是为了把kernel的启动分成两个阶段:在kernel中保留最少最基本的启动代码,然后把对各种各样硬件设备的支持以模块的方式放在initrd中,这样就在启动过程中可以从initrd所mount的根文件系统中装载需要的模块。这样的一个好处就是在保持kernel不变的情况下,通过修改initrd中的内容就可以灵活的支持不同的硬件。在启动完成的最后阶段,根文件系统可以重新mount到其他设备上。
Linux启动一定要用initrd么
  不必,如果把需要的功能全都编译到内核中(非模块方式),只需要一个内核文件即可,initrd能够减小启动内核的体积并增加灵活性。如果你的内核以模块方式支持某种文件系统(例如ext3, UFS),而启动阶段的驱动模块(如jbd)放在这些文件系统上,内核是无法读取文件系统的,从而只能通过initrd的虚拟文件系统来装载这些模块
  这里有些人会问: 既然内核此时不能读取文件系统,那内核的文件是怎么装入内存中的呢?答案很简单,Grub是file-system sensitive的,能够识别常见的文件系统
initrd文件是怎么生成的? 
  使用mkinitrd命令,这个命令其实是一个Bash脚本,该脚本先建立一个8M的空文件,并在此上建立一个文件系统,并拷贝相应的的文件。
initrd是什么内容 ?
initrd.img不像通常的以.img为扩展名的ramdisk cramfs文件。它是经过用gzip -Array进行压缩过的ramdisk文件。所以,如果直接用#mount initrd.img /temppath -o loop不能mount上,会报告你指定一个文件类型。 所以拆解它的过程要先将其进行解压缩,然后再mount。

# file initrd-2.6.5-1.358.img
initrd-2.6.5-1.358.img: gzip compressed data, from Unix, max compression
# mv initrd-2.6.5-1.358.img initrd-2.6.5-1.358.gz
# gzip -d initrd-2.6.5-1.358.gz
# ll
-rw-r--r-- 1 root root 81Array2000 Jan 14 11:32 initrd-2.6.5-1.358
# mkdir /mnt/loop
# mount -o loop initrd-2.6.5-1.356 /mnt/loop
………… 中间修改此文件系统,等等…………
# umount loop
# cd /boot
# gzip -Array initrd-2.6.5-1.356
# mv initrd-2.6.5-1.356.gz initrd-2.6.5-1.356.img ※※※init进程和inittab引导指令
  加载完内核后由内核执行的第一个程序就是/sbin/init,所以init的PID永远是1。init程序需要读取配置文件/etc/inittab。inittab是一个不可执行的文本文件,它有若干行指令所组成。我们来分析一下inittab文件的内容及意义:
inittab文件每一行都有以下格式:
id:runlevel:action:process
如id:3:initdefault:对上面各项的详细解释如下:
  1. id
  id是指入口标识符,它是一个字符串,对于getty或mingetty等其他login程序项,要求id与tty的编号相同,否则getty程序将不能正常工作。
  2. runlevel
  runlevel是init所处于的运行级别的标识,一般使用0-6以及S或s。0、1、6运行级别被系统保留:其中0作为shutdown动作,1作为重启至单用户模式,6为重启;S和s意义相同,表示单用户模式,且无需inittab文件,因此也不在inittab中出现,实际上,进入单用户模式时,init直接在控制台(/dev/console)上运行/sbin/sulogin。在一般的系统实现中,都使用了2、3、4、5几个级别,在Redhat系统中,2表示无NFS支持的多用户模式,3表示完全多用户模式(也是最常用的级别),4保留给用户自定义,5表示XDM图形登录方式。7-Array级别也是可以使用的,传统的Unix系统没有定义这几个级别。runlevel可以是并列的多个值,以匹配多个运行级别,对大多数action来说,仅当runlevel与当前运行级别匹配成功才会执行。
  3. action
  action是描述其后的process的运行方式的。action可取的值包括:initdefault、sysinit、boot、bootwait等:
  initdefault是一个特殊的action值,用于标识缺省的启动级别;当init由核心激活以后,它将读取inittab中的initdefault项,取得其中的runlevel,并作为当前的运行级别。如果没有inittab文件,或者其中没有initdefault项,init将在控制台上请求输入runlevel。
  sysinit、boot、bootwait等action将在系统启动时无条件运行,而忽略其中的runlevel。
  其余的action(不含initdefault)都与某个runlevel相关。各个action的定义在inittab的man手册中有详细的描述。
????????????????????????????????????????????????????????????
id:3:initdefault:
init的首先根据/etc/inittab的信息确定以何种运行级别进入Linux系统。1表示以单用户模式登陆。2表示无NFS支持的多用户模式,3表示完全多用户模式,5则是以图形界面启动。0和6代表关机与重启,所以这里千万不可以设置成0或6。4保留给用户自定义。
si::sysinit:/etc/rc.d/rc.sysinit
启动时自动执行/etc/rc.d/rc.sysinit脚本完成系统初始化,详见下面
l0:0:wait:/etc/rc.d/rc 0
当运行级别为0时,以0为参数运行/etc/rc.d/rc脚本,init将等待其返回
ca::ctrlaltdel:/sbin/shutdown -t3 -r now
在启动过程中允许按CTRL-ALT-DELETE重启系统
1:2345:respawn:/sbin/mingetty tty1
在2、3、4、5级别上以ttyX为参数执行/sbin/mingetty程序,打开ttyX终端用于用户登录。如果进程退出则再次运行mingetty程序(respawn)
x:5:respawn:/etc/X11/prefdm -nodaemon
在5级别上运行xdm程序,提供xdm图形方式登录界面,并在退出时重新执行(respawn)
????????????????????????????????????????????????????
/etc/rc.d/rc.sysinit 是一个bash shell的脚本,它主要是完成一些系统初始化的工作,rc.sysinit是每一个运行级别都要首先运行的重要脚本。它主要完成的工作有:激活交换分区,检查磁盘,加载硬件模块以及其它一些需要优先执行任务。这个文件的主要内容包括:
  设定默认路径(PATH环境变量)
  设定网络状态,读取/etc/sysconfig/network所记录的网络信息如NETWORKING,HOSTNAME,GATEWAY等。
  挂载系统交换分区
  检查文件系统:系统自动检查/fsckoptions、/forcfsck、/fastboot等目录,在非正常关机情况下强制检查root的文件系统。
  装载/proc这个保存在内存中的主机外设信息
  以及Linux操作系统最基本的其它信息
  设定即插即用设备的参数数据
  清除/etc/mtab
  加载模块 /etc/modules.conf(RHEL3)   /etc/modprobe.conf(RHEL4)
  检查其它文件系统
  挂载其它文件系统
  设定console字型
  打开quota
  清除不必要的文件如lock,pid等
  设定clock
  串口初始化
  将启动信息由dmesg放入/var/log/dmesg中
???????????????????????????????????????????????????????
运行级别脚本
  在rc.sysinit执行后,将返回init接下来执行/etc/rc.d/rc程序。
l5:5:wait:/etc/rc.d/rc 5
    这一行表示以5为参数运行/etc/rc.d/rc,/etc/rc.d/rc是一个Shell脚本,它接受5作为参数,去执行/etc/rc.d/rc5.d/目录下的所有的rc启动脚本,/etc/rc.d/rc5.d/目录中的这些启动脚本实际上都是一些链接文件,而不是真正的rc启动脚本,真正的rc启动脚本实际上都是放在/etc/rc.d/init.d/目录下。而这些rc启动脚本有着类似的用法,它们一般能接受start、stop、restart、status等参数。
    /etc/rc.d/rc5.d/中的rc启动脚本通常是K或S开头的链接文件,对于以以S开头的启动脚本,将以start参数来运行。而如果发现存在相应的脚本也存在K打头的链接,而且已经处于运行态了(以/var/lock/subsys/下的文件作为标志),则将以stop为参数停止这些已经启动了的守护进程。后面的数字则表示启动的顺序,如先启动网络服务才能启动apache服务。
        rc.local, 可以根据需求定制rc.local,2345级别下sArrayArraylocal都是/etc/rc.d/rc.local的软链接
  当需要在某一运行级别启动某服务时,只需要把支持start/stop/status参数的脚本文件拷贝到/etc/rc.d/init.d/下,并运行 chkconfig --add 脚本名 来添加服务即可,或者也可以在运行级别目录下创建该脚本文件的S开头的链接文件。
???????????????????????????????????????????????????????
※※※getty和login
    rc执行完毕后,返回init。这时基本系统环境已经设置好了,各种守护进程也已经启动了。init接下来会打开6个终端,以便用户登录系统。通过按Alt+Fn(n对应1-6)可以在这6个终端中切换。在这个登录界面中会提示用户输入用户名,而用户输入的用户将作为参数传给login程序来验证用户的身份。
    login会接收mingetty传来的用户名作为用户名参数,启用pam系统对用户进行验证。login会对用户名进行分析:如果用户名不是root,且存在/etc/nologin文件,login将输出nologin文件的内容,然后退出。这通常用来系统维护时防止非root用户登录。只有/etc/securetty中登记了的终端才允许root用户登录,如果不存在这个文件,则root可以在任何终端上登录。/etc/usertty文件用于对用户作出附加访问限制,如果不存在这个文件,则没有其他限制。
    在分析完用户名后,login将搜索/etc/passwd以及/etc/shadow来验证密码以及设置账户的其它信息,比如:主目录是什么、使用何种shell。如果没有指定主目录,将默认为根目录;如果没有指定shell,将默认为/bin/bash。
    login程序成功后,会向对应的终端在输出最近一次登录的信息(在/var/log/lastlog中有记录),并检查用户是否有新邮件(在/usr/spool/mail/的对应用户名目录下)。然后开始设置各种环境变量:对于bash来说,系统首先寻找/etc/profile脚本文件,并执行它;然后如果用户的主目录中存在.bash_profile文件,就执行它,在这些文件中又可能调用了其它配置文件,所有的配置文件执行后后,各种环境变量也设好了,这时会出现大家熟悉的命令行提示符,到此整个启动过程就结束了。
故障排除
引导修复
  引导阶段是系统特别脆弱的一个阶段,配置文件的错误、丢失设备或设备不可靠,及文件系统损坏都会阻碍系统的正常启动。常见的系统启动故障及排除思路如下:
1系统启动时显示DISK BOOT FAILURE,INSERT SYSTEM DISK AND PRESS ENTER或大写GRUB等字样
2系统启动时显示:grub>
3系统提示输入密码或Ctrl+D重新启动电脑
4系统启动时显示:kernel panic,键盘上三个灯同时闪动
1. 如果选择把GRUB安装在MBR中,由于MBR只有446字节的空间可利用,Grub只能把一部分stage1保存在MBR中,另一部分stage2保存在硬盘/boot/grub/中。如果stage1出现故障,系统将无法找到启动盘,屏幕提示:“DISK BOOT FAILURE,INSERT SYSTEM DISK AND PRESS ENTER”;如果GRUB的stage 2出现故障,系统启动时将出现大写GRUB提示。这时需要用第一张系统盘重新安装GRUB。重新安装GRUB可以修改stage1和stage2的错误。
用第一张系统光盘启动计算机,在“boot #”提示符下输入“linux rescue”启动系统。如果光盘启动后无法找到Linux分区,则说明MBR中分区表损坏或者更严重,请使用其它分区表恢复工具修复硬盘分区表。

boot: linux rescue
#更改根目录:
chroot /mnt/sysimage/ 
#安装GRUB到MBR:
grub-install /dev/hda
#如果安装到其它分区,如安装到第三个分区,运行 grub-install /dev/hda32.    出现这种情况是因为GRUB配置文件grub.conf出错,这时可以在此命令行模式下手工加载内核与initrd启动系统后修改配置文件。
手工加载的方法就是依照grub.conf配置文件的格式手工给出内核与init的位置

grub>root (hd0,0) #这一步根据你的boot分区的位置指定,此设定为主硬盘的MBR
kernel /vmlinuz-2.6.Array-22.EL   root=LABEL=/    #根据分区情况指定内核位置与主目录,在此过程中可通过tab键查看
initrd /initrd-2.6.Array-22.EL.img
boot3.   出现这种情况是因为系统加载分区失败,在此情况下,输入root用户的密码,进入系统后重新手工加载读写权限的分区,修改自动加载分区的配置文件/etc/fstab,注意检查分区标签是否一致。 
4.   出现这种情况一般是因为加载根分区出错,这时可通过修改grub.conf文件,使root指向正确的分区。如果这里使用的是卷标,就要使 root=LABEL=/ 中的卷标与根分区的实际卷标一致。可通过e2labe /dev/hda2 /来修正分区卷标或者使root=/dev/hda2直接指向分区。(此处hda2只是个人情况,你要根据你的根分区所在分区变化)
by Johnny,转载请注明出处

你可能感兴趣的:(linux,shell,脚本,action,login,linux内核)