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 处置。

Linux命令Linux系统UbuntuUbuntu 22.04命令行进程管理2026/10/16

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/ 软链接,按运行级串行启停传统发行版、极简环境(本文实测的脚本层)
systemdunit 文件 + socket 激活 + cgroup 跟踪 + 并行启动,现代事实标准Ubuntu 16.04+、RHEL 7+
Upstart基于事件的 init 系统Ubuntu 6.10~14.10,15.04 起弃用
OpenRC依赖感知、无 D-Bus 强依赖的轻量 initGentoo、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-alllist-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 写法。

六、参考链接