systemd简明教程

systemd是什么?

Linux系统在加载内核文件(/boot文件夹下的文件)以后,就会启动第一个程序systemd(以前是init),用于初始化系统环境,其进程号(pid)为1,其他进程都是他的子进程。

Systemd目的是要取代Unix时代以来一直在使用的init系统,兼容SysV和LSB的启动脚本,而且够在进程启动过程中更有效地引导加载服务。

systemd的特性有:
1、支持并行化任务
2、同时采用socket式与D-Bus总线式激活服务;
3、按需启动守护进程(daemon);
4、利用 Linux 的 cgroups 监视进程;
5、支持快照和系统恢复;
6、维护挂载点和自动挂载点;
7、各服务间基于依赖关系进行精密控制。

systemd加载路径

systemd 将会从一组在编译时确定好的目录中加载单元文件,并且较先列出的目录拥有更高的优先级(细节见后文)。也就是说,较先目录中的单元文件,会覆盖较后目录中的同名单元文件。
单元加载的先后顺序(较前的优先级较高):

/etc/systemd/system       本地的配置
/run/systemd/system       运行时的单元
/usr/lib/systemd/system   软件包安装的单元

systemd基本结构

首先看一个systemd服务文件的简单实例,写的一个rc-local.service 文件,内容如下:

#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

# This unit gets pulled automatically into multi-user.target by
# systemd-rc-local-generator if /etc/rc.d/rc.local is executable.
[Unit]
Description=/etc/rc.d/rc.local Compatibility
ConditionFileIsExecutable=/etc/rc.d/rc.local
After=neutron-openvswitch-agent.service openvswitch.service network.target

[Service]
Type=forking
ExecStart=/etc/rc.d/rc.local start
TimeoutSec=0
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

在systemd中,所有引导过程中systemd要控制的东西就是一个单元,这里rc-local.service就是一个单元,单元名为rc-local,但不只有以.service 为后缀的文件才可以是一个单元,单元还可以以.target,.path为后缀,systemd中单元的部分类型如下:

系统服务(.service)
套接字(.socket)
设备(.device)
挂载点(.mount)
自动挂载点(.automount)
SWAP 文件(.swap)
启动对象(.target)
文件系统路径(.path)
定时器(.timer)
快照(.snapshot)



systemd单元主要由三部分组成:[Unit][Service][Install]

[Unit]

单元文件中的 [Unit] 小节包含与单元类型无关的通用信息。

以上具体参数可通过如下命令查看

man systemd.unit

或者点击systemd.unit 中文手册 查看

[Service]

定义了单元的通用信息之后,使用[Service]定义服务本体, 每个服务单元文件都必须包含一个”[Service]”小节。

以上具体参数可通过如下命令查看

man systemd.service

或者点击systemd.service中文手册

[Install]

此小节包含单元的安装信息。事实上,systemd(1) 并不使用它。
此小节仅对单元的启用|禁用命令”systemctl enable|disable …”有意义
一般如下

[Install]
WantedBy=multi-user.target

示例

例1. 简单服务
    下面的单元文件创建了一个运行 /usr/sbin/foo-daemon 守护进程的服务。未设置的 Type= 等价于默认的 Type=simple
    执行 /usr/sbin/foo-daemon 进程之后,systemd 即认为该单元已经启动成功。

        [Unit]
        Description=Foo

        [Service]
        ExecStart=/usr/sbin/foo-daemon

        [Install]
        WantedBy=multi-user.target

    注意,/usr/sbin/foo-daemon 必须在启动后持续运行直到服务被停止。
    如果该进程只是为了派生守护进程,那么应该使用 Type=forking 

    因为没有设置 ExecStop= ,所以在停止服务时,systemd 将会直接向该服务启动的所有进程发送 SIGTERM 信号,
    若超过指定时间依然存在未被杀死的进程,那么将会继续发送 SIGKILL 信号。详见 systemd.kill(5) 手册。

    默认的 Type=simple 并不包含任何通知机制(例如通知”服务已完成初始化”)。要想使用通知机制,应该将 Type= 设为其他非默认值。
    例如:Type=notify 可用于能够理解 systemd 通知协议的服务;
          Type=forking 可用于能将自身切换到后台的服务;
          Type=dbus 可用于能够在完成初始化之后获得一个 D-Bus 名称的单元

例2. 一次性服务
    Type=oneshot 用于那些只需要执行一次性动作而不需要持久运行的单元,例如文件系统检查或者清理临时文件。
    此类单元,将会在启动后一直等待指定的动作完成,然后再回到停止状态。下面是一个执行清理动作的单元:

        [Unit]
        Description=Cleanup old Foo data

        [Service]
        Type=oneshot
        ExecStart=/usr/sbin/foo-cleanup

        [Install]
        WantedBy=multi-user.target

    注意,在 /usr/sbin/foo-cleanup 执行结束前,该服务一直处于’正在启动中’的状态,而一旦执行结束,
    该服务又立即变为’停止’状态,也就是说,对于 Type=oneshot 类型的服务,不存在’活动’状态。
    这意味着,如果再一次启动该服务,将会再一次执行该服务定义的动作。
    注意,在时间顺序上晚于该服务的单元,将会一直等到该服务变成’停止’状态后,才会开始启动。

    Type=oneshot 是唯一可以设置多个 ExecStart= 的服务类型。多个 ExecStart= 指令将按照它们出现的顺序依次执行,
    一旦遇到错误,就会立即停止,不再继续执行,同时该服务也将进入’失败’状态。

