0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【RT-Thread学习笔记】实现boot跳转到kernel

嵌入式物联网开发 来源:嵌入式物联网开发 作者:嵌入式物联网开发 2022-07-30 13:51 次阅读

在之前的一篇文章【C语言】没想到指针还能这么用 @!!!中介绍了【函数指针】的基本概念和简单应用;今天再给大家分享一个【函数指针】的高级应用;在嵌入式系统开发中,此类用法非常地常见,但如果对【函数指针】的理解不够透彻,很有可能会看得一头雾水。

代码片段如下:

typedef void (*kernel_func)(void);

void jump_to_kernel(void)
{
	uint32_t *kernel_start = (uint32_t *)0x410000;	
	kernel_func func = (kernel_func)kernel_start[0];
	printk("%s()%d: %08x\n", __FUNCTION__, __LINE__, func);
	local_irq_disable();
	func();
}
poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

我们来分析下这段代码:

从函数名,我们可以知道,这个函数的功能就是实现从boot程序到kernel程序的跳转,即boot程序将kernel程序跑起来。

函数的第一句代码:uint32_t *kernel_start = (uint32_t *)0x410000; 告诉我们kernel程序的执行地址是0x410000;这是一个绝对地址,在C语言中,地址就是可运行代码的起始位置,它一般就是一个整型数,比如在32位的CPU上,它就是一个32位的整型数。

函数的第二句代码:kernel_func func = (kernel_func)kernel_start[0]; 这里用到了一个typedef定义的函数指针别名,它的定义为:typedef void (*kernel_func)(void); 它定义了函数指针,此指针指向一种函数,这种函数返回值为void型,入参也为void。所以 kernel_func func = (kernel_func)kernel_start[0]; 这整一句代码的意思就是: 定义一个名称为func的函数指针,它指向一个由kernel_start这个变量(地址为0x410000)代表的函数,为了保证函数指针赋值的正确性,还加上了(kernel_func)做强制类型转换。

函数的第三局代码为printk打印输出,不在此讨论范围内。

函数的第四句代码:local_irq_disable(); 表示关闭当前系统的中断,为kernel程序的运行创造干净的环境,也不在此讨论的范围。

函数的第五句代码:func(); 非常的干净、简单。就一个简单的func执行就完成了从boot程序切换到kernel程序运行。这就是【函数指针】的魅力所在,在执行func()之前,它已经指向了kernel程序的起始地址0x41000,根据【函数指针】的语法规则,执行func(),实则就是执行0x410000这个地址对应的函数,也就把kernel程序跑起来了。



以上分析,相信有一定C语言基础的童鞋,都能分析得出来。但是,当我接触到这段代码的时候,看了下那句printk调试输出,我发现了一个疑问: %08x输出func时,居然输出的不是0x410000,而是一个可能跟0x410000毫不相干的数值。

why ? 到底是为什么啊? 当时我好纳闷,函数的第一句不是把kernel_start变量赋值为0x410000,然后函数的第二句不是把func这个函数指针变量赋值为kernel_start,这不就是相当于func就等于0x41000吗?这也有错?

为了一探究竟,我特意请教了一个别的部门同事,当时他帮我捋了捋,两人最后得出的一致结论就是: 既然是函数指针,终究是个指针,那么按照我们的需求,应该理解为 “指针的内容是0x41000”,而根据指针的访问规则,访问其内容应该要加*符号,所以*func的输出才是0x410000,func的输出是其他值。当时这个说法,我也是认同的;不过抱着严谨好学的态度,我还是决定在代码是调试调试,一试便知真假。

但是,不试不知道,一试吓一跳。我将printk那句代码,改了下:

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

​编辑

输出的结果:竟然是只有kernel_start输出的是0x410000,其他的几个输出都是同样的一个别的数值。

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

​编辑

kernel程序的bin文件的hexdump图片: 【小端模式存储】

为什么啊?

我们重新捋一捋这段代码:

先明确各个变量是什么东西:
func是一个函数指针变量
kernel_start是一个指针变量,即它是一个地址,说白了就是一个数值
kernel_start[0] 可以这么理解,kernel_start是一个数组名,即数组的首地址,取它的第一个元素

再分析下每句打印语句:
printk("%s()%d: %08x\n", __FUNCTION__, __LINE__, kernel_start); 输出00410000最好理解,它就是打印一个数值,肯定就是00410000
printk("%s()%d: %08x\n", __FUNCTION__, __LINE__, *kernel_start); 输出00419690,需要转换下思路,kernel_start是一个指针;
所以*kernel_start打印的是指针指向的内容,打印不是00410000,而是00410000地址存放的内容,即00419690
printk("%s()%d: %08x\n", __FUNCTION__, __LINE__, kernel_start[0]); 把kernel_start理解成一个数组名(指针和数组名有相通之处)
我们也不难推断出kernel_start[0]打印的应该是00419690,而不是00410000

最后分析有关func的输出,为何都是00419690,而不是00410000:
函数指针的特殊性,与普通指针不太一样,如一个函数指针p指向了一个已经定义函数test_func,那么相当于 *p 等同于 test_func (与指针的基本概念一致)所以调用函数时,可以使用 test_func(param_in),也可以使用(*p)(param_in);两者是等价的。
根据指针与数组名的关系类比,调用函数也可以使用 p(param_in) 和 (*test_func)(param_in)。
根据上面的分析,我们可以得出结论,当是用%08x打印func和*func的时候,打印的都是地址00410000指向的内容00419690,而不是地址值00410000

