Warning

此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。

Note

如果您发现本文档与原始文件有任何不同或者有翻译问题,请联系该文件的译者, 或者请求时奎亮的帮助:<alexs@kernel.org>。

Original:

Linux Magic System Request Key Hacks

翻译:

黄军华 Junhua Huang <huang.junhua@zte.com.cn>

校译:

司延腾 Yanteng Si <siyanteng@loongson.cn>

Linux 魔法系统请求键骇客

针对 sysrq.c 的文档说明

什么是魔法 SysRq 键?

它是一个你可以输入的具有魔法般的组合键。 无论内核在做什么,内核都会响应 SysRq 键的输入,除非内核完全卡死。

如何使能魔法 SysRq 键?

在配置内核时,我们需要设置 ‘Magic SysRq key (CONFIG_MAGIC_SYSRQ)’ 为 ‘Y’。 当运行一个编译进 sysrq 功能的内核时,/proc/sys/kernel/sysrq 控制着被 SysRq 键调用的功能许可。这个文件的默认值由 CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE 配置符号设定,文件本身默认设置为 1。以下是 /proc/sys/kernel/sysrq 中可能的 值列表:

  • 0 - 完全不使能 SysRq 键

  • 1 - 使能 SysRq 键的全部功能

  • >1 - 对于允许的 SysRq 键功能的比特掩码(参见下面更详细的功能描述):

      2 =   0x2 - 使能对控制台日志记录级别的控制
      4 =   0x4 - 使能对键盘的控制 (SAK, unraw)
      8 =   0x8 - 使能对进程的调试导出等
     16 =  0x10 - 使能同步命令
     32 =  0x20 - 使能重新挂载只读
     64 =  0x40 - 使能对进程的信号操作 (term, kill, oom-kill)
    128 =  0x80 - 允许重启、断电
    256 = 0x100 - 允许让所有实时任务变普通任务
    

你可以通过如下命令把值设置到这个文件中:

echo "number" >/proc/sys/kernel/sysrq

这里被写入的 number 可以是 10 进制数,或者是带着 0x 前缀的 16 进制数。 CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE 必须是以 16 进制数写入。

注意,/proc/sys/kernel/sysrq 的值只影响通过键盘触发 SySRq 的调用,对于 通过 /proc/sysrq-trigger 的任何操作调用都是允许的 (通过具有系统权限的用户)。

如何使用魔法 SysRq 键?

在 x86 架构上

你可以按下键盘组合键 ALT-SysRq-<command key>

Note

一些键盘可能没有标识 ‘SySRq’ 键。’SySRq’ 键也被当做 ‘Print Screen’键。 同时有些键盘无法处理同时按下这么多键,因此你可以先按下键盘 Alt 键, 然后按下键盘 SysRq 键,再释放键盘 SysRq 键,之后按下键盘上命令键 <command key>,最后释放所有键。

在 SPARC 架构上

你可以按下键盘组合键 ALT-STOP-<command key>

在串行控制台(只针对 PC 类型的标准串口)

你可以发一个 BREAK ,然后在 5 秒内发送一个命令键, 发送 BREAK 两次将被翻译为一个正常的 BREAK 操作。

在 PowerPC 架构上

按下键盘组合键 ALT - Print Screen (或者 F13) - <命令键>Print Screen (或者 F13) - <命令键> 或许也能实现。

在其他架构上

如果你知道其他架构的组合键,请告诉我,我可以把它们添加到这部分。

在所有架构上

写一个字符到 /proc/sysrq-trigger 文件,例如:

echo t > /proc/sysrq-trigger

这个命令键 <command key> 是区分大小写的。

什么是命令键?

命令键

功能

b

将立即重启系统,不会同步或者卸载磁盘。

c

将执行系统 crash,如果配置了系统 crashdump,将执行 crashdump。

d

显示所有持有的锁。

e

发送 SIGTERM 信号给所有进程,除了 init 进程。

f

将调用 oom killer 杀掉一个过度占用内存的进程,如果什么任务都没杀, 也不会 panic。

g

kgdb 使用(内核调试器)。

h

