Karukan macOS睡眠唤醒处理:管道断裂的4层防御与优雅重启完整指南
2026/9/18 18:03:06 网站建设 项目流程

Karukan macOS睡眠唤醒处理:管道断裂的4层防御与优雅重启完整指南

【免费下载链接】karukanJapanese Input Method System for Linux, macOS, Neural Kana-Kanji Conversion Engine项目地址: https://gitcode.com/GitHub_Trending/ka/karukan

Karukan 是一款面向 macOS 与 Linux 的日语输入法,内置神经网络的平假名-汉字转换引擎。在 Mac 上,它采用"Swift 前端 + Rust 引擎子进程"的双进程架构,通过标准输入/输出管道通信。macOS 睡眠后唤醒,管道可能被系统悄悄回收,导致键盘"失灵"。本文带你搞懂为什么管道会断,以及 Karukan 如何优雅重启自救。

双进程架构:问题出在哪根管道上

Karukan 的 macOS 版由两个进程组成:

进程职责
KarukanIME(Swift)按键捕获、假名转换展示、候选窗渲染、子进程管理
karukan-imserver(Rust)状态机、罗马字转换、汉字转换与学习

两者都打包在 Karukan.app 内,靠 stdin/stdout 管道跑 JSON-RPC 协议——所有按键请求从 stdin 写入,所有响应从 stdout 读回。架构定义见 macOS 版 README,协议实现在 protocol.rs。

只要管道不断,这套通信就是可靠的。问题恰恰出在管道上。

为什么睡眠唤醒后管道会断

macOS 睡眠时会让所有进程挂起,期间还可能回收部分内核资源。唤醒之后,Swift 前端进程往往"毫不知情",但它手里的管道句柄已经失效:写入不再送达引擎,读取永远等不到新数据。

不处理的话,用户会看到这样的现象:

  • 按什么键都没反应,候选窗也不出现
  • 假名不再上屏,汉字转换彻底"失联"
  • 输入法进程还活着,却永远恢复不过来

所以 Karukan 的策略很直接:不等故障暴露,监听唤醒通知,一醒来就主动把引擎进程重启掉

第1层防御:忽略 SIGPIPE,绝不让 IME 被杀死

管道断开的经典死法是 SIGPIPE:向一条已失效的管道写数据,操作系统会直接发 SIGPIPE 信号把进程杀掉。Swift 入口在启动第一行就把它屏蔽了(main.swift):

// Writing to a dead karukan-imserver must not kill the IME with SIGPIPE. signal(SIGPIPE, SIG_IGN)

这是整个恢复方案的生存基础:哪怕唤醒后立刻有按键打到断掉的管道上,前端进程也只会在日志里记录一条错误,而不是当场"暴毙"。

第2层防御:监听唤醒通知,第一时间重启引擎

核心逻辑只有寥寥几行,位于 main.swift:

// macOS can discard pipe connections during sleep; restart the engine // process on wake to recover. NSWorkspace.shared.notificationCenter.addObserver( forName: NSWorkspace.didWakeNotification, object: nil, queue: .main ) { _ in NSLog("KarukanIME: wake from sleep — restarting karukan-imserver") engineProcess.restart() }

系统一发出didWakeNotification(睡眠唤醒广播),engineProcess.restart()立刻被调用。与其事后排查"管道到底断没断",不如默认它会断,醒来就换一条新的——这就是"优雅重启"的第一原则。

第3层防御:后台等旧进程退出,绝不冻结键盘

重启看似简单,其实藏着一个大坑:关闭旧进程需要"等它退出",而唤醒时刻恰好是旧进程可能永远收不到 EOF 的时刻。如果在主线程上阻塞等待(最长 2 秒),你打的每一个键都会卡住——对输入法来说是致命的。

Karukan 的做法(EngineProcess.swift):

  1. 后台排队等退出:旧进程的退出等待丢到后台线程,主线程立刻返回,按键处理照常进行;
  2. 优雅关闭优先:先关闭 stdin 发送 EOF,让引擎保存学习缓存后自行退出,最多等 2 秒,超时才用 SIGTERM 兜底;
  3. 防重复启动restartInFlight标志位保证唤醒通知连发两次也不会启动两个引擎;
  4. 复位退避计数:强制重启会把崩溃退避计数器清零,避免和崩溃重启的指数退避叠加。

第4层防御:自动重连 + 按键放行兜底

新引擎进程起来后,EngineClient.swift 的onRestart回调自动完成两件事:重连 stdout 读取循环、重发init请求重新加载模型。你无需任何手动操作。

在引擎"还没醒过来"的几秒窗口里,按键同步请求会超时返回 nil。此时 KarukanInputController.swift 的兜底逻辑接管:

guard let result = engineClient.processKeySync(key) else { // Engine busy or dead: let the key pass through rather than // freezing input. return false }

按键直接透传给系统,打字不中断。这个取舍来自一个硬约束:macOS 的键盘回调必须同步回答"这个键被吃掉了没有",所以宁可短暂退化成英文直输,也绝不阻塞输入。

如何排查:看日志确认自动恢复是否生效

所有日志(Swift 侧 NSLog 与 Rust 侧 tracing)统一落在:

~/Library/Logs/KarukanIME/karukan-ime.log

唤醒恢复正常的标志:先出现wake from sleep — restarting karukan-imserver,随后出现karukan-imserver started。模型重新加载需要几秒钟,期间英文直输,加载完成后日文转换自动恢复,无需干预。

如果唤醒后完全无反应,按顺序排查:

  1. 打开上述日志文件,确认是否有wake from sleep记录;
  2. 若出现karukan-imserver not found,说明引擎二进制缺失,重新执行make install
  3. 手动killall KarukanIME,下次点击文本框时会自动重新拉起(见 macOS README 的更新流程);
  4. 极端情况:从系统设置的输入源中删除并重新添加 Karukan,或注销重新登录。

如果反复崩溃重启:日志会显示指数退避间隔(2s、4s、8s、15s 封顶)。可以用 JSON-RPC 单独直连引擎进程做调试(cargo run -p karukan-im --bin karukan-imserver,逐行发送 JSON 请求),快速定位是引擎本身的问题还是前端通信的问题。

常见问题

  • 唤醒后要等多久才能转汉字?取决于模型加载速度,通常几秒。假名输入与直输期间完全可用,模型就绪后自动切换,无需重启任何应用。
  • 频繁唤醒会不会很耗电?重启只发生在你合盖/开盖的时刻,平时零开销。
  • Linux 版(fcitx5)需要这套处理吗?不需要。睡眠唤醒丢管道是 macOS 的特性,Linux 端由 fcitx5 框架管理生命周期,不经过这条 stdin/stdout 管道链路。

总结

Karukan 处理 macOS 睡眠唤醒的思路,本质是四层递进的工程防御:

  • 忽略 SIGPIPE:让进程先活下来;
  • 监听didWakeNotification:把"排查"变成"预防",醒来即重启;
  • 后台等待 + 优雅 EOF 关闭:重启过程不占用主线程,键盘永不冻结;
  • 自动重连 + 按键放行:恢复窗口期体验降级但不中断。

对普通用户来说,这套机制完全透明——合盖、开盖、继续打字,就像什么都没发生过。这也是 Karukan 作为日常主力输入法的底气所在。

【免费下载链接】karukanJapanese Input Method System for Linux, macOS, Neural Kana-Kanji Conversion Engine项目地址: https://gitcode.com/GitHub_Trending/ka/karukan

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询