每次开机都自检d盘,每次开机都出现自检是什么原因

首页 > 数码 > 作者:YD1662024-04-21 22:12:02

平常工作经常涉及systemd,之前掌握更多的是使用方法。源码走读了很久,准备系统的整理下,供其他人参考和交流。systemd提供的不仅仅是一个简单的启动管理系统,而是一个高度集成、功能丰富的操作系统基础架构解决方案,一套协同管理Linux系统各个方面的大规模软件套件,旨在简化系统配置、提高系统启动速度和整体性能。由于本人知识有限,梳理会有所侧重,其他未能覆盖到的地方可以参考其他文章。unti以service为主,源码梳理主路线:main->service ExecStart。

linux启动时序

在介绍systemd之前,有必要介绍下系统启动从上电到systemd的流程,有助于把握systemd在整个系统中的位置。

每次开机都自检d盘,每次开机都出现自检是什么原因(1)

  1. 上电后启动ROMCode,做开机自检(POST)。
  2. 选择启动设备,从磁盘加载引导程序bootloader到内存。
  3. bootloader加载kernel镜像到内存,执行各个内核模块初始化和driver初始化,如mm_init、sched_init、init_IRQ等。
  4. kernel完成初始化进入用户空间,执行第一个用户空间init程序。
  5. 对于使用systemd的系统,内核不再寻找传统的/sbin/init,而是查找配置为init的程序,通常指向/usr/lib/systemd/systemd。
systemd管理方式

用户空间进程众多,systemd充当管理者的角色按照一定的时序高效并行启动各个进程。systemd用单元(unit)的概念来描述待管理的结构,每个单元都是系统的一个组件,具有特定的配置和行为,根据要实现的功能或作用划分为:service、mount、device、timer、socket、scope、target等[1]

需要简单介绍下target单元类型,以便后面介绍sytemd分段启动做铺垫。它代表了系统的runlevel或者一组相关service的目标状态。target本身并不执行任何服务,而是作为一个容器,定义了一组需要同时激活的service集合。每个target unit文件(通常以.target为扩展名)包含所依赖的其他service和target单元。当系统切换到某个target时,systemd会确保所有该target依赖的service都被加载并启动成功,从而实现特定级别的系统功能。在Linux系统中常见的几个target units包括:

  1. sysinit.target:系统初始化阶段,完成系统底层服务及设备挂载等操作;
  2. basic.target:包含了基本系统服务,是许多其他target的基础。
  3. multi-user.target:代表多用户命令行模式,不包含图形界面。
  4. graphical.target:代表带有图形桌面环境的多用户模式。

default.target是一个特殊的unit,systemd在启动系统时始终使用default.target作为起点。default.target默认情况下会软连接到multi-user.target(服务器系统)或graphical.target(GUI)其中之一。可以通过下面命令查看和修改默认值:

root@localhost:~# ls -l /usr/lib/systemd/system/default.target lrwxrwxrwx 1 root root 16 Jan 5 14:27 /usr/lib/systemd/system/default.target -> graphical.target // 查看default.target root@localhost:~# systemctl get-default graphical.target // 修改default.target root@localhost:~# systemctl set-default multi-user.target Created symlink /etc/systemd/system/default.target → /usr/lib/systemd/system/multi-user.target.systemd分层启动

每次开机都自检d盘,每次开机都出现自检是什么原因(2)

参考 systemd bootup

上图是参考systemd bootup,进行了缩减以便清晰说明systemd分层启动的流程。

systemd以default.target为启动开始点,default.target软连接到graphical.target(以GUI系统为例),因为graphical.target配置了after=multi-user.target和Requires=multi-user.target,会先等待multi-user.target启动完成,依次类推到sysinit.target,最终会以sysinit.target以及依赖项为最早启动项,而graphical.target为最终达到的目标状态。

// graphical.target [Unit] Requires=multi-user.target Wants=display-manager.service After=multi-user.target rescue.service rescue.target display-manager.service // multi-user.target [Unit] Requires=basic.target After=basic.target rescue.service rescue.target //basic-user.target [Unit] Requires=sysinit.target Wants=sockets.target timers.target paths.target slices.target After=sysinit.target sockets.target paths.target slices.target tmp.mount //sysinit.target [Unit] Wants=local-fs.target swap.target After=local-fs.target swap.target emergency.service emergency.targetRequires、Wants和After的区别

