pstore

news2025/4/11 18:46:31

pstore简介

pstore最初是用于系统发生oops或panic时,自动保存内核log buffer中的日志。不过在当前内核版本中,其已经支持了更多的功能,如保存console日志、ftrace消息和用户空间日志。同时,它还支持将这些消息保存在不同的存储设备中,如内存、块设备或mtd设备。 为了提高灵活性和可扩展性,pstore将以上功能分别抽象为前端和后端,其中像dmesg、console等为pstore提供数据的模块称为前端,而内存设备、块设备等用于存储数据的模块称为后端,pstore core则分别为它们提供相关的注册接口。

通过模块化的设计,实现了前端和后端的解耦,因此若某些模块需要利用pstore保存信息,就可以方便地向pstore添加新的前端。而若需要将pstore数据保存到新的存储设备上,也可以通过向其添加后端设备的方式完成。
  在这里插入图片描述
  除此之外,pstore还设计了一套pstore文件系统,用于查询和操作上一次重启时已经保存的pstore数据。当该文件系统被挂载时,保存在backend中的数据将被读取到pstore fs中,并以文件的形式显示。

代码实现

pstore初始化流程

pstore初始化主要是为其指定压缩算法,以及初始化pstore文件系统,其流程如下:

若存储空间比较有限,可以将pstore的内容压缩后再保存到backend设备上,相应的压缩算法可以通过配置选项CONFIG_PSTORE_COMPRESS_DEFAULT或模块参数compress指定。当前支持的压缩算法如下:

static const struct pstore_zbackend zbackends[] = {
#if IS_ENABLED(CONFIG_PSTORE_DEFLATE_COMPRESS)
	{
		.zbufsize	= zbufsize_deflate,
		.name		= "deflate",
	},
#endif
#if IS_ENABLED(CONFIG_PSTORE_LZO_COMPRESS)
	{
		.zbufsize	= zbufsize_lzo,
		.name		= "lzo",
	},
#endif
#if IS_ENABLED(CONFIG_PSTORE_LZ4_COMPRESS)
	{
		.zbufsize	= zbufsize_lz4,
		.name		= "lz4",
	},
#endif
#if IS_ENABLED(CONFIG_PSTORE_LZ4HC_COMPRESS)
	{
		.zbufsize	= zbufsize_lz4,
		.name		= "lz4hc",
	},
#endif
#if IS_ENABLED(CONFIG_PSTORE_842_COMPRESS)
	{
		.zbufsize	= zbufsize_842,
		.name		= "842",
	},
#endif
#if IS_ENABLED(CONFIG_PSTORE_ZSTD_COMPRESS)
	{
		.zbufsize	= zbufsize_zstd,
		.name		= "zstd",
	},
#endif
	{ }
}

pstore_init_fs用于初始化pstore文件系统,其代码如下:

int __init pstore_init_fs(void)
{
	…
	err = sysfs_create_mount_point(fs_kobj, "pstore");1)
	…
	err = register_filesystem(&pstore_fs_type);2)
	…
}

(1)在/sys/fs目录下为其创建挂载点pstore

(2)向内核注册pstore文件系统

在pstore文件系统挂载时会调用pstore_fs_type的mount回调,它会执行挂载相关的操作,其流程如下:
在这里插入图片描述
其中pstore文件系统需要实现自身的超级块填充函数pstore_fill_super,它主要为其设置inode和file相关的操作函数,并且将上一次重启时已保存在pstore backend设备中的pstore信息读取出来,并加入文件系统中。其主要实现如下:

void pstore_get_backend_records(struct pstore_info *psi,
				struct dentry *root, int quiet)
{if (psi->open && psi->open(psi))1)
		goto out;

	for (; stop_loop; stop_loop--) {pstore_record_init(record, psi);                   

		record->size = psi->read(record);2)
		…
		decompress_record(record);3)
		rc = pstore_mkfile(root, record);4)
		…
	}
	if (psi->close)
		psi->close(psi);}

