嵌入式Linux驱动笔记(三十一)------SYSRQ组合键使用

て烟熏妆下的殇ゞ 提交于 2020-08-06 04:04:43

你好!这里是风筝的博客,

欢迎和我一起交流。


ALT+SYSRQ组合键是Linux调试的一种手段,即使在系统死机、panic、卡住等情况,只要系统还能响应中断,那么SYSRQ就派上用场了(比如触发crash查看系统当前在干啥),具体的使用情况可以参考内核文档里的详细描述:Documentation/sysrq.txt

note:SYSRQ键也就是键盘上的Print Screen键.

使用SYSRQ组合键需要在内核开启配置:CONFIG_MAGIC_SYSRQ
可以通过在系统中使用

cat /proc/sys/kernel/sysrq

查看sysrq是否开启,sysrq值对应:

in /proc/sys/kernel/sysrq:
0 - disable sysrq completely
1 - enable all functions of sysrq
1 - bitmask of allowed sysrq functions (see below for detailed function
description):
2 - enable control of console logging level
4 - enable control of keyboard (SAK, unraw)
8 - enable debugging dumps of processes etc.
16 - enable sync command
32 - enable remount read-only
64 - enable signalling of processes (term, kill, oom-kill)
128 - allow reboot/poweroff
256 - allow nicing of all RT tasks











0就是关闭,1就是开启,当然也可以通过手动echo “number” >/proc/sys/kernel/sysrq设置sysrq,比如

    echo 1 >/proc/sys/kernel/sysrq

开启了sysrq怎么用呢?
有两种方式,如下:

一、/proc/sysrq-trigger节点

在终端输入:

echo <key> > /proc/sysrq-trigger

