目录

  1. 计算机的启动过程
    1. 第一阶段:BIOS
      1. 硬件自检
      2. 启动顺序选择
    2. 第二阶段:主引导记录(MBR)
      1. 主引导记录的结构
      2. 分区表
    3. 第三阶段:磁盘启动(Grub)
      1. 情况一:卷引导记录
      2. 情况二:扩展分区和逻辑分区
      3. 情况三:启动管理器
    4. 第四阶段:操作系统启动
  2. Linux(操作系统) 启动
    1. 第一步:加载内核
    2. 第二步:启动初始化进程
    3. 第三步:确定运行级别
    4. 第四步:加载开机启动程序
    5. 第五步:用户登录
      1. 命令行登录
      2. ssh 登录
      3. 图形界面登录
    6. 第六步:进入login shell
      1. 命令行登录 & ssh 登录
      2. 图形界面登录
    7. 第七步:打开non-login shell
  3. 系统运行级别
    1. Centos6
    2. Centos7 运行目标(target)
  4. 附录

本文主要摘录自阮一峰老师的博客,想要查看原文的请访问原文

计算机的启动过程

“pull oneself up by one’s bootstraps”——通过拽着鞋带将自己拉起来

✨最早的时候,工程师们用它来比喻,计算机启动是一个很矛盾的过程:必须先运行程序,然后计算机才能启动,但是计算机不启动就无法运行程序!

第一阶段:BIOS

上个世纪 70 年代初,“只读内存”(read-only memory,缩写为 ROM)发明,开机程序被刷入 ROM 芯片,计算机通电后,第一件事就是读取它

这块芯片里的程序叫做基本输入输出系统(Basic Input/Output System),简称 BIOS

硬件自检

BIOS 程序首先检查,计算机硬件能否满足运行的基本条件,这叫做「硬件自检」(Power-On Self-Test),缩写为POST,如果硬件出现问题,主板会发出不同含义的蜂鸣,启动中止。如果没有问题,屏幕就会显示出 CPU、内存、硬盘等信息

启动顺序选择

硬件自检完成后,BIOS 把控制权转交给下一阶段的启动程序。此时,BIOS 需要知道,下一阶段的启动程序具体存放在哪一个设备之上。也就是说,BIOS 需要有一个外部储存设备的排序排在前面的设备就是优先转交控制权的设备。这种排序叫做启动顺序(Boot Sequence)

✨打开 BIOS 的操作界面,里面有一项就是设定启动顺序

第二阶段:主引导记录(MBR)

BIOS 按照「启动顺序」,把控制权转交给排在第一位的储存设备。这时,计算机读取该设备的第一个扇区,也就是读取最前面的 512 个字节。如果这 512 个字节的最后两个字节是 0x55 和 0xAA,表明这个设备可以用于启动;如果不是,表明设备不能用于启动,控制权转交给启动顺序中的下一个设备

🎶最前面的 512 字节,称为主引导记录(Master boot record,MBR)

主引导记录的结构

MBR 由于只有 512 字节,放不了太多信息,它的主要作用是:告诉计算机硬盘的哪一个位置存放着操作系统

