嵌入式的ubuntu系统如何写好SysV Init脚本呢?与system服务又有什么差别呢?一起随着文章来探究吧。
问题背景
许多传统Linux服务仍使用SysV Init脚本(/etc/init.d/),但在Systemd系统中,这些脚本需通过systemd-sysv-generator换为原生服务单元。若脚本未遵循Linux Standard Base (LSB)规范,会导致以下问题:
- 服务启动/停止有误:缺失关键命令(如status)或错误退出码。
- 依赖混乱:未声明依赖关系,导致服务启动顺序错误。
- 日志污染:输出未重定向,干扰Systemd日志(journald)。
SysV Init脚本规范指南
1. 必须支持的标准命令脚本需实现以下命令:
1.case"$1"in2.start)3.# 启动服务逻辑4.;;5.stop)6.# 停止服务逻辑7.;;8.restart)9.# 建议直接调用自身stop再start,或委托给Systemd10.$0stop11.$0start12.;;13.status)14.# 返回服务运行状态(必需!)15.ifpidof -x"$DAEMON">/dev/null;then16.echo"Service is running"17.exit018.else19.echo"Service is stopped"20.exit3# LSB规范:未运行状态码21.fi22.;;23.*)24.echo"Usage:$0{start|stop|restart|status}"25.exit1esac
2. LSB头部注释(必需)在脚本开头添加元数据:
1.### BEGIN INIT INFO2.# Provides: my_service3.# Required-Start: $network $syslog4.# Required-Stop: $network $syslog5.# Default-Start: 23456.# Default-Stop: 0167.# Short-Description: My Custom Service8.# Description: This service does something magical.
- Provides:服务名称,需唯一。
- Required-Start/Stop:定义依赖的其他服务或设施如($network表示网络就绪)。
3. 正确退出状态码退出码含义
- 0 成功
- 1 泛型错误
- 2 无效命令参数
- 3 服务未运行(status命令专用)
- 4 权限不足
退出码含义0成功1泛型错误2无效命令参数3服务未运行(status命令专用)4权限不足4. 避免破坏Systemd的特性Systemd 是一个进程生命周期管理器,它需要直接跟踪服务的主进程(PID)。如果服务脚本不遵循规范,会导致Systemd无法正确管理服务状态(如判断是否运行、自动重启等)。4.1 禁止后台化进程4.1.1 错误写法
# 传统SysV脚本中常见的后台化方式/usr/bin/my_daemon --config /etc/my.conf &
4.1.2 问题
- Systemd 会启动脚本,但脚本中的 & 会让 /usr/bin/my_daemon 在后台运行。
- Systemd 只能监控到脚本自身(即Shell进程)的状态,而无法追踪真正的服务进程(my_daemon)。
- 当Shell脚本执行完毕后,Systemd 会认为服务已退出,导致服务状态显示为 inactive (dead),即使my_daemon仍在运行。
4.1.3 正确写法
# 使用 exec 替换当前进程(Shell脚本进程被替换为服务进程)exec/usr/bin/my_daemon --config /etc/my.conf
4.1.4 原理
- exec命令会让当前Shell进程直接被替换为my_daemon进程。
- Systemd 可以追踪到 my_daemon 的PID,从而正确管理服务状态。
4.2 日志输出规范4.2.1错误写法
# 直接输出到控制台或文件echo"Starting service..."> /dev/console/usr/bin/my_daemon >> /var/log/my_service.log2>&1
4.2.2 问题
- Systemd 默认通过 journald 集中管理日志,直接写入文件会导致日志分散,难以查询。
- 若服务崩溃,Systemd 可能无法捕获崩溃前的最后日志。
4.2.3 原理
- Systemd 会自动捕获服务的stdout/stderr 并写入 journald。
- 可通过journalctl -u my_service 查看完整日志,无需手动管理日志文件。
5. 注册服务依赖SysV脚本需通过 LSB头部注释 声明依赖关系,帮助Systemd理解服务启动顺序和条件。这是SysV与Systemd兼容的关键!5.1 LSB头部详解
### BEGIN INIT INFO#Provides: my_service# 服务名称(唯一标识)#Required-Start:$network$syslog# 依赖的服务或系统设施#Required-Stop:$network$syslog# 停止时的依赖#Default-Start: 2345# 启用服务的运行级别#Default-Stop: 016# 禁用服务的运行级别#Short-Description: My Service# 简短描述#Description: Long description here.### END INIT INFO
5.1.1 关键字段Required-Start 服务启动前必须就绪的依赖项,例如:
- $network 等待网络就绪;
- $syslog 等待日志系统就绪;
- nginx 等待$network服务启动;
- docker.socket 等待Docker套接字就绪;
- Default-Start/Default-Stop 定义服务在哪些运行级别自动启动或停止;
- 2 3 4 5 多用户模式(无图形界面或图形界面);
- 0 1 6 关机、单用户模式、重启。
5.2 依赖注册命令5.2.1 目的将LSB头部中的依赖关系转换为系统实际的启动链接(符号链接到 /etc/rcX.d/ 目录)。
Debian/Ubuntu:# 生成依赖链接 sudoupdate-rc.d my_service defaults
# 移除依赖链接sudoupdate-rc.d my_service remove
5.3 依赖不注册的后果假设 my_service 依赖网络,但未声明 $network:
- SysV场景:服务可能在网络未就绪时启动,导致连接失败。
- Systemd场景:systemd-sysv-generator 无法生成正确的依赖关系,服务可能并行启动,引发竞态条件。
6. 总结
- 避免后台化 → 让Systemd直接跟踪主进程。
- 规范日志输出 → 信任Systemd的日志管理能力。
- 声明依赖 → 通过LSB头部告诉Systemd“谁先谁后”。
- 注册服务 → 确保依赖关系生效,服务按预期启动。
- SysV脚本的“四不像”问题本质是 对Systemd机制的不理解。遵循上述规范,可最大限度兼容Systemd,同时为未来迁移到原生Systemd服务铺平道路。
创建并启用 SysV Init 脚本的完整流程
1. 创建 SysV Init 脚本的位置SysV Init 脚本必须放置在 /etc/init.d 目录下,这是所有 SysV 风格服务脚本的标准位置。Systemd 的兼容层(systemd-sysv-generator)会自动扫描此目录,将符合规范的脚本转换为 Systemd 服务单元。
2. 创建脚本的步骤
2.1 创建脚本文件使用文本编辑器(如 nano)在/etc/init.d/下创建脚本文件:
sudo nano/etc/init.d/my_service
2.2 编写脚本内容粘贴以下模板(需根据实际服务修改):
#!/bin/bash### BEGIN INIT INFO# Provides: my_service# Required-Start: $network $syslog# Required-Stop: $network $syslog# Default-Start: 2345# Default-Stop: 016# Short-Description: My Custom Service# Description: This service does something important.### END INIT INFO
# 服务主程序路径DAEMON="/usr/bin/my_daemon"NAME="my_service"
case"$1"instart)echo"Starting$NAME"exec$DAEMON;;stop)echo"Stopping$NAME"killall -9$DAEMON;;restart)$0stop$0start;;status)ifpidof -x"$DAEMON">/dev/null;thenecho"$NAMEis running"exit0elseecho"$NAMEis stopped"exit3fi;;*)echo"Usage:$0{start|stop|restart|status}"exit1esac
exit0
3. 启用服务的两种方式3.1 传统 SysV 方法(兼容 Systemd)
# Debian/Ubuntusudoupdate-rc.d my_service defaults
3.2 Systemd 原生方法(推荐)
# 直接通过 Systemd 启用(等效于 update-rc.d)sudo systemctlenablemy_serviceSystemd 会自动生成符号链接:Created symlink /etc/systemd/system/multi-user.target.wants/my_service.service → /lib/systemd/system/my_service.service.
4. 验证脚本兼容性4.1 检查 LSB 头部合规性
# Debian/Ubuntusudoinsserv -n my_service
输出应无错误,显示依赖关系已解析。4.2 查看 Systemd 生成的单元文件
# 查看生成的 .service 文件systemctl cat my_service输出示例:# /run/systemd/generator.late/my_service.service# Automatically generated by systemd-sysv-generator
[Unit]Documentation=man:systemd-sysv-generator(8)SourcePath=/etc/init.d/my_serviceDescription=My Custom ServiceAfter=network.target syslog.target
[Service]Type=forkingRestart=noTimeoutSec=5minIgnoreSIGPIPE=noKillMode=processGuessMainPID=noRemainAfterExit=yesExecStart=/etc/init.d/my_service startExecStop=/etc/init.d/my_service stopExecReload=/etc/init.d/my_service restart
5. 管理服务
# 启动服务sudosystemctl start my_service
# 查看状态systemctlstatus my_service
# 停止服务sudosystemctl stop my_service
# 重启服务sudosystemctl restart my_service
6. 关键注意事项6.1 避免冲突配置如果服务已存在 Systemd 原生单元文件(如/lib/systemd/system/my_service.service),需删除或禁用它,否则 SysV 脚本可能被忽略。6.2 禁用传统服务干扰停止并禁用旧版networking服务(如果存在):
systemctl stop networkingsudo systemctldisablenetworking
6.3 日志输出规范确保服务进程的日志输出到 stdout/stderr,而非直接写入文件。Systemd 会通过 journalctl 捕获日志:
journalctl-u my_service -f
7. 总结步骤命令/操作目的创建脚本sudo nano /etc/init.d/my_service定义服务行为赋予执行权限sudo chmod +x /etc/init.d/my_service确保脚本可执行启用服务sudo systemctl enable my_service设置开机自启验证 Systemd 兼容性systemctl cat my_service检查自动生成的单元文件管理服务生命周期systemctl start/stop/restart my_service控制服务运行状态通过遵循上述步骤,您可以在 Systemd 系统中安全地使用 SysV Init 脚本,同时享受 Systemd 的监控和管理功能。但长期来看,仍建议迁移到原生 Systemd 服务单元(.service 文件),以充分利用资源控制、依赖管理和自动重启等高级特性。
EM系列储能边缘智能网关
EM系列储能边缘智能网关是ZLG致远电子专为新能源储能系统设计的一款高性能、多接口通讯管理设备,可在储能系统应用中作为边缘EMS(能源管理系统)总控、通讯管理机、规约转换器或BAU(电池管理总控)使用。该系列产品集成丰富的外设接口,支持各类BMS、PCS、空调、电表、屏显等设备的通讯传输,且软件上支持RT-Linux、Ubuntu等操作系统,支持IEC-61850/IEC-104/EtherCAT等专用协议,可广泛满足各类储能系统的本地能源管理应用需求。
-
嵌入式
+关注
关注
5188文章
20177浏览量
329271 -
脚本
+关注
关注
1文章
407浏览量
29076 -
Ubuntu系统
+关注
关注
0文章
93浏览量
4531
发布评论请先 登录
Linux使用Systemd管理进程服务
如何在 Linux 上从 NetworkManager 切换为 systemd
现代 Linux 的五大初始化系统(1992-2015)
i.M8XMQ开发板如何通过Systemd服务实现应用自启
如何将imx8mpevk板的默认Systemd - Init Manager更改为Sysvinit?
走进Linux之systemd启动过程
Linux项目开发,你必须了解Systemd服务!
Systemd是什么?Systemd Service配置文件详解
systemd journal收集日志的三种方式
【技术分享】Systemd原生服务配置最佳实践(下)

【技术分享】正确编写SysV Init脚本以实现Systemd兼容(上)
评论