例3. 可停止的一次性服务
    有时候,单元需要执行一个程序以完成某个设置(启动),然后又需要再执行另一个程序以撤消先前的设置(停止),
    而在设置持续有效的时段中,该单元应该视为处于’活动’状态,但实际上并无任何程序在持续运行。
    网络配置服务就是一个典型的例子。此外,只能启动一次(不可多次启动)的一次性服务,也是一个例子。

    可以通过设置 RemainAfterExit=yes 来满足这种需求。
    在这种情况下,systemd 将会在启动成功后将该单元视为处于’活动’状态(而不是’停止’状态)。
    RemainAfterExit=yes 虽然可以用于所有 Type= 类型,但是主要用于 Type=oneshot 和 Type=simple 类型。
    对于 Type=oneshot 类型,systemd 一直等到服务启动成功之后,才会将该服务置于’活动’状态。
    所以,依赖于该服务的其他单元必须等待该服务启动成功之后,才能启动。
    但是对于 Type=simple 类型,依赖于该服务的其他单元无需等待,将会和该服务同时并行启动。

    下面的类似展示了一个简单的静态防火墙服务(simple-firewall.service):

        [Unit]
        Description=Simple firewall

        [Service]
        Type=oneshot
        RemainAfterExit=yes
        ExecStart=/usr/local/sbin/simple-firewall-start
        ExecStop=/usr/local/sbin/simple-firewall-stop

        [Install]
        WantedBy=multi-user.target

    因为服务启动成功后一直处于’活动’状态,所以再次执行”systemctl start simple-firewall.service”命令不会有任何效果。

例4. 传统的服务
    多数传统的守护进程(服务)在启动时会转入后台运行。systemd 通过 Type=forking 来支持这种工作方式。
    对于 Type=forking 类型的服务,如果最初启动的进程尚未退出,那么该单元将依然处于’正在初始化中’状态。
    当最初的进程成功退出,并且至少有一个进程仍然在运行(并且 RemainAfterExit=no),该服务才被视为处于’活动’状态。

    对于单进程的传统服务,当最初的进程成功退出后,将会只剩单独一个进程仍然在持续运行,
    systemd 将会把这个唯一剩余的进程视为该服务的主进程。
    仅在这种情况下,才将可以在 ExecReload=, ExecStop= … 之类的选项中使用 $MAINPID 变量。

    对于多进程的传统服务,当最初的进程成功退出后,将会剩余多个进程在持续运行,
    因此,systemd 无法确定哪一个进程才是该服务的主进程。在这种情况下,不可以使用 $MAINPID 变量。
    然而,如果主进程会创建传统的PID文件,那么应该将 PIDFile= 设为此PID文件的绝对路径,
    以帮助 systemd 从该PID文件中读取主进程的PID,从而帮助确定该服务的主进程。
    注意,守护进程必须在完成初始化之前写入PID文件,否则可能会导致 systemd 读取失败(读取时文件不存在)。

    下面是一个单进程传统服务的示例:

        [Unit]
        Description=Some simple daemon

        [Service]
        Type=forking
        ExecStart=/usr/sbin/my-simple-daemon -d

        [Install]
        WantedBy=multi-user.target

    参见 systemd.kill(5) 以了解如何结束服务进程。

例5. D-Bus 服务
    对于需要在 D-Bus 系统总线上注册一个名字的服务,应该使用 Type=dbus 并且设置相应的 BusName= 值。
    该服务不可以派生任何子进程。一旦从 D-Bus 系统总线成功获取所需的名字,该服务即被视为初始化成功。
    下面是一个典型的 D-Bus 服务:

        [Unit]
        Description=Simple DBus service

        [Service]
        Type=dbus
        BusName=org.example.simple-dbus-service
        ExecStart=/usr/sbin/simple-dbus-service

        [Install]
        WantedBy=multi-user.target

    对于用于 D-Bus 激活的服务来说,不可以包含”[Install]”小节,
    而是应该在对应的 D-Bus service 文件中设置 SystemdService= 选项,
    例如(/usr/share/dbus-1/system-services/org.example.simple-dbus-service.service):

        [D-BUS Service]
        Name=org.example.simple-dbus-service
        Exec=/usr/sbin/simple-dbus-service
        User=root
        SystemdService=simple-dbus-service.service

    参见 systemd.kill(5) 手册以了解如何结束服务。

例6. 能够通知初始化已完成的服务
    Type=simple 类型的服务非常容易编写,但是无法将’初始化已完成’的消息及时通知给 systemd 是一个重大缺陷。
    Type=notify 可以弥补该缺陷,它支持将’初始化已完成’的消息及时通知给 systemd 
    下面是一个典型的例子:

        [Unit]
        Description=Simple notifying service

        [Service]
        Type=notify
        ExecStart=/usr/sbin/simple-notifying-service

        [Install]
        WantedBy=multi-user.target

    注意,该守护进程必须支持 systemd 通知协议,否则 systemd 将会认为该服务一直处于’正在启动中’,并在超时后将其杀死。
    关于如何支持该通知协议,参见 sd_notify(3) 手册页。

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
永久连接: http://www.nfvschool.cn/?p=493
标签:

发表评论