(1)打开pstore backend设备

(2)从backend设备中逐条读取pstore record信息

(3)若使用了压缩算法,则需要将数据解压缩

(4)根据recored的内容,为其在pstore文件系统中创建一个文件。此后,用户就可以通过文件查询相关消息了

pstore backend注册流程

pstore core通过pstore_info结构体来描述一个pstore后端,该结构体主要包含了后端设备相关的信息,以及相关的操作函数,其定义如下:

struct pstore_info {
	struct module	*owner;
	const char	*name;1)

	struct semaphore buf_lock;
	char		*buf;2)
	size_t   bufsize;

	struct mutex	read_mutex;

	int		flags;3int		max_reason;4void		*data;5int		(*open)(struct pstore_info *psi);6int		(*close)(struct pstore_info *psi);
	ssize_t		(*read)(struct pstore_record *record);
	int		(*write)(struct pstore_record *record);
int		(*write_user)(struct pstore_record *record,
				      const char __user *buf);
int		(*erase)(struct pstore_record *record);
}

(1)后端设备的名字

(2)buf用于保存实际的消息

(3)flag用于表示消息的类型,其定义如下:

#define PSTORE_FLAGS_DMESG	BIT(0)
#define PSTORE_FLAGS_CONSOLE	BIT(1)
#define PSTORE_FLAGS_FTRACE	BIT(2)
#define PSTORE_FLAGS_PMSG	BIT(3)

(4)该参数只有前端为DMESG时才有效,用于控制哪些消息允许被dump到pstore中。只有dump reason小于等于该值的消息才会被dump,其reason取值如下:

enum kmsg_dump_reason {
	KMSG_DUMP_UNDEF,
	KMSG_DUMP_PANIC,
	KMSG_DUMP_OOPS,
	KMSG_DUMP_EMERG,
	KMSG_DUMP_SHUTDOWN,
	KMSG_DUMP_MAX
}

(5)后端设备的私有数据,可由回调函数传回

(6)后端设备相关的回调函数,如open和close用于打开和关闭设备,read、write用于读写pstore recored数据。write_user也用于向后端写入recored,但是其buffer为用户态地址,erase用于删除一个指定的recored

后端设备首先需要构造一个pstore_info结构体,然后调用pstore_register函数将其注册到pstore core中。以ram后端为例,其注册流程如下:
  在这里插入图片描述
  其中后端的配置信息可通过dts配置,如ramoops使用的内存地址、长度、recored长度等,ramoops_parse_dt用于解析dts中的相关信息。由于pstore可以包含多个前端,因此后端设备需要根据前端类型进行分区,ramoops_init_przs和ramoops_init_prz就是用于执行相关的分区操作。

pstore_register用于执行实际的注册工作,它的主要工作为校验输入参数是否合法,以及根据配置参数,注册相应的前端,其主要代码如下(去除了参数校验等部分):

int pstore_register(struct pstore_info *psi)
{if (psi->flags & PSTORE_FLAGS_DMESG)
		allocate_buf_for_compression();1pstore_get_records(0);2if (psi->flags & PSTORE_FLAGS_DMESG) {
		pstore_dumper.max_reason = psinfo->max_reason;
		pstore_register_kmsg();3}
	if (psi->flags & PSTORE_FLAGS_CONSOLE)
		pstore_register_console();
	if (psi->flags & PSTORE_FLAGS_FTRACE)
		pstore_register_ftrace();
	if (psi->flags & PSTORE_FLAGS_PMSG)
		pstore_register_pmsg();}

(1)若为pstore设置了压缩功能,则为其分配压缩时使用的内存

(2)从backend中读取上次重启时已保存的pstore recored,并为其建立相应的文件

(3)根据flags的值,注册相应的pstore前端

pstore frontend注册流程

pstore前端主要工作包含两部分,何时触发pstore写操作,以及需要向pstore写入什么内容,下面以dmesg前端为例,简要介绍一下其实现。

