General troubleshooting (简体中文)

From ArchWiki
翻译状态:本文是 General troubleshooting翻译。上次翻译日期:2018-04-02。如果英文版本有所更改,则您可以帮助同步翻译。

本文介绍了一些常规的故障排除方法。有关特定应用程序的问题,请参阅该特定程序的 wiki 页面。

常用手段

注意细节

为了解决遇到的问题,对当前使用的这个程序,你应该有一个基本的了解,这是 至关重要 的。它是如何工作的,它依赖什么才能正常运行?如果不能很好地回答这些问题,最好阅读一下这些出错程序的 Archwiki 文章。一旦你觉得你已经理解了它,你将更容易找出问题的原因。

常见问题 / 检查项

下面列出了许多常见问题,用于排查故障。在每个问题下都有注释,说明你该如何回答这个问题,紧接着的是一些如何收集数据的简单示例,还有查看各种日志的工具。

  1. 出现了什么故障?
    请尽可能精确地描述问题,这将帮助你在查找特定信息的时候不至感到困惑或扯到别的方面去。
  2. 是否显示了错误信息?(如果有的话)
    将包含相关 错误信息完整输出 复制并粘贴到类似 $HOME/issue.log 这样单独的文件中。例如,可以将以下 mkinitcpio 命令的输出转发到 $HOME/issue.log
    $ mkinitcpio -p linux >> $HOME/issue.log
  3. 可以复现这个故障码?
    如果是这样,请 按步骤 给出 确切的 指示/命令来做到这一点。
  4. 第一次遇到这些故障的时间,以及从没有故障到故障发生之间你修改了什么内容?
    如果这种情况发生在某次升级之后,可以列出 所有已升级的包。包括 版本号,同时粘贴 pacman.log (/var/log/pacman.log) 的升级日志。使用 systemd 的 systemctl 工具检查故障程序依赖的 所有 服务的运行状态。例如,可以将 systemd 命令的输出转发到 $HOME/issue.log
    $ systemctl status [email protected] >> $HOME/issue.log
    注意: 使用 >> 可以保证 $HOME/issue.log 中之前保存的内容不会被覆盖。

寻求解决

不要通过这样的表述来寻求问题的解决:

程序 X 不工作了。

描述整个系统的环境更有助于解决问题,比如:

应用程序 X 在执行 Z 任务时,会报出 Y 错误,条件是 A 和 B。

额外支援

利用你眼前的所有信息,你应该对系统里发生了什么有一个较好的认识,并且可以开始着手修复了。

