我是老温,一名热爱学习的嵌入式工程师,关注我,一起变得更加优秀!
在嵌入式软件开发领域,全局变量由于用起来比较顺手,并且不用来回传参就可以实现跨函数(跨文件)调用,成为了很多嵌入式软件初学者刚刚入门的“心头好”。(全局变量用起来实在太爽了!)
尤其是遇到硬件资源紧张,需要赶项目进度的时候,使用全局变量简直是一种捷径,可以帮助我们快速完成业务逻辑开发,少写很多代码。
但是,随着项目规模越来越大,代码业务逻辑越来越复杂,“全局变量满天飞”这种做法的各个弊端就会慢慢显现,拖慢开发效率,还会给软件稳定性和后续维护增加不少麻烦。
接下来,我们来聊一下,在嵌入式软件里面滥用全局变量,有哪些弊端。
一、破坏代码封装,读起来费劲
嵌入式软件代码大多数都是围绕着硬件驱动、任务调动、数据交互而运转,好的模块封装就像给不同的功能划分了专属区域,让各个模块之间各司其职,互不干扰。
但是,全局变量偏偏就会打破这种“边界感”,让模块里面的数据可以被程序在任何地方进行随便读取和修改,造成了“牵一发而动全身”的纠缠局面。
工程师想要摸清一个全局变量的调用情况,就必须逐行查找所有调用过它的函数,不仅消耗大量的脑细胞,还容易漏掉关键的业务逻辑判断。
二、代码维护麻烦,难以扩展
嵌入式软件通常都是针对特定的硬件而设计,有时候还需要添加新的功能或者适配新的硬件接口,一旦全局变量用多了,模块间的依赖关系就会变得一团乱麻,改一个变量,就会连同其他模块也出现问题。
比如说,在团队合作开发的时候,如果全局变量没有什么访问限制,好几个团队成员都可以同时修改同一个全局变量,如果沟通不到位,就会很容易出现冲突,这样在合并代码和排查问题的时候,会耗费不少时间。
三、RTOS并发安全问题
某些嵌入式系统会用到RTOS进行多任务调度(比如FreeRTOS、RT-Thread),多个任务之间有可能同时去读取或修改同一个全局变量,但操作全局变量并不是“一步到位”的,任务切换会很容易出现数据争抢的情况出现。
举个例子,比如在工业控制器里面,任务A负责实时采集传感器数据并更新全局变量g_sensor_data,而任务B负责读取这个数据并执行对应的操作。
假如任务A还没有改完变量就被任务B抢占打断,而任务B读取到的不是传感器的最新数值,就很有可能发出错误的控制命令,让设备产生故障。
就算是没有采用RTOS开发,采用单片机裸机开发的时候,中断服务函数和主函数如果共用一个全局变量,主函数也有可能会被中断打断操作,从而导致数据出错。
四、浪费单片机内存
单片机的内存空间(RAM和ROM)本来就很吃紧,大多数8位或16位的单片机芯片,RAM容量可能就几十KB,全局变量在定义的时候就会一直占用内存空间,直到程序停止运行才会释放出来。
如果全局变量应用得太多,内存的利用率就会大大降低,严重的时候还会出现栈溢出或者内存泄漏的问题。
而且,全局变量分散存储会破坏内存空间的连续性,让CPU读取内存更加耗费时间,影响系统的实时响应速度,如果在汽车电子、医疗设备这些对实时性要求极高的场景里面,还有可能引发严重的安全事故。
五、增加测试难度,可靠性打折
嵌入式软件在完成开发之后,通常要经过单元测试、集成测试、系统测试、可靠性测试,等多个环节,而在软件里面大量使用全局变量,会让单元测试很难独立开展。
模块单元测试的核心就是,把每个模块单独隔离开来,然后通过各种测试用例验证单个函数或者模块是否可靠,但如果模块之间使用了全局变量,就会让模块依赖外部数据,根本没有办法搭建纯净的开发环境。
比如,要测试一个依赖全局变量“g_config_param”的函数,需要先手动初始化这个变量,如果这个全局变量被其他测试用例修改了,测试结果就会变得不准确。
更加麻烦的是,全局变量引发的问题通常都很隐蔽,在进行单任务测试的时候可能根本复现不了,只有在多任务同时运行和特定时序下才会暴露出来,排查和修复起来特别棘手,从而影响软件的整体可靠性。
六、总结
虽然全局变量有以上的弊端,但也不是说完全不能使用全局变量,关键还是要工程师在编程的时候进行合理管控。分享以下几个全局变量优化的方法:
1、做好模块化划分和封装,把模块数据和操作数据的函数捆绑在一起,只能通过特定的接口进行模块数据访问,不允许对数据进行直接操作。
2、可以在模块内部使用局部变量,必要时可以通过函数传参的方式进行数据传递,减少模块与模块之间的耦合和依赖。
3、使用静态变量代替部分全局变量,把静态变量的作用域限制在当前的文件或模块内部,不让整个程序都能访问。
4、在多任务的场景下,可以使用互斥锁/信号量这些同步工具,对共享数据进行保护,避免多任务直接对数据进行抢夺使用。
总的来说,全局变量使用起来虽然简单直接方便快捷,但也藏着不少容易被忽略的隐患在里面,尤其是嵌入式软件对稳定性、实时性、可维护性的要求极高,滥用全局变量只会给软件项目埋下长期隐患。
建议嵌入式软件工程师养成“少用全局变量,合理控制变量使用范围”的编程习惯,通过科学的模块化设计和规范的编码方式,让嵌入式软件更加稳定、更好维护,从而保障设备长期正常运行。
390