Linux启动过程

许多人对Linux的启动过程感到很神秘,因为所有的启动信息都在屏幕上一闪而过。其实 Linux的启动过程并不象启动信息所显示的那样复杂,它主要分成两个阶段:
   1.启动内核。在这个阶段,内核装入内存并在初始化每个设备驱动器时打印信息。
   2.执行程序init。装入内核并初始化设备后,运行init程序。init程序处理所有程序的启动,
包括重要系统精灵程序和其它指定在启动时装入的软件。
   下面以Red Hat为例简单介绍一下Linux的启动过程。
 一、启动内核
   首先介绍启动内核部分。电脑启动时,BIOS装载MBR,然后从当前活动分区启动,LILO获得引导过程的控制权后,会显示LILO提示符。此时如果用户不进行任何操作,LILO将在等待制定时间后自动引导默认的操作系统,而如果在此期间按下TAB键,则可以看到一个可引导的操作系统列表,选择相应的操作系统名称就能进入相应的操作系统。当用户选择启动LINUX操作系统时,LILO就会根据事先设置好的信息从ROOT文件系统所在的分区读取LINUX映象,然后装入内核映象并将控制权交给LINUX内核。LINUX内核获得控制权后,以如下步骤继续引导系统:
   1. LINUX内核一般是压缩保存的,因此,它首先要进行自身的解压缩。内核映象前面的一些代码完成解压缩。
   2. 如果系统中安装有可支持特殊文本模式的、且LINUX可识别的SVGA卡,LINUX会提示用户选择适当的文本显示模式。但如果在内核的编译过程中预先设置了文本模式,则不会提示选择显示模式。该显示模式可通过LILO或RDEV工具程序设置。
   3. 内核接下来检测其他的硬件设备,例如硬盘、软盘和网卡等,并对相应的设备驱动程序进行配置。这时,显示器上出现内核运行输出的一些硬件信息。
   4. 接下来,内核装载ROOT文件系统。ROOT文件系统的位置可在编译内核时指定,也可通过LILO或RDEV指定。文件系统的类型可自动检测。如果由于某些原因装载失败,则内核启动失败,最终会终止系统。
  
二、执行init程序
   其次介绍init程序,利用init程序可以方便地定制启动其间装入哪些程序。init的任务是启动新进程和退出时重新启动其它进程。例如,在大多数Linux系统中,启动时最初装入六个虚拟的控制台进程,退出控制台窗口时,进程死亡,然后init启动新的虚拟登录控制台,因而总是提供六个虚拟登陆控控制台进程。控制init程序操作的规则存放在文件/etc/inittab中。Red Hat Linux缺省的inittab文件如下:
   #
   #inittab This file describes how the INIT process should set up the system in a certain
   #run-level.
   #
   #
   #Default runlevel.The runlevels used by RHS are:
   #0-halt(Do NOT set initdefault to this)
   #1-Single user mode
   #2-Multiuser,without NFS(the same as 3,if you do not have networking)
   #3-Full multiuser mode
   #4-unused
   #5-X11
   #6-reboot(Do NOT set initdefault to this)
   #
   id:3:initdefault:
  
   #system initialization
   si::sysinit:/etc/rc.d/rc.sysinit
   10:0:wait:/etc/rc.d/rc 0
   11:1:wait:/etc/rc.d/rc 1
   12:2:wait:/etc/rc.d/rc 2
   13:3:wait:/etc/rc.d/rc 3
   14:4:wait:/etc/rc.d/rc 4
   15:5:wait:/etc/rc.d/rc 5
   16:6:wait:/etc/rc.d/rc 6
   #Things to run in every runlevel
   ud:once:/sbin/update
  
   #Trap CTRL-ALT-DELETE
   ca::ctrlaltdel:/sbin/shutdown -t3 -r now
  
   #When our UPS tells us power has failed,assume we have a few minutes of
   power left.Schedule a
   #shutdown for 2 minutes from now.
   #This does,of course,assume you have powered installed and your UPS
   connected and working
   #correctly.
   pf::powerfail:/sbin/shutdown -f -h 2 “Power Restored;Shutdown Cancelled”
  
   #Run gettys in standard runlevels
   1:12345:respawn:/sbin/minggetty tty1
   2:2345:respawn:/sbin/minggetty tty2
   3:2345:respawn:/sbin/minggetty tty3
   4:2345:respawn:/sbin/minggetty tty4
   5:2345:respawn:/sbin/minggetty tty5
   6:2345:respawn:/sbin/minggetty tty6
   #Run xdm in runlevel 5
  
   x:5:respawn:/usr/bin/X11/xdm -nodaemon
   Linux有个运行级系统,运行级是表示系统当前状态和init应运行哪个进程并保持在这种
   系统状态中运行的数字。在inittab文件中,第一个项目指定启动时装入的缺省运行级。
   上例中是个多用户控制台方式,运行级为3。然后,inittab文件中每个项目指定第二个
   字段的项目用哪种运行级(每个字段用冒号分开)。因此,对运行级3,下列行是相关的:
   13:3:wait:/etc/rc.d/rc 3
   1:12345:respawn:/sbin/minggetty tty1
   2:2345:respawn:/sbin/minggetty tty2
   3:2345:respawn:/sbin/minggetty tty3
   4:2345:respawn:/sbin/minggetty tty4
   5:2345:respawn:/sbin/minggetty tty5
   6:2345:respawn:/sbin/minggetty tty6
   最后六行建立Linux提供的六个虚拟控制台。第一行运行启动脚本/etc/rc.d/ rc 3;
   这将运行目录/etc/
  rc.d/rc3.d中包含的所有脚本,这些脚本表示系统初始化时要启动的程序。一般来说,
  这些脚本不需要编辑或改变,是系统缺省的。
  
  
  
  
  
  
  
  
 Linux启动流程
  从用户打开电源到用户可以登录的这短短的一段时间内,Red Hat Enterprise Linux到底都作了哪些事情,只有知道了这些事情,用户在以后的使用过程中,如果出现了一些问题,我们可以借助这些过程来为我们排除一些故障。
  Red Hat Enterprise Linux在电脑的启动阶段,一共经历以下两个阶段,如图1:
  
  
