当前位置:网站首页>嵌入式开发中的防御性C语言编程

嵌入式开发中的防御性C语言编程

2022-07-06 09:03:00 大方老师嵌入式

嵌入式开发中的防御性C语言编程

\\\插播一条:文章末尾有惊喜哟~///

8979a6f2201c455f47a386de1c2851c9.png

嵌入式产品的可靠性自然与硬件密不可分,但在硬件确定、并且没有第三方测试的前提下,使用防御性编程思想写出的代码,往往具有更高的稳定性。

防御性编程首先须要认清C语言的种种缺少陷和陷阱,C语言对于运行时的检查十分弱小,须要程序员谨慎的考虑代码,在必要的时候增加判断;防御性编程的另一个核心思想是假设代码运行在并不可靠的硬件上,外接干扰有可能会打乱程序执行顺序、更改RAM存储数据等等。

1.具有形参的函数,需判断传递来的实参是否合法

程序员可能没意识的传递了错误参数;外界的强干扰可能将传递的参数修改掉,或者使用随机参数意外的调用函数,因此在执行函数主体前,须要先确定实参是否合法。

2.仔细检查函数的返回值

对函数返回的错误码,要进行全面仔细处理,必要时做错误记录。

3.防止指针越界

假如动态计算一个地址时,要保证被计算的地址是合理的并指向某个有意义的地方。特别对于指向一个构造或数组的内部的指针,当指针增加或者变更后依然指向同一个构造或数组。

4.防止数组越界

数组越界的问题前文已经讲述的很多了,由于C不会对数组进行有效的检测,因此必需在应用中显式的检测数组越界问题。下面的例子可用于中断接管通讯数据。

在使用一些库函数时,同样须要对边界进行检查,假如下面的memset(RecBuf,0,len)函数把RecBuf指指向的内存区的前len个字节用0填充,假如不注意len的长度,就会将数组RecBuf之外的内存区清零:

5.数学算数运算

5.1除法运算,只检测除数为零就可靠吗?

除法运算前,检查除数是否为零简直已经成为共识,但是仅检查除数是否为零就够了吗?

考虑两个整数相除,对于一个signed long类型变量,它能表示的数值范围为:-2147483648 ~+2147483647,假如让-2147483648/ -1,那么结果应该是+2147483648,但是这个结果已经超出了signedlong所能表示的范围了。所以,在这种情况下,除了要检测除数是否为零外,还要检测除法是否溢出。

#include

signed long sl1,sl2,result;

/*初始化sl1和sl2*/

if((sl2==0)||(sl1==LONG_MIN && sl2==-1))

{

//处理错误

}

else

{

result = sl1 / sl2;

}

5.2检测运算溢出

整数的加减乘运算都有可能发生溢出,在探讨未定义行为时,给出过一个有符号整形加法溢出判断代码,这里再给出一个没符号整形加法溢出判断代码段:

#include

unsigned int a,b,result;

/*初始化a,b*/

