Fedora 启动顺序

来源:互联网 发布:全栈工程师知乎 编辑:程序博客网 时间:2024/05/18 02:32

http://hi.baidu.com/wwwkljoel/item/29620217882a585b2b3e2244

The start of the Fedora

fedora 系统加电或复位后,中央处理器将内存中的所有数据清零,并对内存进行校验。如果没有错误,则CS(Code Segment)寄存器中全部置1,而IP(Instruction Pointer)寄存器中全部置0。[CS]:[IP]地址单元中存储的指令是一条跳转指令,这条指令跳转到BIOS代码的首部。CPU就根据CS及IP 的值进入BIOS启动过程。

一、BIOS ¶
  • 当微机接通电源后,系统将有一个对内部各个设备进行检查的过程,这是由一个通常称之为POST(Power On Self Test,上电自检)的程序来完成的。这也是BIOS的一个功能。BIOS系统设置程序 微机部件配置情况是放在一块可读写的CMOS RAM芯片中的,BIOS ROM将按照系统CMOS设置中的启动顺序搜寻软硬盘驱动器及CDROM、网络服务器等有效的启动驱动器,读入操作系统引导记录,然后将系统控制权交给引 导记录,由引导记录完成系统的启动。所以有时候启动机子起不来,声卡解压卡显示卡发生冲突;CD-ROM挂不上;不能正常运行一些在DOS、 Windows 3.x下运行得很好的程序,可能和BIOS有密切关系。

关于BIOS
注:Top

二、Boot Loader ¶
  • BIOS 会记录可用來开机的裝置搜索顺序!所以,系統会开始去读取第一个开机裝置上面开机程序。首先第一个要读取的就是该硬盘的主要开机磁区 (Master Boot Record, MBR) 了,而系统可以由主要开机区所安裝的开机引导程序 (boot loader) 开始执行核心程序。 *简单地说,Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状 态,以便为最终调用操作系统内核准备好正确的环境。

通常,Boot Loader 是严重地依赖于硬件而实现的,特别是在嵌入式世界。因此,在嵌入式世界里建立一个通用的 Boot Loader 几乎是不可能的。不过它们仍有一些共性。

  • 怎么会执行先执行Boot Loader呢?
  • 系统加电或复位后,所有的CPU 通常都从某个由 CPU 制造商预先安排的地址上取指令, 取它的第一条指令(BIOS)。而基于CPU 构建的嵌入式系统通常都有某种类型的固态存储设备(比如:ROM、EEPROM 或 FLASH 等)被映射到这个预先安排的地址上。BIOS会将MBR的Boot Loader调至内存0x07C00处,检查 0x0000:7DEF 是不是等于0xAA55,如果不等于则去尝试其他启动介质,如果没有其他启动介质就显示"No ROM BASIC",然后死机。 CPU会读取内存0x07C00处代码,执行。因此在系统加电后,CPU 将执行Boot Loader 程序。
  • 硬盘的0磁道的第一个扇区称为MBR(Master Boot Recorder),它的大小是512字节。Boot Loader通常是一段汇编代码,存放在MBR中,作用是将系统启动代码读到内存中,目前主流的Boot loader有grub, lilo, 还有spfdisk.

下面系统要做的工作就是顺着启动的代码一步步执行。对于Linux源代码来说启动的代码流程如图1所示:

  • Bootsect.S用于装载核心系统和setup.S。 setup.S是用汇编语言写的程序代码。Setup.S对已调入内存内核代码进行检查,如果没有错误的话,它会通过BIOS中断获取内存容量信息、设置 显示器基本模式、获取硬盘信息,这些操作,都是在X86的实模式下进行的,这时操作系统就准备让CPU进入保护模式。进入保护模式的启动阶段的同时把控制 权交给head.S,head.S是段保护模式的汇编程序。Head.S进行页表初始化、检查CPU类型后,调用/init/main.c中的 start_kernel()。Start_kernel()用于和处理器、内存等最基本硬件相关部分的初始化、与中断有关的初始化、对Linux文件系 统进行初始化。
  • 内核映像一般都是经过压缩的,压缩以后的内核映像就像是一大块数据,跟引导扇区和引导辅助程序的映像拼接在一起,成为内核的“引导映 像”。解除压缩以后的内核映像总是放在地址为0x100000(1MB)的地方。Linux核心加载后,由init进程初始化硬件和设备驱动,然后在后台 运行init进程。init进程是系统的第一个进程,init进程运行时将用到两个重要的系统引导配置文件/etc/inittab和 /etc/rc.d。

