Linux启动过程综述

Linux启动过程综述
2011年05月08日
  [b][/b] Linux启动过程综述
  [b][/b]启动第一步:加载BIOS
  当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它。这是因为BIOS中包含了CPU的相关信息、设备启动顺序信息、硬盘信息、内存信息、时钟信息、PnP特性等等。在此之后,计算机心里就有谱了,知道应该去读取哪个硬件设备了。在BIOS将系统的控制权交给硬盘第一个扇区之后,就开始由Linux来控制系统了。
  启动第二步:读取MBR
  硬盘上第0磁道第一个扇区被称为MBR,也就是Master Boot Record,即主引导记录,它的大小是512字节,可里面却存放了预启动信息、分区表信息。可分为两部分:第一部分为引导(PRE-BOOT)区,占了446个字节;第二部分为分区表(PARTITION PABLE),共有66个字节,记录硬盘的分区信息。预引导区的作用之一是找到标记为活动(ACTIVE)的分区,并将活动分区的引导区读入内存。
  系统找到BIOS所指定的硬盘的MBR后,就会将其复制到0×7c00地址所在的物理内存中。其实被复制到物理内存的内容就是Boot Loader,而具体到你的电脑,那就是lilo或者grub了。
  启动第三步:Boot Loader
  Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核做好一切准备。通常,BootL oade:是严重地依赖于硬件而实现的,不同体系结构的系统存在着不同的Boot Loader。
  Linux的引导扇区内容是采用汇编语言编写的程序,其源代码在arch/i386/boot中(不同体系的CPU有其各自的boot目录),有4个程序文件:
  ◎bootsect.S,引导扇区的主程序,汇编后的代码不超过512字节,即一个扇区的 大 小
  ◎setup.S, 引导辅助程序
  ◎edd.S,辅助程序的一部分,用于支持BIOS增强磁盘设备服务
  ◎video.S,辅助程序的另一部分,用于引导时的屏幕显示
  Boot Loader有若干种,其中Grub、Lilo和spfdisk是常见的Loader,这里以Grub为例来讲解吧。
  系统读取内存中的grub配置信息(一般为menu.lst或grub.lst),并依照此配置信息来启动不同的操作系统。
  cat /boot/grub/grub.conf
  # grub.conf generated by anaconda
  #
  # Note that you do not have to rerun grub after making changes to this file
  # NOTICE: You have a /boot partition. This means that
  # all kernel and initrd paths are relative to /boot/, eg.
  # root (hd0,0)
  # kernel /vmlinuz-version ro root=/dev/sda3
  # initrd /initrd-version.img
  #boot=/dev/sda
  default=0
  timeout=5
  splashimage=(hd0,0)/grub/splash.xpm.gz
  hiddenmenu
  title Red Hat Enterprise Linux Server (2.6.18-164.el5)
  root (hd0,0)
  kernel /vmlinuz-2.6.18-164.el5 ro root=LABEL=/ rhgb quiet
  initrd /initrd-2.6.18-164.el5.img
  启动第四步:加载内核
  根据grub设定的内核映像所在路径,系统读取内存映像,并进行解压缩操作。此时,屏幕一般会输出“Uncompressing Linux”的提示。当解压缩内核完成后,屏幕输出“OK, booting the kernel”。系统将解压后的内核放置在内存之中,并调用start_kernel()函数来启动一系列的初始化函数并初始化各种设备,完成Linux核心环境的建立。至此,Linux内核已经建立起来了,基于Linux的程序应该可以正常运行了。
  核心数据结构初始化--内核引导第一部分
  start_kenrel()定义在init/main.c中,它就类似于一般可执行程序中的main()函数,系统在此之前所做的仅仅是一些能让内核程序最低限度执行的初始化操作,真正的内核初始化过程是从这里才开始。函数start_kerenl()将会调用一系列的初始化函数,用来完成内核本身的各方面设置,目的是最终建立起基本完整的Linux核心环境。
  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 SLABcache("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_kenrel()的结尾,内核通过kenrel_thread()创建出第一个系统内核线程(即1号进程),该线程执行的是内核中的init()函数,负责的是下一阶段的启动任务。最后调用cpues_idle()函数:进入了系统主循环体口默认将一直执行default_idle()函数中的指令,即CPU的halt指令,直到就绪队列中存在其他进程需要被调度时才会转向执行其他函数。此时,系统中唯一存在就绪状态的进程就是由kerne_hread()创建的init进程(内核线程),所以内核并不进入default_idle()函数,而是转向init()函数继续启动过程。
  外设初始化--内核引导第二部分
  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根据inittab文件来设定运行等级init进程是系统所有进程的起点,内核在完成核内引导以后,即在本线程(进程)空间内加载init程序,它的进程号是1。
  init程序需要读取/etc/inittab文件作为其行为指针,inittab是以行为单位的描述性(非执行性)文本,每一个指令行都具有以下格式:
  id:runlevel:action:process其中id为入口标识符,runlevel为运行级别,action为动作代号,process为具体的执行程序。
  其实/etc/inittab文件最主要的作用就是设定Linux的运行等级,其设定形式是“:id:5:initdefault:”,这就表明Linux需要运行在等级5上。Linux的运行等级设定如下:
  0:关机
  1:单用户模式
  2:无网络支持的多用户模式
  3:有网络支持的多用户模式
  4:保留,未使用
  5:有网络支持有X-Window支持的多用户模式
  6:重新引导系统,即重启
  [b][/b]启动第六步:init进程执行rc.sysinit
  在设定了运行等级后,Linux系统执行的第一个用户层文件就是/etc/rc.d/rc.sysinit脚本程序,它做的工作非常多,包括设定PATH、设定网络配置(/etc/sysconfig/network)、启动swap分区、设定/proc等等。如果你有兴趣,可以到/etc/rc.d中查看一下rc.sysinit文件。
  线程init的最终完成状态是能够使得一般的用户程序可以正常地被执行,从而真正完成可供应用程序运行的系统环境。它主要进行的操作有:
      (1) 执行函数do_basic_setup(),它会对外部设备进行全面地初始化。
      (2) 构建系统的虚拟文件系统目录树,挂接系统中作为根目录的设备(其具体的文 件系统已经在上一步骤中注册)。
      (3) 打开设备/dev/console,并通过函数sys_dup()打开的连接复制两次,使得文件号0,1 ,2 全部指向控制台。这三个文件连接就是通常所说的“标准输入”stdin,“标准输出”stdout和“标准出错信息”stderr这三个标准I/O通道。
      (4) 准备好以上一切之后,系统开始进入用户层的初始化阶段。内核通过系统调用execve()加载执T子相应的用户层初始化程序,依次尝试加载程序"/sbin/initl"," /etc/init"," /bin/init',和“/bin/sh。只要其中有一个程序加载获得成功,那么系统就将开始用户层的初始化,而不会再回到init()函数段中。至此,init()函数结束,Linux内核的引导 部分也到此结束。
  [b][/b]启动第七步:装载内核模块
  具体是依据/etc/modules.conf文件或/etc/modules.d目录下的文件来装载内核模块。
  启动第八步:启动不同运行级别的rc.d脚本程序
  [b][/b]根据运行级别的不同,系统会运行rc0.d到rc6.d中的相应的脚本程序,来完成相应的初始化工作和启动相应的服务。
  不同的run-level会有不同的服务启动。到/etc/rc。d目录中,不同的level会有不同的目录。如启动 3模式,会有个rc3。d目录,里面就保存着服务。其中,S(start)开头的表明开机启动,K(kill)开头的表明开机不启动。数字表示启动顺序。 数字越小,启动越早。
  [b][/b]启动第九步:执行rc.local用户启动脚本
  你如果打开了此文件,里面有一句话,读过之后,你就会对此命令的作用一目了然:
  # This script will be executed *after* all the other init scripts.
  # You can put your own initialization stuff in here if you don’t
  # want to do the full Sys V style init stuff.
  rc.local就是在一切初始化工作后,Linux留给用户进行个性化的地方。你可以把你想设置和启动的东西放到这里。、
  启动第十步:执行/bin/login程序,进入登录状态
  在rc返回后,init将得到控制,并启动mingetty(见第五节)。mingetty是getty的简化,不能处理串口操作。getty的功能一般包括:
  打开终端线,并设置模式输出登录界面及提示,接受用户名的输入以该用户名作为login的参数,加载login程序 注:用于远程登录的提示信息位于/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的任务也就完成了。
  此时,系统已经进入到了等待用户输入username和password的时候了,你已经可以用自己的帐号登入系统了。
  启动第十一步:运行等级为3的用户,启动指定的bash[b]
  [/b]
  运行级别3下的用户login以后,将启动一个用户指定的shell,以下以/bin/bash为例继续我们的启动过程。
  bash是BourneShell的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作为用户的命令解释器。
  启动第十一步:运行等级为5的用户,启动指定的图形登录界面
  [b][b][/b][/b][b][b][/b][/b]
  如果缺省运行级别设为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都差不多。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值