Linux 内核 调用堆栈打印函数

文章目录

  • 内核函数调用堆栈打印
  • 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.rhkb.cn/news/480215.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言——指针初阶(一)

目录 一.什么是指针&#xff1f;&#xff1f;&#xff1f; 指针是什么&#xff1f; 指针变量&#xff1a; 总结&#xff1a; 总结&#xff1a; 二.指针和指针类型 指针-整数&#xff1a; 总结&#xff1a; 指针的解引用 总结&#xff1a; 三.野指针 如何规避野指针 往期…

【Redis】Redis 预备知识

目录 1. 基本全局命令 KEYS EXISTS DEL EXPIRE TTL TYPE 2. 数据结构和内部编码 3. 单线程架构 Redis 提供了5种数据结构&#xff0c;理解每种数据结构的特点对于 Redis 开发运维非常重要&#xff0c;同时掌握每种数据结构的常见命令&#xff0c;会在使用 Redis 的时…

Facebook广告无法投放是什么原因?

Facebook作为全球知名的社媒平台&#xff0c;同时也成为许多知名海外企业的广告首选。但很投手在投放过程中也发现&#xff0c;Facebook 广告投放失败或者被拒投&#xff0c;那到底为什么呢&#xff1f; 其实Facebook广告有着非常严格的审核制度&#xff0c;通常投放失败可能是…

【uniapp】轮播图

前言 Uniapp的swiper组件是一个滑块视图容器组件&#xff0c;可以在其中放置多个轮播图或滑动卡片。它是基于微信小程序的swiper组件进行封装&#xff0c;可以在不同的平台上使用&#xff0c;如微信小程序、H5、App等。 效果图 前端代码 swiper组件 <template><vi…

【JavaEE】多线程(3)

首先回顾一下线程不安全的原因&#xff1a; 线程是随机调度&#xff0c;抢占式执行的修改共享数据&#xff0c;多个线程修改同一个变量多个线程修改共享数据的操作不是原子性&#xff0c;&#xff08;count是3个CPU指令&#xff0c;但是赋值操作就是原子性的&#xff09;内存可…

(0基础保姆教程)-JavaEE开课啦!--12课程(Spring MVC注解 + Vue2.0 + Mybatis)-实验10

一、常见的SpringMVC注解有哪些&#xff1f; 1.Controller&#xff1a;用于声明一个类为 Spring MVC 控制器。 2.RequestMapping&#xff1a;用于将 HTTP 请求映射到特定的处理方法上。可以指定请求类型&#xff08;GET、POST等&#xff09;和URL路径。 3.GetMapping&#xff…

20241124 Typecho 视频插入插件

