LinPad OS 起不来时,图形界面没了,但日志通道并没有全部消失。通常还能从四个地方入手:iPad 上启动器本身的 iOS 侧日志、USB 串口、USB 网卡或 Wi‑Fi 上ssh 到 guest、以及接了键盘后的本地 tty 切换。建议按“越不依赖 guest 系统越优先”的顺序试,先把能进命令环境的那一条打通;拿到 shell 后第一时间抓本次启动的日志并导出成文件,再从日志头部按时间往下找第一个失败项,而不是盯着尾部那一堆连锁报错。
在 iPad 上排查 Linux 启动失败,最先可用的通道往往不在 Linux 里:iOS 侧启动器日志、串口、USB 网络上的 SSH、本地 tty,按依赖程度由低到高试。任何一条通了,就先把 /dev/kmsg 和 journalctl -b 落成文件再分析。若内核在进用户态前就 panic,用户态日志不会生成,只能回看串口输出或 pstore。各入口是否默认启用,需按你所用镜像与项目文档核对。
先确认还有哪些入口能进系统
目标只有一个:拿到一个能敲命令的环境。没桌面不等于没终端,先判断系统卡在哪一层,再选代价最小的入口。
- 本地 tty 切换:如果屏幕上有输出、只是图形没起来,接有线 USB 键盘按 Ctrl+Alt+F2 到 F6 切到 getty。前提是镜像里启用了对应 getty 单元、内核虚拟终端正常;蓝牙键盘在启动早期多半不可用,优先用有线键盘。
- 远程登录:ssh 进 guest,前提是网络在启动阶段已被拉起(USB gadget 网卡或自动连上的 Wi‑Fi),且 sshd 已启动。系统如果卡在图形服务,sshd 常常还活着,成功率不低;如果连根文件系统都没挂上,这条路基本不通。
- 串口控制台:USB‑C 暴露的调试串口或板载调试排针,需要内核命令行里有 console= 指向对应 UART、且该串口驱动已编译进内核。接线电平、引脚定义、波特率必须按硬件说明核对,接错可能损伤设备。
- iOS 侧日志:启动器/加载器本身是 iPad 上的普通应用,它的日志和崩溃报告不依赖 guest 系统是否启动成功。抓取方式可用 macOS 的控制台应用按设备过滤,或用 libimobiledevice 里的 idevicesyslog 重定向到文件;iPad 上“设置 → 隐私与安全性 → 分析与改进 → 分析数据”里的条目也可作为补充。
常见选择顺序是:先看 iOS 侧日志确认启动器把内核和 initrd 交接出去了没有,再试串口看内核有没有打印,最后才指望 SSH 和本地 tty。每一层能给出的信息不同,别在一条通道上耗太久。
确认启动日志落在哪里
抓错文件是最常见的返工原因。Linux 侧启动日志通常分两套,覆盖的时间范围不一样。
- 内核环形缓冲:/dev/kmsg 与 dmesg 读取的是同一份缓冲。它从内核第一行打印开始,但容量有限,早期行会被后来的打印覆盖;只有本次启动,重启即丢失。
- 用户态日志:systemd 系统用 journalctl 读 journal,是否跨重启保留取决于 /var/log/journal 目录是否存在(不存在时通常是内存里的易失 journal)。传统 rsyslog 系统一般落在 /var/log/syslog、/var/log/messages、/var/log/kern.log、/var/log/boot.log,具体文件名和是否启用需按发行版默认配置确认。
- 崩溃残留:/sys/fs/pstore 下若出现 dmesg-ramoops-* 之类文件,说明镜像预留了 ramoops 区域,能拿到上一轮异常重启前留下的内核日志。这个能力是否可用,取决于设备树是否配置,不能默认存在。
简单判断:想看“内核有没有起来、驱动探测到哪一步”看 dmesg;想看“哪个服务没起来、依赖谁”看 journalctl -b 或 syslog。两者时间起点不同,内核日志更早,用户态日志更细。
抓取本次启动的完整日志
拿到 shell 后第一件事是落盘,别在终端里滚屏读。下面是通用的命令骨架,设备名、路径和可用工具按实际环境替换。
# 本次启动的内核日志(尽早执行,避免环形缓冲被冲掉)
dmesg > /var/tmp/dmesg-$(date +%s).log
# 本次启动的用户态日志(有 systemd-journald 时可用)
journalctl -b -0 `--no-pager` -o short-precise > /var/tmp/journal-b0.log 2>/var/tmp/journal-b0.err
# 上一次启动,用于和“以前能起来”的状态对比
journalctl -b -1 `--no-pager` > /var/tmp/journal-b1.log 2>/var/tmp/journal-b1.err
# 没有 journald 时,改为读 syslog 或直接抓内核缓冲
cp /var/log/syslog /var/tmp/syslog-copy.log 2>/dev/null
几点经验:dmesg 越早抓越好,等系统跑起来再抓,开头那段驱动探测信息可能已经被刷掉;如果卡在启动中途,可以临时在内核命令行加 systemd.unit=rescue.target 或 single 之类的目标,先取得一个不依赖图形栈的环境再抓日志;某些精简镜像里 dmesg 缓冲区很小,可以先执行 dmesg -s 提高读取大小再重定向。抓完后立刻确认文件非空(wc -l 看行数),避免拿一个 0 字节文件去分析。
按时间顺序找第一个失败项
启动失败的日志里,后面一大段几乎都是前一个错误引发的连锁反应:挂载失败导致服务起不来,服务起不来导致目标超时。从尾部读很容易被带偏,正确做法是从日志头部开始,按时间戳往下扫,找第一个真正“失败”的行,再往上翻十几到几十行看是谁触发的。
可以先做一次粗筛,把可疑行连行号一起列出来:
grep -n -E 'Failed|failed|timed out|Timeout|panic|Call Trace|not syncing|error -[0-9]+' /var/tmp/journal-b0.log | head -n 50
- 通常算失败的信号:Failed to start / Failed to mount / Dependency failed for / Timed out waiting for device / Job xxx failed / Kernel panic - not syncing / Call Trace 之后紧跟的驱动函数名。
- 往往只是警告:WARNING: 开头的行、ACPI Error(不少固件都有,未必影响启动)、deprecated、random: crng init done、部分 firmware failed to load(非关键硬件的固件缺失不一定阻断启动)。
- 需要单独看的一类:deferred probe,它表示驱动等依赖还没就绪,本身不是最终错误,要找到最后仍未成功的那个设备。
另一个有用分界:日志里如果已经出现 Started xxx、Reached target xxx,说明内核和 init 大体走通,问题在用户态服务或图形栈;如果一条用户态日志都没有,dmesg 到某行戛然而止,问题就在内核、设备树或内核命令行参数上。这个判断直接决定下一步是查驱动还是查单元依赖。
把日志导出到可读的位置
设备上通常没有顺手的翻页和搜索工具,把文件搬到电脑上读效率高得多。有网络时优先 scp,没有网络就写到一个能被 iPad 侧访问的分区,或者用 nc 直传。
# 在电脑上拉取(把 user 和地址换成实际值)
scp user@192.168.xx.xx:/var/tmp/journal-b0.log ./journal-b0.log
# 设备端生成校验值,导出后在电脑上对比
sha256sum /var/tmp/journal-b0.log
wc -l /var/tmp/journal-b0.log
# 没有 ssh 时可用 nc 简单传输(两端需先约定端口)
nc -l -p 9000 < /var/tmp/journal-b0.log
核对完整性建议做三步:比对两端的 sha256sum,确认行数一致,再看文件末尾是否停在一条完整行上(截断的文件最后一行往往没有换行或明显断在半句)。如果传输中断,重传一次比在残缺文件上分析更省时间。导出完成后保留原始文件不要覆盖,后续复现同一个问题时,上一次启动的 journalctl -b -1 和这次的日志对照着看,往往比单看一次更快定位第一个失败点。