Systemd简介与使用

2020-09-12 0 453 百度已收录

按下电源键,随着风扇转动的声音显示器上开启的图标亮起。之后,只需要静静等待几秒钟,登录界面显示,输入密码,即可愉快的玩耍了。
这是我们大概每天都做的事情。那么中间到底发生了什么?

简单地说,从BIOS或者UEFI开始读取硬盘。接下来,进入bootloader(LILO或者GRUB),bootloader开始载入内核,内核初始化完毕后,紧接着进入用户空间的初始化。

用户空间的启动的第一个进程即pid=1,就是从一个叫init程序开始的,这也是本文的主角

  • 1. Systemd简介与使用
    • 1.1. 用户空间的启动顺序
    • 1.2. SystemV
      • 1.2.1. 运行级别
    • 1.3. Systemd
      • 1.3.1. systemd启动步骤
      • 1.3.2. 单元和单元类型
        • 1.3.2.1. 单元类型
      • 1.3.3. systemd相关指令
        • 1.3.3.1. 电源管理
        • 1.3.3.2. 分析系统状态
        • 1.3.3.3. 单元的管理
        • 1.3.3.4. 单元的依赖列表
        • 1.3.3.5. 其他
    • 1.4. systemd配置
      • 1.4.1. Service文件
        • 1.4.1.1. [Unit]
        • 1.4.1.2. [Service]
        • 1.4.1.3. [Install]
      • 1.4.2. target文件
    • 1.5. systemd日志服务
    • 1.6. 在systemd中添加单元
      • 1.6.1. 写一个小栗子
    • 1.7. systemd 的按需和资源并行启动
    • 1.8. 参考

1. Systemd简介与使用

1.1. 用户空间的启动顺序

用户的空间的大致启动顺序如下:

  • init
  • 基础底层服务,如udevd(设备管理器),syslogd(日志管理)
  • 网络配置
  • 中高层服务,如cron(定时器)
  • 登录提示符(getty)、GUI、MySQL(如果设置开机启动的话)

init是内核启动的第一个用户空间进程,主要负责启动、终止系统中的基础服务进程。

Linux下,init主要有三个实现版本

  • System V,传统的init
  • Upstart,Ubuntu后期针对sys-v的一个改进实现版本
  • systemd,是一套中央化系统及设置管理程序(init),包括有守护进程、程序库以及应用软件,兼容sys-v。现代大部分桌面版都使用此实现。也是本文主要介绍的一个…emmmm…框架。是的,systemd更像一个服务管理框架。

1.2. SystemV

先说说传统的SystemV,他其实就是利用一系列脚本来启动服务,之后的事就撒手不管了。

SystemV init依赖一个特定的启动顺序每次只能启动执行一个启动任务。

这些都是通过一个核心配置文件tab(/etc/init)和一组启动脚本以及符号链接集执行的,本质上为系统提供了合理的启动顺序,
支持不同的运行级别。

他的好处是依赖关系简单,任务之间泾渭分明的一个一个启动,即使某个基础服务出了错也便于排查。但也正因为如此,他的启动性能不好
服务无法并行启动不说,而且只能按照预先规定的顺序启动服务。如果你安装了新的硬件或者新服务,他不提供及时支持的标准方法

Systemd简介与使用
图1

我们把用户空间init的服务分别叫做Job AJob B…图1可以看到,在SysV init之下,服务必须一个接一个的顺序启动,前面的服务初始化完毕,后面才可以开始。因此,启动时间就是所有服务启动时间之和。

他的改进版Upstart在此基础上就做了优化——互不相关的服务可以并行启动,这样启动总时间就等于时间消耗最大的一组服务,而不是所有服务之和。systemd在并行启动上采取了比Upstart更加激进的方案

Systemd简介与使用
图2