✨MBR 由三部分组成

  • 1-446 Bytes:调用操作系统的机器码
  • 447-510 Bytes:分区表(Partition table)
  • 511,512Bytes:主引导记录签名(存放0x55与0xAA

🎶其中分区表的作用,是将硬盘分成若干个区

分区表

磁盘分区有很多好处,考虑到每个区可以安装不同的操作系统,MBR 因此必须知道将控制权交给哪个区。分区表的长度只有 64 个字节,里面又分成四项,每项 16 个 字节。所以,一个硬盘最多只能分四个一级分区,又叫做主分区

✨每个主分区的 16 字节,由以下 6 部分组成

  • 第 1 个字节:如果为 0x80,就表示该主分区是激活分区,控制权要转交给这个分区。四个主分区里面只能有一个是激活的
  • 第 2-4 个字节:主分区第一个扇区的物理位置(柱面、磁头、扇区号等等)
  • 第 5 个字节:主分区类型
  • 第 6-8 个字节:主分区最后一个扇区的物理位置
  • 第 9-12 字节:该主分区第一个扇区的逻辑地址
  • 第 13-16 字节:主分区的扇区总数

🎶最后的四个字节(主分区的扇区总数),决定了这个主分区的长度,也就是说,一个主分区的扇区总数最多不超过 $2^{32}$

如果每个扇区为 512 个字节,就意味着单个分区最大不超过 2 TB。再考虑到扇区的逻辑地址也是 32 位,所以单个硬盘可利用的空间最大也不超过 2TB。如果想使用更大的硬盘,只有 2 个方法:一是提高每个扇区的字节数,二是增加扇区总数

第三阶段:磁盘启动(Grub)

此时,计算机的控制权转交给硬盘的某个分区,此时又分成三种情况

情况一:卷引导记录

四个主分区里面,只有一个是激活的。计算机会读取激活分区的第一个扇区,叫做卷引导记录(Volume Boot Record,VBR)

卷引导记录的主要作用是:告诉计算机,操作系统在这个分区里的位置,随后计算机就会加载操作系统

情况二:扩展分区和逻辑分区

随着硬盘越来越大,四个主分区已经不够了,需要更多的分区。但是,分区表只有四项,因此规定有且仅有一个区可以被定义成扩展分区(Extended Partition),所谓扩展分区,就是指这个区里面又分成多个区。这种分区里面的分区,就叫做逻辑分区(logical partition)。

计算机先读取扩展分区的第一个扇区,称为扩展引导记录(Extended Boot Record,EBR),它里面也包含一张 64 字节的分区表,但是最多只有两项(也就是两个逻辑分区)

📓计算机接着读取第二个逻辑分区的第一个扇区,再从里面的分区表中找到第三个逻辑分区的位置,以此类推,直到某个逻辑分区的分区表只包含它自身为止(即只有一个分区项)。因此,扩展分区可以包含无数个逻辑分区,但是,似乎很少通过这种方式启动操作系统。如果操作系统确实安装在扩展分区,一般采用下一种方式启动

情况三:启动管理器

在这种情况下,计算机读取主引导记录前面 446 字节的机器码之后,不再把控制权转交给某一个分区,而是运行事先安装的启动管理器(Boot Loader),由用户选择启动哪一个操作系统。Linux 环境中,目前最流行的启动管理器是Grub

第四阶段:操作系统启动

控制权转交给操作系统后,操作系统的内核首先被载入内存。

以 Linux 系统为例,先载入 /boot 目录下面的 kernel。内核加载成功后,第一个运行的程序是 /sbin/init。它根据配置文件(Debian 系统是 /etc/initab)产生 init 进程。这是 Linux 启动后的第一个进程,pid 进程编号为 1,其他进程都是它的后代。然后,init 线程加载系统的各个模块,比如窗口程序和网络程序,直至执行 /bin/login 程序,跳出登录界面,等待用户输入用户名和密码

具体的操作系统启动流程在下面由阐述

Linux(操作系统) 启动

计算机的启动最后一步,就是操作系统的启动,接下来将研究操作系统的启动流程

✨在 BIOS 阶段,计算机的行为基本上被写死了,程序员可以做的事情并不多;但是,一旦进入操作系统,程序员几乎可以定制所有方面

第一步:加载内核

操作系统接管硬件以后,首先读入 /boot 目录下的内核文件

1
2
3
4
5
6
7
8
9
10
ls /boot
  config-3.2.0-3-amd64
  config-3.2.0-4-amd64
  grub
  initrd.img-3.2.0-3-amd64
  initrd.img-3.2.0-4-amd64
  System.map-3.2.0-3-amd64
  System.map-3.2.0-4-amd64
  vmlinuz-3.2.0-3-amd64
  vmlinuz-3.2.0-4-amd64

第二步:启动初始化进程

内核文件加载以后,就开始运行第一个程序 /sbin/init,它的作用是初始化系统环境,由于 init 是第一个运行的程序,它的进程编号(pid)就是 1。其他所有进程都从它衍生,都是它的子进程

第三步:确定运行级别

许多程序需要开机启动。它们在 Windows 叫做「服务」(service),在 Linux 就叫做守护进程(Daemon)

init 进程的一大任务,就是去运行这些开机启动的程序。但是,不同的场合需要启动不同的程序,比如用作服务器时,需要启动 Apache,用作桌面就不需要。Linux 允许为不同的场合,分配不同的开机启动程序,这就叫做运行级别(Run Level)

🎶也就是说,启动时根据运行级别,确定要运行哪些程序

✨Linux 预置七种运行级别(0-6)。一般来说,0 是关机,1 是单用户模式(也就是维护模式),6 是重启。运行级别 2-5,各个发行版不太一样,对于 Debian 来说,都是同样的多用户模式(也就是正常模式)

init 进程首先读取文件 /etc/inittab,它是运行级别的设置文件。如果你打开它,可以看到第一行是这样的:

1
id:2:initdefault:

initdefault 的值是 2,表明系统启动时的运行级别为 2。如果需要指定其他级别,可以手动修改这个值

🤔那么,运行级别 2 有些什么程序呢,系统怎么知道每个级别应该加载哪些程序呢?

  • 每个运行级别在/etc 目录下面,都有一个对应的子目录,指定要加载的程序
1
2
3
4
5
6
7
/etc/rc0.d
/etc/rc1.d
/etc/rc2.d
/etc/rc3.d
/etc/rc4.d
/etc/rc5.d
/etc/rc6.d

上面目录名中的rc,表示run command(运行程序),最后的 d 表示 directory(目录)

看看 /etc/rc2.d 目录中到底指定了哪些程序

1
2
3
4
5
6
7
8
9
10
11
12
 $ ls  /etc/rc2.d
  
  README
  S01motd
  S13rpcbind
  S14nfs-common
  S16binfmt-support
  S16rsyslog
  S16sudo
  S17apache2
  S18acpid
  ...

✨运行程序命名与运行特点

  • 可以看到,除了第一个文件 README 以外,其他文件名都是「字母 S+两位数字+程序名」的形式。
  • 字母 S 表示 Start,也就是启动的意思(启动脚本的运行参数为 start),如果这个位置是字母 K,就代表 Kill(关闭),即如果从其他运行级别切换过来,需要关闭的程序(启动脚本的运行参数为 stop)
  • 后面的两位数字表示处理顺序,数字越小越早处理,所以第一个启动的程序是 motd,然后是 rpcbing、nfs…数字相同时,则按照程序名的字母顺序启动,所以 rsyslog 会先于 sudo 启动

这个目录里的所有文件(除了 README ),是启动时要加载的程序。如果想增加或删除某些程序,不建议手动修改 /etc/rcN.d 目录,最好是用一些专门命令进行管理

第四步:加载开机启动程序

七种预设的运行级别各自有一个目录,存放需要开机启动的程序

😣如果多个运行级别需要启动同一个程序,那么这个程序的启动脚本,就会在每个目录都有一个拷贝,这样必然会造成管理上的困扰,如何做到优雅的修改启动脚本?

Linux 的解决办法,是七个 /etc/rcN.d 目录里列出的程序,都设为链接文件,指向另外一个目录 /etc/init.d ,真正的启动脚本都统一放在这个目录中。init 进程逐一加载开机启动程序,其实就是运行这个目录里的启动脚本

1
2
3
4
5
6
7
8
9
10
11
  $ ls -l /etc/rc2.d
  
  README
  S01motd -> ../init.d/motd
  S13rpcbind -> ../init.d/rpcbind
  S14nfs-common -> ../init.d/nfs-common
  S16binfmt-support -> ../init.d/binfmt-support
  S16rsyslog -> ../init.d/rsyslog
  S16sudo -> ../init.d/sudo
  S17apache2 -> ../init.d/apache2
  S18acpid -> ../init.d/acpid

🎶/etc/init.d 这个目录名最后一个字母 d,是 directory 的意思,表示这是一个目录,用来与程序 /etc/init 区分

第五步:用户登录

开机启动程序加载完毕以后,就要让用户登录了

✨一般而言,用户的登录方式有三种(命令行登录、SSH 登陆和图形界面登陆),不同的登录方式存在各自验证用户身份的方式

命令行登录

init 进程调用 getty 程序(意为 get teletype),让用户输入用户名和密码;输入完成后,再调用 login 程序,核对密码(Debian 还会再多运行一个身份核对程序 /etc/pam.d/login),如果密码正确,就从文件 /etc/passwd 读取该用户指定的 shell,然后启动这个 shell

ssh 登录

这时系统调用 sshd 程序(Debian 还会再运行/etc/pam.d/ssh ),取代 getty 和 login,然后启动 shell

图形界面登录

init 进程调用显示管理器,Gnome 图形界面对应的显示管理器为 gdm(GNOME Display Manager),然后用户输入用户名和密码。如果密码正确,就读取 /etc/gdm3/Xsession,启动用户的会话

第六步:进入login shell

所谓 shell,简单说就是命令行界面,让用户可以直接与操作系统对话,其中用户登录时打开的 shell,就叫做 login shell

🎶Debain 默认的 shell 是 Bash,它会读入一系列的配置文件,上一步的三种情况,在这一步的处理,也存在差异

命令行登录 & ssh 登录

首先读入 /etc/profile,这是对所有用户都有效的配置;然后依次寻找下面三个文件,这是当前用户对bash的配置

1
2
3
~/.bash_profile
~/.bash_login
~/.profile

🎶这三个文件只要有一个存在,就不再读入后面的文件

图形界面登录

✨只加载 /etc/profile 和 ~/.profile

第七步:打开non-login shell

完成上一步,Linux 的启动过程已经结束,用户已经可以看到命令行提示符或者图形界面了,但是,为了内容的完整,必须再介绍一下这一步。用户进入操作系统以后,常常会再手动开启一个 shell。这个 shell 就叫做 non-login shell,意思是它不同于登录时出现的那个 shell,不读取 /etc/profile.profile 等配置文件

non-login shell 的重要性,不仅在于它是用户最常接触的那个 shell,还在于它会读入用户自己的 bash 配置文件 ~/.bashrc。大多数时候,我们对于 bash 的定制,都是写在这个文件里面的

✨只要运行 ~/.profile 文件,~/.bashrc 文件就会连带运行

1
2
3
4
5
6
#~/.profile文件中的一部分代码
if [ -n "$BASH_VERSION" ]; then
    if [ -f "$HOME/.bashrc" ]; then
      . "$HOME/.bashrc"
    fi
  fi

🤔为什么 bash 的设置这么繁琐?不能和 Spring 一样统一配置,统一启动?

Bash 的设置之所以如此繁琐,是由于历史原因造成的。早期的时候,计算机运行速度很慢,载入配置文件需要很长时间,Bash 的作者只好把配置文件分成了几个部分,阶段性载入。系统的通用设置放在 /etc/profile,用户个人的、需要被所有子进程继承的设置放在 .profile,不需要被继承的设置放在 .bashrc

系统运行级别

Centos6

系统一共有七个运行级别

临时设置

使用runlevel命令查看运行级别,使用init命令调整运行级别

永久设置

通过修改 /etc/inittab 可以做到永久性更改启动级别

00:系统的关机级别 init 1 进入到关机状态

01:用户的单用户模式 用于修复系统 或重置密码信息,

02: 系统的多用户模式, 没有网络

03: 系统的多用户模式 正常系统运行多用户模式级别 有网络

04:预留级别

05: 图形化界面

06: 系统的重新启动级别

Centos7 运行目标(target)

/usr/lib/systemd/system/runlevel*target

1
2
systemctl get-default
systemctl set-default <run-target> # 永久设置

0(poweroff target):系统的关机级别 init 1 进入到维修模式
1(rescue target):用户的单用户模式 用于修复系统 或重置密码信息,
2(multi-user target): 系统的多用户模式, 没有网络
3(multi-user target): 系统的多用户模式 正常系统运行多用户模式级别 有网络
04(multi-user target):预留级别
05(graphical target): 图形化界面
06(reboot target): 系统的重新启动级别

附录

计算机是如何启动的?
Linux 的启动流程——阮一峰
开机流程、模组管理与 Loader——鸟哥
开机流程管理——鸟哥
Linux 开机详细流程——骏马金龙