优先级怎么设?聊聊开机启动顺序那些事
你有没有遇到过这样的情况:系统一开机,某个服务就卡住不动,等半天才进入桌面;或者两个脚本抢着访问同一个文件,结果一个失败、一个异常;又或者明明配置好了自动启动,重启后却什么都没发生?这些问题背后,往往不是脚本写错了,而是启动顺序没理清,优先级没设对。
今天不讲虚的,我们就用一个真实可运行的镜像——“测试开机启动脚本”——来把 Ubuntu 系统里最让人头疼又最容易被忽略的环节:开机启动顺序与优先级设置,掰开揉碎讲清楚。全文没有一句空话,所有操作都在真实环境验证过,代码可复制、步骤可回溯、问题有解法。
你不需要是系统管理员,只要会敲几行命令、能看懂注释,就能真正掌握“什么时候启动、谁先谁后、为什么这样设”的底层逻辑。
1. 启动顺序不是玄学,而是有章可循的流程
很多人以为开机启动就是“把脚本丢进某个目录就完事”,其实 Ubuntu(特别是使用 SysV init 的老版本或兼容模式)的启动过程是一套严格分阶段执行的机制。理解这个流程,是设对优先级的前提。
1.1 Ubuntu SysV init 启动的四个关键阶段
系统从加电到登录界面,大致经历以下阶段:
- 内核加载与基础设备初始化:硬件识别、驱动加载、/proc /sys 挂载
- init 进程启动(PID 1):读取
/etc/inittab或默认运行级别(通常是runlevel 2或3) - 运行级别脚本执行:按
/etc/rc?.d/目录下符号链接的字母顺序,依次调用/etc/init.d/中的真实脚本 - 用户登录准备:启动 getty、显示登录界面或桌面环境
其中,第三阶段才是我们能干预的“启动脚本执行层”,而优先级,就体现在/etc/rc?.d/下那些以Sxx开头的链接名里。
1.2 优先级数字到底代表什么?
看这个常见命令:
sudo update-rc.d run.sh defaults 96这里的96就是优先级值。它不是越大越好,也不是越小越快,而是决定脚本在当前运行级别(如rc2.d)中被调用的先后顺序:
- 所有
Sxx链接按xx数值从小到大排序执行(S01→S99) S01类脚本最早执行(如S01procps,负责挂载 proc 文件系统)S99类脚本最晚执行(如S99rc.local,留给用户最后兜底)- 同一数值下,按字母顺序(a-z)执行;若冲突,系统会提示警告
关键提醒:优先级只影响同一运行级别内的执行顺序,不跨级别。比如
rc2.d和rc3.d是两套独立列表。Ubuntu 桌面版默认进入runlevel 2,服务器版常用runlevel 3。
1.3 什么情况下必须调优先级?
别盲目改数字。真正需要调整优先级的场景,只有三类:
- 依赖网络的服务:比如你的脚本要 curl 外部 API,就必须确保在
S10networking(通常为 10)之后运行 - 依赖文件系统挂载的服务:比如脚本要读
/home/ubuntu/trx/config.json,就得比S15mountall(挂载用户分区)更晚 - 避免资源争抢:比如两个脚本都要写同一个日志文件,可让一个设为
S80,另一个设为S85,错开执行时间
记住了:优先级的本质,是表达“我需要等谁做完,才能开始”。
2. 实战:用“测试开机启动脚本”镜像完成一次完整部署
我们不再纸上谈兵。下面全程基于镜像“测试开机启动脚本”环境操作,所有路径、权限、命令均来自真实验证。假设你的目标是:让/home/ubuntu/trx/bin/mywork这个程序,在系统联网完成后、桌面启动前,以 root 权限自动运行。
2.1 第一步:编写带标准头的启动脚本
注意,这不是普通 shell 脚本,而是符合 LSB(Linux Standard Base)规范的 init 脚本。头部注释不是摆设,update-rc.d会解析它来生成依赖关系。
# 创建并编辑脚本 cd /home/ubuntu nano run.sh粘贴以下内容(请逐字复制,尤其注意空格和换行):
#!/bin/sh ### BEGIN INIT INFO # Provides: run.sh # Required-Start: $local_fs $remote_fs $network $syslog # Required-Stop: $local_fs $remote_fs $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts mywork daemon # Description: runs /home/ubuntu/trx/bin/mywork with root privileges ### END INIT INFO case "$1" in start) echo "Starting mywork..." cd /home/ubuntu/trx # 使用 sudo -S 配合 echo 输入密码(仅用于测试环境,生产请改用 visudo 配置免密) echo "123456" | sudo -S ./bin/mywork > /var/log/mywork.log 2>&1 & ;; stop) echo "Stopping mywork..." pkill -f "mywork" ;; restart) $0 stop sleep 2 $0 start ;; *) echo "Usage: $0 {start|stop|restart}" exit 1 ;; esac exit 0为什么这样写?
Required-Start: $network告诉系统:必须等网络服务启动完再执行我Default-Start: 2 3 4 5表示在运行级别 2~5 都启用(覆盖桌面和服务器模式)start/stop/restart分支让脚本能被service run.sh start正常调用,方便调试- 日志重定向
> /var/log/mywork.log是排障第一手资料,千万别省
2.2 第二步:赋予执行权限并移入系统目录
chmod +x /home/ubuntu/run.sh sudo cp /home/ubuntu/run.sh /etc/init.d/ sudo chmod 755 /etc/init.d/run.sh注意:
/etc/init.d/下的脚本必须是root:root所有者且权限为755,否则update-rc.d会拒绝注册。
2.3 第三步:用 update-rc.d 注册,并设置合理优先级
现在最关键:设多少?
查看当前网络服务的优先级:
ls -l /etc/rc2.d/ | grep network # 输出类似:S10networking -> ../init.d/networking我们的脚本需要在网络之后,但不能太靠后(避免被
rc.local或桌面服务抢占资源)。稳妥起见,设为90:sudo update-rc.d run.sh defaults 90这条命令做了三件事:
- 在
/etc/rc0.d/到/etc/rc6.d/中创建对应K10run.sh(关机)和S90run.sh(启动)链接 - 根据
Default-Start自动选择rc2.d、rc3.d等目录 - 解析
Required-Start,确保依赖检查通过
如果看到
update-rc.d: error: ... missing LSB information,说明脚本头部注释格式错误,请回头检查### BEGIN INIT INFO块是否完整、缩进是否正确。- 在
2.4 第四步:立即测试,不等重启
别急着reboot。先手动触发一次,确认脚本能跑通:
sudo service run.sh start # 检查进程 ps aux | grep mywork # 查看日志 sudo tail -20 /var/log/mywork.log如果进程存在、日志有输出,说明脚本本身和权限都没问题。此时再执行:
sudo reboot重启后,等待约 30 秒,执行:
ps aux | grep mywork # 应该能看到正在运行的 mywork 进程成功!你的程序已真正融入系统启动流程。
3. 三种常用方式对比:选对方法,少踩 80% 的坑
虽然update-rc.d是最规范的方式,但实际中还有其他路径。我们用一张表说清它们的适用边界、风险点和优先级控制能力:
| 方式 | 是否支持优先级设置 | 依赖管理能力 | 适用场景 | 风险提示 |
|---|---|---|---|---|
/etc/init.d/+update-rc.d | 完全支持(Sxx数字) | 自动解析Required-Start | 生产环境、需精确控制依赖的服务 | 脚本必须符合 LSB 规范,头部注释缺一不可 |
/etc/rc.local | ❌ 无优先级(固定为S99) | ❌ 无显式依赖声明 | 快速验证、简单命令、兜底任务 | Ubuntu 20.04+ 默认禁用,需手动启用;exit 0必须在末尾,否则后续脚本不执行 |
桌面自启动(~/.config/autostart/) | ❌ 不适用(GUI 层) | ❌ 仅等 X11 启动 | 用户级图形程序(如托盘工具) | 仅对当前用户生效,root 权限脚本无法直接运行 |
为什么 rc.local 经常“失效”?
- Ubuntu 18.04+ 默认将
rc-local.service设为disabled- 即使启用,它也只在
multi-user.target末尾执行,早于graphical.target- 若你的脚本依赖
DISPLAY环境变量(比如要弹窗),在这里必然失败
所以结论很明确:只要不是纯用户级、纯图形界面的小工具,就别碰rc.local和桌面启动。老老实实用init.d + update-rc.d,稳、准、可维护。
4. 排查指南:当启动失败时,这五步帮你快速定位
再严谨的配置也可能出错。以下是真实环境中高频问题的排查路径,按顺序执行,90% 的问题能在 5 分钟内解决:
4.1 第一步:确认脚本是否被加载
# 查看 rc2.d 中是否有 S90run.sh 链接 ls -l /etc/rc2.d/ | grep run.sh # 应输出:S90run.sh -> ../init.d/run.sh如果没看到,说明update-rc.d未成功注册,回到第 2.3 步检查。
4.2 第二步:检查脚本语法与权限
# 测试脚本能否被 sh 解析(排除语法错误) sudo sh -n /etc/init.d/run.sh # 检查权限和所有者 ls -l /etc/init.d/run.sh # 应为:-rwxr-xr-x 1 root root ...4.3 第三步:手动模拟启动流程
# 用 init 脚本标准方式启动(等同于开机时调用) sudo /etc/init.d/run.sh start # 观察终端输出,比日志更直接常见报错:
sudo: no tty present→sudoers未配置requiretty,或密码输入方式错误Permission denied→mywork二进制文件缺少执行权限:chmod +x /home/ubuntu/trx/bin/mywork
4.4 第四步:查看系统启动日志
# 过滤所有与 run.sh 相关的日志 sudo journalctl -b | grep -i "run.sh\|mywork" # 或查看传统 syslog sudo cat /var/log/syslog | grep -i "run.sh"重点关注Failed to start、Permission denied、No such file类错误。
4.5 第五步:验证依赖服务是否就绪
你的脚本声明了依赖$network,那就确认 networking 服务确实启动成功了:
systemctl status networking # 或查看其 rc2.d 链接 ls -l /etc/rc2.d/ | grep networking如果networking本身启动失败,你的脚本永远不会被执行——这就是依赖管理的价值。
5. 进阶建议:让启动更健壮、更安全、更易维护
做到“能用”只是起点。在真实项目中,还需考虑这些工程化细节:
5.1 密码明文?绝不允许!
示例脚本中echo "123456"是为了演示,生产环境必须删除。正确做法是:
- 编辑
/etc/sudoers(用sudo visudo安全打开) - 添加一行:
ubuntu ALL=(ALL) NOPASSWD: /home/ubuntu/trx/bin/mywork - 脚本中改为:
sudo /home/ubuntu/trx/bin/mywork > /var/log/mywork.log 2>&1 &
这样既免密,又最小化授权范围,符合最小权限原则。
5.2 加入健康检查与自动恢复
在start分支末尾添加:
# 检查进程是否存活,5秒后重试一次 if ! pgrep -f "mywork" > /dev/null; then echo "mywork failed to start, retrying..." >> /var/log/mywork.log sleep 5 sudo /home/ubuntu/trx/bin/mywork > /var/log/mywork.log 2>&1 & fi5.3 日志轮转,避免磁盘占满
创建/etc/logrotate.d/mywork:
/var/log/mywork.log { daily missingok rotate 30 compress delaycompress notifempty create 644 root root }6. 总结:优先级不是数字游戏,而是系统思维的体现
我们从一个看似简单的“开机启动”问题出发,一路拆解到启动流程、优先级原理、脚本规范、实操部署、故障排查和工程优化。你会发现:
- 设优先级,本质是在绘制一张服务依赖图:谁是基石,谁是上层建筑,谁必须等谁
update-rc.d不是命令,而是一套契约:你承诺遵守 LSB 规范,系统才承诺按你期望的顺序执行- 能
reboot成功,不等于部署完成:日志、进程监控、异常恢复,才是生产级的标配
下次再看到S90xxx,别再只把它当成一个数字。它是系统启动交响乐中的一个音符,而你,正握着指挥棒。
--- > **获取更多AI镜像** > > 想探索更多AI镜像和应用场景?访问 [CSDN星图镜像广场](https://ai.csdn.net/?utm_source=mirror_blog_end),提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。