记录嵌入式C内存划分,后续会更新动态内存管理
- 内存划分
- 栈区 stack有时也称为堆栈,重点在栈字,存放函数内部临时变量
- 堆区 heap也就是动态申请(malloc)、释放(free)的内存区域
- 数据区 data初始化的全局变量和静态变量, 占用可执行文件空间;rodata 固定不变const修饰的全局变量,不占内存空间
- bss区未初始化的全局变量、静态变量(static关键字描述的),初始化为全0的全局变量,不占用可执行文件大小
- 代码区 text程序二进制文件
- 文件映射区域:实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一映射关系。
1.1 栈区域内存测试
#include <stdio.h>
int main(void)
{
int a=100;
int b[3]={0};
int c=200;
printf("ori> a[%p]=%d,c[%p]=%d\r\n",&a,a,&c,c);
printf(" > b[%p]\r\n",&b);
b[0]=0;
b[1]=1;
b[2]=2;
b[3]=3;//error ->a
printf("new> a[%p]=%d,c[%p]=%d\r\n",&a,a,&c,c);
return 0;
}
运行结果:
ori> a[0028FEBC]=100,c[0028FEAC]=200
> b[0028FEB0]
new> a[0028FEBC]=3,c[0028FEAC]=200
结合打印的变量地址,栈空间分配如下图,因为数组b的操作越界,导致了变量a的值被覆盖。图片针对个人情况,一般情况下内存溢出都是使用数组越界,所以在异常值后或者前查看有没数组(全局变量可以查map文件),检查数组的操作是否正确。
除了堆区,其他几个区都是有编译器和系统运行时自动处理的,而堆区由开发者来操作的。这既是便利,也是隐患,一旦操作失误就是内存泄漏或溢出。
1.2 项目实战------------堆和栈的使用和分配
【场景描述】
嵌入式Linux
,在一个任务中,初始化一个较大的局部变量 char arry[1024*1024]
, 1M
的内存空间
while(1)
里面一直获取数据到一片地址,使用memcpy
进行数据拷贝,从char型数组中拷贝出去,代码会一直segmentstion fault
段错误
void main(){
char arry[1024*1024] = {0}; // 申请 1M 的内存空间
char uart_buff[1024]; // 1024 字节 接收串口收到的数据
char cnt = 0;
while(1){
uart_receive(uart_buff,1024); // 串口每次接收 1024 个数据
memcpy(arry[1024*cnt],uart_buff,1024);
cnt++;
}
}
【问题调试】
若以上问题,不在栈分配空间,而使用 malloc 动态分配堆的空间,则不报错误
【问题分析】
segmentstion fault
段错误肯定是 堆栈的问题;但是如果堆栈有问题的话,在最开始申请char数组的时候就会申请不到啊,运行就会出问题,而不应该在多次调用memcpy之后才出问题
- 变量在定义的时候不会报错,在实际使用的时候会由于空间不足报错
- 压栈操作是在运行中进行的,当你当前栈呀不进去了就溢出
- 栈的大小一般也就几M
【注意事项】
存储方式:栈内存采用“ 先进后出”的存储方式,而堆内存采用 随机存储的方式。
空间大小:栈内存的空间 远小于 堆的内存空间。堆可以在需要时 动态增长,而战的大小是 固定的。而栈的大小受限于系统分配的栈空间,通常为2MB
或更小。
访问速度:由于栈内存的存储方式较为简单,访问速度比堆内存快。访问栈的速度 远大于 访问堆的速度