不推荐用 os/exec 检查进程是否存在;应优先使用 syscall.Kill(pid, 0)(Linux)或 golang.org/x/sys/windows(Windows)校验进程存活,配合 PID 文件管理与 goroutine 轮询。

用 os/exec 检查进程是否存在是否可靠?
不推荐。直接调用 ps 或 pgrep 做字符串匹配看似简单,但容易误判(比如进程名包含子串)、跨平台兼容差、且无法区分同名不同用户的进程。Go 本身没有内置“查进程”API,但可以通过系统接口更稳妥地确认:Linux 下读取 /proc/[pid]/stat 或用 syscall.Kill(pid, 0) 发送空信号——只要进程存在且有权限访问,就返回 nil。
实操建议:
-
syscall.Kill(pid, 0)是最轻量、最通用的存活检查方式,不中断进程,只做权限+存在性校验 - 注意:PID 为 0 是非法值;负数 PID 表示进程组,不能用于此检查
- 在容器环境(如 Docker)中,若监控的是宿主机 PID,需确保容器挂载了
/proc,否则/proc/[pid]不可见 - Windows 不支持
syscall.Kill(pid, 0),需改用syscall.OpenProcess+syscall.WaitForSingleObject,但 Go 标准库封装较弱,建议用golang.org/x/sys/windows
如何安全获取目标进程的 PID?
硬编码 PID 不可维护;靠命令行参数传入又容易遗漏。更合理的方式是让被监控程序启动时写一个 PID 文件(pidfile),监控脚本读取并验证它指向的进程是否还活着。
实操建议:
- PID 文件路径应固定且可配置,例如
/var/run/myapp.pid或./myapp.pid - 读取后必须用
strconv.Atoi转换,并检查错误——文件存在但内容非数字很常见 - 读到 PID 后,立刻用
syscall.Kill(pid, 0)验证,避免“文件存在但进程已死”的假阳性 - 不要自动清理旧 PID 文件:如果上次异常退出没删干净,直接覆盖可能掩盖问题;建议加锁或用原子写入(如先写临时文件再
os.Rename)
用 time.Ticker 定期轮询会阻塞主线程吗?
不会,但必须配合 goroutine 使用,否则整个程序会在 for range ticker.C 中卡住,无法响应信号或做其他事。
实操建议:
- 把轮询逻辑放进
go func() { ... }(),主 goroutine 留给信号监听或初始化 - Ticker 间隔别设太短(如
- 务必在程序退出前调用
ticker.Stop(),否则 goroutine 泄漏,尤其在反复重启的守护场景下易积压 - 轮询中遇到错误(如 PID 文件消失、
syscall.Kill返回ESRCH)应记录日志,而不是 panic —— 进程死亡本就是监控要处理的正常事件
进程挂了之后,怎么自动拉起?
自动拉起不是监控脚本的核心职责,但常被一起要求。Go 本身不提供进程管理能力,强行 exec.Command 启动容易失控(孤儿进程、环境变量缺失、工作目录错乱)。
实操建议:
- 优先交给系统级进程管理器:Linux 用
systemd(Restart=always),macOS 用launchd,Docker 用restart: unless-stopped - 如果必须用 Go 拉起,用
exec.Command时显式设置Cmd.Dir、Cmd.Env,并重定向Stdout/Stderr到文件,否则输出丢失难排查 - 避免循环 fork:拉起后立即检查 PID 是否变化,防止因启动失败导致无限重试
- 最关键的一点:不要在同一个二进制里既做监控又做业务逻辑——编译产物体积大、升级困难、故障边界模糊。监控脚本应保持小而专


















