[software development] 搜狗输入法打不出字?cpis 后端看门狗方案
Tofloor
poster avatar
winds
deepin
2026-10-22 16:50
Author

Deepin 25 搜狗输入法突然打不出字?给 cpis 多进程加 systemd 看门狗

环境:Deepin 25 / X11 / DDE / fcitx5 5.1.12 / 搜狗输入法 NG(sogou-ime-ng 10.0.10.0)
说明:非官方方案,只是把"进程死了没人拉活"这件事补上;搜狗升级后路径可能变化,需自行核对。

一、现象

用着用着切到搜狗就打不出中文,候选窗不出现,有时过几秒恢复,有时必须重启 fcitx5 甚至注销重登。检查进程会发现:

  • fcitx5 主进程还活着
  • 但 cpis-engine-service / cpis-panel-service / cpis-hotkey-service 三个后端已经不见了,或者只剩一个"空壳"

二、根因

搜狗 NG 在 fcitx5 里只是一个前端插件,真正的拼词引擎和候选窗是两个独立的 D-Bus 按需激活进程:

应用 → fcitx5 → libcpis-module-im-...so(前端插件)
                    ├── com.cpis.engine → cpis-engine-service(拼词/词库)
                    └── com.cpis.panel  → cpis-panel-service(候选窗)

这些后端由 D-Bus 激活,没有 systemd Restart。后端异常退出后,前端插件还缓存着失效连接,于是每次按键都失败。

本机 ~/.cache/fcitx5/fcitx5.log 里的直接证据:

call method [KeyDown] error: [107]   # ENOTCONN,后端 socket 已断开,按键直接丢
call method [KeyDown] error: [110]   # ETIMEDOUT,后端无响应
KeyUp error: [Process com.cpis.panel exited with status 1]
Failed to add match for signal ...: -107   # 候选窗彻底不出现

另外 /opt/apps/com.cpis/etc/ise.ini 里有引擎自检:

check_proc_status_vmrss_size = true
check_proc_status_vm_rss_size_limit_kb = 512000   # 超过 500MB 自行退出

内存紧张时会更容易触发,所以这问题常常和"内存/swap 快满"一起出现。

三、方案:让 systemd 接管三个后端

思路:开机时由 systemd 先把三个服务拉起来。服务自己拿到 D-Bus 名字后,D-Bus 激活机制发现"名字已有 owner",就不会再产生第二个实例;进程挂了由 Restart=always 拉活。

1. 等 X socket 的小脚本

~/.local/bin/ime-wait-x.sh:

#!/usr/bin/env bash
# 等 X socket 就绪,避免 Gtk-WARNING: cannot open display 那类抢跑
TIMEOUT=${1:-60}
i=0
while [ "$i" -lt "$TIMEOUT" ]; do
  [ -S /tmp/.X11-unix/X0 ] && exit 0
  sleep 1; i=$((i+1))
done
exit 0
chmod +x ~/.local/bin/ime-wait-x.sh

2. 三个 user unit

以 engine 为例,~/.config/systemd/user/cpis-engine.service:

[Unit]
Description=Sogou IME NG engine (systemd supervised)
After=graphical-session.target
Wants=graphical-session.target
# 抢名失败时不要熔断,否则单元永久 failed,再也没人拉活
StartLimitIntervalSec=0

[Service]
Type=simple
ExecStartPre=/bin/bash %h/.local/bin/ime-wait-x.sh 60
ExecStart=/opt/apps/com.cpis/bin/com.cpis.engine.service.start.sh
Environment=DISPLAY=:0
Environment=XAUTHORITY=%h/.Xauthority
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%U/bus
Restart=always
RestartSec=3
TimeoutStartSec=90
TimeoutStopSec=15
Nice=10

[Install]
WantedBy=graphical-session.target

cpis-panel.service、cpis-hotkey.service 复制一份,把 Description、ExecStart 里的 engine 换成 panel / hotkey 即可。

3. 启用(关键:不要在当前会话直接 start)

