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

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

前言

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

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

目录

  • 前言
  • 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

相关文章

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

如何在Ubuntu上安装NVIDIA显卡驱动? Ubuntu安装英伟达显卡驱动教程

《如何在Ubuntu上安装NVIDIA显卡驱动?Ubuntu安装英伟达显卡驱动教程》Windows系统不同,Linux系统通常不会自动安装专有显卡驱动,今天我们就来看看Ubuntu系统安装英伟达显卡... 对于使用NVIDIA显卡的Ubuntu用户来说,正确安装显卡驱动是获得最佳图形性能的关键。与Windo

嵌入式Linux之使用设备树驱动GPIO的实现方式

《嵌入式Linux之使用设备树驱动GPIO的实现方式》:本文主要介绍嵌入式Linux之使用设备树驱动GPIO的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、设备树配置1.1 添加 pinctrl 节点1.2 添加 LED 设备节点二、编写驱动程序2.1

嵌入式Linux驱动中的异步通知机制详解

《嵌入式Linux驱动中的异步通知机制详解》:本文主要介绍嵌入式Linux驱动中的异步通知机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、异步通知的核心概念1. 什么是异步通知2. 异步通知的关键组件二、异步通知的实现原理三、代码示例分析1. 设备结构

快速修复一个Panic的Linux内核的技巧

《快速修复一个Panic的Linux内核的技巧》Linux系统中运行了不当的mkinitcpio操作导致内核文件不能正常工作,重启的时候,内核启动中止于Panic状态,该怎么解决这个问题呢?下面我们就... 感谢China编程(www.chinasem.cn)网友 鸢一雨音 的投稿写这篇文章是有原因的。为了配置完

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

Java程序进程起来了但是不打印日志的原因分析

《Java程序进程起来了但是不打印日志的原因分析》:本文主要介绍Java程序进程起来了但是不打印日志的原因分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java程序进程起来了但是不打印日志的原因1、日志配置问题2、日志文件权限问题3、日志文件路径问题4、程序