嵌入式C语言的自我修养:内存泄漏与防范

news2024/9/27 17:32:27
内存泄漏与防范
一个内存泄漏的例子

如果我们使用malloc()申请的内存在使用结束后没有及时被释放,则C标准库中的内存分配器ptmalloc和内核中的内存管理子系统都失去了对这块内存的追踪和管理。

#include <stdlib.h>
int main(void){
char *p;
p=(char *)malloc(32);
strcpy(p,“hello");
puts(p);
return 0;
}

在函数退出之前,如果我们没有使用free()函数及时 将 这 块 内 存 归 还 给 内 存 分 配 器 ptmalloc或内存管理子系统,ptmalloc和内存管理子系统就失去了对这块内存的控制权,它们可能认为用户还在使用这片内存。等下次去申请内存时,内存分配器和内存管理子系统都没有这块内存的信息,所以不可能把这块内存再分配给用户使用。

图中有大小为548 Byte和504 Byte的两个内存块,一开始这两个内存块是在空闲链表中的,当用户使用malloc()申请内存时,内存分配器ptmalloc将这两个内存块节点从空闲链表中摘除,并把内存块的地址返给用户使用。如果用户使用后忘了归还,那么空闲链表中就没有这两个内存块的信息,这两块内存也就无法继续使用了,在内存中就产生了两个漏洞,即发生内存泄漏。
预防内存泄漏

预防内存泄漏最好的方法就是:内存申请后及时地释放,两者要配对使用,内存释放后要及时将指针设置为NULL,使用内存指针前要进行非空判断。

内存泄漏检测:MTrace

MTrace,Valgrind,Dmalloc,purify,KCachegrind,MallocDebug

#include <mcheck.h>
void mtrace(void);
void muntrace(void);

如果想检测一段代码是否有内存泄漏,则可以把这两个函数添加到要检测的程序代码中。

#include <stdlib.h>
#include <string.h>
#include <mcheck.h>
int main (void){
mtrace();
//开启跟踪
char *p,*q;
p =(char *)malloc(8);
9 =(char *)malloc(8);
strcpy(p, “hello”);
strcpy(9,“world”);
free(p);
muntrace();//关闭跟踪
return 0;
}

根据动态内存的使用记录,我们可以很快定位到内存泄漏发生在mcheck.c文件中的第11行代码。

#mtrace a.out mtrace.log
Memory not freed:
address Size Caller
0x0901a380 0x8 at /home/c/mem leak/mcheck.c:11
广义上的内存泄漏

广义上的内存泄漏指系统频繁地进行内存申请和释放,导致内存碎片越来越多,无法再申请一片连续的大块内存。如fast bins,主要用来保存用户释放的小于80 Bytes(M_MXFAST)的内存,在提高内存分配效率的同时,带来了大量的内存碎片。

为了最大化地提高系统性能,我们可以通过一些参数对glibc的内存分配器进行调整,使之与我们的实际业务需求达到更大的匹配度,更高效地应对实际业务的需求。

glibc底层实现了一个mallopt()函数,可以通过这个函数对上面的各种参数进行调整。

#include <malloc.h>
int mallopt(int param, int value);
常见的内存错误及检测

处理器引入MMU后,操作系统接管了内存管理的工作,负责虚拟空间和物理空间的地址映射和权限管理。

内存管理子系统将一个进程的虚拟空间划分为不同的区域,如代码段、数据段、BSS段、堆、栈、mmap映射区域、内核空间等,每个区域都有不同的读、写、执行权限。

通过内存管理,每个区域都有具体的访问权限,如只读、读写、禁止访问等。数据段、BSS段、堆栈区域都属于读写区,而代码段则属于只读区,如果你往代码段的地址空间上写数据,就会发生一个段错误。

对于应用程序来说,常见的内存错误一般主要分为以下几种类型:内存越界、内存踩踏、多次释放、非法指针。

段错误

当我们往一个只读区域的地址空间执行写操作时,或者访问一个禁止访问的地址(如零地址)时,都会发生段错误。内核空间、零地址、堆和mmap区域之间的内存空间,这部分地址空间要么被内核占用,要么还处于“未开发”状态,需要申请才能使用。

(1)在调试链表时,通常通过指针来操作每一个节点。如果指针在遍历链表时已经指向链表的末尾或头部,指针已经指向NULL了,此时再通过该指针去访问节点的成员,就相当于访问零地址了,也会发生一个段错误,这个指针也就变成了非法指针。

(2)每一个用户进程默认有8MB大小的栈空间,如果在函数内定义大容量的数组或局部变量,就可能造成栈溢出,会引发一个段错误。内核中的线程也是如此,每一个内核线程只有8KB的内核栈,在实际使用中也要非常小心,防止堆栈溢出。

(3)使用malloc()申请的堆内存,如果不小心多次使用free()进行释放,通常也会触发一个段错误。

由于C语言语法检查的宽松性,程序中对内存访问的各种操作并不报错,或者给一个警告信息,这会导致程序在运行期间出现段错误时很难定位。此时我们可以借助一些第三方工具来快速定位段错误。

使用core dump调试段错误

在Linux环境下运行的应用程序,由于各种异常或Bug,会导致程序退出或被终止运行。此时系统会将该程序运行时的内存、寄存器状态、堆栈指针、内存管理信息、各种函数的堆栈调用信息保存到一个core文件中。

core dump功能开启后运行a.out,发生段错误后就会在当前目录下生成一个core文件,然后我们就可以使用gdb来解析这个core文件来定位程序到底出错在哪里。在GDB交互环境下,我们通过bt查看调用栈信息,可以查看错误的具体行数。

内存踩踏

比如申请两块动态内存,对其中的一块内存写数据时产生了溢出,就会把溢出的数据写到另一块缓冲区里。在缓冲区释放之前,系统是不会发现任何错误的,也不会报任何提示信息,但是程序却可能因为误操作,覆盖了另一块缓冲区的数据,造成程序莫名其妙的错误。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void){
char *p,*q;
p= malloc(16);
9= malloc(16);
strcpy(p, "hello world! hello zhaixue.cc!\n");
printf("%s\n",p);
printf("%s\n",q);
while (1);
free(q);
free(p);
return 0;
}

