Telechips TCC803X Systemd 笔记
一、systemd是什么
首先systmed是一个用户空间的程序,属于应用程序,不属于Linux内核范畴,Linux内核的主要特征在所有发行版中是统一的,厂商可以*改变的是用户空间的应用程序。Linux内核加载启动后,用户空间的第一个进程就是初始化进程,这个程序的物理文件约定位于/sbin/init,当然也可以通过传递内核参数来让内核启动指定的程序。这个进程的特点是进程号为1,代表第一个运行的用户空间进程。
CentOS6.5采用的是systemV init
CentOS7 采用的是systemd
二、Systemd物理文件组成
systemd是一个完整的软件包,安装完成后有很多物理文件组成,大致分布为:
- 配置文件,位于/etc/systemd这个目录下。
- 配置工具命令,位于/bin和/sbin这两个目录下。
- 预先准备的备用配置文件,位于/lib/systemd目录下。
systemd启动后,首先会去三个目录下找相应的配置文件,按优先级从高到底为/etc/systemd/,/usr/lib/systemd/和/lib/systemd/,优先级高的配置文件会覆盖优先级低的配置文件
在systemd*后,inittab不再起作用,也没有了“运行级”的概念。现在起作用的配置文件是/etc/systemd/system/default.target这个文件了。此文件的内容如下
看下graphical.target的组成(官方Demo),systemd的配置文件后缀根据配置单元类型的不同而不同,主要有.service,.target等。
# 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.
[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
看一下TCC803X Demo的Systemd目录组成内容
/etc/systemd
/lib/systemd
在/etc/systemd/system 目录下还可以看到诸如*.wants 的目录,放在该目录下的配置单元文件等同于在[Unit]小节中的 wants 关键字,即本单元启动时,还需要启动这些单元。比如您可以简单地把您自己写的 foo.service 文件放入 multi-user.target.wants 目录下,这样multi-user.target启动的时候就会将他的的子单元一并启动了
三、Systemd运行原理
systemd使用target取代了systemV的运行级的概念
Sysvinit 运行级别 | Systemd 目标 | 备注 |
---|---|---|
0 | runlevel0.target, poweroff.target | 关闭系统。 |
1, s, single | runlevel1.target, rescue.target | 单用户模式。 |
2, 4 | runlevel2.target, runlevel4.target, multi-user.target | 用户定义/域特定运行级别。默认等同于 3。 |
3 | runlevel3.target, multi-user.target | 多用户,非图形化。用户可以通过多个控制台或网络登录。 |
5 | runlevel5.target, graphical.target | 多用户,图形化。通常为所有运行级别 3 的服务外加图形化登录。 |
6 | runlevel6.target, reboot.target | 重启 |
emergency | emergency.target | 紧急 Shell |
其他自行百度
-
配置单元unit
-
依赖关系
-
并发启动原理之一:解决 socket 依赖
-
并发启动原理之二:解决 D-Bus 依赖
-
并发启动原理之三:解决文件系统依赖
四、Systemd配置使用
自动依赖
- 设置了 Type=dbus 的服务会自动添加 Requires=dbus.socket 与 After=dbus.socket 依赖
- 基于套接字**的服务会自动添加对与其相关的 .socket 单元的 After= 依赖。
除非明确设置了 DefaultDependencies=false,否则 service 单元都自动隐含如下依赖:
Requires=sysinit.target
After=sysinit.target
After=basic.target
Conflicts=shutdown.target
Before=shutdown.target
这样可以确保普通的服务单元:
- 在基础系统启动完毕之后才开始启动,
- 在关闭系统之前先被干净的停止。 只有那些需要在系统启动的早期就必须启动的服务,以及那些必须在关机动作的结尾才能停止的服务才需要设置 DefaultDependencies=false 。 systemd.exec(5) 与 systemd.resource-control(5) 中的某些资源限制选项也会自动隐含的添加一些其他的依赖关系。
从同一个模版实例化出来的所有服务单元(单元名称中带有 "@
" 字符), 默认全部属于与模版同名的同一个 slice 单元。 该同名 slice 一般在系统关机时,与所有模版实例一起停止。如果你不希望像上面这样,那么可以在模版单元中明确设置DefaultDependencies=no
,并且:要么在该模版文件中明确定义特定的slice单元(同样也要明确设置 DefaultDependencies=no
),要么在该模版文件中明确设置 Slice=system.slice
(或其他合适的 slice)。
[Unit]小节选项
Unit一般是第一部分,用来描述unit文件元数据以及服务的依赖关系。常用的命令有:
- Description=:这里一般写服务简短的描述。
- Documentation=:这里一般是服务文档的链接等。
- Requires=:这里写本服务依赖的其他服务,启动本服务时,一般会并行的启动该服务和它所依赖的服务,如果它依赖的服务启动失败了,本服务将无法启动成功。
- Wants=:这个命令和Requires=类似但是相对宽松一些,即使依赖的服务启动失败了,本服务也可以继续正常启动。一般的依赖都推荐使用这个命令。
- BindsTo=:和Requires=类似,但是如果依赖的服务停止了,本服务也会停止。
- Before=和After=:这两个需要和上面描述依赖关系的命令一起使用,表示依赖的当前服务与依赖的服务启动的先后顺序:Before=表示当前服务启动成功后才可以启动依赖服务,After=相反。
- Conflicts=:这个命令后面跟的服务将不能和当前服务同时运行,如果当前服务运行则会导致该命令列举的服务被停止。
- Condition...=:这个命令往往和许多其他命令一起使用,用来测试一些条件,比如测试当前的操作系统。如果条件不满足,则跳过当前服务的启动。
- Assert...=:和Condition...=类似,但是如果条件检测不满足会导致失败
[Service]小节选项
我们之前说了,unit对象有很多种类型,其中device
,target
,snapshot
,scope
这几种类型没有对应该类型的Section,其他的都有,比如service
这种类型特有的section就是[Service],也就是本节要介绍的。[Service]有一个必须的命令就是Type
,它根据进程的行为将服务分为好多类别,不同的类别管理不是不太一样:
- simple:这种是最普遍的类型,在启动行(使用ExecStart=指定)指定进程,如果Type=和Busname=没有设置,但是ExecStart=却指定了的话,那默认就是这种类型。
- forking:这种类别指的是那种fork出来子进程后,父进程就马上退出的情况。这种类型下,父进程退出后,systemd仍然认为进程是OK的。而且可以使用PIDFile=命令来指定存放主子进程pid的文件。Nginx就属于这种类型。
- oneshot:这种一般用在存活时间不长的一次性任务的进程上,它告诉systemd应该等待进程退出后再接着去处理其它的unit。
- dbus:这种类别的告诉systemd该unit会在D-Bus上面获取一个名字。
- notify:这种类别的服务会在启动完之后发出一个消息,systemd必须等到接收到这个消息后才可以接着去处理其它unit。
- idle:这种类别表示在收到所有任务前,服务都不会运行。
- WangtedBy 表明这个服务是在多用户模式下所需要的
上面就是Type=可取得值。下面介绍除Type=以外的其他命令:
- ExecStart=:用来指定进程文件(必须是绝对路径)和启动参数,一般该命令只能指定一次。有一个特殊的用法就是比如其他文件里面已经设置了,我们现在想在优先级更高的地方覆盖它,就可以先写一行ExecStart=(前面的表示清空之前的设置),然后再在另外一行写上完整的命令ExecStart=***。另外,如果在命令之前加上-的话表示进程如果以非0退出,也不算失败。
- ExecStartPre=和ExecStartPost=:看名字就看出来了,通过这两个指令可以指定在进程运行前和运行后执行的命令,同样也可以加-,表示接受非0的退出状态。
- ExecReload=:重新加载服务的配置。
- ExecStop=:指定停止服务的命令,如果未指定,服务停止后将使用kill来杀掉进程。
- ExecStopPost=:指定服务停止后运行的命令。
- RestartSec=:如果服务的自动重启设置了的话,这个命令指定多久重启。
- Restart=:指定systemd在何种状态下重启服务,可用值有:"always", "on-success", "on-failure", "on-abnormal", "on-abort", "on-watchdog".
- TimeoutSec=:指定systemd在标记服务失败多久前强制杀掉进程。也可以分别指定TimeoutStartSec=和TimeoutStopSec=。
[Install]小节选项
Install一般是最后一部分,用来描述unit的行为或者是否开机自启动等,是可选的。而且只有可以开机自启的(即可以被enable)的才会有这个Section。一般常用的命令有:
- WantedBy=:这个命令是最通用的用来指定服务如何被enable,即在哪些target/runlevel下被设置为开机自启动。我们可以通过这个命令来指定服务捡的依赖关系,有点像[Unit]部分的Wants=,但是这个只是辅助性的。当一个unit被enable后,就会在/etc/systemd/system目录下创建以.wants为后缀的目录,比如当前unit文件里面写了WantedBy=multi-user.target,那么enable当前unit后,就会在/etc/systemd/system目录下创建multi-user.target.wants目录,并且将当前unit及其依赖的unit的符号链接放在新创建的目录里面。disable该unit之后,它的软连接及其依赖的unit的软连接都将被删除。
- RequiredBy=:和WantedBy=类似,但是它指定的依赖条件如果不满足,就会导致服务启动失败。如果enable的话,创建的是.requires结尾的目录。
- Alias=:给服务创建别名。
- Also=:将多个unit设置为一个组,可以一起操作。
以TCC803X Demo的启动为例
# /lib/systemd/system/graphical.target
# 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.
[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一起运行。
- Wants字段:希望display-manager.service一起运行
- Conflicts字段:如果 rescue.service 或 rescue.target 正在运行,graphical.target就不能运行,反之亦然。
- After:表示graphical.target在multi-user.target rescue.service rescue.target display-manager.service之后启动,如果它们有启动的话。
- AllowIsolate:允许使用systemctl isolate命令切换到graphical.target
Requires和Wants的区别
典型的情况是,单元A要求单元B在A启动之前运行。在此情况下,向单元A配置文件中的 [Unit] 段添加 Requires=B 和 After=B 即可。若此依赖关系是可选的,可添加 Wants=B 和 After=B。请注意 Wants= 和Requires= 并不意味着 After=,即如果 After= 选项没有制定,这两个单元将被并行启动。
再跟踪multi-user.target
# /lib/systemd/system/multi-user.target
# 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.
[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
rescue.target
[Unit]
Description=Rescue Mode
Documentation=man:systemd.special(7)
Requires=sysinit.target rescue.service
After=sysinit.target rescue.service
AllowIsolate=yes
[Install]
Alias=kbrequest.target
rescue.service
[Unit]
Description=Rescue Shell
Documentation=man:sulogin(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=sysinit.target plymouth-start.service
Before=shutdown.target
[Service]
Environment=HOME=/home/root
WorkingDirectory=-/home/root
ExecStart=-/lib/systemd/systemd-sulogin-shell rescue
Type=idle
StandardInput=tty-force
StandardOutput=inherit
StandardError=inherit
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes
display-manager.service 没有
以此类推,得到如下关系图
这两个图的关系也基本一致
service :代表一个后台服务进程,比如 mysqld。这是最常用的一类。
target :此类配置单元为其他配置单元进行逻辑分组。它们本身实际上并不做什么,只是引用其他配置单元而已。这样便可以对配置单元做一个统一的控制。这样就可以实现大家都已经非常熟悉的运行级别概念。比如想让系统进入图形化模式,需要运行许多服务和配置命令,这些操作都由一个个的配置单元表示,将所有这些配置单元组合为一个目标(target),就表示需要将这些配置单元全部执行一遍以便进入目标所代表的系统运行状态。
socket :此类配置单元封装系统和互联网中的一个套接字 。当下,systemd 支持流式、数据报和连续包的 AF_INET、AF_INET6、AF_UNIX socket 。每一个套接字配置单元都有一个相应的服务配置单元 。相应的服务在第一个"连接"进入套接字时就会启动(例如:nscd.socket 在有新连接后便启动 nscd.service)。
device :此类配置单元封装一个存在于 Linux 设备树中的设备。每一个使用 udev 规则标记的设备都将会在 systemd 中作为一个设备配置单元出现。
mount :此类配置单元封装文件系统结构层次中的一个挂载点。Systemd 将对这个挂载点进行监控和管理。比如可以在启动时自动将其挂载;可以在某些条件下自动卸载。Systemd 会将 /etc/fstab 中的条目都转换为挂载点,并在开机时处理。
automount :此类配置单元封装系统结构层次中的一个自挂载点。每一个自挂载配置单元对应一个挂载配置单元 ,当该自动挂载点被访问时,systemd 执行挂载点中定义的挂载行为。
swap:和挂载配置单元类似,交换配置单元用来管理交换分区。用户可以用交换配置单元来定义系统中的交换分区,可以让这些交换分区在启动时被**。
timer:定时器配置单元用来定时触发用户定义的操作,这类配置单元取代了 atd、crond 等传统的定时服务。
snapshot :与 target 配置单元相似,快照是一组配置单元。它保存了系统当前的运行状态。
path:文件系统中的一个文件或目录。
scope:用于 cgroups,表示从 systemd 外部创建的进程。
slice:用于 cgroups,表示一组按层级排列的单位。slice 并不包含进程,但会组建一个层级,并将 scope 和 service 都放置其中。
五、系统引导性能分析
Systemd提供了工具用于识别和定位引导相关的问题或性能影响。Systemd-analyze是一个内建的命令,可以用来检测引导过程。你可以找出在启动过程中出错的单元,然后跟踪并改正引导组件的问题。在下面列出一些常用的systemd-analyze命令。
systemd-analyze time 用于显示内核和普通用户空间启动时所花的时间。
$ systemd-analyze time
Startup finished in1440ms(kernel)+3444ms(userspace)
systemd-analyze blame 会列出所有正在运行的单元,按从初始化开始到当前所花的时间排序,通过这种方式你就知道哪些服务在引导过程中要花较长时间来启动。
$ systemd-analyze blame
2001ms mysqld.service
234ms httpd.service
191ms vmms.service
systemd-analyze verify 显示在所有系统单元中是否有语法错误。
systemd-analyze plot 可以用来把整个引导过程写入一个SVG格式文件里。整个引导过程非常长不方便阅读,所以通过这个命令我们可以把输出写入一个文件,之后再查看和分析。下面这个命令就是做这个。
systemd-analyze plot > boot.svg
Telschips + Dolphin 通用配置启动信息矢量图(systemd-analyze plot > boot.svg)
Telschips + Dolphin 官方Linux Demo启动信息矢量图(systemd-analyze plot > boot.svg)
引用
https://www.linuxidc.com/Linux/2015-05/117640.htm
https://blog.csdn.net/smstong/article/details/39317491
https://time-track.cn/systemd-introduction.html
https://www.cnblogs.com/0616--ataozhijia/p/8037867.html
https://segmentfault.com/a/1190000009723940
下一篇:Daemon
帮助我们管理HMI等希望一直被监视的进程
- 后台服务进程代码不需要执行两次派生来实现后台精灵进程,只需要实现服务本身的主循环即可。
- 不要调用 setsid(),交给 systemd 处理
- 不再需要维护 pid 文件。
- Systemd 提供了日志功能,服务进程只需要输出到 stderr 即可,无需使用 syslog。
- 处理信号 SIGTERM,这个信号的唯一正确作用就是停止当前服务,不要做其他的事情。
- SIGHUP 信号的作用是重启服务。
- 需要套接字的服务,不要自己创建套接字,让 systemd 传入套接字。
- 使用 sd_notify()函数通知 systemd 服务自己的状态改变。一般地,当服务初始化结束,进入服务就绪状态时,可以调用它。