Systemd
Systemd是Linux系统中最新的初始化系统(init),如CentOS 7。它主要解决CentOS7之前的System V init的缺点,提高系统启动速度,Systemd的概念来自于MacOS上的Launchd
System V介绍
Linux系统类似Windows一样可以自启动和禁止一些服务程序,在Syst V init的管理体系中,这些服务脚本程序存放在/etc/init.d文件下,在/etc存放rc0~rc6等目录指向init.d下的相应目录,这些目录下的文件即为不同运行级别需要启动或者禁止的服务。
System V有7个级别:runlevel0~runlevel7
级别 | 说明 |
---|---|
0 | 关机状态,默认系统启动级别不能设置0,否则不能正常启动 |
1 | 单用户,无网络,用于系统维护,类似windows下的安全模式 |
2 | 多用户 |
3 | 完整多用户模式。 |
4 | 自定义,通常保留不用 |
5 | 图形模式,如果系统有安装GUI软件,XWindows会进入图形模式 |
6 | 重启系统,默认系统启动级别也不能设置6,否则不能正常启动 |
- 标准运行级别是3和5,可在/etc/inittab 下的
id:{$levelnum}:initdefault:
修改启动级别,如修改为3级别id:3:initdefault
,下次启动后生效,Linux系统init进程会根据inittab配置文件确定当前运行级别并执行相应级别rc3
的服务脚本程序。
rc3目录下存放两种文件,一种S开头:表示启动服务,一种K开头:表示禁止服务,字母后面的数字表示启动顺序,按小到大执行
System V基本工具
System V主要用chkconfig
,service
,命令管理服务,在使用命令前需要将相应服务脚本放在/etc/init.d目录下。
1 | chekconfig格式 |
Systemd介绍
Systemd是Linux最新的初始化系统。取代了之前Unix时代一直使用的init系统,兼容Syst V init 和 LSB init script,而且在进程启动过程中更有效的引导加载服务。
在Systemd管理体系中,老的运行级别(runlevel)
的概念被新的 运行目标(target)
所取代。
target
命名类似multi-user.targe
t这种形式。
old | new |
---|---|
runlevel3 | multi-user.target |
runlevel5 | graphical.target |
在新版的CentOS上默认target是通过软连接形式实现
ln -s /lib/systemd/system/runlevel3.target /etc/systemd/system/default.target
在/lib/systemd/system/下面定义的runlevelX
文件目的主要是为了兼容以前运行levelrunX
的管理方式,实际是被链接到了multi-user.target
Systemd特点
兼容SysVinit和LSB init scripts
更快的启动速度,以并发启动的原理
尽可能启动更少进程
尽可能将更多进程并行启动
提供按需启动能力
- 采用Linux的CGroup特性跟踪和管理进程的生命周期
- 启动挂载点自动挂载管理
- 实现事务性依赖关系管理
- 能够对系统进行快照和恢复
- 日志服务
- 能够向后支持SysV的服务脚本
特点说明:
按需提供能力
当Sysinit系统初始化的时候,它会将所有可能用到的后台服务进程全部启动运行,并且系统必须等待所有服务都启动就绪后才可以运行用户登录。
这种方式的缺陷启动时间过长,系统资源浪费。如果某些服务可能很长一段时间内或者运行期间没有被使用过的服务,如CPUS,如果花费在启动这些服务上的时间完全没有必要,对系统资源也是一种浪费。
Systemd可以按需启动,在某个服务被真正请求的时候才启动它,当服务结束时,systemd可以关闭它,在下次需要的时候在启动它
采用Linux的CGroup特性跟踪和管理进程的生命周期
init系统的一个重要职责就是负责跟踪和管理服务进程生命周期,它不仅可以启动一个服务,也必须也能停止一个服务,看上去没特别的,但是在真正用到代码实现的时候,获取就会发现停止服务比一开始想的要困难。
服务进程一般都会作为
daemon
在后台运行,为此服务程序有时会派生fork
两次。在UpStart中,需要在配置文件中正确配置expect
,这样Upstart通过对fork
系统调用进行计数,从而获得真正的daemon PID
如果Upstart找错了,则会出现误杀死情况还有特殊的情况,比如一个CGI程序会派生两次,从而脱离了和apache的父子关系,当apache进程被停止后,该CGI程序还继续运行,而我们希望服务停止后,所有由它所启动的相关进程也会被停止。为了处理这类问题,UpStart通过
strace
来跟踪fork
和exit
等系统调用,但是这种方式简单粗暴,且扩展性弱。Systemd则利用Linux内核CGroup特性来完成跟踪任务,当服务停止后,查询CGroup,Systemd可以确保找到所有相关的进程,从而干净停止服务。CGroup提供能类似文件系统的接口,使用方便,主要实现系统资源配额管理。当创建子进程时,子进程会继承父进程的CGroup,因此无论服务如何启动新的子进程,所有这些相关的进程都会属于同一个CGroup。Systemd只需要简单的遍历指定的CGroup,即可正取找到所有相关进程,将它逐步停止
启动挂载点自动挂载管理
传统Linux系统中,用户可以用
/etc/fstab
文件来维护固定文件系统挂载点,这些挂载点在系统启动的过程中被自动挂载,一旦启动过程结束,这些挂载点会确保存在。挂载点是对系统运行至关重要的文件系统,如home目录。和SysVinit一样,Systemd管理这些挂载点,以便在系统启动时自动挂载它们,有时候用户还需要动态挂载,比如打算访问DVD内容时,才临时执行挂载一遍访问其内容,而不访问光盘时候,该挂载点被卸载umount
以便节约资源,SysVinit依赖autofs服务来实现这种功能,Systemd内建自动挂载服务,无需额外安装autofs服务,可以直接用systemd提供的自动挂载管理能力来实现autofs功能。另外Systemd也兼容/etc/fstab文件,可以继续使用该文件管理挂载点
实现事务性依赖关系管理
系统启动过程是由很多独立组件共同组成,这些组件存在依赖关系,如果挂载一个NFS文件系统必须依赖网络才能正常工作,System虽然能够最大限度的并发执行很多依赖关系的工作,但是类似
挂载NFS
和启动网络
这两个工作还是存在先后关系,无法并发执行,对于这些任务,Systemd维护一个’事物一致性‘’的概念,保证所有相关服务都可以正常启动而不会出现互相依赖,以至于死锁情况
能够对系统进程快照和恢复
Systemd支持按需启动,因此系统的运行状态是动态变化的,无法确定和准确知道系统当前运行了哪些服务,System快照提供了一种将当前系统运行状态保存并恢复的能力。
如系统当前正运行服务APP1和APP2,可以使用Systemd命令对当前系统运行状况创建快照,然后将APP1停止,或者做其他任意对系统的改变(如启动新的程序APP3),在改变后运行Systemd的快照恢复命令,即可立即将系统恢复到创建快照时候的状态,即只有APP1和APP2运行。常见场景:比如服务器出现一些异常,为了调试用户当前状态保存快照,然后可以任意操作,比如停止服务等,当调试完成后,恢复操作即可。(注意:改功能并不完善,使用时需要慎重)
日志服务
简单性:代码少,依赖少,开销最小
零维护:日志是排错和监控系统的核心功能,因此它自己不能再产生问题,比如自动管理磁盘空间,避免由于日志不断产生的磁盘空间耗尽
移植性:日志文件在所有类型的Linux系统可用
性能:添加 和 游览日志 非常快
最小资源占用:日志数据文件小
统一化:各种不同日志存储应该统一,将所有可记录事件保存在同一个数据存储中,所以日志内容的全局上下文都会被保存并且可供日后查看,如一条固件记录后通常会跟随一条内核记录,最终还有一条用户态记录。重要的是当保存到硬盘上时这三种关系不会丢失。早期Syslog会将不同信息保存到不同文件中,分析时候很难确定哪些条目是相关的
扩展性:日志使用范围广,嵌入式或者超级计算群集都可以满足
安全性:日志文件可以验证
Systemd基本概念
Systemd单元概念
系统初始化需要做很多事情。需要启动后台服务,如sshd服务,需要做配置工作,比如挂载文件系统,这个过程中每一步都被systemd抽象成一个配置单元,即unit,可以认为一个服务就是一个配置单元;一个挂载点是一个配置单元,一个交换分区配置是一个配置单元。systemd将配置单元归纳为以下不同的类型。
配置单元常见类型
类型 | 扩展名 | 说明 |
---|---|---|
service unit | .service | 定义系统类服务 |
target unit | .target | 实现模拟“运行级别” |
device unit | .device | 定义实现内核识别设备 |
mount unit | .mount | 定义文件系统挂载点,利用logind服务,为用户会话进程分配CGroup资源 |
socket unit | .socket | 定义表示进程间通信的socket文件 |
snapshot unit | .snapshot | 管理系统快照 |
swap unit | .swap | 表示swap设备 |
automount unit | .automount | 文件系统自动挂载设备 |
path unit | .path | 定义文件系统中 |
timer unit | .timer | 定时器配置单元,用来定义触发用户定义的操作,取代了atd,crond等传统定时服务 |
Target和运行级别对应关系
Sys V init | Systemd target | 说明 |
---|---|---|
0 | poweroff.target | 关机 |
1,s,single | rescue.target | 单用户 |
2,4 | multi-user.target | 用户定义,默认等同于3 |
3 | multi-user.target | 多用户,非图形化 |
5 | graphical.target | 多用户,图形模式 |
6 | reboot.target | 重启 |
emergency | emergency.target | 紧急shell |
Systemd并发启动原理
- 解决socket依赖
绝大多数的服务依赖是套接字依赖。比如服务 A 通过一个套接字端口 S1 提供自己的服务,其他的服务如果需要服务 A,则需要连接 S1。因此如果服务 A 尚未启动,S1 就不存在,其他的服务就会得到启动错误。所以传统地,人们需要先启动服务 A,等待它进入就绪状态,再启动其他需要它的服务。Systemd 认为,只要我们预先把 S1 建立好,那么其他所有的服务就可以同时启动而无需等待服务 A 来创建 S1 了。如果服务 A 尚未启动,那么其他进程向 S1 发送的服务请求实际上会被 Linux 操作系统缓存,其他进程会在这个请求的地方等待。一旦服务 A 启动就绪,就可以立即处理缓存的请求,一切都开始正常运行。
那么服务如何使用由 init 进程创建的套接字呢?Linux 操作系统有一个特性,当进程调用 fork 或者 exec 创建子进程之后,所有在父进程中被打开的文件句柄 (file descriptor) 都被子进程所继承。套接字也是一种文件句柄,进程 A 可以创建一个套接字,此后当进程 A 调用 exec 启动一个新的子进程时,只要确保该套接字的 close_on_exec 标志位被清空,那么新的子进程就可以继承这个套接字。子进程看到的套接字和父进程创建的套接字是同一个系统套接字,就仿佛这个套接字是子进程自己创建的一样,没有任何区别。
这个特性以前被一个叫做 inetd 的系统服务所利用。Inetd 进程会负责监控一些常用套接字端口,比如 Telnet,当该端口有连接请求时,inetd 才启动 telnetd 进程,并把有连接的套接字传递给新的 telnetd 进程进行处理。这样,当系统没有 telnet 客户端连接时,就不需要启动 telnetd 进程。Inetd 可以代理很多的网络服务,这样就可以节约很多的系统负载和内存资源,只有当有真正的连接请求时才启动相应服务,并把套接字传递给相应的服务进程。
和 inetd 类似,systemd 是所有其他进程的父进程,它可以先建立所有需要的套接字,然后在调用 exec 的时候将该套接字传递给新的服务进程,而新进程直接使用该套接字进行服务即可。
- 解决D-bus依赖
D-Bus 是 desktop-bus 的简称,是一个低延迟、低开销、高可用性的进程间通信机制。它越来越多地用于应用程序之间通信,也用于应用程序和操作系统内核之间的通信。很多现代的服务进程都使用D-Bus 取代套接字作为进程间通信机制,对外提供服务。比如简化 Linux 网络配置的 NetworkManager 服务就使用 D-Bus 和其他的应用程序或者服务进行交互:邮件客户端软件 evolution 可以通过 D-Bus 从 NetworkManager 服务获取网络状态的改变,以便做出相应的处理。
D-Bus 支持所谓”bus activation”功能。如果服务 A 需要使用服务 B 的 D-Bus 服务,而服务 B 并没有运行,则 D-Bus 可以在服务 A 请求服务 B 的 D-Bus 时自动启动服务 B。而服务 A 发出的请求会被 D-Bus 缓存,服务 A 会等待服务 B 启动就绪。利用这个特性,依赖 D-Bus 的服务就可以实现并行启动。
- 解决文件系统依赖
系统启动过程中,文件系统相关的活动是最耗时的,比如挂载文件系统,对文件系统进行磁盘检查(fsck),磁盘配额检查等都是非常耗时的操作。在等待这些工作完成的同时,系统处于空闲状态。那些想使用文件系统的服务似乎必须等待文件系统初始化完成才可以启动。但是 systemd 发现这种依赖也是可以避免的。
Systemd 参考了 autofs 的设计思路,使得依赖文件系统的服务和文件系统本身初始化两者可以并发工作。autofs 可以监测到某个文件系统挂载点真正被访问到的时候才触发挂载操作,这是通过内核 automounter 模块的支持而实现的。比如一个 open()系统调用作用在”/misc/cd/file1”的时候,/misc/cd 尚未执行挂载操作,此时 open()调用被挂起等待,Linux 内核通知 autofs,autofs 执行挂载。这时候,控制权返回给 open()系统调用,并正常打开文件。
Systemd 集成了 autofs 的实现,对于系统中的挂载点,比如/home,当系统启动的时候,systemd 为其创建一个临时的自动挂载点。在这个时刻/home 真正的挂载设备尚未启动好,真正的挂载操作还没有执行,文件系统检测也还没有完成。可是那些依赖该目录的进程已经可以并发启动,他们的 open()操作被内建在 systemd 中的 autofs 捕获,将该 open()调用挂起(可中断睡眠状态)。然后等待真正的挂载操作完成,文件系统检测也完成后,systemd 将该自动挂载点替换为真正的挂载点,并让 open()调用返回。由此,实现了那些依赖于文件系统的服务和文件系统本身同时并发启动。
当然对于”/“根目录的依赖实际上一定还是要串行执行,因为 systemd 自己也存放在/之下,必须等待系统根目录挂载检查好。
不过对于类似/home 等挂载点,这种并发可以提高系统的启动速度,尤其是当/home 是远程的 NFS 节点,或者是加密盘等,需要耗费较长的时间才可以准备就绪的情况下,因为并发启动,这段时间内,系统并不是完全无事可做,而是可以利用这段空余时间做更多的启动进程的事情,总的来说就缩短了系统启动时间
- 服务的循环依赖
Systemd 能保证事务完整性。Systemd 的事务概念和数据库中的有所不同,主要是为了保证多个依赖的配置单元之间没有环形引用,存在循环依赖,那么 systemd 将无法启动任意一个服务。此时 systemd 将会尝试解决这个问题,因为配置单元之间的依赖关系有两种:required 是强依赖;want 则是弱依赖,systemd 将去掉 wants 关键字指定的依赖看看是否能打破循环。如果无法修复,systemd 会报错。Systemd 能够自动检测和修复这类配置错误,极大地减轻了管理员的排错负担
- 基于path激活机制
判断一个文件在不在, 如果在可以立即激活一个进程或服务
System V init 和 Systemd命令对比
Sys V init | Systemd | 作用 |
---|---|---|
service NAME start | systemtl start NAME.service | 启动 |
service NAME stop | systemctl stop NAME.service | 停止 |
service NAME restart | systemctl restart NAME.service | 重启 |
service NAME status | systemtl status NAME.service | 查看状态 |
service NAME condrestart | systemctl condrestart NAME.service | 条件重启 |
service NAME reload | systemctl reload NAME.service | 重载 |
不支持 | systemctl is-active NAME.service | 查看服务当前激活状态 |
chkconfig –list | systemctl list-units -t service | 查看所有已激活服务 |
不支持 | systemctl list-units -t service -a | 查看所有服务(包括未激活) |
chkconfig NAME on | systemctl enable NAME.service | 设置开机启动 |
chkconfig NAME off | systemctl disable NAME.service | 禁止开机启动 |
chkconfig –list NAME | systemctl is-enabled NAME.service | 查看服务是否开机启动 |
不支持 | systemctl mask NAME.service | 禁止服务设置开机启动 |
不支持 | systemctl unmask NAME.service | 取消禁止服务设置开机启动 |
不支持 | systemctl list-dependencies NAME.service | 查看服务依赖关系 |
修改/etc/inittab文件 | systemctl set-default NAME.target | 修改默认运行级别 |
init RUNLEVEL | systemctl isolate NAME.target | 切换系统运行级别 |
runlevel,who -r | systemctl get-default | 查看运行级别 |
Systemd 电源管理
命令 | 操作 |
---|---|
systemctl reboot | 重启 |
systemctl poweroff | 关机 |
systemctl suspend | 挂起 |
systemctl hibernate | 休眠 |
systemctl hybrid | 混合休眠模块(快照并挂起) |
Service unit file配置说明
Systemd的相关配置文件路径
/etc/lib/systemd/system
/run/systemd/system
/etc/systemd/system
文件通常由三个部分组成
[Unit]
: 定义与unit类型无关的通用选项,用于提供当前unit描述信息,unit行为以及依赖关系等
[Service]
:定义与此处类型相关的专用选项,此类为service类型
[Install]
:定义由systemctl enable
或者systemtl disable
命令在实现服务启动或禁止用到的选项
Unit 常用选项
Description
: 描述信。
After
: 定义unit的启动次数,表示当前unit应该晚于哪些unit启动,功能和before相反
Requies
: 依赖到其他units,强依赖,被依赖的units无法激活时,当前unit即无法激活
Wants
: 指明依赖到其他的units,弱依赖,依赖的units无法激活时,当前unit无影响
Conflicts
: 定义units见的冲突关系
Service 常用选项
Type
: 用于定义影execstart
及相关参数的功能的unit进程启动
Simple
: 由execstart
启动的命令为主进程
Forking
: 由execstart
启动的命令,其中一个子进程会成为主进程,父进程会退出
onehot
: 功能类似simple dubs
notify
: 类似于simple idle
environmentfile
: 启动时环境配置文件,为execstart
提供变量
execstart
: 指明启动unit要运行的命令或脚本,execstartpre
,execstartpost
execreload
: 指明重载unit要运行的命令或脚本
execstop
: 指明要停止units要运行的命令或脚本
restart
: 指明要重启units要运行命令或脚本
Install 常用选项
alias
:
requireby
: 被那些units所依赖,强依赖
wantsby
: 被那些units所依赖,弱依赖
注意:
当对新创建的unit文件或修改了unit文件,都需要重载配置文件
systemctl daemon-reload