C语言函数在硬件中的执行原理

2021-10-22 16:36:38 digiproto

01

什么是函数?



众所周知,C语言的应用程序总是以「main」函数作为程序入口地址,那到底什么是函数呢?


程序的执行其实本质上是CPU对指令的处理,那么一个C函数的本质其实也相当于一组协同工作的指令,而这组指令能够通过相互之间的配合实现特定的功能(Function)。例如从SPI缓冲区中提取数据,配置定时器的延时时间,或者从内存读取数据并写入DAC的寄存器中。


一个函数能完成的功能往往不止一个,比如某个函数能够实现通过串口传输一个字节的数据,然后检查状态位直到接收到一个字节,最后把接收到的所有数据组合起来做实际的计算。


另外,如下图所示,使用多个函数能够清晰地描述整个代码中的工作流,一个好的函数名能够让查看代码的人员清楚地了解到函数所完成的功能。

图片关键词


02

函数和硬件的联系


嵌入式系统的设计一般基于冯诺伊曼的架构设计,CPU无法直接读取并处理C语言中的函数和变量,而是需要通过编译器将C代码编译生成CPU可识别的机器码(机器码相对于人类可读取的就是汇编码),之后再执行机器码。所有的数据、指令,包括函数地址,在嵌入式系统中统一都是用「内存」来进行存放和管理。


如果你之前有过汇编调试或者阅读的经验,就能够清楚的了解到代码实际执行的底层逻辑:被执行的程序由多个函数组成,函数被编译器编译后生成汇编指令(按顺序排列),每一条指令在内存中都有一个唯一地址,CPU需要处理这条指令时,程序指针(PC)就会跳转到这个地址。


函数跳转也是如此,当你通过一个函数调用另一个函数时,CPU的PC指针就会从当前地址跳转到被调用函数的首地址,然后执行被调用函数的汇编码。


03

调用时的堆栈信息


函数跳转时需要把调用函数的返回地址,形参等信息压栈,以确保执行完被调用函数后能够返回当前地址继续执行。调用时的栈中存储内容参考下图:

图片关键词


04

使用SkyEye全数字仿真进一步了解底层

函数执行的原理


全数字仿真即能够在不具备真实硬件的情况下,通过数字仿真实现windows/linux上模拟出一块「虚拟的硬件目标板」,该目标板能够运行真实嵌入式的二进制程序,比如真实在Arm上运行的嵌入式linux可以直接放到全数字仿真软件SkyEye中加载运行,整个过程都在软件中完成。


关于SkyEye的说明和使用参考链接:《一步一步写嵌入式操作系统》与全数字实时仿真平台

SkyEye - 迪捷软件的文章- 知乎 https://zhuanlan.zhihu.com/p/190087814


我们可以通过下面的过程加深对函数在嵌入式硬件中的执行原理:


1.首先我们拿到一个嵌入式的二进制程序,嵌入式程序main函数部分的代码如下,先对其进行简单的执行流程分析:

图片关键词


该程序是在Ti DSP C6713开发板中的一个定时串口打印输出程序。编译生成二进制程序后,我们可以通过objdump工具将该文件反汇编,即可得到对应程序的反汇编文件,了解其函数翻译成汇编码后的执行流程,截取其中一部分代码如下:

图片关键词
图片关键词


从上2图中可以得知,main函数的PC地址为0x1263c,进入main后对应源码马上跳转到printf函数,也就是汇编中执行到0x12640后调用printf函数,对应通过「b」也就是无条件跳转指令跳转到printf函数所在的地址。


2.SkyEye全数字仿真在执行该程序的过程中提供了一系列嵌入式开发的调试手段,本次主要介绍的是反汇编调试:


1)程序加载启动后,通过如下图的断点命令断在main函数的入口并执行程序,当PC执行到此时程序会自动断在此处;

图片关键词


2)第一步骤中的PC地址执行到后,再通过执行「两次」单步操作,由于执行到PC = 0x12640时为一条跳转指令「b」,跳转地址对应pritnf的首地址,因此此时PC地址会跳转到printf所在的位置--也即0x15640。

图片关键词


3)继续往下执行,printf函数所在的为0x15640 - 0x15738,通过之前的断点操作,将断点设定在「printf」函数最后一条pc地址继续执行程序;

图片关键词


4)此时,再次点击单步操作后后PC地址返回到main函数,printf函数已经执行完毕,一个完整的函数调用和执行过程也就完成了;

图片关键词


5)最终串口终端输出main函数中的「hello」字符串,这一步可以参考源码中的实现。

图片关键词


以上就是本次的介绍内容,通过SkyEye对函数内部的原理以及实际的执行过程做了一些简单介绍,细心的朋友可能会想到上面提到了关于函数调用过程中堆栈,函数返回地址,形参等各个参数的变化及过程并没有在本文中表现出来,那么实际调试时关于函数调用时,堆栈等信息如何查看呢?


在以后的文章中将为大家带来如何使用SkyEye全数字仿真平台查看程序运行过程中函数调用时,程序保存的上下文相关的信息是如何存储的,CPU又是如何获取这些信息,并确保跳转后能够正确返回的。



标签: c语言 SkyEye
首页
产品
新闻
联系