starttime 是相对值
/proc/<pid>/stat 里的 starttime 字段记录的不是绝对时刻,而是进程启动时相对系统启动时刻经过的时钟节拍数。要换算出实际时间,需要两个外部量:系统启动时刻(/proc/stat 里的 btime,单位是 Unix 秒)与内核的时钟节拍频率(getconf CLK_TCK,通常是 100)。三者组合才能得到精确到秒的启动时刻。
为什么在取证中有用
进程启动时间是判断「哪个进程先出现」的关键依据,尤其是在同一个时间窗里出现多个可疑进程时。它也是判断持久化机制落地时机的旁证:如果某个可疑进程的启动时刻紧接着一次登录或一次计划任务触发,这条链条的说服力就明显增强。
基本信息
| 适用平台 | Linux(/proc 文件系统) |
|---|---|
| 关键字段 | /proc/<pid>/stat 第 22 列 starttime(自启动起的时钟节拍数) |
| 基准字段 | /proc/stat 中 btime(系统启动时刻,Unix 秒) |
| 节拍频率 | getconf CLK_TCK,多数系统为 100 |
| 交叉验证 | ps -o lstart= -p <pid> 给出本地时间格式的启动时刻 |
典型使用场景
- 多个可疑进程并存:按启动时刻排出先后,判断谁是父谁是子
- 判断持久化落地时间:进程启动时刻紧接登录或任务触发时值得深查
- 核对内存镜像中的进程列表:与在线采样结果比对启动时刻
- 排查短命进程:启动与结束时间接近的进程常是脚本或载荷释放器
- 为时间线补充主机侧锚点:进程启动时刻是少数可精确到秒的本地证据
换算启动时刻
换算启动时刻
# 取启动基准与节拍频率
btime=$(awk '/^btime/{print $2}' /proc/stat)
hz=$(getconf CLK_TCK)
# 取目标进程的 starttime(第 22 列)
st=$(awk '{print $22}' /proc/1234/stat)
# 换算:btime 加 starttime 除以频率,输出 UTC 时刻
date -u -d "@$((btime + st / hz))" +"%Y-%m-%dT%H:%M:%SZ"
# 交叉核对:直接读本地时间格式的启动时刻
ps -o pid,lstart,cmd -p 1234相关字段读法
| 来源 | 字段 | 含义 |
|---|---|---|
| /proc/stat | btime | 系统启动时刻(Unix 秒) |
| /proc/<pid>/stat | 第 22 列 starttime | 进程启动时的时钟节拍数 |
| getconf | CLK_TCK | 每秒时钟节拍数,换算分母 |
| ps | lstart | 本地时间格式的启动时刻 |
| uptime | 运行时长 | 与 btime 互相验证 |
重启之后 starttime 会相对新的启动时刻重新计算,因此采样的时机很重要:重启前采集的数据在重启后无法再从 /proc 取得。
字段序号在不同内核版本的历史命名中有过变化,脚本里最好不要硬编码列号,或者在使用前先用 proc 手册确认当前内核的字段顺序。