Systemd启动Common Lisp Web应用报compilation unit aborted错误排查
问题根因
手动终端执行make run正常、systemd启动失败的核心原因是SBCL完成服务初始化后默认进入交互REPL尝试读取用户输入,但systemd运行的服务未绑定交互式终端(TTY),标准输入处于不可读状态,SBCL读取输入失败触发致命错误直接中止进程。
日志里出现的*就是SBCL的REPL提示符,提示符输出完成后SBCL会立刻尝试读取下一条输入,非交互环境下该读操作直接失败,就抛出了编译单元中止的错误。这个错误发生在你的业务代码执行完成、控制权交还给SBCL顶层之后,所以你写的handler-case/handler-bind无法捕获。
修复方案
按以下步骤调整配置即可解决:
1. 补全Systemd服务配置
当前.service文件缺少必要的运行身份配置,修改为如下内容:
[Unit] Description=my app After=network.target [Service] # 指定服务运行用户为普通用户vince,避免默认root运行导致的权限、环境变量、家目录路径不匹配问题 User=vince Group=vince WorkingDirectory=/home/vince/projets/myapp/ ExecStart=/usr/bin/make run Type=simple Restart=on-failure # 明确配置标准输入为空,不尝试绑定终端 StandardInput=null # 输出统一交由journald管理,方便日志排查 StandardOutput=journal StandardError=journal
2. 调整Lisp启动逻辑,禁止进入交互REPL
当前启动代码在执行完myapp:startapp后直接将控制权交还给SBCL顶层,必须阻塞进程避免进入REPL读输入逻辑,两步调整:
- 修改Makefile中SBCL启动参数,追加非交互运行标识:
LISP ?= /usr/bin/sbcl --core /home/vince/projets/ciel/ciel --disable-debugger --noinform --non-interactive --userinit /home/vince/.sbclrc
- 在Lisp启动脚本的服务启动逻辑后增加永久阻塞逻辑,不要让代码执行完毕返回顶层:
(load "myapp.asd") (unless (ql:quickload "myapp") (uiop:quit 1)) (handler-case (progn (uiop:format! t "-------- start app…") (myapp:startapp :port (ignore-errors (parse-integer (uiop:getenv "PORT")))) (uiop:format! t "~&✔ Ready. You can access the application!~%") ;; 关键:永久阻塞当前线程,不返回SBCL顶层REPL (loop (sleep 60))) (error (c) (format *error-output* "~&An error occured: ~a~&" c) (trivial-backtrace:print-backtrace c) (uiop:quit 1)))
3. 可选排查项
如果调整后仍有问题,逐项检查:
- 确认自定义core文件
/home/vince/projets/ciel/ciel对vince用户有可读权限 - 暂时移除启动参数中的
--userinit /home/vince/.sbclrc测试,部分用户的sbclrc中包含依赖交互终端的逻辑(比如终端颜色判断、自动连接Slime等),非交互环境下会触发报错 - 不要修改
Type=forking配置,Hunchentoot默认在当前进程启动端口监听,不会fork后台进程,simple类型是正确配置
验证方法
配置修改完成后依次执行以下命令重载配置、启动服务:
sudo systemctl daemon-reload sudo systemctl start myapp.service sudo systemctl status myapp.service
服务状态显示为active (running)即为启动成功,9999端口可正常访问。
内容的提问来源于stack exchange,提问作者Ehvince
相关产品推荐
相关产品推荐

