Linux内核调试技术之kprobes(1)基本原理与使用

news2024/11/26 20:23:04

概述

Linux kprobes技术是一种可以跟踪内核函数执行状态的轻量级内核调试技术,利用kprobes技术可以在运行的内核中动态的插入探测点,当内核运行到该探测点后可以执行用户预定义的回调函数,以收集所需的调试状态信息而基本不影响内核原有的执行流程。

kprobes探测手段

kprobes技术包括3种探测手段分别为kprobe、jprobe和kretprobe,其中:

  • kprobe是最基本的探测方式,是实现后两种的基础,它可以在内核的任何指令位置插入探测点;
  • jprobe基于kprobe实现,只能插入到一个内核函数的入口,它用于获取被探测函数的入参值;
  • kretprobe也是基于kprobe实现,可以在指定的内核函数返回时才被执行。利用该方式可以获取被探测函数的返回值,还可以用于计算函数执行时间等方面。

kprobes的工作机制

kprobes技术依赖硬件架构相关的支持,主要包括CPU的异常处理和单步调试机制,前者用于让程序的执行流程陷入到用户注册的回调函数中去,而后者则用于单步执行被探测点指令,目前支持kprobes技术的架构包括i386、x86_64、ppc64、ia64、sparc64、arm、ppc和mips。kprobe的工作原理和流程如图所示:
在这里插入图片描述

  1. 注册kprobe时,kprobe会复制被探测位置的指令,并用断点指令(例如i386和x86_64上的int3,ARM上的brk)替换被探测指令的第一个字节;
  2. 当CPU执行到断点指令时,会触发陷阱,CPU自动保存寄存器上下文,并通过notifier_call_chain机制将控制传递给kprobe;
  3. kprobe将kprobe结构的地址和保存的寄存器传递给注册时指定的pre_handler处理程序,并执行;
  4. 接下来,kprobe单步执行其被探测指令的副本;
  5. 指令单步执行后,kprobe执行注册的post_handler处理程序;
  6. 继续执行探测点后面的指令。

kprobe使用方式

kprobe的使用方式有两种:

  • 方式一:通过编写内核模块,向内核注册探测点。探测函数可根据需要自行定制,使用灵活方便;
  • 方式二:是使用 kprobes on ftrace,这种方式是 kprobe 和 ftrace 结合使用,即可以通过 kprobe 来优化 ftrace 来跟踪函数的调用。

本文关注于编写内核模块的方式使用kprobe。

编写kprobe探测模块

内核提供了一个struct kprobe结构体以及一系列的内核API函数接口,用户可以通过这些接口自行实现探测回调函数并实现struct kprobe结构,然后将它注册到内核的kprobes子系统中来达到探测的目的。struct kprobe结构体定义如下:

struct kprobe {
    ...
	kprobe_opcode_t *addr; /* 被探测点的地址 */
	const char *symbol_name;   /* 被探测函数的名称 */
	unsigned int offset;   /* 被探测点在函数内部的偏移,可用于探测函数内部的指令,若为0则表示函数入口 */
 
	kprobe_pre_handler_t pre_handler;      /* 该回调函数用于在执行被探测指令前执行 */
	kprobe_post_handler_t post_handler;    /* 该回调函数用于在执行完被探测指令后执行 */
	kprobe_fault_handler_t fault_handler;  /* 此函数用于在出现内存访问错误时进行处理 */
 
	kprobe_opcode_t opcode;
    
    ...
};

kprobe另外提供了注册与卸载kprobe探测点的API函数接口,其函数原型定义如下:

int register_kprobe(struct kprobe *kp); /* 向内核注册kprobe探测点 */
void unregister_kprobe(struct kprobe *kp);  /* 从内核卸载kprobe探测点 */

在内核的samples/kprobes目录下有一个实例kprobe_example.c描述了kprobe模块最简单的编写方式,后续分析实际问题时,我们可以以此为模板编写自己的探测模块。

定义回调函数

static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
	pr_info("<%s> pre_handler: p->addr = 0x%p, ip = %lx, flags = 0x%lx\n",
		p->symbol_name, p->addr, regs->ip, regs->flags);

	return 0;
}

/* kprobe post_handler: called after the probed instruction is executed */
static void handler_post(struct kprobe *p, struct pt_regs *regs,
				unsigned long flags)
{
    pr_info("<%s> post_handler: p->addr = 0x%p, flags = 0x%lx\n",
		p->symbol_name, p->addr, regs->flags);
}

int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
{
	pr_info("fault_handler: p->addr = 0x%p, trap #%dn", p->addr, trapnr);
	/* Return 0 because we don't handle the fault. */
	return 0;
}

定义kprobe结构

