嵌入式驱动学习第六周——内核函数调用(堆栈打印)

本文主要是介绍嵌入式驱动学习第六周——内核函数调用(堆栈打印),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

   在内核中,函数调用堆栈非常重要,因为它可以帮助开发人员理解代码是如何执行的,从而进行调试、性能优化或问题排查。堆栈可以显示当前执行的函数以及导致该函数调用的先前函数,从而形成一个函数调用链。本篇博客就介绍堆栈打印内核函数的调用。

   嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程,未来预计四个月将高强度更新本专栏,喜欢的可以关注本博主并订阅本专栏,一起讨论一起学习。现在关注就是老粉啦!

目录

  • 前言
  • dump_stack函数
  • WARN_ON函数
  • BUG_ON函数
  • panic函数
  • 参考资料

dump_stack函数

   该函数作用是打印内核调用堆栈,并打印函数的调用关系。

   下面给出一段实验代码,在该内核模块中,我们定义四个函数aaabbbcccddd,然后bbb中调用aaaccc中调用bbbddd函数谁都不调用。在入口函数中,我们调用cccddd函数。

   在aaa函数中使用dump_stack函数,查看aaa函数的调用栈

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>void aaa(void) {printk(KERN_EMERG "aaa\n");dump_stack();msleep(100);
}void bbb(void) {printk(KERN_EMERG "bbb\n");aaa();msleep(100);
}void ccc(void) {printk(KERN_EMERG "ccc\n");bbb();msleep(100);
}void ddd(void) {printk(KERN_EMERG "ddd\n");msleep(100);
}static int __init chrdevTest_init(void) {printk(KERN_EMERG "INIT func\r\n");ccc();ddd();return 0;
}static void __exit chrdevTest_exit(void) {printk(KERN_EMERG "EXIT func\r\n");
}module_init(chrdevTest_init);
module_exit(chrdevTest_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wp");

   加载驱动后,显示信息如下所示,可以看到先打印INIT func,然后按照调用关系分别打印ccc,bbb,aaa。

   之后就是aaa的调用栈,框中就是调用关系,因为是chrdevTest_init调用cccccc调用bbbbbb调用aaa,由于Cortex-A处理器的堆栈是向下生长的,因此先压入chrdevTest_init函数(地址较大),在压入ccc函数(地址递减),以此类推到aaa函数。

   最后打印了一下谁都不调用的ddd函数。
在这里插入图片描述

   dump_stack()函数有助于我们调试,查看目标函数的调用关系

   如果信息消失了,可以使用dmesg指令重新再控制台打印出来

WARN_ON函数

   WARN_ON(condition)函数的作用是,在括号中的条件成立时,内核会抛出栈回溯,打印函数的调用关系,通常用于内核抛出一个警告,暗示某种不太合理的事情发生了。

   WARN_ON实际上也是调用了dump_stack,只是多了一个条件判断是否成立。

   在刚刚dump_stack函数的实验代码基础上,我们将aaa函数中dump_stack函数调用的位置改为WARN_ON(1)

void aaa(void) {printk(KERN_EMERG "aaa\n");WARN_ON(1);						// 条件为真,打印调用信息msleep(100);
}

   加载模块后,基本和上一个dump_stack的结果一样,但是可以看到,dump_stack函数也被压进调用栈了,因此可以确定WARN_ON是调用的dump_stack函数。
在这里插入图片描述

   现在我们将WARN_ON中的条件改为false,再看看结果:

void aaa(void) {printk(KERN_EMERG "aaa\n");WARN_ON(0);						// 条件为假,不打印调用信息msleep(100);
}

   可以看到控制台并没有输出调用栈。

在这里插入图片描述

BUG_ON函数

   内核中也有许多地方用到了BUG_ON函数,这个函数就像一个内核运行时的断言,意味着本来不该执行到BUG_ON这句,一旦执行就会抛出oops,导致栈的回溯和错误信息的打印,大部分体系结构把BUG()BUG_ON()定义成某种非法操作,这样自然会产生需要的oops

   在上面代码的基础上,将aaa函数中改为BUG_ON函数

void aaa(void) {printk(KERN_EMERG "aaa\n");BUG_ON(1);msleep(100);
}

   加载模块后,打印出来的信息如下所示,可以看到其中抛出了oops,并且最后并没有打印ddd函数的信息。然后函数调用关系也打印出来了,寄存器值也都打印出来了。

在这里插入图片描述

   我们将BUG_ON中的条件改为false

void aaa(void) {printk(KERN_EMERG "aaa\n");BUG_ON(0);msleep(100);
}

   加载驱动后如下所示,可以看到不打印任何堆栈信息,并且ddd函数可以顺利执行。

在这里插入图片描述

panic函数

   可以用panic()引发更严重的错误。调用panic()不但会打印错误消息(Oops)而且还会挂起整个系统。显然,你只应该在极端恶劣的情况下使用它。

   将同样的位置换为panic()函数

void aaa(void) {printk(KERN_EMERG "aaa\n");panic("###########################################wpwpwpwp");msleep(100);
}

   可以看到,打印完aaa函数后,控制台打印出panic中的字符串,然后整个进程进入到了阻塞状态。

在这里插入图片描述

参考资料

[1] Linux打印内核函数调用栈(dump_stack)

[2] Linux内核之BUG_ON()和WARN_ON()

[3] linux 内核态调试函数BUG_ON()

这篇关于嵌入式驱动学习第六周——内核函数调用(堆栈打印)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/880684

相关文章

Linux内核之内核裁剪详解

《Linux内核之内核裁剪详解》Linux内核裁剪是通过移除不必要的功能和模块,调整配置参数来优化内核,以满足特定需求,裁剪的方法包括使用配置选项、模块化设计和优化配置参数,图形裁剪工具如makeme... 目录简介一、 裁剪的原因二、裁剪的方法三、图形裁剪工具四、操作说明五、make menuconfig

如何安装HWE内核? Ubuntu安装hwe内核解决硬件太新的问题

《如何安装HWE内核?Ubuntu安装hwe内核解决硬件太新的问题》今天的主角就是hwe内核(hardwareenablementkernel),一般安装的Ubuntu都是初始内核,不能很好地支... 对于追求系统稳定性,又想充分利用最新硬件特性的 Ubuntu 用户来说,HWEXBQgUbdlna(Har

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]