BIOS自检
当电脑开机的时候,电脑会进入BIOS,BIOS的工作主要是侦测电脑的周边配套设备是否工作正常,如CPU的类型、速度、缓存等
主板类型
内存的速度,容量
硬盘的大小,类型和工作模式
风扇速度等
主要是为了检查这些设备在开机的时候是否能通过检测,如果能通过检测,说明电脑可以正常的工作。
  ------------------------------
载入启动程序
  BIOS自检完成后,BIOS会根据用户设置的启动顺序来由那个设备来启动电脑的操作系统,这个设备一般是硬盘。
  也就是进入到硬盘的MBR区域,这个区域中的有512个字节的大小,其中前446个字节中保存的程序是选择启动分区,也就是电脑由那个硬盘分区来载入开机的程序。那么在这个446个字节的空间中保存的就是启动程序,然后由这个小程序来加载存储在其他位置的操作系统,也就是启动grub程序。如图02所示:
  
  
grub程序的这个配置文件是保存在:/boot/grun/grub.conf这个文件中,如果修改完这个文件后,设置会立刻生效。现在来看看这个文件的内容以及语法:如图03所示:
  
  ? 使用cat /boot/grub/grub.conf,就会出现这个文件的内容,最前面是注释。
  ? 可以将这个文件逻辑上分为两个部分,第一个部分是基本设定,第二个部分是区分开多个操作系统的设定。
  ? 第一个部分中的defaule=0,是指第一组操作系统开机。如果有两组操作系统的开机设定,而defaule=1,那么预设使用第二组操作系统开机。
? 所谓 第一组和第二组程序就是指的是title开始的部分,这里是区分操作系统的部分。如何知道要使用那一个系统呢,可以看到在title开始的部分的下一行有:
? root (hd0,0)
? hd0,表示第一个硬盘
? 0:指的是硬盘的第一个分区,
? 在括号中的那个0和defaule=0是一一对应的。这就可以知道是启动的是那一个操作系统。
  ? timeout=5是指进入GRUB的画面后,会有5秒的时间让使用者选择使用那个操作系统开机。如果在这个时间没有作出选择那么,那么就使用defaule的设定
  ? splashimage=是开机使用的背景图案。
? hd0,表示第一个硬盘
? 0:指的是硬盘的第一个分区,和上面的一样,
? /grub/splash.xpm.gz就是开机使用的背景图案的文件名称
  ? hiddenmenu指令是隐藏开机的选单。
  ? title:开机选单的标题名称。