图2是systemd的并行启动方式,他让配置所有的服务同时启动。如果Job Aing依赖Job B怎么办呢?首先两个Job是同时启动的,A如果先启动,就向B发送请求服务,B会先将请求缓存起来,等到B初始化完毕之后,再处理缓存的请求。
相比SysV init,这也带来了不确定性,即你不知道此时到底哪些服务起了,哪些没起,全依赖系统管理

1.2.1. 运行级别

运行级别的概念最早应该也是来自于SysV init.

简单地说,运行级别定义了系统的特定状态,这种状态可以看成一系列服务状态的集合。

不同的发行版有不同的运行级别,但比较公认的如下:

  • 0,关机
  • 1,单用户模式(修复模式),如果你的系统凉凉了,这将是你的救命稻草
  • 6,重启

以我个人的deepin15.7为例,如图

Systemd简介与使用
Systemd简介与使用

其中runlevel2/3/4都属于同一个运行等级(multi-user),而系统的默认的运行等级为5——graphical。我们平时所用的桌面环境就是这个等级了。其实,现代大部分采用systemd的发行版都和这个大同小异。

我们使用systemctl cat graphical.target打开graphical.target文件,可以看到下面内容

[Unit]  Description=Graphical Interface  Documentation=man:systemd.special(7)  Requires=multi-user.target  Wants=display-manager.service  Conflicts=rescue.service rescue.target  After=multi-user.target rescue.service rescue.target display-manager.service  AllowIsolate=yes

其中的Requires=multi-user.target表示,如果想启动graphical.target(即运行等级5)就必须先启动multi-user.target(运行等级3).由此可见,在systemd中,运行等级5就是在等级3基础上,同时启动一个display-manager服务。display-manage顾名思义,肯定是和图像显示有关的咯。

如果你对.target文件,和他的定义语法很迷惑,没有关系,后面还会详细解释。我举这个例子,只是想让你了解systemd是兼容systemV的运行等级概念的。所以,你关于SystemV的认识也是可以继续沿用的

1.3. Systemd

在Linux中以d结尾的,表示这是一个守护程序,systemd就是这个系统的守护程序

相比于之前的版本,systemd最关键的特性是:

  • 延迟启动某些服务和系统功能,等到需要他们的时候才开启
  • 完全并行启动

Systemd简介与使用

systemd 架构图

1.3.1. systemd启动步骤

systemd的特性复杂,下面给出大致的启动步骤,使我们有个总体观:

  1. systemd加载配置信息
  2. 判定启动目标,一般是default.target
  3. 判定启动目标的依赖关系
  4. 激活依赖服务,启动目标
  5. 响应系统消息,激活其他组件

1.3.2. 单元和单元类型

systemd不光负责处理进程和服务,同时还能挂载文件系统、监控网络套接字等等。在systemd中
所有服务和功能都被抽象成一个个单元(Unit),根据功能不同,单元类型也不同。systemd正是通过配置这些单元
来开关、管理服务的。

1.3.2.1. 单元类型

比较常用几种

  • 服务单元,传统的守护进程(XXX.service文件表示)
  • 挂载单元,控制文件系统挂载(XXX.mount文件表示)
  • 目标单元,将服务单元、挂载单元等单元组织在一起的单元,一般对应Sys-V的运行等级(XXX.target文件表示)

上面的尤其是服务单元我们会经常打交道,而且必要时也可以自定义服务单元等。比如我们的蓝牙功能就抽象成
blueteeth.service,管理磁盘的udev系统对应systemd-udevd.service文件。如果你安装了mysql
还可以找到一个mysql.service文件。

使用deepin15.7的过程中遇到过一个bug,就是在系统长期休眠之后再重启,蓝牙模块莫名其妙的关闭了,进入[设置]面板
无法找到蓝牙配置选项了。这时执行systemctl restart blueteeth.service重启蓝牙模块,大概率就会修复了

除了以上几种,还有其他类型,比如
socket单元(.socket)、系统设备单元(.device)、交换单元(.swap)、路径单元(.path)、定时单元(.time),
不一而足

