Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

Linux的三种Init机制

26
Oct
2015

Linux的三种Init机制

By Alex
/ in Linux
/ tags Linux知识
0 Comments

一台安装了Linux操作系统的计算机的启动,从BIOS加电开始,随后进入Bootloader,由Bootloader加载Linux内核并初始化。

内核本身不能做任何有意义的事情,内核初始化的最后一步,就是创建PID为1、名为init的守护进程,该进程是操作系统中所有用户进程的祖先——所有用户进程都由它创建。

所谓Init机制,就是确定了init进程行为的一套规范及其实现(此实现通常就反应在/sbin/init上)。Linux发展到今天,依次出现了sysvinit、upstart 、systemd三种广泛应用的Init机制。

Sysvinit

sysvinit就是System V风格的初始化机制,它来源于System V风格的UNIX。绝大部分Linux发行版均兼容sysvinit。

sysvinit引入了运行级别(runlevel )的概念,不同模式下需要进行的系统初始化工作不一样。各发行版对运行级别的定义各有不同,但是通常:0表示关机、1表示单用户模式、6表示重启。

sysvinit正常启动(单用户或者GUI模式)系统时,大致执行的工作流如下:

  1. 读取/etc/inittab获得需要进入的运行级别
  2. 依次执行:
    1. 执行rc.sysinit脚本,完成一些重要的系统初始化工作,例如:设置系统时钟、设置定义在/etc/sysctl.conf中的内核参数、启用swap、挂载文件系统等
    2. 执行rc*.d中的脚本,其中*为运行级别。被执行的脚本均以S开头,S后面的数字表示其被执行的顺序
    3. 执行rc.local脚本
    4. 如果需要,启动X显示管理器

sysvinit正常关闭(关机或者重启)系统时,则以数字顺序,执行rc*.d中的K开头的脚本。

sysvinit在启动、关闭时均是串行的执行各脚本的,这一方面让问题排查简单,一方面则导致运行速度缓慢。

很多Linux发行版为sysvinit配套了管理工具,例如service、chkconfig等。

参考Linux运行级别和启动顺序了解sysvinit的更多知识。

Upstart
基础知识

从2.6版本的内核开始,即插即用被良好的支持,Linux被越来越多的用在个人计算机领域,这让sysvinit面临两个问题:

  1. 串行化、阻塞的初始化过程导致启动消耗的时间长,而个人计算机却经常性的重启
  2. 一次性启动所有系统服务浪费很多资源,而即插即用要求按需启动、卸载服务

早期的Ubuntu版本(6.10+)即启用了upstart这一init机制,以解决上述问题。

upstart是一个事件驱动的init机制,例如,当U盘插入到接口时,内核会通知upstart,后者会进行挂载操作。upstart的优势包括:

  1. 能够很好的实现即插即用——当新硬件被发现时动态启动服务,硬件被拔出时停止服务
  2. 提高系统启动速度,一方面减少启动时的工作量,一方面事件驱动改变了串行话的执行方式
Upstart的启动过程
  1. 执行内部的初始化
  2. 触发一个名为 startup 的特殊事件,该事件会触发系统剩余部分的初始化
  3. init进程触发定义在/etc/init/mountall.conf的任务mountall,这是因为mountall声明了 start on startup 
  4. mountall进一步触发若干事件:local-filesystem、virtual-filesystem、all-swaps
  5. local-filesystem导致udev的执行,后者导致upstart-udev-bridge的执行
  6. local-filesystem发布filesystem事件、upstart-udev-bridge发布net-device-up IFACE=lo,这两个事件导致rc-sysinit的执行
  7. rc-sysinit导致telinit的执行,后者发布runlevel事件
  8. runlevel事件导致rc的执行,rc负责读取/etc/rc*.d中的脚本并执行
对sysvinit的兼容

很多现有的服务已经基于System V风格的Init脚本来编写,Upstart必须兼任它。尽管Upstart完全是基于任务+事件的,但是它模拟了对运行级别的支持。

修改/etc/init/rc-sysinti.conf中的DEFAULT_RUNLEVEL可以改变默认的“运行级别”。在启动过程中,rc任务会根据设置的运行级别,访问系统中的sysvinit脚本。

重要概念
任务(Job)

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事件
事件(Event)

事件是一个通知,由Upstart发送给所有订阅者(任务或者其它事件),管理员随时可以手工的触发事件:

Shell
1
initctl emit myevent

