最准的进程启动时间用ps -o lstart= -p pid,因其输出完整稳定时间戳;运行时长唯一可靠字段是etime,表示自启动以来的整秒数。

直接看 ps -o lstart= -p PID,这是最准、最省事的启动时间获取方式;需要运行时长就用 etime,别碰 time 或 STIME——它们根本不是一回事。
为什么优先用 lstart 字段而不是 stime 或 STIME
lstart 输出完整时间戳(如 Wed May 21 07:56:12 2026),带年份、带星期、格式稳定,一眼能判跨年/跨月。而 stime(ps aux 的第 10 列)或 ps -ef 的 STIME 列只显示 May21、10:23 或 2025,没有上下文根本分不清是今天还是去年。
- 当前是 2026 年 5 月,
STIME显示Jan01→ 很可能指 2026-01-01;若显示2025→ 实际是 2025 年启动,已跑一年多 -
stime在中文 locale 下可能输出5月21日,脚本解析极易崩,lstart虽也受 locale 影响,但英文系统默认格式可靠得多 -
lstart不可用的场景极少(仅 Alpine/BusyBox 等极简系统),遇到时再 fallback,不必一上来就妥协
etime 是计算运行时长的唯一靠谱字段
etime 表示进程自启动以来经过的**整秒数**(向下取整),含休眠时间,数值稳定、易脚本化。它和 time 字段有本质区别:time 是 CPU 实际占用时间(单位 1/100 秒),值通常远小于真实运行时长。
- 查某个 PID 的秒数:
ps -o etime= -p 1234 | xargs(| xargs去首尾空格) - 换算成可读时间(bash):
date -d "@$(($(date +%s) - $(ps -o etime= -p 1234 | xargs)))" "+%Y-%m-%d %H:%M:%S" - 注意:
etime在旧版容器(如 Docker 19.03 之前)中可能从容器启动起算,而非进程自身exec时间 - 极少数情况下
etime为负值,说明内核时间异常或进程刚fork尚未exec,此时应跳过该值
别被 top 和 TIME+ 带偏
top 默认不显示真实运行时长。TIME+ 列是累计 CPU 时间(毫秒级),不是存活时间;即使你按 f 加了 ELAPSED 字段,它的值也只在较新版本 procps-ng(如 RHEL 8+/Ubuntu 20.04+)中存在,且数值和 etime 一致——不如直接用 ps 一锤定音。
- 想动态盯住一个进程的运行时长,用:
watch -n 5 'ps -o pid,etime,comm -C nginx' -
-C按命令名精确匹配,比grep nginx更干净,不会把grep nginx自身也列进来 -
watch默认用sh执行,如果命令里用了 bash 特性(比如数组或$((...))),得显式写成watch -n 5 'bash -c "ps ..."'
底层精度够高但没必要日常用:/proc/PID/stat 第 22 字段
/proc/PID/stat 第 22 字段是进程启动时刻相对于系统 boot time 的 jiffies 数(非秒),需结合 /proc/stat 中的 btime 和 CLK_TCK 换算。虽然精度最高,但太重:
- 直接读
awk '{print $22}' /proc/1234/stat当作秒数?错——jiffies 默认 100Hz,结果会大 100 倍 - 换算公式复杂:
start_sec=$(( $(awk '{print $22}' /proc/1234/stat) / $(getconf CLK_TCK) + $(grep btime /proc/stat | awk '{print $2}'))) -
ps etime已帮你做完这整套转换,除非你在写内核调试工具,否则没理由绕开它
真正容易被忽略的是:不同环境对“启动时间”的定义不统一——systemd 服务的 ActiveEnterTimestamp 是服务就绪时刻,容器里 etime 可能从容器启动起算,而 lstart 始终反映进程被 execve 的那个瞬间。查之前,先想清楚你要的到底是“内核看到它的时间”,还是“用户感知它开始工作的时间”。