如果需要额外支援,官方论坛 或 IRC (irc.freenode.net 上的 #archlinux 频道) 都可以提供帮助。更多频道可参考 IRC channels

当要求贴出 完整 的 输出 / 日志 时,不能仅仅贴出你认为的重要部分。信息来源应该包括:

  • 所有涉及到的命令的完整输出,不要只选择你认为相关的东西。
  • 来自 systemd 的 journalctl 的输出。要获取更多的输出,请使用 systemd.log_level=debug 引导参数。
  • 日志文件(看一下 /var/log 目录)
  • 相关的配置文件
  • 相关的驱动程序
  • 相关软件包的版本
  • 内核相关:dmesg。对于启动问题,至少贴出最后 10 行,多贴一些当然更好。
  • 网络相关:相关命令的完整输出,再加上所有相关配置文件。
  • Xorg 相关: /var/log/Xorg.0.log,如果你已经覆盖了出问题的日志,就贴在这之前的日志。
  • Pacman 相关:如果最近的更新弄坏了什么东西,请到 /var/log/pacman.log 找它。

贴出这些信息有一个更好的方式,就是使用在线剪贴板 (pastebin)。你可以 安装 pbpstAURgist 来自动上传信息。例如,要上传这次启动以来的 systemd 日志,可以这么做:

# journalctl -xb | pbpst -S

这将返回一个链接,你可以把它贴到论坛或 IRC。

另外,在提问之前,请先阅读 提问的智慧行为准则

系统启动问题

诊断 启动过程 中的问题主要是修改 内核参数,然后重启系统。

如果系统无法启动,可以从 live 镜像 启动并 chroot 到现有系统。

控制台的输出信息

在启动过程完成以后,屏幕会被清空并显示登录提示符,这使得用户无法看到初始化过程中的输出和其中的错误信息。这一默认特性可以使用接下来几节中的方法进行修改。

请注意,无论选择下面哪个方法,在启动后通过使用 dmesgjournalctl -b 都可以显示内核消息,用于检查错误。

输出流控制

以下是适用于大多数终端模拟器的基本操作,包括虚拟终端 (vc):

  • Ctrl+S 暂停输出
  • Ctrl+Q 继续输出

这样不仅会暂停输出,而且会暂停尝试打印到终端的程序,即暂停输出时会阻塞 write() 调用。 如果你的 init 进程出现冻结,请确保系统控制台没有暂停。

要查看已经显示过的错误信息,参见 Getty (简体中文)#将引导消息保留在 tty1 上

回滚显示

回滚显示允许用户查看已经从控制台滚动过去并消失的文字内容。这通常是在视频适配器和显示设备之间创建缓冲(称为回滚缓冲区)实现的。默认情况下,将缓冲的内容上下滚动的快捷键是 Shift+PageUpShift+PageDown

如果回滚内容没有完全包含足够的信息,可能需要扩大回滚缓冲区的大小来容纳更多输出。这可以通过配置内核的 framebuffer console(fbcon,帧缓冲控制台)来实现,修改 内核参数 fbcon=scrollback:Nk 即可,其中 N 是缓冲区大小,单位是 kB,默认是 32k。

如果这不奏效,你的 framebuffer console 可能没有正确地启用。参阅 Framebuffer Console documentation 来查找其他影响参数,比如修改帧缓冲驱动。

Debug 输出

在启动时,大部分来自内核的消息都是隐藏的,可以加入不同的内核参数来输出更多信息。最简单的是这些:

  • debug 可以同时启用来自内核和 systemd 的调试信息
  • ignore_loglevel 强制显示 所有 内核消息

在特定情况下有用的其他参数如下:

  • earlyprintk=vga,keep 在早期启动过程中就输出内核消息,用于内核在有输出前就崩溃的情况下。在 EFI 系统上要把 vga 改成 efi
  • log_buf_len=16M 用于分配一个更大 (16MB) 的内核消息缓冲区,用来保证 debug 输出不被覆盖。

还有很多独立的调试参数,用于在特定的子系统中启用调试,例如 bootmem_debugsched_debug。更多详细信息请参考 kernel parameter documentation

注意: 如果无法回滚的足够远以查看所需的启动输出,你需要扩大 回滚缓冲区 的大小。

故障恢复控制台

在启动过程中的某个阶段获取一个交互式 shell 可以帮助你准确找出问题出在哪里,以及为何失败。有几个内核参数可以做到这一点,但它们都启动了一个正常的shell,可以随时 exit 让内核恢复正在执行的操作:

  • rescue 在根文件系统刚刚被挂载为读写模式的时候启动一个 shell
  • emergency 可以在更早的时候启动 shell,早于大部分文件系统挂载之前
  • init=/bin/sh(作为最后的选择)把 init 程序改成 root shell。因为 rescueemergency 都依赖于 systemd,而这可以在 systemd 坏掉的时候工作

还有一个选择,那就是 systemd 的 debug-shell,它在 tty9 上新增了一个 root shell,可以按 Ctrl+Alt+F9 来使用。这个功能可以通过在 内核参数 中添加 systemd.debug-shell,或者是 启用 debug-shell.service 来打开。注意在使用完后禁用该服务,避免在每次启动时都打开 root shell 而带来安全风险。

Intel 显卡造成的黑屏

这很可能是 kernel mode setting 的问题造成的。尝试 disabling modesetting 或者修改 video port

加载内核时卡住

尝试添加 acpi=off 内核参数来关闭 ACPI。

调试内核模块

参见 Kernel modules#Obtaining information

调试硬件

  • 按照 udev#Debug output 的说明可以显示额外硬件调试信息。
  • 确保你的系统已经安装了 Microcode 更新。
  • 使用 Memtest86+ 来测试设备的 RAM,不可靠的 RAM 可能会导致一些非常奇怪的问题,从随机崩溃到数据损坏都有可能。

内核崩溃(Kernel panic)

Linux 内核进入不可恢复的故障状态时,即发生了“内核崩溃” (kernel panic)。这种状态通常来源于错误的硬件驱动程序,导致内核死锁,无响应并需要重新启动。在死锁之前,会生成一条诊断消息,其中包括:发生崩溃时的机器状态,导致崩溃的内核函数的调用栈,以及当前加载的模块的列表。幸运的是,使用 mainline(主线)版本的内核(比如官方仓库提供的内核)不会经常发生内核崩溃,但是当它们发生时,你需要知道如何处理它们。

注意: Kernel panics 有时被称为 oopsKernel oops,虽然 panics 和 oops 都是在出错的状态下才会发生,但是 oops 更为通用,因为它并不一定会导致内核死锁,有时内核可以通过结束出错的任务来恢复并继续运行。
提示: 在启动时传递参数 oops=panic 或者在 /proc/sys/kernel/panic_on_oops 中写入 1 来强制用 panic 代替可恢复的 oops。如果你认为自动恢复 oops 导致了小概率的系统不稳定,进而导致了后来的错误难以被诊断,那么建议你这样做。

查看 panic 信息

如果在早期启动过程中发生了 kernel panic,你或许会在控制台上看到包含 "Kernel panic - not syncing:" 的消息,一旦 Systemd 开始运行,内核消息就会被捕捉到并写入系统日志。但是,当 Kernel panic 发生时,内核发出的诊断信息 几乎不会 被写入硬盘上的日志文件,因为在 system-journald 运作前内核就死锁了。因此,检查内核崩溃消息的唯一方法是在崩溃时从控制台看错误信息(如果没有设置 kdump crashkernel 的话)。可以用以下内核参数来启动,这样就能重现 tty1 上的错误信息:

systemd.journald.forward_to_console=1 console=tty1
提示: 如果崩溃信息滚动得太快导致无法阅读,可以在启动时尝试添加 pause_on_oops=seconds 内核参数。

示例场景:模块损坏

根据诊断信息,我们可以大致推断出是哪个子系统或者模块导致了崩溃。在这个示例中,我们假设机器在启动时发生了 panic。注意那些用 粗体 标记的行:

kernel: BUG: unable to handle kernel NULL pointer dereference at (null) [1]
kernel: IP: fw_core_init+0x18/0x1000 [firewire_core] [2]
kernel: PGD 718d00067 
kernel: P4D 718d00067 
kernel: PUD 7b3611067 
kernel: PMD 0 
kernel: 
kernel: Oops: 0002 [#1] PREEMPT SMP
kernel: Modules linked in: firewire_core(+) crc_itu_t cfg80211 rfkill ipt_REJECT nf_reject_ipv4 nf_log_ipv4 nf_log_common xt_LOG nf_conntrack_ipv4 ... [3] 
kernel: CPU: 6 PID: 1438 Comm: modprobe Tainted: P           O    4.13.3-1-ARCH #1
kernel: Hardware name: Gigabyte Technology Co., Ltd. H97-D3H/H97-D3H-CF, BIOS F5 06/26/2014
kernel: task: ffff9c667abd9e00 task.stack: ffffb53b8db34000
kernel: RIP: 0010:fw_core_init+0x18/0x1000 [firewire_core]
kernel: RSP: 0018:ffffb53b8db37c68 EFLAGS: 00010246
kernel: RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000
kernel: RDX: 0000000000000000 RSI: 0000000000000008 RDI: ffffffffc16d3af4
kernel: RBP: ffffb53b8db37c70 R08: 0000000000000000 R09: ffffffffae113e95
kernel: R10: ffffe93edfdb9680 R11: 0000000000000000 R12: ffffffffc16d9000
kernel: R13: ffff9c6729bf8f60 R14: ffffffffc16d5710 R15: ffff9c6736e55840
kernel: FS:  00007f301fc80b80(0000) GS:ffff9c675dd80000(0000) knlGS:0000000000000000
kernel: CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
kernel: CR2: 0000000000000000 CR3: 00000007c6456000 CR4: 00000000001406e0
kernel: Call Trace:
kernel:  do_one_initcall+0x50/0x190 [4]
kernel:  ? do_init_module+0x27/0x1f2
kernel:  do_init_module+0x5f/0x1f2
kernel:  load_module+0x23f3/0x2be0
kernel:  SYSC_init_module+0x16b/0x1a0
kernel:  ? SYSC_init_module+0x16b/0x1a0
kernel:  SyS_init_module+0xe/0x10
kernel:  entry_SYSCALL_64_fastpath+0x1a/0xa5
kernel: RIP: 0033:0x7f301f3a2a0a
kernel: RSP: 002b:00007ffcabbd1998 EFLAGS: 00000246 ORIG_RAX: 00000000000000af
kernel: RAX: ffffffffffffffda RBX: 0000000000c85a48 RCX: 00007f301f3a2a0a
kernel: RDX: 000000000041aada RSI: 000000000001a738 RDI: 00007f301e7eb010
kernel: RBP: 0000000000c8a520 R08: 0000000000000001 R09: 0000000000000085
kernel: R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000c79208
kernel: R13: 0000000000c8b4d8 R14: 00007f301e7fffff R15: 0000000000000030
kernel: Code: <c7> 04 25 00 00 00 00 01 00 00 00 bb f4 ff ff ff e8 73 43 9c ec 48 
kernel: RIP: fw_core_init+0x18/0x1000 [firewire_core] RSP: ffffb53b8db37c68
kernel: CR2: 0000000000000000
kernel: ---[ end trace 71f4306ea1238f17 ]---
kernel: Kernel panic - not syncing: Fatal exception [5]
kernel: Kernel Offset: 0x80000000 from 0xffffffff810000000 (relocation range: 0xffffffff800000000-0xfffffffffbffffffff
kernel: ---[ end Kernel panic - not syncing: Fatal exception
  • [1] 表明了导致 panic 的错误类型。此处表示一个程序 bug。
  • [2] 表明 panic 发生在 firewire_core 模块中的 fw_core_init 函数中。
  • [3] 表明 firewire_core 模块是最后一个要加载的模块。
  • [4] 表明调用 fw_core_init 的函数是 do_one_initcall
  • [5] 表明这个 oops 消息事实上是一次 kernel panic 并且系统已经死锁。

我们可以猜测,panic 发生在模块 firewire_core 加载后的初始化例程中。(我们或许可以认为,由于程序错误,这台机器的火线 (IEEE 1394) 相关硬件与这一版本的驱动模块不兼容,并且需要等待下一个版本的驱动发布)与此同时,让机器运行起来的最简单方法,就是不要加载这个模块。我们可以通过以下两种方式之一来完成这个操作:

  • 如果这个模块在加载 initramfs 时就会被加载,那就在重启时加上内核参数 rd.blacklist=firewire_core
  • 否则就用这个内核参数重启 module_blacklist=firewire_core

重启至 root shell 并修复故障

为了更改系统设置使得 panic 不再发生,你需要一个 root shell。如果 panic 发生在启动时,有几种方法可以在内核死锁前获得一个 root shell:

  • 使用内核参数 emergencyrd.emergency-b 重启电脑,这样可以在根文件系统挂载且 systemd 启动后就收到登录提示符。
注意: 此时,根文件系统将会以只读方式挂载。执行 # mount -o remount,rw / 来改变这一设置。
  • 使用内核参数 rescuerd.rescuesinglesS1 在本地文件系统挂载完成后就得到登录提示符。
  • 使用内核参数 systemd.debug-shell=1 在 tty9 上获得一个早期 root shell,然后按 Ctrl-Alt-F9 切换到它。
  • 通过使用不同的内核参数重新引导来尝试禁用导致 panic 的内核功能。尝试“备用参数” acpi=offnolapic
提示: 参阅 Linux 内核源码树中的 Documentation/admin-guide/kernel-parameters.txt 文件来查找所有内核参数。
  • 作为最后的手段,你还可以使用 Arch Linux Installation CD 启动电脑,把根文件系统挂载到 /mnt,然后运行 # arch-chroot /mnt

禁用导致 panic 的服务或程序,回滚有故障的更新或修复配置问题。

软件包管理

参阅适用于一般主题的 Pacman#Troubleshooting,以及适用于 PGP 密钥问题的 pacman/Package signing#Troubleshooting

fuser

Tango-view-fullscreen.pngThis article or section needs expansion.Tango-view-fullscreen.png

Reason: 需要更多关于其用法的信息 (Discuss in Talk:General troubleshooting (简体中文))

fuser 是一个用于识别进程占用的资源(如打开的文件、文件系统和 TCP/UDP 端口)的命令行工具。

fuser 由软件包 psmisc 提供,已经由 base 组安装。更多信息请查看 fuser(1)

会话权限

注意: 你必须使用 systemd 作为你的 init 进程,本地会话才能正常工作。[1] 它是各种设备的 polkit 权限和 ACL 所必需的 (参见 /usr/lib/udev/rules.d/70-uaccess.rules[2]

首先,确保你有一个带 X 的可用本地会话:

$ loginctl show-session $XDG_SESSION_ID

在输出中需要带有 Remote=no and Active=yes 字样。如果没有,确保 X 运行在和登录时一样的 tty 里面。这是保留登录会话所必须的。

基本 polkit 操作不需要额外的配置。但有一些 polkit 操作需要请求额外的身份认证,即使是本地会话也是如此。为了达成这项工作,必须运行一个 polkit 身份认证组件。更多信息可参见 polkit (简体中文)#身份认证组件

错误信息: "error while loading shared libraries"

Tango-inaccurate.pngThe factual accuracy of this article or section is disputed.Tango-inaccurate.png

Reason:soname bump 之后相关程序可能需要重新编译。 (Discuss in Talk:General troubleshooting (简体中文))

如果在运行程序时遇到类似于这样的错误:

error while loading shared libraries: libusb-0.1.so.4: cannot open shared object file: No such file or directory

使用 pacmanpkgfile 来查找包含丢失共享库的软件包:

$ pacman -Fs libusb-0.1.so.4
extra/libusb-compat 0.1.5-1
    usr/lib/libusb-0.1.so.4

在上述例子中,需要 安装 软件包 libusb-compat

这个错误也有可能意味着你用来安装这个软件的 PKGBUILD 里没有将这个共享库作为它的依赖库:如果来自官方源,请 报告一个 bug;如果来自 AUR,请在 AUR 网站相关页面上把它报告给维护者。

错误信息: "file: could not find any magic files!"

如果看到这条消息,则可能表示软件包更新破坏了动态链接程序的运行时依赖文件,并且系统现在已经基本瘫痪。在修复之前,你将无法重新编译或重新安装软件包或重建 initramfs

错误原因

某次软件更新可能在 /etc/ld.so.conf.d 目录中添加了非法的 filename.conf 文件,或是错误地编辑了 /etc/ld.so.conf 文件。其后果就是动态链接程序的运行时依赖文件 /etc/ld.so.cache 使用了错误的数据重新生成了。这可能导致系统中所有依赖共享库的程序都出错(即几乎所有程序出错)。

解决方法

  1. 使用 Arch Linux Installation CD 启动。
  2. 挂载根文件系统 //mnt,挂载 /boot 文件系统到 /mnt/boot,然后用命令 # arch-chroot /mnt 切换到受损的系统中。
  3. 检查 /etc/ld.so.conf 文件,删除所有不正确的内容。
  4. 检查放在 /etc/ld.so.conf.d/ 目录里的文件,删除所有不正确的文件。
  5. 使用命令 # ldconfig 重新生成动态链接程序的运行时依赖文件 /etc/ld.so.cache
  6. 使用命令 # mkinitcpio -p linux 重新生成 Initramfs
  7. 退出 chroot 环境,卸载文件系统,然后重启到原来的系统中。

参阅