一个Job的启动/停止行为,可能是其它Job启动/停止行为的结果,这是依靠事件来驱动的。Upstart提供了一系列特殊事件(starting、started、stopping、stopped),用来广播Job的状态转换。尽管某些Job状态的名称与事件名称相同,但是它们不是一回事。

根据产生、消费方式的不同,事件分为:

事件分类 说明
Signals

非阻塞的异步事件,发出信号后调用者立即返回,调用者不关心谁关注此信号,手工触发信号的方式如下:

Shell
1
initctl emit --no-wait mysignal
Methods 阻塞的同步事件,这类事件的行为类似于编程语言中的方法/函数调用,调用者必须等待事件的订阅者处理完毕。手工触发时,initctl执行后的$?反应订阅者的处理结果
Hooks

介于信号和方法之间:

  1. 它像信号一样,通知系统发生了变化
  2. 它像方法一样,是阻塞的同步事件

钩子的典型例子是starting、stopping这两个Job事件

系统中预定义的事件取决于发行版,可以执行 man 7 upstart-events 查看之。 

定义自己的Job

Job需要进行的工作由一个配置文件描述,该文件位于/etc/init目录中,以.conf结尾。

小节详解

配置文件由多个小节(stanza) 构成,小节定义Job某个方面的特性:

类 小节 说明
文
档
author 定义任务的作者
description 定义任务的描述
进
程
控
制
 
expect

Upstart基于进程PID来跟踪Job,如果不明确指定,Upstart使用它执行exec/script小节时产生的第一个进程的PID

但是,大部分作为系统服务的Job都会守护进程化(daemonize),这些Job会再一次的fork出子进程,以确保守护程序与Job的初始进程没有关联性

Upstart本身无法知晓任务是否fork两次,这就需要使用expect来声明:

  1. expect fork 表示任务的进程只会fork一次次
  2. expect daemonize 表示任务的进程会fork两次

此外, expect stop 期望Job的主进程触发一个SIGSTOP信号来指示它已经准备好,Upstart会等待此信号,然后发送SIGCONT信号给Job主进程提示它继续,并执行任务的post-start脚本

kill signal

设置Upstart发送何种信号以停止Job,默认SIGTERM,示例:

Shell
1
2
kill signal INT
kill signal SIGINT
kill timeout

设置Upstart强制杀死Job进程前等待的秒数,默认5

reload signal

设置任务需要被reload时,Upstart发送的信号,默认SIGHUP

进
程
定
义
exec

指定Job的主逻辑。定义一个需要执行的单行的命令,如果命令行中包含任何Shell元字符,会直接传递给Shell,以确保Shell重定向、变量展开等功能的正常。语法格式:

Shell
1
2
3
exec COMMAND [ ARG ]...
# 示例
# exec /usr/bin/my-daemon --option foo -v
pre-start

定义Job进入Pre-start状态时需要执行的逻辑,例如清空临时/缓存目录,最好不要包含耗时的逻辑。语法格式:

Shell
1
2
3
4
5
6
7
8
pre-start exec|script
# 脚本可以具有多行内容
pre-start script
  # 在这里编写Shell脚本
  ...
  # 下面的这行脚本用于取消Job的启动
  stop ; exit 0
end script
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启动的一系列事件,语法格式:

Shell
1
2
3
4
5
# EVENT为事件的名称,可以指定多个事件,并用and或者or连接,支持用( ) 来限定逻辑操作的优先级
# KEY和VALUE可以用来限定环境变量,这些变量可以由事件定义,并传递给Job的小节
# VALUE支持fnmatch风格的通配符
# KEY和VALUE可以使用 != 表示不相等
start on EVENT [[KEY=]VALUE]... [and|or...]

该小节的内容必须编写在单行中

如果要 在系统的基础设施准备完毕后启动Job,参考下面的例子:

Shell
1
2
3
4
5
6
7
8
9
# 在本地文件系统、非环回网卡准备好后,启动该Job
# 如果你的Job绑定了非0.0.0.0地址,则它需要在目标网卡启动后,才能启动
start on (local-filesystems and net-device-up IFACE!=lo)
 
# 在指定的运行级别启动Job
start on runlevel [2345]
 
# 在抽象任务network-services启动完毕之后,启动Job
start on started network-services

如果要在一个服务启动之后,启动Job,使用:

Shell
1
start on started other-service

反之,如果要在一个服务启动之前,启动Job,使用:

Shell
1
start on starting other-service 
stop on

该小节定义一系列导致Job停止的事件(如果Job正在运行),语法格式:

