OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 2634|回复: 1

实用的嵌入式编码技巧:第一部分

[复制链接]

31

主题

31

帖子

0

精华

初级会员

Rank: 2

积分
147
金钱
147
注册时间
2020-5-13
在线时间
16 小时
发表于 2020-5-29 17:26:59 | 显示全部楼层 |阅读模式

几乎每个嵌入式系统都使用中断。许多支持多任务或多线程操作。这些类型的应用程序可以期望程序的控制流几乎在任何时候更改上下文。当该中断到来时,将暂停当前操作,并开始运行另一个功能或任务。如果函数和任务共享变量会怎样?如果一个例程破坏了对方的数据,那么灾难肯定会隐现。

通过仔细控制数据的共享方式,我们创建了可重入 函数,这些函数允许多个并发调用而不会相互干扰。“纯”一词有时可与“折返”互换使用。

Reentrancy最初是为大型机发明的,当时内存是一种有价值的商品。系统操作员注意到,几个大程序的十几个或数百个相同副本将随时存储在计算机的内存阵列中。在马里兰大学,骇人听闻的骇客之地,怪兽Univac 1108拥有最早进入市场的FORTRAN编译器之一。

它烧毁了惊人的(当时)32 kW的系统内存,但是可重入,即使50个用户运行它也只需要32 k。每个人都从相同的地址集执行相同的代码。每个人都有自己的数据区,但是每个运行编译器的人实际上都执行相同的代码。当操作系统在用户之间更改上下文时,它交换数据区域,因此一个人的工作不会影响其他任何人。共享代码,但不共享数据。

在嵌入式世界中,例程必须满足以下条件才能重新进入:

规则1。 它以原子方式使用所有共享变量,除非每个变量都分配给函数的特定实例。

规则2。 它不调用不可重入函数。

规则3. 它不会以非原子方式使用硬件。

原子变量

第一个规则和最后一个规则都使用“原子”一词,该词源于希腊语,意思是“不可分割”。在计算机世界中,“原子”是指不能中断的操作。考虑汇编语言说明:

mov ax,bx

由于只有复位才能停止或中断该指令,所以它是原子的。它会启动并完成,而不会受到其他任务或中断的干扰。第一部分规则1需要原子使用共享变量。假设两个函数共享全局变量“ foobar”。函数A包含:

temp = foobar; temp + = 1; foob​​ar = temp;

这段代码不是可重入的,因为foobar是非原子使用的,也就是说,它需要三个语句来更改其值,而不是一个。foob​​ar的处理不是不可分割的。这些语句之间可能会发生中断,将上下文切换到其他函数,然后该函数也可能尝试并更改foobar。

显然存在冲突,foobar将以错误的值结束,自动驾驶仪将崩溃,成千上万的尖叫者会惊叹道:“为什么他们不教那些开发人员关于可重入性?”相反,假设函数A看起来像:

foob​​ar + = 1;

现在该操作是原子操作,中断将不会在foobar处于部分更改状态的情况下暂停处理,因此例程是可重入的。

除了 。。。您真的知道C编译器生成什么吗?在x86处理器上,代码可能如下所示:

movax,[foobar]

incax mov [foobar],ax

这显然不是原子的,因此也不是可重入的。atomicversion是:

inc [foobar]

道德上要警惕编译器。假设它生成了atomiccode,您可能会发现60分钟敲门。

第一条重入规则的第二部分显示为。。。除非每个分配给该函数的特定实例。” 这是绕过共享变量问题的原子规则的一个例外。

一个实例 是通过代码的路径。没有理由不能从许多其他地方调用单个函数。在多任务环境中,该功能的多个副本确实确实有可能同时执行。(假设例程是一个从队列中检索数据的驱动程序;代码的许多不同部分可能或多或少同时需要排队的数据。)每个执行路径都是代码的“实例”。考虑:

intfoo;

无效some_function(void){ foo ++; }

foo是一个全局变量,其作用域超出了函数的作用域。即使没有其他例程使用foo,如果变量的多个实例在任何时间运行,some_function也会破坏该变量。C和C ++可以避免这种危险。使用自动变量。也就是说,在函数内部声明foo。然后,例程的每个实例将使用从堆栈创建的foo的新版本,如下所示:

voidsome_function(void){

int foo; foo ++; }

另一个选择是动态分配内存(使用malloc),因此每个化身都使用唯一的数据区。这样就避免了基本的重入问题,因为多个实例不可能在该变量的通用版本上加盖戳记。

另外两个规则

其余规则非常简单。

规则2 告诉我们调用函数继承了被调用方的重入问题。如果函数内的其他代码破坏了共享变量,那就太有意义了。该系统将崩溃。

但是,使用编译语言存在一个隐患。您确定(真的确定)运行时程序包是可重入的吗?显然,字符串操作和许多其他复杂的事情都使用运行时调用来完成实际工作。大量的编译器还会生成运行时调用,以进行较长的数学运算,甚至是整数乘法和除法运算。

如果某个函数必须是可重入的,请与编译器供应商联系以确保整个运行时包都是纯净的。如果您购买了可能在多个地方调用的软件包(如协议栈),请采取类似的预防措施,以确保所购买的例程也可重入。

规则3 是唯一嵌入的警告。硬件看起来很像一个变量,如果要用一个I / O操作来处理设备多于一个,就会产生重入问题。

考虑Zilog的SCC串行控制器。访问设备的任何内部寄存器都需要两个步骤:首先将寄存器的地址写入端口,然后从相同的端口(具有相同的I / O地址)读取或写入寄存器。如果在设置端口和访问寄存器之间出现中断,则另一个功能可能会接管并访问设备。当控制返回到第一个功能时,您设置的寄存器地址将不正确。

保持代码可重入

消除非重入代码的最佳选择是什么?经验法则是避免共享变量。全局变量是调试麻烦和失败代码的源泉。使用自动变量或动态分配的内存。

然而,全局变量还是传递数据的最快方法。从实时系统中完全消除它们是不可能的。因此,在使用共享资源(变量或硬件)时,我们必须采取不同的操作。

最常见的方法是在非重入代码期间禁用中断。随着中断的关闭,系统突然变成了单一进程环境。将没有上下文切换。禁用中断,执行不可重入的工作,然后重新打开中断。

文章来源:http://emb.hqyj.com/Column/20209509.html


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

61

帖子

0

精华

初级会员

Rank: 2

积分
62
金钱
62
注册时间
2018-12-27
在线时间
0 小时
发表于 2020-5-29 17:26:59 | 显示全部楼层
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-8 17:50

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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