环境:
macOS 27.0 Beta(26A5378n) Emacs 32.0.50 Homebrew emacs-plus@32 NS/macOS GUI 版本
最近在 macOS 27 Beta 上遇到了一个与 restart-emacs 有关的问题。
现象
正常启动 Emacs 没有问题,例如通过 Hammerspoon 或:
open -na /Applications/Emacs.app
启动后窗口、键盘输入和焦点都正常。
但是执行:
M-x restart-emacs
之后会出现以下现象:
- Emacs 窗口已经显示出来。
- 鼠标可以点击窗口。
- Dock 中的 Emacs 图标一直跳动,类似应用仍在等待完成启动。
- Emacs 无法正常获得键盘焦点。
- M-x 等键盘操作不可用。
- Emacs 进程实际上仍然存在,并没有崩溃。
重新编译和安装 Emacs 后问题依然存在;只有彻底结束旧进程,再以新 PID 启动 Emacs 才能恢复正常。
调查结果
对异常状态下的 Emacs 进程执行 sample,可以看到 Emacs 主线程仍然处于正常事件循环:
Frecursive_edit command_loop read_key_sequence read_char wait_reading_process_output NSApplication run
因此它不是普通的启动卡死或 Emacs Lisp 阻塞。
macOS Unified Log 中则可以看到类似的状态变化:
SETFRONT: pid=76505 QUITTING: pid=76505 CHECKIN: pid=76505
也就是说,同一个 PID 被系统记录为正在退出,随后又重新向 LaunchServices 注册。
但是 RunningBoard 仍然将该进程保留在 terminating 状态。之后尝试激活 Emacs 时会出现:
Error Domain=RBSAssertionErrorDomain Code=2 “Specified target process 76505 does not exist”
这与实际表现吻合:Emacs 进程和窗口仍然存在,但 macOS 已经不再将该 PID 视为可以正常激活的前台应用。
可能的原因
Emacs 32 中的 restart-emacs 最终调用:
(save-buffers-kill-emacs nil t)
第二个参数为 t 时,Emacs 会通过 exec 重新执行自身,因此重启前后复用同一个 PID。
目前的推测是:
- Emacs 通知 AppKit/LaunchServices 当前应用正在退出。
- macOS 将该 PID 标记为 QUITTING 或 terminating。
- Emacs 没有真正结束该 PID,而是在同一 PID 内 exec 自身。
- 新的 Emacs 再次 CHECKIN。
- macOS 27 Beta 没有正确清除该 PID 原来的终止状态。
- 最终导致窗口存在,但应用无法正常激活,Dock 图标持续跳动。
这可能是 macOS 27 Beta 的 LaunchServices/RunningBoard 生命周期变化与 Emacs 同 PID 重启机制之间的兼容性问题。
与 Background App Activity 的关系
最初怀疑过 macOS 27 新增的 Background App Activity 管理,但进一步检查后发现:
- Emacs 不在当前的 Background App Activity 数据库中。
- Emacs.app/Contents/Info.plist 没有 LSBackgroundOnly。
- 也没有 LSUIElement。
- Emacs 本身是普通的前台 GUI App。
因此目前不能认定这是后台运行权限导致的。
RunningBoard 不只管理后台 App,也负责普通前台 App 的启动、退出和激活。更准确地说,这应该是普通 GUI App 生命周期状态在同 PID exec 后没有正确恢复。
临时解决办法
不要使用 Emacs 原本的同 PID exec 重启,而是:
- 让当前 Emacs 正常退出。
- 等待旧 PID 完全消失。
- 通过 open -na 启动一个全新 PID 的 Emacs。
目前使用的 Emacs Lisp workaround:
;; 2026-07-15: Work around a macOS 27 beta LaunchServices/RunningBoard issue.
;; `restart-emacs' reuses the current PID, which can leave the relaunched Emacs
;; visible but unable to activate while its Dock icon keeps bouncing. Wait for
;; the old process to exit completely, then launch a fresh app process.
(setup files
(defun +restart-emacs-after-exit ()
"Launch a new macOS Emacs after the current process exits."
(remove-hook 'kill-emacs-hook #'+restart-emacs-after-exit)
(call-process
"/usr/bin/nohup" nil 0 nil
"/bin/sh" "-c"
(format
"while /bin/kill -0 %d 2>/dev/null; do /bin/sleep 0.1; done; exec /usr/bin/open -na %s"
(emacs-pid)
(shell-quote-argument "/Applications/Emacs.app"))))
(define-advice restart-emacs (:override () macos-safe-relaunch)
"Restart Emacs without reusing the current process ID."
(interactive)
(add-hook 'kill-emacs-hook #'+restart-emacs-after-exit 100)
(unwind-protect
(save-buffers-kill-emacs)
(remove-hook 'kill-emacs-hook #'+restart-emacs-after-exit))))
使用该方案后,新的 Emacs 拥有全新 PID,可以正常完成 LaunchServices 注册,Dock、焦点和键盘输入均恢复正常。