适用场景
已经用上 Codex CLI 写代码,但发现机器上时不时多出几个后台进程,不确定它们在干什么、能不能出网、会不会读到不该读的目录。这套流程帮你把 Codex 相关的常驻进程摸清楚:哪些是必需的、哪些是安装方式顺带塞进来的,然后按需收敛权限或直接关掉。
环境与前置条件
- 操作系统:macOS 12 及以上,或主流 Linux 发行版(Ubuntu / Debian / Fedora 等)。Windows 建议在 WSL2 里操作,排查思路一致。
- 运行时:Node.js 与 npm,具体版本要求以 Codex CLI 官方文档当前版本为准。若用 Homebrew 等包管理器安装,也一并确认。
- 已经安装并登录过 Codex CLI,至少跑通过一次,这样才有进程和日志可观察。
- 会用到的工具:
ps、pgrep、lsof;macOS 额外需要launchctl,Linux 额外需要systemctl和ss。查自己账号下的进程不需要 sudo,查系统级服务或别的用户需要。 - 磁盘:配置、会话历史与日志会随使用量增长,长期使用建议预留几百 MB 到数 GB,取决于会话保留策略。
- 内存:单个 agent 进程通常在数百 MB 量级,长上下文、多 MCP 服务会明显更高;具体不做承诺,以你机器上的实测为准。
- 动手前先备份:
cp -a ~/.codex ~/.codex.bak(如果该目录存在),出问题能回滚。
需要先明确一点:不同安装渠道的默认行为差别很大。Codex CLI 本体是一个前台命令,你敲它才启动;但 IDE 扩展、MCP 服务、以及部分包管理器注册的辅助/自动更新任务,会以子进程或后台服务的形式常驻。所以第一步不是"关掉它",而是"先看清楚机器上到底跑着什么"。
分步骤部署
第 1 步:抓一份进程基线
先把和 codex 沾边的进程全列出来,存成文件,后面做对比。
```bash
pgrep -af codex | tee ~/codex-procs-baseline.txt
```
pgrep -af 会输出 PID 和完整命令行。如果一类进程同时存在多个,也能一眼看出来。
再看进程树,父子关系比进程名更能说明问题:
```bash
ps -ef --forest | grep -iE 'codex|node' | grep -v grep
```
macOS 上 ps 不支持 --forest,改成:
```bash
ps -axo pid,ppid,lstart,etime,rss,command | grep -i codex | grep -v grep
```
对每个感兴趣的 PID,补充查看详细信息:
```bash
ps -o pid,ppid,lstart,etime,rss,command -p <PID>
```
关键看三列:PPID(父进程是谁)、lstart(什么时候起来的)、etime(活了多久)。
成功标志:你能说清楚"有几类 codex 进程、各自父进程是谁"。这一步的产出是信息,不是改动。
第 2 步:判断哪些是常驻,哪些是一次性
看 PPID 就能大概分类:
| PPID 指向 | 含义 |
|---|---|
| 你刚关掉的终端 | 前台命令,通常已经退出 |
| IDE(Code / Cursor 等) | IDE 扩展拉起的服务进程,关 IDE 就不在 |
| 1(init / launchd) | 已被系统收养,多半是注册过的后台服务或更新任务 |
| 另一个 codex 或 node | MCP 子进程 / 内部 worker |
PPID 为 1 的,进一步查注册来源。
macOS:
```bash
launchctl list | grep -i codex
ls -la ~/Library/LaunchAgents /Library/LaunchDaemons 2>/dev/null | grep -i codex
```
Linux:
```bash
systemctl --user list-units --all | grep -i codex
systemctl list-units --all --type=service | grep -i codex
crontab -l
```
再顺带看一眼你用的是哪个包管理器装的,这决定了更新是由谁触发的:
```bash
which -a codex
npm ls -g --depth=0 2>/dev/null | grep -i codex
```
成功标志:每个常驻进程都能对应到一个明确的拉起方(IDE 扩展 / launchd / systemd / cron / npm 任务)。找不到来源的,继续往下走。
第 3 步:查看它的权限范围
进程能碰什么,取决于它打开了哪些文件和目录。列出全部打开的文件句柄:
```bash
lsof -p <PID>
```
只想看它是不是在读凭据、配置或整个项目目录,过滤一下:
```bash
lsof -p <PID> | grep -iE 'codex|\.ssh|\.aws|\.env|workspace'
```
看可执行文件究竟在哪(Linux):
```bash
ls -l /proc/<PID>/exe
```
同时检查 Codex 自身的沙箱与审批配置。配置文件是 TOML,一般位于配置目录下(常见为 ~/.codex/config.toml,具体路径以官方文档为准):
```bash
ls -la ~/.codex/
codex --help
```
--help 里会列出当前版本支持的审批策略、沙箱模式等参数;配置文件里对应的键名各版本可能有调整,一律以官方文档为准。概念上要控制的是三件事:
1. 写权限:是只读、仅工作区可写,还是全盘可写。
2. 审批策略:命令是否需要人工确认才执行。
3. 网络访问:沙箱内能不能发起对外连接。
凭据文件顺手收紧权限:
```bash
chmod 600 ~/.codex/auth.json 2>/dev/null
```
成功标志:你能答出"这个进程读得到我的项目目录吗?读得到我的 SSH 私钥吗?"如果答案是"能,而且我没打算让它读",就进入第 6 步收敛。
第 4 步:观察出网行为
先看已建立的连接:
```bash
lsof -p <PID> -i
lsof -p <PID> -iTCP -sTCP:LISTEN
```
第一条看它连出去哪了,第二条看它有没有在本地监听端口(IDE 集成和 MCP 常走本地端口)。
Linux 上更直观:
```bash
ss -tp | grep <PID>
```
macOS 上可以持续观察:
```bash
nettop -p <PID>
```
想看得更细,让进程走本地代理,用抓包工具只做观察、不改内容。也可以用 tcpdump 粗看目标地址:
```bash
sudo tcpdump -i any -n -c 50 'tcp and not port 22'
```
成功标志:你能列出这个进程对外连接的域名或 IP 集合。正常的编码智能体必然要连模型 API,除此之外的持续连接值得追问一句"这是干什么的"。
第 5 步:定位日志
先看配置目录里都有什么:
```bash
ls -la ~/.codex/
```
配置目录下通常会有配置、凭据、会话记录和日志子目录,命名以你当前版本为准。找出最近变动的文件:
```bash
find ~/.codex -type f -mmin -60 -exec ls -la {} \;
```
系统层面的日志:
```bash
Linux,如果是 systemd 用户服务
journalctl --user -u <unit> -f
macOS,按进程名过滤最近一小时
log show --last 1h --predicate 'process CONTAINS "codex"' --style compact
```
如果是 IDE 扩展拉起的进程,日志在 IDE 的"输出"面板里,选对应的扩展频道。
注意:会话日志里会有你贴进对话的代码片段和路径。清理前先确认没有敏感内容,清理时按时间删,别整个目录清空。
第 6 步:按需收敛,三个档位
档位一:只收紧权限,不关进程
用 codex --help 确认当前版本支持的沙箱模式与审批参数,然后把设置写进配置文件,让默认行为变成"只读"或"仅工作区可写 + 需审批"。具体键名以官方文档为准。这一档适合"我想用,但不想让它随手改全盘"。
档位二:减少常驻来源
- 不用 IDE 集成时,关掉对应扩展,而不是只关它拉起的进程——否则下次打开 IDE 又回来。
- 关闭包管理器层面的自动更新任务(如果存在),改为需要时手动升级。
- 检查配置里的 MCP 服务列表,把不用的条目注释掉。每个 MCP 条目通常就是一个常驻子进程。
档位三:彻底关闭
```bash
先确认要关的是谁
pgrep -af codex
macOS:卸载 launchd 注册的服务(<label> 用第 2 步查到的真实名字)
launchctl bootout gui/$(id -u)/<label>
Linux:停用并禁止自启 systemd 用户服务
systemctl --user disable --now <unit>
兜底:结束残留进程
pkill -f codex
```
关之前先退出 IDE,否则它会把进程重新拉起来。
成功标志:第 6 步执行完,pgrep -af codex 的结果符合你的预期——要么为空,要么只剩你主动保留的那一两个。
验证部署是否成功
按顺序验证,别只看一条命令:
```bash
1. 进程是否真的没了(或只剩预期的)
pgrep -af codex
2. 有没有残留的对外连接
lsof -iTCP -n -P 2>/dev/null | grep -i codex || echo "无 codex 相关连接"
3. 服务注册是否已移除
launchctl list | grep -i codex || echo "launchd 无残留" # macOS
systemctl --user list-units --all | grep -i codex || echo "systemd 无残留" # Linux
```
预期结果:第 1 条无输出,或只剩你明确保留的进程;第 2 条输出"无 codex 相关连接";第 3 条输出无残留。
最后做一次功能回归:重新开一个终端,运行一次只读任务(例如让它读一个文件并解释),确认该用的功能还正常。然后重启终端、重启 IDE,再跑一遍上面的验证——很多"关掉了"其实只是当次会话关掉了。
常见报错与解决
1. pkill -f codex 之后进程几秒内又出现
- 原因:IDE 扩展或服务管理器监守并自动重启子进程。
- 解决:先退出 IDE,再卸载服务注册,最后清进程。
```bash
退出 IDE 后
launchctl bootout gui/$(id -u)/<label> # macOS
systemctl --user disable --now <unit> # Linux
pkill -f codex
```
2. launchctl bootout 报 No such process 或 Boot-out failed
- 原因:
<label>写错了,或者该服务属于系统域而非用户域。 - 解决:先用
launchctl list | grep -i codex拿到准确 label 和 PID 列,确认服务在用户域后重试;系统域的用sudo launchctl bootout system/<label>。
3. systemctl --user disable 报 Unit not found
- 原因:进程根本不是 systemd 管起来的,可能是 cron 或包管理器的后台任务。
- 解决:换查注册来源。
```bash
crontab -l
ls -la ~/.config/systemd/user/ | grep -i codex
```
4. lsof -p <PID> 提示 Permission denied 或输出为空
- 原因:权限不足(进程属于其他用户或 root),或进程在你执行命令前已退出。
- 解决:先
pgrep -af codex确认 PID 还在,再提权查看。
```bash
sudo lsof -p <PID>
```
5. 关掉进程后,Codex 报连接失败或某个工具不可用
- 原因:你关掉的是 MCP 子进程或 IDE 集成服务,而不是更新任务。
- 解决:回到配置文件里把需要的条目重新启用,只保留确实要用的那几个,不必全开。
6. 升级/重装后旧版本命令仍在
- 原因:PATH 里存在多个安装位置。
- 解决:
```bash
which -a codex
npm ls -g --depth=0 2>/dev/null | grep -i codex
```
按输出清理多余的那一份,只保留一个。
后续维护
备份:把 ~/.codex/ 纳入定期备份,但注意里面可能有 API 凭据和完整会话记录。备份文件权限设成 600,别放进公开的云盘目录。改动配置前手动留一份带日期的副本。
升级:走你安装时用的那个包管理器的升级路径,升级前记录 codex --version,升级后重跑一遍本文的验证流程。如果做过权限收敛,升级后回看配置文件是否被覆盖或新增了默认项。
日志轮转与清理:定期检查配置目录体积。
```bash
du -sh ~/.codex
find ~/.codex -type f -mtime +30 -ls
```
确认无敏感内容后再按时间删除旧会话,不要直接 rm -rf 整个目录。
监控:把第 1 步的基线文件保留下来,定期重新采集做对比。
```bash
pgrep -af codex > ~/codex-procs-$(date +%F).txt
diff ~/codex-procs-baseline.txt ~/codex-procs-$(date +%F).txt
```
出现基线里没有的新进程,就按第 2 步重新查它的拉起来源。有条件的,把这条采集脚本丢进 cron 每天跑一次,日志留存一周,比事后回忆有效得多。
团队协作:把你验证过的收敛配置放进 dotfiles 版本管理,新同事装完 Codex CLI 后直接套用,省去重复排查。