如果一个进程中有多个线程,多个线程都申请堆内存,这些堆内存就可能彼此相邻,使用时需要谨慎,提防越界。在内核驱动开发中,驱动代码运行在特权状态,对内存访问比较自由,多个驱动程序申请的物理内存也可能彼此相邻。

内存踩踏监测:mprotect

mprotect()是Linux环境下一个用来保护内存非法写入的函数,它

会监测要保护的内存的使用情况,一旦遇到非法访问就立即终止当前

进程的运行,并产生一个core dump。

#include <sys/mman.h>
int mprotect(void *addr, size t len, int prot);

mprotect()函数的第一个参数为要保护的内存的起始地址,len表示内存的长度,第三个参数prot表示要设置的内存访问权限。

内存检测神器:Valgrind

Valgrind包含一套工具集,其中一个内存检测工具Memcheck可以对我们的内存进行内存覆盖、内存泄漏、内存越界检测。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2165685.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

plt常用函数介绍一

目录 前言plt.figure()plt.subplot()plt.subplots()plt.xticks()plt.xlim() 前言 Matplotlib是Python中的一个库&#xff0c;它是数字的-NumPy库的数学扩展。 Pyplot是Matplotlib模块的基于状态的接口。在Pyplot中可以使用各种图&#xff0c;例如线图&#xff0c;轮廓图&#…

C++独立开发开源大数计算库 CBigNum

项目简介&项目地址 CBigNum 是本人独立开发开源的一款大数计算库&#xff0c;支持任意位数整数带任意位数小数的浮点运算。您可以通过本库执行非常大的数据运算或非常高精度的除法运算(您可以随意指定除法的小数保留到第几位)以及各种科学计算(详见1.3)。 项目地址&#…

数字电路基础(锁存器+触发器)+Proteus仿真

1.锁存器 1.1.基本概念 1.1.1基本双稳态电路 下面电路中&#xff0c;具有0、1两种逻辑状态&#xff0c;一旦进入其中一种状态&#xff0c;就能长期保持不变的单元电路称为双稳态存储电路&#xff0c;简称双稳态电路。 锁存器和触发器都属于双稳态电路 该双稳态电路没有输入…

【Godot4自学手册】第四十八节创建雨粒子效果

今天我们要利用GPU粒子节点玩雨粒子效果&#xff0c;下雨天。 一、添加GPU粒子系统 添加GPUParticles2D节点。选择根节点&#xff0c;单击添加按钮&#xff0c;选择GPUParticles2D&#xff0c;完成添加。 二、修改属性 1.设置粒子数量。 在GPUParticles2D检查器中将Amount设…

智慧城市主要运营模式分析

(一)运营模式演变 作为新一代信息化技术落地应用的新事物,智慧城市在建设模式方面借鉴了大量工程建设的经验,如平行发包(DBB,Design-Bid-Build)、EPC工程总承包、PPP等模式等,这些模式在不同的发展阶段和条件下发挥了重要作用。 在智慧城市发展模式从政府主导、以建为主、…

求一个数的因子数(c语言)

1.计算并输出给定整数n的所有因子&#xff08;不包括1与n自身&#xff09;之和。规定n的值不大于1000。&#xff08;因子是能整除n的数 即n%i0&#xff09; // 例如&#xff0c;在主函数中从键盘给n输入的值为856&#xff0c;则输出为: sum763。 2.第一步我们先输入n的数&…

C++深入学习string类成员函数(1):默认与迭代

引言 在 C 编程中&#xff0c;std::string 类是处理字符串的核心工具之一。作为一个动态管理字符数组的类&#xff0c;它不仅提供了丰富的功能&#xff0c;还通过高效的内存管理和操作接口&#xff0c;极大地方便了字符串操作。通过深入探讨 std::string 的各类成员函数&#…

【java21】java21新特性之简单的Web服务器jwebserver