将会显示帮助。(实际上除了这里列举的键,其他的都将显示帮助, 但是 h 容易记住):-)

i

发送 SIGKILL 给所有进程,除了 init 进程。

j

强制性的 “解冻它” - 用于被 FIFREEZE ioctl 操作冻住的文件系统。

k

安全访问秘钥(SAK)杀掉在当前虚拟控制台的所有程序,注意:参考 下面 SAK 节重要论述。

l

显示所有活动 cpu 的栈回溯。

m

将导出当前内存信息到你的控制台。

n

用于使所有实时任务变成普通任务。

o

将关闭系统(如果配置和支持的话)。

p

将导出当前寄存器和标志位到控制台。

q

将导出每个 cpu 上所有已装备的高精度定时器(不是完整的 time_list 文件显示的 timers)和所有时钟事件设备的详细信息。

r

关闭键盘的原始模式,设置为转换模式。

s

将尝试同步所有的已挂载文件系统。

t

将导出当前所有任务列表和它们的信息到控制台。

u

将尝试重新挂载已挂载文件系统为只读。

v

强制恢复帧缓存控制台。

v

触发 ETM 缓存导出 [ARM 架构特有]

w

导出处于不可中断状态(阻塞)的任务。

x

在 ppc/powerpc 架构上用于 xmon 接口。 在 sparc64 架构上用于显示全局的 PMU(性能监控单元)寄存器。 在 MIPS 架构上导出所有的 tlb 条目。

y

显示全局 cpu 寄存器 [SPARC-64 架构特有]

z

导出 ftrace 缓存信息

0-9

设置控制台日志级别,该级别控制什么样的内核信息将被打印到你的 控制台。(比如 0 ,将使得只有紧急信息,像 PANICs or OOPSes 才能到你的控制台。)

好了,我能用他们做什么呢?

嗯,当你的 X 服务端或者 svgalib 程序崩溃,unraw(r) 非原始模式命令键是非常 方便的。

sak(k)(安全访问秘钥)在你尝试登陆的同时,又想确保当前控制台没有可以获取你的 密码的特洛伊木马程序运行时是有用的。它会杀掉给定控制台的所有程序,这样你 就可以确认当前的登陆提示程序是实际来自 init 进程的程序,而不是某些特洛伊 木马程序。

Important

在其实际的形式中,在兼容 C2 安全标准的系统上,它不是一个真正的 SAK, 它也不应该误认为此。

似乎其他人发现其可以作为(系统终端联机键)当你想退出一个程序, 同时不会让你切换控制台的方法。(比如,X 服务端或者 svgalib 程序)

reboot(b) 是个好方法,当你不能关闭机器时,它等同于按下”复位”按钮。

crash(c) 可以用于手动触发一个 crashdump,当系统卡住时。 注意当 crashdump 机制不可用时,这个只是触发一个内核 crash。

sync(s) 在拔掉可移动介质之前,或者在使用不提供优雅关机的 救援 shell 之后很方便 – 它将确保你的数据被安全地写入磁盘。注意,在你看到 屏幕上出现 “OK” 和 “Done” 之前,同步还没有发生。

umount(u) 可以用来标记文件系统正常卸载,从正在运行的系统角度来看,它们将 被重新挂载为只读。这个重新挂载动作直到你看到 “OK” 和 “Done” 信息出现在屏幕上 才算完成。

日志级别 0 - 9 用于当你的控制台被大量的内核信息冲击,你不想看见的时候。 选择 0 将禁止除了最紧急的内核信息外的所有的内核信息输出到控制台。(但是如果 syslogd/klogd 进程是运行的,它们仍将被记录。)

term(e)kill(i) 用于当你有些有点失控的进程,你无法通过其他方式杀掉 它们的时候,特别是它正在创建其他进程。

“just thaw it(j) “ 用于当你的系统由于一个 FIFREEZE ioctl 调用而产生的文件 系统冻结,而导致的不响应时。

有的时候 SysRq 键在使用它之后,看起来像是“卡住”了,我能做些什么?