博文免不了涉及到视频插入这些,网上的插件都或多或少的比较重,和Typecho的风格不搭配 后面就有了DPlay插件精简而来的VideoInsertion插件 VideoInsertion: Typecho 视频插入插件 目录结构 rockhinlink-ht2:/var/www/html/typecho/usr/plugins/VideoInsertion$ tree -h [4.…

网络地址转换

NAT概述 解决公有地址不足&#xff0c;并且分配不均匀的问题 公有地址&#xff1a;由专门的机构管理、分配&#xff0c;可以在因特网上直接通信 私有地址&#xff1a;组织和个人可以任意使用&#xff0c;只能在内网使用的IP地址 A、B、C类地址中各预留了一些私有IP地址 A&…

H5流媒体播放器EasyPlayer.js网页直播/点播播放器如果H.265视频在播放器上播放不流畅,可以考虑的解决方案

随着流媒体技术的迅速发展&#xff0c;H5流媒体播放器已成为现代网络视频播放的重要工具。其中&#xff0c;EasyPlayer.js网页直播/点播播放器作为一款功能强大的H5播放器&#xff0c;凭借其全面的协议支持、多种解码方式以及跨平台兼容性&#xff0c;赢得了广泛的关注和应用。…

以达梦为数据库底座时部署的微服务页面报乱码,调整兼容模式

1.问题描述 部署微服务&#xff0c;文件、代码是延用的mysql类型的&#xff0c;部署前做了部分适配&#xff0c;但是在使用dm数据库进行安装的服务在页面上查询出的数据却都是乱码 2.查询官网&#xff0c;注意到一个参数COMPATIBLE_MODE兼容模式的配置 考虑是延用mysql&…

【RL Base】强化学习核心算法:深度Q网络(DQN)算法

&#x1f4e2;本篇文章是博主强化学习&#xff08;RL&#xff09;领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅…

Spring Boot【三】

自动注入 xml中可以在bean元素中通过autowire属性来设置自动注入的方式&#xff1a; <bean id"" class"" autowire"byType|byName|constructor|default" /> byName&#xff1a;按照名称进行注入 byType&#xff1a;按类型进行注入 constr…

软件报错:找不到vcomp140.dll的原因分析,总结六种解决vcomp140.dll的方法

vcomp140.dll是一个与MicrosoftVisualCRedistributableforVisualStudio2015相关的动态链接库文件&#xff0c;主要用于支持并行编程。这个DLL文件是VisualC库的一部分&#xff0c;用来处理并行计算&#xff0c;特别是那些利用OpenMP(OpenMulti-Processing)技术编写的程序。分析…

android 项目多电脑共用github及github项目迁移

背景&#xff1a;最新需要将公司的项目在本地电脑进行使用&#xff0c;将项目迁移到本地电脑。 操作步骤&#xff1a; ssh 公钥绑定github上 : https://blog.csdn.net/mo_sss/article/details/137910910 用github进行克隆时无法下载&#xff08;已将本地创建的公钥上传gith…

常用元器件使用方法18:单节锂电池充电管理芯片XT4052的使用方法

文章目录 一、产品概述二、产品特点三、典型应用电路图四、引脚分配五、应用电路六、PCB应用建议一、产品概述 XT4052 是一个完善的单片锂离子电池恒流/恒压线形电源管理芯片。它薄的尺寸和小的外包装使它便于便携应用。更值得一提的是,XT4052专门设计适用于USB的供电规格。得…

java八股-分布式服务的接口幂等性如何设计?

文章目录 接口幂等token Redis分布式锁 原文视频链接&#xff1a;讲解的流程特别清晰&#xff0c;易懂&#xff0c;收获巨大 【新版Java面试专题视频教程&#xff0c;java八股文面试全套真题深度详解&#xff08;含大厂高频面试真题&#xff09;】 https://www.bilibili.com/…

python的字体如何调整

首先打开pycharm&#xff0c;新建一个Python文件&#xff0c;点击“File”&#xff0c;在下拉菜单栏中选择“New”。 然后点击“File”&#xff0c;输入我们的Python文件名称&#xff0c;后缀一定要填写“.py”&#xff0c;要不会出错&#xff0c;单击“OK”。 在新文件中输入代…

【Linux系列】Chrony时间同步服务器搭建完整指南

1. 简介 Chrony是一个用于Linux系统的高效、精准的时间同步工具&#xff0c;通常用于替代传统的NTP&#xff08;Network Time Protocol&#xff09;服务。Chrony不仅在系统启动时提供快速的时间同步&#xff0c;还能在时钟漂移较大的情况下进行及时调整&#xff0c;因此广泛应…

Ubuntu问题 -- 使用scp将本机文件传输至ubuntu服务器中

目的 临时没有文件传输工具使用一条命令快速传输指定文件或文件夹 使用scp命令 传输指定文件 scp -P 22 D:\Storage\myCache\UE\Linux_ue_demo.zip txl10.1.112.93:/home/txl-P是远程机器的ssh端口号, SCP&#xff08;安全复制协议&#xff09;使用和SSH&#xff08;安全外壳…

Web 毕设篇-适合小白、初级入门练手的 Spring Boot Web 毕业设计项目:电影院后台管理系统(前后端源码 + 数据库 sql 脚本)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 项目介绍 2.0 用户登录功能 3.0 用户管理功能 4.0 影院管理功能 5.0 电影管理功能 6.0 影厅管理功能 7.0 电影排片管理功能 8.0 用户评论管理功能 9.0 用户购票功…