jwebserver是Java 18中引入的一个全新功能点&#xff0c;它允许用户通过命令行工具快速启动一个提供静态资源访问的迷你Web服务器。这个服务器不支持CGI和Servlet&#xff0c;因此其主要用途是轻量级的静态文件服务&#xff0c;如HTML、CSS、JavaScript和图片等。 其实在如Pyt…

热源检测系统源码分享

热源检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

大语言模型之LlaMA系列- LlaMA 2及LLaMA2_chat(上)

LlaMA 2是一个经过预训练与微调的基于自回归的transformer的LLMs&#xff0c;参数从7B至70B。同期推出的Llama 2-Chat是Llama 2专门为对话领域微调的模型。 在许多开放的基准测试中Llama 2-Chat优于其他开源的聊天模型&#xff0c;此外Llama 2-Chat还做了可用性与安全性评估。 …

大数据 flink 01 | 从零环境搭建 简单Demo 运行

什么是Flink Flink是一个开源的流处理和批处理框架,它能够处理无界和有界的数据流&#xff0c;具有高吞吐量、低延迟和容错性等特点 Flink 可以应用于多个领域如&#xff1a;实时数据处理、数据分析、机器学习、事件驱动等。 什么是流式处理&#xff1f;什么是批处理 流处理…

Python 如何使用 unittest 模块编写单元测试

Python 如何使用 unittest 模块编写单元测试 单元测试是软件开发过程中的重要环节&#xff0c;它帮助开发者验证代码的正确性&#xff0c;确保功能按预期工作。Python 提供了一个强大的内置模块 unittest&#xff0c;使得编写和执行单元测试变得非常方便。本文将深入探讨如何使…

计算机组成原理(笔记5原码和补码的乘法以及直接补码阵列乘法器 )

原码一位乘法 手算&#xff1a;过程 令x′|x|0.x1x2…xn-1xn&#xff0c;y′|y|0.y1y2…yn-1yn 同时令乘积P′ |P| x′ y′&#xff0c;有&#xff1a; x′ y′ x′(0.y1y2…yn-1yn) x′ (y12-1y22-2…yn-12-(n-1)yn2-n) 2-1(y1x′2-1(y2x′…2-1(yn-1x′2-1(ynx′0))…))…

使用awvs测试站点并输出漏洞报告教程

环境配置 pikachu靶场 awvs 使用步骤 1.访问本机3443端口&#xff08;安装时自己设定的端口&#xff09; 2.点击【Targets】--》【Add Targets】新建扫描目标&#xff0c;输入目标网址&#xff08;以pikachu靶场为例&#xff09;&#xff0c;点击【Save】开始扫描 2.点击【…

【AI大模型】股票价格预测精度增强,基于变分模态分解、PatchTST和自适应尺度加权层

简介 股票价格指数是金融市场和经济健康的晴雨表&#xff0c;准确预测对投资决策至关重要。股票市场的高频交易和复杂行为使得预测具有挑战性&#xff0c;需开发稳定、准确的预测模型。研究表明&#xff0c;估值比率、数据驱动模型&#xff08;如支持向量机&#xff09;、股票…

机器学习 | 使用scikit-learn学习Python中的PCA(主成分分析)

为什么选择PCA&#xff1f; 当有许多输入属性时&#xff0c;很难将数据可视化。在机器学习领域有一个非常著名的术语“维度诅咒”。基本上&#xff0c;它指的是数据集中的属性数量越多&#xff0c;对机器学习模型的准确性和训练时间产生不利影响。主成分分析&#xff08;PCA&a…

使用Postman工具接口测试

文章目录 一、接口1.1 接口的概念1.2 接口的类型 二、接口测试2.1 概念2.2 原理2.3 特点 三、HTTP协议3.1 http协议简介3.2 URL格式3.3 HTTP请求3.3.1 请求行3.3.2 请求头3.3.3 请求体 3.4 HTTP响应3.4.1 状态行3.4.2 响应头3.4.3 响应体 3.4 传统风格接口3.5 RESTful风格接口 …

二网络复习

软路由&#xff1a; 1. ikuai 实现了一个多宽带线路的一个聚合可用家庭环境 2. Linux通过开启路由转发模拟路由器 &#xff08;仅学习使用&#xff09; #开启路由转发命令 vim /etc/sysctl.conf net.ipv4.ip_forward 1 sys…

C++学习笔记----8、掌握类与对象(一)---- 对象中的动态内存分配(1)

1、FRIENDS c允许类声明为其它类&#xff0c;其它类的成员函数&#xff0c;或者非成员函数为friend。可以访问protected与private数据成员与成员函数。例如&#xff0c;假设你有两个类Foo与Bar。你可以指定Bar类是Foo类的一个friend&#xff1a; class Foo {friend class Bar;…

《声入人心》团综重启,芒果能否再造一个群像神话?

随着《声入人心》团综《吾湖音乐局》于9月20日宣布重启&#xff0c;芒果的又一群像综艺“杀”回了市场。 从2018年音综市场冲出的一匹黑马&#xff0c;到2024年“声人”分散在影视综各个领域&#xff0c;这六年间芒果上演了无数次“狼来了”&#xff0c;但这一次团综是真的来了…