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

描述

 


 

嵌入式的ubuntu系统如何写好SysV Init脚本呢?与system服务又有什么差别呢?一起随着文章来探究吧。


 

嵌入式  问题背景

 

许多传统Linux服务仍使用SysV Init脚本(/etc/init.d/),但在Systemd系统中,这些脚本需通过systemd-sysv-generator换为原生服务单元。若脚本未遵循Linux Standard Base (LSB)规范,会导致以下问题:

  1. 服务启动/停止有误:缺失关键命令(如status)或错误退出码。
  2. 依赖混乱:未声明依赖关系,导致服务启动顺序错误。
  3. 日志污染:输出未重定向,干扰Systemd日志(journald)。


 

嵌入式  SysV Init脚本规范指南

 

1. 必须支持的标准命令脚本需实现以下命令:

  •  

1.case"$1" in2.start)3.# 启动服务逻辑4.;;5.stop)6.# 停止服务逻辑7.;;8.restart)9.# 建议直接调用自身stop再start,或委托给Systemd10.$0 stop11.$0 start12.;;13.status)14.# 返回服务运行状态(必需!)15.if pidof -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:# 生成依赖链接  sudo update-rc.d my_service defaults
# 移除依赖链接sudo update-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)$0 stop$0 start;;status)if pidof -x "$DAEMON" >/dev/null; thenecho "$NAME is running"exit0elseecho "$NAME is stopped"exit3fi;;*)echo "Usage: $0 {start|stop|restart|status}"exit1esac
exit0

3. 启用服务的两种方式3.1 传统 SysV 方法(兼容 Systemd)

  •  
  •  

# Debian/Ubuntusudo update-rc.d my_service defaults

3.2 Systemd 原生方法(推荐)

  •  
  •  
  •  
  •  

# 直接通过 Systemd 启用(等效于 update-rc.d)sudo systemctl enable my_serviceSystemd 会自动生成符号链接:Created symlink /etc/systemd/system/multi-user.target.wants/my_service.service → /lib/systemd/system/my_service.service.

4. 验证脚本兼容性4.1 检查 LSB 头部合规性

  •  
  •  

# Debian/Ubuntusudo insserv -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. 管理服务

  •  
  •  

# 启动服务sudo systemctl start my_service

  •  
  •  

# 查看状态systemctl status my_service

  •  
  •  

# 停止服务sudo systemctl stop my_service

  •  
  •  

# 重启服务sudo systemctl restart my_service

6. 关键注意事项6.1 避免冲突配置如果服务已存在 Systemd 原生单元文件(如/lib/systemd/system/my_service.service),需删除或禁用它,否则 SysV 脚本可能被忽略。6.2 禁用传统服务干扰停止并禁用旧版networking服务(如果存在):

  •  
  •  

systemctl stop networkingsudo systemctl disable networking

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等专用协议,可广泛满足各类储能系统的本地能源管理应用需求。

 

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分