此时 D-Bus 激活的三个实例通常已经在跑。如果直接 systemctl --user start,systemd 实例会因为 D-Bus 名字被占用而注册失败退出,接着 Restart=always 进入重启死循环。

正确做法是二选一:

  • 做法 A(推荐):只 enable,然后注销/重启,让 systemd 在下次登录时抢先拉起:
systemctl --user daemon-reload
systemctl --user enable cpis-engine.service cpis-panel.service cpis-hotkey.service
# 重启或注销重登
  • 做法 B(不重启):先结束现有实例,再启动:
for n in cpis-engine-service cpis-panel-service cpis-hotkey-service ISE_NODE; do
  pkill -x "${n:0:15}" 2>/dev/null
done
systemctl --user daemon-reload
systemctl --user enable --now cpis-engine.service cpis-panel.service cpis-hotkey.service

4. 验证

systemctl --user status cpis-engine.service cpis-panel.service cpis-hotkey.service
busctl --user list | grep com.cpis     # 每个名字只应有一个 owner
fcitx5-remote -n                       # 当前输入法

再故意 kill 一个后端:

pkill -x cpis-engine-ser
sleep 5
systemctl --user is-active cpis-engine.service   # 应为 active

5. 回退

systemctl --user disable cpis-engine.service cpis-panel.service cpis-hotkey.service
rm ~/.config/systemd/user/cpis-{engine,panel,hotkey}.service
rm ~/.local/bin/ime-wait-x.sh
# 注销重登,交回 D-Bus 激活

四、补充:进程活着但主循环卡死怎么办

上面的 unit 只能处理"进程死了"。还有一种更隐蔽的情况:fcitx5 进程在、D-Bus 名字也在,但主循环被残缺后端阻塞,完全不应答。

可以加一个每分钟探活的 timer,探针用与窗口焦点无关的 D-Bus Introspect:

#!/usr/bin/env bash
set -u
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus"
STAMP=/tmp/fcitx5-watchdog.last
bad=0
for i in 1 2 3; do
  if timeout 8 busctl --user --timeout=6 --no-pager call \
       org.fcitx.Fcitx5 / org.freedesktop.DBus.Introspectable Introspect >/dev/null 2>&1; then
    bad=0; break
  fi
  bad=$((bad+1)); sleep 5
done
[ "$bad" -lt 3 ] && exit 0

now=$(date +%s); last=$(cat "$STAMP" 2>/dev/null || echo 0)
[ $((now - last)) -lt 600 ] && exit 0    # 10 分钟冷却,避免恢复风暴
echo "$now" > "$STAMP"
systemctl --user restart cpis-engine.service cpis-panel.service cpis-hotkey.service

配套 fcitx5-ime-watchdog.service(Type=oneshot)+ .timer(OnUnitActiveSec=1min)即可。

注意:如果卡死的是 fcitx5 本体而不是 cpis 后端,重启三个后端不一定能救回来,最稳仍是注销重登。

五、已知限制

  • 只在 X11 下验证;Wayland(treeland)下 DISPLAY=:0 和 X socket 逻辑需要另测。
  • 搜狗升级可能改 start.sh 路径或启动参数,升级后建议 systemctl --user status cpis-* 看一眼。
  • 这套方案不修复搜狗自身的架构问题(多进程 + 无重连),只是把"彻底打不出字"降级成"短暂卡顿后自动恢复"。
  • 如果不需要搜狗,换 fcitx5 原生拼音/Rime 可以从根上避免这类 IPC 断裂。

如果你也遇到"切到搜狗没反应",可以先看两处日志再决定要不要上这套:

tail -50 ~/.cache/fcitx5/fcitx5.log
systemctl --user status cpis-engine.service cpis-panel.service cpis-hotkey.service
Reply Favorite View the author
All Replies
avatar
沈拙言
deepin
2026-10-23 08:56
#1

确实是个不错的方案

Reply View the author
avatar
靓仔哥
deepin
2026-10-23 09:52
#2

我每次都是重启输入法sob

Reply View the author