static struct kprobe kp = {
    .symbol_name   = "do_fork",      // 要追踪的内核函数为 do_fork
    .pre_handler   = handler_pre    // pre_handler 回调函数
    .post_handler  = handler_post;   // post_handler 回调函数
    .fault_handler = handler_fault;  // fault_handler 回调函数
};

注册探测点

static int __init kprobe_init(void)
{
	int ret;

	ret = register_kprobe(&kp);
	if (ret < 0) {
		pr_err("register_kprobe failed, returned %d\n", ret);
		return ret;
	}
	pr_info("Planted kprobe at %p\n", kp.addr);
	return 0;
}

static void __exit kprobe_exit(void)
{
	unregister_kprobe(&kp);
	pr_info("kprobe at %p unregistered\n", kp.addr);
}

module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");

最后,使用如下Makefile编译kprobe_example模块:

obj-m := kprobe_example.o 
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
        rm -f *.mod.c *.ko *.o

安装内核模块,任意执行一条shell命令,都会在后台看到kprobe_example模块的输出。

参考链接

  • 官方文档:https://www.kernel.org/doc/Documentation/kprobes.txt
  • kprobe kretprobe example:https://kernelgo.org/kprobe.html
  • Kernel调试追踪技术之 Kprobe on ARM64:https://www.cnblogs.com/hpyu/articles/14257305.html
  • Linux内核调试工具Kprobe机制的研究:https://wenku.baidu.com/view/98d7864acf84b9d528ea7ad5.html

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

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

相关文章

XXL-JOB详解(整合springboot)保姆级教程

文章目录XXL-JOB简介XXL-JOB是什么为什么需要任务调度平台&#xff0c;而不用传统的 Timer 与 Quartz为什么选择XXL-JOB&#xff0c;不选择elasticjob学习之前必看&#xff0c;少走很多弯路安装XXL-JOB一、源码编译&#xff08;Windows&#xff09;1、拉取源码&#xff1a;[xxl…

NR HARQ(二) CBG HARQ-ACK codebook

这篇开始看下HARQ-ACK codebook的相关内容&#xff0c;先看CBG-based HARQ-ACK codebook。那第一个关注点就是CBG 的划分规则&#xff0c;这部分内容主要在38.213 9.1.1章节中&#xff0c;PDSCH和PUSCH 的CBG 划分规则基本是一样的&#xff0c;这里以PDSCH为例介绍。 PDSCH 和P…

MyBatis-Plus之通用枚举

系列文章目录 Mybatis-PlusSpringBoot结合运用_心态还需努力呀的博客-CSDN博客MyBaits-Plus中TableField和TableId用法_心态还需努力呀的博客-CSDN博客 MyBatis-Plus分页查询&#xff08;快速上手运用&#xff09;_心态还需努力呀的博客-CSDN博客_mybatis plus分页查询 MyBa…

CyclicBarrier 多线程处理数据

文章目录前言需求环境准备单线程处理多线程处理总结前言 开发中&#xff0c;我们经常会遇到处理批量数据&#xff0c;最后把处理成功和失败的数据结果记录下来。普通方法一个循环就可以搞定这个需求&#xff0c;但是面临大量数据单个线程去处理可能面临很大的瓶颈&#xff0c;…

怎么进行视频配音?建议收藏这些配音方法

最近我的朋友向我求助&#xff0c;他想要自己制作一个视频&#xff0c;但是视频里面有些片段需要配音&#xff0c;可是他又不想用自己的声音来配音。一方面担心容易NG&#xff0c;需要录制很多遍&#xff0c;会浪费较多的时间&#xff1b;另一方面是&#xff0c;如果视频录制和…

​单张图像三维人脸重建必备入门face3d—3DMM

作者&#xff1a;小灰灰 来源&#xff1a;投稿 编辑&#xff1a;学姐 本次的例子是将pipeline生成的图片作用于3DMM&#xff0c;重新拟合成新的图片。 load model 3DMM的表达式&#xff1a; &#x1d446;̅ ∈ &#x1d445;3&#x1d45b;是平均人脸形状&#xff0c;&#x…

国产网关apisix安装

1、安装docker 参考&#xff1a;centos7安装docker_代码手艺人老羊的博客-CSDN博客 2、下载包&#xff08;从github&#xff09; # Download the Docker image of Apache APISIX git clone https://github.com/apache/apisix-docker.git 3、安装 # Switch the current di…

单点登录设计

01 单系统登录机制 1、http无状态协议 web应用采用browser/server架构&#xff0c;http作为通信协议。http是无状态协议&#xff0c;浏览器的每一次请求&#xff0c;服务器会独立处理&#xff0c;不与之前或之后的请求产生关联&#xff0c;这个过程用下图说明&#xff0c;三…

JavaScript高级 |彻底搞懂原型对象

