Linux 内核 调用堆栈打印函数

server/2024/11/29 19:56:55/

文章目录

  • 内核函数调用堆栈打印
  • 1. dump_stack()
      • 一、作用
      • 二、工作原理
      • 三、实现方式
      • 四、示例
        • 实际演示
  • 2.WARN_ON()
  • 3. panic()
      • 一、函数作用
      • 二、函数行为
      • 三、panic() 函数的参数
      • 四、使用场景
  • 4. BUG_ON()
      • 使用场景

内核函数调用堆栈打印

1. dump_stack()

dump_stack()是Linux内核中用于打印栈回溯信息的函数,主要用于内核调试。以下是对dump_stack()内核打印的详细介绍:

一、作用

当内核出现严重的错误,如Oops错误或内核认为系统运行状态异常时,内核会打印出当前进程的栈回溯信息。这些信息包括当前执行代码的位置、相邻的指令、产生错误的原因、关键寄存器的值以及函数调用关系等,对于调试内核错误非常有用。dump_stack()函数就是用于打印这些函数调用关系的,它可以帮助开发者了解内核代码的执行流程,定位问题所在。

二、工作原理

dump_stack()函数的工作原理是通过遍历堆栈,找到所有可能是内核函数的内容,并打印对应的函数。函数调用时会把下一条指令的地址放到堆栈中,因此只要找到这些返回地址,就可以找到它们所在的函数,进而打印出函数的调用关系。

具体来说,dump_stack()函数会执行以下步骤:

  1. 从当前进程的栈中找到当前函数的返回地址。
  2. 根据函数返回地址,从代码段中定位该地址位于哪个函数中,找到的函数即为调用者(caller)函数。
  3. 打印caller函数的函数名。
  4. 重复以上步骤,直到返回值为0或不在内核记录的符号表范围内。

三、实现方式

dump_stack()函数的实现与系统结构紧密相关,不同的硬件架构对应的实现也不同。但原理大致相同,都是通过读取关键寄存器的值(如栈指针、返回地址、程序计数器等),然后遍历堆栈,打印出函数调用关系。

以ARM体系为例,dump_stack()函数会读取FP(帧指针)寄存器的值,通过FP的值找到当前函数的函数栈的地址。然后,它会获取当前函数的代码段地址(通过PC寄存器获得),并在函数栈中找到保存的PC寄存器的备份,以定位到函数的第一条指令(即函数的入口地址)。接着,内核中保存了所有函数地址和函数名的对应关系,因此可以打印出函数名。在当前函数的函数栈中,还保存了caller函数的帧指针(FP寄存器的值),因此可以找到caller函数的函数栈的位置,并继续执行上述步骤,直到某个函数的函数栈中保存的帧指针为0或非法。

内核代码

dump_stack()

四、示例

以下是一个使用dump_stack()函数的示例代码:

文件名 dump_stack.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>static int __init hello_init(void) {printk(KERN_ALERT "dump_stack() start\n");dump_stack();printk(KERN_ALERT "dump_stack() over\n");return 0;
}static void __exit hello_exit(void) {printk(KERN_ALERT "test module\n");
}module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(" Name");
MODULE_DESCRIPTION("A simple test module using dump_stack().");

对应的makefile

obj-m += dump_stack.oall:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

在内核模块加载时,会调用hello_init()函数,该函数会打印出"dump_stack start",然后调用dump_stack()函数打印栈回溯信息,最后打印出"dump_stack over"。通过dmesg命令可以查看打印的栈回溯信息。

实际演示

执行make命令生成对应的.ko文件

sudo insmod dump_stack.ko // 加载模块进内核

dmesg 查看dump_stack()打印如下

dmesg

最后卸载模块

sudo rmmod dump_stack 

2.WARN_ON()

WARN_ON 是 Linux 内核中用于检测不应该发生的条件的一个宏。当 WARN_ON 中的条件评估为真(非零)时,它会在内核日志中打印一条警告消息,并且可能会触发内核的调试器(如果内核是在调试模式下编译的,并且调试器是附着的)。这个宏的目的是帮助开发者在开发过程中发现潜在的错误或异常行为。