1.3.3. systemd相关指令

1.3.3.1. 电源管理

主要涉及开关、系统重启等,如果你是当前唯一用户的话则不需要提权,否则需要root密码

systemctl reboot #重启  systemctl poweroff #关机  systemctl suspend #待机  systemctl hibernate #休眠  systemctl rescue #进入单用户模式

1.3.3.2. 分析系统状态

主要是查看系统中纳入systemd管理的服务的状态

systemctl status #系统状态  systemctl list-units #所有激活单元列表  systemctl --failed #运行失败单元列表    # 列出所有配置文件  $ systemctl list-unit-files    # 列出指定类型的配置文件  $ systemctl list-unit-files --type=service

1.3.3.3. 单元的管理

使用systemd操作单元的激活与关闭

systemctl start <unit> #立即激活单元  systemctl stop <unit> #立即关闭单元  sudo systemctl kill <unit> #前面的stop不好使了,就强行杀死这个单元  systemctl restart <unit> #重启单元  systemctl status <unit> #单元状态,这是和好用的指令,能够看到服务单元的几乎所有信息    systemctl is-enabled <unit> #单元是否配置自动启动  systemctl enable <unit># 配置自动启动单元  systemctl disable <unit>#关闭单元自动启动    systemctl help <unit>#单元帮助手册,一般是服务单元    systemctl daemon-reload <unit>#扫描单元配置文件变动,重新载入    systemctl mask <unit> #禁用单元  systemctl unmask <unit>#取消禁用

下面是我本人计算机上mysql的状态信息:

systemctl status mysql.service  ● mysql.service - MySQL Community Server     Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled)     Active: active (running)    Process: 2666 ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/run/mysqld/mysqld.pid (code=exited, status=0/SUCCESS)    Process: 2602 ExecStartPre=/usr/share/mysql/mysql-systemd-start pre (code=exited, status=0/SUCCESS)   Main PID: 2668 (mysqld)      Tasks: 27 (limit: 4915)     Memory: 218.1M     CGroup: /system.slice/mysql.service             └─2668 /usr/sbin/mysqld --daemonize --pid-file=/run/mysqld/mysqld.pid
  • loaded,单元配置文件地址
  • active:激活状态
  • process:开启服务时执行的指令
  • main Pid:主进程ID
  • memory:占用内存
  • CGroup:systemd通过CGroup控制进程,这里展示该服务的所有子进程

1.3.3.4. 单元的依赖列表

systemctl list-depandencies <xxx.service> #列出xxx.service的依赖单元

在systemd中的单元的依赖关系

1.3.3.5. 其他

一些杂七杂八的指令

systemd-analyze #系统启动时间统计  systemd-analyze blame #查看所有服务启动时间列表,blame就能看出,这是要等一个背锅位  localectl #本地化信息  timedatectl #时区信息  loginctl list-user #列出当前登录用户

systemd的指令非常丰富,可以通过查询文档获取全部指令

1.4. systemd配置

systemd的配置文件主要分布在两个地方
系统单元目录(全局配置,我的是/lib/systemd/system)和系统配置目录(局部配置,我的是/etc/systemd/system)

你可以通过下面的指令查询配置目录:

pkg-config systemd --variable=systemdsystemunitdir #单元目录  pkg-config systemd --variable=systemdsystemconfdir #配置目录

其实配置目录的很多文件都是指向单元目录的软链接。

单元配置文件就像一个蓝图,定义了一个单元的依赖关系、启动顺序、开启关闭指令或者挂载点等,
systemd就是读取这些信息来管理单元的。

1.4.1. Service文件

在systemd中一个.service是一个服务类型的配置单元,同时也代表了一个服务功能。

我们使用sysctemctl cat ssh.service来查看ssh.service文件内容,该文件就在/lib/systemd/system下.

