Linux的三种Init机制
一台安装了Linux操作系统的计算机的启动,从BIOS加电开始,随后进入Bootloader,由Bootloader加载Linux内核并初始化。
内核本身不能做任何有意义的事情,内核初始化的最后一步,就是创建PID为1、名为init的守护进程,该进程是操作系统中所有用户进程的祖先——所有用户进程都由它创建。
所谓Init机制,就是确定了init进程行为的一套规范及其实现(此实现通常就反应在/sbin/init上)。Linux发展到今天,依次出现了sysvinit、upstart 、systemd三种广泛应用的Init机制。
sysvinit就是System V风格的初始化机制,它来源于System V风格的UNIX。绝大部分Linux发行版均兼容sysvinit。
sysvinit引入了运行级别(runlevel )的概念,不同模式下需要进行的系统初始化工作不一样。各发行版对运行级别的定义各有不同,但是通常:0表示关机、1表示单用户模式、6表示重启。
sysvinit正常启动(单用户或者GUI模式)系统时,大致执行的工作流如下:
- 读取/etc/inittab获得需要进入的运行级别
- 依次执行:
- 执行rc.sysinit脚本,完成一些重要的系统初始化工作,例如:设置系统时钟、设置定义在/etc/sysctl.conf中的内核参数、启用swap、挂载文件系统等
- 执行rc*.d中的脚本,其中*为运行级别。被执行的脚本均以S开头,S后面的数字表示其被执行的顺序
- 执行rc.local脚本
- 如果需要,启动X显示管理器
sysvinit正常关闭(关机或者重启)系统时,则以数字顺序,执行rc*.d中的K开头的脚本。
sysvinit在启动、关闭时均是串行的执行各脚本的,这一方面让问题排查简单,一方面则导致运行速度缓慢。
很多Linux发行版为sysvinit配套了管理工具,例如service、chkconfig等。
参考Linux运行级别和启动顺序了解sysvinit的更多知识。
从2.6版本的内核开始,即插即用被良好的支持,Linux被越来越多的用在个人计算机领域,这让sysvinit面临两个问题:
- 串行化、阻塞的初始化过程导致启动消耗的时间长,而个人计算机却经常性的重启
- 一次性启动所有系统服务浪费很多资源,而即插即用要求按需启动、卸载服务
早期的Ubuntu版本(6.10+)即启用了upstart这一init机制,以解决上述问题。
upstart是一个事件驱动的init机制,例如,当U盘插入到接口时,内核会通知upstart,后者会进行挂载操作。upstart的优势包括:
- 能够很好的实现即插即用——当新硬件被发现时动态启动服务,硬件被拔出时停止服务
- 提高系统启动速度,一方面减少启动时的工作量,一方面事件驱动改变了串行话的执行方式
- 执行内部的初始化
- 触发一个名为 startup 的特殊事件,该事件会触发系统剩余部分的初始化
- init进程触发定义在/etc/init/mountall.conf的任务mountall,这是因为mountall声明了 start on startup
- mountall进一步触发若干事件:local-filesystem、virtual-filesystem、all-swaps
- local-filesystem导致udev的执行,后者导致upstart-udev-bridge的执行
- local-filesystem发布filesystem事件、upstart-udev-bridge发布net-device-up IFACE=lo,这两个事件导致rc-sysinit的执行
- rc-sysinit导致telinit的执行,后者发布runlevel事件
- runlevel事件导致rc的执行,rc负责读取/etc/rc*.d中的脚本并执行
很多现有的服务已经基于System V风格的Init脚本来编写,Upstart必须兼任它。尽管Upstart完全是基于任务+事件的,但是它模拟了对运行级别的支持。
修改/etc/init/rc-sysinti.conf中的DEFAULT_RUNLEVEL可以改变默认的“运行级别”。在启动过程中,rc任务会根据设置的运行级别,访问系统中的sysvinit脚本。
Job就是一个明确定义的工作单元,它可以启动一个系统服务,或者运行一个配置命令。每个Job都会等待一个或者多个事件,一旦事件发生,upstart就会触发Job完成工作。
Job可以分为三种类型:
Job类型 | 说明 |
task job | 表示一个在有限时间内会完成的任务,例如删除一个文件 |
service job |
表示后台服务进程,例如HTTP服务。这种任务通常不会停止,它启动后即由init进程管理,后者会在它崩溃后重新启动之 某些事件可以触发service job的终止,但是upstart也提供了手工管理的工具 |
abstract job | 和upstart内部运作机制有关。尽管没有脚本段(Sections)或者exec节(stanzas),这种任务仍然可以启动,但是它没有对应的子进程(PID),除非被管理员停止,这种任务会一直运行 |
根据Job的作用范围,可以将其分为系统级服务、会话级服务。后者仅为某个用户服务。
在Job的生命周期中,它可以处于以下状态之一:
状态 | 说明 |
Waiting | 初始状态 |
Starting | 即将开始任务。此状态触发starting事件 |
Pre-start | 任务启动前应该完成的准备工作 |
Spawned | 准备执行 script 或者 exec 段 |
Post-start | 任务启动完毕后执行的后置工作。此状态触发started事件 |
Running | post-start之后的一个临时状态 |
Pre-stop | 任务停止前应该完成的准备工作 |
Stopping | pre-stop之后的一个临时状态。此状态触发stopping事件 |
Killed | 任务即将被停止 |
Post-stop | 任务停止后的清理工作。此状态触发stopped事件 |
事件是一个通知,由Upstart发送给所有订阅者(任务或者其它事件),管理员随时可以手工的触发事件:
1 |
initctl emit myevent |
一个Job的启动/停止行为,可能是其它Job启动/停止行为的结果,这是依靠事件来驱动的。Upstart提供了一系列特殊事件(starting、started、stopping、stopped),用来广播Job的状态转换。尽管某些Job状态的名称与事件名称相同,但是它们不是一回事。
根据产生、消费方式的不同,事件分为:
事件分类 | 说明 | ||
Signals |
非阻塞的异步事件,发出信号后调用者立即返回,调用者不关心谁关注此信号,手工触发信号的方式如下:
|
||
Methods | 阻塞的同步事件,这类事件的行为类似于编程语言中的方法/函数调用,调用者必须等待事件的订阅者处理完毕。手工触发时,initctl执行后的$?反应订阅者的处理结果 | ||
Hooks |
介于信号和方法之间:
钩子的典型例子是starting、stopping这两个Job事件 |
系统中预定义的事件取决于发行版,可以执行 man 7 upstart-events 查看之。
Job需要进行的工作由一个配置文件描述,该文件位于/etc/init目录中,以.conf结尾。
配置文件由多个小节(stanza) 构成,小节定义Job某个方面的特性:
类 | 小节 | 说明 | |||||||
文 档 |
author | 定义任务的作者 | |||||||
description | 定义任务的描述 | ||||||||
进 程 控 制 |
expect |
Upstart基于进程PID来跟踪Job,如果不明确指定,Upstart使用它执行exec/script小节时产生的第一个进程的PID 但是,大部分作为系统服务的Job都会守护进程化(daemonize),这些Job会再一次的fork出子进程,以确保守护程序与Job的初始进程没有关联性 Upstart本身无法知晓任务是否fork两次,这就需要使用expect来声明:
此外, expect stop 期望Job的主进程触发一个SIGSTOP信号来指示它已经准备好,Upstart会等待此信号,然后发送SIGCONT信号给Job主进程提示它继续,并执行任务的post-start脚本 |
|||||||
kill signal |
设置Upstart发送何种信号以停止Job,默认SIGTERM,示例:
|
||||||||
kill timeout |
设置Upstart强制杀死Job进程前等待的秒数,默认5 |
||||||||
reload signal |
设置任务需要被reload时,Upstart发送的信号,默认SIGHUP |
||||||||
进 程 定 义 |
exec |
指定Job的主逻辑。定义一个需要执行的单行的命令,如果命令行中包含任何Shell元字符,会直接传递给Shell,以确保Shell重定向、变量展开等功能的正常。语法格式:
|
|||||||
pre-start |
定义Job进入Pre-start状态时需要执行的逻辑,例如清空临时/缓存目录,最好不要包含耗时的逻辑。语法格式:
|
||||||||
post-start |
定义Job主线程产生之后(spawned)需要执行的逻辑,此时Job的started事件尚未发布。语法格式类似pre-start 可以使用该小节来实现这样的功能:让Job进入started状态前,需要一个延迟或者达到某个条件。例如MySQL,在接受网络流量之前,它需要先执行Recovery操作 |
||||||||
pre-stop |
定义Job主线程被杀死(SIGTERM)之前、stopping事件被发布之前需要执行的逻辑。语法格式类似pre-start 停止Job时Upstart发送SIGTERM信号,如果超过Kill timeout(默认5秒)Job尚未退出,Upstart会发送SIGKILL,这可能导致数据丢失(例如磁盘操作)。可以把敏感操作转移到pre-stop中以避免此情况 你也可以使用该小节来取消服务的停止 |
||||||||
post-stop | 在Job进程被杀死后,执行清理工作 | ||||||||
script | 指定Job的主逻辑。定义一个多行的脚本,以 end script 标注脚本块的结束 | ||||||||
事 件 定 义 |
manual | 提示Upstart,忽略start on和stop on小节,总是手工的启动、停止 | |||||||
start on |
该小节定义导致Job启动的一系列事件,语法格式:
该小节的内容必须编写在单行中 如果要 在系统的基础设施准备完毕后启动Job,参考下面的例子:
如果要在一个服务启动之后,启动Job,使用:
反之,如果要在一个服务启动之前,启动Job,使用:
|
||||||||
stop on |
该小节定义一系列导致Job停止的事件(如果Job正在运行),语法格式:
用法类似于start on,示例:
|
||||||||
任 务 环境 |
env | 设置一个所有小节都能看到的环境变量,语法格式: env KEY[=VALUE] | |||||||
export | 导出Job中通过env小节设置的所有环境变量到该Job触发的全部事件中去 | ||||||||
其 它 |
normal exit |
指定那些退出码(exit status)表示正常退出,默认0。示例:
|
|||||||
respawn |
在不指定此小节的情况下,静默退出的Job进入 stop/waiting 状态 使用此小节的情况下,一旦Job从它的主逻辑exec/script退出,Upstart都会重新启动它,一并执行定义的pre-start,、post-start、 post-stop小节(pre-stop 不被执行) 使用此特性的例子包括各种网络服务,例如MySQL,只要不是管理员有意的停止它,都应该一直运行 |
||||||||
respawn limit |
限制重新启动的次数,语法格式:
|
||||||||
instance | 声明Job的实例名,有时你需要运行一个Job的多个实例 | ||||||||
task |
从概念上说,task是一种短暂运行完毕的Job 使用此关键字后,导致此Job启动的事件将被阻塞,直到当前Job转换到stopped状态 |
当开发自己的Job时,需要注意一下内容:
- fork的次数需要声明:很多Linux后台服务基于Double fork实现守护进程,这必须在Job配置文件中声明
- fork后即可用:你必须保证在Double fork后,服务立刻进入可用状态,因为Upstart基于fork计数判断服务是否就绪
- 遵循SIGHUP要求:Upstart发送SIGHUP信号给Job后,期望Job的行为满足以下要求:
- 完成必要的重现初始化工作,例如重新读取配置文件
- Job必须使用现有的PID,即Job不能通过fork来实现reload
- 遵循SIGTEM要求:Job收到此信号后,应当立即停止,并释放所有资源
这里我们讲解一下MySQL的Job定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# 任务的描述信息 description "MySQL Server" author "Mario Limonciello <superm1@ubuntu.com>" # 在2345运行级别启动服务 start on runlevel [2345] # 在rc服务启动之前,如果目标运行级别是016则停止服务 stop on starting rc RUNLEVEL=[016] # 当任务以外退出时重启,最多重启2次,间隔5秒 respawn respawn limit 2 5 # 设置整个文件内可见的变量 env HOME=/etc/mysql # 设置进程的umask umask 007 # 延长强杀超时,因为MySQL刷出缓冲需要时间 kill timeout 300 pre-start script # 在启动前的准备工作,普通Shell脚本,略 # 访问变量的语法与Bash相同 $HOME end script # 主逻辑脚本 exec /usr/sbin/mysqld post-start script # 启动后的清理工作 end script |
和Upstart相关的命令包括:initctl,关于此命令的细节请参考Linux命令知识集锦。
Systemd是主流Init系统中最新的一个,是Upstart的竞争者。值得注意的是,作为Upstart起源的Ubuntu从15.04版本开始,也改用了Systemd。
Systemd具有以下特点:
- 兼容SysVinit或者 LSB initscripts
- 提供比Upstart更加激进的并行启动能力,通过socket / D-Bus activation等技术启动服务,速度更快
- 提供按需启动的能力
- 利用cgroup跟踪进程的生命周期,比Upstart基于PID的方式更加高效。在fork时,子进程会遍历父进程的cgroup,Systemd通过遍历cgroup可以把服务相关的所有进程都正确的停止掉
- 内建挂载服务,以便自动挂载U盘、光盘,不再需要安装autofs服务
- 事务性的依赖管理,Systemd在最大程度上保证并发启动的同时,确保那些有明确依赖顺序的服务(例如NFS依赖网络),按照顺序启动
- 内建日志服务journald,可以代替syslog,克服了syslog的一系列缺点
Systemd把系统启动过程中的每一项工作,例如:
- 启动后台服务,例如SSHD
- 执行系统配置,例如挂载文件系统
等都抽象为一个配置单元,它们的角色类似于Upstart的Job。配置单元被划分为不同的类型:
配置单元类型 | 说明 |
service | 封装一个后台服务,例如MySQL,最常用 |
socket | 封装系统中的一个套接字,每个socket单元都有一个相应的service单元,当第一个连接到达时,与socket关联的service就会启动 |
device | 封装一个存在于Linux设备树中的设备,每个使用udev规则标记的设备都会在Systemd中作为一个device单元。例如/dev/sda2会自动被转换为dev-sda2.device单元 |
mount |
封装文件系统中的一个挂载点,Systemd会对这个挂载点进行监控管理,例如在系统启动时执行挂载,在某些情况下卸载 |
automount | 封装文件系统中需要自动挂载的挂载点,每个automount单元都对应一个mount单元,当自动挂载点被访问时,它被自动的挂载 |
swap | 管理交换分区 |
target | 对其它配置单元进行逻辑分组,这样可以很方便的实现运行级别,例如multi-user.target相当于sysvinit中的运行级别5,分组中的所有配置单元都被执行 |
每个配置单元都有一个对应的配置文件,例如MySQL服务对应mysql.service。配置文件的语法比起System V脚本要简单的多。
Systemd尽量的保证了并发启动,但是某些任务之间存在天然的依赖关系,不能通过套接字激活(socket activation)、D-Bus激活、autofs这三种方法来解除依赖。为了满足先后启动的需要,Systemd允许在配置文件中声明单元的依赖关系。
要声明一个强依赖,可以使用 A require B 的语法,声明弱依赖则使用 A want B 。如果服务之间存在依赖循环,则先去掉弱依赖,如果循环仍然存在,则Systemd会报错。
前面我们提到过target这类特殊的配置单元,用于分组其它配置单元。利用Target把sysvinit某个运行级别中需要启动的所有服务归纳到同一个组中,可以很方便的模拟运行级别。Systemd预定义了运行级别和Target之间的映射:
运行级别 | 运行级别含义 | Systemd targets |
0 | 关机 | runlevel0.target, poweroff.target |
1,s,single | 单用户模式 | runlevel1.target, rescue.target |
2,4 | 用户定义的运行级别 | runlevel2.target, runlevel4.target, multi-user.target |
3 | 多用户模式,非图形化 | runlevel3.target, multi-user.target |
5 | 多用户模式,图形化 | runlevel5.target, graphical.target |
6 | 重现启动 | runlevel6.target, reboot.target |
emergency | 紧急Shell | emergency.target |
Systemd通过并行加速启动过程,要提高并行度,必须解除服务之前的依赖关系。Systemd通过三种手段来解除依赖,这三种手段都基于延迟(Lazy)启动的思想。
绝大部分服务之间的依赖是套接字依赖——被依赖者(A)的套接字(S)必须准备好监听,依赖者(B)才能启动。传统的Init机制都是启动A,然后再启动B。
Systemd的想法是,直接启动S,当B第一次向S发起连接时,延迟的启动A。能让服务A的套接字先于A本身启动,需要依靠内核的一个特性:子进程可以继承父进程的文件描述符(文件句柄,file descriptor) ,而套接字是一种文件句柄。因此先于A启动S的流程可以如下:
- 由init进程创建套接字S,清空其close_on_exec位,以便允许它被子进程继承
- 当B向S发起连接时,由init进程fork/exec出A的主进程
- A的主进程自动获得套接字S
其实这种内核特性以前就被使用过,例如inetd。
所谓D-Bus即桌面总线(Desktop-bus),是一种低延迟、低开销、高可用的进程间通信机制。它被越来越多的用于应用程序之间的通信,以及应用程序与内核的通信。例如NetworkManager就使用D-Bus和其它应用程序交互。
D-Bus支持所谓Bus activation特性:如果A依赖于B的D-Bus服务,而B尚未运行,则D-Bus可以在A第一次请求B的时候,启动B。在B启动完毕之前A会一直等待。依赖于D-Bus的这一特性,也可以实现并行启动。
文件系统相关的活动是相当耗时的,因为与之相关的配置单元而导致串行化执行,会大大的拖累系统启动速度。
Autofs是一个实现按需挂载的软件,它利用了内核的automounter模块:当open()系统调用作用在/mnt/dsk/file1时,/mnt/dsk可能尚未挂载,此时内核将让open()调用挂起,并通知Autofs执行挂载,完毕后把控制权返回给open()调用。
Systemd在内部集成了Autofs的实现。对于系统中的挂载点它会在启动时创建一个临时的Mock,这样,依赖于此挂载点的服务就可以立即启动。当文件系统相关的配置单元都启动成功后,Systemd再把那些Mock替换为真正的挂载点。如果在文件系统尚未准备好时,Mock就被访问,Systemd则让访问者陷入等待。
Systemd允许用户管理自己控制下的服务 —— 启动、停止、禁用自己的配置单元,每个用户都具有自己Systemd实例。
用户级单元可以放在/usr/lib/systemd/user、 ~/.config/systemd/user等位置。
使用--user参数,systemctl管理当前用户的systemd单元:
1 |
systemctl --user enable myapp.service |
配置单元同样由文件文件来定义,命名格式为 unitName.unitType (后缀为此配置单元的类型),存放路径取决于具体发行版,例如/etc/systemd/system,Ubuntu则存放在/lib/systemd/system目录中。
配置单元由一系列的段组成,格式类似于Windows下的INI文件。
Unit段:基础元数据和依赖关系
配置项 | 说明 | ||
Description | 对单元的描述 | ||
Documentation | 文档URL | ||
Requires | 当前单元所强依赖的其它单元,多个单元用空白分隔 ,如果依赖没法满足,该单元无法启动 | ||
Wants | 当前单元所弱依赖于的其它单元 | ||
BindsTo | 类似于Requires,如果所依赖的单元退出,则当前单元也退出 | ||
Before | 限定当前单元必须在指定的单元之前启动 | ||
After | 限定当前单元必须在指定的单元之后启动 | ||
Conflicts | 指定不能和当前单元同时运行的单元 | ||
Condition*** |
限定当前单元运行必须满足的条件(如果不满足则不运行),例如:
|
||
Assert*** | 限定当前单元运行必须满足的条件(如果不满足则报启动失败) |
Service段:专用于服务类单元的配置
配置项 | 说明 | ||
Type | 定义启动时的进程行为,有效值: simple 默认值,执行ExecStart指定的命令,启动服务主进程 forking 以fork调用创建子进程,并退出父进程 oneshot 一次性服务,Systemd会等待该服务退出,然后继续init过程 dbus 当前服务通过D-Bus启动 notify 当服务启动完毕,通知Systemd继续 idle 当其它单元都启动完毕后,才执行此单元 |
||
ExecStart |
启动服务的命令行 默认情况下,这些生命周期钩子执行失败会终止后续流程。要改变此逻辑,使用 =-操作符:
这样,即使 ExecStartPre 执行失败,后续的ExecStart仍然会执行 |
||
ExecStartPre |
启动服务的前置命令行 这些生命周期钩子可以指定多次,前面的先执行 |
||
ExecStartPost | 启动服务的后置命令行 | ||
ExecReload | 重新加载服务时执行的命令行 | ||
ExecStop | 停止服务时执行的命令行 | ||
ExecStopPost | 停止服务的后置命令行 | ||
RestartSec | 重启的间隔秒数 | ||
Restart |
重启服务的时机,有效值: no 不重启 干净退出:退出码为0,或者因为SIGHUP, SIGINT, SIGTERM or SIGPIPE退出。或者定义在 SuccessExitStatus中的其它退出码、信号 |
||
TimeoutSec | 启动超时的秒数,超过这个时间还未启动完毕,终止服务 | ||
Environment | 用于指定环境变量 |
Install段:定义如何启动单元,是否开机启动
配置项 | 说明 |
WantedBy |
指定一个或者多个目标(Target)对当前单元具有弱依赖,当systemctl enable当前单元时,会把当前单元的符号连接放到/etc/systemd/system下的Target名称.wants目录中 该名称项可用于模拟sysvinit的初始化方式 |
RequiredBy | 指定一个或者多个目标(Target)对当前单元具有强依赖 |
Alias | 调用systemctl时可以使用的当前单元的别名 |
Also | 指定当前单元被systemctl enable时,会同时激活的其它单元 |
- 后台服务不再需要使用Double fork来产生守护进程
- 不要调用 setsid(),应该由Systemd处理
- 不需要维护PID文件
- Systemd提供了日志功能,服务进程只需要:
- 输出到stderr即可,取决于配置
- 打开systemd-cat管道并输出,例如 echo "message" | systemd-cat
- 可以在服务脚本中调用log_daemon_msg、log_failure_msg、log_progress_msg、log_end_msg等LSB函数
- 正确处理SIGTERM信号,立即停止当前服务
- 正确处理SIGHUP信号,重新启动服务
- 需要套接字的服务,不要自己创建,让Systemd传入
- 调用 sd_notify()函数告知Systemd服务本身状态的改变。当服务初始化完毕后进入就绪状态时,即可调用之
Systemd支持模板化的Unit文件,用于从单个配置文件启动多个单元。调用模板化Unit时,需要使用特殊的@符号: <service_name>@<argument>.service。
其中argument是传递给模板的字符串,在模板文件中可以这样访问:
- %i:进行转义的参数
- %I:原样传递的参数
Ceph Mon就是这样的模板Unit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
[Unit] Description=Ceph cluster monitor daemon After=network-online.target local-fs.target time-sync.target Wants=network-online.target local-fs.target time-sync.target PartOf=ceph-mon.target [Service] LimitNOFILE=1048576 LimitNPROC=1048576 EnvironmentFile=-/etc/default/ceph Environment=CLUSTER=ceph ; 访问参数 ExecStart=/usr/bin/ceph-mon -f --cluster ${CLUSTER} --id %i --setuser ceph --setgroup ceph ExecReload=/bin/kill -HUP $MAINPID PrivateDevices=yes ProtectHome=true ProtectSystem=full PrivateTmp=true TasksMax=infinity Restart=on-failure StartLimitInterval=30min StartLimitBurst=5 RestartSec=10 [Install] WantedBy=ceph-mon.target |
在目录下有指向上述模板的符号链接,参数就是通过符号链接的名字传递的:
1 2 3 4 |
cd /etc/systemd/system# ls ceph-mon.target.wants/ -la # ceph-mon@xenon.service -> /lib/systemd/system/ceph-mon@.service # ceph-mon@Xenon.service -> /lib/systemd/system/ceph-mon@.service |
这里我们讲解一下MySQL的Unit定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# MySQL systemd service file [Unit] # 配置单元描述 Description=MySQL Community Server # 声明依赖 After=network.target [Install] # 该服务在多用户模式下启动 WantedBy=multi-user.target [Service] User=mysql Group=mysql PermissionsStartOnly=true # 启动服务之前需要运行的命令 ExecStartPre=/usr/share/mysql/mysql-systemd-start pre # 启动服务主程序 ExecStart=/usr/sbin/mysqld # 启动服务之后需要运行的命令 ExecStartPost=/usr/share/mysql/mysql-systemd-start post TimeoutSec=600 Restart=on-failure RuntimeDirectory=mysqld RuntimeDirectoryMode=755 |
我们再看看multi-user.target这个配置单元组的定义:
1 2 3 4 5 6 7 8 9 10 11 12 |
[Unit] Description=Multi-User System Documentation=man:systemd.special(7) # 该组被启动之前basic组必须被启动,同样的,basic停止前该组必须先停止 Requires=basic.target Conflicts=rescue.service rescue.target After=basic.target rescue.service rescue.target AllowIsolate=yes [Install] # 为本单元组定义别名,调用systemctl命令时可以使用别名 Alias=default.target |
和Systemd相关的命令包括:systemctl、journal、systemd-analyze、hostnamectl、localectl、timedatectl、loginctl等,关于此命令的细节请参考Linux命令知识集锦。
Leave a Reply