使用 WARN_ON 时,你需要提供一个布尔表达式作为参数。如果这个表达式的结果为真,那么 WARN_ON 会执行以下操作:

  1. 打印警告消息:它会将一条警告消息输出到内核日志中,通常包含触发警告的文件名、行号和条件表达式。

  2. 性能影响:在大多数情况下,WARN_ON 的性能开销是可以忽略不计的,因为它只会在不应该发生的条件下执行。然而,在性能敏感的代码路径中,频繁触发 WARN_ON 可能会导致性能下降。

  3. 调试和验证WARN_ON 通常用于开发阶段,以捕获和调试不应该发生的条件。一旦代码被充分测试并验证为正确,这些 WARN_ON 检查可能会被移除或替换为更轻量级的断言。

  4. 避免潜在问题:通过及时发出警告,WARN_ON 可以帮助开发者避免可能导致系统不稳定或安全漏洞的问题。

Linux代码定义如下

WARN_ON

WARON_ON()实际上也是调用dump_stack() 加了参数condition判断。

使用代码如下

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>static int __init hello_init(void) {printk(KERN_ALERT "WARON_ON()\n");WARN_ON(1);printk(KERN_ALERT "WARON_ON() over\n");return 0;
}static void __exit hello_exit(void) {printk(KERN_ALERT "test module\n");
}module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(" Name");
MODULE_DESCRIPTION("A simple test module using WARON_ON().");

3. panic()

panic() 函数是 Linux 内核中一个重要的函数,用于处理系统遇到的严重错误。当系统检测到无法处理的异常情况,如内核内部错误、硬件故障等,会调用 panic() 函数,将系统置于一个不可恢复的状态,并停止运行。以下是对 panic() 函数的详细解释:

一、函数作用

panic() 函数的主要作用是停止当前系统的运行,并在系统监测到异常时调用。它会记录错误信息,并可能触发系统重启。在调用 panic() 函数后,系统会进入一种紧急状态,通常会显示蓝屏信息,并给出一些参数帮助用户了解错误原因。

二、函数行为

在 panic() 函数被调用时,内核会执行一系列操作,包括但不限于:

  1. 关闭本地中断:防止任务抢占,确保只有一个 CPU 执行紧急代码。
  2. 修改 console 级别:以便 printk 能把消息打印出来。
  3. 通知运行 panic 处理函数:通过通知链调用注册的 panic 处理函数。
  4. dump CPU 寄存器的状态:输出一些堆栈的信息,帮助开发人员定位问题。
  5. 重启系统:如果配置了 panic_timeout 参数,并且其值大于 0,则在 panic_timeout 后重启系统。

三、panic() 函数的参数

panic() 函数通常会接受一些参数,这些参数有助于开发人员快速定位问题和进行故障排查。常见的参数包括:

  1. message:描述出错原因的字符串,通常会包含一些错误信息和调试信息。
  2. pid:出错的进程号,有助于定位到具体哪个进程导致了系统崩溃。
  3. time:系统发生错误的时间戳,有助于追踪问题发生的时间点。
  4. stack:系统当前的调用栈信息,有助于了解系统在出错时的调用流程。

四、使用场景

panic() 函数通常用于以下场景:

  1. 内核内部错误:如非法内存访问、内核态引用未初始化的野指针等。
  2. 硬件故障:如 CPU 过热、内存故障等。
  3. 系统崩溃:当系统遇到无法处理的异常情况时,会调用 panic() 函数停止系统运行。

内核代码如下

panic

案例代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/panic.h> static int __init hello_init(void) {printk(KERN_ALERT "panic()\n");panic("BUG!"); printk(KERN_ALERT "panic() over\n");return 0;
}static void __exit hello_exit(void) {printk(KERN_ALERT "test module\n");
}module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Name");
MODULE_DESCRIPTION("A simple test module using panic().");

4. BUG_ON()

BUG_ON() 是 Linux 内核中用于断言的一个宏,它用于检测代码中不应该发生的条件。如果 BUG_ON() 中的条件为真(即表达式的结果非零),则系统会立即触发 panic(),导致内核崩溃,并打印出错误信息,包括调用栈的回溯。这种机制主要用于开发阶段的调试,帮助开发者及早发现并修复代码中的严重错误。

使用场景