?
? root (hd0,0):0表示下面要介绍的档案位于那个目录中。(hd0,0)同样是指/boot目录
  ? kernel:存放内核的位置,由于(hd0,0)指的是/boot目录,所以这个文件在boot目录中。
? ro root=LABEL=/就是设根目录的位置,ro表示read only,所以有这行的设定,才能读取根目录。
? rhgb:red hat图形界面启动,取代以前的文本界面。如果要使用文本界面的形式启动,只要将rhgb删除即可。
? quite:在开机过程中不要显示错误的信息。如果要显示错误信息,只要删除quite即可。
? initrd:将initrd映像文件加载到内存。这个文件里面存放的都是驱动程序。
  
--------------------------------------
加载内核
  正确的启动了启动程序之后,接下来的工作就是就会载入操作系统的内核(Kernel)。内核主要作用是
取得BIOS所检测到的硬件设备的信息,然后将这些硬件设备自己来管理,这样才能够提供给Linux系统使用。接手了硬件设备后,然后就要加载这些设备的驱动程序。以便于控制电脑上的设备如何正确的工作。
  加载完硬件的驱动程序后,接下来就股灾文件系统了,也就是加载开机所需要的库文件,程序等,所以/etc /bin /sbin /dev /lib这些目录的根目录必须是同一个分区,否则会造成Linux的开机失败。
  上面在grub中有“ro root=LABEL=/”这样的信息,是以只读的方式来加载所需要的文件,程序,这是为什么呢?
  是因为Linux的内核在启动的过程中,不知道将会发生什么故障,可能不是很稳定,如果以可读可写的方式来加载,那么启动的过过程组中如果出现异常或者是断电,那么就有可能破坏,为了避免这些问题的发生,就采取只读的方式来挂载文件系统。
  -----------------------------------------
启动init服务
  加载完成内核之后,Kernel会启动init这个程序,也是Linux内核启动后的第一个动作,如图04所示:
  
  ?
  所以这个程序的PID是永远是1,也是Red Hat Enterprise Linux中执行的第一个程序,这个程序会根据Run Level来执行一些相关的程序程序。
  启动init服务的目的就是为了用来初始化Linux的环境。也就代表了Linux已经顺利的加载了内核,这个时候的系统启动就进入了另外的一个阶段:系统初始化阶段
  
-------------------------------
系统初始化系统环境
  
init程序其实是去读取/etc/inittab配置文件的配置,根据配置来决定作什么工作
  可以使用cat /etc/inittab指令,就可以看到这个文件的内容,里面设置了init需要作那些工作。如图05所示:
  
1、决定要使用那个run level
?? run level可以从0到6分成7种,一般是为了执行不同的程序或环境而设置的
  run level 0:是作关机,所以不能设置initdefault中,也就是id:5:initdefaule的设置。否者一开机就会作关机的动作。
run level 1:是Single user mode模式,只允许root账号登录,主要是作一些系统维护的工作。
  run level 2:可以使所有的用户登录,但不会启用NFS working,也就是没有网络功能
  run level 3:可以使所有用户登录,并拥有完整的功能。包含run level 2没有的功能,但是开机后是用文本模式
  run level 4 :使用者自己定义,但是默认情况下和run level 3完全相同,
  run level 5:和run level 3几乎一样,唯一的不同是开机后是图形界面,
  run level 6:重启。所以run level 6也不会设置在initdefault中,否者开机后立刻重启
  一般情况下我们使用最多的是1,3,5这三个run level,那么这三个有什么不同的呢,主要差别在执行程序的数量不同。
  可以使用init指令来定义使用那个run level,只要在命名提示符号的后面输入"init 3"就进入文本界面,如果输入"init 6"那么电脑就会重启,如果是"init 0",那就关机。
  可以在以下的地方来定义电脑启动后进行那个run level,如图05中的所示:
  
  ?? id:5:initdefault:默认是run level 5,是图形界面。
  2、执行初始操作系统的程序
?? 如图06所示:
  
  ?? 设定执行/etc/rc.d/rc.sysinit这个程序,来初始化操作系统。如图07所示:
  
  ?? 这个程序的作用主要是用来:
  ?? 1、设定电脑的名称,hostname在/etc/sysconfig/network文件中
?? 2、设定系统的时间
?? 3、定义键盘
?? 4、启用swap分区
?? 5、检查根目录(/)有没有问题。并且挂载成可读可写的状态。
?? 6、设定系统的时间
?? 7、加载一些其他的设备,如RAID和LVM
  ?? 具体的内容大家可以自己去研究这个文件的其他的功能,但是主要是用来来初始化操作系统的。
  
