按下电源键,随着风扇转动的声音,显示器上开启的图标亮起。之后,只需要静静等待几秒钟,登录界面显示,输入密码,即可愉快的玩耍了。

这是我们大概每天都做的事情。那么中间到底发生了什么?

简单地说,从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)和一组启动脚本以及符号链接集执行的,本质上为系统提供了合理的启动顺序,

支持不同的运行级别。

他的好处是依赖关系简单,任务之间泾渭分明的一个一个启动,即使某个基础服务出了错也便于排查。但也正因为如此,他的启动性能很不好。

服务无法并行启动不说,而且只能按照预先规定的顺序启动服务。如果你安装了新的硬件或者新服务,他不提供及时支持的标准方法。



图1

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

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



图2

图2是systemd的并行启动方式,他让配置所有的服务同时启动。如果Job Aing依赖Job B怎么办呢?首先两个Job是同时启动的,A如果先启动,就向B发送请求服务,B会先将请求缓存起来,等到B初始化完毕之后,再处理缓存的请求。

相比SysV init,这也带来了不确定性,即你不知道此时到底哪些服务起了,哪些没起,全依赖系统管理

1.2.1. 运行级别

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

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

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

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

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



其中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 架构图

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

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

1.8. 参考

Systemd简介与使用的更多相关文章

  1. CentOS7开机启动管理systemd简介及使用

    systemd提供更优秀的框架以表示系统服务间的依赖关系实现系统初始化时服务的并行启动,同时达到降低Shell的系统开销的效果systemd的目标是:尽可能启动更少进程:尽可能将更多进程并行启动.sy ...

  2. Systemd 添加自定义服务(开机自启动)

    Systemd 简介:https://fedoraproject.org/wiki/Systemd/zh-cn 一.service unit 常用命令,以 mysql 服务为例 # 开机启动 syst ...

  3. centos7 systemd 必知必会

    systemd 简介: systemd 是一个 Linux 系统基础组件的集合, 提供了一个系统和服务管理器, 运行为 PID 1 并负责启动其它程序 功能包括: 1.支持并行化任务 2.同时采用 s ...

  4. systemd - CentOS 7进程守护&监控

    需求: 运行环境为CentOS 7系统,我们开发了一个程序,需要在开机时启动它,当程序进程crash或者开机之后,守护进程立即拉起进程. 解决方案: 使用CentOS 7中的init进程systemd ...

  5. Linux 初始化系统(init)- systemd

    wikipedia 浅析 Linux 初始化 init 系统 systemd 中文手册 fedoraproject - systemd 1. Systemd 简介 Systemd 是 Linux 系统 ...

  6. Linux Systemd 详细介绍: Unit、Unit File、Systemctl、Target

    Systemd 简介 CentOS 7 使用 Systemd 替换了SysV Ubuntu 从 15.04 开始使用 Systemd Systemd 是 Linux 系统工具,用来启动守护进程,已成为 ...

  7. CentOS 7.0体验与之前版本的不同

    RHEL7和CentOS7出来有一段时间了,拿出点时间研究下,有几个地方跟6和5系列相比改变比较大,估计不少童鞋有点不太习惯.下面简要举例说明改变比较大的要点: 一.CentOS的Services使用 ...

  8. centOS7服务管理与启动流程

    centOS7服务管理与启动流程 centOS7启动流程 systemd简介 unit对象 unit类型 特性 service unit文件格式 service unit file文件通常由三部分组成 ...

  9. 如何配置Linux的服务设置为自动启动或崩溃重新启动后

    介绍 在本教程中,自动启动 Linux 服务,我们将退后一步,更详细地解释 init 进程. 你应该很好地了解它们如何控制守护进程的启动行为. 在第一部分本系列教程我们分享使用 MySQL 的如何崩溃 ...

随机推荐

  1. JavaScript中实现DI的原理

    什么是依赖注入 按照上面图的流程中我们可以知道我们需要实现这么几件事: 提供一个服务容器 为目标函数注册需要的依赖 获取目标函数注册的依赖项 通过依赖项来查询对应服务 将获取的依赖项传入目标函数 提供 ...

  2. python读写不同编码txt文件

        以后整理规范 import os import codecs filenames=os.listdir(os.getcwd()) out=file("name.txt",& ...

  3. Oracle基础之Merge into

    Merge into语句是Oracle9i新增的语法,用来合并UPDATE和INSERT语句. 通过MERGE语句,根据一张表或多表联合查询的连接条件对另外一张表进行查询,连接条件匹配上的进行UPDA ...

  4. Python unittest模块心得(二)

    基础概念介绍请参看: http://www.cnblogs.com/frost-hit/p/8295818.html 组织测试用例 unittest.TestSuite(tests=()): 除了使用 ...

  5. webstorm出现黑色块光标

    取消掉此项对勾即可

  6. 用java读取多种文件格式的文件(pdf,pptx,ppt,doc,docx..)

    本文通过开源pdfbox和poi进行处理多种文件格式的文本读入 1.需要的jar的maven坐标: <dependency> <groupId>org.apache.pdfbo ...

  7. C#中的多线程 - 并行编程 z

    原文:http://www.albahari.com/threading/part5.aspx 专题:C#中的多线程 1并行编程Permalink 在这一部分,我们讨论 Framework 4.0 加 ...

  8. win10系统 WMI Provider Host cpu 占用过高

    今天上班,发现系统有点卡,QQ总是没响应. 打开任务管理器,发现有一个程序- WMI Provider Host 占用cpu竟然高达80% 然后通过事件查看器发现是一个pid为9832的程序造成的 然 ...

  9. jsp和servlet的问题收集.... 答案有部分是自己理解的,可能有点差异

    如何创建一个动态工程? File ---->  New ---->other ---->Web ---->Dynamic Web Project  选择动态WEB 项目工程 W ...

  10. Linux修改权限命令chmod用法详解

    Linux系统中的每个文件和目录都有访问许可权限,用它来确定谁可以通过何种方式对文件和目录进行访问和操作. 文件或目录的访问权限分为只读,只写和可执行三种.以文件为例,只读权限表示只允许读其内容,而禁 ...