BUG_ON() 通常用于以下场景:

  1. 检测逻辑错误:当代码中的某个条件不应该为真时,可以使用 BUG_ON() 来检测这个条件。如果条件为真,说明代码中存在逻辑错误,需要修复。
  2. 防止资源泄漏:在资源管理中,如果某个资源(如内存、文件描述符等)没有被正确释放或回收,可能会导致资源泄漏。BUG_ON() 可以用于检测这种情况,并在发现时触发内核崩溃,以便开发者能够及时发现并修复问题。
  3. 验证系统状态:在某些情况下,系统状态应该满足特定的条件。如果条件不满足,说明系统可能处于不稳定或错误的状态。BUG_ON() 可以用于验证这些条件,并在不满足时触发内核崩溃。

BUG_ON()调用BUG()调用panic();

内核代码如下

bug_on

bugon

案例代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/bug.h>static int __init hello_init(void) {printk(KERN_ALERT "BUG_ON()\n");BUG_ON(1);printk(KERN_ALERT "BUG_ON()over\n");return 0;
}static void __exit hello_exit(void) {printk(KERN_ALERT "test module\n");
}module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Name");
MODULE_DESCRIPTION("A simple test module using BUG_ON().");

http://www.ppmy.cn/server/145976.html

相关文章

如何利用Java爬虫按关键字搜索工厂数据

在当今这个信息爆炸的时代&#xff0c;数据的获取和处理能力成为了企业和个人竞争力的关键。对于工厂数据的获取&#xff0c;传统的人工搜索方式不仅效率低下&#xff0c;而且容易出错。因此&#xff0c;利用Java编写爬虫程序&#xff0c;自动化地按关键字搜索工厂数据&#xf…

mac 安装node提示 nvm install v14.21.3 failed可能存在问题

如果你在 macOS 上使用 nvm&#xff08;Node Version Manager&#xff09;安装 Node.js 版本 v14.21.3 时遇到安装失败的问题&#xff0c;可以按照以下步骤进行排查和解决&#xff1a; 1. 确认 nvm 安装是否正确 首先&#xff0c;确认你的 nvm 是否正确安装&#xff0c;并且能…

【Flink】快速理解 FlinkCDC 2.0 原理

快速理解 FlinkCDC 2.0 原理 要详细理解 Flink CDC 原理可以看看这篇文章&#xff0c;讲得很详细&#xff1a;深入解析 Flink CDC 增量快照读取机制 (https://juejin.cn/post/7325370003192578075)。 FlnkCDC 2.0&#xff1a; Flink 2.x 引入了增量快照读取机制&#xff0c;…

vue3 + vite + antdv 项目中自定义图标

前言&#xff1a; 去iconfont-阿里巴巴矢量图标库 下载自己需要的icon图标&#xff0c;下载格式为svg&#xff1b;项目中在存放静态资源的文件夹下 assets 创建一个存放svg格式的图片的文件夹。 步骤&#xff1a; 1、安装vite-plugin-svg-icons npm i vite-plugin-svg-icons …

python爬虫案例——猫眼电影数据抓取之字体解密,多套字体文件解密方法(20)

文章目录 1、任务目标2、网站分析3、代码编写1、任务目标 目标网站:猫眼电影(https://www.maoyan.com/films?showType=2) 要求:抓取该网站下,所有即将上映电影的预约人数,保证能够获取到实时更新的内容;如下: 2、网站分析 进入目标网站,打开开发者模式,经过分析,我…

全面解析 Map、WeakMap、Set、WeakSet

目录 Map 是什么&#xff1f; Map 实例属性和方法 WeakMap 是什么? WeakMap 常用方法 WeakMap 使用场景举例 Set 是什么&#xff1f; 创建 Set Set 实例属性和方法 遍历 Set Set 与数组的转换 Set 使用场景 WeakSet 是什么? WeakSet 特点 WeakSet实例方法 W…

【工具】JS解析XML并且转为json对象

【工具】JS解析XML并且转为json对象 <?xml version1.0 encodingGB2312?> <root><head><transcode>hhhhhhh</transcode></head><body><param>ccccccc</param><param>aaaaaaa</param><param>qqqq<…

Java爬虫:深入探索1688接口的奥秘

在数字化时代&#xff0c;数据成为了企业最宝贵的资产之一。对于电商企业来说&#xff0c;获取和分析数据的能力直接关系到其市场竞争力。阿里巴巴旗下的1688平台&#xff0c;作为中国领先的批发贸易平台&#xff0c;拥有海量的商家和商品信息&#xff0c;成为了众多企业获取数…