本文已收录于专栏⭐️ 《JavaScript》⭐️ 学习指南&#xff1a;对象的原型函数的原型new操作符将方法放原型里constructor总结梳理原型对象内存表现完结散花参考文献对象的原型 JavaScript 当中每个对象都有一个特殊的内置属性[[prototype ]] ,这个特殊的对象可以指向另外一个…

科技云报道:畅想无人化运维的AIOps,还有多远的路要走?

科技云报道原创。 在IT行业&#xff0c;运维人常常自我调侃“赚着5k的月薪&#xff0c;操着5千万的心&#xff0c;名下挂着5亿的资产”。 机房的暖通、网络、综合布线&#xff0c;系统的监控告警、故障响应等一大堆繁杂琐碎的工作&#xff0c;充斥着运维人的日常。 与开发和产…

自定义Feign的配置

SpringBoot虽然帮我们实现了自动装配&#xff0c;但是也是支持自定义配置的。 Feign运行自定义配置来覆盖默认配置&#xff0c;可以修改的默认配置如下&#xff1a; 配置Feign日志有两种方式 方式一&#xff1a;配置文件方式 1&#xff09;全局生效 feign:client:config:defa…

【愚公系列】2022年12月 Elasticsearch数据库-ELK添加SQL插件和浏览器插件(二)

文章目录前言一、ELK添加SQL插件和浏览器插件1.配置插件2.浏览器插件3.Elasticsearch术语介绍4.测试SQL插件和浏览器插件前言 下载SQL插件地址&#xff1a;https://github.com/NLPchina/elasticsearch-sql 我们选择7.15.2版本&#xff0c;ES页选择7.15.2版本把最后面的下载链…

车间调度|基于遗传算法的柔性车间调度(Matlab代码实现)

目录 1 概述 2 遗传优化算法 3 车间调度 4 运行结果 5 参考文献 6 Matlab代码实现 1 概述 调度通过合理安排生产资源,以缩短生产时间和提高资源利用率为目的,在生产系统中扮演着重要的角色。作业车间调度问题(Job-shop Schedu-ling Problem&#xff0c;JSP)是一类经典…

1996-2020年全国31省农村电力和农田水利建设相关数据

1996-2020年全国31省农村电力和农田水利建设相关数据 1、1996-2020年 2、范围&#xff1a;31省 3、指标包括&#xff1a; 乡村办水电站、装机容量、发电量、农村用电量、有效灌溉面积、旱涝保收面积、机电排灌面积、实际耕地灌溉面积、新增耕地灌溉面积、节水灌溉面积、新增…

2023年第六届先进控制,自动化与机器人国际会议(ICACAR 2023)

2023年第五届先进控制&#xff0c;自动化与机器人国际会议&#xff08;ICACAR 2023&#xff09; 重要信息 会议网址&#xff1a;www.icacar.org 会议时间&#xff1a;2023年4月14-16日 召开地点&#xff1a;中国北京 截稿时间&#xff1a;2023年2月28日 录用通知&#xf…

排序子序列

1 题目来源&#xff1a; 牛客网&#xff1a;排序子序列 2 题目描述  牛牛定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序。牛牛有一个长度为n的整数数组A,他现在有一个任务是把数组A分为若干段排序子序列,牛牛想知道他最少可以把这个数组分…

Ashampoo Burning Studio创建可启动磁盘

Ashampoo Burning Studio创建可启动磁盘 Ashampoo的产品通常适合质量&#xff0c;但在其中&#xff0c;它是世界上最好的软件之一&#xff0c;名为Ashampoo Burning Studio。与著名的Nero程序相比&#xff0c;该软件几乎一无是处&#xff0c;所有用于制作、写入和复制光盘的软件…

Python Tutorial——模块

如果你从Python解释器中退出&#xff0c;并且再次进入&#xff0c;你会发现你以前定义的函数和变量都已经丢失了。所以&#xff0c;如果你想写一个在某种程度上更长的程序&#xff0c;使用一个文本编辑器来准备解释器的输入会使情况有所好转&#xff0c;并且使用文件代替输入来…

最简单的方式实现Zotero文件同步+坚果云在多台电脑设备之间

应用场景&#xff1a; 放假回家&#xff0c;只带了笔记本搞科研的好童靴&#xff0c;发现实验室台式机的zotero中的PDF没办法在笔记本上读取。于是探索了一下午如何不重新在网页上保存下载台式机中的PDF&#xff0c;轻松获取异地的文献。 方式一&#xff1a; 氪金付费zotero…

参数估计与假设检验

推断统计&#xff1a;研究如何利用样本数据来推断总体特征 描述统计&#xff1a;描述一组数据的特征 参数估计&#xff1a;利用样本信息估计总体特征 假设检验&#xff1a;利用样本信息判断对总体的假设是否成立 一.参数估计 就是对于总体指标的估计 估计&#xff1a;根据…