3、执行run level对应目录中的程序。
  ?? 用来决定启用那些服务,也就是执行rc程序,如图08所示:
  
  ?? 本例中是run level是5,那么就会把5的参数传递给/etc/rc.d/rc这个程序,后面的黑色的5,就是把5这个参数传递个rc这个程序执行的意思。rc 5所执行的结果就会是执行/etc/rc.d/rc5.d/这个目录下的所有程序。
  ?? 如输入ls -l /etc/rc.d这个指令,就可以看到所有的run level所对应的目录。如图09所示:
  
  
在这些目录当中,都是内置一些要执行的一些程序。也就是要启动那些服务。本例的run level是5,就会执行rc5.d这个目录中的程序。那么就会启动这个目录中的服务。如图10,查看这个目录中有哪些内容:
  
  
可以发现,这个文件夹中的都是一些链接文件,这些链接就是来呼叫/init.d目录中程序,并带有一个启动或停止服务的参数。
  分析一下这些名称链接的格式:如图10中所示,这些文件可以分为主要的三个小段:
  ??? 1、以K或S开头。K=Kill表示停用,S=Start表示启用服务,并且K先执行,然后执行S。
??? 2、是两位的数字,代表执行的先后顺序,数字越小,越先执行。
??? 3、是程序名称。
  系统会按照以上的这个目录中的程序,先关闭某些程序,然后启动某些需要的服务。
  ?
  4、再设定某个组合键
?? 如图11所示:
  
  
?? 当启动的过程中按下crtl+alt+del键会执行shutdown -t3 -r now这个指令。
  
5、定义UPS不间断电源系统
? 如图12所示:
  ?
  
  
?? 如果你的服务器有UPS,当电源发生问题或电源恢复的时候,执行这些程序,
?? 当电源发生问题时,执行shutdown -f -h +2
  ?? 当电源恢复工作时,执行shutdown -c 表示在关机前取消关机的操作,这个时候必须取消关机操作。
?? 由于UPS可以短暂的为linux服务器提供电力服务,但是UPS不会一直提供电力,到UPS电力用完,可能会造成Linux不正常关机,而是UPS会通知电脑,电源发生问题,要求linux正常的关机,避免内存的资料没有保存到硬盘上。以上的功能必须是UPS支持以上的功能的是够才会发生这些动作,如果UPS不支持这些功能,这两条语句没有任何的作用。
  
6、产生6个虚拟控制台。也就是tty1到tty6,
  ?? 是使用mingetty指令分别产生tty1到tty6这6个虚拟控制台。如图13所示:
  
  
7、如果启用的是run level 5,就会初始化X Windows环境。
?? 如图14所示:
  
  
?? /etc/X11/prefdm -nodemon:就是初始化X Windows环境
  ------------------------------------------
  /etc/rc.d/rc.local程序
  如图15所示:
  
  ??? 执行Run Level目录中相对的程序链接后,都会最后执行rc.local程序。