注:这个Service只有在你安装openssh-server之后才会有.

[Unit]  Description=OpenBSD Secure Shell server  After=network.target auditd.service  ConditionPathExists=!/etc/ssh/sshd_not_to_be_run    [Service]  EnvironmentFile=-/etc/default/ssh  ExecStartPre=/usr/sbin/sshd -t  ExecStart=/usr/sbin/sshd -D $SSHD_OPTS  ExecReload=/usr/sbin/sshd -t  ExecReload=/bin/kill -HUP $MAINPID  KillMode=process  Restart=on-failure  RestartPreventExitStatus=255  Type=notify  RuntimeDirectory=sshd  RuntimeDirectoryMode=0755    [Install]  WantedBy=multi-user.target  Alias=sshd.service

可以看到service文件分为Unit/Service/Install三个区块,我们分开解释

1.4.1.1. [Unit]

主要描述启动顺序与依赖关系

[Unit]  Description=OpenBSD Secure Shell server  After=network.target auditd.service  ConditionPathExists=!/etc/ssh/sshd_not_to_be_run

Description,一段描述Service的信息

After,表示ssh.servicenetwork.target auditd.service单元之后启动。另外还有一个属性Before
表示当前单元在列出的单元之前启动。比如Before=bar.service,说明当前单元在bar.service之前启动。
AfterBefore定义了单元之间启动的顺序

ConditionPathExists,表示在后面的路径存在时返回true,这里使用了!非运算符,应该是取反的意思。
同样还有其他几个路径判断条件——ConditionPathIsDirectoryConditionFileNotEmpty,顾名思义,他们的
意义应该不难猜吧。这些条件必须返回为true,否则该单元不会运行

1.4.1.2. [Service]

这个区块定义如何启动当前服务

[Service]  EnvironmentFile=-/etc/default/ssh  ExecStartPre=/usr/sbin/sshd -t  ExecStart=/usr/sbin/sshd -D $SSHD_OPTS  ExecReload=/usr/sbin/sshd -t  ExecReload=/bin/kill -HUP $MAINPID  KillMode=process  Restart=on-failure  RestartPreventExitStatus=255  Type=notify

EnvironmentFile,指定当前服务环境参数文件,内部使用键值对定义,可以使用$key读取值,比如后面的$SSHD_OPTS

ExecStartPre,定义启动服务前执行的指令

ExecStart,定义启动程序执行的指令

ExecReload,表示重启服务时执行的命令。其他的诸如ExecStop等等,望文生义即可

KillMode,定义 Systemd 如何停止 sshd 服务,process表示当kill sshd服务的时候,仅杀死主进程,子进程还是留着的。
其他的kill模式还有:

  • control-group(默认值):当前控制组里面的所有子进程,都会被杀掉
  • mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
  • none:没有进程会被杀掉,只是执行服务的 stop 命令

Restart字段,定义了 sshd 退出后,Systemd 的重启方式。on-failure,表示任何意外的失败,就将重启sshd。
另外还有其他重启模式定义:

  • no(默认值):退出后不会重启
  • on-success:只有正常退出时(退出状态码为0),才会重启
  • on-abnormal:只有被信号终止和超时,才会重启
  • on-abort:只有在收到没有捕捉到的信号终止时,才会重启
  • on-watchdog:超时退出,才会重启
  • always:不管是什么退出原因,总是重启

最后一个比较重要的是Type字段,定义启动类型。notify,表示启动结束后会发出通知信号,然后 Systemd 再启动其他服务。
其他的类型如下:

  • simple(默认值):ExecStart字段启动的进程为主进程
  • forking:ExecStart字段将以fork()方式启动,此时父进程将会退出,子进程将成为主进程
  • oneshot:类似于simple,但只执行一次,Systemd 会等它执行完,才启动其他服务
  • dbus:类似于simple,但会等待 D-Bus 信号后启动

1.4.1.3. [Install]

