美国军方禁止在嵌入式C程序使用malloc(),99%的人都不知道的原因

2024-04-12 02:32

本文主要是介绍美国军方禁止在嵌入式C程序使用malloc(),99%的人都不知道的原因,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

[导读]想在C语言程序员之间开始一个激烈的,或者说有争议的讨论很简单,只需要问:“使用动态内存分配安全吗?”
 

想在C语言程序员之间开始一个激烈的,或者说有争议的讨论很简单,只需要问:“使用动态内存分配安全吗?”

为了更加安全稳定,美国军方禁止在C语言程序中使用malloc()

使用动态内存分配安全吗?

在C语言程序开发中,动态内存分配允许程序在运行时向系统申请内存使用,只不过在使用完毕后,需要显式的释放之,这就要求程序员对动态分配的内存了然于胸。

图片.png

在非常重视安全(safety-critical)的嵌入式C语言程序开发中,动态内存分配广泛被认为是禁忌。使用C语言的malloc()和free()库函数可能会带来灾难性的副作用,例如内存泄漏或者碎片。此外,malloc()常常会表现出极其不可预测的特性,这使其成为在多核系统上进行多线程C语言程序开发的瓶颈。

事实上,由于malloc()存在安全风险,美国军方按照DO-178B标准,在safety-critical的嵌入式航空电子设备代码中禁止动态内存分配。

为了更加安全稳定,美国军方禁止在C语言程序中使用malloc()

禁止动态内存分配

嵌入式行业的C语言程序员似乎对这个话题有着发自内心的反应。在最近的一次互联网技术小组讨论中,当提到问题:“在嵌入式C语言程序设计种是否使用动态内存分配?”时,77条回复称“使用动态内存分配是对系统容错性的最大危害之一”,还有5条回复称“如果希望系统正常运行时间能够达到‘5个9’(即99.999%),答案就是‘永远不会’使用动态内存分配”。

甚至有相关部门在招聘嵌入式C语言程序员时,会问求职者是否会使用动态内存分配,如果他们使用,就不会被雇用了。

为了通过相关的工作面试,也为了提升C语言代码安全,更好的办法是自定义一套内存分配器,一般分为两种:基于栈的分配器,以及基于本地线程的分配器。写出更安全稳定的C语言代码,就不该再使用标准库提供的malloc()和free()函数了。

为什么美国军方认为C语言标准库函数提供的动态内存分配管理函数malloc()和free()是个糟糕的选择呢?这其实要从malloc()和free()的设计上考虑,通常,它们是基于列表分配器算法的,该算法将内存池组织到单个链表中的连续位置,分配器管理该链表,每次分配实际上就是寻找空闲位置。这种分配器在各种情况下都能相当好的分配和释放内存,但是在极端的safety-critical系统中。

为了更加安全稳定,美国军方禁止在C语言程序中使用malloc()

 基于栈的内存管理器

在C语言程序开发中,某些应用程序可能只需要申请一些短期对象,很快就会使用完并释放。基于栈(此“栈”不同于函数的调用栈)的内存分配器此时就能大派用场了,该分配器每次分配都返回栈指针当前位置的地址,并按照需求推进指针,如下图:

图片.png

为了更加安全稳定,美国军方禁止在C语言程序中使用malloc()

当该内存被使用完毕,需要被释放时,只需要将栈指针往后返回即可。这样一来,处理内存分配的开销就减少了,因为没有需要管理的指针链表了,也没有需要跟踪的分配内存大小,以及空闲内存位置。另外,由于C语言程序不再跟踪特定的分配内存,所以这种内存分配器也更加安全:不匹配的内存释放不会导致内存泄漏。

对于C语言标准库提供的内存分配器来说,当内存以随机顺序释放时,列表分配器通常需要向它的链中添加指针和内存长度(这称为碎片)。当程序继续运行时,列表分配器的开销会增加,因为需要管理的元数据数量增加了,寻找合适的空闲内存位置也会更加耗时。而基于堆栈的内存分配器分配的所有内存块都将返回到堆中,碎片化就被避免了。

当在多核处理器平台进行多线程编程时,默认由互斥体做同步控制的malloc()和free()函数就比较难用了。因为他们可能会导致锁冲突,操作系统要解决这些冲突,只能通过损耗性能的上下文切换。

为了更加安全稳定,美国军方禁止在C语言程序中使用malloc()

  多核处理器多线程编程的挑战

