美文网首页
Systemd简介与使用

Systemd简介与使用

作者: 全狗 | 来源:发表于2020-01-07 19:17 被阅读0次

    按下电源键,随着风扇转动的声音,显示器上开启的图标亮起。之后,只需要静静等待几秒钟,登录界面显示,输入密码,即可愉快的玩耍了。
    这是我们大概每天都做的事情。那么中间到底发生了什么?
    简单地说,从BIOS或者UEFI开始读取硬盘。接下来,进入bootloader(LILO或者GRUB),bootloader开始载入内核,内核初始化完毕后,紧接着进入用户空间的初始化。
    用户空间的启动的第一个进程即pid=1,就是从一个叫init的程序开始的,这也是本文的主角

    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)和一组启动脚本以及符号链接集执行的,本质上为系统提供了合理的启动顺序,
    支持不同的运行级别。

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

    time

    图1

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

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

    systemd-time

    图2

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

    1.2.1 运行级别

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

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

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

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

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

    runlevel
    default

    其中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 architecture

    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 服务的缺点。

    • 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 的按需和资源并行启动

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

    参考

    相关文章

      网友评论

          本文标题:Systemd简介与使用

          本文链接:https://www.haomeiwen.com/subject/zvjdactx.html