printk实现了一个kmsg_dump函数,用于方便其它模块dump内核的log buffer,当内核发生oops、panic或重启等事件时,都会调用该函数dump log信息。其代码实现如下

void kmsg_dump(enum kmsg_dump_reason reason)
{list_for_each_entry_rcu(dumper, &dump_list, list) {1enum kmsg_dump_reason max_reason = dumper->max_reason;

		if (max_reason == KMSG_DUMP_UNDEF) {                     
			max_reason = always_kmsg_dump ? KMSG_DUMP_MAX :
							KMSG_DUMP_OOPS;
		}
		if (reason > max_reason)2continue;

		dumper->dump(dumper, reason);3}
	rcu_read_unlock();
}

(1)遍历dump_list链表中的所有的dumper,并对它们分别执行如下log dump操作

(2)根据dumper的max_reason值,确定是否需要向其dump log

(3)若需要dump,则调用该dumper的dump回调,执行实际的dump操作

因此pstore前端只需将自身注册到dump_list链表中即可,该操作由以下函数实现:

int kmsg_dump_register(struct kmsg_dumper *dumper)
{if (!dumper->registered) {
		dumper->registered = 1;
		list_add_tail_rcu(&dumper->list, &dump_list);
		err = 0;
	}}

其中dmesg前端的主要工作就是为其实现一个dump函数,该函数将从log buffer中读取lou信息,然后将其封装为recored之后写入对应的后端设备,其主要定义如下:

static struct kmsg_dumper pstore_dumper = {
	.dump = pstore_dump,
}

static void pstore_dump(struct kmsg_dumper *dumper,
			enum kmsg_dump_reason reason)
{while (total < kmsg_bytes) {pstore_record_init(&record, psinfo);1)
		…
		header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why,
				 oopscount, part);2)
		dst_size -= header_size;

		if (!kmsg_dump_get_buffer(&iter, true, dst + header_size,
					  dst_size, &dump_size))3break;

		if (big_oops_buf) {
			zipped_len = pstore_compress(dst, psinfo->buf,
						header_size + dump_size,
						psinfo->bufsize);4)
			…
		} else {
			record.size = header_size + dump_size;
		}

		ret = psinfo->write(&record);5)
		…
	}}

(1)初始化一个record结构体

(2)先向其写入pstore头信息,如dmesg reason、oops发生次数等

(3)从log buffer中读取一行log信息

(4)若需要压缩信息,则执行压缩操作

(5)调用后端的写函数,将record写入相应的后端设备

pstore配置和使用

pstore使用主要包含以下四个步骤:
(1)通过配置内核参数,使能pstore功能。由于dmesg前端是默认使能的,因此若以ramoops为后端,且使能所有前端为例,则其相应的配置参数如下:

CONFIG_PSTORE=y
CONFIG_PSTORE_RAM=y
CONFIG_PSTORE_PMSG=y
CONFIG_PSTORE_FTRACE=y
CONFIG_PSTORE_CONSOLE=y

(2)配置devicetree,为后端设备设置相关参数。以ramoops为例,pstore使用的内存需要被添加到reserved内存中,以下为其中的一个示例:

reserved-memory {
#address-cells = <2>;
    #size-cells = <2>;
    ranges;

    ramoops@21f00000 {
         compatible = "ramoops";
         reg = <0x0 0x21f00000 0x0 0x00100000>;
         record-size     = <0x00020000>;
         console-size    = <0x00020000>;
         ftrace-size     = <0x00020000>;
     };
};

(3)挂载pstore文件系统,由于其挂载点为/sys/fs/pstore,因此相应的挂载操作如下:

mount -t pstore pstore /sys/fs/pstore

(4)挂载完成后,若后端中已经保存了先前的pstore消息,则可在/sys/fs/pstore目录下查看到对应的文件。用户可对该文件执行读取,删除等操作

参考: 内核崩溃日志抓取pstore

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/75860.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新手教程 | 手把手教你 谷歌浏览器如何使用HTTP代理?

