干货分享 - Systemd 技术原理&实践(下)
systemd 文章续集来啦,上期我们介绍了 systemd 的基本概念 ,本期就为大家详细讲解 systemd 相关的运行逻辑,希望能对研究系统资源优化的优客有所帮助。
systemd 时代的开机启动流程
在 systemd 作为系统的 init 程序的时代下,Linux 系统的启动流程可以大致分为 6 个阶段:BIOS 自检阶段、GRUB 引导阶段、kernel 内核加载阶段、initrd 虚拟根文件系统阶段、systemd 初始化阶段、终端登录阶段。每个阶段都各司其职,为下一个阶段的进行做铺垫,相互联系,缺一不可。接下来对每个阶段做一下介绍:
1.BIOS 自检阶段
从我们启动计算机从按下电源键开始,计算机开始通电,然后系统就开始加载主板内存上的第一段代码:BIOS,系统进入 BIOS 自检阶段。
BIOS 为基本输入输出系统,全称 Basic Input Output System,它烧录在主板的内存上,其中的内容只能读不能改,如果要进行更改只能重新烧录到主板的内存上。BIOS 在开机阶段最主要的功能为上电自检,它会对主板上接入的硬件设备一个个进行检查,例如检查 CPU、主板、内存、软硬盘系统、键盘、光驱等等硬件是否接入正常,有无故障,当某一些主要的硬件(例如 CPU、内存等)出现问题时,BIOS 就会报错,无法继续启动系统。我们在启动电脑时听到的滴滴的声音就是 BIOS 蜂鸣器发出的声音,当硬件出问题的时候就可以听到它蜂鸣器响两到三声报错,系统就无法进行下一步的启动。
BIOS 检查完所有硬件状态并状态无误的时候,就会按照设置的启动顺序去找相应的启动盘,然后引导系统进入相应的启动盘继续启动系统。有过刷机经验的朋友应该知道在系统启动时按 F12 或者 delete 键就会进入 BIOS 界面,然后就会去选择相应的启动盘进行刷机,启动盘可以是装机 U 盘、光驱,也可以是已经装了系统的磁盘等等,BIOS 可以设置默认的启动顺序,例如:可以设置 U 盘为第一启动项,开机启动时 BIOS 就会引导系统去找 U 盘对应的硬件接口,当找不到 U 盘时,BIOS 会继续尝试第二启动项,当选择好了启动项时,系统进入相应的启动盘,并开始执行启动盘中第一块磁盘第一个扇区的代码,至此 BIOS 自检阶段结束。
2.GRUB 引导阶段
GRUB 是 GRand Unified Bootloader 的缩写,它是一个多重操作系统的管理器,存放在第一个磁盘的第一个扇区的主引导扇区里面,如果你的电脑里面装了多个系统,例如 Linux 系统和 Windows 系统,那么你可以通过 GRUB 来移动光标选择自己想要进入的系统,选择好系统以后 GRUB 就会根据系统分区表里找到对应系统所在的磁盘分区,加载相应的 grub.cfg 配置文件,通过配置文件,加载 /boot 分区的文件系统驱动,然后在文件系统中找到系统内核,把内核加载进来并启动,最后把系统的控制权交给内核,至此 GRUB 引导阶段结束。
GRUB 除了引导系统这一主要功能外,还可以通过 grub.cfg 配置文件来实现其他的一些功能。grub.cfg 配置文件存放在 /boot/grub/目录下,配置文件中,Linux 参数表示系统启动时对应加载的内核,当系统里存放了多个内核、或者在你电脑上重新修改编译了新的内核的时候,可以配置此项来选择相应的内核进行加载;quiet 参数类似于 loglevel 参数,用来配置日志启动的等级;splash 参数用来配置相应的启动动画等等。
3.Kernel 内核加载阶段
在讲解内核的启动之前,先简单介绍一下 Linux 内核。Linux 内核是一种宏内核,运行在单一地址空间的单一的程序,把系统的进程线程管理、内存管理、文件系统、驱动管理、网络管理等一些基本功能集中在一起,内核中的每一个函数都可以访问到其内核的其他部分,不同于微内核(代表:Windows 系统),微内核则是将这些功能独立的划分成不同的服务,服务之间通过通讯接口与中心内核通讯。
在结束了 GRUB 引导阶段,内核拿到系统的控制权后,首先开始初始化系统中各种设备的相关配置工作,其中包括 CPU、I/O 设备、存储设备的初始化等等,其次,内核创建内核态的 kernel_init 的进程,然后找到 initrd 文件并解压,加载 initrd 虚拟根文件系统中的驱动程序完成相关硬件的初始化,最后调用 initrd 虚拟根文件系统的 init 脚本,至此,内核在系统启动过程中的作用基本上就已经完成,内核开始等待 initrd 执行 init 进程,内核加载阶段结束。
4.Initrd 虚拟根文件系统阶段
initrd(Initial RAM Disk)它是一个虚拟的根文件系统,在 GRUB 阶段被拷贝到内存,在内核中被解压,是一个临时的虚拟根文件系统,对其进行解压后可以看到它的目录结构和实际的根文件系统类似,并且包含了些驱动程序,下图为解压麒麟 initrd.img 5.4.18-32 版本下的虚拟根文件系统的目录结构:
由于内核为了精简,只保留了最基本的模块,因此没有各种设备硬件的驱动程序,这些驱动程序就存放在了 initrd 里面,内核启动的时候,就从 initrd 中加载必要的驱动模块,完成硬件的初始化工作,接着,内核就开始执行虚拟根文件系统里的 init 程序,即虚拟根文件系统下的 systemd 程序,systemd 就作为内核的子程序,拿到了系统的控制权,开始做一些系统初始化方面的工作。
通过上面的描述,可以总结一下,虚拟根文件系统的阶段可以大致的分为:内核加载 initrd 里面的驱动程序、虚拟根文件系统下的 systemd 程序加载这两个过程,因此也可把虚拟根文件系统阶段分别归到内核加载阶段与 systemd 初始化阶段两个里面,是与上下两个阶段重合的一个阶段。此外,initrd 还提供了美化启动图形界面的功能,用来遮盖系统启动过程中的 log 日志输出,提升用户的体验。当 initrd 下的 systemd 进程完成环境的初始化,系统切换到真正的根文件系统的时候,initrd 阶段结束。
5.systemd 初始化阶段
systemd 是 system deamon 的简称,是一个 Linux 系统基础组件的集合,提供了系统与服务的管理,是 pid 为 1 的 init 进程,是所有进程的父进程。需要详细了解 systemd 进程的小伙伴可以阅读上一篇文章:systemd 介绍,这里重点讲解一下 systemd 开机过程中所做的事情。
通过前面的描述,我们可以把 systemd 分为两个阶段:虚拟根文件系统阶段与实根文件系统阶段。内核通过解压 initrd 文件得到虚拟根文件系统,然后执行虚拟根文件系统下的 init 程序来启动 systemd,systemd 作为内核的子进程在虚拟根文件系统下开始运行。虚拟根文件下的 systemd 首先对目前的系统进行一些检查,例如判断系统的运行状态是 user 态还是 system 态,系统是正常的启动状态还是异常出错后的重启状态等等,然后进行一些系统的初始化配置,包括:环境变量的配置、日志的相关配置等,接着对一些关键的文件系统进行挂载,主要包括 /proc、/sys、/dev、/var 这些基本的文件系统目录,到这一步后,systemd 就开始为切换实根文件目录做准备,保存一些已经配置的项目,并进行一些环境的适配之后,systemd 执行 setsid()系统调用脱离内核控制,成为一个完全独立的父进程,至此 systemd 的虚拟根文件系统阶段结束,systemd 进入到实根文件系统阶段。
在实根文件系统阶段,systemd 首先进行一些切换后的环境适配,然后开启日志终端的功能,并把系统启动时临时保存在内核中的日志提取出来,整理后存放到相应的日志文件中,下一步,systemd 开始进行一些系统能力的获取与系统相关的初始化与配置,例如:CPU 亲和力的获取、主机名的配置、系统 ID 的配置,cgroup 控制器的挂载、回环网络的配置等,完成以上的这些所有初始化工作后,systemd 作为 PID 为 1 的守护进程,开始了各个系统服务的创建与管理工作,根据相应 Unit 配置单元文件执行相应的系统服务,通过各个服务逐步完成系统的启动工作。systemd 执行 Unit 的顺序大致可以分为 sysinit.target->basic.target->default.target,其中 sysinit.target 与 basic.target 主要用来启动一些系统初始化相关的一些服务与执行一些开机启动早期的一些任务,default.target 则指向不同的“运行级别”target 文件,如果是进入命令行模式则指向 multi-user.target 文件,如果是进入图形界面模式则指向 graphical.target 文件。至此,systemd 开机启动阶段的工作完成。
6.终端登录阶段
在完成了 systemd 初始化阶段以后,系统根据配置的运行级别,进入不同的登录界面,下面主要从图形登录界面进行介绍。在优麒麟操作系统中,systemd 之后的启动流程主要如下:systemd->lightdm->Xorg->lightdm-greeter->ukui-greeter->ukui-session,在优麒麟的终端通过 pstree 命令可以看到如下两个进程树:
lightdm 是一个全新的、轻量的 Linux 桌面的桌面显示管理器,它首先会拉起 Xorg,Xorg 是一个显示的后台,负责屏幕的绘制,然后 lightdm 还会拉起 lightdm-greeter,lightdm-greeter 是 lightdm 的子进程,它会拉起 ukui-greeter 进程,ukui-greeter 是登录界面进程,因此 ukui-greeter 起来以后,系统进入到登录界面,当输入登录的用户名与密码,用户名与密码效验通过以后,lightdm 建立个人的 ukui-session 桌面窗口管理器进程,至此,终端登录阶段结束,系统完成启动。
systemd 相关命令
systemd 提供了 systemctl 相关命令,用于管理系统,下面对一些基础常用命令进行介绍:
1.系统管理命令,控制系统电源状态
# 重启系统
$ sudo systemctl reboot
# 关闭系统,切断电源
$ sudo systemctl poweroff
# 暂停系统,使系统进入睡眠状态
$ sudo systemctl suspend
# 让系统进入冬眠状态
$ sudo systemctl hibernate
# 让系统进入交互式休眠状态
$ sudo systemctl hybrid-sleep
2.systemd-analyze 命令,用于查看启动耗时,可用来分析系统的启动效率
#查看启动耗时
$ systemd-analyze
#查看每个服务的启动耗时
$ systemd-analyze blame
#显示瀑布状的启动过程流
$ systemd-analyze critical-chain
#显示指定服务的启动流
$ systemd-analyze critical-chain atd.service
3.hostnamectl 命令,用于查看当前主机的信息
#显示当前主机的信息
$ hostnamectl
#设置主机名
$ sudo hostnamectl set-hostname rhel7
4.Unit 相关命令,用来管理 Unit 配置单元
#列出正在运行的 Unit
$ systemctl list-units
#列出所有 Unit,包括没有找到配置文件的或者启动失败的
$ systemctl list-units --all
#列出所有没有运行的 Unit
$ systemctl list-units --all --state=inactive
#列出所有加载失败的 Unit
$ systemctl list-units --failed
#列出所有正在运行的、类型为 service 的 Unit
$ systemctl list-units --type=service
#立即启动一个服务
$ sudo systemctl start apache.service
#立即停止一个服务
$ sudo systemctl stop apache.service
#重启一个服务
$ sudo systemctl restart apache.service
#杀死一个服务的所有子进程
$ sudo systemctl kill apache.service
#重新加载一个服务的配置文件
$ sudo systemctl reload apache.service
#重载所有修改过的配置文件
$ sudo systemctl daemon-reload
#显示某个 Unit 的所有底层参数
$ systemctl show httpd.service
#显示某个 Unit 的指定属性的值
$ systemctl show -p CPUShares httpd.service
#设置某个 Unit 的指定属性
$ sudo systemctl set-property httpd.service CPUShares=500
5.日志管理,用来查看和过滤系统日志
#查看所有日志(默认情况下 ,只保存本次启动的日志)
$ sudo journalctl
#查看内核日志(不显示应用日志)
$ sudo journalctl -k
#查看系统本次启动的日志
$ sudo journalctl -b
$ sudo journalctl -b -0
#查看上一次启动的日志(需更改设置)
$ sudo journalctl -b -1
#查看指定时间的日志
$ sudo journalctl --since="2012-10-30 18:17: 16"
$ sudo journalctl --since "20 min ago"
$ sudo journalctl --since yesterday
$ sudo journalctl --since "2015-01-10" --until "2015-01-11 03:00"
$ sudo journalctl --since 09:00 --until "1 hour ago"
#显示尾部的最新 10 行日志
$ sudo journalctl -n
#显示尾部指定行数的日志
$ sudo journalctl -n 20
#实时滚动显示最新日志
$ sudo journalctl -f
#查看指定服务的日志
$ sudo journalctl /usr/lib/systemd/systemd
#查看指定进程的日志
$ sudo journalctl _PID=1
#查看某个路径的脚本的日志
$ sudo journalctl /usr/bin/bash
#查看指定用户的日志
$ sudo journalctl _UID=33 --since today
#查看某个 Unit 的日志
$ sudo journalctl -u nginx.service
$ sudo journalctl -u nginx.service --since today
#实时滚动显示某个 Unit 的最新日志
$ sudo journalctl -u nginx.service -f
#查看指定优先级(及其以上级别)的日志,共有8级
# 0: emerg
# 1: alert
# 2: crit
# 3: err
# 4: warning
# 5: notice
# 6: info
# 7: debug
$ sudo journalctl -p err -b
#日志默认分页输出,--no-pager 改为正常的标准输出
$ sudo journalctl --no-pager
#显示日志占据的硬盘空间
$ sudo journalctl --disk-usage
#指定日志文件占据的最大空间
$ sudo journalctl --vacuum-size=1G
#指定日志文件保存多久
$ sudo journalctl --vacuum-time=1years
END
关于 systemd 的完整介绍可以参考 systemd 官网手册:https://www.freedesktop.org/software/systemd/man/index.html
以及 systemd 的官网中文翻译手册:http://www.jinbuguo.com/systemd/systemd.index.html
systemd 源码地址:
https://launchpad.net/ubuntu/+source/systemd
https://github.com/systemd/systemd
以上就是开机启动的过程中 systemd 的作用机制介绍,如果有什么建议,欢迎前往论坛留言或加入优麒麟交流群一同探讨哦~