service文件组成
控制单元(Unit)
主要包括服务描述、启动顺序和依赖关系
Description
:当前服务的简单描述;
Documentation
:文档位置;
After
:在什么服务之后启动;
Before
:在什么服务之前启动;
Wants
:表示“弱依赖”关系。某服务停止运行或退出不影响该服务继续运行;
Requires
:表示”强依赖”关系。列在其中的Uint模块会在这个服务启动的同时被启动,如果其中任意一个启动失败,这个服务会被终止。某服务停止运行或退出,该服务也必须停止运行。
PartOf
:该参数仅用于单元的停止或重启。
服务定义(Service)
Type
定义启动类型。可设置的值如下:
simple
(默认值):ExecStart字段启动的进程为主进程;
forking
:ExecStart字段以fork()方式启动,父进程退出后,子进程将成为主进程;
oneshot
:类似于simple,但只执行一次,Systemd 会等它执行完,才启动其他服务;
dbus
:类似于simple,但会等待 D-Bus 信号后启动;;
notify
:类似于simple,启动结束后会发出通知信号,然后 Systemd 再启动其他服务;
idle
:类似于simple,但是要等到其他任务都执行完,才会启动该服务。一种使用场合是为让该服务的输出,不与其他服务的输出相混;
Environment
为服务设置环境变量
EnvironmentFile
加载包含服务所需环境变量参数的文件。
PIDFile
该服务PID文件的路径(一般位于 /run/ 目录下)。 建议在 Type=forking 的情况下明确设置此选项。 如果设为相对路径,那么表示相对于 /run/ 目录。 systemd 将会在此服务启动完成之后,从此文件中读取主服务进程的PID 。 systemd 不会写入此文件,但会在此服务停止后删除它(若仍然存在)。
ExecStart
启动程序时执行的命令。
ExecReload
重启服务执行的命令。
Restart定义sshd退出后,systemd的重启方式。设置值如下:
no
(默认值):退出后不会重启;
on-success
:只有正常退出时(退出状态码为0),才会重启;
on-failure
:非正常退出时(退出状态码非0),包括被信号终止和超时,才会重启;
on-abnormal
:只有被信号终止和超时,才会重启;
on-abort
:只有在收到没有捕捉到的信号终止时,才会重启;
on-watchdog
:超时退出,才会重启;
always
:不管是什么退出原因,总是重启;
服务出现情况与重启对应列表
SuccessExitStatus
额外定义其他的进程"正常退出"状态。 也就是,在退出码"0"、以及表示"正常退出"的 SIGHUP, SIGINT, SIGTERM, SIGPIPE 信号之外, 再额外添加一组表示"正常退出"的退出码或信号。 可以设为一系列以空格分隔的数字退出码或者信号名称, 例如:SuccessExitStatus=1 2 8 SIGKILL\n\n表示当进程的退出码是 1, 2, 8 或被 SIGKILL 信号终止时, 都可以视为"正常退出"。如果多次使用此选项, 那么最终的结果将是多个列表的合并。 如果将此选项设为空, 那么先前设置的列表 将被清空。
RestartSec
重启服务之前需要等待的秒数。
安装部分(Install)
用来定义如何启动以及是否开机启动。
WantedBy
表示该服务所在的Target。 该项目就是将此unit 安装到哪个target 里面去的意思;
multi-user.target是systemd默认启动的Target,表示在这个组里的所有服务,都开机启动。
# 查看当前系统的所有 Target
$ systemctl list-unit-files --type=target
# 查看一个 Target 包含的所有 Unit
$ systemctl list-dependencies multi-user.target
# 查看启动时的默认 Target
$ systemctl get-default
# 设置启动时的默认 Target
$ systemctl set-default multi-user.target
# 切换 Target 时,默认不关闭前一个 Target 启动的进程,systemctl isolate 命令改变这种行为,关闭前一个 Target 里面所有不属于后一个 Target 的进程
实战
创建服务文件
cd /lib/systemd/system
sudo vim myprogram.service
编写服务文件内容
[Unit]
Description=myprogram //当前服务的简单描述
After=network.target //在网络模块运行成功后再启动服务
[Service]
Type=idle //等到其他任务执行完才会启动该服务
Restart=on-failure//如果启动服务失败
RestartSec=5//则5秒后重新启动
ExecStart=/usr/bin/rs485_mqtt -t 1800 -o 2 -d -c //开始运行程序
[Install]
WantedBy=multi-user.target
保存并关闭文件
systemctl是一个systemd工具,负责控制systemd系统和管理系统服务。
当新增或修改service单元文件时,需要系统重新加载所有修改过的配置文件
sudo systemctl daemon-reload
设置开机启用
sudo systemctl enable myprogram.service
关闭开机启用
sudo systemctl disable myprogram.service
启动服务
sudo systemctl start myprogram.service
关闭服务后,进程会全部被kill掉
sudo systemctl stop myprogram.service
查看服务的状态
systemctl status myprogram.service
实战过程中遇到的问题
1.上电自启服务,程序总是退出,查看日志系统发现程序总是是在deamon()函数附近退出
原因:systemd自启服务给程序fork了一个进程来执行程序,假设进程号为1000,但是由于程序中调用了deamon(),该函数调用了fork()又创建了一个进程,假设进程号为1001,此时程序不在1000这个进程中运行了,而是在1001这个进程上运行,然后systemd自启服务发现程序不在1000这个进程中运行了,就把程序杀死了。
解决办法就是systemd自启服务中的程序千万别调用deamon函数
2.上述问题解决完又出现新问题,程序在mqtt初始化函数附近退出
原因:设备在上电后,网络模块没这么快加载完毕,而mqtt程序又是需要联网才能正常运行的
解决办法在mqtt初始化代码前中加入sleep()阻塞一段,等待设备联网成功后再执行mqtt初始化,到此程序一切正常,大功告成。
systemd启动服务管理机制,有如下好处:
平行处理所有服务,加速开机流程
:不相依服务可以同时启动 一经要求就响应的on-demand 启动方式
:管理系统服务只需systemctl一个指令 服务相依性的自我检查
:无需管理员进行相依性分析,在启动某个服务前会自动帮管理员启动未启动的依赖服务; 依daemon 功能分类
:每个服务为一个unit单元,并有不同的type(service, socket, target, path, snapshot, etc.); 将多个daemons 集合成为一个群组
:类似于systemV中的runlevel,将许多的功能集合为一个target选项,作为操作环境,亦即执行了多个daemon; 向下兼容旧有的init 服务脚本
:如使用init 3可以切换到图形界面。
但是systemd不能完全取代systemv。其中有runlevel对应不完全,systemctl支持语法有限制,不可自定义参数;管理员手动启动服务无法通过systemd进行管理;编制systemd启动设定,不能存在互动机制等。