﻿---
title: Nushell 孤儿进程 100% CPU 的排查记录
date: 2026-10-18
excerpt: 一个终端早已关闭的 Nushell 进程连续烧了一小时 CPU：退出提示写入死终端触发 panic，又被 REPL 的重试循环无限放大。附一行命令自查同类进程。
tags:
  - Shell
  - Nushell
  - Process
updated: 2026-10-21 23:05:20
---

## 现象

今天 macBook 有些温热，打开活动监视器，看到一个 `nu` 进程长期 100%+ CPU。它没有 tty、父进程是 launchd，说明原终端早已关闭，只剩下这个孤儿进程。奇怪的是它累计 CPU 时间只有约一小时，也就是挂了三天多都相安无事，最近才开始空转。

![1792595116194](assets/nushell_high_cpu/1792595116194.png)

macOS 上用 `{sh} sample <pid> 3` 采样，主线程一直停在 REPL 退出时同步 history 文件的路径上，一轮接一轮地重复。对照 Nushell 0.116.1 的源码，链条就清楚了。

## 原因

1. 终端被关闭后，stdout 已经写不进去，写入会返回 EIO。
2. Nushell 退出前，如果还有后台任务没结束，会先用 `println!` 提示「再输一次 exit 才真的退出」。
3. Rust 的 `println!` 写 stdout 失败会直接 panic。panic 发生时正持有后台任务表的锁，锁随之被标记为 poisoned。
4. REPL 每轮迭代外面有一层 `catch_unwind`，panic 被捕获后会重建 line editor 继续跑；下一轮又走到同一个退出路径，这次直接卡在 poisoned 锁上再次 panic。
5. 崩溃、重建、再崩溃，一秒几十次，CPU 就满了。每一轮还要重读 history 文件、跑 hooks、克隆状态，所以单轮也不便宜。

panic 时的调用链：

```text
failed printing to stdout: Input/output error (os error 5)
    std::io::stdio::_print
    nu_engine::exit::cleanup
    nu_cli::repl::loop_iteration
```

整个过程完全静默：报错信息也写往那个已经死掉的终端，唯一症状就是 100% CPU。触发条件是「终端被强制关闭 + 当时还有后台任务」，正常关终端不会遇到；上游报告显示 Linux 同样能复现。

## 处理

这种进程没有任何可挽救的状态，直接 `{sh} kill <pid>` 即可。

想检查机器上有没有类似情况，在 Nushell 里跑：

```nu
ps | where name == 'nu' | where cpu > 50 or ppid == 1
```

- `cpu` 高的是正在空转的；
- `ppid` 是 1 的表示原终端已经没了，属于孤儿，迟早会变成前者；
- 命令行里带脚本路径的（比如 `{sh} nu foo.nu`）可能是自己跑的脚本，别误杀。

## 上游

这是 Nushell 自身的 bug。同类位置在 #18428 修过（`repl.rs`、`nu-mcp`、`main.rs`），但 `nu-engine/src/exit.rs` 里的两处 `println!` 漏掉了。把提示换成不会 panic 的 `stdout_write_all_and_flush` 即可解决。

---

本文的排查过程借助 Claude Code 完成。
