1. 简要介绍:
Systemd 是一个系统和服务管理器,也是 Linux 操作系统中最常用的【初始化系统】之一。最早是为了代替传统的初始化系统(init)而开发的,相较于传统 init,systemd 具有支持并行启动,【可同时启动多个服务】,提高系统启动速度的优势,可以更好地管理系统和服务进程。目前,许多主流 Linux 发行版都采用了 systemd 作为其默认的初始化系统,包括 Ubuntu、Debian、Fedora、CentOS、Arch Linux 等。
1.1 服务单元文件
Systemd通过【服务单元文件】进行开机启动管理。【服务单元文件】指的是在 /etc/systemd/system 目录中以“.service”后缀的结尾的文件。
2. 操作指导:
2.1 配置开机启动服务
建立adb连接(不清楚如何操作,可参考《入门指南/调试方式介绍/adb调试》)
执行命令,切换到板卡环境:
adb shell
首先我们在 /etc/systemd/system/ 创建一个自己的【服务单元文件(Systemd Service)】:myservice.service。
cd /etc/systemd/system/
vim myservice.service
注意:Systemd Service 既可位于 /etc/systemd/system(供系统管理员和用户使用),也可以位于/usr/lib/systemd/system(供发行版打包者使用),我们一般使用前者即可。
【服务单元文件】脚本文件以 .service 结尾,由 Unit、Service 和 Install 三个区块组成,以下为 service 文件脚本样例:
注意:GUI应用需要在桌面系统启动后再启动ExecStart为指定启动单元的命令或者脚本,是配置文件里面最重要的字段,下面来演示如何创建并编辑脚本。
2.2 创建并编辑脚本
创建一个可执行的脚本用于启动Service进程,用户可根据需求向脚本写入需要执行的可执行文件,此处的脚本仅服务于演示需要。
执行命令,先定位到/userdata目录:
cd /userdata
然后通过vim命令,可以创建start_app.sh脚本进行编辑:
touch start_app.sh
chmod 755 start_app.sh
vim start_app.sh
注意:这里简单地通过编译hellomonster.c生成了hellomonster可执行文件,仅为在此进行说明演示。用户需根据自己需求向脚本写入需要执行的可执行文件。
2.3 验证开机启动:
在创建或修改任何【服务单元文件】后,我们必须让 systemd 知道有新的【服务单元文件被创建】或者【服务单元文件被修改】,可通过下方命令让systemd执行查找与同步:
systemctl daemon-reload
此时,我们新的【服务单元文件】应该已经被识别,我们可通过下方命令启动它:
systemctl start myservice.service
最后,告诉 systemd 使能新的【服务单元文件】,以便每次开机启动时它都会启动:
systemctl enable myservice.service
注意:您不会从此命令中获得反馈,因为它所做的只是向 systemd 发送一条消息,告诉它启动您的服务。您键入的命令不会停留以查看接下来会发生什么。
我们可以用以下命令来检查我们的服务,确保它看起来没问题
systemctl status myservice.service
Loaded行:配置文件的位置,是否设为开机启动
Active行:表示正在运行
Main PID行:主进程ID
CGroup块:应用的所有子进程
日志块:应用的日志
下面我们进行软件重启,在adb shell环境下输入重启命令
reboot
进入板卡环境
adb shell
最终程序运行后,可在ps -x进程列表中看到进程在启动运行
ps -x
注意:要在该程序源代码中加入死循环,否则无法看到该程序正在运行
3. 详细说明
3.1 [Unit] 启动顺序与依赖关系
- 定义控制单元 [Unit]
所有引导过程中 Systemd 要控制的东西都是一个单元。基本的用法如下:
Description:代表整个单元的描述,可根据需要简单填写。
Environment:环境变量或参数(系统环境变量此时无法使用)
After:描述服务类别,表示本服务需要在某服个务(*.service)或多个服务启动后再启动,也可以表示需要在某个服务组(*.target)启动后再启动。【注意:GUI应用需要在桌面系统启动后再启动】
Before:表示本服务需要在某些服务启动之前启动。
Defaultdependencies=no 可以禁止默认依赖的注入,是为了【减少错误】 和 【减少单元配置文件的体量】。
Wants:本单元启动了,它“想要”的单元也会被启动。但是这个单元若启动不成功,对本单元没有影响。
Requires: 这个设定并不能控制启动顺序,它表示"强依赖"关系,即如果该服务启动失败或异常退出,则本单元也无法启动,所以不建议使用这个字段。
OnFailure:若本单元启动失败了,那么启动这个单元作为折衷。
3.2 [Service] 启动行为
- 定义服务本体 [service]
在定义完了 Systemd 用来识别服务的单元后,我们来定义服务本体。基本的用法如下:
Type:服务的类型,各种类型的区别如下所示
simple:默认,这是最简单的服务类型。ExecStart字段启动的进程为主进程
forking:ExecStart 字段将以 fork() 方式启动,此时父进程将会退出,子进程将成为主进程(例如用 shell 脚本启动服务进程)。
oneshot:适用于那些被一次性执行的任务或者命令,它运行完成后便了无痕迹。类似于simple,但只执行一次,Systemd 会等它执行完,才启动其他服务。
dbus:这个程序启动时需要获取一块 DBus 空间,所以需要和 BusName= 一起用。只有它成功获得了 DBus 空间,依赖它的程序才会被启动。
ExecStart:在输入的命令是start时候执行的命令,这里的命令启动的程序必须使用绝对路径,比如你必须用/sbin/arp而不能简单的以环境变量直接使用arp。
ExecStop:在输入的命令是stop时候执行的命令,要求同上。
ExecReload:这个不是必需,如果不写则你的service就不支持restart命令。ExecStart和ExecStop是必须要有的。
3.3 [Install] 安装服务
- 安装服务 [install]
服务编写完之后还需要被systemd装载,定义安装单元各个字段如下:
WantedBy:设置服务被谁装载,一般设置为multi-user.target
执行以下命令可查看multi-user.target所包含的所有服务
systemctl list-dependencies multi-user.target
3.4 关于TARGET
Target 的含义是【服务组】,如 WantedBy=multi-user.target 指的是该服务所属于 multi-user.target。当执行以下命令时,xxx.service 的符号链接就会被创建在 /etc/systemd/system/multi-user.target 目录下。
systemctl enable xxx.service
可以通过以下命令查看系统默认启动的 target
systemctl get-default
一般为 multi-user 或者是 graphical。因此配置好相应的 WantedBy 字段,可以实现服务的开机启动。
注意:常用的 Target 有两个:一个是multi-user.target,表示多用户命令行状态;另一个是graphical.target,表示图形用户状态,它依赖于multi-user.target。