news 2026/2/25 9:03:42

详解systemd与init.d区别,选择更适合的开机方式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
详解systemd与init.d区别,选择更适合的开机方式

详解systemd与init.d区别,选择更适合的开机方式

1. 启动机制的本质差异

1.1 init.d:线性执行的老派方式

init.d 是 SysV init 的实现,它把启动过程看作一条单行道:系统按固定顺序依次执行脚本,前一个没结束,后一个就只能等着。

它的核心逻辑藏在/etc/rc?.d/目录里。比如/etc/rc2.d/下的文件名S01gpio-init.sh中的S表示“start”,01是执行序号。系统会按数字从小到大,一个接一个地运行这些脚本。

每个脚本都是独立的黑盒,只认三个参数:startstoprestart。它不关心别的服务是否已就绪,也不管自己依赖的资源有没有准备好。如果某个脚本卡住或失败,整个启动流程可能就停在那里,排查起来像在迷宫里找出口。

这种模式简单直接,但就像用算盘处理大数据——能用,但效率和可控性都有限。

1.2 systemd:并行协作的现代引擎

systemd 则彻底重构了这套逻辑。它不再是一条线,而是一张网。每个服务被定义为一个unit(单元),最常见的就是.service文件,存放在/etc/systemd/system//lib/systemd/system/

关键在于,unit 文件里可以明确声明依赖关系。比如:

[Unit] Description=GPIO initialization After=multi-user.target Wants=network.target

这段配置告诉 systemd:“这个服务必须在 multi-user.target 完成之后启动,并且希望 network.target 也一起运行”。systemd 会自动解析这张依赖图,把没有依赖关系的服务并行拉起,把有先后顺序的排好队。

更进一步,它自带日志中枢journalctl,所有服务的输出都统一归档;支持失败自动重启、健康检查(watchdog)、资源限制等高级特性。它不是在“执行脚本”,而是在“管理服务生命周期”。

1.3 为什么不能只说“新旧”?——兼容层的真实作用

很多人误以为 Armbian 等系统是“在 systemd 和 init.d 之间二选一”。事实恰恰相反:systemd 是唯一的主角,init.d 只是一个被兼容的配角

当你在/etc/init.d/下放了一个脚本,并用update-rc.d gpio-init.sh defaults注册它时,systemd 并不会去调用传统的init程序。它会悄悄生成一个临时的 unit 文件,把你的脚本包装进去,然后像管理其他服务一样管理它。

你可以用这个命令验证:

systemctl status gpio-init.sh

你会看到输出中明确写着Loaded: loaded (/etc/init.d/gpio-init.sh; generated)。那个generated就是关键——说明这不是原生 unit,而是 systemd 动态生成的兼容封装。

这意味着,即使你写的是 init.d 脚本,最终的启动顺序、超时控制、日志归属,全由 systemd 决定。你只是在用老语法,跑在新引擎上。

2. 实战对比:从编写到调试的全流程

2.1 编写一个 GPIO 初始化脚本

无论选择哪种方式,脚本的核心逻辑是一样的。我们以点亮一个 LED 为例,创建/usr/local/bin/gpio-init.sh

#!/bin/bash # /usr/local/bin/gpio-init.sh # 导出并配置 GPIO 引脚 for pin in 6 7 8 9 10; do if [ ! -d "/sys/class/gpio/gpio${pin}" ]; then echo ${pin} > /sys/class/gpio/export sleep 0.1 fi done # 设置方向 echo "out" > /sys/class/gpio/gpio6/direction echo "in" > /sys/class/gpio/gpio7/direction echo "out" > /sys/class/gpio/gpio8/direction echo "out" > /sys/class/gpio/gpio9/direction echo "out" > /sys/class/gpio/gpio10/direction # 点亮 LED(假设 GPIO6 连接 LED) echo "1" > /sys/class/gpio/gpio6/value exit 0

注意两点:一是脚本放在/usr/local/bin/(标准可执行路径),二是加了简单的存在性检查和延时,避免因内核初始化未完成而报错。

2.2 方式一:用 init.d 兼容模式

给脚本加执行权限:

sudo chmod +x /usr/local/bin/gpio-init.sh

创建/etc/init.d/gpio-init(注意,这里不带.sh后缀,是惯例):

#!/bin/sh ### BEGIN INIT INFO # Provides: gpio-init # Required-Start: $local_fs $network # Required-Stop: $local_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Initialize GPIO pins # Description: Set up GPIO for LED and sensors ### END INIT INFO case "$1" in start) echo "Starting GPIO initialization..." /usr/local/bin/gpio-init.sh ;; stop) echo "Stopping GPIO initialization..." # 可选:添加清理逻辑,如关闭 LED echo "0" > /sys/class/gpio/gpio6/value 2>/dev/null || true ;; restart|force-reload) $0 stop $0 start ;; *) echo "Usage: $0 {start|stop|restart}" >&2 exit 3 ;; esac

