service 命令详解与参数实测(Ubuntu 22.04)
基于 Ubuntu 22.04(init-system-helpers 1.62)逐参数实测 service:用法提示与 --version、--status-all 的 [+]/[-]/[?] 符号与 stderr 细节、status 退出码 0/3、start/stop/restart/reload/force-reload 全生命周期、不支持 reload 的报错形态、--full-restart、直接调 /etc/init.d 脚本等价性、crond 拼写差异、LSB init 脚本骨架、systemctl 对照与 policy-rc.d 处置。
service 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 service 的每一个常用参数——无参数与
--help的用法提示、--version、--status-all的[+]/[-]/[?]状态符号解读、status的 LSB 退出码(0/3)、start/stop/restart/reload/force-reload完整生命周期、不支持 reload 的服务报错形态、--full-restart、直接调用/etc/init.d/脚本的等价性、unrecognized service报错与 crond 拼写差异、SysV init 脚本骨架解读,以及 systemctl 的对照与自动转发,全部命令与回显已在 Ubuntu 22.04(init-system-helpers 1.62)实测通过。
一、环境信息
本文所有命令与回显均在以下环境实测采集,读者在相同版本的 Ubuntu 上可直接复现:
| 项目 | 实测值 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS |
| 架构 | x86_64 |
| service 所属包 | init-system-helpers 1.62 |
| 被管理的演示服务 | cron 3.0pl1-137ubuntu3、atd 3.2.5-1ubuntu1 |
| init 脚本目录 | /etc/init.d/ |
| 演示目录 | /root/demo/service |
环境确认命令与实测回显如下:
$ head -2 /etc/os-release
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
$ uname -m
x86_64
$ dpkg -l init-system-helpers cron | tail -2
ii cron 3.0pl1-137ubuntu3 amd64 process scheduling daemon
ii init-system-helpers 1.62 all helper tools for all init systems
$ ls -l /usr/sbin/service
-rwxr-xr-x 1 root root 9097 Feb 15 2022 /usr/sbin/service
$ ls /etc/init.d/
apparmor
atd
cron
dbus
hwclock.sh
kmod
procps
rsync
ssh
sysstat
udev
💡 说明:service 本身是一个 shell 脚本(
/usr/sbin/service),它的职责是「包装」/etc/init.d/目录下的 SysV init 脚本,让管理员用统一的语法service 服务名 动作来启停服务,而不必记住每个脚本的具体路径。本文用 cron 和 atd 两个真实守护进程做演示对象。
二、命令概览
2.1 语法与测试数据
service [--status-all] [service_name [command | --full-restart]]
参数结构解读:
--status-all:一次性列出/etc/init.d/下全部脚本的状态(运行中[+]、已停止[-]、不支持状态查询[?]);service_name:服务名,即/etc/init.d/下的脚本文件名(如cron、atd);command:动作,由目标 init 脚本自己决定支持哪些,通用约定为start/stop/restart/reload/force-reload/status;--full-restart:放在服务名之后,等价于对该服务执行一次完整的 restart。
按下面的步骤准备演示环境(确保 cron 与 atd 两个服务可用,且初始处于停止态):
$ mkdir -p /root/demo/service
$ apt-get install -y -qq cron at; echo rc=$?
rc=0
$ service cron status
* cron is not running
$ service atd status
* atd is not running
💡 说明:service 管理的是「服务进程」,不负责开机自启(那是 update-rc.d 或 systemctl enable 的职责,相关内容见本专栏《update-rc.d 命令详解与参数实测(Ubuntu 22.04)》篇)。
2.2 --help 全量输出(逐行照抄)
service 是脚本工具,--help 只回显一行用法说明(不带参数时的提示与之相同,见 3.1 节):
$ service --help
Usage: service < option > | --status-all | [ service_name [ command | --full-restart ] ]
要点解读:
< option >指脚本级选项,实测可用的只有--status-all(另见--version,3.2 节);[ service_name [ command | --full-restart ] ]:先服务名、后动作;--full-restart出现在「动作」的位置;- 用法行中
< >与[ ]是帮助文本的书写习惯(必选/可选),不是实际要输入的字符。
2.3 版本信息
$ service --version
service ver. 1.62
service 的版本与所属包 init-system-helpers 一致(dpkg 查询回显见第一章);命令本身没有更长的版本说明输出。
三、参数逐项实测
3.1 无参数:用法提示
不带任何参数直接运行 service,打印用法并返回退出码 1:
$ service; echo rc=$?
Usage: service < option > | --status-all | [ service_name [ command | --full-restart ] ]
rc=1
💡 要点:退出码 1 提示「参数不足」,适合在脚本里用
service ... || echo "参数错误"这类模式做前置校验;与--help的区别仅在于后者退出码为 0。
3.2 --version:打印自身版本
$ service --version; echo rc=$?
service ver. 1.62
rc=0
💡 要点:版本号 1.62 与
dpkg -l init-system-helpers中的版本一致,写运维文档时可用此命令快速标注工具版本。
3.3 --status-all:全量服务状态巡检
--status-all 遍历 /etc/init.d/ 下所有脚本并逐个查询状态。实测(服务全部未启动时):
$ service --status-all
[ - ] apparmor
[ - ] atd
[ - ] cron
[ - ] dbus
[ ? ] hwclock.sh
[ ? ] kmod
[ - ] procps
[ - ] rsync
[ + ] ssh
[ - ] sysstat
[ + ] udev
状态符号解读(这是本命令最有价值的知识点):
| 符号 | 含义 | 判定依据 |
|---|---|---|
[ + ] | 服务正在运行 | init 脚本支持 status 动作,且 status 退出码为 0 |
[ - ] | 服务未运行 | init 脚本支持 status 动作,且 status 退出码非 0(典型为 3) |
[ ? ] | 状态未知 | init 脚本里没有 status) 分支,service 无法查询(如 hwclock.sh、kmod 属于一次性配置脚本,无常驻进程可查) |
启动 cron 与 atd 之后再巡检一次,可以看到 [ - ] 变为 [ + ]:
$ service cron start
* Starting periodic command scheduler cron
...done.
$ service atd start
* Starting deferred execution scheduler atd
...done.
$ service --status-all
[ - ] apparmor
[ + ] atd
[ + ] cron
[ - ] dbus
[ ? ] hwclock.sh
[ ? ] kmod
[ - ] procps
[ - ] rsync
[ + ] ssh
[ - ] sysstat
[ + ] udev
💡 要点一:
[ ? ]行走的是 stderr,[ + ]/[ - ]行走 stdout——这是 service 脚本的固定行为。因此service --status-all | grep xxx时[ ? ]行会绕过管道直接打到终端上,看起来像「grep 没过滤干净」,正确姿势是先2>/dev/null丢弃 stderr(实测见 4.3 节)。💡 要点二:
[ + ]/[ - ]的判定完全依赖 init 脚本的 status 退出码(LSB 约定),不解析任何输出文本,所以 status 动作写得规范的脚本都能被正确巡检。
3.4 status:查询单个服务状态与 LSB 退出码
status 是日常巡检最常用的动作。服务未运行时:
$ service cron status; echo rc=$?
* cron is not running
rc=3
服务运行中时:
$ service cron status; echo rc=$?
* cron is running
rc=0
$ ps -ef | grep [c]ron
root 20984 1 0 23:27 ? 00:00:00 /usr/sbin/cron -P
💡 要点:退出码遵循 LSB(Linux Standard Base)init 脚本约定——
0表示运行中、3表示未运行、4表示状态未知(服务名不存在等场景实际返回 1,见 3.13 节)。写巡检脚本时必须判退出码而不是匹配输出文本,例如service cron status >/dev/null 2>&1 || echo "cron 未运行"。输出行首的*是/lib/lsb/init-functions中status_of_proc的日志前缀。
3.5 start:启动服务
$ service cron start; echo rc=$?
* Starting periodic command scheduler cron
...done.
rc=0
$ service cron status; echo rc=$?
* cron is running
rc=0
输出共两行:第一行 log_daemon_msg 打印动作描述,第二行 log_end_msg 0 打印 ...done.。重复 start 一个已运行的服务是幂等的:
$ service cron start; echo rc=$?
* Starting periodic command scheduler cron
...done.
rc=0
💡 要点:start 的实际执行者是 init 脚本里的
start_daemon -p $PIDFILE $DAEMON(LSB 工具函数),它借助 PID 文件/var/run/crond.pid判断进程是否已存在,因此重复 start 不会拉起第二个守护进程。
3.6 stop:停止服务
$ service cron stop; echo rc=$?
* Stopping periodic command scheduler cron
...done.
rc=0
$ service cron status; echo rc=$?
* cron is not running
rc=3
💡 要点:stop 通过
killproc -p $PIDFILE $DAEMON(LSB 工具函数)按 PID 文件找到进程发 SIGTERM,并清理 PID 文件;cron 的 init 脚本在 stop 成功后会显式rm -f $PIDFILE。
3.7 restart:重启服务
$ service cron restart; echo rc=$?
* Restarting periodic command scheduler cron
* Stopping periodic command scheduler cron
...done.
* Starting periodic command scheduler cron
...done.
rc=0
$ service cron status; echo rc=$?
* cron is running
rc=0
💡 要点:从回显能看出 cron 的 restart 实现就是「先 stop 后 start」(init 脚本里
$0 stop; $0 start);服务在 restart 过程中存在短暂空窗,对连续性敏感的场景应优先考虑 reload(见 3.8 节)。
3.8 reload 与 force-reload:平滑重载
reload 要求服务「不中断进程、重新读取配置」。cron 的 init 脚本把 reload 和 force-reload 归到同一分支,而 cron 守护进程本身每分钟都会自动重载 crontab,所以这里只是确认性动作(回显成功、进程不动):
$ service cron reload; echo rc=$?
* Reloading configuration files for periodic command scheduler cron
...done.
rc=0
$ service cron status; echo rc=$?
* cron is running
rc=0
$ service cron force-reload; echo rc=$?
* Reloading configuration files for periodic command scheduler cron
...done.
rc=0
配合链式命令可一次观察「reload 前后状态不变」:
$ service cron status && service cron reload && service cron status; echo rc=$?
* cron is running
* Reloading configuration files for periodic command scheduler cron
...done.
* cron is running
rc=0
💡 要点:reload 与 restart 的核心差异是进程 PID 不变(reload 不杀进程);
force-reload的约定语义是「能 reload 就 reload,不能 reload 则 restart」,多数脚本把它与 reload 合并处理。
3.9 不支持 reload 的服务:报错形态
并非所有 init 脚本都实现了 reload 分支。atd 的 init 脚本只有 {start|stop|restart|force-reload|status},对它执行 reload 会得到 init 脚本的用法提示、退出码 1:
$ service atd reload; echo rc=$?
Usage: /etc/init.d/atd {start|stop|restart|force-reload|status}
rc=1
需要重载效果时只能用 restart 替代(实测重启 atd):
$ service atd restart; echo rc=$?
* Stopping deferred execution scheduler atd
...done.
* Starting deferred execution scheduler atd
...done.
rc=0
$ service atd status; echo rc=$?
* atd is running
rc=0
💡 要点:
Usage: /etc/init.d/服务名 {支持的动词列表}是「脚本不认识这个动作」的标准报错形态;报错里直接列出了该脚本支持的全部动作,比查文档更快。注意 rc=1 而非 2——这是 atd 脚本自身的返回值,不同脚本对「未知动作」的退出码可能略有差异。
3.10 --full-restart:服务名后的完整重启
--full-restart 写在服务名之后,等价于对该服务执行 restart:
$ service cron --full-restart; echo rc=$?
* Stopping periodic command scheduler cron
...done.
* Starting periodic command scheduler cron
...done.
rc=0
$ service cron status
* cron is running
它必须跟在服务名后面——单独使用会被当成服务名解析,报 unrecognized service:
$ service --full-restart; echo rc=$?
--full-restart: unrecognized service
rc=1
💡 要点:这是 service 用法里最容易踩的位置参数陷阱:
--full-restart不是全局选项,而是「动作」位置的关键字。日常运维直接用 restart 即可,--full-restart仅在需要与 Red Hat 风格脚本保持一致的脚本里偶有出现。
3.11 省略动作:service cron(无动作参数)
只给服务名不给动作时,service 把空动作转交给 init 脚本,后者打印自己的用法、退出码 2:
$ service cron; echo rc=$?
* Usage: /etc/init.d/cron {start|stop|status|restart|reload|force-reload}
rc=2
💡 要点:这条「报错」其实是快速查看某服务支持哪些动作的捷径——不用翻脚本源码,一条命令列出 cron 支持
start|stop|status|restart|reload|force-reload六个动作。退出码 2 是 LSB 约定的「用法错误」。
3.12 直接调用 /etc/init.d/ 脚本(等价性对照)
service 本质上是 /etc/init.d/ 脚本的包装,直接执行脚本效果完全一致:
$ /etc/init.d/cron status; echo rc=$?
* cron is running
rc=0
$ /etc/init.d/cron restart; echo rc=$?
* Restarting periodic command scheduler cron
* Stopping periodic command scheduler cron
...done.
* Starting periodic command scheduler cron
...done.
rc=0
$ /etc/init.d/cron status; echo rc=$?
* cron is running
rc=0
💡 要点:两者输出逐字相同。差别只有两点:一是 service 写法更短且记忆负担小;二是在 systemd 引导的系统上 service 会自动把请求转发给 systemctl(见 3.15 节),而直接调脚本永远走脚本本身。编写跨机器复用的脚本时推荐 service 形态。
3.13 不存在的服务:unrecognized service 与拼写差异
对 /etc/init.d/ 下不存在的服务名执行任何动作,service 直接拦截报错、退出码 1:
$ service nosuchsvc status; echo rc=$?
nosuchsvc: unrecognized service
rc=1
$ service crond status; echo rc=$?
crond: unrecognized service
rc=1
💡 要点一:
xxx: unrecognized service的判断发生在 service 层(找不到/etc/init.d/xxx),根本不会触达任何脚本。💡 要点二:
crond是 Red Hat/CentOS 系的服务名,Ubuntu/Debian 系叫 cron(没有 d 后缀);从 CentOS 迁移过来的运维经常在这里翻车。同理 Red Hat 系的定时任务配置目录/var/spool/cron/用户名在 Ubuntu 上是/var/spool/cron/crontabs/用户名。
3.14 SysV init 脚本骨架解读
理解 service 的行为钥匙是 init 脚本本身。以 /etc/init.d/cron 为例,先看头部 29 行(LSB 头 + 变量定义):
$ sed -n "1,29p" /etc/init.d/cron
#!/bin/sh
# Start/stop the cron daemon.
#
### BEGIN INIT INFO
# Provides: cron
# Required-Start: $remote_fs $syslog $time
# Required-Stop: $remote_fs $syslog $time
# Should-Start: $network $named slapd autofs ypbind nscd nslcd winbind sssd
# Should-Stop: $network $named slapd autofs ypbind nscd nslcd winbind sssd
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: Regular background program processing daemon
# Description: cron is a standard UNIX program that runs user-specified
# programs at periodic scheduled times. vixie cron adds a
# number of features to the basic UNIX cron, including better
# security and more powerful configuration options.
### END INIT INFO
PATH=/bin:/usr/bin:/sbin:/usr/sbin
DESC="cron daemon"
NAME=cron
DAEMON=/usr/sbin/cron
PIDFILE=/var/run/crond.pid
SCRIPTNAME=/etc/init.d/"$NAME"
test -f $DAEMON || exit 0
. /lib/lsb/init-functions
再看 case "$1" 动作分发骨架(节选自同一脚本,与实测输出一致):
case "$1" in
start) log_daemon_msg "Starting periodic command scheduler" "cron"
start_daemon -p $PIDFILE $DAEMON -P $EXTRA_OPTS
log_end_msg $?
;;
stop) log_daemon_msg "Stopping periodic command scheduler" "cron"
killproc -p $PIDFILE $DAEMON
RETVAL=$?
[ $RETVAL -eq 0 ] && [ -e "$PIDFILE" ] && rm -f $PIDFILE
log_end_msg $RETVAL
;;
restart) log_daemon_msg "Restarting periodic command scheduler" "cron"
$0 stop
$0 start
;;
reload|force-reload) log_daemon_msg "Reloading configuration files for periodic command scheduler" "cron"
# cron reloads automatically
log_end_msg 0
;;
status)
status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $?
;;
*) log_action_msg "Usage: /etc/init.d/cron {start|stop|status|restart|reload|force-reload}"
exit 2
;;
esac
exit 0
逐段解读:
- LSB 头(
### BEGIN INIT INFO块):Provides声明服务名;Required-Start/Stop声明硬依赖($remote_fs文件系统就绪、$syslog、$time);Default-Start: 2 3 4 5声明默认在哪些运行级自启(这就是 update-rc.d 生成S01cron链接的依据,见本专栏 update-rc.d 篇);Default-Stop为空表示关机运行级不需要专门停止动作。 - 变量区:
DAEMON是守护进程真实二进制,PIDFILE是进程号文件,service 的 status/stop 全靠它定位进程。 . /lib/lsb/init-functions:引入 Debian 的 LSB 工具库,log_daemon_msg/start_daemon/killproc/status_of_proc/log_action_msg全部来自这里——这就是为什么所有服务的回显风格高度统一(*前缀 +...done.)。- case 分支:
$1是 service 转交的动作名;*)兜底分支打印用法并退出 2(对应 3.11 节实测的 rc=2);cron 的 reload 分支只有一句注释「cron reloads automatically」加log_end_msg 0——守护进程每次唤醒都会重读 crontab,所以 reload 天然成立。
💡 技术拓展:Linux 的 init 系统(PID 1 进程,负责引导与管理服务)不止 SysV init 一种,主流实现对比如下——
init 系统 原理/特点 典型应用 SysV init /etc/init.d/shell 脚本 +/etc/rcN.d/软链接,按运行级串行启停传统发行版、极简环境(本文实测的脚本层) systemd unit 文件 + socket 激活 + cgroup 跟踪 + 并行启动,现代事实标准 Ubuntu 16.04+、RHEL 7+ Upstart 基于事件的 init 系统 Ubuntu 6.10~14.10,15.04 起弃用 OpenRC 依赖感知、无 D-Bus 强依赖的轻量 init Gentoo、Alpine Ubuntu 22.04 默认以 systemd 引导,但完整保留了
/etc/init.d脚本层与 service/update-rc.d 工具链;cron 官方包同时提供 SysV 脚本与 systemd unit(/lib/systemd/system/cron.service),两套机制指向同一个守护进程。
3.15 systemctl 对照:systemd 时代的平行命令
Ubuntu 22.04 桌面版/服务器版由 systemd 引导,此时管理服务的首选是 systemctl,service 会自动转发请求。在一台以 systemd 引导的标准 Ubuntu 22.04 服务器上实测 service dbus status(dbus 是 systemd 环境必有的服务),回显已是 systemd 单元格式:
$ service dbus status; echo rc=$?
● dbus.service - D-Bus System Message Bus
Loaded: loaded (/lib/systemd/system/dbus.service; static)
Active: active (running) since Thu 2026-10-15 18:07:39 UTC; 5h 20min ago
TriggeredBy: ● dbus.socket
Docs: man:dbus-daemon(1)
Main PID: 41 (dbus-daemon)
Tasks: 1 (limit: 7196)
Memory: 808.0K
CPU: 121ms
CGroup: /system.slice/dbus.service
rc=0
等价的 systemctl 原生写法输出完全相同:
$ systemctl status dbus.service --no-pager -l
● dbus.service - D-Bus System Message Bus
Loaded: loaded (/lib/systemd/system/dbus.service; static)
Active: active (running) since Thu 2026-10-15 18:07:39 UTC; 5h 20min ago
TriggeredBy: ● dbus.socket
Docs: man:dbus-daemon(1)
Main PID: 41 (dbus-daemon)
Tasks: 1 (limit: 7196)
Memory: 808.0K
CPU: 121ms
CGroup: /system.slice/dbus.service
服务总览的对照(systemctl 侧,输出节选):
$ systemctl list-units --type=service --no-pager
UNIT LOAD ACTIVE SUB DESCRIPTION
dbus.service loaded active running D-Bus System Message Bus
getty@tty1.service loaded active running Getty on tty1
networkd-dispatcher.service loaded active running Dispatcher daemon for systemd-networkd
systemd-binfmt.service loaded active exited Set Up Additional Binary Formats
systemd-journal-flush.service loaded active exited Flush Journal to Persistent Storage
两套工具的横向对比:
| 对比项 | service(本文主角) | systemctl |
|---|---|---|
| 语法 | service <服务名> <动作> | systemctl <动作> <服务名>(语序相反) |
| 管理对象 | 仅 /etc/init.d/ 传统脚本 | 全部 unit(.service/.socket/.timer/.mount 等) |
| 状态总览 | --status-all | list-units --type=service |
| 状态输出 | * cron is running(LSB 风格) | Active: active (running) 多行详情(含 PID/内存/CGroup) |
| 开机自启 | 不管(需配 update-rc.d) | enable/disable 原生支持 |
| 转发行为 | systemd 引导时自动转发给 systemctl | 无转发,原生操作 |
⚠️ 注意:在未以 systemd 引导的环境(极简安装、救援模式、chroot 等)调用 systemctl 会连接失败:
$ systemctl status cron; echo rc=$? System has not been booted with systemd as init system (PID 1). Can't operate. Failed to connect to bus: Host is down rc=1而 service 走
/etc/init.d/脚本依然可用——这正是 service 在排障场景下的不可替代价值:它不依赖 systemd 总线存活。
💡 要点:两套工具的选择口诀——「日常用 systemctl,救急用 service」。systemd 总线(dbus/systemd 自身)异常时 systemctl 一律报
Failed to connect to bus,而 service 直接执行 init 脚本照常工作;重启服务器前用service cron status这类命令做最后确认,是排障流程里的可靠兜底。
四、实战示例
4.1 场景一:服务启动三步验证(start → status → 进程确认)
标准启动闭环:启动、查状态、确认进程真实存在,三步都有回显才算数。
$ service cron start
* Starting periodic command scheduler cron
...done.
$ service cron status
* cron is running
$ ps -ef | grep [c]ron
root 20984 1 0 23:27 ? 00:00:00 /usr/sbin/cron -P
验证要点:status 退出码为 0(service cron status >/dev/null 2>&1; echo $? 得 0),ps 能看到 /usr/sbin/cron 进程且 PPID 为 1(守护进程已脱离终端)。
4.2 场景二:配置变更后的平滑重载链
修改 /etc/crontab 或 /etc/cron.d/ 下的定时任务后,用链式命令一次完成「确认运行 → 重载 → 确认仍在运行」:
$ service cron status && service cron reload && service cron status; echo rc=$?
* cron is running
* Reloading configuration files for periodic command scheduler cron
...done.
* cron is running
rc=0
任一环失败(&& 短路)都会让 rc 非 0,可直接用于自动化脚本的条件判断。cron 场景其实可以省略 reload(守护进程每分钟自动重载),但对不支持自动重载的服务这一模式同样适用。
4.3 场景三:巡检——找出所有未运行的服务
--status-all 配合 grep 过滤 [ - ] 行(丢弃 stderr 避免 [ ? ] 行混入,见 3.3 节要点):
$ service --status-all 2>/dev/null | grep '\[ - \]'
[ - ] apparmor
[ - ] atd
[ - ] dbus
[ - ] procps
[ - ] rsync
[ - ] sysstat
这行命令是巡检基线的雏形:把「应当常驻」的服务名与之比对,缺失即告警。反之把模式换成 '\[ + \]' 即列出运行中的服务。
4.4 场景四:服务异常时的重启排障闭环
现象是 cron 不跑任务时,按「查状态 → 停 → 确认停 → 启 → 确认启」的顺序排除僵尸进程/配置卡死:
$ service cron status
* cron is not running
$ service cron restart
* Restarting periodic command scheduler cron
* Stopping periodic command scheduler cron
...done.
* Starting periodic command scheduler cron
...done.
$ service cron status
* cron is running
$ service cron reload
* Reloading configuration files for periodic command scheduler cron
...done.
经验:status 显示 not running 却仍有残留进程时(status 依据 PID 文件判断),restart 会先 stop 清理再拉起,比盲目 start 更干净。
4.5 场景五:apt 安装后服务未自动启动的处置
受 Debian 的启动策略机制约束,apt-get install cron 时可能看到服务未被拉起的提示(实测安装回显节选):
$ apt-get install -y -qq cron at
Setting up at (3.2.5-1ubuntu1) ...
invoke-rc.d: could not determine current runlevel
invoke-rc.d: policy-rc.d denied execution of start.
Setting up cron (3.0pl1-137ubuntu3) ...
invoke-rc.d: could not determine current runlevel
invoke-rc.d: policy-rc.d denied execution of start.
原因是系统存在 /usr/sbin/policy-rc.d 策略文件(Debian 官方机制,云平台预装系统与最小化安装环境常用它禁止包管理器自动启服务),实测内容:
$ cat /usr/sbin/policy-rc.d
#!/bin/sh
exit 101
退出码 101 表示「拒绝任何自动启动请求」。处置方式:手动执行一次 start 即可,自启链接(update-rc.d/enable)不受影响:
$ service cron start
* Starting periodic command scheduler cron
...done.
$ service cron status
* cron is running
五、注意事项与常见问题
Q1:执行 service crond status 报 crond: unrecognized service,cron 坏了吗?
没有坏,是服务名拼写差异。Ubuntu/Debian 系统的定时服务名叫 cron(service cron status),Red Hat/CentOS 系才叫 crond。判断服务名的最快方法是 ls /etc/init.d/ 或 service --status-all。
Q2:service --status-all | grep '\[ - \]' 为什么 [ ? ] 行还是出现在屏幕上,grep 是不是没生效?
grep 生效了。[ ? ] 行由 service 脚本写到 stderr(echo " [ ? ] $SERVICE" >&2),[ + ]/[ - ] 行才走 stdout 参与管道。加 2>/dev/null 丢弃 stderr 即可只看 stdout 结果(实测见 4.3 节)。顺带说明:受控实验 printf " [ - ] apparmor\n [ ? ] hwclock\n" | grep '\[ - \]' 只会留下 [ - ] 行,可自证模式本身没有问题。
Q3:status 动作的退出码有哪些,巡检脚本该怎么写?
LSB 约定:0 运行中、3 未运行、4 状态未知;service 层对不存在的服务返回 1(unrecognized service),init 脚本对未知动作多返回 1 或 2。巡检脚本以退出码为准:service cron status >/dev/null 2>&1 || service cron start。实测退出码:running 时 0、not running 时 3、unrecognized service 时 1、service cron(缺动作)时 2。
Q4:service cron reload 显示 ...done. 但配置好像没生效?
先确认服务是否实现了真正的 reload——查看 init 脚本的 reload 分支(grep -A2 'reload)' /etc/init.d/cron)。cron 的 reload 分支只有注释与成功标记,因为 cron 每分钟自动重读 crontab,无需额外信号;而 atd 干脆没有 reload 分支,service atd reload 直接回 Usage: /etc/init.d/atd {start|stop|restart|force-reload|status}(3.9 节实测)。拿不准时用 restart 最稳。
Q5:在标准 Ubuntu 22.04 服务器上应该用 service 还是 systemctl?
日常管理优先 systemctl(信息全、支持 enable/timer 等系统能力);service 的价值在两个场景:一是命令更短、脚本兼容性更好(自动转发,见 3.15 节实测 service dbus status 与 systemctl status dbus.service 输出一致);二是 systemd 总线异常的救援场景(systemctl 报 System has not been booted with systemd as init system (PID 1). Can't operate. 时 service 仍可操作 init 脚本)。
Q6:apt 装完服务后为什么没有自动启动?
安装脚本经 invoke-rc.d 请求启动时被 /usr/sbin/policy-rc.d 拦截(回显 invoke-rc.d: policy-rc.d denied execution of start.),该文件 exit 101 表示一律拒绝。这是 Debian 官方的启动策略机制,常见于云平台预装系统与最小化安装环境。处置:手动 service <服务> start;若想恢复安装即启动,删除或改名该策略文件即可。
Q7:同一动作,service cron status 和 /etc/init.d/cron status 有区别吗?
输出与退出码完全一致(3.12 节实测对照)。区别在 systemd 引导的系统上:service 会转发给 systemctl,直接调脚本永远走脚本本身。日常推荐 service 写法。
六、参考链接
- service(8) 手册页(Ubuntu Jammy):https://manpages.ubuntu.com/manpages/jammy/en/man8/service.8.html
- invoke-rc.d(8) 手册页(安装期启动策略):https://manpages.ubuntu.com/manpages/jammy/en/man8/invoke-rc.d.8.html
- update-rc.d(8) 手册页(开机自启配套工具):https://manpages.ubuntu.com/manpages/jammy/en/man8/update-rc.d.8.html
- LSB Core 规范之 Init Script Actions(退出码约定):https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
- systemd 官方文档:https://www.freedesktop.org/wiki/Software/systemd/
- cron 守护进程相关:本专栏《crontab 命令详解与参数实测(Ubuntu 22.04)》《at 命令详解与参数实测(Ubuntu 22.04)》