嵌入式内存分配

定义:内存分配是指在程序执行的过程中分配或者回收存储空间的分配内存的方法。内存分配方法有静态内存分配和动态内存分配两种。
1.内存
关于存储器可以分为许多种类,它们分别有:寄存器(register)、随机存储器(RAM)、只读存储器(ROM)、可擦除存储器(EEPROM)、闪存(Flash),我们常说的内存,其实指的就是随机存储器(RAM)。一个C/C++程序中,内存分布分为几个部分:
图片说明
(1)栈(stack):栈又称堆栈,是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出(FIFO)特点,所以栈特别方便用来保存/恢复调用现场。
(2)堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
(3)bss段:(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。bss是英文Block Started by Symbol的简称。bss段属于静态内存分配。
(4)data段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
(5)text段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域,一般存放在ROM中。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
在嵌入式应用程序中,编译出来的生成文件里面都会有一个.map文件。这个文件是根据链接脚本文件编译生成的文件。它里面描述了代码函数变量在内存中的分配信息。我们可以查看这个.map文件来查看函数和变量在内存里面的具***置。
2.嵌入式系统中对内存分配的要求
(1)快速性。嵌入式系统中对实时性的保证,要求内存分配过程要尽可能地快。因此在嵌入式系统中,不可能采用通用操作系统中复杂而完善的内存分配策略,一般都采用简单、快速的内存分配方案。当然,对实性要求的程序不同,分配方案也有所不同。
(2)可靠性。也就是内存分配的请求必须得到满足,如果分配失败可能会带来灾难性的后果。
(3)高效性。内存分配要尽可能地少浪费。不可能为了保证满足所有的内存分配请求而将内存配置得无限大。一方面,嵌入式系统对成本的要求使得内存在其中只是一种很有限的资源;另一方面,即使不考虑成本的因素,系统有限的空间和有限的板面积决定了可配置的内存容量是很限的。
3.静态分配与动态分配
(1)静态内存分配就是在程序运行前,就已经对内存进行分配好,运行过程中不会再改变得内存分配方法。比如,我在代码里面给栈分配256个字节的大小空间,在整个代码的运行生命周期里,这个栈的空间大小不能再改变。
(2)动态内存分配是在程序运行时,进行内存分配的内存分配方法。它允许程序运行时,给需要的变量和开辟内存空间。程序结束后,回收所开辟的内存。C语言有两个函数就是专门干这个活,它们分别是malloc() 和 free()函数。
(3)选择
如果是系统对于实时性和可靠性的要求极高(硬实时系统),不能容忍一点延时或者一次分配失败,当然需要采用静态分配方案,也就是在程序编译时所需要的内存都已经分配好了。例如,火星探测器上面的嵌入式系统就必须采用静态分配的方案。在这样的应用场合,成本不是优先考虑的对象,实时性和可靠性才是必须保证的。当然,采用静态分配一个不可避免的缺点就是系统失去了灵活性,必须在设计阶段就预先知道所需要的内存并对之作出分配;必须在设计阶段就预先考虑到所有可能的情况,因为一旦出现没有考虑到的情况,系统就无法处理。这样的分配方案必须导致很大的浪费,因为内存分配必须按照最坏情况进行最大的配置,而实际上在运行中可能使用的只是其中的一小部分;而且在硬件平台不变的情况下,不可能灵活地为系统添加功能,从而使得系统的升级变得困难。

大多数的系统是硬实时系统和软实时系统的综合。也就是说,系统中的一部分任务有严格的时限要求,而另一部分只是要求完成得越快越好。按照rms(rate monotoin scheduling)理论,这样的系统必须采用抢先式任务调度;而在这样的系统中,就可以采用动态内存分配来满足那一部分可靠性和实时性要求不那么高的任务。采用动态内存分配的好处就是给设计者很大的灵活性,可以方便地将原来运行于非嵌入式操作系统的程序移植到嵌入式系统中,比如,许多嵌入式系统中使用的网络协议栈。另外,采用动态内存分配可以使设计者在不改变基本的硬件平台的情况下,比较灵活地调整系统的功能,在系统中各个功能之间作出权衡。说到底,动态内存分配给了嵌入式系统的程序设计者在比较少的限制和较大的自由。因此,大多数实时操作系统提供了动态内存分配接口,例如malloc和free函数
3.相关习题
1、内存是进程范围or线程范围;____

CPU调度时,针对进程or线程;____(内核线程)

函数调用堆栈,针对进程or线程。____
2、写出内存拷贝函数memcpy的功能实现代码

#include <stdio.h>
#include <assert.h>
void*mymemcpy(void *dest,const void *src,size_t count)
{
    char*dest_t=(char*)dest;
    char*src_t=(char*)src;
    assert(dest!=NULL&&src!=NULL);
    if(dest_t>=src_t+count||src_t>=dest_t+count)
    {
        while(count--)

        {
            *dest_t++=*src_t++;
        }
    }
    return dest_t;
}
int main(int argc, const char *argv[])
{
    char buf[]={"i love"};
    char buf1[20]={0};
    mymemcpy(buf1,buf,5);
    printf("%s\n",buf1);
    return 0;
}

3、内存有哪些分配方式,他们的区别是什么?
内存有三种分配方式:
在堆区 程序运行的时候
在栈上 执行函数的时候
在静态区(程序编译时候分配)
4、

char a[]=”hello”,
int b[100]={0};
void*p=malloc(sizeof(int)*100);
const char *strstr(char str[100])
{
} 

则下面结果是:
sizeof(a)=6
sizeof(b)=400
sizeof(p)=4
sizeof(str)=4
5、栈与堆的区别
6、当你申请空间失败的时候,该怎么做
7、程序里如何保证内存不泄***r>8、

ptr1 = (char *)malloc(4);
ptr2= (char *)malloc(100);
ptr3 = (char *)malloc(400);
a = sizeof(ptr1);
b = sizeof(prt2);
c = sizeof(ptr3);

问a =4,b=4,c=4
9、以下程序的正确与否?并说明原因

(1)void Memory_0(char p)
{
p = (char *)malloc(100);
}
(2)Char *Memory_1(char *p)
{
char p[] = “abcdefgh”;
return p;
}
(3)void Memory_3(char *
p)
{
p = (char *)malloc(100);
}
10、下面4个例子中存在哪些问题,请一一指出:
(1)、
void GetMemory( char p )
{
p = (char *) malloc( 100 );
}
void Test( void )
{
char *str = NULL;
GetMemory( str );
strcpy( str, "hello world" );
printf( str );
}
(2)、
char *GetMemory( void )
{
char p[] = "hello world";
return p;
}
void Test( void )
{
char *str = NULL;
str = GetMemory();
printf( str );
}
(3)、
void GetMemory( char *
p, int num )
{
*p = (char *) malloc( num );
}
void Test( void )
{
char *str = NULL;
GetMemory( &str, 100 );
strcpy( str, "hello" );
printf( str );
}
(4)、
void Test( void )
{
char *str = (char *) malloc( 100 );
strcpy( str, "hello" );
free( str );
... //省略的其它语句
}

(1)、GetMemory( char p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完char *str = NULL;GetMemory( str );后的str仍然为NULL;
(2)、char p[] = "hello world"; return p;的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。
(3)、GetMemory避免了试题4的问题,传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句
p = (char *) malloc( num );后未判断内存是否申请成功,应加上:
if ( *p == NULL )
{
...//进行申请内存失败处理
}
10、写一个安全的内存拷贝的函数
11、以下哪句说法是正确的。D
A 在页式存储管理中,用户应将自己的程序划分成若干个相等的页。
B 所有进程都挂起时,系统陷入死锁。
C 执行系统调用可以被中断。
D 进程优先数是进程调度的重要依据,必须根据进程运行情况动态改变。
12、内存对齐指的是什么
(1)内存对齐的主要作用:
1、平台原因(移植原因)
A 不是所有的硬件平台都能访问任意地址上的任意数据的;
B 某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:
A 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
B 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
(2)内存对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。(详情参考https://baike.baidu.com/item/%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90/9537460?fr=aladdin)
13、程序分析:

char g_c[6];
int g_i1;
int g_i2 = 10;
int main()
{
Int l_i = 89;
return 0;
}

请尝试说明。
(1) 变量g_c, g_il, g_i2, 以及 l_i各自在a.out中的那些段,(text, data, bss…等等…)?
(2) g_c, g_il, g_i2, l_i 各自运行在哪些地址空间中?
14、解答题
写出下面四个函数你认为有问题的地方,并说明原因

int tst_malloc_buf(char *buf)
{
buf = (char *)malloc(128);
if(buf  !=  NULL)
return 0;
return -1;
}

int tst_fill_str(char *buf)
{
if(buf != NULL)
{
sprintf(buf,”hello world!\n”);
return 0;
}
return -1;
}

int tst_free_buf(char *buf)
{
if(buf  != NULL)
{
free(buf);
buf = NULL;
}
return 0;
}

int main(int argc, char *argv[])
{
char *buf1 = malloc(128);
if(buf1 != NULL)
{
tst_fill_str(buf1);
printf(“%s\n”,buf1); 
tst_free_buf(buf1);
}

char *buf2 = “wuhanzhihuibweiishen”;
tst_fill_str(buf2);
printf(“%s”,buf2);
char *buf3 = NULL;
if(tst_malloc_buf(buf3)  ==  0)
{
tst_fill_str(buf3);
printf(“%s”,buf3);
}
if(buf 1 != NULL)
{
free(buf1);
buf1 = NULL;
}
}

15、自己编程实现memcpy函数,void *memcpy(void *dest, const void *src, size_t n),注意不能产生数据覆盖;
16、数据库存满了,会怎样?
17、memcpy 与 memmove 区别?
都是C语言中bai的库函数,在头文件string.h中,作用是拷贝du一定长度的内zhi存的内容,原型分别如下:
void *memcpy(void *dst, const void *src, size_t count);
void *memmove(void *dst, const void *src, size_t count);
他们的作用是一样的,唯一的区别是,当内存发生局部重叠的时候,memmove保证拷贝的结果是正确的,memcpy不保证拷贝的结果的正确。

内容整理来源:https://www.jianshu.com/p/53ee3ad9aa50
https://www.21ic.com/embed/hardware/development/201811/82018.html
https://blog.csdn.net/xt18971492243/article/details/79371823

全部评论

相关推荐

3 5 评论
分享
牛客网
牛客企业服务