彻底清理 codex-universal-proxy:从杀不掉到干净卸载

笔者在服务器上遇到过一个奇怪现象:codex-universal-proxy 进程用 kill -9 杀不掉,杀掉后立刻又冒出一个新的。排查后发现,它其实是被 systemd 用户服务托管着,只要服务还在运行,进程就会被自动拉起。这篇文章记录完整的清理过程。

01 | 现象:kill -9 杀不掉

先用 ps aux 查看进程:

bash 复制代码
ps aux | grep codex-universal-proxy

可以看到一个由 node 启动的进程,路径指向全局安装的 codex-universal-proxy。笔者尝试直接强杀:

bash 复制代码
kill -9 2191510

再次查看,进程换了个 PID 又出现了。这说明背后有守护机制在自动重启它。

这里的关键特征是:PID 变了,但进程还在 。如果只是普通进程,kill -9 之后应该彻底消失;而 PID 变化说明有另一个"管理者"在进程退出后立刻重新拉起了一个新实例。这个管理者可能是 systemd 用户服务,也可能是 supervisor、cron 或父进程循环拉起等其他守护机制。要确认具体是谁,需要进一步排查,本文通过 systemctl 定位到的是 systemd 用户服务。
flowchart LR A"kill -9 目标进程" --> B"进程退出" B --> C{"是否有守护机制?"} C -- "无" --> D"进程彻底消失" C -- "有 (systemd)" --> E"服务检测到进程退出" E --> F"自动拉起新进程 (新 PID)" F --> A

02 | 定位:systemd 用户服务

用 systemctl 查看用户级服务:

bash 复制代码
systemctl --user list-units | grep codex

结果很明确:

text 复制代码
codex-universal-proxy.service  loaded active running  Codex Universal Proxy

原来它被注册成了一个 systemd 用户服务,且该服务单元配置了自动重启(如 Restart=always/on-failure),所以 kill -9 只是杀掉当前进程,服务会立刻重新拉起一个新的。是否会自动拉起取决于服务单元的 Restart 配置,可用 systemctl --user cat codex-universal-proxy 查看确认。

注意这里是 --user 而不是系统级服务。用户级服务由当前登录用户的 systemd 实例管理,服务单元通常位于 ~/.config/systemd/user/ 下,开机自启则通过 default.target.wants 目录中的软链接实现。这也解释了为什么用普通用户身份就能操作它,而不需要 sudo。

03 | 清理:停用服务再卸载

正确的顺序是先停服务、再禁用开机自启、删除残留的单元文件,最后卸载包。只 stop 不 disable,下次登录或重启服务还会回来;只 disable 不 stop,当前进程仍在运行;而 disable 只移除 default.target.wants 下的软链接,单元文件本身仍保留在 ~/.config/systemd/user/ 下,需要一并删除才算彻底清理。
flowchart TD S1"systemctl --user stop\
停止当前运行的服务"
--> S2"systemctl --user disable\
移除开机自启链接"
S2 --> S3"npm uninstall -g\
卸载全局 npm 包"
S3 --> S4"验证: 无服务、无进程、无包"

停止服务:

bash 复制代码
systemctl --user stop codex-universal-proxy

停止后再查进程,已经只剩 grep 自身,说明进程不再被拉起。

禁用服务:

bash 复制代码
systemctl --user disable codex-universal-proxy

输出提示已移除 default.target.wants 下的服务链接。再次确认:

bash 复制代码
systemctl --user list-units | grep codex
ps -ef | grep codex-universal-proxy

两条命令都没有匹配到真实服务或进程,只剩 grep 命令本身。

最后卸载全局 npm 包:

bash 复制代码
npm uninstall -g codex-universal-proxy

输出显示已移除 24 个包。最后做一次整体验证:

bash 复制代码
systemctl --user list-units | grep codex
ps -ef | grep codex-universal-proxy
npm ls -g codex-universal-proxy

三条命令均无真实匹配(只剩 grep 自身)时,才算清理干净。

04 | 小结

遇到 kill -9 杀不掉的进程,先别急着反复强杀。用 systemctl --user list-units 检查是否被用户级服务托管,往往才是根因。清理顺序记住三步:stop 停服务、disable 禁用自启、npm uninstall -g 卸载包。这样才算彻底清理干净。

关注我,和AI一起成长~