??? S后面的99表示最后执行的意思。
??? 所以平时我们可以修改这个文件,可以将需要将开机执行的程序或者是指令写入这个文件中。
  ------------------------------
  这样,系统就启动完成了,这就是Linux的整个的详细的一个启动过程,了解了这个过程以后,我们就可以在以后的使用中根据这些来对系统作一些简单的故障排除。如到润level 0中更改root用户的密码等,为grub加密等。
 linux启动过程
  
  一. Bootloader
  二.Kernel引导入口
  三.核心数据结构初始化–内核引导第一部分
  四.外设初始化–内核引导第二部分
  五.init进程和inittab引导指令
  六.rc启动脚本
  七.getty和login
  八.bash
  附:XDM方式登录 作者:杨沙洲
  本文以Redhat 6.0 Linux 2.2.19 for Alpha/AXP为平台,描述了从开机到登录的Linux 启动全过程。该文对i386平台同样适用。
  
  一. Bootloader
  
  在Alpha/AXP平台上引导Linux通常有两种方法,一种是由MILO及其他类似的引导程序引 导,另一种是由Firmware直接引导。MILO功能与i386平台的LILO相近,但内置有基本的磁盘驱动程序(如IDE、SCSI等),以及常见的文件系统驱动程序(如ext2,iso9660等), firmware有ARC、SRM两种形式,ARC具有类BIOS界面,甚至还有多重引导的设置;而SRM则具有功能强大的命令行界面,用户可以在控制台上使用boot等命令引导系统。ARC有分区 (Partition)的概念,因此可以访问到分区的首扇区;而SRM只能将控制转给磁盘的首扇区。两种firmware都可以通过引导MILO来引导Linux,也可以直接引导Linux的引导代码。
  
  “arch/alpha/boot”下就是制作Linux Bootloader的文件。“head.S”文件提供了对OSF PAL/1的调用入口,它将被编译后置于引导扇区(ARC的分区首扇区或SRM的磁盘0扇区), 得到控制后初始化一些数据结构,再将控制转给“main.c”中的start_kernel(), start_kernel()向控制台输出一些提示,调用pal_init()初始化PAL代码,调用openboot() 打开引导设备(通过读取Firmware环境),调用load()将核心代码加载到START_ADDR(见 “include/asm-alpha/system.h”),再将Firmware中的核心引导参数加载到ZERO_PAGE(0) 中,最后调用runkernel()将控制转给0x100000的kernel,bootloader部分结束。
  
  “arch/alpha/boot/bootp.c”以“main.c”为基础,可代替“main.c”与“head.S”生成用于BOOTP协议网络引导的Bootloader。
  Bootloader中使用的所有“srm_”函数在“arch/alpha/lib/”中定义。
  
  以上这种Boot方式是一种最简单的方式,即不需其他工具就能引导Kernel,前提是按照 Makefile的指导,生成bootimage文件,内含以上提到的bootloader以及vmlinux,然后将bootimage写入自磁盘引导扇区始的位置中。
  
  当采用MILO这样的引导程序来引导Linux时,不需要上面所说的Bootloader,而只需要 vmlinux或vmlinux.gz,引导程序会主动解压加载内核到0x1000(小内核)或0x100000(大内核),并直接进入内核引导部分,即本文的第二节。
  对于I386平台
  i386系统中一般都有BIOS做最初的引导工作,那就是将四个主分区表中的第一个可引导 分区的第一个扇区加载到实模式地址0x7c00上,然后将控制转交给它。
  
  在“arch/i386/boot”目录下,bootsect.S是生成引导扇区的汇编源码,它首先将自己 拷贝到0x90000上,然后将紧接其后的setup部分(第二扇区)拷贝到0x90200,将真正的内核代码拷贝到0x100000。以上这些拷贝动作都是以bootsect.S、setup.S以及vmlinux在磁盘上 连续存放为前提的,也就是说,我们的bzImage文件或者zImage文件是按照bootsect,setup, vmlinux这样的顺序组织,并存放于始于引导分区的首扇区的连续磁盘扇区之中。
  
  bootsect.S完成加载动作后,就直接跳转到0x90200,这里正是setup.S的程序入口。 setup.S的主要功能就是将系统参数(包括内存、磁盘等,由BIOS返回)拷贝到0x90000-0x901FF内存中,这个地方正是bootsect.S存放的地方,这时它将被系统参数覆盖。 以后这些参数将由保护模式下的代码来读取。除此之外,setup.S还将video.S中的代码包含进来,检测和设置显示器和显示模式。最 后,setup.S将系统转换到保护模式,并跳转到0x100000(对于bzImage格式的大内核是0x100000,对于zImage格式的是0x1000)的内核引导代码,Bootloader过程结束。
  二.Kernel引导入口
  
  在arch/alpha/vmlinux.lds的链接脚本控制下,链接程序将vmlinux的入口置于 “arch/alpha/kernel/head.S"中的__start上,因此当Bootloader跳转到0x100000时, __start处的代码开始执行。__start的代码很简单,只需要设置一下全局变量,然后就跳转 到start_kernel去了。start_kernel()是"init/main.c"中的asmlinkage函数,至此,启动过程转入体系结构无关的通用C代码中。
  对于I386平台
  在i386体系结构中,因为i386本身的问题,在"arch/alpha/kernel/head.S"中需要更多的设置,但最终也是通过call
  SYMBOL_NAME(start_kernel)转到start_kernel()这个体系结构无关的函数中去执行了。
  
  所不同的是,在i386系统中,当内核以bzImage的形式压缩,即大内核方式 (BIG_KERNEL)压缩时就需要预先处理bootsect.S和setup.S,按照大核模式使用$(CPP)处理生成bbootsect.S和bsetup.S,然后再编译生成相应的.o文件,并使用 “arch/i386/boot/compressed/build.c"生成的build工具,将实际的内核(未压缩的,含kernel中的head.S代码)与"arch/i386/boot/compressed"下的head.S和misc.c合成到一起,其中的head.S代替了"arch/i386/kernel/head.S"的位置,由Bootloader引导执行(startup_32入口),然后它调用misc.c中定义的decompress_kernel()函数,使用 “lib/inflate.c"中定义的gunzip()将内核解压到0x100000,再转到其上执行"arch/i386/kernel/head.S"中的startup_32代码。  
  三.核心数据结构初始化–内核引导第一部分
  
  start_kernel()中调用了一系列初始化函数,以完成kernel本身的设置。 这些动作有的是公共的,有的则是需要配置的才会执行的。
  
  在start_kernel()函数中,
  输出Linux版本信息(printk(linux_banner))
  设置与体系结构相关的环境(setup_arch())
  页表结构初始化(paging_init())
  使用"arch/alpha/kernel/entry.S"中的入口点设置系统自陷入口(trap_init())
  使用alpha_mv结构和entry.S入口初始化系统IRQ(init_IRQ())
  核心进程调度器初始化(包括初始化几个缺省的Bottom-half,sched_init())
  时间、定时器初始化(包括读取CMOS时钟、估测主频、初始化定时器中断等,time_init())
  提取并分析核心启动参数(从环境变量中读取参数,设置相应标志位等待处理,(parse_options())
  控制台初始化(为输出信息而先于PCI初始化,console_init())
  剖析器数据结构初始化(prof_buffer和prof_len变量)
  核心Cache初始化(描述Cache信息的Cache,kmem_cache_init())
  延迟校准(获得时钟jiffies与CPU主频ticks的延迟,calibrate_delay())
  内存初始化(设置内存上下界和页表项初始值,mem_init())
  创建和设置内部及通用cache(“slab_cache”,kmem_cache_sizes_init())
  创建uid taskcount SLAB cache(“uid_cache”,uidcache_init())
  创建文件cache(“files_cache”,filescache_init())
  创建目录cache(“dentry_cache”,dcache_init())
  创建与虚存相关的cache(“vm_area_struct”,“mm_struct”,vma_init())
  
  块设备读写缓冲区初始化(同时创建"buffer_head"cache用户加速访问,buffer_init())
  创建页cache(内存页hash表初始化,page_cache_init())
  创建信号队列cache(“signal_queue”,signals_init())
  初始化内存inode表(inode_init())
  创建内存文件描述符表(“filp_cache”,file_table_init())
  检查体系结构漏洞(对于alpha,此函数为空,check_bugs())
  SMP机器其余CPU(除当前引导CPU)初始化(对于没有配置SMP的内核,此函数为空,smp_init())
  启动init过程(创建第一个核心线程,调用init()函数,原执行序列调用cpu_idle() 等待调度,init())
  至此start_kernel()结束,基本的核心环境已经建立起来了。
  对于I386平台
  i386平台上的内核启动过程与此基本相同,所不同的主要是实现方式。
  对于2.4.x版内核
  2.4.x中变化比较大,但基本过程没变,变动的是各个数据结构的具体实现,比如Cache。
  
  四.外设初始化–内核引导第二部分
  
  init()函数作为核心线程,首先锁定内核(仅对SMP机器有效),然后调用 do_basic_setup()完成外设及其驱动程序的加载和初始化。过程如下:
  
  总线初始化(比如pci_init())
  网络初始化(初始化网络数据结构,包括sk_init()、skb_init()和proto_init()三部分,在proto_init()中,将调用protocols结构中包含的所有协议的初始化过程,sock_init())
  创建bdflush核心线程(bdflush()过程常驻核心空间,由核心唤醒来清理被写过的内存缓冲区,当bdflush()由kernel_thread()启动后,它将自己命名为kflushd)
  创建kupdate核心线程(kupdate()过程常驻核心空间,由核心按时调度执行,将内存缓冲区中的信息更新到磁盘中,更新的内容包括超级块和inode表)
  设置并启动核心调页线程kswapd(为了防止kswapd启动时将版本信息输出到其他信息中间,核心线调用kswapd_setup()设置kswapd运行所要求的环境,然后再创建kswapd核心线程)
  创建事件管理核心线程(start_context_thread()函数启动context_thread()过程,并重命名为keventd)
  设备初始化(包括并口parport_init()、字符设备chr_dev_init()、块设备 blk_dev_init()、SCSI设备scsi_dev_init()、网络设备net_dev_init()、磁盘初始化及分区检查等等,device_setup())
  执行文件格式设置(binfmt_setup())
  启动任何使用__initcall标识的函数(方便核心开发者添加启动函数,do_initcalls())
  文件系统初始化(filesystem_setup())
  安装root文件系统(mount_root())
  至此do_basic_setup()函数返回init(),在释放启动内存段(free_initmem())并给内核解锁以后,init()打开/dev/console设备,重定向stdin、stdout和stderr到控制台,最后,搜索文件系统中的init程序(或者由init=命令行参数指定的程序),并使用execve()系统调用加载执行init程序。
init()函数到此结束,内核的引导部分也到此结束了,这个由start_kernel()创建的第一个线程已经成为一个用户模式下的进程了。此时系统中存在着六个运行实体:
  
  start_kernel()本身所在的执行体,这其实是一个"手工"创建的线程,它在创建了init()线程以后就进入cpu_idle()循环了,它不会在进程(线程)列表中出现
  init线程,由start_kernel()创建,当前处于用户态,加载了init程序
  kflushd核心线程,由init线程创建,在核心态运行bdflush()函数
  kupdate核心线程,由init线程创建,在核心态运行kupdate()函数
  kswapd核心线程,由init线程创建,在核心态运行kswapd()函数
  keventd核心线程,由init线程创建,在核心态运行context_thread()函数
  对于I386平台
  基本相同。
  对于2.4.x版内核
  这一部分的启动过程在2.4.x内核中简化了不少,缺省的独立初始化过程只剩下网络 (sock_init())和创建事件管理核心线程,而其他所需要的初始化都使用__initcall()宏
  包含在do_initcalls()函数中启动执行。
  
  五.init进程和inittab引导指令
  
  init进程是系统所有进程的起点,内核在完成核内引导以后,即在本线程(进程)空 间内加载init程序,它的进程号是1。
  init程序需要读取/etc/inittab文件作为其行为指针,inittab是以行为单位的描述性(非执行性)文本,每一个指令行都具有以下格式:
  id:runlevel:action:process其中id为入口标识符,runlevel为运行级别,action为动作代号,process为具体的执行程序。
  id一般要求4个字符以内,对于getty或其他login程序项,要求id与tty的编号相同,否则getty程序将不能正常工作。
  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-9级别也是可以使用的,传统的Unix系统没有定义这几个级别。runlevel可以是并列的多个值,以匹配多个运行级别,对大多数action来说,仅当runlevel与当前运行级别匹配成功才会执行。
  initdefault是一个特殊的action值,用于标识缺省的启动级别;当init由核心激活 以后,它将读取inittab中的initdefault项,取得其中的runlevel,并作为当前的运行级别。如果没有inittab文件,或者其中没有initdefault项,init将在控制台上请求输入 runlevel。
  sysinit、boot、bootwait等action将在系统启动时无条件运行,而忽略其中的runlevel,其余的action(不含initdefault)都与某个runlevel相关。各个action的定义在inittab的man手册中有详细的描述。
  在Redhat系统中,一般情况下inittab都会有如下几项:
  id:3:initdefault:
  #表示当前缺省运行级别为3–完全多任务模式;
  si::sysinit:/etc/rc.d/rc.sysinit
  #启动时自动执行/etc/rc.d/rc.sysinit脚本
  l3:3:wait:/etc/rc.d/rc 3
  #当运行级别为3时,以3为参数运行/etc/rc.d/rc脚本,init将等待其返回
  0:12345:respawn:/sbin/mingetty tty0
  #在1-5各个级别上以tty0为参数执行/sbin/mingetty程序,打开tty0终端用于
  #用户登录,如果进程退出则再次运行mingetty程序
  x:5:respawn:/usr/bin/X11/xdm -nodaemon
  #在5级别上运行xdm程序,提供xdm图形方式登录界面,并在退出时重新执行
  
  六.rc启动脚本
  上一节已经提到init进程将启动运行rc脚本,这一节将介绍rc脚本具体的工作。
  
  一般情况下,rc启动脚本都位于/etc/rc.d目录下,rc.sysinit中最常见的动作就是激活交换分区,检查磁盘,加载硬件模块,这些动作无论哪个运行级别都是需要优先执行的。仅当rc.sysinit执行完以后init才会执行其他的boot或bootwait动作。如果没有其他boot、bootwait动作,在运行级别3下,/etc/rc.d/rc将会得到执行,命令行参数为3,即执行/etc/rc.d/rc3.d/目录下的所有文件。rc3.d下的文件都是指向/etc/rc.d/init.d/目录下各个Shell脚本的符号连接,而这些脚本一般能接受start、stop、restart、status等参数。rc脚本以start参数启动所有以S开头的脚本,在此之前,如果相应的脚本也存在K打头的链接,而且已经处于运行态了(以/var/lock/subsys/下的文件作为标志),则将首先启动K开头的脚本,以stop作为参数停止这些已经启动了的服务,然后再重新运行。显然,这样做的直接目的就是当init改变运行级别时,所有相关的服务都将重启,即使是同一个级别。
  rc程序执行完毕后,系统环境已经设置好了,下面就该用户登录系统了。
  
  七.getty和login
在rc返回后,init将得到控制,并启动mingetty(见第五节)。mingetty是getty的简化,不能处理串口操作。getty的功能一般包括:
  
  打开终端线,并设置模式
  输出登录界面及提示,接受用户名的输入
  以该用户名作为login的参数,加载login程序
  缺省的登录提示记录在/etc/issue文件中,但每次启动,一般都会由rc.local脚本根据系统环境重新生成。
  注:用于远程登录的提示信息位于/etc/issue.net中。
  login程序在getty的同一个进程空间中运行,接受getty传来的用户名参数作为登录的用户名。
  如果用户名不是root,且存在/etc/nologin文件,login将输出nologin文件的内容,然后退出。这通常用来系统维护时防止非root用户登录。
  只有/etc/securetty中登记了的终端才允许root用户登录,如果不存在这个文件,则root可以在任何终端上登录。/etc/usertty文件用于对用户作出附加访问限制,如果不存在这个文件,则没有其他限制。
  当用户登录通过了这些检查后,login将搜索/etc/passwd文件(必要时搜索 /etc/shadow文件)用于匹配密码、设置主目录和加载shell。如果没有指定主目录,将默认为根目录;如果没有指定shell,将默认为/bin/sh。在将控制转交给shell以前, getty将输出/var/log/lastlog中记录的上次登录系统的信息,然后检查用户是否有新邮件(/usr/spool/mail/{username})。在设置好shell的uid、gid,以及TERM,PATH等环境变量以后,进程加载shell,login的任务也就完成了。
  
  八.bash
 运行级别3下的用户login以后,将启动一个用户指定的shell,以下以/bin/bash为例继续我们的启动过程。
 bash是Bourne Shell的GNU扩展,除了继承了sh的所有特点以外,还增加了很多特性和功能。由login启动的bash是作为一个登录shell启动的,它继承了getty设置的TERM、PATH等环境变量,其中PATH对于普通用户为”/bin:/usr/bin:/usr/local/bin”,对于root为”/sbin:/bin:/usr/sbin:/usr/bin"。作为登录shell,它将首先寻找/etc/profile脚本文件,并执行它;然后如果存在~/.bash_profile,则执行它,否则执行 /.bash_login,如果该文件也不存在,则执行/.profile文件。然后bash将作为一个交互式shell执行/.bashrc文件(如果存在的话),很多系统中,/.bashrc都将启动/etc/bashrc作为系统范围内的配置文件。
 当显示出命令行提示符的时候,整个启动过程就结束了。此时的系统,运行着内核,运行着几个核心线程,运行着init进程,运行着一批由rc启动脚本激活的守护进程(如inetd等),运行着一个bash作为用户的命令解释器。
  
  附:XDM方式登录
  
  如果缺省运行级别设为5,则系统中不光有1-6个getty监听着文本终端,还有启动了一个XDM的图形登录窗口。登录过程和文本方式差不多,也需要提供用户名和口令,XDM
  的配置文件缺省为/usr/X11R6/lib/X11/xdm/xdm-config文件,其中指定了 /usr/X11R6/lib/X11/xdm/xsession作为XDM的会话描述脚本。登录成功后,XDM将执行这个脚本以运行一个会话管理器,比如gnome-session等。
  
  除了XDM以外,不同的窗口管理系统(如KDE和GNOME)都提供了一个XDM的替代品,如gdm和kdm,这些程序的功能和XDM都差不多。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值