再一次讲到Linux系统启动流程:
POST --> Boot Sequence --> Bootloader(grub) --> kernel + initramfs(initrd) --> rootfs --> /sbin/init
对于init,即系统内核加载完毕后(加载kernel和切换根文件系统)运行的第一个用户空间进程,是管理用户空间的首进程,其他用户进程都是直接或间接由此进程创建的。init 进程以守护进程的方式存在,负责组织与运行系统的相关初始化工作,让系统进入定义好的运行模式。对于不同的CentOS版本,init也不同:
CentOS 5:SysV init
CentOS 6:Upstart
CentOS 7:systemd
CentOS5和6的init虽然不同,但进程名都是init,CentOS7则改成了systemd
对于SysVinit,执行的流程是:
内核加载完毕后,执行第一个进程init,即/sbin/init, PID 为1。
启动init程序后,init进程首先读取/etc/inittab文件,分析文件内容,获得以下的配置信息:
▲ 系统需要进入的运行级别(runlevel)
▲ 捕获组合键的定义
▲ 定义电源fail/restore脚本
▲ 启动getty和虚拟控制台
获取系统运行级别之后,根据运行级别顺序的执行以下位置的启动脚本,从而将系统初始化为预设的运行级别:
▲ /etc/rc.d/rc.sysinit 重要的系统初始化服务
▲ /etc/rc.d/rc 和 /etc/rc.d/rcX.d ,X为运行级别
▲ /etc/rc.d/rc.local 用户个性化服务
这样,整个系统就启动起来了。
/etc/rc.d/rc.sysinit执行一些重要的系统初始化任务:
- 激活 udev 和 selinux
- 设置定义在/etc/sysctl.conf 中的内核参数
- 设置系统时钟
- 加载 keymaps
- 使能交换分区
- 设置主机名(hostname)
- 根分区检查和 remount
- 激活 RAID 和 LVM 设备
- 开启磁盘配额
- 检查并挂载所有文件系统
- 清除过期的 locks 和 PID 文件
/etc/rc.d/rc脚本文件,就是确定当前运行级别,然后遍历/etc/rc.d/rcX.d/目录下的文件(X是运行级别,如3级别,就是rc3.d),这些文件名字是有规律的,都是以K或S开头,后跟两位数字,在后面是脚本名字,并且,这些文件都是软链接,链接到/etc/init.d/下对应的脚本文件。如下:
K##*,##运行次序,越小越先运行,K是Kill要关闭的,数字越小的服务,通常为依赖到别的服务。
S##*,S是Start,要启动的,数字越小越先运行,数字小的服务,通常为被依赖到的服务。
为什么要使用软链接?因为rc0.d到rc6.d中都可能要运行这些脚本,每个目录下有一份拷贝,维护和存储都有问题,使用软链接,统一且空间占用少。
SysVinit是串行启动,即前一个脚本运行完,才能运行后面的,而不管之间是否存在依赖关系,速度慢,一个进程启动失败,容易导致其后所有进程无法启动,对于如文件系统挂载等不能很好的处理,如usb即插即用支持不好。
对于Upstart:
针对SysVinit的缺点,Ubuntu 开发人员重新设计和开发一个全新的 init 系统,即 UpStart。UpStart 基于事件机制,比如 U 盘插入 USB 接口后,udev 得到内核通知,发现该设备,这就是一个新的事件。UpStart 在感知到该事件之后触发相应的等待任务,比如处理/etc/fstab 中存在的挂载点。采用这种事件驱动的模式,upstart 完美地解决了即插即用设备带来的新问题。
此外,采用事件驱动机制也带来了一些其它有益的变化,比如加快了系统启动时间。sysvinit 运行时是同步阻塞的。一个脚本运行的时候,后续脚本必须等待。这意味着所有的初始化步骤都是串行执行的,而实际上很多服务彼此并不相关,完全可以并行启动,从而减小系统的启动时间。
Upstart 的特点:UpStart 解决了之前提到的 sysvinit 的缺点。采用事件驱动模型,UpStart 可以:
更快地启动系统
当新硬件被发现时动态启动服务(PnP,英文 Plug-and-Play,译文为即插即用)
硬件被拔除时动态停止服务
Upstart 概念和术语
UpStart 主要的概念是 job 和 event。
(一)Job
Job是一个工作的单元(unit),一个任务(task)或者一个服务(service)。每个 Job 都等待一个或多个事件,一旦事件发生,UpStart 就触发该 Job 完成相应的工作。可以理解为 sysvinit 中的一个服务脚本。有三种类型的工作:
● task job;
● service job;
● abstract job;
task job 代表在一定时间内会执行完毕的任务,比如删除一个文件;
service job 代表后台服务进程,比如 apache httpd。这里进程一般不会退出,一旦开始运行就成为一个后台精灵进程,由 init 进程管理,如果这类进程退出,由 init 进程重新启动,它们只能由 init 进程发送信号停止。它们的停止一般也是由于所依赖的停止事件而触发的,不过 upstart 也提供命令行工具,让管理人员手动停止某个服务;
Abstract job 仅由 upstart 内部使用。
init daemon会监测每个服务的状态,如果服务出现问题会重启服务,在某些事件触发时或手工停止时会杀死服务。
Upstart init daemon只能监测那些使用exec运行的作业,无法监测使用script…end script运行的作业。也就是说,服务应该使用exec运行,而任务则可以使用任意的方法。
Upstart init守护进程读取/etc/init目录下的作业配置文件,并使用inotify来监控它们的改变。配置文件名必须以.conf结尾,可以放在/etc/init/下的子目录中。每个文件定义一个服务或作业,其名称按路径名来称呼。例如定义在/etc/init/rc-sysinit.conf中的作业就称为rc-sysinit,而定义在/etc/init/net/apache.conf的作业称为net/apache。这些文件必须是纯文本且不可执行的。
进程(Process): Process是由工作(jobs)定义的服务(Services)或者任务(Task),它将被init daemon运行。每个job可以定义一个或者多个不同的process,分别在其生命周期的不同状态运行。除抽象作业(Abstact Job)外的所有作业配置文件都必须要含有exec节(exec stanza)或者script节(script stanza)。它们指定这个工作运行什么文件。
除了以上的分类之外,还有另一种工作(Job)分类方法。Upstart 不仅可以用来为整个系统的初始化服务,也可以为每个用户会话(session)的初始化服务。系统的初始化任务就叫做 system job,比如挂载文件系统的任务就是一个 system job;用户会话的初始化服务就叫做 session job。
Job 生命周期
Upstart 为每个工作都维护一个生命周期。一般来说,工作有开始,运行和结束这几种状态。为了更精细地描述工作的变化,Upstart 还引入了一些其它的状态。比如开始就有开始之前(pre-start),即将开始(starting)和已经开始了(started)几种不同的状态,这样可以更加精确地描述工作的当前状态。
工作从某种初始状态开始,逐渐变化,或许要经历其它几种不同的状态,最终进入另外一种状态,形成一个状态机。在这个过程中,当工作的状态即将发生变化的时候,init 进程会发出相应的事件(event)。
Upstart 中 Job 的可能状态
状态名 : 含义
Waiting : 初始状态
Starting Job : 即将开始
pre-start : 执行 pre-start 段,即任务开始前应该完成的工作
Spawned : 准备执行 script 或者 exec 段
post-start : 执行 post-start 动作
Running : interim state set after post-start section processed denoting job is running (But it may have no associated PID!)
pre-stop : 执行 pre-stop 段
Stopping : interim state set after pre-stop section processed
Killed : 任务即将被停止
post-stop : 执行 post-stop 段
有四个状态会引起 init 进程发送相应的事件,表明该工作的相应变化:
Starting、Started、Stopping、Stopped,而其它的状态变化不会发出事件。
事件 Event
Event 就是一个事件。事件在 upstart 中以通知消息的形式具体存在。一旦某个事件发生了,Upstart 就向整个系统发送一个事件消息,依赖于该事件的程序就会开始运行。没有任何手段阻止事件消息被 upstart 的其它部分知晓,也就是说,事件一旦发生,整个 upstart 系统中所有工作和其它的事件都会得到通知。
Event 可以分为三类: signal,methods 或者 hooks。
常见事件:
系统上电启动,init 进程会发送"start"事件
根文件系统可写时,相应 job 会发送文件系统就绪的事件
一个块设备被发现并初始化完成,发送相应的事件
某个文件系统被挂载,发送相应的事件
类似 atd 和 cron,可以在某个时间点,或者周期的时间点发送事件
另外一个 job 开始或结束时,发送相应的事件
一个磁盘文件被修改时,可以发出相应的事件
一个网络设备被发现时,可以发出相应的事件
缺省路由被添加或删除时,可以发出相应的事件
Job 和 Event 的相互协作
Upstart 就是由事件触发工作运行的一个系统,每一个程序的运行都由其依赖的事件发生而触发的。
系统初始化的过程是在工作和事件的相互协作下完成的,可以大致描述如下:系统初始化时,init 进程开始运行,init 进程自身会发出不同的事件,这些最初的事件会触发一些工作运行。每个工作运行过程中会释放不同的事件,这些事件又将触发新的工作运行。如此反复,直到整个系统正常运行起来。
启动一个作业的流程
1)Upstart把作业的目标从stop改成start。正如目标的名字指示的一样,作业(实例)现在尝试启动。目标可以用initctl list和status命令显示。
2)Upstart触发starting事件,指示作业即将启动。这个事件包括两个环境变量:JOB指定作业名;INSTANCE指定实例名,如果启动单一的实例(没有instance配置节),则实例名为空。
3)starting事件完成。
4)如果pre-start节存在,则产生pre-start进程。如果pre-start失败,Upstart把目标从start改成stop,设置表示失败的变量并触发stopping和stopped事件。
5)Upstart产生主进程。即运行script或exec配置节,如果没有script或exec配置节,则Upstart什么也不做。
6)Upstart确定作业的最终PID,可参考expect fork和expect守护r进程。
7)如果post-start配置节存在,则产生post-start进程。。如果post-start失败,Upstart把目标从start改成stop,设置表示失败的变量并触发stopping和stopped事件。
8)Upstart触发started事件。这个事件包含与starting同样的环境变量。对Service job,当started事件完成后,主进程即完全地运行起来了。如果是Task job,则任务执行完成(成功或失败)。
终止一个作业的流程
1)Upstart把作业的目标从start改为stop。现在作业(实例)尝试终止。
2)如果pre-stop配置节存在,则产生pre-stop进程。如果pre-stop失败,Upstart设置表示失败的变量,并触发stopping和stopped事件。
3)如果作业有script或exec配置节,则终止主进程,首先向主进程发送SIGTERM信号,然后Upstart等待kill timeout秒数(默认为5秒),如果进程仍然在运行,则向进程发送SIGKILL信号,因为进程不能选择忽略此信号,因此能保证进程被终止。
4)Upstart触发stopping事件。这个事件有一系列的相关环境变量,包括:
JOB: 与本事件关联的作业名。
INSTANCE: 实例名。
RESULT: “ok”表示作业正常退出,”failed”表示作业失败,注意退出结果的显示可以用normal exit配置节修改。
PROCESS: 导致作业失败的配置节名称。如果RESULT=ok,则本变量不会被设置。如果设置了,可能值有pre-start, post-start, main(表示script或exec配置节), pre-stop, post-stop, respawn(表示作业产生次数超过了respawn limit配置节设置的限制)。
EXIT_STATUS或EXIT_SIGNAL: 如果作业自己退出则设置EXIT_STATUS,如果由于接收到信号退出则设置EXIT_SIGNAL。如果两个变量都没有设置,则进程在产生的过程中出现了问题(例如指定要运行的命令没有找到)。
5)如果post-stop配置节存在,则生成post-stop进程。如果post-start失败,Upstart设置表示失败的变量并触发stopped事件。
6)Upstart触发stopped事件。当stopped事件完成后,作业即完全终止。stopped事件与stopping事件有相同的环境变量集。
究竟哪些事件会触发某个工作的运行?这是由工作配置文件定义的。
1.工作配置文件
任何一个工作都是由一个工作配置文件(Job Configuration File)定义的。这个文件是一个文本文件,包含一个或者多个小节(stanza)。每个小节是一个完整的定义模块,定义了工作的一个方面,比如 author 小节定义了工作的作者。工作配置文件存放在/etc/init 下面,是以.conf 作为文件后缀的文件。
比较重要的小节有以下几个:
(1)“expect” Stanza
Upstart 除了负责系统的启动过程之外,和 SysVinit 一样,Upstart 还提供一系列的管理工具。当系统启动之后,管理员可能还需要进行维护和调整,比如启动或者停止某项系统服务。或者将系统切换到其它的工作状态,比如改变运行级别。
为了启动,停止,重启和查询某个系统服务。Upstart 需要跟踪该服务所对应的进程。比如 httpd 服务的进程 PID 为 1000。当用户需要查询 httpd 服务是否正常运行时,Upstart 就可以利用 ps 命令查询进程 1000,假如它还在正常运行,则表明服务正常。当用户需要停止 httpd 服务时,Upstart 就使用 kill 命令终止该进程。为此,Upstart 必须跟踪服务进程的进程号。
部分服务进程为了将自己变成后台守护进程(daemon),会采用两次派生(fork)的技术,另外一些服务则不会这样做。假如一个服务派生了两次,那么 UpStart 必须采用第二个派生出来的进程号作为服务的 PID。但是,UpStart 本身无法判断服务进程是否会派生两次,为此在定义该服务的工作配置文件中必须写明 expect 小节,告诉 UpStart 进程是否会派生两次。
Expect 有两种,"expect fork"表示进程只会 fork 一次;"expect daemonize"表示进程会 fork 两次。
(2)“exec” Stanza 和"script" Stanza
一个 UpStart 工作一定需要做些什么,可能是运行一条 shell 命令,或者运行一段脚本。用"exec"关键字配置工作需要运行的命令;用"script"关键字定义需要运行的脚本。
(3)“start on” Stanza 和"stop on" Stanza
"start on"定义了触发工作的所有事件。"start on"的语法很简单,如下所示:
start on EVENT [[KEY=]VALUE]… [and|or…]
EVENT 表示事件的名字,可以在 start on 中指定多个事件,表示该工作的开始需要依赖多个事件发生。多个事件之间可以用 and 或者 or 组合,"表示全部都必须发生"或者"其中之一发生即可"等不同的依赖条件。除了事件发生之外,工作的启动还可以依赖特定的条件,因此在 start on 的 EVENT 之后,可以用 KEY=VALUE 来表示额外的条件,一般是某个环境变量(KEY)和特定值(VALUE)进行比较。如果只有一个变量,或者变量的顺序已知,则 KEY 可以省略。
"stop on"和"start on"非常类似,只不过是定义工作在什么情况下需要停止。
systemd:
systemd是目前最流行的init程序,通过套接字激活的机制,让所有无论有无依赖关系的程序全部并行启动,并且仅按照系统启动的需要启动相应的服务,最大化提高开机启动速度。Systemd使用单个配置文件来管理所有服务,并提供了丰富的命令行工具来管理服务。Systemd还支持动态加载和卸载服务,可以在系统运行时添加或删除服务。
systemd架构:
systemd体系架构如下:
最底层:systemd 内核层面依赖 cgroup、autofs、kdbus
第二层:systemd libraries 是 systemd 依赖库
第三层:systemd Core 是 systemd 自己的库
第四层:systemd daemons 以及 targets 是自带的一些基本 unit、target,类似于 sysvinit 中自带的脚本
最上层:就是和 systemd 交互的一些工具,比如systemctl;
systemd 的守护进程主要分为系统态(system)与用户态(user)
PID为1的进程/sbin/init就是system态的systemd,是一个软链接,指向真实的 systemd 路径,一般放在/usr/lib/systemd/
目录
systemd 为进程服务集合的总称,它包含许多的进程,负责控制、管理系统的资源,其中包括 systemd-login,负责用户登录相关信息的创建、修改与删除;systemd-sleep 控制系统的休眠、睡眠状态切换等等。主要集中在/usr/lib/systemd/文件目录:
systemd新特性:
● 系统引导时实现服务并行启动:可以并行启动多个服务,提高系统的启动速度。启动速度的快慢取决于系统中启动的服务数量和服务启动顺序。在启动过程中,Systemd 会根据服务之间的依赖关系并行启动服务,从而尽量减少服务之间的等待时间,Systemd 还会对服务进行优先级排序,将优先级高的服务优先启动,加快系统启动的时间。其他一些优化措施,如,使用二进制日志格式来记录系统日志,支持动态加载和卸载服务,可以在系统运行时添加或删除服务,而无需重启系统,从而减少了系统的停机时间。
● 按需激活进程:只有在某个服务被真正请求的时候才启动它,这称为“启动时懒加载”(Lazy Loading);按需启动服务的功能需要服务单元支持。
● 系统状态快照;
● 基于依赖关系定义服务控制逻辑:systemd 维护一个"事务一致性"的概念,保证所有相关的服务都可以正常启动而不会出现互相依赖,以至于死锁的情况;每个服务单元都可以指定一组依赖项,这些依赖项可以是其他服务单元、套接字、挂载点等。Systemd 通过事务性依赖关系管理来确保依赖项按正确的顺序启动,并且在依赖项未满足时暂停或者终止服务单元。事务性依赖关系管理的核心思想是事务(transaction),即将所有依赖项按正确的顺序组合成一个事务,并且在该事务中启动、停止或重新启动服务单元。当一个服务单元的依赖项未满足时,Systemd 会暂停或终止该服务单元,并且等待依赖项满足后再重新启动。
事务性依赖关系管理的实现主要包括以下几个方面:
依赖图分析:Systemd 使用依赖图来描述服务单元之间的依赖关系,并且通过分析依赖图来确定正确的启动顺序。
事务管理器:Systemd 使用事务管理器来管理依赖项的启动、停止或重新启动,并且在依赖项未满足时暂停或终止服务单元。
依赖项状态追踪:Systemd 会追踪每个依赖项的状态,并且在依赖项状态发生变化时重新计算依赖关系。
核心概念:unit(单元) 是 Systemd 中管理和控制系统资源、服务或任务的基本单元,每个 unit 都有独有的名称、类型和配置文件,并且通过依赖关系来控制 unit 的启动和停止顺序。
配置文件进行标识和配置:文件中主要包含了系统服务、监听socket、保存的系统快照以及其他与init相关的信息;
保存至:
/usr/lib/systemd/system/:该目录包含了所有已安装软件的服务单元文件,包括系统服务和第三方软件服务。
/run/systemd/system/:该目录包含了所有正在运行的服务单元文件,这些服务单元文件是基于 /etc/systemd/system/ 和 /usr/lib/systemd/system/ 目录中的文件生成的。
/etc/systemd/system/:该目录包含了所有本地系统服务单元文件的配置文件,包括启动时运行的系统服务和用户服务。
/etc/systemd/system.conf:该文件包含了 Systemd 的全局配置。
/etc/systemd/user/:该目录包含了用户定义的服务单元文件,这些服务单元文件会在用户登录时自动启动。
/etc/systemd/logind.conf:该文件包含了 login 管理器的配置,用于管理用户登录时的会话。
/etc/systemd/systemd-journald.conf`:该文件包含了 journal 日志的配置,用于管理系统日志。
/etc/systemd/timesyncd.conf:该文件包含了 timesyncd 时间同步服务的配置。
/usr/lib/systemd/systemd-sleep:该目录包含了睡眠模式相关的服务脚本。
unit的类型:
Service unit:文件扩展名为.service,用于定义系统服务;
Target unit:文件扩展名为.target,用于模拟实现“运行级别”
Device unit:扩展名为.device,用于定义内核识别的设备
Mount unit:扩展名为.mount,定义文件系统挂载点
Socket unit:扩展名为.socket,用于标识进程间通信用的socket文件
Snapshot unit:扩展名为.snapshot,管理系统快照
Swap unit:扩展名为.swap,用于标识swap设备
Automount unit:扩展名为.automount,文件系统的自动挂载点;
Path unit:扩展名为.path,用于定义文件系统中的一个文件或目录
Unit: 是 Systemd 的基本构建块,用于定义系统服务和其他资源,包括服务的启动顺序、依赖关系、部署状态等。每个 Unit 都有一个唯一的名字和类型,例如 service、socket、mount 等。
在 Unit 文件中,可以定义以下信息:
1)Unit 的类型、名称和描述
2)Unit 之间的依赖关系和启动顺序
3)Unit 的执行程序、启动参数和环境变量
4)Unit 的状态和执行结果
5)Unit 的启动、停止、重启和状态查询等操作
Target: 是一组相关的 Unit 的集合,用于定义系统运行的目标状态。每个 Target 都有一个唯一的名字和一组依赖的 Unit,当系统启动时,Systemd 会尝试启动指定的 Target,从而满足系统运行的需求。在 Target 文件中,可以定义以下信息:
1)Target 的名称和描述
2)Target 所包含的 Unit 和依赖关系
3)Target 的启动、停止、重启和状态查询等操作
Target 的类型和 Unit 的类型类似,例如 multi-user、graphical、network 等。其中,multi-user.target 用于启动多用户命令行界面,graphical.target 用于启动图形化界面,network.target 用于启动网络服务等。
总之,Unit 和 Target 是 Systemd 的核心概念,通过定义 Unit 和 Target,可以对系统服务和状态进行管理和控制。
查看一个服务属于哪个target,只需要看[install]
部分就可以看出属于哪个target
在 Systemd 中,Unit 的状态有以下几种:
- active (running):Unit 正在运行。
- active (exited):Unit 已经完成运行并退出。
- inactive (dead):Unit 已经停止运行。
- activating (start):Unit 正在启动。
- activating (auto-restart):Unit 正在自动重启。
- deactivating (stop):Unit 正在停止。
- deactivating (restart):Unit 正在重启。
- failed:Unit 启动失败,或者在运行过程中突然停止。
关键特性:
基于socket的激活机制:socket与服务程序分离,
基于bus的激活机制:
基于device的激活机制:
基于path的激活机制:
系统快照:保存各unit的当前状态信息于持久存储设备中;
向后兼容sysv init脚本;
不兼容特性:
systemctl命令固定不变
非由systemd启动的服务,systemctl无法与之通信
管理系统服务:
CentOS 7:通过service unit来管理服务
注意:能兼容早期的服务脚本
命令:systemctl COMMAND name.service
服务6与7的差别:
启动:service name start ==> systemctl start name.service
停止:service name stop ==> systemctl stop name.service
重启:service name restart ==> systemctl restart name.service
状体:service name status ==> systemctl status name.service
条件式重启:service name condrestart ==> systemctl try-restart name.service
重载或重启服务:systemctl reload-or-restart name.service
重载或条件式重启服务:systemctl reload-or-try-restart name.service
禁止设定为开机自启:systemctl mask name.service
取消禁止设定为开机自启:systemctl unmask name.service
查看某服务当前激活与否的状态:systemctl is-active name.service
查看所有已经激活的服务:systemctl list-units --type service
查看所有服务:systemctl list-units --type service --all
chkconfig命令的对应关系:
设定服务开机自启动:chkconfig name on ==> systemctl enable name.service
禁止:chkconfig name off ==> systemctl disable name.service
查看所有服务的开机自启状态:chkconfig --list ==> systemctl list-unit-files --type service
查看服务是否开机自启:systemctl is-enabled name.service
查看服务启动的依赖关系:
systemctl list-dependences name.service
target units:
unit配置文件:以.target结尾,
运行级别:
0:runlevel0.target,poweroff.target
1:runlevel1.target,rescue.target
2:runlevel2.target,multi-user.target
3:runlevel3.target,multi-user.target
4:runlevel4.target,multi-user.target
5:runlevel5.target,graphical.target
6:runlevel6.target,reboot.target
级别切换:
init N ==> systemctl isolate name.target
查看级别:
systemctl list-units --type target
获取默认运行级别:
/etc/inittab ==> systemctl get-default
修改默认级别:
/etc/inittab ==> systemctl set-default name.target
切换至紧急救援模式:
systemctl rescue
切换至emergency模式:
systemctl emergency
其他常用命令:
关机:systemctl halt、systemctl poweroff
重启:systemctl reboot
挂起:systemctl suspend
快照:systemctl hibernate
快照并挂起:systemctl hybrid-sleep
Target 与 SysV-init 进程的主要区别:
- 默认的 RunLevel(在 /etc/inittab 文件设置)现在被默认的 Target 取代,位置是 /etc/systemd/system/default.target,通常符号链接到graphical.target(图形界面)或者multi-user.target(多用户命令行)
- 启动脚本的位置,以前是 /etc/init.d 目录,符号链接到不同的 RunLevel 目录 (比如 /etc/rc3.d、/etc/rc5.d 等),现在则存放在 /lib/systemd/system 和 /etc/systemd/system 目录
- 配置文件的位置,以前 init 进程的配置文件是 /etc/inittab,各种服务的配置文件存放在 /etc/sysconfig 目录。现在的配置文件主要存放在 /lib/systemd 目录,在 /etc/systemd 目录里面的修改可以覆盖原始设置
unit 依赖关系
在 Systemd 中,Unit 之间可以建立依赖关系,以确保它们按照正确的顺序启动和停止。以下是 Unit 之间依赖关系的几种类型:
- Requires:表示 Unit A 依赖于 Unit B,如果 Unit B 无法启动,则 Unit A 也无法启动。
- Wants:表示 Unit A 希望依赖于 Unit B,但是如果 Unit B 无法启动,Unit A 仍然可以启动。
- RequiresOverridable:与 Requires 类似,但是允许在启动时覆盖依赖的 Unit,例如在容器中运行时。
- BindsTo:表示 Unit A 依赖于 Unit B,如果 Unit B 停止运行,则 Unit A 也会被停止。
- PartOf:表示 Unit A 是 Unit B 的一部分,如果 Unit B 停止运行,则 Unit A 也会被停止。
- Conflicts:表示 Unit A 与 Unit B 冲突,如果 Unit B 正在运行,则 Unit A 无法启动。
unit 文件:通常分成三段:[unit][service][install]
- Unit 段:此部分所有 Unit 文件通用,用来定义 Unit 的元数据、配置以及与其他 Unit 的关系,Description 描述 Unit 文件信息,Documentation 表示指定服务的文档,Condition 表示服务启动的条件,有些 Unit 还包含 wants、before、after、require 字段,这些表示服务的一个依赖关系。
- Install 段:此部分所有 Unit 文件通用,通常指定运行目标的 target,使得服务在系统启动时自动运行。Wantedby、RequiredBy 与 Unit 段 Wants 字段类似,表示依赖关系,Alias 字段表示启动运行时使用的别名。
- service 段:服务(Service)类型的 Unit 文件特有的字段,用于定义服务的具体管理和执行动作。其中包括 Type 字段,定义进程的行为,例如使用 fork()启动,使用 dbus 启动等等;ExecStart、ExecStartPre、ExecStartPos、ExecReload、ExecStop 分别表示启动当前服务执行的命令、启动当前服务之前执行的命令、启动当前服务之后启动的命令、重启当前服务时执行的命令、停止当前服务时执行的命令。
Unit 段参数
- Description:描述这个 Unit 文件的信息
- Documentation:指定服务的文档,可以是一个或多个文档的 URL 路径
- Requires:依赖的其它 Unit 列表,列在其中的 Unit 模板会在这个服务启动时的同时被启动。并且,如果其中任意一个服务启动失败,这个服务也会被终止
- Wants:与 Requires 相似,但只是在被配置的这个 Unit 启动时,触发启动列出的每个 Unit 模块,而不去考虑这些模板启动是否成功
- After:与 Requires 相似,但是在后面列出的所有模块全部启动完成以后,才会启动当前的服务
- Before:与 After 相反,在启动指定的任务一个模块之间,都会首先确证当前服务已经运行
- Binds To:与 Requires 相似,失败时失败,成功时成功,但是在这些模板中有任意一个出现意外结束或重启时,这个服务也会跟着终止或重启
- Part Of:一个 Bind To 作用的子集,仅在列出的任务模块失败或重启时,终止或重启当前服务,而不会随列出模板的启动而启动
- OnFailure:当这个模板启动失败时,就会自动启动列出的每个模块
- Conflicts:与这个模块有冲突的模块,如果列出的模块中有已经在运行的,这个服务就不能启动,反之亦然
Install 段参数
这部分配置的目标模块通常是特定运行目标的 .target 文件,用来使得服务在系统启动时自动运行。这个区段可以包含三种启动约束:
- WantedBy:和 Unit 段的 Wants 作用相似,只有后面列出的不是服务所依赖的模块,而是依赖当前服务的模块。它的值是一个或多个 Target,当前 Unit 激活时(enable)符号链接会放入 /etc/systemd/system 目录下面以 <Target 名> + .wants 后缀构成的子目录中,如 /etc/systemd/system/multi-user.target.wants/
- RequiredBy:和 Unit 段的 Wants 作用相似,只有后面列出的不是服务所依赖的模块,而是依赖当前服务的模块。它的值是一个或多个 Target,当前 Unit 激活时,符号链接会放入 /etc/systemd/system 目录下面以 <Target 名> + .required 后缀构成的子目录中
- Also:当前 Unit enable/disable 时,同时 enable/disable 的其他 Unit
- Alias:当前 Unit 可用于启动的别名
Service 段参数
用来 Service 的配置,只有 Service 类型的 Unit 才有这个区块。它的主要字段分为服务生命周期和服务上下文配置两个方面
服务生命周期控制相关
- Type:定义启动时的进程行为,它有以下几种值:
- Type=simple:默认值,执行ExecStart指定的命令,启动主进程
- Type=forking:以 fork 方式从父进程创建子进程,创建后父进程会立即退出
- Type=oneshot:一次性进程,Systemd 会等当前服务退出,再继续往下执行
- Type=dbus:当前服务通过D-Bus启动
- Type=notify:当前服务启动完毕,会通知Systemd,再继续往下执行
- Type=idle:若有其他任务执行完毕,当前服务才会运行
- RemainAfterExit:值为 true 或 false(默认)。当配置为 true 时,Systemd 只会负责启动服务进程,之后即便服务进程退出了,Systemd 也仍然会认为这个服务还在运行中。这个配置主要是提供给一些并非常驻内存,而是启动注册后立即退出,然后等待消息按需启动的特殊类型服务使用的。
- ExecStart:启动当前服务的命令
- ExecStartPre:启动当前服务之前执行的命令
- ExecStartPost:启动当前服务之后执行的命令
- ExecReload:重启当前服务时执行的命令
- ExecStop:停止当前服务时执行的命令
- ExecStopPost:停止当其服务之后执行的命令
- RestartSec:自动重启当前服务间隔的秒数
- Restart:定义何种情况 Systemd 会自动重启当前服务,可能的值包括 always(总是重启)、on-success、on-failure、on-abnormal、on-abort、on-watchdog
- TimeoutStartSec:启动服务时等待的秒数,这一配置对于使用 Docker 容器而言显得尤为重要,因其第一次运行时可能需要下载镜像,严重延时会容易被 Systemd 误判为启动失败杀死。通常,对于这种服务,将此值指定为 0,从而关闭超时检测
- TimeoutStopSec:停止服务时的等待秒数,如果超过这个时间仍然没有停止,Systemd 会使用 SIGKILL 信号强行杀死服务的进程
- KillMode:定义 Systemd 如何停止 sshd 服务。
- control-group(默认值):当前控制组里面的所有子进程,都会被杀掉
- process:只杀主进程
- mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
- none:没有进程会被杀掉,只是执行服务的 stop 命令。
服务上下文配置相关
- Environment:为服务指定环境变量
- EnvironmentFile:指定加载一个包含服务所需的环境变量的列表的文件,文件中的每一行都是一个环境变量的定义,该文件内部的key=value键值对,可以用$key的形式,在当前配置文件中获取
- Nice:服务的进程优先级,值越小优先级越高,默认为 0。其中 -20 为最高优先级,19 为最低优先级
- WorkingDirectory:指定服务的工作目录
- RootDirectory:指定服务进程的根目录(/ 目录)。如果配置了这个参数,服务将无法访问指定目录以外的任何文件
- User:指定运行服务的用户
- Group:指定运行服务的用户组
- MountFlags:服务的 Mount Namespace 配置,会影响进程上下文中挂载点的信息,即服务是否会继承主机上已有挂载点,以及如果服务运行执行了挂载或卸载设备的操作,是否会真实地在主机上产生效果。可选值为 shared、slaved 或 private
- shared:服务与主机共用一个 Mount Namespace,继承主机挂载点,且服务挂载或卸载设备会真实地反映到主机上
- slave:服务使用独立的 Mount Namespace,它会继承主机挂载点,但服务对挂载点的操作只有在自己的 Namespace 内生效,不会反映到主机上
- private:服务使用独立的 Mount Namespace,它在启动时没有任何任何挂载点,服务对挂载点的操作也不会反映到主机上
- LimitCPU / LimitSTACK / LimitNOFILE / LimitNPROC 等:限制特定服务的系统资源量,例如 CPU、程序堆栈、文件句柄数量、子进程数量等
- 日志相关类,这里输出到journal,否则默认syslog
Unit 文件占位符
在 Unit 文件中,有时会需要使用到一些与运行环境有关的信息,例如节点 ID、运行服务的用户等。这些信息可以使用占位符来表示,然后在实际运行被动态地替换实际的值。
- %n:完整的 Unit 文件名字,包括 .service 后缀名
- %p:Unit 模板文件名中 @ 符号之前的部分,不包括 @ 符号
- %i:Unit 模板文件名中 @ 符号之后的部分,不包括 @ 符号和 .service 后缀名
- %t:存放系统运行文件的目录,通常是 “run”
- %u:运行服务的用户,如果 Unit 文件中没有指定,则默认为 root
- %U:运行服务的用户 ID
- %h:运行服务的用户 Home 目录,即 %{HOME} 环境变量的值
- %s:运行服务的用户默认 Shell 类型,即 %{SHELL} 环境变量的值
- %m:实际运行节点的 Machine ID,对于运行位置每个的服务比较有用
- %b:Boot ID,这是一个随机数,每个节点各不相同,并且每次节点重启时都会改变
- %H:实际运行节点的主机名
- %v:内核版本,即 “uname -r” 命令输出的内容
- %%:在 Unit 模板文件中表示一个普通的百分号
服务的生命周期
当一个新的 Unit 文件被放入 /etc/systemd/system/ 或 /usr/lib/systemd/system/ 目录中时,它是不会被自识识别的。
在 Systemd 中,一个服务的生命周期包括以下几个阶段:
- 加载阶段:Systemd 加载服务的 Unit 文件,并根据配置启动或禁止服务。
- 准备阶段:Systemd 准备服务的启动环境,包括设置环境变量、创建所需目录、检查依赖关系等操作。
- 启动阶段:Systemd 启动服务,执行服务的启动命令或程序,并记录服务的 PID。
- 运行阶段:服务正常运行期间,Systemd 监视服务的状态,并记录日志、处理信号等操作。
- 停止阶段:当服务需要停止时,Systemd 发送 stop 信号给服务进程,并等待服务进程退出。
- 停止后处理阶段:当服务进程退出后,Systemd 执行服务的停止后处理命令或程序,并记录服务的退出状态。
- 卸载阶段:当服务不再需要时,Systemd 卸载服务的 Unit 文件,并清理相关的运行时文件和日志。
除了以上阶段,Systemd 还提供了其他的功能,例如自动重启、服务状态查询、依赖关系管理等。通过 Systemd 的管理,可以更方便地管理和监控系统服务的运行状态。
服务的激活
- systemctl enable:在 /etc/systemd/system/ 建立服务的符号链接,指向 /usr/lib/systemd/system/ 中
- systemctl start:依次启动定义在 Unit 文件中的 ExecStartPre、**ExecStart **和 **ExecStartPost **命令
服务的启动和停止
- systemctl start:依次启动定义在 Unit 文件中的 ExecStartPre、ExecStart 和 ExecStartPost 命令
- systemctl stop:依次停止定义在 Unit 文件中的 ExecStopPre、ExecStop 和 ExecStopPost 命令
- systemctl restart:重启服务
- systemctl kill:立即杀死服务
服务的开机启动和取消
systemctl enable
:除了激活服务以外,也可以置服务为开机启动systemctl disable
:取消服务的开机启动
服务的修改和移除
- systemctl daemon-reload:Systemd 会将 Unit 文件的内容写到缓存中,因此当 Unit 文件被更新时,需要告诉 Systemd 重新读取所有的 Unit 文件
- systemctl reset-failed:移除标记为丢失的 Unit 文件。在删除 Unit 文件后,由于缓存的关系,即使通过 daemon-reload 更新了缓存,在 list-units 中依然会显示标记为 not-found 的 Unit。