DeepSeek Harness macOS Dock 图标重复:939 个 session.lock 不是泄漏
看到「939 个 session.lock 从不回收」,几乎所有排查思路都会指向「锁泄漏」,然后顺手写个清理脚本——但这一步恰好会把清理做成并发 bug。 同一份报告里还有另一半:Dock 里越堆越多的图标,那才是真缺陷(#10757)。把两句话拆开读,结论会完全不同。
DeepSeek Harness 第一句的语义:「939 个锁文件」等于什么
它只说明这个工作区里被创建过 939 个会话,不说明有 939 个进程活着,也不说明有 939 把锁被持有。
- 数一遍:
find ~/.dsh/sessions -name "session.lock" | wc -l,社区结果 939。 - 看时间分布:最早的是一个月前,其中 09-26 到 09-28 三天分别是 89、115、146,这种台阶更像启动循环,不像自然的会话增长。
- 看单个文件:0 字节,和活跃会话的锁长得一模一样——这正是它「看起来像泄漏」的原因。
- 看真实活跃度:当天只有 3 个工作区目录被碰过,其中只有一个会话是活跃的。
- 所以数量与活跃进程无关,只与历史会话数有关。
DSH插件 锁文件的真实语义:文件在,锁就可能在
POSIX 锁文件按设计永不删除:留下的文件保持稳定 inode,后续加锁者靠它校验是不是同一个对象;真正仲裁归属的是内核,不是文件本身。
- 持有者退出后,内核释放锁,但文件仍在——这是「已释放」,不是「泄漏」。
- 删掉文件会换掉 inode,后续加锁者校验时看到的就不是同一个对象。
- 删掉一个正在使用中的会话锁,等于放弃它存在的意义:互斥。
- 社区对此的态度很明确:这种「清理插件」看起来像打扫,行为上等于 mutex bug。
- 如果确实嫌文件多,正确做法是内核提供有界目录或明确的保留规则,而不是在使用点删除。
DeepSeek Harness 第二句的语义:「Dock 一排图标」等于什么
它说明每次启动都产生了新进程,系统把它们当成不同实例;缺的是主进程里的单实例保护。
- 先排除安装态:
ls -d /Applications/*Harness*.app | wc -l结果是 1,只有一份 app。 - 再排除固定项:
defaults read com.apple.dock persistent-apps | grep -c "com.deepseek.dsh"结果是 1。 ~/Applications、/Users/Shared、桌面都没有第二份,桌面也没有启动器。- 查 Info.plist:
LSMultipleInstancesProhibited出现 0 次,LSUIElement也没有。 - 于是 LaunchServices 对每次双击都新起一个进程,每个进程各占一个 Dock 图标。
- 同一个根因还解释了另一个旧症状:退出后要点两次图标才能重新打开。
DeepSeek插件 为什么这两件事被写在同一份报告里
它们共享「重复启动」这个触发条件,但一个是运行态表现,一个是文件系统残留,处置方式相反。
| 维度 | Dock 重复图标 | session.lock 累积 |
|---|---|---|
| 是不是缺陷 | 是 | 否,属设计 |
| 直接证据 | 单份 app、单个固定项、缺单实例键 | 文件 0 字节、持有者已退出 |
| 能清理吗 | 重启后会再次出现 | 不该清理 |
| 修复归属 | 核心主进程 | 核心(保留策略)或不动 |
这也是为什么社区回复只承接第二半:把两件事混在一起,就会得出「删锁能减少 Dock 图标」这种错误因果。
DeepSeek Harness 三十秒自查:把两个数字分开看
两个命令就够:一个数安装态,一个数运行态;安装态永远是 1,运行态会变,但两者都不该被当成「锁泄漏」的证据。
# 1. 安装态:应该只有 1
ls -d /Applications/*Harness*.app | wc -l
defaults read com.apple.dock persistent-apps | grep -c "com.deepseek.dsh"
# 2. 运行态:会随启动次数增长
find ~/.dsh/sessions -name "session.lock" | wc -l
# 3. 单实例声明:缺了就会每次新起进程
/usr/libexec/PlistBuddy -c "Print :LSMultipleInstancesProhibited" \
"/Applications/DeepSeek Harness.app/Contents/Info.plist"
第 3 条如果报 Does Not Exist,就与社区的观察一致:单实例保护没有声明,重复启动会直接产生新进程。
DSH插件 能做的与不能做的
能做的:减少重复启动、按需清理桌面快捷方式;不能做的:删锁、指望插件修单实例。
- 观察期先确认现象:连续几天从 Dock 双击启动,记录图标数量与
find ... session.lock | wc -l的变化。 - 不要写脚本批量删
session.lock,尤其不要在应用运行时删。 - 如果想临时缓解图标堆积,退出所有实例后重新打开一次,Dock 会回到单个图标。
- 桌面与 Dock 上只保留一个启动入口,避免无意中双击多次。
- 真正的修复需要主进程调用
app.requestSingleInstanceLock(),未获得锁就退出,或在second-instance事件里focus()已有窗口。 - 也可以在 Info.plist 里声明
LSMultipleInstancesProhibited,让 LaunchServices 层面直接限制。 - 锁文件这一半若要改进,应由核心在启动时清扫「持有者 PID 已死且无活跃写入者」的锁,而不是让用户手工删。
注意事项
- 别把「0 字节」当成「空文件可删」。活跃锁与废弃锁在文件层面无法区分,这正是它不可随手清理的原因。
- 别用图标数量反推进程数量。Dock 图标的累积是历史启动的结果。
- 别指望插件修 Dock。第二次启动发生在任何插件加载之前。
- 记录版本与系统。社区报告基于 0.2.0-rc.2,行为可能随版本变化。
- 区分「锁文件多」和「锁等待超时」。后者是另一个问题,错误里只会给出路径,同样不该靠删文件解决。
如果你的 Dock 图标重复只在装了多份 app 时出现,那属于安装态问题,按卸载多余副本处理即可。相关排查见 DeepSeek Harness 桌面端启动异常:没有窗口、启动崩溃、EMFILE 与 80 秒单核满载。
常见问题
不是,DeepSeek Harness 的 session.lock 按设计永不删除,它保持同一个 inode 供后续加锁者校验,真正仲裁的是内核而不是文件是否存在。零字节且持有者已退出,表示锁已释放,不表示锁被泄漏。
不建议,删掉 DeepSeek Harness 正在使用的会话锁会放弃它本该提供的互斥保护,等于把清理做成了并发 bug。如果只是嫌多,应该由内核提供有界目录或保留策略,而不是在使用点删除。
因为 DeepSeek Harness 没有单实例保护。Info.plist 里没有声明 LSMultipleInstancesProhibited,LaunchServices 对每次 open 或双击都会启动一个新进程,于是每个进程在 Dock 里各占一个图标。
社区核对过都不是 DeepSeek Harness 装了两份:/Applications 下只有一份 app,Dock 的 persistent-apps 里也只有一个 com.deepseek.dsh 固定项。Dock 的重复来自运行态,不是安装态。
不能,DeepSeek Harness 插件管不到单实例:第二次启动在任何插件加载之前就已经发生,单实例保护必须由主进程在启动早期完成,属于核心决策。
相关术语
- session.lock
- DeepSeek Harness 会话目录下的 POSIX 锁文件是用于保证同一会话只有一个写入者的锁(session.lock)。文件按设计不删除,持有者退出即视为锁已释放。— https://github.com/deepseek-ai/deepseek-harness/discussions/10757
- LSMultipleInstancesProhibited
- LSMultipleInstancesProhibited 是 macOS Info.plist 的布尔键,禁止同一应用启动多个实例。缺失时 LaunchServices 会为每次打开动作启动新进程,而不是激活已有窗口。— https://github.com/deepseek-ai/deepseek-harness/discussions/10757
- LaunchServices
- LaunchServices 是 macOS 负责注册与启动应用的系统服务。它依据 Info.plist 声明决定是复用现有实例还是新起一个进程,因此单实例行为需要在这里声明。— https://github.com/deepseek-ai/deepseek-harness/discussions/10757
- inode
- inode 是文件系统里标识一个文件实体的编号。锁文件保持同一 inode 才能让后续加锁者确认自己锁的是同一个对象,因此这个文件不能随手删。— https://github.com/deepseek-ai/deepseek-harness/discussions/10757
来源
- #10757 — macOS: Dock accumulates duplicate icons — no single-instance lock, 939 stale session.lock· deepseek-ai(GitHub Discussions)
- deepseek-ai/deepseek-harness(源码仓库)· GitHub