关于Boot loader
注:Top

三、Initrd ¶
  • 为什么要单独把initrd拿出来当成单独的一步呢?呵呵,因为initrd相当的重要,没有这个或者有点错误就无法启动文件系统 同时在上面的过程中会借助Boot loader来载入initrd ,用 initrd来制作虚拟硬盘(RAM Disk)并且利用linuxrc这支程序来进行模组的载入。 linuxrc 主要的特性是:
  • 必须是 linuxrc 这个档名;
  • 必须放置在 initrd 所建立的虚拟磁碟的最顶层目录;
  • 必须要可以被核心所执行。
  • initrd(boot loader initialized RAM disk)在 linux内核启动前, boot loader 会将存储介质中的 initrd 文件加载到内存,内核启动时会在访问真正的根文件系统前先访问该内存中的 initrd 文件系统。在 boot loader 配置了 initrd 的情况下,内核启动被分成了两个阶段,第一阶段先执行 initrd 文件系统中的"某个文件",完成加载驱动模块等任务,第二阶段才会执行真正的根文件系统中的 /sbin/init 进程。
    2.4版的initrd和2.6版的initrd是不一样的:
  • 为了使读者清晰的了解Linux2.6内核initrd机制的变化,在重点介绍Linux2.6内核initrd之前,先对 linux2.4内核的 initrd进行一个简单的介绍。Linux2.4内核的initrd的格式是文件系统镜像文件,本文将其称为image-initrd,以区别后面介绍 的linux2.6内核的cpio格式的initrd。 linux2.4内核对initrd的处理流程如下:
    • 1. boot loader把内核以及/dev/initrd的内容加载到内存,/dev/initrd是由boot loader初始化的设备,存储着initrd。
    • 2. 在内核初始化过程中,内核把 /dev/initrd 设备的内容解压缩并拷贝到 /dev/ram0 设备上。
    • 3. 内核以可读写的方式把 /dev/ram0 设备挂载为原始的根文件系统。
    • 4. 如果 /dev/ram0 被指定为真正的根文件系统,那么内核跳至最后一步正常启动。
    • 5. 执行 initrd 上的 /linuxrc 文件,linuxrc 通常是一个脚本文件,负责加载内核访问根文件系统必须的驱动, 以及加载根文件系统。
    • 6. /linuxrc 执行完毕,真正的根文件系统被挂载。
    • 7. 如果真正的根文件系统存在 /initrd 目录,那么 /dev/ram0 将从 / 移动到 /initrd。否则如果 /initrd 目录不存在, /dev/ram0 将被卸载。
    • 8. 在真正的根文件系统上进行正常启动过程 ,执行 /sbin/init。 linux2.4 内核的 initrd 的执行是作为内核启动的一个中间阶段,也就是说 initrd 的 /linuxrc 执行以后,内核会继续执行初始化代码,我们后面会看到这是 linux2.4 内核同 2.6 内核的 initrd 处理流程的一个显著区别。
  • Linux2.6 内核对 Initrd 的处理流程
  • linux2.6 内核支持两种格式的 initrd,一种是前面第 3 部分介绍的 linux2.4 内核那种传统格式的文件系统镜像-image-initrd,它的制作方法同 Linux2.4 内核的 initrd 一样,其核心文件就是 /linuxrc。另外一种格式的 initrd 是 cpio 格式的,这种格式的 initrd 从 linux2.5 起开始引入,使用 cpio 工具生成,其核心文件不再是 /linuxrc,而是 /init,本文将这种 initrd 称为 cpio-initrd。尽管 linux2.6 内核对 cpio-initrd和 image-initrd 这两种格式的 initrd 均支持,但对其处理流程有着显著的区别,下面分别介绍 linux2.6 内核对这两种 initrd 的处理流程。 cpio-initrd 的处理流程
    • 1. boot loader 把内核以及 initrd 文件加载到内存的特定位置。
    • 2.内核判断initrd的文件格式,如果是cpio格式。
    • 3. 将initrd的内容释放到rootfs中。
    • 4. 执行initrd中的/init文件,执行到这一点,内核的工作全部结束,完全交给/init文件处理。
  • image-initrd的处理流程
    • 1. boot loader把内核以及initrd文件加载到内存的特定位置。
    • 2. 内核判断initrd的文件格式,如果不是cpio格式,将其作为image-initrd处理。
    • 3. 内核将initrd的内容保存在rootfs下的/initrd.image文件中。
    • 4. 内核将/initrd.image的内容读入/dev/ram0设备中,也就是读入了一个内存盘中。
    • 5. 接着内核以可读写的方式把/dev/ram0设备挂载为原始的根文件系统。
    • 6. .如果/dev/ram0被指定为真正的根文件系统,那么内核跳至最后一步正常启动。
    • 7. 执行initrd上的/linuxrc文件,linuxrc通常是一个脚本文件,负责加载内核访问根文件系统必须的驱动, 以及加载根文件系统。
    • 8. /linuxrc执行完毕,常规根文件系统被挂载
    • 9. 如果常规根文件系统存在/initrd目录,那么/dev/ram0将从/移动到/initrd。否则如果/initrd目录不存在, /dev/ram0将被卸载。
    • 10. 在常规根文件系统上进行正常启动过程 ,执行/sbin/init。 通过上面的流程介绍可知,Linux2.6内核对image-initrd的处理流程同linux2.4内核相比并没有显著的变化, cpio-initrd的处理流程相比于image-initrd的处理流程却有很大的区别,流程非常简单,在后面的源代码分析中,读者更能体会到处理的 简捷。举个简单的例子