这也会发生在我这,我发现轻敲键盘两侧的 shift、alt 和 control 键,然后再次敲击 一个无效的 SysRq 键序列可以解决问题。(比如,像键盘组合键 alt-sysrq-z ) 切换到另一个虚拟控制台(键盘操作 ALT+Fn ),然后再切回来应该也有帮助。

我敲击了 SysRq 键,但像是什么都没发生,发生了什么错误?

有一些键盘对于 SysRq 键设置了不同的键值,而不是提前定义的 99 (查看在 include/uapi/linux/input-event-codes.h 文件中 KEY_SYSRQ 的定义) 或者就根本没有 SysRq 键。在这些场景下,执行 showkey -s 命令来找到一个合适 的扫描码序列,然后使用 setkeycodes <sequence> 99 命令映射这个序列值到通用 的 SysRq 键编码上(比如 setkeycodes e05b 99 )。最好将这个命令放在启动脚本 中。 哦,顺便说一句,你十秒钟不输入任何东西就将退出 “showkey”。

我想添加一个 SysRq 键事件到一个模块中,如何去做呢?

为了注册一个基础函数到这个表中,首先你必须包含 include/linux/sysrq.h 头 文件,这个头文件定义了你所需要的所有东西。然后你必须创建一个 sysrq_key_op 结构体,然后初始化它,使用如下内容,A) 你将使用的这个键的处理函数, B) 一个 help_msg 字符串,在 SysRq 键打印帮助信息时将打印出来,C) 一个 action_msg 字 符串,就在你的处理函数调用前打印出来。你的处理函数必须符合在 ‘sysrq.h’ 文件中 的函数原型。

sysrq_key_op 结构体被创建后,你可以调用内核函数 register_sysrq_key(int key, const struct sysrq_key_op *op_p);, 该函数在表中的 ‘key’ 对应位置内容是空的情况下,将通过 op_p 指针注册这个操作 函数到表中 ‘key’ 对应位置上。在模块卸载的时候,你必须调用 unregister_sysrq_key(int key, const struct sysrq_key_op *op_p) 函数,该函数 只有在当前该键对应的处理函数被注册到了 ‘key’ 对应位置时,才会移除 ‘op_p’ 指针 对应的键值操作函数。这是为了防止在你注册之后,该位置被改写的情况。

魔法 SysRq 键系统的工作原理是将键对应操作函数注册到键的操作查找表, 该表定义在 ‘drivers/tty/sysrq.c’ 文件中。 该键表有许多在编译时候就注册进去的操作函数,但是是可变的。 并且有两个函数作为操作该表的接口被导出:

register_sysrq_key 和 unregister_sysrq_key.

当然,永远不要在表中留下无效指针,即,当你的模块存在调用 register_sysrq_key() 函数,它一定要调用 unregister_sysrq_key() 来清除它使用过的 SysRq 键表条目。 表中的空指针是安全的。:)

如果对于某种原因,在 handle_sysrq 调用的处理函数中,你认为有必要调用 handle_sysrq 函数时,你必须意识到当前你处于一个锁中(你同时也处于一个中断处理 函数中,这意味着不能睡眠)。所以这时你必须使用 __handle_sysrq_nolock 替代。

当我敲击一个 SysRq 组合键时,只有标题打印出现在控制台?

SysRq 键的输出和所有其他控制台输出一样,受制于控制台日志级别控制。 这意味着,如果内核以发行版内核中常见的 “quiet” 方式启动,则输出可能不会出现在实际 的控制台上,即使它会出现在 dmesg 缓存中,也可以通过 dmesg 命令和 /proc/kmsg 文件的消费访问到。作为一个特例,来自 sysrq 命令的标题行将被传递给所有控制台 使用者,就好像当前日志级别是最大的一样。如果只发出标题头,则几乎可以肯定内核日志 级别太低。如果你需要控制台上的输出,那么你将需要临时提高控制台日志级别,通过使用 键盘组合键 alt-sysrq-8 或者:

echo 8 > /proc/sysrq-trigger

在触发了你感兴趣的 SysRq 键命令后,记得恢复日志级别到正常情况。

我有很多问题时,可以请教谁?

请教在内核邮件列表上的人,邮箱:

linux-kernel@vger.kernel.org

致谢