if(UINT_MAX-a<>

{

//处理溢出

}

else

{

result=a+b;

}

嵌入式硬件一般没有浮点处理器,浮点数运算在嵌入式也比较少见并且溢出判断严重依赖C库支持,这里不探讨。

5.3检测移位

在探讨未定义行为时,提到有符号数右移、移位的数量是负值或者大于操作数的位数都是未定义行为,也提到不对有符号数进行位操作,但要检测移位的数量是否大于操作数的位数。下面给出一个没符号整数左移检测代码段:

unsigned int ui1;

unsigned int ui2;

unsigned int uresult;

/*初始化ui1,ui2*/

if(ui2>=sizeof(unsigned int)*CHAR_BIT)

{

//处理错误

}

else

{

uresult=ui1<<>

}

6.假如有硬件看门狗,则使用它

在其它一切措施都失效的情况下,看门狗可能是最后的防线。它的原理特别简略,但却能大大提高设备的可靠性。假如设备有硬件看门狗,一定要为它编写驱动程序。

?要尽可能早的开启看门狗

这是由于从上电复位完毕到开启看门狗的这段时长内,设备有可能被干扰而跳过看门狗初始化程序,导致看门狗失效。尽可能早的开启看门狗,能够降低这种概率;

?不要在中断中喂狗,除非有其他联动措施

在中断程序喂狗,由于干扰的存在,程序可能一直处于中断之中,这样会导致看门狗失效。假如在主程序中设置标志位,中断程序喂狗时与这个标志位联合判断,也是允许的;

?喂狗间隔跟产品需求有关,并非特定的时长

产品的特性决定了喂狗间隔。对于不波及安全性、实时性的设备,喂狗间隔比较宽松,但间隔时长不宜过长,否则被用户感知到,是影响用户体验的。对于设计安全性、有实时控制类的设备,原则是尽可能快的复位,否则会造成事故。

克莱门汀号在进行第二阶段的任务时,原本预订要从月球飞行到太空深处的Geographos小行星进行探勘,然而这艘太空探测器在飞向小行星时却由于一个软件缺少陷而使其中断运作20分钟,不光未能到达小行星,也由于控制喷嘴焚烧了11分钟使电力供给降低,没法再透过远端控制探测器,最终完毕这项任务,但也导致了资源与资金的浪费。

“克莱门汀太空任务失败这件事让我感到十分震惊,它其实能够透过硬件中一款简略的看门狗计时器避免掉这项意外,但由于当时的开发时长相当紧缩,程序设计人员没时长编写程序来启动它,”Ganssle说。

遗憾的是,1998年发射的近地号太空船(NEAR)也遇到了相同的问题。由于编程人员并未采纳建议,因此,当推进器减速器系统故障时,29公斤的储备燃料也随之报销──这同样是一个原本可经由看门狗定时器编程而避免的问题,同时也证明要从其他程序设计人员的错误中进修并不容易。

7.关键数据储存多个备份,取数据采用“表决法”

RAM中的数据在受到干扰情况下有可能被变更,对于系统关键数据应该进行珍爱。关键数据包括全局变量、静态变量以及须要珍爱的数据区域。备份数据与原数据不应该处于相邻位置,因此不应由编译器默认分配备份数据位置,而应该由程序员指定区域存储。

能够将RAM分为3个区域,第一个区域保存原码,第二个区域保存反码,第三个区域保存异或码,区域之间预留一定量的“空白”RAM作为隔离。能够使用编译器的“分散加载”机制将变量分别存储在这些区域。须要进行读取时,同时读出3份数据并进行表决,取至少有两个相同的那个值。

假设设备的RAM从0x1000_0000初始,我须要在RAM的0x1000_0000~0x10007FFF内存储原码,在0x1000_9000~0x10009FFF内存储反码,在0x1000_B000~0x1000BFFF内存储0xAA的异或码,编译器的分散加载能够设置为:

LR_IROM1 0x00000000 0x00080000 { ; load region size_region

ER_IROM1 0x00000000 0x00080000 { ; load address = execution address

*.o (RESET, +First)

*(InRoot$$Sections)

.ANY (+RO)

}

RW_IRAM1 0x10000000 0x00008000 { ;保存原码

.ANY (+RW +ZI )

}

RW_IRAM3 0x10009000 0x00001000{ ;保存反码

.ANY (MY_BK1)

}

RW_IRAM2 0x1000B000 0x00001000 { ;保存异或码

.ANY (MY_BK2)

}

}

假如一个关键变量须要多处备份,能够依照下面方式定义变量,将三个变量分别指定到三个不不间断的RAM区中,并在定义时依照原码、反码、0xAA的异或码进行初始化。

uint32 plc_pc=0; //原码

__attribute__((section("MY_BK1"))) uint32 plc_pc_not=~0x0; //反码

__attribute__((section("MY_BK2"))) uint32 plc_pc_xor=0x0^0xAAAAAAAA; //异或码

当须要写这个变量时,这三个位置都要更新;读取变量时,读取三个值做判断,取至少有两个相同的那个值。

为什么选取异或码而不是补码?这是由于MDK的整数是依照补码存储的,正数的补码与原码相

写文章

keil下的STM32程序开发部署(一)

史强

https://github.com/freeeyes

?关注他

3人赞同了该文章

买一块&nbsp;STM32的进修版很重要,虽然STM32的老本不足5元,但是对应的接口GPIO输出到不同的硬件连接,假如完全自己做的话还是比较大的工程,且意义有限。

首先下载keil编译器,这个和STM配合比较好。

举荐keil5,下载后,安装一下&nbsp;STM的驱动包。

这里最好吧STM的所有芯片驱动都装上,由于自身也不大,STM小型号比较多,假如常见的103XX等等。

这里所有的数据包,安装好保持最新的即可。

然后配置一下keil环境。

这里有几个地方注意一下。

首先,设置DEBUG的参数。

这里要选择ST-LInk,这是一个小的硬件。能够和STM进修板连接。能够去京东搜搜,都有,这里要注意一点,第一次ST-LInk接入板子,这里请更新一下ST-Link的驱动,详细在买ST-LInk的时候一般会有一个小光盘,或者直接找对应厂商要,由于ST-LInk的老驱动对keil5兼容有问题,升级后就能够了。

还有一个注意下面的DEBUG配置

这里须要指定连接后,直接reset板子,让程序生效,这样,当你烧录程序后,马上就能够看到结果了。

另外,建议初学者,找一个keil的样例工程来改写。

由于文件组织目录是有学问的。

假如,最简略的。

这里的目录构造。最好和你的实际文件目录构造一致。

所有的驱动放在一个目录下,系统文件放在一个目录下。

你的主程序放在一个目录下。组织比较清晰。

keil的所有主入口是main,和C代码是一致的。

然后就是如何让程序跑起来。

这里是编译

你能够在这里编译你的代码。

这里有一个小技巧。假如你的代码比较复杂,你能够使用F12查找你的函数定义和实现。

编译的结果,能够在下面的输出看到

最后一步,等鄙汆译都没错了。

把程序烧录进STM板子即可。

以上就是最根本的keil5和stm板子调试方法。

后面,我会慢慢会补充一些实际有用的小程序代码以及说明。

如何使用串口来给STM32下载程序

彩蛋:最近有同学跟我要单片机的资料,我特意花几个月时间,总结了我10年产品研发经验,资料包几乎覆盖了C语言、单片机、模电数电、原理图和PCB设计、单片机高级编程等等,非常适合初学者入门和进阶。除此以外,再含泪分享我压箱底的22个热门开源项目,包含源码+原理图+PCB+说明文档,不是市面上打包卖的那种课程,我认为教程多未必是好事,10年前我自学快,除了自身执行力以外,还有就是教程少。不要害羞做伸手党,等你一个小红点。后期我也会组建一些纯技术交流的小圈子,让大家能认识更多的大佬,有个好的圈子,你对行业的认知一定是最前沿的。50477e660b08c3e9ccd7f0a9a44789e4.pnge76be661bf7c90a40dcd6a3f035a0084.png

原网站

版权声明
本文为[大方老师嵌入式]所创,转载请带上原文链接,感谢
https://blog.csdn.net/l16756062003/article/details/125091982