Shell
1
stop on EVENT [[KEY=]VALUE]... [and|or...]

用法类似于start on,示例:

Shell
1
2
3
stop on runlevel [016]
stop on stopping network-services
stop on stopped other-service
任
务
环境 
env  设置一个所有小节都能看到的环境变量,语法格式: env KEY[=VALUE] 
export 导出Job中通过env小节设置的所有环境变量到该Job触发的全部事件中去
其
它
normal exit

指定那些退出码(exit status)表示正常退出,默认0。示例:

Shell
1
2
3
4
# 0和13视为正常退出
normal exit 0 13
# 如果因为SIGUSR1 SIGWINCH信号而退出,也视为正常
normal exit 0 13 SIGUSR1 SIGWINCH

 

respawn

在不指定此小节的情况下,静默退出的Job进入 stop/waiting 状态

使用此小节的情况下,一旦Job从它的主逻辑exec/script退出,Upstart都会重新启动它,一并执行定义的pre-start,、post-start、 post-stop小节(pre-stop 不被执行)

使用此特性的例子包括各种网络服务,例如MySQL,只要不是管理员有意的停止它,都应该一直运行

respawn limit

限制重新启动的次数,语法格式:

Shell
1
2
3
4
5
# 最多重新启动limit次,每次启动间隔INTERVAL秒
respawn limit COUNT INTERVAL | unlimited
# 示例:
respawn limit 10 5
respawn limit unlimited
instance 声明Job的实例名,有时你需要运行一个Job的多个实例
task

从概念上说,task是一种短暂运行完毕的Job

使用此关键字后,导致此Job启动的事件将被阻塞,直到当前Job转换到stopped状态

编写Job的注意点

当开发自己的Job时,需要注意一下内容:

  1. fork的次数需要声明:很多Linux后台服务基于Double fork实现守护进程,这必须在Job配置文件中声明
  2. fork后即可用:你必须保证在Double fork后,服务立刻进入可用状态,因为Upstart基于fork计数判断服务是否就绪
  3. 遵循SIGHUP要求:Upstart发送SIGHUP信号给Job后,期望Job的行为满足以下要求:
    1. 完成必要的重现初始化工作,例如重新读取配置文件
    2. Job必须使用现有的PID,即Job不能通过fork来实现reload
  4. 遵循SIGTEM要求:Job收到此信号后,应当立即停止,并释放所有资源
自定义Job的例子

这里我们讲解一下MySQL的Job定义:

/etc/init/mysql.conf
Shell
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
基础知识

Systemd是主流Init系统中最新的一个,是Upstart的竞争者。值得注意的是,作为Upstart起源的Ubuntu从15.04版本开始,也改用了Systemd。

Systemd具有以下特点:

  1. 兼容SysVinit或者 LSB initscripts
  2. 提供比Upstart更加激进的并行启动能力,通过socket / D-Bus activation等技术启动服务,速度更快
  3. 提供按需启动的能力
  4. 利用cgroup跟踪进程的生命周期,比Upstart基于PID的方式更加高效。在fork时,子进程会遍历父进程的cgroup,Systemd通过遍历cgroup可以把服务相关的所有进程都正确的停止掉
  5. 内建挂载服务,以便自动挂载U盘、光盘,不再需要安装autofs服务
  6. 事务性的依赖管理,Systemd在最大程度上保证并发启动的同时,确保那些有明确依赖顺序的服务(例如NFS依赖网络),按照顺序启动
  7. 内建日志服务journald,可以代替syslog,克服了syslog的一系列缺点
重要概念
单元(Unit)

Systemd把系统启动过程中的每一项工作,例如:

  1. 启动后台服务,例如SSHD
  2. 执行系统配置,例如挂载文件系统

等都抽象为一个配置单元,它们的角色类似于Upstart的Job。配置单元被划分为不同的类型:

配置单元类型 说明
service 封装一个后台服务,例如MySQL,最常用
socket 封装系统中的一个套接字,每个socket单元都有一个相应的service单元,当第一个连接到达时,与socket关联的service就会启动
device 封装一个存在于Linux设备树中的设备,每个使用udev规则标记的设备都会在Systemd中作为一个device单元。例如/dev/sda2会自动被转换为dev-sda2.device单元
mount

封装文件系统中的一个挂载点,Systemd会对这个挂载点进行监控管理,例如在系统启动时执行挂载,在某些情况下卸载
/etc/fstab中的条目均被转换为mount单元并在系统启动时处理,例如/home会被转换为home.mount单元

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这类特殊的配置单元,用于分组其它配置单元。利用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)启动的思想。

