Linux是如何启动的?
参考资料:
An introduction to the Linux boot and startup processes
这篇随笔,可以理解为是对这篇英文文章的翻译与个人理解、笔记的整合。
扩展阅读:
BIOS interrupt call - Wikipedia
Multiboot specification - Wikipedia
Master boot record - Wikipedia
Best Couple of 2016: Display manager and window manager
浅析 Linux 初始化 init 系统,第 1 部分:SysV init
前言
理解Linux是如何启动的对于配置和针对Linux启动失败的排错是至关重要的。
本篇文章大体上把Linux启动过程分成了两部分:引导(boot)和启动(startup)。
引导:从计算机开机至内核初始化完毕启动systemd程序的这个阶段。
启动:从systemd程序启动至系统完全启动完毕的这个阶段。
引导阶段主要涉及了引导装载程序(boot loader),对于我所学习的CentOS系列的系统来说,主要涉及的均是GRUB这个引导装载程序。
- 0.x系列的版本,是老的版本,也叫做GRUB 1/Legacy,主要用于以前的Linux发行版,例如CentOS 5/6。
- 2.x系列的版本,是新的版本,也叫做GRUB 2,现在主流的例如CentOS 7上会使用它。虽然是新版的GRUB,版本号差别也不是很大,但是GRUB 2几乎是完全重写的GRUB!
启动阶段主要涉及了系统初始化进程的方式(init 进程/系统)。
- CentOS 5:System V init,可简称System V或者SysV。
- CentOS 6:Upstart。
- CentOS 7:Systemd。
这3种进程初始化方式,配置文件的写法有较大的不同。
本篇随笔,所涉及的引导和启动的版本是当前的主流版本,即GRUB 2和Systemd。下文如果没特别说明或者GRUB单独出现的话,那么GRUB就表示GRUB 2了。
将引导和启动的过程,更细致的划分的话,如下所示。
- Boot
- BIOS POST
- Boot loader(GRUB 2)
- Stage 1
- Stage 1.5
- Stage 2
- Kernel initialization
- Startup
- Systemd
引导过程(boot)
想要启动引导过程的话,主要有两种方式。一种是计算机处于关机状态,用户物理性开机;另一种是计算机处于开机状态,用户通过GUI或者CLI程序性重启(当然如果有物理重启按钮,也可以物理性重启)。
译者注:我也不晓得为何作者要说这句简单的话。
BIOS POST
引导的第一步和Linux系统是无关的,所有的OS都会有这个步骤。这步是BIOS中提供的POST(Power On Self Test,开机自检)功能,主要用于检测硬件是否可正常工作。
如果POST检测失败的话,则引导终止,后续的步骤也不会再有了,系统启动失败。
POST检测成功,确保硬件可正常工作后,BIOS会释出BIOS中断(BIOS interrupt,详见扩展阅读)INT 13H,它用于在所有已连接的可引导设备上定位引导扇区(boot sector)。第一个找到的包含有效引导记录(boot record)引导扇区会被加载入内存当中,并且将控制权传递给从引导扇区中加载的代码。
引导扇区是引导装载程序的第一个阶段(stage)。现代大多数的Linux发行版中采用三种引导装载程序,分别是LILO、GRUB 1和GRUB 2,GRUB 2是当前阶段最新也是最常用的引导装载程序。
GRUB 2
GRUB 2的全称是“GRand Unified Bootloader, version 2”,它使得计算机可以找到OS的内核文件并将其载入内存中。
GRUB被设计用于兼容多引导规范(multiboot specification,详见扩展阅读),这个规范使得它可以引导许多的Linux和自由OS;它还可以链式加载(chain load,详见扩展阅读)专有(proprietary)OS的引导记录。
GRUB还可以允许用户从某个Linux发行版中选择不同的内核来引导。这样在某个内核更新发生了错误,或者更新使得某些重要的软件使用出现异常的时候,就可以引导之前版本的内核来正常工作。
GRUB 1的配置文件是/boot/grub/grub.conf,GRUB 2的配置文件是/boot/grub2/grub.cfg。红帽系的Linux,从Fedora 15和CentOS/RHEL 7开始将GRUB升级到了GRUB 2版本。GRUB 2和GRUB 1在功能上是相同的,但是经过了几乎完全的重写,所以是要优于GRUB 1的。
GRUB 1和2都是有三个阶段(stage),本文会大致阐述这三个阶段。不过,关于GRUB具体是如何配置的,不在本文的范围中。
GRUB 2在官方中并不是使用这3个阶段,即stage 1/1.5/2,这个应该是GRUB 1所使用的,不过使用这3个阶段来阐述GRUB 2,也是可以的。
译者注:在扩展阅读中,GRUB 2应该是4个阶段。
Stage 1
当BIOS POST的自检阶段通过后,BIOS从已连接的设备上寻找可用的引导记录,引导记录一般位于磁盘的MBR上。stage 1的引导记录叫做boot.img。
MBR位于磁盘的第一个扇区sector 0,大小为512B,扣除64B的分区信息和2个字节的boot signature,仅剩下446B的空间。这个空间非常小,所以boot.img的代码量也必须很小,它不会包含文件系统的驱动(即没有识别文件系统相关的代码),boot.img的唯一作用就是找到并加载stage 1.5。为了完成stage 1的任务,stage 1.5的位置必须位于stage 1自身和第一个分区之间。如图所示。
By Shmuel Csaba Otto Traian, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=28427221
将stage 1.5加载入内存后,stage 1就将控制权转交给了stage 1.5了。
译者注:在我的CentOS 7系统上,该文件位于/boot/grub2/i386-pc/boot.img,大小为512B,理论上是应该小于446B才对。
Stage 1.5
如上所述,stage 1.5位于stage 1和第一个分区之间,即sector 1~62,总容量为512*62=31,744。
译者注:上面的图片stage 1.5的容量为32,256B,比31,744多了一个512B,不懂为什么。
这个阶段的文件是core.img,作者的该文件大小是25,389B,我的是26,664(/boot/grub2/i386-pc/core.img)。可见该阶段还可以有许多的剩余容量。
这个阶段主要是包含了各种各样的文件系统驱动程序,例如EXT家族、XFS、NTFS等。只有具备了文件系统驱动,才可以基于文件的路径和名称(例如/boot/vmlinuz-3.10.0-862.el7.x86_64)来查找/加载某个文件。
剩余的容量,应该是让开发人员自己扩展、增加想要加载的文件系统驱动程序的。
这个阶段所加载的文件系统驱动,就是用于识别stage 2的文件所在的文件系统的。
GRUB 2的stage 1.5中的core.img要比GRUB 1版本中的更加复杂和强大,因此GRUB 2中的stage 2可以位于标准的EXT系列文件系统,但不能是在LVM分区上。
标准的stage 2的文件的位置是在/boot文件系统上,准确地说,是/boot/grub2。
Stage 2
该阶段的文件位于/boot/grub2目录中。该阶段不算前2个阶段有镜像文件(image file:boot.img和core.img),它主要由许多运行时按需加载的内核模块文件构成(位于/boot/grub2/i386-pc目录)。
该阶段的主要功能是定位并加载内核文件,而后将控制权转交给内核。内核文件的名称均以vmlinuz开头,可在/boot目录下找到当前系统已安装的内核。
[root@C7 ~]# ls -l /boot/vmlinuz-*
-rwxr-xr-x. root root Sep /boot/vmlinuz--rescue-341d78e441db4d8b985b51fc31f40be9
-rwxr-xr-x. root root Apr /boot/vmlinuz-3.10.-.el7.x86_64
[root@C7 ~]# file /boot/vmlinuz-*
/boot/vmlinuz--rescue-341d78e441db4d8b985b51fc31f40be9: Linux kernel x86 boot executable bzImage, version 3.10.-.el7.x86_64 (builder@kbuilder.dev.centos.org) # SMP , RO-rootFS, swap_dev 0x5, Normal VGA
/boot/vmlinuz-3.10.-.el7.x86_64: Linux kernel x86 boot executable bzImage, version 3.10.-.el7.x86_64 (builder@kbuilder.dev.centos.org) # SMP , RO-rootFS, swap_dev 0x5, Normal VGA
GRUB有一个预引导菜单,可以让用户选择想要引导的内核,包含了救援(rescue)模式,配置得当的话还包含恢复(recovery)模式。
Kernel
内核是一种可自我展开的压缩格式的文件,它们和RAM disk镜像文件、硬盘的设备映射都位于/boot目录下。
/boot/initramfs--rescue-341d78e441db4d8b985b51fc31f40be9.img
/boot/initramfs-3.10.-.el7.x86_64.img
/boot/System.map-3.10.-.el7.x86_64
当内核被载入内存执行,自我展开完毕后,它会启动systemd进程。此时引导过程就结束了,正式进入启动过程。
注:引导过程的结束时,Linux仍然无法执行任何生产任务。因为生产任务是需要具体的进程来完成的,而systemd进程只是所有生产任务进程的最上级父进程(系统的第一个进程)。
启动过程(startup)
Systemd
systemd是Linux系统中的第一个进程,即最顶级的父进程。它的任务是启动系统中的其他进程使得Linux可以进入生产环境得状态。包含挂载文件系统、启动和管理服务。和启动顺序无关的systemd任务不在本文的讨论范围之内。
首先,systemd根据/etc/fstab挂载文件系统,包含swap文件和分区。此时systemd就可以访问位于/etc目录下的配置文件了,包含自己的配置文件。它使用自己的配置文件(/etc/systemd/system/default.target)来决定要将系统启动为哪种状态(state)或者target。default.target仅仅是一个到真实target文件的字符链接文件。对于桌面工作站来说,是链接到graphical.target(相当于System V init中的运行级别5)。对于服务器来说,一般是链接到multi-user.target(相当于System V init中的运行级别3)。emergency.target则相当于单用户模式。
注意:target和服务是systemd的单元(unit)。
Table 1是systemd的target和System V init的运行级别的对照表格。systemd提供了target别名(alias)用于向后兼容。target别名允许脚本和管理员使用SysV的命令(例如init 3)来切换运行级别,当然,这些命令会传递给systemd解析与执行。
SystemV Runlevel |
systemd target |
systemd target aliases |
Description |
halt.target |
Halts the system without powering it down. |
||
0 | poweroff.target | runlevel0.target |
Halts the system and turns the power off. |
S | emergency.target |
Single user mode. No services are running; filesystems are not mounted. This is the most basic level of operation with only an emergency shell running on the main console for the user to interact with the system. |
|
1 | rescue.target | runlevel1.target |
A base system including mounting the filesystems with only the most basic services running and a rescue shell on the main console. |
2 | runlevel2.target |
Multiuser, without NFS but all other non-GUI services running. |
|
3 | multi-user.target | runlevel3.target |
All services running but command line interface (CLI) only. |
4 | runlevel4.target |
Unused. |
|
5 | graphical.target | runlevel5.target |
multi-user with a GUI. |
6 | reboot.target | runlevel6.target |
Reboot. |
default.target |
This target is always aliased with a symbolic link to either multi-user.target or graphical.target. systemd always uses the default.target to start the system. The default.target should never be aliased to halt.target, poweroff.target, or reboot.target. |
每个target在其配置文件中会说明依赖关系,有依赖就有启动的先后顺序之分,被依赖方启动失败,那么依赖方也会启动失败(依赖方依赖于被依赖方)。这些依赖一般是系统要运行于某个功能级别下(例如web服务器、DB服务器、缓存服务器等)所需要的服务。当target配置文件中所有的依赖都正常启动完毕后,系统会运行于那个target级别下。
Systemd也会去查找SysV配置目录下是否含有启动脚本,如果有的话,也会加载的。基本上在CentOS 7上,服务已经都使用systemd风格的配置文件来管理了,不过在SysV配置目录下还有一个网络相关的配置文件,可能只是用作一个学习案例吧,或者网络服务在CentOS 7上依然使用SysV管理(概率较低)。
[root@C7 ~]# ls -l /etc/rc.d/init.d/
total
-rw-r--r--. root root Jan functions
-rwxr-xr-x. root root Jan netconsole
-rwxr-xr-x. root root Jan network
-rw-r--r--. root root Apr README
接下来我们来看一张文本图,Figure 1。这张图来源于bootup(7)。展示了systemd的启动流程。
local-fs-pre.target
|
v
(various mounts and (various swap (various cryptsetup
fsck services...) devices...) devices...) (various low-level (various low-level
| | | services: udevd, API VFS mounts:
v v v tmpfiles, random mqueue, configfs,
local-fs.target swap.target cryptsetup.target seed, sysctl, ...) debugfs, ...)
| | | | |
\__________________|_________________ | ___________________|____________________/
\|/
v
sysinit.target
|
____________________________________/|\________________________________________
/ | | | \
| | | | |
v v | v v
(various (various | (various rescue.service
timers...) paths...) | sockets...) |
| | | | v
v v | v rescue.target
timers.target paths.target | sockets.target
| | | |
v \_________________ | ___________________/
\|/
v
basic.target
|
____________________________________/| emergency.service
/ | | |
| | | v
v v v emergency.target
display- (various system (various system
manager.service services services)
| required for |
| graphical UIs) v
| | multi-user.target
| | |
\_________________ | _________________/
\|/
v
graphical.target
sysinit.target和basic.target可以看作是一个检测点(checkpoint)。尽管systemd的设计目标之一是并行启动系统服务,但是依然存在某些特定的服务和target是需要在其他服务和target启动之前启动的。直到所有被该检测点依赖的服务和target被满足后,那么该检测点才可以通过。
因此直到所有被依赖的单元完成后,才可以到达sysinit.target。所有的那些单元,挂载文件系统、配置swap文件、启动udev、建立随机生成器种子(random genorator seed)、初始化低级设备、如果有文件系统需要加密的话则配置加密服务。不过在sysinit.target内部,上述的这些单元是可以并行执行的。也就是说systemd支持单元在某个target内部的并行执行。
sysinit.target完成了一些底层的功能,即较接近硬件层,慢慢地会往高层发展,接近用户层,即basic.target。这个阶段涉及的unit,如图所示。
最后就可以启动multi-user.target和graphical.target这两个我们熟悉的用户级别的target了。注意:multi-user.target必须启动成功,才可以启动graphical.target。
Figure 1中,加粗带下划线的target,是常用的启动target,到达这些target,就表示系统已经启动完毕了。multi-user.target启动完毕后展示了文本模式的登录界面,而graphical.target则展示了图形化的登陆界面,想了解关于图形化登陆界面的话,可查阅扩展阅读中的Best Couple of 2016: Display manager and window manager。
Linux是如何启动的?的更多相关文章
- 探索 Linux 系统的启动过程
引言 之所以想到写这些东西,那是因为我确实想让大家也和我一样,把 Linux 桌面系统打造成真真正正日常使用的工具,而不是安装之后试用几把再删掉.我是真的在日常生活和工作中都使用 Linux,比如在 ...
- windows下制作linux U盘启动盘或者安装优盘(转)
windows下制作linux U盘启动盘或者安装优盘(转) Linux发行版排行榜:http://iso.linuxquestions.org/ [方案一]:UltraISO(不推荐,在Window ...
- 腾讯云Linux系统中启动自己安装的tomcat
腾讯云Linux系统中启动自己安装的tomcat 首先通过工具查看一下安装的tomcat的位置 进入命令行之后输入以下指令: 此时,tomcat已经启动了.
- [转] 浅谈Linux系统的启动流程
原文:http://blog.csdn.net/justdb/article/details/9621271 版权声明:本文为博主原创文章. Linux系统的启动时通过读取不同的配置文件,执行相应的S ...
- Linux内核分析之跟踪分析Linux内核的启动过程
一.实验过程 使用实验楼虚拟机打开shell cd LinuxKernel/ qemu -kernel linux-/arch/x86/boot/bzImage -initrd rootfs.img ...
- 详解linux系统的启动过程及系统初始化
一.linux系统的启动流程 关于linux系统的启动流程我们可以按步进行划分为如下: POST加电自检 -->BIOS(Boot Sequence)-->加载对应引导上的MBR(boot ...
- linux下如何启动/停止/重启mysql:
一.启动方式1.使用linux命令service 启动:service mysqld start2.使用 mysqld 脚本启动:/etc/inint.d/mysqld start3.使用 safe_ ...
- 走进Linux之systemd启动过程
Linux系统的启动方式有点复杂,而且总是有需要优化的地方.传统的Linux系统启动过程主要由著名的init进程(也被称为SysV init启动系统)处理,而基于init的启动系统被认为有效率不足的问 ...
- Linux系统快速启动方案
========================= 基本常识 ========================= Linux系统基本启动流程: 1. CPU从ROM(如果有的 ...
- CentOS(四)--Linux系统的启动级别
对于绝大多数Linux程序员来说,进入Linux系统后一般看到的是黑乎乎的界面(开发模式),因为系统如果启动选择开发模式,会减少启动时间,优化内存等.但是通常我们刚安装完Linux系统,然后进去以后是 ...
随机推荐
- Intellij 生成exe可执行文件
生成jar包 编写源代码 此处我使用kotlin来编码,主函数实际功能就是输出一行文字. /** * 应用入口 * @author mazaiting */ object TestExe { @J ...
- 在IOS中实现新浪微博OAuth认证
主要讲解在ios中的认证流程,至于如何得到新浪用户授权账号,地址,这些可以见视频0506. 1.用UIWebView访问新浪授权页面https://api.weibo.com/oauth2/autho ...
- Flask入门学习——自定义一个url转换器
我们知道,flask的url规则是可以添加变量部分的,这个参数变量是写在尖括号里的,比如:/item/<id>/,如果需要指出参数的类型要符合<converter:vai ...
- php: $$str
这种写法称为可变变量有时候使用可变变量名是很方便的.就是说,一个变量的变量名可以动态的设置和使用.一个普通的变量通过声明来设置,例如: <?php$a = "hello";? ...
- SpringBoot和SpringCloud的版本对应关系
1.详细的SpringBoot和SpringCloud对应的关系: Spring官方对应关系 2.springCloud与各组件的版本对应关系 官方文档
- 常用tab选项卡代码
<div class="box"> <ul> <li class="one">课程介绍</li> <li& ...
- 使用Python编写打字训练小程序
你眼中的程序猿 别人眼中的程序猿,是什么样子?打字如飞,各种炫酷的页面切换,一个个好似黑客般的网站破解.可现实呢? 二指禅的敲键盘,写一行代码,查半天百度-那么如何能让我们从外表上变得更像一个程序猿呢 ...
- luogu P1316 丢瓶盖 |二分答案
题目描述 陶陶是个贪玩的孩子,他在地上丢了A个瓶盖,为了简化问题,我们可以当作这A个瓶盖丢在一条直线上,现在他想从这些瓶盖里找出B个,使得距离最近的2个距离最大,他想知道,最大可以到多少呢? 输入格式 ...
- kali linux中文乱码解决
命令中输入 LANG=en_US.UTF-8 apt-get install ttf-wqy-microhei xfonts-wqy gnome-tweak-tool
- 声明式服务调用Feign
什么是 Feign Feign 是种声明式.模板化的 HTTP 客户端(仅在 consumer 中使用). 什么是声明式,有什么作用,解决什么问题? 声明式调用就像调用本地方法一样调用远程方法;无 ...