在对内核做修改时,可能存在直接无法开机或者运行某个应用崩溃的情况,记录一些检查错误的办法。

1. 重新启动系统

首先,尝试重新启动系统,以查看是否可以解决内核崩溃的问题。有时候,崩溃可能是由于暂时的故障或资源问题引起的,重新启动可以清除这些问题。

2. 检查系统日志(非常有用)

换成可以正常运行的内核,登录到系统后,查看系统日志以获取关于内核崩溃的信息。系统日志文件通常位于/var/log目录下,具体文件名可能因不同的Linux发行版而异,常见的包括syslogmessageskern.log等。使用文本编辑器打开相关日志文件,查找与内核崩溃时间相关的条目,以获取错误消息和其他有用的信息。

3. 查看内核转储文件

当内核崩溃时,通常会生成一个内核转储文件(core dump),其中包含有关崩溃时内核状态的信息。转储文件通常位于系统的当前工作目录或/var/crash目录下,文件名可能包含日期、时间戳或内核版本号等信息。可以使用调试工具(如GDB)来分析内核转储文件,并查看崩溃的堆栈跟踪信息以及其他有关错误原因的数据。

4.运行时查看内核输出

如果你在内核里自己添加了输出或者内核本来定义的输出可以从如下两个命令看到:
已经输出了的:

sudo cat /proc/kmsg

从输入命令开始后的输出:

dmesg | tail

在linux中查看日志tail和head命令的使用

文本过于长以至于在图形界面中加载缓慢,以及滑动条拉半天也到不了底。搭配tail和head灵活读取文本中任意位置的数据。

使用tail命令来输出文件的最后几行。如要输出文件的后面50行,可以使用以下命令:

tail -n 50 文件名

要指定输出文件的倒数1600到800行,可以使用tail结合head命令来实现。首先,使用tail命令获取倒数1600行;然后,将输出管道传递给head命令,指定要输出的行数为800行:

tail -n 1600 文件名 | head -n 800

printk使用输出来调试

在Linux内核中,printk函数可以使用不同的优先级来记录日志消息。以下是一些常用的printk优先级:

  1. KERN_EMERG:紧急情况。用于记录最紧急的系统消息,通常表示系统崩溃或无法继续运行的情况。这个优先级的消息通常会导致系统宕机。

  2. KERN_ALERT:警报。用于记录需要立即采取行动的情况,通常表示严重的系统问题,但不一定导致宕机。

  3. KERN_CRIT:关键。用于记录严重的系统问题,但没有导致系统崩溃。这可以是一个需要紧急关注的问题。

  4. KERN_ERR:错误。用于记录错误消息,表示系统遇到了一个问题,但仍然可以继续运行。

  5. KERN_WARNING:警告。用于记录警告消息,表示可能存在一些问题,但系统仍在正常运行。

  6. KERN_NOTICE:通知。用于记录一般信息,通常表示系统中发生的一些重要事件。

  7. KERN_INFO:信息。用于记录普通信息,通常用于调试和跟踪系统行为。

  8. KERN_DEBUG:调试。用于记录调试信息,通常只在开发和调试阶段使用。

这些优先级可以与printk函数一起使用,例如:

printk(KERN_ERR "This is an error message.\n");

在这个例子中,KERN_ERR表示这是一个错误消息,该消息将被记录到系统日志中。

debug时输出内核数据到文件

在Linux内核中,通常可以通过sysfs文件系统提供给用户空间。sysfs是一个虚拟文件系统,用于暴露内核数据和控制接口给用户空间。通过sysfs,用户可以以文件的形式读取和写入内核的各种属性和状态信息。

  • 输出方式:一种方法是使用序列文件seq_file接口来将统计信息输出到文件。序列文件是一种特殊的文件类型,它允许按顺序逐行写入数据。序列文件的输出通常通过seq_printf等函数进行。

  • 文件位置:要找到某段代码输出的位置,尝试查找与该函数相关的sysfs文件或目录。在代码中,可能会有将属性或属性组注册到sysfs的代码段。这些注册代码会创建与该函数相关的sysfs文件或目录,并将输出函数与相应的sysfs文件关联起来。通常,sysfs文件位于/sys目录下,可以在/sys/fs/cgroup/memory目录或类似的位置找到与内存控制组(memcg)相关的sysfs文件。


来自论文Adaptive Page Migration Policy with Huge Pages in Tiered Memory Systems公布的源码中的一个代码段:

enum hotness_metric {
	NONE = 0,
	PAGE_AGE,
	PAGE_ACCESS_FREQ
};

static unsigned int hotness_metric = NONE;

static ssize_t hotness_metric_show(struct kobject *kobj,
		struct kobj_attribute *attr,
		char *buf)
{
	ssize_t pos = 0;

	switch (hotness_metric) {
		case NONE:
			pos = sprintf(buf, "NONE\n");
			break;

		case PAGE_AGE:
			pos = sprintf(buf, "PAGE_AGE\n");
			break;

		case PAGE_ACCESS_FREQ:
			pos = sprintf(buf, "PAGE_ACCESS_FREQ\n");
			break;

		default:
			break;
	}

	return pos;
}

static ssize_t hotness_metric_store(struct kobject *kobj,
		struct kobj_attribute *attr,
		const char *buf, size_t count)
{
	int err;
	unsigned long input;

	err = kstrtoul(buf, 10, &input);
	if (err)
		return -EINVAL;
	hotness_metric = input;

	switch (hotness_metric) {
		case PAGE_AGE:
			scan_page = scan_page_age;
			collect_stats = collect_stats_age;
			break;

		case PAGE_ACCESS_FREQ:
			scan_page = scan_page_access_freq;
			collect_stats = collect_stats_access_freq;
			break;

		default:
			hotness_metric = NONE;
			scan_page = scan_page_none;
			collect_stats = collect_stats_none;
			return -EINVAL;
	}

	return count;
}

static struct kobj_attribute scanning_interval_attr
= __ATTR(scanning_interval, 0644,
		scanning_interval_show,
		scanning_interval_store);
static struct kobj_attribute hotness_metric_attr
= __ATTR(hotness_metric, 0644,
		hotness_metric_show,
		hotness_metric_store);
static struct kobj_attribute age_reset_attr
= __ATTR(age.reset, 0644,
		NULL,
		age_reset_store);

static struct attribute *profd_attrs[] = {
	&scanning_interval_attr.attr,
	&hotness_metric_attr.attr,
	&age_reset_attr.attr,
	NULL
};

在该代码中,hotness_metric是一个枚举类型的变量,具体的取值是NONEPAGE_AGEPAGE_ACCESS_FREQ。在sysfs中,我们无法直接写入枚举类型的值,而是需要使用对应的整数值来表示。由于没有显式指定PAGE_AGE的值,根据默认规则,它的值将是前一个枚举值NONE的值加1。而NONE的值是0,所以PAGE_AGE的默认值为1。

hotness_metric_store函数中,通过将输入的字符串转换为无符号长整型数(unsigned long),我们获取到用户在sysfs中写入的值。然后,通过一个switch语句将该值与枚举类型的取值进行匹配,以便将其设置为相应的选项。

在终端上,可以通过写入对应的整数值来设置hotness_metric,而不是直接写入PAGE_AGE。这样设计的原因是为了与sysfs接口的规范一致性,并提供更通用的方式来表示枚举类型的取值。


文章作者: 易百分
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 易百分 !
  目录