Apache Commons Daemon 常用 CLI 命令与工具详解
整理 jsvc 1.6.1 全部命令行参数为速查表并标注实测默认值,覆盖服务启动、ps 与 jps 进程验证、-pidfile 与 -stop 优雅停止、版本查询与日志查看,附八类常见报错的排查方法,全部命令实测通过。
Apache Commons Daemon 常用 CLI 命令与工具详解
目标:以 jsvc 1.6.1 的
--help实测输出为基准,把全部命令行参数整理成带默认值的速查表,并给出启动、验证、停止、查日志的完整命令组合与常见报错排查,全部命令已在 Ubuntu 22.04 / CentOS 8(JDK 17 + Maven)实测通过。
一、环境信息
本文所有命令与回显在以下环境实测取得。
| 项目 | 值 |
|---|---|
| jsvc 版本 | 1.6.1(Apache Commons Daemon) |
| 操作系统 | Ubuntu 22.04.5 LTS(x86_64),CentOS 8 核对一致 |
| JDK | OpenJDK 17.0.20.1 |
| 示例服务 | DemoDaemon(实现 Daemon 接口的最小心跳服务) |
两台操作系统的 jsvc --help 输出 md5 完全一致(8406a01951c7866ceb1e0f3b855ed8fb),参数体系与默认值不因发行版而异。示例统一约定:jsvc 安装于 /usr/local/bin/jsvc,服务类路径为 /opt/demo-daemon/commons-daemon-1.6.1.jar:/opt/demo-daemon,JDK 位于 /usr/lib/jvm/java-17-openjdk-amd64。
一个必须先建立的认知:jsvc 的输出重定向发生在 JVM 初始化之前,不传 -outfile/-errfile 时两者默认指向 /dev/null,这会吞掉 -version、-check 等命令的输出。本文所有需要在控制台看到结果的命令都显式携带 -outfile '&1' -errfile '&2'(含义分别为 stdout 保持原样、stderr 保持原样)。
💡 技术拓展:把 Java 程序做成守护进程有四种常见方案,选型对比如下。
方案 pid 管理 降权运行 日志 信号处理 开机自启 jsvc pidfile 记录 -user 参数原生支持 -outfile/-errfile 重定向 内建优雅停机协议 需配合 systemd 或 init 脚本 prunsrv Windows 服务管理器托管 服务账号/凭据配置 stdout/stderr 重定向到文件 Windows 服务控制协议 注册为 Windows 服务即自启 systemd Unit cgroup 全程托管 User= 指令 journald 或重定向 KillSignal 可配 WantedBy= 即自启 nohup+disown 需手工记录 pid 需 su/runuser 自行处理 需自行重定向 默认无优雅停机 无,需自写开机脚本 本文展开的是 jsvc 的命令行操作面;Windows 平台对应工具为 prunsrv。
二、jsvc --help 完整输出
以下为 1.6.1 的 --help 逐字实测输出(Ubuntu 22.04 与 CentOS 8 一致):
/usr/local/bin/jsvc --help
回显(实测完整输出):
Usage: jsvc [-options] class [args...]
Where options include:
-help | --help | -?
show this help page (implies -nodetach)
-jvm <JVM name>
use a specific Java Virtual Machine. Available JVMs:
'server'
-client
use a client Java Virtual Machine.
-server
use a server Java Virtual Machine.
-cp | -classpath <directories and zip/jar files>
set search path for service classes and resouces
-java-home | -home <directory>
set the path of your JDK or JRE installation (or set
the JAVA_HOME environment variable)
-version
show the current Java environment version (to check
correctness of -home and -jvm. Implies -nodetach)
-showversion
show the current Java environment version (to check
correctness of -home and -jvm) and continue execution.
-nodetach
don't detach from parent process and become a daemon
-debug
verbosely print debugging information
-check
only check service (implies -nodetach)
-user <user>
user used to run the daemon (defaults to current user)
-verbose[:class|gc|jni]
enable verbose output
-cwd </full/path>
set working directory to given location (defaults to /)
-outfile </full/path/to/file>
Location for output from stdout (defaults to /dev/null)
Use the value '&2' to simulate '1>&2'
-errfile </full/path/to/file>
Location for output from stderr (defaults to /dev/null)
Use the value '&1' to simulate '2>&1'
-pidfile </full/path/to/file>
Location for output from the file containing the pid of jsvc
(defaults to /var/run/jsvc.pid)
-D<name>=<value>
set a Java system property
-X<option>
set Virtual Machine specific option
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]
enable assertions
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>]
disable assertions
-esa | -enablesystemassertions
enable system assertions
-dsa | -disablesystemassertions
disable system assertions
-agentlib:<libname>[=<options>]
load native agent library <libname>, e.g. -agentlib:hprof
-agentpath:<pathname>[=<options>]
load native agent library by full pathname
-javaagent:<jarpath>[=<options>]
load Java programming language agent, see java.lang.instrument
-procname <procname>
use the specified process name
-wait <waittime>
wait waittime seconds for the service to start
waittime should multiple of 10 (min=10)
-restarts <maxrestarts>
maximum automatic restarts (integer)
-1=infinite (default), 0=none, 1..(INT_MAX-1)=fixed restart count
-stop
stop the service using the file given in the -pidfile option
-keepstdin
does not redirect stdin to /dev/null
--add-modules=<module name>
Java 9 --add-modules option. Passed as it is to JVM
--module-path=<module path>
Java 9 --module-path option. Passed as it is to JVM
--upgrade-module-path=<module path>
Java 9 --upgrade-module-path option. Passed as it is to JVM
--add-reads=<module name>
Java 9 --add-reads option. Passed as it is to JVM
--add-exports=<module name>
Java 9 --add-exports option. Passed as it is to JVM
--add-opens=<module name>
Java 9 --add-opens option. Passed as it is to JVM
--limit-modules=<module name>
Java 9 --limit-modules option. Passed as it is to JVM
--patch-module=<module name>
Java 9 --patch-module option. Passed as it is to JVM
--illegal-access=<value>
Java 9 --illegal-access option. Passed as it is to JVM.
Refer java help for possible values.
--enable-preview
Java 11 --enable-preview option. Passed as it is to JVM
--enable-native-access=<module name>
Java 17 --enable-native-access option. Passed as it is to JVM
jsvc (Apache Commons Daemon) 1.6.1
Copyright (c) 1999-2026 Apache Software Foundation.
注意命令尾部会自动打印版本横幅,因此 --help | tail -2 也是最轻量的版本确认手段。
三、参数速查表
下表覆盖 --help 全部参数,默认值一栏均来自 help 原文或实测确认。
| 参数 | 作用 | 默认值 | 备注 |
|---|---|---|---|
| -help、--help、-? | 显示帮助并退出 | 无 | 隐含 -nodetach |
| -jvm <name> | 指定 JVM 名称 | server | JDK 17 下仅 server 可用,-client 实测报 Invalid JVM name |
| -client / -server | client 或 server JVM 简写 | server | 现代 JDK 无 client VM |
| -cp、-classpath | 类与资源搜索路径 | 无(必填项之一) | 冒号分隔,可混 jar 与目录 |
| -java-home、-home | JDK/JRE 目录 | JAVA_HOME 环境变量或系统 default-java | 多 JDK 机器必须显式指定 |
| -version | 打印 Java 环境与 commons daemon 版本后退出 | 无 | 隐含 -nodetach,且必须带类名参数 |
| -showversion | 打印版本后继续启动 | 无 | 常用于排查 JVM 选择问题 |
| -nodetach | 前台运行不脱离终端 | 后台脱离 | Ctrl-C 或 SIGTERM 走优雅停机 |
| -debug | 全流程调试输出 | 关闭 | 输出参数转储与生命周期日志 |
| -check | 仅预检查服务加载 | 无 | 隐含 -nodetach,输出 checked successfully |
| -user <user> | 运行身份 | 当前用户 | 仅 root 可用;pidfile 在降权前由 root 写入 |
| -verbose[:class | gc | jni] | JVM 详细输出 |
| -cwd <path> | 工作目录 | / | 实测 /proc/<pid>/cwd 可验证 |
| -outfile <path> | stdout 去向 | /dev/null | 特殊值 &2(转 stderr)与 SYSLOG |
| -errfile <path> | stderr 去向 | /dev/null | 特殊值 &1(转 stdout)与 SYSLOG |
| -pidfile <path> | pid 文件位置 | /var/run/jsvc.pid | 含存活检测与文件锁,正常停止后自动删除 |
| -D<name>=<value> | Java 系统属性 | 无 | 自动注入 commons.daemon.process.id 等内置属性 |
| -X<option> | JVM 专有参数 | 无 | 如 -Xms256m -Xmx512m |
| -ea / -da(含包限定) | 启用/禁用断言 | 禁用 | 透传 JVM |
| -esa / -dsa | 启用/禁用系统断言 | 禁用 | 透传 JVM |
| -agentlib / -agentpath / -javaagent | 加载 agent | 无 | 透传 JVM,错误由 JVM 报出 |
| -procname <name> | 进程改名 | jsvc.exec | 便于按名检索与监控 |
| -wait <seconds> | 等待启动完成 | 不等待 | 须为 10 的倍数且最小 10 |
| -restarts <count> | 自动重启上限 | -1(无限) | 0 为禁用;两次重启间隔至少 60 秒 |
| -stop | 按 pidfile 停止 | 无 | 必须带类名参数 |
| -keepstdin | 保留 stdin | stdin 重定向到 /dev/null | 交互式服务才需要 |
| --add-modules 等模块化系列 | JPMS 参数透传 | 无 | Java 9+,原样透传 JVM |
| --enable-preview | 预览特性 | 关闭 | Java 11+ |
| --enable-native-access | 本地访问声明 | 无 | Java 17 |
四、启动服务与运行验证
4.1 标准启动命令
root 启动、-user appuser 降权、堆 256 至 512MB、日志与 pidfile 显式落盘:
sudo /usr/local/bin/jsvc -java-home /usr/lib/jvm/java-17-openjdk-amd64 -cp /opt/demo-daemon/commons-daemon-1.6.1.jar:/opt/demo-daemon -user appuser -Xms256m -Xmx512m -outfile /opt/demo-daemon/demo-stdout.log -errfile /opt/demo-daemon/demo-stderr.log -pidfile /opt/demo-daemon/demo-daemon.pid DemoDaemon
echo RC=$?
回显(实测):
RC=0
4.2 进程层验证:ps
ps -ef | grep jsvc | grep -v grep
回显(实测,root 控制进程加 appuser JVM 子进程):
root 6749 1 0 07:51 ? 00:00:00 jsvc.exec -java-home /usr/lib/jvm/java-17-openjdk-amd64 -cp /opt/demo-daemon/commons-daemon-1.6.1.jar:/opt/demo-daemon -user appuser -Xms256m -Xmx512m -outfile /opt/demo-daemon/demo-stdout.log -errfile /opt/demo-daemon/demo-stderr.log -pidfile /opt/demo-daemon/demo-daemon.pid DemoDaemon
appuser 6750 6749 1 07:51 ? 00:00:00 jsvc.exec -java-home /usr/lib/jvm/java-17-openjdk-amd64 -cp /opt/demo-daemon/commons-daemon-1.6.1.jar:/opt/demo-daemon -user appuser -Xms256m -Xmx512m -outfile /opt/demo-daemon/demo-stdout.log -errfile /opt/demo-daemon/demo-stderr.log -pidfile /opt/demo-daemon/demo-daemon.pid DemoDaemon
设置了 -procname 时进程名会替换 jsvc.exec,例如 -procname demo-app 后 ps 显示(实测):
root 6878 1 0 07:52 ? 00:00:00 demo-app -java-home /usr/lib/jvm/java-17-openjdk-amd64 -procname demo-app -cp /opt/demo-daemon/commons-daemon-1.6.1.jar:/opt/demo-daemon -outfile /opt/demo-daemon/demo-stdout.log -errfile /opt/demo-daemon/demo-stderr.log -pidfile /opt/demo-daemon/demo-daemon.pid -wait 20 DemoDaemon --env production port=8080
4.3 JVM 层验证:jps
jps -l
回显(实测):
6779 jdk.jcmd/sun.tools.jps.Jps
6750 DemoDaemon
CentOS 8 的 jps 不在默认 PATH,使用完整路径 /usr/lib/jvm/java-17-openjdk/bin/jps -l,实测同样能列出 DemoDaemon 主类进程。
4.4 pidfile 验证
cat /opt/demo-daemon/demo-daemon.pid
回显(实测):
6750
pidfile 中的值就是 JVM 子进程的 pid,可直接用于 jcmd 观测。实测验证堆参数已生效:
jcmd $(cat /opt/demo-daemon/demo-daemon.pid) VM.flags | tr ' ' '\n' | grep -E 'InitialHeapSize|MaxHeapSize'
回显(实测):
-XX:InitialHeapSize=268435456
-XX:MaxHeapSize=536870912
五、停止服务
标准停止命令(-stop 加 -pidfile 加类名三者缺一不可):
sudo /usr/local/bin/jsvc -stop -pidfile /opt/demo-daemon/demo-daemon.pid DemoDaemon
echo RC=$?
回显(实测):
RC=0
停止后三重确认:进程消失、日志收尾完整、pidfile 自动删除。
jps -l
回显(实测,仅剩 jps 自身):
6848 jdk.jcmd/sun.tools.jps.Jps
tail -4 /opt/demo-daemon/demo-stdout.log
回显(实测):
[DemoDaemon] heartbeat #6, thread=demo-worker
[DemoDaemon] stop() called, notifying worker to exit
[DemoDaemon] worker thread exited
[DemoDaemon] destroy() called, resources released
ls -l /opt/demo-daemon/demo-daemon.pid
回显(实测):
ls: cannot access '/opt/demo-daemon/demo-daemon.pid': No such file or directory
两个实测坑:其一,-stop 缺类名直接失败(No class specified);其二,-stop 找不到 pidfile 时返回 255 并报 Cannot open PID file(实测回显见第八章)。
六、版本与预检查命令
6.1 -version
打印 jsvc 版本、Java 环境与 commons daemon 版本后退出。必须显式携带类名与控制台输出参数:
/usr/local/bin/jsvc -version -outfile '&1' -errfile '&2' -java-home /usr/lib/jvm/java-17-openjdk-amd64 -cp /opt/demo-daemon/commons-daemon-1.6.1.jar:/opt/demo-daemon DemoDaemon
回显(实测):
jsvc (Apache Commons Daemon) 1.6.1
Copyright (c) 1999-2026 Apache Software Foundation.
java version "17.0.20.1"
OpenJDK Runtime Environment (build 17.0.20.1+1-1-22.04-Ubuntu)
OpenJDK 64-Bit Server VM (build 17.0.20.1+1-1-22.04-Ubuntu, mixed mode, sharing)
commons daemon version "1.6.1"
commons daemon process (id: 6525, parent: 6524)
6.2 -showversion
与 -version 信息一致,但打印后继续启动服务。实测回显中版本横幅之后紧接 init() 与心跳日志(见配置参数详解篇 8.4 节的完整实测输出)。
6.3 -check
部署前的加载预检查,验证类路径与主类可被 DaemonLoader 解析:
/usr/local/bin/jsvc -check -java-home /usr/lib/jvm/java-17-openjdk-amd64 -outfile '&1' -errfile '&2' -cp /opt/demo-daemon/commons-daemon-1.6.1.jar:/opt/demo-daemon DemoDaemon
回显(实测):
Service "DemoDaemon" checked successfully
6.4 默认 pidfile 位置
不传 -pidfile 时实例把 pid 写到 /var/run/jsvc.pid(help 默认值的实测确认):
cat /var/run/jsvc.pid
回显(实测):
7056
七、日志文件查看
7.1 -outfile 与 -errfile 文件
心跳与业务日志实时追加在 -outfile 指定的文件中:
cat /opt/demo-daemon/demo-stdout.log
回显(实测):
[DemoDaemon] init() called, args=[]
[DemoDaemon] start() called, worker thread starting
[DemoDaemon] heartbeat #1, thread=demo-worker
文件由 root 在降权前打开,属主为 root 且权限 600(实测 ls -l 显示 -rw------- 1 root root)。追踪日志用 tail -f /opt/demo-daemon/demo-stdout.log。
7.2 systemd 托管时的 journald
以 systemd Type=forking 单元托管 jsvc 时,ExecStart 的 stdout 仍受 -outfile 控制;纯 Java 形态(不经 jsvc)的 stdout 由 journald 收集:
journalctl -u demo-plain.service --no-pager -n 3 | tail -3
回显(实测):
Oct 13 08:10:35 1a283e06d6da systemd[1]: Started DemoMain Service (plain java + systemd).
Oct 13 08:10:35 1a283e06d6da java[9705]: [DemoMain] main() started, args=[]
Oct 13 08:10:40 1a283e06d6da java[9705]: [DemoMain] heartbeat #1
7.3 -debug 控制台调试
-debug 把参数解析、JVM 创建、类加载全流程打到 stderr,定位启动问题时最有用。实测节选:
+-- DUMPING JAVA VM CREATION ARGUMENTS -----------------
| Extra options: 1
| "-Djava.class.path=/opt/demo-daemon/commons-daemon-1.6.1.jar:/opt/demo-daemon" (0x00000000)
+-------------------------------------------------------
Java VM created successfully
Class org/apache/commons/daemon/support/DaemonLoader found
Native methods registered
八、常见报错速查
以下报错信息全部为实测原文,可直接对照排查。
| 报错信息 | 触发场景 | 解法 |
|---|---|---|
| JSVC re-exec requires execution with an absolute or relative path | 用裸命令名 jsvc 调用 | 改用绝对路径如 /usr/local/bin/jsvc |
| No class specified / Cannot parse command line arguments | -version 或 -stop 未带类名 | 命令尾部补上主类名 |
| Invalid option --version | 传入 jsvc 不认识的选项(注意 -version 是合法的,双横线 --version 不是) | 对照第二章 help 输出修正拼写 |
| java.lang.ClassNotFoundException: NoSuchClass | -cp 配置错误或类名写错 | 核对 -cp 路径与类全名,可用 -check 预检 |
| UnsupportedClassVersionError class file version 61.0 this version only up to 55.0 | jsvc 落到低版本 JVM(如系统默认 Java 11) | 显式传 -java-home 指向 JDK 17 |
| Cannot open PID file /var/run/jsvc.pid, PID is 7205 | 非 root 运行且无默认 pidfile 写权限,或 pidfile 目录不存在 | 显式指定可写位置的 -pidfile |
| Cannot set group id for user 'root' / Error validating user 'root' | 非 root 用户使用 -user 切换他人身份 | -user 仅限 root 使用 |
| Invalid JVM name specified client / Cannot locate JVM library file | JDK 17 下使用 -client | 去掉 -client,现代 JDK 仅有 server VM |
| Service exit with a return value of 2 | 服务启动阶段失败(如类版本不匹配) | 结合 -errfile 定位栈信息 |
九、参考链接
- Apache Commons Daemon 官方主页:https://commons.apache.org/proper/commons-daemon/
- jsvc 启动器文档(含参数说明):https://commons.apache.org/proper/commons-daemon/jsvc.html
- 官方下载目录(bin 包与 src 包):https://downloads.apache.org/commons/daemon/
- Daemon 接口 Javadoc:https://commons.apache.org/proper/commons-daemon/apidocs/org/apache/commons/daemon/Daemon.html
- Maven Central 上的 commons-daemon 构件:https://repo1.maven.org/maven2/commons-daemon/commons-daemon/