key的值在sysrq.txt也有说明,如下:

  • What are the ‘command’ keys?
    ‘b’ - Will immediately reboot the system without syncing or unmounting
    your disks.
    ‘c’ - Will perform a system crash by a NULL pointer dereference.
    A crashdump will be taken if configured.
    ‘d’ - Shows all locks that are held.
    ‘e’ - Send a SIGTERM to all processes, except for init.
    ‘f’ - Will call oom_kill to kill a memory hog process.
    ‘g’ - Used by kgdb (kernel debugger)
    ‘h’ - Will display help (actually any other key than those listed
    here will display help. but ‘h’ is easy to remember 😃
    ‘i’ - Send a SIGKILL to all processes, except for init.
    ‘j’ - Forcibly “Just thaw it” - filesystems frozen by the FIFREEZE ioctl.
    ‘k’ - Secure Access Key (SAK) Kills all programs on the current virtual
    console. NOTE: See important comments below in SAK section.
    ‘l’ - Shows a stack backtrace for all active CPUs.
    ‘m’ - Will dump current memory info to your console.
    ‘n’ - Used to make RT tasks nice-able
    ‘o’ - Will shut your system off (if configured and supported).
    ‘p’ - Will dump the current registers and flags to your console.
    ‘q’ - Will dump per CPU lists of all armed hrtimers (but NOT regular
    timer_list timers) and detailed information about all
    clockevent devices.
    ‘r’ - Turns off keyboard raw mode and sets it to XLATE.
    ‘s’ - Will attempt to sync all mounted filesystems.
    ‘t’ - Will dump a list of current tasks and their information to your
    console.
    ‘u’ - Will attempt to remount all mounted filesystems read-only.
    ‘v’ - Forcefully restores framebuffer console
    ‘v’ - Causes ETM buffer dump [ARM-specific]
    ‘w’ - Dumps tasks that are in uninterruptable (blocked) state.
    ‘x’ - Used by xmon interface on ppc/powerpc platforms.
    ‘y’ - Show global CPU Registers [SPARC-64 specific]
    ‘z’ - Dump the ftrace buffer
    ‘0’-‘9’ - Sets the console log level, controlling which kernel messages
    will be printed to your console. (‘0’, for example would make
    it so that only emergency messages like PANICs or OOPSes would
    make it to your console.)




































比如我要重启机器,向sysrq-trigger节点写入相应的字母即可:

echo b > /proc/sysrq-trigger

note:无论/proc/sys/kernel/sysrq是什么值,sysrq-trigger都是有效的。

二、键盘SYSRQ组合键

在键盘上通过组合键 ALT + SYSRQ + key 的方式使用(串口serial console不适用这种方法,后面会讲)

比如我要查看当前内存信息,可以在键盘上按 ALT + SYSRQ + m ,这样即可。

那么串口该怎么用SYSRQ呢,其实sysrq.txt文档里面也有说明:

On the serial console (PC style standard serial ports only) -
You send a BREAK, then within 5 seconds a command key. Sending
BREAK twice is interpreted as a normal BREAK.

也就是使用串口时,单机键盘上的BREAK 键,在5秒内,输出key值。
比如我要查看当前内存信息,可以在键盘上按 BREAK + m ,这样即可。
note:BREAK 键也就是键盘上的Pause Break键.

那么为什么在串口(嵌入式常用串口作为调试)上不适用ALT + SYSRQ + key 的方式的方式呢?我们可以简单的看下sysrq的实现:
sysrq的实现在drivers/tty/sysrq.c

static const struct input_device_id sysrq_ids[] = {
	{
		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
				INPUT_DEVICE_ID_MATCH_KEYBIT,
		.evbit = { [BIT_WORD(EV_KEY)] = BIT_MASK(EV_KEY) },
		.keybit = { [BIT_WORD(KEY_LEFTALT)] = BIT_MASK(KEY_LEFTALT) },
	},
	{ },
};

static struct input_handler sysrq_handler = {
	.filter		= sysrq_filter,
	.connect	= sysrq_connect,
	.disconnect	= sysrq_disconnect,
	.name		= "sysrq",
	.id_table	= sysrq_ids,
};
static inline void sysrq_register_handler(void)
{
	int error;

	sysrq_of_get_keyreset_config();

	error = input_register_handler(&sysrq_handler);
	if (error)
		pr_err("Failed to register input handler, error %d", error);
	else
		sysrq_handler_registered = true;
}

可以看到,sysrq是通过注册到input的,关于input可以参考我之前的文章:
嵌入式Linux驱动笔记(二十五)------Input子系统框架

sysrq_ids里面keybit字段填充的是:KEY_LEFTALT
这点在代码注释可以看到解释:
因为并不是所有键盘都有SysRq键,所以用KEY_LEFTALT键来匹配,因为基本每个键盘都会有Alt键。
接着看input_handler结构体的filter字段填充:sysrq_filter函数
SYSRQ的处理也在这里面了:



static bool sysrq_filter(struct input_handle *handle,
			 unsigned int type, unsigned int code, int value)
{
	switch (type) {
	case EV_SYN:
		suppress = false;
		break;
	case EV_KEY:
		suppress = sysrq_handle_keypress(sysrq, code, value);
		break;
	default:
		suppress = sysrq->active;
		break;
	}
	return suppress;
}

当输入设备上报KEY事件时,调用sysrq_handle_keypress函数。

static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
				  unsigned int code, int value)
{
	switch (code) {
	case KEY_LEFTALT:
	case KEY_RIGHTALT:
		if (!value) {
			/* One of ALTs is being released */
			if (sysrq->active && code == sysrq->alt_use)
				sysrq->active = false;
			sysrq->alt = KEY_RESERVED;

		} else if (value != 2) {
			sysrq->alt = code;
			sysrq->need_reinject = false;
		}
		break;

	case KEY_SYSRQ:
		if (value == 1 && sysrq->alt != KEY_RESERVED) {
			sysrq->active = true;
			sysrq->alt_use = sysrq->alt;
			sysrq->need_reinject = true;
		}
		if (sysrq->active)
			clear_bit(KEY_SYSRQ, sysrq->handle.dev->key);

		break;

	default:
		if (sysrq->active && value && value != 2) {
			sysrq->need_reinject = false;
			__handle_sysrq(sysrq_xlate[code], true);
		}
		break;
	}
	//后面省略......
}

这里就是会对input子系统上报的键值进行判断了,当键值是ALT + SYSRQ + key 的方式时,就会调用__handle_sysrq函数进行处理,__handle_sysrq函数就是根据key进行对应的操作。

我们知道,键盘就是一个标准的输入设备,平时按下按键,是会通过input子系统进行键值上报的,所以我们在键盘按下ALT + SYSRQ ,input子系统上报ALT + SYSRQ的键值,sysrq驱动是能解析处理的。

但是嵌入式设备在使用串口时,串口输入走的是tty子系统,并不会对你在串口输入的值往input里上报,所以你在串口里面按ALT + SYSRQ + key 是不会触发sysrq的!!!

那么uart走的是tty流程,不走input,那uart怎么使用sysrq呢?
其实,sysrq里export有一个接口出来:handle_sysrq