以下图片是从C语言的教科书截取的;

函数指针,不知你绕晕了没?


延伸阅读:

【C语言】没想到指针还能这么用 @!!!

​审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C语言
    +关注

    关注

    180

    文章

    7528

    浏览量

    128337
  • Boot
    +关注

    关注

    0

    文章

    142

    浏览量

    35226
  • RT-Thread
    +关注

    关注

    31

    文章

    1148

    浏览量

    38865
  • Kernel
    +关注

    关注

    0

    文章

    48

    浏览量

    11034
收藏 人收藏

    评论

    相关推荐

    RT-Thread学习笔记】使用scons命令生成静态库

    RT-Thread学习笔记】如何使用scons 命令中buildlib的生成静态库?
    的头像 发表于 07-27 09:13 4750次阅读
    【<b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】使用scons命令生成静态库

    RT-Thread学习笔记】ARM汇编基础的三大块知识

    RT-Thread学习笔记】ARM汇编基础的三大块知识
    的头像 发表于 07-30 10:59 1603次阅读
    【<b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】ARM汇编基础的三大块知识

    RT-Thread学习笔记】一种C语言宏定义的写法

    RT-Thread学习笔记】一种C语言宏定义的写法
    的头像 发表于 07-30 13:41 1935次阅读
    【<b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】一种C语言宏定义的写法

    u-boot是如何实现跳转到Kernel

    u-boot是如何实现跳转到Kernel的?有哪些基本步骤?
    发表于 11-30 06:32

    RT-Thread Nano入门学习笔记

    RT-Thread Nano入门学习笔记
    发表于 11-26 12:36 20次下载
    <b class='flag-5'>RT-Thread</b> Nano入门<b class='flag-5'>学习</b><b class='flag-5'>笔记</b>

    RT-Thread 内核学习笔记 - 理解defunct僵尸线程

    RT-Thread 内核学习笔记 - 内核对象rt_objectRT-Thread 内核学习笔记
    发表于 01-25 18:19 8次下载
    <b class='flag-5'>RT-Thread</b> 内核<b class='flag-5'>学习</b><b class='flag-5'>笔记</b> - 理解defunct僵尸线程

    RT-Thread 内核学习笔记 - 设备模型rt_device的理解

    RT-Thread 内核学习笔记 - 内核对象rt_objectRT-Thread 内核学习笔记
    发表于 01-25 18:19 8次下载
    <b class='flag-5'>RT-Thread</b> 内核<b class='flag-5'>学习</b><b class='flag-5'>笔记</b> - 设备模型<b class='flag-5'>rt</b>_device的理解

    RT-Thread 内核学习笔记 - 内核对象链表结构深入理解

    RT-Thread 内核学习笔记 - 内核对象rt_objectRT-Thread 内核学习笔记
    发表于 01-25 18:23 6次下载
    <b class='flag-5'>RT-Thread</b> 内核<b class='flag-5'>学习</b><b class='flag-5'>笔记</b> - 内核对象链表结构深入理解

    RT-Thread 内核学习笔记 - 内核对象初始化链表组织方式

    RT-Thread 内核学习笔记 - 内核对象rt_objectRT-Thread 内核学习笔记
    发表于 01-25 18:24 3次下载
    <b class='flag-5'>RT-Thread</b> 内核<b class='flag-5'>学习</b><b class='flag-5'>笔记</b> - 内核对象初始化链表组织方式

    RT-Thread 内核学习笔记 - 内核对象操作API

    RT-Thread 内核学习笔记 - 内核对象rt_objectRT-Thread 内核学习笔记
    发表于 01-25 18:26 7次下载
    <b class='flag-5'>RT-Thread</b> 内核<b class='flag-5'>学习</b><b class='flag-5'>笔记</b> - 内核对象操作API

    RT-Thread学习笔记 RT-Thread的架构概述

    RT-Thread 简介 作为一名 RTOS 的初学者,也许你对 RT-Thread 还比较陌生。然而,随着你的深入接触,你会逐渐发现 RT-Thread 的魅力和它相较于其他同类型 RTOS
    的头像 发表于 07-09 11:27 3989次阅读
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b> <b class='flag-5'>RT-Thread</b>的架构概述

    RT-Thread学习笔记】Makefile的FORCE

    RT-Thread学习笔记】十分钟学会Makefile的FORCE
    的头像 发表于 07-30 13:55 1646次阅读
    【<b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】Makefile的FORCE

    RT-Thread学习笔记】如何抓取终端的网络报文

    RT-Thread学习笔记】如何抓取终端的网络报文?
    的头像 发表于 07-30 13:57 2150次阅读
    【<b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】如何抓取终端的网络报文

    RT-Thread学习笔记】用memwatch排除内存泄露

    RT-Thread学习笔记】使用memwatch排除内存泄露
    的头像 发表于 07-30 14:01 1725次阅读
    【<b class='flag-5'>RT-Thread</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>】用memwatch排除内存泄露

    基于RT-Thread Studio学习

    前期准备:从官网下载 RT-Thread Studio,弄个账号登陆,开启rt-thread学习之旅。
    的头像 发表于 05-15 11:00 2576次阅读
    基于<b class='flag-5'>RT-Thread</b> Studio<b class='flag-5'>学习</b>