从上面给出的配置中可以看到使用了三个关键字,用来定义和管理unit之间的依赖关系,根据不同的需求选择不同的关键字,为了达到更好的效果有时候也需要组合使用:

  1. Requires: 侧重于业务之间的强依赖。如A.service Requires B.service,systemd会保证A启动前B处于启动状态(start)或者已经启动完成状态(running),所以如果A启动时发现B未启动,systemd会拉起B,这样A和B就同时启动了。如果B启动失败或者运行中出现异常退出,那么A同样也会退出,体现了2者的强绑定。场景举例:web服务器依赖于mysql数据库才能正常工作,依赖关系必须使用Requires
  2. wants: 同Requires,区别在于依赖关系弱依赖,即被依赖者(B)不存在或者异常退出,依赖者(A)不受影响,依然正常运行。场景举例:系统大多service都需要收集log用于定位,弱依赖log service,即使log service未能启动,其他service也能正常运行。
  3. after: 侧重于启动顺序,解决的是时间上的依赖问题,而非功能上的依赖。如A.service After B.service,A必须等待B启动完成,B启动完成表示B处于active状态或者启动完成后正常退出。

启动完成的概念有必要解释下,即服务有start状态转为active状态。对于Type=notify类型的service来说开发者可以根据功能流程通过sd_notify(0, "READY=1")通知systemd已经启动完成。

用下面2个实验说明下Requires和After的区别:

// A.c #include <stdio.h> #include <unistd.h> int main() { while (1) {} return 0; } // /etc/systemd/system/A.service [Unit] After=B.service //实验1 # Requires=B.service //实验2 [Service] ExecStart=/home/xxx/Desktop/A [Install] WantedBy=multi-user.target

// B.c #include <stdio.h> #include <unistd.h> #include <systemd/sd-daemon.h> #include <sys/wait.h> int main() { // sleep 200ms后通知systemd ready usleep(200000); sd_notify(0, "READY=1"); while (1) {} return 0; } // /etc/systemd/system/B.service [Unit] Description=B [Service] Type=notify ExecStart=/home/xxx/Desktop/B [Install] WantedBy=multi-user.target

每次开机都自检d盘,每次开机都出现自检是什么原因(3)

实验2结果:A Requires B,A和B同时启动

每次开机都自检d盘,每次开机都出现自检是什么原因(4)

实验1结果:A After B,A在B sleep 200ms后启动

最常见的场景是当A.service需要在启动前确保B.service已经运行,在A.service中同时配置"Requires=B"和"After=B"。

注解

[1]:

systemd Unit

Description

.automount

用于实现启动时按需(即插即用)并行挂载文件系统单元。当访问特定的挂载点时,对应的文件系统会自动挂载。

.device

定义在/dev/目录下暴露给系统管理员的硬件和虚拟设备。并非所有设备都有unit文件;通常,硬盘、网络设备等块设备会有相应的unit文件。

.mount

定义Linux文件系统结构中的一个挂载点,用于管理文件系统的挂载操作。

.scope

.scope单元用于定义和管理一组系统进程集合。此类单元不由unit文件配置,而是通过程序方式创建。主要用于组织和管理服务工作者进程资源。

.service

.service unit文件定义由systemd管理的进程,包括cron定时任务服务、CUPS打印系统、iptables防火墙规则、逻辑卷管理服务(如LVM)、NetworkManager网络管理服务以及其他更多服务。

.slice

.slice单元定义了一个“切片”,它是系统资源的一个概念性划分,与一组进程相关联。可以将所有系统资源视为一个整体,而“切片”则代表从这个整体中划分出来的一部分资源。

.socket

.socket单元定义了进程间通信套接字,如网络套接字。当有连接请求到达时,systemd可以根据此单元来激活对应的服务。

.swap

.swap单元定义交换设备或交换文件,用于管理系统中的虚拟内存空间。

.target

.target单元定义了一组unit文件集合,它们表示启动同步点、运行级别和服务组。目标单元确定为了成功启动必须处于活动状态的服务和其他单元。

.timer

.timer单元定义了可以在指定时间触发程序执行的计时器

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.