定义如何安装这个配置文件,即怎样做到开机启动

WantedBy字段:表示该服务所在的Target。
Target的含义是服务组,表示一组服务。WantedBy=multi-user.target指的是,sshd 所在的 Target 是multi-user.target。

systemctl enable sshd.service其实就是将sshd服务的链接放在multi-user.target.wants目录下。
同时multi-user.target是系统的默认target,在启动该target的时候,他下面的服务都会开机启动。
这也就是只要挂上multi-user.target就能开机启动的原因

1.4.2. target文件

执行systemctl cat multi-user.target,可得:

[Unit]  Description=Multi-User System  Documentation=man:systemd.special(7)  Requires=basic.target  Conflicts=rescue.service rescue.target  After=basic.target rescue.service rescue.target  AllowIsolate=yes

target文件只是组织一批服务,因此他没有[service]、[mount]等定义启动或者挂载的区块

Requires,表示强依赖关系,即必须要求basic.target启动,否则multi-user启动失败。
其他的依赖关系如下:

  • Wants,只用于激活依赖,没有强依赖关系,该服务没起来也不影响当前服务
  • Conflicts,冲突关系,有我没他,否则不能运行
  • Requisite,前置依赖,当前单元激活前,必须激活它,否则失败,属于强依赖

Wants是比较重要的依赖关系,他不会将启动错误扩散给其他单元。systemd文档鼓励我们多用Wants关系

AllowIsolate,表示允许使用systemctl isolate命令切换到multi-user.target

1.5. systemd日志服务

systemd 自带日志服务 journald,该日志服务的设计初衷是克服现有的 syslog 服务的缺点

Systemd Journal 用二进制格式保存所有日志信息,用户使用 journalctl 命令来查看日志信息。无需自己编写复杂脆弱字符串分析处理程序。

常见的指令如下:

# 查看所有日志(默认情况下 ,只保存本次启动的日志)  $ 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    # 合并显示多个 Unit 的日志  $ journalctl -u nginx.service -u php-fpm.service --since today    # 查看指定优先级(及其以上级别)的日志,共有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    # 以 JSON 格式(单行)输出  $ sudo journalctl -b -u nginx.service -o json    # 以 JSON 格式(多行)输出,可读性更好  $ sudo journalctl -b -u nginx.serviceqq   -o json-pretty    # 显示日志占据的硬盘空间  $ sudo journalctl --disk-usage    # 指定日志文件占据的最大空间  $ sudo journalctl --vacuum-size=1G    # 指定日志文件保存多久  $ sudo journalctl --vacuum-time=1years

1.6. 在systemd中添加单元

关于自定义单元的首要一点建议:不要更改/lib/systemd/system(系统单元目录),他由系统维护。
我们一般在/etc/systemd/system下自定义启动单元。

1.6.1. 写一个小栗子

  • 创建一个名为test1.target的单元
[Unit]  Description=test 1
  • 创建test2.target,依赖与test1
[Unit]  Description=test 2  Wants=test1.target
  • 激活test2.target,test1作为依赖也会被激活

systemctl start test2.target

  • 验证两个是否都被激活

systemctl status test1.target test2.target

注:如果单元内包含[Install]模块,需要在start前enable他.
systemctl enable <unit>

  • 删除单元
systemctl stop <unit> #首先停止单元  systemctl disable <unit> #如果有[Install]模块,则删除连接符号  #最后删除单元文件即可

1.7. systemd 的按需和资源并行启动

这是一个很复杂的概念,最好单独讨论

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

栗子博客 软件 Systemd简介与使用 https://www.lizi.tw/soft/12427.html

常见问题
  • 1、杰齐1.7仅适用于PHP5.2 2、需Zend支持 3、尽量使用宝塔面板 4、尽量使用Windows 系统,关关对Linux支持不太友好。
查看详情

相关文章

评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务

Systemd简介与使用-海报

分享本文封面