注册并启用:

sudo update-rc.d gpio-init defaults sudo systemctl daemon-reload

此时,systemctl list-unit-files | grep gpio会显示gpio-init.service,状态为enabled,但类型是generated

2.3 方式二:用原生 systemd unit

创建/etc/systemd/system/gpio-init.service

[Unit] Description=GPIO Initialization Service Documentation=https://github.com/armbian/gpio-init After=multi-user.target Wants=local-fs.target [Service] Type=oneshot ExecStart=/usr/local/bin/gpio-init.sh RemainAfterExit=yes StandardOutput=journal StandardError=journal SysMaxUse=50M [Install] WantedBy=multi-user.target

关键字段说明:

  • Type=oneshot:表示这是一个只运行一次就退出的脚本。
  • RemainAfterExit=yes:告诉 systemd,即使脚本退出了,服务状态仍视为“active”,这对初始化类服务至关重要。
  • StandardOutput=journal:确保所有echo输出都能被journalctl捕获。

启用服务:

sudo systemctl daemon-reload sudo systemctl enable gpio-init.service

2.4 调试与验证:两套工具,一套真相

验证 PID 1 是否为 systemd:

ps -p 1 -o comm= # 输出应为:systemd

查看两个服务的状态:

# 查看 init.d 兼容服务 systemctl status gpio-init.service # 查看原生 systemd 服务 systemctl status gpio-init.service

你会发现,前者显示generated,后者显示loaded (/etc/systemd/system/gpio-init.service; enabled)

查看日志(这才是最大优势):

# 查看原生服务的完整日志 journalctl -u gpio-init.service -n 20 --no-pager # 查看所有启动阶段的日志 journalctl -b -u multi-user.target --no-pager

如果你的脚本里有echo "LED initialized",这条信息会清晰地出现在 journal 日志里,而 init.d 模式下,它可能直接被丢弃或混在系统日志里难以查找。

3. 关键决策点:什么情况下该选哪一种?

3.1 优先选择 systemd unit 的三大场景

场景一:需要精确控制启动时机

比如你的设备需要等网络完全就绪、NTP 时间同步完成后再初始化传感器。init.d 脚本只能粗略地写Required-Start: $network,但无法指定“等 DHCP 获取 IP 后”。而 systemd 可以:

[Unit] After=network-online.target Wants=network-online.target

network-online.target是一个专门设计的“网络真正可用”的信号点。

场景二:服务需要长期存活或自动恢复

如果你的脚本不只是初始化,还要持续监听 GPIO 中断或轮询状态,那么Type=simpleType=forking配合Restart=on-failure就非常必要。init.d 对此无能为力,它只负责“启动”,不管“活着”。

场景三:团队协作与可维护性要求高

一个.service文件是自描述的:它清楚地列出了依赖、执行路径、重启策略、日志设置。新同事接手时,看一个文件就能理解全部。而 init.d 脚本需要同时看脚本本身、update-rc.d的注册记录、/etc/rc?.d/的软链接,信息是割裂的。

3.2 init.d 模式仍有价值的两种情况

情况一:快速移植遗留脚本

你手头有一个为 Ubuntu 14.04(纯 init.d)编写的复杂脚本,里面包含了大量start|stop|status的分支逻辑和状态文件管理。直接重写为 unit 文件成本过高。此时,利用 systemd 的兼容层是最务实的选择——先让它跑起来,再逐步重构。

情况二:极简嵌入式环境,追求最小化

在某些资源极度受限的 ARM 设备上,你可能禁用了部分 systemd 功能,或者使用了systemd-sysv-generator的轻量模式。此时,init.d 脚本因其零依赖、零额外开销的特性,反而成为最可靠的选择。

但请注意,这并非推荐,而是权衡下的妥协。

4. 常见陷阱与避坑指南

4.1 “脚本不执行”的五大元凶

陷阱一:权限与路径错误

  • 错误:脚本放在/root/下,或没有+x权限。
  • 正解:脚本必须放在PATH包含的目录(如/usr/local/bin/),且ls -l显示rwxr-xr-x

陷阱二:环境变量缺失

  • 错误:脚本里用了$(pwd)~,但在 systemd 的干净环境中,HOMEPATH都被重置。
  • 正解:在 unit 文件中显式声明:
    [Service] Environment="PATH=/usr/local/bin:/usr/bin:/bin" Environment="HOME=/root"

陷阱三:内核 GPIO 接口未就绪

  • 错误:脚本在multi-user.target之前就尝试写/sys/class/gpio/,但内核模块还没加载。
  • 正解:添加Wants=sys-devices-platform-gpiochip0.device或更通用的After=sysinit.target