更多关于initrd和linuxrc有参考文献
注:Top

四、Init ¶
  • 1.init会根据设定档运行不同的run level,其设定档是/etc/inittab,根据不同的run level会在开机时启动不同的服务.
    在inittab里的格式是这样的:
    id:runlevels:action:process 如下表:
id is a unique sequence of 1-4 characters which identifies an entry in inittab (for versions of sysvinit compiled with libraries < 5.2.18 or a.out libraries the limit is 2 characters). Note: For gettys or other login processes, the id field should be the tty suffix of the corresponding tty, e.g. 1 for tty1. Otherwise, the login accounting might not work correctly. runlevels lists the runlevels for which the specified action should be taken. action "describes which action should be taken. Valid actions for the action field are:" respawn The process will be restarted whenever it terminates (e.g. getty). wait The process will be started once when the specified runlevel is entered and init will wait for its termination. once The process will be executed once when the specified runlevel is entered. boot The process will be executed during system boot. The runlevels field is ignored. bootwait The process will be executed during system boot, while init waits for its termination (e.g. /etc/rc). The runlevels field is ignored. off This does nothing. ondemand A process marked with an ondemand runlevel will be executed whenever the specified ondemand runlevel is called. However, no runlevel change will occur (ondemand runlevels are a', b', and `c'). initdefault An initdefault entry specifies the runlevel which should be entered after system boot. If none exists, init will ask for a runlevel on the console. The process field is ignored. sysinit The process will be executed during system boot. It will be executed before any boot or bootwait entries. The runlevels field is ignored. powerwait The process will be executed when init receives the SIGPWR signal, indicating that there is something wrong with the power. Init will wait for the process to finish before continuing. powerfail As for powerwait, except that init does not wait for the process's completion. powerokwait The process will be executed when init receives the SIGPWR signal, provided there is a file called /etc/powerstatus containing the word OK. This means that the power has come back again. ctrlaltdel The process will be executed when init receives the SIGINT signal. This means that someone on the system console has pressed the CTRL-ALT-DEL key combination. Typically one wants to execute some sort of shutdown either to get into single-user level or to reboot the machine. kbrequest The process will be executed when init receives a signal from the keyboard handler that a special key combination was pressed on the console keyboard. The documentation for this function is not complete yet; more documentation can be found in the kbdx.xx packages (most recent was kbd-0.94 at the time of this writing). Basically you want to map some keyboard combination to the "KeyboardSignal?" action. For example, to map Alt-Uparrow for this purpose use the following in your keymaps file:alt keycode 103=KeyboardSignal? process specifies the process to be executed. If the process field starts with a `+' character, init will not do utmp and wtmp accounting for that process. This is needed for gettys that insist on doing their own utmp/wtmp housekeeping. This is also a historic bug.

有以下几种run level:

# 0 - 停机(千万不能把initdefault 设置为0 )

# 1 - 单用户模式

# 2 - 多用户,没有 NFS

# 3 - 完全多用户模式(标准的运行级)

# 4 - 预设保留

# 5- X11 (xwindow)

# 6 - 重新启动 (千万不要把initdefault 设置为6 )

常用的是3 和5 模式 其中3 是文字模式,含有网络功能,2 模式比3少了网络功能,5 是桌面模式,也是最常用的,跟windows的桌面模式很像,如果习惯了windows的用户,建议使用这种模式。

init启动时,它会在/etc/inittab内查找一个代码行,这一行指定了默认的运行级别:

id : 5 : initdefault : 这一行指出了默认情况下是启动5模式

根据inittab里的设定资料,会启动相应的服务,最先运行的服务是放在/etc/rc.d 目录下的文件。在大多数的Linux 发行版本中,启动脚本都是位于 /etc/rc.d/init.d中的。这些脚本被用ln 命令连接到 /etc/rc.d/rc*.d 目录。(这里的* 就是运行级0-6)

有关设定档inittab 内的具体内容可以查相关资料,这里不做解释

  • 2.接下来init 执行 /etc/rc.d/rc.sysinit 档案,这个档案主要是为我们设定好系统环境,例如环境变量,设置系统时间,设定终端字形,清楚开机过程中的暂存档,载入模组等,做的事情还是挺多的。

  • 3.服务,一般是放在/etc/init.d/下,也有的放在/etc/xinetd.d,放在这俩个目录下去的区别是,在 /etc/init.d/下的是一直存在于内存中,也就是说开机就启动的,在 /etc/xinet.d/下的是用的时候才启动的,其设定档为/etc/xinetd.conf。
    • 现在来了解一下在/etc/init.d/下的服务,这里面就保存了所有的开机启动服务,不管是在什么模式下,每个模式下开机所启动的 服务是不一样的,比如是5模式,我们就到/etc/rc.d/rc5.d/查看,里面的脚本的名字都有点怪,都是K(S)**name,不适以K开头就是 以S开头,以S开头的表示开机时要启动的服务,K开头的是表示开机时不启动的服务,后面接的两位数字表示,开机时服务执行的顺序,跟nice值有点像,都 是越小越先被执行。
    • 为什么以S开头就要被执行,以K开头的服务将不被启动呢?
    • 在inittab里有类似这样一句
    • l5:5:wait:/etc/rc.d/rc 5
    • 当然还有几句和它类似,上面这句表示在5模式执行:"/etc/rc.d/rc 5"这样一条命令,脚本rc里有两个for循环分别指出了要启动以S开头的服务,要关闭以K开头的服务,因为这时还没有启动登录界面,所以刚启动的服务就 变成了开机启动服务了。rc里的内容可以自己去参考一下。
    • 如果要想开机启动自定义程序,可以去修改/etc/rc.d/rc.local,因为这个是shell登录界面启动前的最后执行的脚本。这样即使你开始忘了在哪个脚本里关闭了某个程序,使它在开机时无法运行,你可以在这里面写,因为它是最后被执行的。
    • 如果你自己写了一个程序,要想在开机时也像/etc/rc.d/rc.d/下的服务一样,用那种方法启动它,你可以用chkconfig这个指令。也可以用man chkconfig参考一下用法
    • 最重要的一点,比如你写了一个脚本liuwenhan,那里面的格式要像下面的格式
      #!/bin/bash
      # chkconfig:35 80 70
      #接下来就是写你所需要的内容
    • 注意上面的第二行是很重要的,必须按上面的格式写一下, 在#和chkconfig之间是至少隔一个空格键,且要把写好的脚本放在/etc/init.d/下,这一行代表代表仅在3和5模式下启动该程序,且在 /etc/rc.d/rc3.d/和/etc/rc.d/rc5.d/下的名字为S80liuwenhan。在其他目录下为K70liuwenhan
    • 怎么用chkconfig添加,删除自定义程序,请参考man chkconfig或者info chkconfig
  • 4.启动登录界面,输入用户名和密码登录。

想了解具体的服务情况可以可以参考services