解除Socket依赖

绝大部分服务之间的依赖是套接字依赖——被依赖者(A)的套接字(S)必须准备好监听,依赖者(B)才能启动。传统的Init机制都是启动A,然后再启动B。

Systemd的想法是,直接启动S,当B第一次向S发起连接时,延迟的启动A。能让服务A的套接字先于A本身启动,需要依靠内核的一个特性:子进程可以继承父进程的文件描述符(文件句柄,file descriptor) ,而套接字是一种文件句柄。因此先于A启动S的流程可以如下:

  1. 由init进程创建套接字S,清空其close_on_exec位,以便允许它被子进程继承
  2. 当B向S发起连接时,由init进程fork/exec出A的主进程
  3. A的主进程自动获得套接字S

其实这种内核特性以前就被使用过,例如inetd。

解除D-Bus依赖

所谓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单元:

Shell
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***

限定当前单元运行必须满足的条件(如果不满足则不运行),例如:

Shell
1
2
# 只有/etc/ssh/sshd_not_to_be_run文件不存在时,才运行此单元
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run 
Assert*** 限定当前单元运行必须满足的条件(如果不满足则报启动失败)

Service段:专用于服务类单元的配置

配置项 说明
Type 定义启动时的进程行为,有效值:
simple  默认值,执行ExecStart指定的命令,启动服务主进程
forking  以fork调用创建子进程,并退出父进程
oneshot  一次性服务,Systemd会等待该服务退出,然后继续init过程
dbus  当前服务通过D-Bus启动
notify  当服务启动完毕,通知Systemd继续
idle  当其它单元都启动完毕后,才执行此单元
ExecStart

启动服务的命令行

默认情况下,这些生命周期钩子执行失败会终止后续流程。要改变此逻辑,使用 =-操作符:

INI
1
ExecStartPre=-/bin/chmod 700       /run/thing

这样,即使 ExecStartPre 执行失败,后续的ExecStart仍然会执行

ExecStartPre

启动服务的前置命令行

这些生命周期钩子可以指定多次,前面的先执行

ExecStartPost 启动服务的后置命令行
ExecReload 重新加载服务时执行的命令行
ExecStop 停止服务时执行的命令行
ExecStopPost 停止服务的后置命令行
RestartSec 重启的间隔秒数
Restart

重启服务的时机,有效值:

no 不重启
always  总是重启
on-success  干净退出后重启
on-failure  非干净退出后重启,包括Core Dump+非
on-abnormal 异常后重启
on-abort  中断后重启
on-watchdog

干净退出:退出码为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时,会同时激活的其它单元
编写配置单元的注意点
  1. 后台服务不再需要使用Double fork来产生守护进程
  2. 不要调用 setsid(),应该由Systemd处理
  3. 不需要维护PID文件
  4. Systemd提供了日志功能,服务进程只需要:
    1. 输出到stderr即可,取决于配置
    2. 打开systemd-cat管道并输出,例如 echo "message" | systemd-cat 
    3. 可以在服务脚本中调用log_daemon_msg、log_failure_msg、log_progress_msg、log_end_msg等LSB函数
  5. 正确处理SIGTERM信号,立即停止当前服务
  6. 正确处理SIGHUP信号,重新启动服务
  7. 需要套接字的服务,不要自己创建,让Systemd传入
  8. 调用 sd_notify()函数告知Systemd服务本身状态的改变。当服务初始化完毕后进入就绪状态时,即可调用之 
模板化

Systemd支持模板化的Unit文件,用于从单个配置文件启动多个单元。调用模板化Unit时,需要使用特殊的@符号: <service_name>@<argument>.service。

其中argument是传递给模板的字符串,在模板文件中可以这样访问:

  1. %i:进行转义的参数
  2. %I:原样传递的参数

Ceph Mon就是这样的模板Unit:

/lib/systemd/system/ceph-mon@.service
INI
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

在目录下有指向上述模板的符号链接,参数就是通过符号链接的名字传递的:

Shell
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定义:

/lib/systemd/system/mysql.service
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这个配置单元组的定义:

/lib/systemd/system/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命令知识集锦。

← Docker学习笔记
IntelliJ平台知识集锦 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • Linux信号、进程和会话
  • Linux网络知识集锦
  • Linux运行级别和启动顺序
  • Linux目录层次和配置文件
  • Linux知识集锦

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2