陷阱四:RemainAfterExit忘记设置

  • 错误:Type=oneshot但没设RemainAfterExit=yes,导致systemctl is-active gpio-init.service返回inactive,后续依赖服务无法启动。
  • 正解:初始化类服务,几乎都需要这一行。

陷阱五:日志被静默丢弃

  • 错误:脚本里echo "debug",但journalctl里找不到。
  • 正解:确认 unit 文件中有StandardOutput=journal,并且脚本没有重定向>/dev/null

4.2 一条命令,看清全局启动图谱

想一眼看懂整个系统的启动脉络?运行:

systemctl list-dependencies --all --reverse multi-user.target

这个命令会反向列出所有“想要”或“需要”multi-user.target的服务,也就是所有在多用户模式下会被拉起的单元。它会清晰地展示出你的gpio-init.service是如何被multi-user.target所包含,以及它又依赖哪些基础目标。

这比翻/etc/rc2.d/下几十个Sxx文件直观得多。

5. 总结:面向未来的启动方式选择

5.1 核心结论

  • 技术本质:Armbian 的启动管理器只有一个,就是 systemd。init.d 是它提供的一个向下兼容的翻译层,而非并列选项。
  • 工程实践:对于新开发的启动任务,无条件优先选用 systemd unit 文件。它提供了 init.d 无法企及的可靠性、可观测性和可维护性。
  • 演进路径:不要把 init.d 当作“简单”,而应视其为“过渡”。今天花 10 分钟写一个 unit 文件,能为你未来半年的调试省下数小时。

5.2 行动清单

  • 新项目:直接创建/etc/systemd/system/xxx.service,按本文模板填写。
  • 老项目:用systemctl status xxx.service检查当前状态,若为generated,则计划迁移。
  • 调试必做:养成journalctl -u xxx.service -f实时跟踪日志的习惯。
  • 避免踩坑:永远检查RemainAfterExitEnvironmentAfter=三个字段。

启动脚本不是系统里的一个“小配件”,它是系统稳定性的第一道防线。选择 systemd,就是选择一个有日志、有依赖、有保障的未来。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/19 21:33:53

4个维度掌握图像识别自动化:MaaFramework从入门到实践

4个维度掌握图像识别自动化:MaaFramework从入门到实践 【免费下载链接】MaaFramework 基于图像识别的自动化黑盒测试框架 | A automation black-box testing framework based on image recognition 项目地址: https://gitcode.com/gh_mirrors/ma/MaaFramework …

作者头像 李华
网站建设 2026/2/16 15:28:18

如何缓存VibeThinker-1.5B结果提升性能?实用技巧分享

如何缓存VibeThinker-1.5B结果提升性能?实用技巧分享 当你第一次在本地部署 VibeThinker-1.5B-WEBUI 镜像,点击“网页推理”按钮,输入 “Solve 2x 5 13” 并按下回车——几秒后,模型返回了清晰的解题步骤和最终答案。体验很流畅…

作者头像 李华
网站建设 2026/2/24 10:21:17

Open-AutoGLM避坑指南:新手常见问题一网打尽

Open-AutoGLM避坑指南:新手常见问题一网打尽 你刚下载完Open-AutoGLM,兴致勃勃连上手机,输入第一条指令——“打开微信发条朋友圈”,结果卡在黑屏、报错、adb devices空列表、模型返回乱码……别急,这不是你操作错了&…

作者头像 李华
网站建设 2026/2/24 22:33:27

3步解锁直播效率提升与智能互动:B站主播必备场控工具完全指南

3步解锁直播效率提升与智能互动:B站主播必备场控工具完全指南 【免费下载链接】Bilibili-MagicalDanmaku 【神奇弹幕】哔哩哔哩直播万能场控机器人,弹幕姬答谢姬回复姬点歌姬各种小骚操作,目前唯一可编程机器人 项目地址: https://gitcode.…

作者头像 李华
网站建设 2026/2/25 0:08:50

万物识别与TensorFlow模型对比:PyTorch生态优势解析

万物识别与TensorFlow模型对比:PyTorch生态优势解析 1. 什么是“万物识别”——中文通用场景下的真实能力 你有没有遇到过这样的情况:拍一张超市货架的照片,想立刻知道上面有哪些商品;或者给孩子辅导作业时,随手拍张…

作者头像 李华
网站建设 2026/2/8 9:32:59

不用买显卡!在线Jupyter快速启动Qwen3-1.7B方法

不用买显卡!在线Jupyter快速启动Qwen3-1.7B方法 你是不是也经历过这样的纠结:想试试最新发布的Qwen3-1.7B模型,但一看显存要求就退缩了——16G显存起步?RTX 4090?租云服务器怕超预算,本地跑又卡成PPT……别…

作者头像 李华