针对此情况,C语言程序员可以自定义本地线程内存分配器,通过为每个线程分配特定的内存池来避免冲突。每个线程的内存分配是在不干扰其他线程的情况下进行的,从而提高了系统性能和程序的可预测性。

当本地线程分配器耗尽内存时,如果系统安全和允许,其他分配器可以再为它分配一个新的内存块。本地线程内存分配器可使用一个列表管理属于自己线程的内存,因此由同一个线程分配和释放的内存不需要协调,不会发生锁冲突导致的性能损耗。

简而言之,不使用malloc()和free()管理内存,使用更具预测性,更安全的自定义内存分配器,可以避免safety-critical代码出现内存安全问题。

 通过第三方应用程序分配内存

使用自定义内存分配器还有一个好处,就是可以通过集成它们的第三方应用程序使用。IMDS(In-Memory Database System,内存数据库系统)就是一个例子,它们是专门设计用来管理RAM中应用程序对象的。下图是使用malloc()和free()的一个例子:

图片.png

为了更加安全稳定,美国军方禁止在C语言程序中使用malloc()

下图则是使用mcobject的extremedb的相同过程,这是一个整合了自定义分配器的IMD,包括基于堆栈和本地线程的内存分配器。在上图的开头,C语言程序定义一个结构,声明一个指向该结构实例的指针,并通过malloc()为其分配内存。

  图片.png

使用mcobject的extremedb

如果使用malloc()/free()的C语言程序是多线程的,并且线程将共享传感器对象,那么程序员必须实现并发控制。再来看看IMD,程序开始获取了句柄,调用sensor_new()将声明一些专用于IMD的内存池,用于新的sensor对象。

在军事/航空航天应用中,传感器对象可以表示任何东西,可以是跟踪导弹目标的光学传感器,也可以是用于化学战防御的生物传感器,还可以是用于帮助飞机导航的运动传感器等。

sensor_new()返回数据库对象的句柄,通过该句柄可以写入和/或读取对象的值。相反,C语言程序直接处理结构的字段,从而在多线程应用程序中创建并发访问控制的需求。

当C语言程序使用完sensor结构后,free()将内存返回到堆。当带有IMDS的代码完成时,数据库中的空间被放弃,事务结束,用于传感器对象的内存返回到专用内存池。

为了更加安全稳定,美国军方禁止在C语言程序中使用malloc()

应用程序可以处理IMD内存不足的错误

IMD的内存可能不足,但这将生成一条“数据库已满”的错误消息,应用程序可以处理该错误消息。相反,由malloc()和free()引起的内存碎片和泄漏会破坏整个系统的稳定性,程序无法处理这种错误,操作系统只能对其做崩溃处理。

另外,IMDS还提供了一种“幕后”工作机制,以更高的效率和灵活性来分配和释放内存,避免使用多个底层分配器类型,从而避免了malloc()/free()固有的风险。

事实上,进入安全关键领域的C语言软件工程师需要知道,需求和风险高于消费者或业务应用程序开发。编写避免动态内存分配的代码,而使用一个或多个自定义内存管理器虽然不太方便,但它增加了C语言程序的安全性和稳定性,这是安全关键系统工程师应该接受的一个折衷方案。

文章作者:嵌入式时代

https://www.21ic.com/tougao/article/18778.html

这篇关于美国军方禁止在嵌入式C程序使用malloc(),99%的人都不知道的原因的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/895855

相关文章

gitlab安装及邮箱配置和常用使用方式

《gitlab安装及邮箱配置和常用使用方式》:本文主要介绍gitlab安装及邮箱配置和常用使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1.安装GitLab2.配置GitLab邮件服务3.GitLab的账号注册邮箱验证及其分组4.gitlab分支和标签的

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项

nginx启动命令和默认配置文件的使用

《nginx启动命令和默认配置文件的使用》:本文主要介绍nginx启动命令和默认配置文件的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录常见命令nginx.conf配置文件location匹配规则图片服务器总结常见命令# 默认配置文件启动./nginx

在Windows上使用qemu安装ubuntu24.04服务器的详细指南

《在Windows上使用qemu安装ubuntu24.04服务器的详细指南》本文介绍了在Windows上使用QEMU安装Ubuntu24.04的全流程:安装QEMU、准备ISO镜像、创建虚拟磁盘、配置... 目录1. 安装QEMU环境2. 准备Ubuntu 24.04镜像3. 启动QEMU安装Ubuntu4

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删