void handle_sysrq(int key)
{
	if (sysrq_on())
		__handle_sysrq(key, true);
}

这里面和通过键盘ALT + SYSRQ最终处理的函数是相同的,都是__handle_sysrq函数!!!所以串口可以通过使用handle_sysrq函数实现。

文章开头描述有,想在串口使用sysrq,得按BREAK键,这部分在uart驱动处理,不过不同厂家的uart驱动各有不同,不过大体都是一样的,如果你在串口按BREAK + key 没反应,可能是你设备的uart驱动没有支持sysrq。

那uart驱动里是怎么解析捕获break的呢,可以参考
drivers/tty/serial/8250/8250_port.c
想要使用SYSRQ,得先定义 SUPPORT_SYSRQ 宏

#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
static void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr)
{
	struct uart_port *port = &up->port;
	unsigned char ch;
	char flag = TTY_NORMAL;

	if (likely(lsr & UART_LSR_DR))
		ch = serial_in(up, UART_RX);
	else
		ch = 0;

	port->icount.rx++;

	lsr |= up->lsr_saved_flags;
	up->lsr_saved_flags = 0;

	if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) {
		if (lsr & UART_LSR_BI) {
			lsr &= ~(UART_LSR_FE | UART_LSR_PE);
			port->icount.brk++;
			/*
			 * We do the SysRQ and SAK checking
			 * here because otherwise the break
			 * may get masked by ignore_status_mask
			 * or read_status_mask.
			 */
			if (uart_handle_break(port))
				return;
		} else if (lsr & UART_LSR_PE)
			port->icount.parity++;
		else if (lsr & UART_LSR_FE)
			port->icount.frame++;
		if (lsr & UART_LSR_OE)
			port->icount.overrun++;

		/*
		 * Mask off conditions which should be ignored.
		 */
		lsr &= port->read_status_mask;

		if (lsr & UART_LSR_BI) {
			pr_debug("%s: handling break\n", __func__);
			flag = TTY_BREAK;
		} else if (lsr & UART_LSR_PE)
			flag = TTY_PARITY;
		else if (lsr & UART_LSR_FE)
			flag = TTY_FRAME;
	}
	if (uart_handle_sysrq_char(port, ch))
		return;

	uart_insert_char(port, lsr, UART_LSR_OE, ch, flag);
}

这是uart中断里面的获取输入的函数,当我们在串口按下Break键时,函数里if (lsr & UART_LSR_BI) 这句话是成立的,UART_LSR_BI也就是Break interrupt indicator

进而调用到uart_handle_break函数,uart_handle_break函数里面操作:port->sysrq = jiffies + HZ*5;
这里也就是5秒。

然后serial8250_read_char会return掉,因为捕获到了Break键,下一次就是解析随之而来的key。在uart_handle_sysrq_char函数里面解析。

static inline int
uart_handle_sysrq_char(struct uart_port *port, unsigned int ch)
{
	if (port->sysrq) {
		if (ch && time_before(jiffies, port->sysrq)) {
			handle_sysrq(ch);
			port->sysrq = 0;
			return 1;
		}
		port->sysrq = 0;
	}
	return 0;
}

这里要求随后输出的key和Break间隔时间不能超过port->sysrq,也就是5秒。最后就会调用到handle_sysrq函数来解析key了。

网上也有使用案例:利用 SysRq 键排除和诊断系统故障


后续:
我看到网上有些文章描述如下:
/****************************************************************************************************
那么如何产生一个SysRq键呢?


  • 在Ubuntu下,图形界面环境不能使用SysRq,需进入文本虚拟终端环境(Ctrl+Alt+F1从图形桌面切换到虚拟终端,Alt+F7可切回来),然后同时按下Alt和Print Screen键以及相应的字母键。
  • 在嵌入式设备上,通过串口工具也可以触发SysRq,如果使用SecureCRT,则同时按下Alt和Print Screen键,会出现上述HELP,然后紧接着按下某个字母。如果使用teraTerm,则点击菜单中的Control->Send Break,会出现上述HELP,然后紧接着按下某个字母。
    ***********************************************************************************************/

对于嵌入式设备这块有点扯,内核文档也有描述,串口是通过Break键触发SYSRQ的,难道同时按下Alt和Print Screen键,会在uart触发UART_LSR_BI中断吗?反正我是没发现Break中断被触发。又触发的网友可以给我留言一下。。。。

参考:llinux 内核 sysrq的功能说明

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!