找回密码
 立即注册
楼主: 杨为民

STC单片机 uC/OS-II核心技术(12):关于临界区保护和函数可重入的测试题

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:432
  • 最近打卡:2025-05-01 16:19:56

5

主题

1127

回帖

4263

积分

荣誉版主

积分
4263
发表于 2024-3-24 15:58:00 | 显示全部楼层
fjst*** 发表于 2024-3-24 15:54
是的,我是想我自己定义的函数希望成为可重入函数

我尽量简单回答一下吧。
首先要解答一下什么是“可重入函数”?
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:432
  • 最近打卡:2025-05-01 16:19:56

5

主题

1127

回帖

4263

积分

荣誉版主

积分
4263
发表于 2024-3-24 16:43:04 | 显示全部楼层
本帖最后由 CosyOS 于 2024-3-24 17:59 编辑
fjst*** 发表于 2024-3-24 15:54
是的,我是想我自己定义的函数希望成为可重入函数

我尽量简单回答一下吧。
首先要解答一下什么是“可重入函数”?

可重入函数:未被设计用来访问特定的全局公共资源,并且所有形参和局部变量均分配在寄存器或可重入栈中。

C51下:
一、寄存器可重入函数
所有形参和局部变量均分配在寄存器中的可重入函数。
必要条件:代码优化等级最低不能低于4级,即必须开启寄存器变量优化。
判别方法:通过查看反汇编代码确认。调用处(传参)、函数入口、形参和局部变量的引用处均需查看,确认是否均为寄存器变量。

如不能生成寄存器可重入函数,可考虑可重入栈可重入函数。

二、可重入栈可重入函数
采用“reentrant”等关键字声明并定义的函数,未采用寄存器优先分配原则,所有形参和局部变量均分配在可重入栈中。
1、使用关键字“reentrant”声明并定义的函数,使用哪个可重入栈,由内存模型决定。
2、使用“small reentrant”等关键字声明并定义的函数,使用哪个可重入栈,由关键字决定(见下表)。

截图202403241607458284.jpg

另需在启动文件中配置栈顶指针并启用相应的可重入栈。
C51的可重入栈效率较低,不建议采用。

C251下:
C251 Version 1:C251 Version 1 的可重入栈,与C51是相同的,仍为模拟栈,但取消了PBPSTACK。但现在不会再有人用 Version 1 了吧?所以 XXX。
C251 Version 2:自 C251 Version 2 开始,可重入栈采用与MDK-Arm相同的方式,即硬件栈、继承调用者栈,并同时采用寄存器优先分配原则。
可重入函数实现方案:
1、生成全局可重入函数(推荐方案)
C251标签页,“Generate reentrant functions”打勾,所有自定义函数都允许使用可重入栈,并试图生成可重入函数。
2、寄存器可重入函数 + 可重入栈可重入函数
寄存器可重入函数的生成必要条件:代码优化等级最低不能低于3级,即必须开启寄存器变量优化。但也未必能生成,必须反汇编验证,方法与C51相同。
可重入栈可重入函数:使用关键字“reentrant”声明并定义。

注意:251的可重入栈只能在edata中,因为硬件栈只能在edata中。

MDK-Arm下:
无需任何操作,直接便可以是可重入函数。

额外的注意:
1、即使一个函数可重入,也要注意它访问的资源是否为全局公共资源、是否可重入,是否需要对资源进行保护。
2、自定义函数内不要有局部的static变量,也不能对它有取址操作。实际上这一点已经被排除在可重入函数的定义之外。



回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:454
  • 最近打卡:2025-05-01 08:52:14

3

主题

109

回帖

855

积分

高级会员

积分
855
发表于 2024-3-24 19:56:03 | 显示全部楼层
Cos*** 发表于 2024-3-24 16:43
我尽量简单回答一下吧。
首先要解答一下什么是“可重入函数”?

其实我需要的不是真正的可重入函数,我想把1602的显示内容更改操作放在定时器中断里,但在主程序的开始阶段调用1602内容更改操作显示一个开机内容,但keil不允许,有解决的办法(keil操作)

点评

(1)首先对你现在的中断服务程序进行改造,把现在的中断服务程序“Old_ISR()” void Old_ISR(void ) interrupt x { ..... } 改为普通函数: void Old_ISR(void ) { ..... } 然后该写新的ISR为对旧  详情 回复 发表于 2024-3-24 21:08
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:454
  • 最近打卡:2025-05-01 08:52:14

3

主题

109

回帖

855

积分

高级会员

积分
855
发表于 2024-3-24 20:06:31 | 显示全部楼层
Cos*** 发表于 2024-3-24 16:43
我尽量简单回答一下吧。
首先要解答一下什么是“可重入函数”?

有些理解,看来这个可重入函数实现挺麻烦的,还耗资源还不如重新定义一个函数,能说说这种形式有啥好处?
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看I
  • 打卡总天数:16
  • 最近打卡:2025-04-30 08:41:32

105

主题

1215

回帖

1万

积分

荣誉版主

积分
12882
发表于 2024-3-24 21:08:46 | 显示全部楼层
本帖最后由 杨为民 于 2024-3-24 21:11 编辑
fjst*** 发表于 2024-3-24 19:56
其实我需要的不是真正的可重入函数,我想把1602的显示内容更改操作放在定时器中断里,但在主程序的开始阶 ...


(1)首先对你现在的中断服务程序进行改造,把现在的中断服务程序“Old_ISR()


void Old_ISR(void ) interrupt  x
{
  .....
}

改为普通函数:
void Old_ISR(void )


{
  .....
}





然后该写新的ISR为对旧ISR的调用:

void New_ISR(void ) interrupt  x
{
  Old_ISR();
}


(2)如果你的意思是在main函数里、打开中断EA=1之前,想要执行一次中断来显示内容,现在就可以在main函数里、打开中断EA=1之前任意使用函数调用Old_ISR();”语句了。
这时候由于中断没有打开,中断不会发生,不存在函数重入的问题,因此函数“Old_ISR()”是否可以重入没有关系。
(3)如果你的意思是在main函数里、打开中断EA=1之后,想要单独执行一次中断来显示内容,那么像上面一样地改造ISR,然后在本文前面给出的解答,在调用“Old_ISR()”的前后加临界区保护语句就可以了:
EA=0;
“Old_ISR()”

EA=1;

(4)在程序中直接调用中断服务函数是不被允许的,所以会编译出错。

点评

看了杨老师的解释,我才明白原来他是想主动调用一个中断函数!杨老师理解能力真好  发表于 2024-3-24 21:46
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:51
  • 最近打卡:2025-04-14 00:31:51
已绑定手机

5

主题

43

回帖

355

积分

中级会员

积分
355
发表于 2025-4-8 15:15:26 | 显示全部楼层
是我的话我可能会考虑写自用的不可重入函数,在不清楚他库函数如何实现的情况下,我要什么就写什么,就是工作量有点大
回复 支持 反对

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-2 00:25 , Processed in 0.118671 second(s), 88 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表