本文将针对谷歌浏览器如何使用代理IP进行详细说明&#xff0c;具体步骤如下&#xff1a; 1、代理IP信息查看 查看自己所购买的相对应的代理IP面板&#xff0c;点击代理IP -查看详情-进行查看“AuthKey”以及“AuthPwd”信息。 2、代理IP资源提取 在控制台-代理IP-中的“提取…

Spring cloud Gateway 服务网关 实战

Spring cloud Gateway 服务网关一、简介优点&#xff1a;特性&#xff1a;总结&#xff1a;二、核心概念三、路由规则1、Path2、Query3、Method4、Datetime5、RomoteAddr6、Header四、动态路由1、依赖2、配置动态获取URI服务名称转发五、过滤器1、网关过滤器 GatewayFilter局部…

Allegro如何给差分过孔添加禁布操作指导

Allegro如何给差分过孔添加禁布操作指导 Allegro支持给差分过孔添加禁布,让它避让周围的铜皮,具体操作如下 以下图两个过孔为例,需要做一个和via避让铜皮尺寸一样大的禁布 选择show element命令 Find选择Other segs 鼠标移动到铜皮避让圆形的地方 出现一个report,记住…

【并发编程】SemaphoreCountDownLatchCyclicBarrier

一、Semaphore Semaphore 通常又被称为信号量&#xff0c; 可以用来控制同时访问特定资源的线程数量&#xff0c;通过协调各个线程&#xff0c;以保证合理的使用资源。 1.简单的使用 1-1.控制线程的并发连接数 public static void main(String[] args) {// 只允许两个线程执…

中英翻译《动物看见了什么》

What animals see 动物看见了什么 一、Pre-reading activity 阅前思考 1.What animals do you like? 你喜欢什么动物&#xff1f; 2.Do you have any animals in your home? 你家里有动物吗&#xff1f; 3.Do you think most animals can see as well as we can? 你认为大多…

Windows下docker安装

安装 1.打开Hyper-V &#xff0c;在"启用或关闭Windows功能" 如果这里的Hyper-V平台灰色不能勾选 显示无法安装Hyper-v该固件中的虚拟化支持被禁用&#xff0c;则需要开启 开始方式&#xff1a; 重启电脑进入BIOSS界面 点击高级–>CPU设置 —> Intel virtu…

44. python的for循环嵌套

44. python的for循环嵌套 文章目录44. python的for循环嵌套1. 什么是嵌套2. for循环中嵌套有if条件判断语句2.1 先创建一个奇数序列2.2 判断一个数是否能被7整除2.3 将2部分代码合二为一3. for循环中嵌套有for循环1. 什么是嵌套 嵌套是指一个对象中包含另一个与它相似的对象。…

Python 中在两个字典中查找公共键

Python 中要在两个词典中查找公共键&#xff1a; 使用 dict.keys() 方法获取每个字典的键的视图对象。使用 & 符号获取常用键。使用 list() 类将结果转换为列表对象。 dict1 {name: jiyik, topic: Python, salary: 100} dict2 {name: alice, salary: 100, experience: …

R语言探索BRFSS数据可视化

设定 加载包 最近我们被客户要求撰写关于BRFSS数据的研究报告&#xff0c;包括一些图形和统计输出。在本实验中&#xff0c;我们将使用dplyr软件包探索数据&#xff0c;并使用ggplot2软件包对其进行可视化以进行数据可视化 library(ggplot2) library(dplyr) 载入资料 load…

初识Netty框架

总体概述 Netty作为一款网络通信框架&#xff0c;底层封装了NIO。我们在使用Netty时&#xff0c;无需再关注NIO细节。下图为Netty处理流程图&#xff1a; 应用程序中使用Netty作为网络通信框架后&#xff0c;会形成一条PipeLine链&#xff0c;PipeLine链上有一个一个的事件处…

buuctf10(异或注入中文字符绕过preg_match伪随机数漏洞seed)

目录 [WUSTCTF2020] 颜值成绩(异或注入) [Zer0pts2020]Can you guess it?(中文字符绕过preg_match) [FBCTF2019]RCEService(/bin/调用命令 || 回溯绕过preg_match) [GKCTF 2021]easycms(后台弱口令&任意文件下载) [GWCTF 2019]枯燥的抽奖(伪随机数漏洞seed) [MRCTF20…

msprofiler 性能调优命令行实战(口罩识别推理)

案例介绍 本案例使用口罩识别推理程序作为例子进行演示&#xff0c;硬件平台是华为昇腾310设备(Ascend 310)&#xff0c;该口罩识别使用目标检测中SSD模型&#xff0c;检测的结果有两个类别&#xff1a;戴口罩的脸、没带口罩的脸。成功执行推理程序后我们对其进行了推理调优&a…

连续7年领跑!在华为云桌面,藏了一盘数字办公的大棋

作者 | 曾响铃 文 | 响铃说 连续7年领跑&#xff01;在国内虚拟客户端计算软件市场&#xff0c;华为云再度占据行业第一的位置&#xff0c;力压Citrix、Microsoft和VMware等全球知名厂商。 所谓的虚拟客户端计算软件市场&#xff0c;简单来理解就是云桌面市场。伴随着数字办…

Oracle报错:ORA-28001:口令已失效

一、链接Oracle报错 &#xff1a;ORA-28001&#xff1a;口令已失效 解决办法 原因&#xff1a;Oracle11G创建用户时缺省密码过期限制是180天&#xff08;即6个月&#xff09;&#xff0c; 如果超过180天用户密码未做修改则该用户无法登录 解决方式&#xff1a; 方式一&#xf…

R语言可视化探索BRFSS数据并逻辑回归Logistic回归预测中风

第1部分&#xff1a;关于数据 行为风险因素监视系统&#xff08;BRFSS&#xff09;是美国的年度电话调查。最近我们被客户要求撰写关于BRFSS的研究报告&#xff0c;包括一些图形和统计输出。BRFSS旨在识别成年人口中的危险因素并报告新兴趋势。例如&#xff0c;询问受访者饮食…

Python 并发编程

一.Python 对并发编程的支持 多线程&#xff1a;threading&#xff0c;利用CPU和IO可同时执行的原理&#xff0c;让CPU不会干巴巴等待IO完成&#xff0c;而是切换到其他Task&#xff08;任务&#xff09;&#xff0c;进行多线程的执行。多进程&#xff1a;multiprocessing&…

微前端总结

微前端概述 微前端概念是从微服务概念扩展而来的&#xff0c;摒弃大型单体方式&#xff0c;将前端整体分解为小而简单的块&#xff0c;这些块可以独立开发、测试和部署&#xff0c;同时仍然聚合为一个产品出现在客户面前。可以理解微前端是一种将多个可独立交付的小型前端应用…

使用极限学习机进行股市预测(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 极限学习机&#xff08;Extreme Learning Machine,ELM&#xff09;作为前馈神经网络学习中一种全新的训练框架,在行为识别、情…

EPICS -- 使用asynPortDriver类编写示一个示例程序

本示例展示了如何使用asynPortDriver类编写一个EPICS端口驱动程序的示例。 这个驱动程序参数库中一个有5个参数&#xff0c;分别支持5个EPICS记录。 如下是具体步骤&#xff1a; 1&#xff09; 用makeBaseApp.pl脚本建立这个IOC应用程序的框架&#xff1a; [blctrlmain-mach…

IDEA中如何实现git的cherry-pick可视化操作?

目录 问题现象&#xff1a; 问题分析&#xff1a; 解决方法&#xff1a; 拓展&#xff1a;如何回退提交记录&#xff1f; 问题现象&#xff1a; 今天在学习了git的cherry-pick功能&#xff0c;于是引出了一个问题&#xff1a; IDEA中如何实现git的cherry-pick可视化操作&am…